Home > Latest topics

Latest topics > An improvement of WebExtensions on Firefox 64 about implicit collaboration of addons

宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能! シス管系女子って何!? - 「シス管系女子」特設サイト

An improvement of WebExtensions on Firefox 64 about implicit collaboration of addons - Oct 14, 2018

(Note that this article describes about an improvement on Firefox 64, and Firefox ESR60 is out of target.)

Good news! An old feature proposal filed at the time Mozilla announced that XUL become deprecated and WebExtensions become the next main line has became fixed: Bug 1280347 - Add ability to provide custom HTML elements working as alias of existing Firefox UI items, especially tabs.

Why it is a news for me? Let's look around the short history of addon migration from XUL to WebExtensions.

XUL addons were collaborative with each other implicitly

In old days, all XUL addons worked in the common namespace and they modified UI elements and behaviors of Firefox itself as they like.

(An illustration describing that XUL addons modified Firefox itself.)

Because old TST's tab tree was the native tab bar of Firefox itself, tab related features added by other addons were also available there.

(An illustration describing that XUL-based TST just modified appearance of Firefox's UI instead of adding new UI elements.)

As I described at the migration story of Tree Style Tab, XUL addons were collaborative implicitely. This was a large advantage compared to other similar extension systems. But such a unified namespace also introduced chaos from many numbers of addons, including security concern.

WebExtensions addons are isolated and require special effort to collaborate with each other

On the other hand, WebExtensions APIs are designed to separate namespace of each addon.

(An illustration decribing WebExtensions-based addons are separated in each namespace.)

Extra context menu commands provided by other addons are not callable from TST's context menu, because TST's menu is just an imitation based on HTML and CSS in the sidebar area. The menu is also impossible to spread out of the sidebar frame, so it is too narrow and stressful.

As a workaround for the incompatibility with other addons, I implemented public APIs callable via browser.runtime.sendMessage() from others.

(An illustration describing addons can communicate with custom APIs.)

TST's custom APIs reintroduced collaboration of addons partially, but it forced to add more codes to call TST's APIs explicitely. Some addon developers did that and I also sent pull requests to some addons. But there are too much number of addons - this approach is endless, especially about future addons.

Then, what's the improvement on Firefox 64 for this uncomfortable situation?

Firefox 64 provides ability to show context menus including commands for tabs (and bookmarks) added by other addons, on a popup panel or a sidebar panel. Yes, now WE-based addons who add extra context menu commands work together with each other implicitly, without any special effort like a workaround on TST's APIs!

(An illustration describing the new context menu can work same as Firefox's native context menu.)

How can addons do such an collaboration? Let's see basics of custom context menu on WebExtensions addons.

(An illustration describing relations between the "contextmenu" event and the native context menu.)

When you do right-click (or something other action to open the context menu), a contextmenu DOM event is fired on the target element, and the default context menu for an webpage will open.

(An illustration describing the native context menu is cancellable.)

The event is cancellable by calling its preventDefault() method. When you cancel it, the default context menu is also canceled. After that you can draw any custom menu-like UI - TST's imitated context menu was implemented based on this logic. That was most major way to provide custom context menu by a WebExtensions addon, for a long time. (There is another approach based on HTML5's <menu> and related element types, but it looked unconvenient around addon purpose for me.)

On Firefox 64 and later, a new API browser.menus.overrideContext() is introduced.

(An illustration describing what we should do on Firefox 64 and later.)

It is callable on event handlers for the contextmenu DOM events. You'll call it with suitable context information -

(An illustration describing the context menu is switched for the specified context.)

then the default menu will be switched to a context menu for the specified context. The menus is not a fake, but a really native menu provided by Firefox. Extra commands added by other addons are also available, and it spreads out of the sidebar area or the popup panel. Wow!

How to use the new feature?

There are three requirements to try that.

  • Adding new permission menus.overrideContext
  • Specifying a context by browser.menus.overrideContext()
  • Adding menu items with the viewTypes parameter by browser.menus.create()

A new permission menus.overrideContext

The new API browser.menus.overrideContext() becomes callable only when the addon has a new permission menus.overrideContext.

You just need to add a new value menus.overrideContext to the list of permissions in the manifest.json, even if your addon still supports Firefox 63 or older. Old Firefox just ignores such an unknown permission, I've confirmed the behavior on Firefox ESR60. So you don't need putting it under optional_permissions and calling browser.permissions.request({ permissions: ['menus.overrideContext'] }) to grant the required permission after the installation.

Specifying a context by browser.menus.overrideContext()

Here is a snippet to open a tab context menu on your custom UI:

document.addEventListener('contextmenu', event => {
  const tab = event.target.closest('.tab');
  if (tab) {
    // When the context menu is opened on a fake tab element, set the
    // context to "opening a tab context menu on the specified tab".
    browser.menus.overrideContext({
      context: 'tab',
      tabId:   parseInt(tab.dataset.id)
    });
  }
  else {
    // Otherwise, don't show the menu.
    event.preventDefault();
  }
}, { capture: true });

Another snippet to open a bookmark context menu on your custom UI:

document.addEventListener('contextmenu', event => {
  const item = event.target.closest('.bookmark');
  if (item) {
    // When the context menu is opened on a fake bookmark item, set the
    // context to "opening a bookmark context menu on the specified item".
    browser.menus.overrideContext({
      context:    'bookmark',
      bookmarkId: parseInt(item.dataset.id)
    });
  }
  else {
    // Otherwise, don't show the menu.
    event.preventDefault();
  }
}, { capture: true });

However, you'll see a sorry menu with only extra commands added by other addons.

(A screenshot of the new context menu opened with a specified tab context. There is no default tab context menu commands, and there are only addon's commands.)

Default commands for the context are missing - is that a bug? No, it is by design. The author of the patch told:

Including Firefox's default menu items is out of scope, because the default menu labels don't always make sense. For example, the "Close Tabs to the Right" menu item makes no sense in a vertical tabs-type extension.

If there are menu items that cannot be replicated with the extension APIs, then we can decide on a case-by-case basis for how this should be supported.

Adding menu items with the viewTypes parameter

So you need to implement imitated commands by yourself, if you hope to make the context menu compatible to Firefox's one.

As you know, multiple extra commands added by browser.menus.create() are grouped under a sub menu. But here is an exception - the addon who called browser.menus.overrideContext() can put top-level commands as ungrouped. Thus you can define imitated default commands without any worrying.

And, a new parameter viewTypes for browser.menus.create() will help you to add such commands only for a context menu on a popup panel or a sidebar panel.

browser.menus.create({
  id:        'context_reloadTab',
  title:     browser.i18n.getMessage('context_reloadTab_title'),
  type:      'normal',
  contexts:  ['tab'],
  viewTypes: ['sidebar'] // Important!!
});

viewTypes is an array of these possible values:

  • popup: a context menu shown on a popup panel
  • sidebar: a context menu shown on the sidebar area
  • tab: a context menu shown on the tab bar

If you specify both contexts and viewTypes, the item will become visible only when both conditions are satisfied.

Most default tab commands are imitable based on WebExtensions APIs. I think that complete imitation of default commands is painful, but finally I did that...

(A screenshot of new context menu on the sidebar. The context menu has default tab commands imitated by Tree Style Tab itself.)

Here are some more knowledge:

Note that both viewTypes andvisible are available only on Firefox 64 or later. If you want to keep your addon compatible to Firefox 63 or older, you need to skip operations to register such commands on old Firefox. For example:

let useNativeMenu = false;
(async () => {
  const browserInfo = await browser.runtime.getBrowserInfo();
  useNativeMenu = parseInt(browserInfo.version.split('.')[0]) >= 64;
  if (useNativeMenu) {
    // codes to create context menu items with `viewTypes` or `visible`
  }
})();

or, simply:

let useNativeMenu = typeof browser.menus.overrideContext == 'function';
if (useNativeMenu) {
  // codes to create context menu items with `viewTypes` or `visible`
}

Conclusion

Things become possible on Firefox 64:

  • Opening custom context menu including extra commands added by other addons. Implicitly collaboration of addons is partially back!
  • The addon calling browser.menus.overrideContext() can put multiple commands at the top-level of the context menu. (They won't be grouped automatically.)

Things still impossible:

  • Opening native context menu on demand - for example mouseover. This new way is available only on cases the contextmenu event is fired.
  • Controlling or overriding behaviors of extra commands added by other addons. You still need to do special collaboration via message based custom APIs.
  • Quoting default context menu commands of Firefox itself. You need to re-implement and imitate them by self.

On the migration from XUL to WebExtensions of Firefox itself, many advantage were lost and people thought that Firefox became same to Chrome. However, now Firefox get back more customizability step by step. I think you should pay attention on Firefox's future improvements around new WebExtensions APIs - Firefox sometimes try to do unique thing like above.

分類:Mozilla > XUL, , , , , 時刻:06:10 | Comments/Trackbacks (0) | Edit

Comments/Trackbacks

TrackBack ping me at


の末尾に2018年6月21日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2018-10-14_override-context-on-fx64.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。

Post a comment

writeback message: Ready to post a comment.

2018年6月21日時点の日本の首相のファミリーネーム(ひらがなで回答)

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のつぶやき