Search code examples
javascriptfirefox-addonfirefox-addon-restartless

Utilizing Firefox's default/built-in Event Listeners


I have a context menuitem which is activated if an image is right-clicked, the exact same way that 'context-copyimage' is activated.

Is it possible to tie/pair that menuitem to the 'context-copyimage' therefore eliminating the need to add extra (duplicate) event-listeners and show/hide handlers??!!
(Adding an observer to 'context-copyimage' defeats the purpose)

If not, is it possible to use the event-listener that 'context-copyimage' uses?

Update:
I am trying to reduce listeners. At the moment, script has a popupshowing listeners. On popupshowing, it checks for gContextMenu.onImag and if true, it shows the menuitem. Firefox's context-copyimage does the exact same thing. I was wondering if it was possible to tie these 2 in order to remove/reduce the in-script event listeners.

I was also chatting with Dagger and he said that:

... the state of built-in items isn't set from an event handler, it's set from the constructor for nsContextMenu, and there are no mechanisms to hook into it

So it seems, that is not possible


Solution

  • No, there is no sane way of avoiding the event listener that would perform better than another event listener and is compatible with unloading the add-on in session.

    Hooking nsContextMenu

    As you have been already told, the state is initialized via gContextMenu = new nsContextMenu(...). So you'd need to hook the stuff, which is actually quite easy.

    var newProto = Object.create(nsContextMenu.prototype);
    newProto.initMenuOriginal = nsContextMenu.prototype.initMenu;
    newProto.initMenu = function() {
        let rv = this.initMenuOriginal.apply(this, arguments);
        console.log("ctx", this.onImage, this); // Or whatever code you'd like to run.
        return rv;
    };
    nsContextMenu.prototype = newProto;
    

    Now, the first question is: Does it actually perform better? After all this just introduced another link in the prototype-chain. Of course, one could avoid Object.create and just override nsContextMenu.prototype.initMenu directly.

    But the real question is: How would one remove the hook again? Answer: you really cannot, as other add-ons might have hooked the same thing after you and unhooking would also unhook the other add-ons. But you need to get rid of the reference, or else the add-on will leak memory when disabled/uninstalled. Well, you could fight with Components.utils.makeObjectPropsNormal, but that doesn't really help with closed-over variables. So lets avoid closures... Hmm... You'd need some kind of messaging, e.g. event listeners or observers... and we're back to square one.

    Also I wouldn't call this sane compared to

    document.getElementById("contentAreaContextMenu").addEventListener(...)
    

    I'd call it "overkill for no measurable benefit".

    Overriding onpopupshowing=

    One could override the <menupopup onpopupshowing=. Yeah, that might fly... Except that other add-ons might have the same idea, so welcome to compatibility hell. Also this again involves pushing stuff into the window, which causes cross-compartment wrappers, which makes things error-prone again.

    Is this a solution? Maybe, but not a sane one.

    What else?

    Not much, really.