Betterbird / thunderbird-patches

Betterbird is a fork of Mozilla Thunderbird. Here are the patches that provide all the goodness.
Other
468 stars 20 forks source link

Formalize a declarative interface for (regular) extensions #328

Closed eyalroz closed 1 month ago

eyalroz commented 1 month ago

As we all know, TB has dropped the regular extension/chrome loading mechanism which had been used for years. Instead, to load an extension, one must use a sort of a "loophole", a WebExtension "Experiment" which asks for some specific privileges, and can then load chrome by using those privileges. This is John Bieling's WindowListener mechanism.

It would be nice if BetterBird could support loading extensions without each extension having to explicitly include the WindowListener, and some boilerplate imperative code which uses it. Instead, BB could arrange it so that an extension could be declarative in describing its content, the way things used to be in the past, and BB would hold the imperative code to parse the declarations and load what's necessary.

Note that this is not far from where extensions already are right now. To use mine as an example: Look at my BiDiMailUI's background.js script (the basic imperative code which gets run for an extension:

(async function () {
  messenger.WindowListener.registerDefaultPrefs("defaults/preferences/bidimailui.js");
  messenger.WindowListener.registerChromeUrl([
    ["content",  "bidimailui",           "chrome/content/"      ],
    ["resource", "bidimailui",           "chrome/"              ],
    ["locale",   "bidimailui", "en-US",  "chrome/locale/en-US/" ],
    ["locale",   "bidimailui", "he",     "chrome/locale/he/"    ],
    ["locale",   "bidimailui", "ar",     "chrome/locale/ar/"    ],
    ["locale",   "bidimailui", "fa",     "chrome/locale/fa/"    ],
    ["locale",   "bidimailui", "ur",     "chrome/locale/ur/"    ]
  ]);
  let registerChromeInjectors = function (registrationInfo) {
    for (let [windowHref, relativeInjectorPath] of registrationInfo) {
      let absoluteWindowHref = (windowHref.startsWith('about:') || windowHref.startsWith("chrome://")) ?
        windowHref : `chrome://messenger/content/${windowHref}`;
      let jsFile = `chrome://bidimailui/content/overlay-injectors/${relativeInjectorPath}`;
      messenger.WindowListener.registerWindow(absoluteWindowHref, jsFile);
    }
  };

  registerChromeInjectors([
    ["about:3pane",                                 "3pane.js"            ],
    ["messenger.xhtml",                             "messenger.js"        ],
    ["about:message",                               "3pane.js"            ],
    ["messageWindow.xhtml",                         "messenger.js"        ],
    ["messengercompose/messengercompose.xhtml",     "messengercompose.js" ],
    ["customizeToolbar.xhtml",                      "customizeToolbar.js" ]
  ]);

  messenger.WindowListener.registerOptionsPage("chrome://bidimailui/content/bidimailui-prefs-dialog.xhtml");
  messenger.WindowListener.startListening();
})();

The main parts of this code are actually two tables and two individual URIs:

Now, an extension could provide the first two items in the manifest, and the tables in some separate file-per-table (e.g. a JSON file or whatever is convenient). Then BB could do the work that my background.js does now. And the extension could (if it were to forego TB support) shed:

which is a nice win. Again, most of the implementation would simply be the current code of WindowListener.

Betterbird commented 1 month ago

This would lead to "Betterbird-only" extensions. We don't think anyone wants this.

The WL is way overloaded to cater for all that former XUL overlay extensions might want to do. We suggest not to use it, but instead pick the required bits and reduce the experiment part of the extension to a minimum, see for example here:

https://github.com/Betterbird/quick-folder-move-extension-classic/blob/Classic/src/api/quickmove.js

eyalroz commented 1 month ago

This would lead to "Betterbird-only" extensions. We don't think anyone wants this.

Actually, it won't. It will have, rather, an opposite effect. You see, everyone wants their extension to be loaded into TB. But - this mechanism will reduce the amount of extensions-specific content to a minimum, that's declarative. The rest will be built-in in BB, but added as a souped-up WindowListener, for TB.

That will make writing and loading a regular extension simpler for both BB and TB.

We suggest not to use it, but instead pick the required bits and reduce the experiment part of the extension to a minimum

A poor suggestion for two reasons:

  1. A lot of extension-specific code, just for loading the extension.
  2. This approach legitimizes TB's eventual decision to close the loophole for their loading.
eyalroz commented 1 month ago

@Betterbird : I am slowly implementing my end of this approach. So far I've gotten to this point:

(async function () {
  messenger.WindowListener.registerChromeUrls();  // no need for arguments, takes a list of locale specs from the manifest
  registerChromeInjectors([
    ["messenger.xhtml",        "messenger.js"],
    ["about:3pane",            "3pane.js"],
    ["customizeToolbar.xhtml", "customizeToolbar.js"]
  ]);
  registerDefaultPrefs(); // no need for arguments, uses the extension's short name 
                          // from the manifest to generate a default location: `default/preferences/${shortname}.js`
  registerOptionsPage();  // no need for arguments, uses the extension's short name 
                          // from the manifest to generate a default URI
  messenger.WindowListener.startListening();
})();

I'm now mulling over whether to put the overlay injector scripts in the manifest, or already go another step further and try to get rid of them altogether. With that done, I will have reached a completely extension-neutral background.js, so that extension authors have nothing to do other than specify a few details in the manifest.json.