Open Bug 1602201 Opened 4 years ago Updated 9 months ago

Provide an Mail Extension API to switch between Original HTML | Simple HTML | Plaintext in Message Reader

Categories

(Thunderbird :: Add-Ons: Extensions API, enhancement)

enhancement

Tracking

(Not tracked)

People

(Reporter: Thunderbird_Mail_DE, Unassigned)

References

Details

(Whiteboard: [Prio2023])

Attachments

(1 file, 7 obsolete files)

In RFE Bug 1598857 an "Allow HTML Temp" function should be integrated to core Thunderbird. If this RFE will not be implemented into core, it would be necessary to have a relating Mail Extension API, to allow this feature (without manipulating core prefs / about:prefs)

For more information read the description from Bug 1598857

I have to describe that more exactly. It would be necessary to change the related prefs without (!) a reload of the displayed message. After showing the message once in original HTML, the prefs have to be reset to the prior values. The message should "stay" with HTML, but the prefs should be reset.

Edit: Wrong bug. Removed.

If ever such an API is created, one could include an optional parameter that (if true) would then optionally reload the currently displayed message as well.

Do you happen to have code, which I could use to start investigating this?

Edit: I will have a look at your AHT add-on.

Flags: needinfo?(john)

Here is my first attemp for the API.

I'm not quite sure, but I think, an onChanged listener could be added. And for a build in API it should require an own permission like "messageContentPolicy".

schema.json:

[
  {
    "namespace": "messageContentPolicy",
    "functions": [
      {
        "name": "getCurrent",
        "description": "Resolves with the object currentPolicy, including the properties msgBodyAs, remoteContent, attachmentsInline",
        "type": "function",
        "async": true,
        "parameters": []
      },
      {
        "name": "update",
        "description": "Updates the related application settings by the given updateProperties object, and than reloads the displayed message, if the optional parameter doNotReloadMsg is not true.",
        "type": "function",
        "async": true,
        "parameters": [
          {
            "name": "windowId",
            "type": "integer",
            "description": "This windowId is used to get the window in API, to use its global vars and functions."
          },
          {
            "name": "updateProperties",
            "type": "object",
            "description": "Object with the possible properties msgBodyAs, remoteContent, attachmentsInline. Given properties will be updated.",
            "properties": {
              "msgBodyAs": {
                "type": "string",
                "optional": true,
                "description": "This message Body mode will be set. Possible values are: plaintext || sanitized || original || allBodyParts"
              },
              "remoteContent": {
                "type": "boolean",
                "optional": true,
                "description": "If value is true, remote content will be allowed and loaded in displayed messages."
              },
              "attachmentsInline": {
                "type": "boolean",
                "optional": true,
                "description": "If value is true, attached images will be displayed inline."
              }
            }
          },
          {
            "name": "doNotReloadMsg",
            "type": "boolean",
            "optional": true,
            "description": "An optional parameter, to suppress message reload after the properties update, if the value = true."
          }
        ]
      }
    ]
  }
]

implementation.js

var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var { ExtensionCommon } = ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");

