WebExtensions dynamic context menu

(kiboke) #1


I plan to switch my addon to WebExtensions but I have an issue with context menu: I can’t change the context menu real-time. The way I try to do it is:

I listen to the right click event and send a message with chrome.runtime.sendMessage which triggers context menu update. Everything is async so this update happens a bit after the context menu is opened so, to see it updated, I need to close the context menu and open it again.

The same thing happens in chrome and opera, of course. Can someone tell me how to make it work properly?

(erosman) #2

There is a talk about an onBeforeShow event, which would have been good for this circumstance, but it is P5 and it is not gong to be available soon.



For the workaround, see also Change label in the contextmenu before the opening

(kiboke) #4

This solution doesn’t work.

Context menu update is triggered separately from menu opening and runs too late. I have to close the context menu and reopen it to see the effect of the previous context menu update.

To make the problem worse, thanks to this delay, I see the result of the context menu update of one tab in ALL tabs, until I reopen the context menu.


If possible, could you show me the link to your code?

(kiboke) #6

Can you download my extension from chrome web store, unpack it and add to firefox? I’d be happy to guide you to this issue, which persists in all browsers.

document.addEventListener('mousedown', function(e) {
	if (e.button != 2)
	var a = document.createElement('a');
	a.href = document.location.href;
	const time = window.performance.now();
	  hostname:`${a.hostname.replace(/^w{2,3}\d*\./i, '')} (${time})`,

I’ve added time to your script for checking.
Seems working.

Add listener to window instead of document, and set 3rd argument (useCapture) to true will be better, I suppose.

window.addEventListener("mousedown", evt => {
}, true);

You should also listen to keydown event.


Off topic.
You don’t have to create anchor element just for getting hostname.

  const {hostname} = new URL(window.location.href);

will do.

(kiboke) #9

Thans for the anchor element tip! This is my code, the problem persists (first content menu opening, nothing happens, the default text is being displayed “Extension doesn’t work on this domain”, the second opening, everything ok. If I don’t open the second time and go to other tab and then open the context menu, it will display the previously set text from the other tab):

function updateMessage()
	const {hostname} = new URL(window.location.href);
	chrome.runtime.sendMessage({request:'updateToggleMenu', hostname:hostname.replace(/^w{2,3}\d*\./i, '')});

window.addEventListener("mousedown", evt => {
	if (evt.button === 2)
}, true);

window.addEventListener("keydown", evt => {
	if (evt.shiftKey && evt.key === "F10" || evt.key === "ContextMenu")
}, true);


Maybe iframe used?

If so, add below in manifest.json, and see if something gets better (or worse).

"content_scripts": [
    "all_frames": true

(kiboke) #11

Nothing has changed. The context menu with an old value is being displayed before the value gets updated.

P.S. in chrome it works fine now so i guess it’s a performance issue. I have FF 55.0.2. I have aslo tried ff with addons disabled in safe mode, hoping that it will run faster but it doesn’t help.


If the hostname is the only matter, how about updating the menu item when the active tab changes?
You don’t need to listen in content script then.


tabs.onActivated.addListener(async info => {
  const {tabId, windowId} = info;
  const tab = await tabs.getCurrent();
  const {id, windowId: currentWindowId, url} = tab;
  if (tabId === id && windowId === currentWindowId && url) {
    const {hostname} = new URL(url);
  } else {
    // default menu item

ref tabs.onActivated - Mozilla | MDN
ref tabs.getCurrent() - Mozilla | MDN

(kiboke) #13

This doesn’t help because the hostname may change many times after onActivated is triggered. I can’t believe nobody run into this issue before :slight_smile:


Then add listener to tabs.onUpdated too?

tabs.onUpdated.addListener(async (tabId, info, tab) => {
  const {active, status, windowId, url} = tab;
  if (active && status === "complete") {
    const {id: currentWindowId} = await windows.getCurrent();
    if (windowId === currentWindowId) {
      const {hostname} = ...

(kiboke) #15

It worked, although not with async/await, I had to do it the old way, and I’ve also optimized the code so it doesn’t have to run anything and slow down the affected events.

Thank you very much for your time and help, asamuzaK!