宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
(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.
In old days, all XUL addons worked in the common namespace and they modified UI elements and behaviors of Firefox itself as they like.
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.
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.
On the other hand, WebExtensions APIs are designed to separate namespace of each addon.
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.
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.
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!
How can addons do such an collaboration? Let's see basics of custom context menu on WebExtensions addons.
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.
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.
It is callable on event handlers for the contextmenu
DOM events. You'll call it with suitable context information -
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!
There are three requirements to try that.
menus.overrideContext
browser.menus.overrideContext()
viewTypes
parameter by browser.menus.create()
There is another article to describe how to implement context menu items for various cases. In this article I describe just general information.
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.
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 && typeof browser.menus.overrideContext == 'function') {
// 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 && typeof browser.menus.overrideContext == 'function') {
// 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.
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.
viewTypes
parameterSo you need to implement imitated commands by yourself, if you hope to make the context menu compatible to Firefox's one. (Moreover, you can also add any other top-level custom items to the menu as you like.)
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 items and/or other top-level custom items 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!!
documentUrlPatterns: [`moz-extension://${location.host}/*`] // Important!!
});
viewTypes
is an array of these possible values:
popup
: a context menu shown on a popup panelsidebar
: a context menu shown on the sidebar areatab
: a context menu shown Please note that there is no value for "the context menu on the tab bar". If you specify viewTypes:["tab","sidebar"]
to show the item in the context menu on the tab bar and the sidebar, and hide on popup panels, actually it will be shown in the context menu on the content area and the sidebar, and hidden on popup panels and the tab bar. On such cases you need to omit viewTypes
and control visibility of the item by a listener for browser.menus.onShown
, based on info.viewType
given to the listener. You can show/hide menu item by browser.menus.update()
with its visible
option.
If you specify both contexts
and viewTypes
, the item will become visible only when both conditions are satisfied.
The documentUrlPatterns
parameter is required to hide your custom top-level items on any sidebar or popup panel provided by other addons. By the bug 1498896 documentUrlPatterns
is effective to control visibility of the item based on the URL of the sidebar/popup panel itself.
Do you want to show an item on the menu except in your sidebar panel and popup panel? For example, the Multiple Tab Handler addon does that: commands for selected tabs are shown as top-level items on its popup panel, otherwise those commands are grouped under a "Selected Tabs" submenu. If you don't mind the submenu has a label same to the name of the addon, you don't think seriously - you just define all items without viewTypes
option. However, if you want to set a label different to the addon's name to the submenu, you need to define top-level items and submenu items separately and control visibility of them.
Items visible only on your sidebar or popup panel are easily definable with the viewTypes
and documentUrlPatterns
options. On the other hand, items invisible on your sidebar or popup panel is hard a little. To do that you need to update visibility of such items dynamically via the visible
option for browser.menus.update()
. How to do that? MTH does by this commit, key points are:
Add codes to track the state of your panel: opened or closed. On the background side:
let gIsPanelOpen = false;
browser.runtime.onConnect.addListener(port => {
if (!/^connection-from-my-panel:/.test(port.name))
return;
gIsPanelOpen = true;
port.onDisconnect.addListener(() => {
gIsPanelOpen = false;
});
});
On the panel side:
browser.runtime.connect({
// the connection name must be unique!
name: `connection-from-my-panel:${Date.now()}`
});
Add codes to update visibility of items to the background side:
if (typeof browser.menus.overrideContext == 'function') {
let gLastVisible = true;
browser.menus.onShown.addListener(async () => {
const visible = !gIsPanelOpen;
if (gLastVisible == visible)
return;
await browser.menus.update('id-of-the-item-you-want-to-hide-in-your-panel', { visible });
gLastVisible = visible;
browser.menus.refresh();
});
}
Note that both viewTypes
and visible
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/update such commands on old Firefox when typeof browser.menus.overrideContext == 'function'
equals to false
, like these snippets.
Most default tab context menu commands are imitable based on WebExtensions APIs. I think that complete imitation of default commands is painful, but finally I did that...
You need to prepare colorized SVG icons for containers and pack them into the XPI package by yourself. Sadly there is no way to quote Firefox's built-in SVG icons directly as colorized versions to your imitated menu items, due to restrictions from security reasons.
And, one more sad thing: "Send Tab to Device" is still not imitable for now, due to missing WebExtensions APIs.
Things become possible on Firefox 64:
browser.menus.overrideContext()
can put multiple commands at the top-level of the context menu.
(They won't be grouped automatically.)Things still impossible:
mouseover
. This new way is available only on cases the contextmenu
event is fired.If you want to know what you should do on real cases in the world, see another article describing about various patterns of custom menus.
On the migration from XUL to WebExtensions of Firefox's addon system 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.
の末尾に2020年11月30日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2018-10-14_override-context-on-fx64.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。
writeback message: Ready to post a comment.