Context menu for canvas

canvas
menu

(Michal Stanke) #1

Hi.

Is there a way to add context menu items to canvas, like you can for images with https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/menus/create?


(Niklas Gollenstede) #2

Nope. But you could use


#3

It’s going to be removed though…
1372276 - Remove HTML context menu (<menu> and <menuitem> tag) support


(Niklas Gollenstede) #4

Wow. I guess you can reduce my previous answer to it’s first two letters, then.


#5

I think that you can enable your menu only on the canvas element by doing as follows. (I did not test it though…)
Note that even if it is not over the canvas, the menu will always be displayed as disabled (grayed out).

/**
 * background script
 */
const {menus, runtime} = browser;

runtime.onMessage.addListener(msg => {
  let func;
  const [item] = Object.keys(msg);
  if (item === 'contextMenu') {
    const {enabled} = item;
    const menuItemId = 'yourMenuItemId';
    func = menus.update(menuItemId, {enabled}).catch(e => {
      throw e;
    });
  }
  return func || null;
});

document.addEventListener('DOMContentLoaded', () => {
  const id = 'yourMenuItemId';
  const contexts = ['all'];
  const enabled = false;
  const title = 'Your Menu Item Title';
  return menus.create({
    id, contexts, enabled, title,
  }).catch(e => {
    throw e;
  });
}, false);
/**
 * content script
 */
'use strict';
const {runtime} = browser;

const handleBeforeContextMenu = evt => {
  let func;
  const {button, key, shiftKey, target} = evt;
  if (button === 2 || key === 'ContextMenu' || shiftKey && key === 'F10') {
    const {localName} = target;
    const enabled = localName === 'canvas';
    func = runtime.sendMessage({
      contextMenu: {
        enabled,
      },
    }).catch(e => {
      throw e;
    });
  }
  return func || null;
};

document.addEventListener('DOMContentLoaded', () => {
  const root = document.documentElement;
  root.addEventListener('mousedown', handleBeforeContextMenu, true);
  root.addEventListener('keydown', evt => {
    let func;
    if (evt.getModifierState('Shift') && evt.key === 'F10' ||
        evt.key === 'ContextMenu') {
      func = handleBeforeContextMenu(evt);
    }
    return func || null;
  }, true);
}, false);

(Niklas Gollenstede) #6

Does Firefox update the enabled state of items in the open menu? If yes, this would be a fine solution, otherwise, you would have to hope that your (twice) asynchronous mousedown event handler works faster than the opening of the context menu, which happens on mousedown on macOS and (some?) Linux systems.

And of course you would need to consider iframes.

But, as I said, this could be a good solution.


#7

Does Firefox update the enabled state of items in the open menu?

No, it’s just a temporary workaround until 1215376 - Implement onBeforeShow event get fixed.
But it works well in most cases.