Categories: developers jetpack

Panel anchoring follow-up.

Last week I blogged about how to use the Add-on SDK’s high-level api to display a panel anchored to a widget. I also talked about how the panel implementation has some shortcomings, in particular that it is not currently possible to anchor a panel to a content node using only the high-level apis in add-on-kit.

This is all true, but I thought it might be useful fun to show you how to do it anyway. Here is an example extension I wrote using Builder that does implement anchoring a panel to a content DOM node:

https://builder.addons.mozilla.org/addon/1013359/revision/33/

The key part of this implementation that makes it work is the function “getWindowNode”:

var {Cc,Ci} = require("chrome");
function getWindowNode(selector) {
    try {
        var dom = Cc['@mozilla.org/appshell/window-mediator;1']
            .getService(Ci.nsIWindowMediator)
            .getMostRecentWindow('navigator:browser')
            .gBrowser.contentWindow.document;
       
        return dom.querySelector(selector);

    } catch(e) {
        console.log(e);
    }
}

The function takes a selector string and returns a handle to the first matching dom node in the current tab. You can then pass the dom handle to panel.show(anchor) and the panel is correctly anchored visually to the node in the page. If you run the extension in Builder you’ll see the panel is hung directly below the ‘mozilla’ logo image:

That’s great, right? This code happens to work, but unfortunately there is no way to guarantee that it will continue to work in the future. In particular the hacky way in which I have accessed the current Tab’s DOM will likely break once Firefox switches to the multi-process model called ‘Electrolysis‘.

Now, That doesn’t mean you should never require chrome in your add-on code, in fact I expect many experienced add-on developers will need access to lower-level apis to write more complex add-ons. What this does mean is that you should be aware that the parts of your code that use lower-level apis are inherently brittle compared to those provided by addon-kit. I personally am considering a standard snippet like this:

/** 
 * XXX WARNING!! Any use of Cc and Ci may break later! 
 * If you have a bug in a new version of Firefox, check this first!! 
 * Here be Dragons, etc. 
 */
var {Cc,Ci} = require("chrome");
// awesome low-level hacking ensues...

The best practice is to break out any code that uses lower-level apis into a separate module with its own set of unit tests. That way when you test your add-on against Beta or Aurora builds, you’ll see problems right away.