var messageContentPolicy = class extends ExtensionCommon.ExtensionAPI {
  getAPI(context) {
    return {
      messageContentPolicy: {
        async getCurrent() {
          return new Promise(resolve => {

//            try {

              /* currentPolicy should resolve as an object
                 currentPolicy.msgBodyAs         = plaintext || sanitized || original || allBodyParts
                 currentPolicy.remoteContent     = true || false
                 currentPolicy.attachmentsInline = true || false
                 if needed, this could later be extended by other related values
                 */

              let currentPolicy = {};

              let prefer_plaintext = Services.prefs.getBoolPref("mailnews.display.prefer_plaintext");
              let html_as = Services.prefs.getIntPref("mailnews.display.html_as");
              let disallow_classes = Services.prefs.getIntPref("mailnews.display.disallow_mime_handlers");

              if ((prefer_plaintext != true) && (html_as == 0) && (disallow_classes == 0)) {
                currentPolicy.msgBodyAs = "original";
              }
              else if ((prefer_plaintext != true) && (html_as == 3) && (disallow_classes > 0)) {
                currentPolicy.msgBodyAs = "sanitized";
              }
              else if ((prefer_plaintext == true) && (html_as == 1) && (disallow_classes > 0)) {
                currentPolicy.msgBodyAs = "plaintext";
              }
              else if ((prefer_plaintext != true) && (html_as == 4) && (disallow_classes == 0)) {
                currentPolicy.msgBodyAs = "allBodyParts";
              }

              currentPolicy.remoteContent = Services.prefs.getBoolPref("mailnews.message_display.disable_remote_image");
              currentPolicy.attachmentsInline = Services.prefs.getBoolPref("mail.inline_attachments");

              resolve(currentPolicy);

//            } catch (e) { resolve(); }

          });
        },

        async update(windowId, updateProperties, doNotReloadMsg) {
          return new Promise(resolve => {

//            try {

              /* updateProperties must be an object with the following possible properties
                 updateProperties.msgBodyAs         = plaintext || sanitized || original || allBodyParts
                 updateProperties.remoteContent     = true || false
                 updateProperties.attachmentsInline = true || false
                 if needed, this could later be extended by other related values

                 doNotReloadMsg is an optional boolean parameter
                 if doNotReloadMsg is false or undefined, the message will be reloaded after prefs update (default behaviour)
                 if doNotReloadMsg is true, than the message must not be reloaded, after prefs update (this is necessary for the "Allow HTML Temp" addon)
                 */

              /* var gDisallow_classes_no_html is provided as a global var by mailWindowOverlay.js
                 See comment in: https://searchfox.org/comm-central/source/mail/base/content/mailWindowOverlay.js#69
                 */

              console.log("updateProperties = ", updateProperties);

              let window = context.extension.windowManager.get(windowId).window
              let gDisallow_classes_no_html = window.gDisallow_classes_no_html;
              console.log("gDisallow_classes_no_html = ", gDisallow_classes_no_html);

              // Fallback to default value = 1, if we don't got the global var
              if (!gDisallow_classes_no_html) {
                gDisallow_classes_no_html = 1;
              }

              // Update the prefs for HTML mode (Menu View -> Message Body As)
              switch(updateProperties.msgBodyAs) {
                case "original":
                  Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", false);
                  Services.prefs.setIntPref("mailnews.display.html_as", 0);
                  Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers", 0);
                  break;
                case "sanitized":
                  Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", false);
                  Services.prefs.setIntPref("mailnews.display.html_as", 3);
                  Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers",
                    gDisallow_classes_no_html);
                  break;
                case "plaintext":
                  Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", true);
                  Services.prefs.setIntPref("mailnews.display.html_as", 1);
                  Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers",
                    gDisallow_classes_no_html);
                  break;
                case "allBodyParts":
                  Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", false);
                  Services.prefs.setIntPref("mailnews.display.html_as", 4);
                  Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers", 0);
                  break;
                default:
                  console.log("updateProperties.msgBodyAs is undefined or has an invalid value")
                }

              // Update the pref for remote content (Preferences → Privacy & Security → Allow remote content in messages)
              if (updateProperties.remoteContent === true) {
                Services.prefs.setBoolPref("mailnews.message_display.disable_remote_image", true);
              }
              else if (updateProperties.remoteContent === false) {
                Services.prefs.setBoolPref("mailnews.message_display.disable_remote_image", false);
              }
              else {
                console.log("updateProperties.remoteContent is undefined or has an invalid value")
              }
              
              // Update the pref for inline attachments (Menu View → Display Attachments Inline)
              if (updateProperties.attachmentsInline === true) {
                Services.prefs.setBoolPref("mail.inline_attachments", true);
              }
              else if (updateProperties.attachmentsInline === false) {
                Services.prefs.setBoolPref("mail.inline_attachments", false);
              }
              else {
                console.log("updateProperties.attachmentsInline is undefined or has an invalid value");
              }

              if (doNotReloadMsg) {
                resolve(true);
              }

              // reload message only, if doNotReloadMsg = false or undefined

              /* ReloadMessage() is provided by: ************************************************************
                 https://searchfox.org/comm-central/source/mail/base/content/msgMail3PaneWindow.js#1909

                  function ReloadMessage() {
                    if (!gFolderDisplay.selectedMessage) {
                      return;
                    }

                    let view = gFolderDisplay.view.dbView;
                    if (view) {
                      view.reloadMessage();
                    }
                  }

                 https://searchfox.org/comm-central/source/mail/base/content/messageWindow.js#949

                  function ReloadMessage() {
                    // If the current message was loaded from a file or attachment, so the dbView
                    // can't handle reloading it. Let's do it ourselves, instead.
                    if (window.arguments[0] instanceof Ci.nsIURI) {
                      gMessageDisplay.displayExternalMessage(window.arguments[0].spec);
                    } else {
                      gFolderDisplay.view.dbView.reloadMessage();
                    }
                  }
              ******************************************************************************************** */

              window.ReloadMessage();
              resolve(true);

//            } catch (e) { resolve(); }

          });
        },


      }
    };
  }
};

The try-out-addon will be attached.

Attachment #9276788 - Attachment is obsolete: true

Fixed missing semicolons

Attachment #9276802 - Attachment is obsolete: true

I would build the experiment API into my own addon - at least into a development branch that might end up in a final version for Thunderbird 2023? So we could test the API (with a few helpers from the forum), expand it (onChange listener) and fix problems.

Can you give me feedback on the current status of the API? For example, I need some help with callbacks, since I have no idea how they should look like.

Major logic change for the property "remoteContent", which has been changed to "disableRemoteContent" to better fit the original pref.

Attachment #9276842 - Attachment is obsolete: true

Add parameter to get and set hidden allBodyParts menuitem.

This is necessary for addons to know, if they should provide their functions and UI elements for this by default hidden option/menuitem.

Attachment #9277694 - Attachment is obsolete: true

Implemented the necessary event

Attachment #9277765 - Attachment is obsolete: true

Don't use var - use let instead.

Attachment #9278246 - Attachment is obsolete: true

Minor changes to log lines

Attachment #9278249 - Attachment is obsolete: true

Thanks for your contribution. I will look at it soon. I may however implement a different approach, which is not depending on global Prefs at all.

Be in mind, that this API should be usable for different Add-ons.

E.g. the "Toggle HTML" addon by Dillinger could use it, to update its button state correctly, when the HTML mode is changed by the user without using the "Toggle HTML" button by his addon. Additionaly Dillingers addon could use the API to know, if the (hidden) allBodyParts option is enabled at all.

The same is for a maybe planned additional addon by me, which could just indicate the HTML mode status in the UI, to be viseble all the time. And this should also be possible, when Thunderbirds own UI was used to change the HTML mode or one of the related "content policy" options. So I have some doubts about your idea and am curious how the approach should/will be.

Severity: normal normal → S3 S3
See Also: → 1829790
Flags: needinfo?(john)
Whiteboard: [Prio2023]
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: