Search code examples
javascriptfirefoxfirefox-addonhttprequest

How to identify requests originating from the `hiddenDOMWindow`?


What event-listeners can I use to identify requests originating from the hiddenDOMWindow (or an iframe within it) in a firefox-addon? I need to do this BEFORE the request has been sent, in the "http-on-modify-request" event, for example.

What I've tried:

  • register for the global "http-on-modify-request"; but I can't distinguish the source window
  • add listener to the hiddenDOMWindow itself; but I can't find any before-load-event
  • add listener to the hiddenDOMWindow.document; no before-load-event
  • add listener to the created hiddenDOMWindow.document.iframe; no before-load-event

Solution

  • First, you need to get a DOMWindow from an nsIChannel:

    function getDOMWindowFromChannel(ch) {
        var wp;
        try {
            if (ch.loadGroup && ch.loadGroup.groupObserver) {
                wp = ch.loadGroup.groupObserver.
                     QueryInterface(Ci.nsIWebProgress);
            }
        } catch (ex) {}
        try {
            if (!wp) {
                wp = ch.notificationCallbacks.
                     getInterface(Ci.nsIWebProgress);
            }
        }
        catch (ex) {}
        try {
            if (wp) {
                return wp.DOMWindow || null;
            }
        }
        catch (ex) {}
        return null;
    }
    

    Now that you got a DOMWindow, you need to find the top level window for that DOMWindow, which is not really intuitive:

    function getToplevelWindow(win) {
        try {
            return win.QueryInterface(Ci.nsIInterfaceRequestor).
                   getInterface(Ci.nsIWebNavigation).
                   QueryInterface(Ci.nsIDocShell).
                   treeOwner.
                   QueryInterface(Ci.nsIInterfaceRequestor).
                   getInterface(Ci.nsIXULWindow).
                   docShell.
                   contentViewer.DOMDocument.defaultView;
        }
        catch (ex) {
            // Likely already a top-level window.
            return win;
        }
    }
    

    Now, lets craft and install the observer, bringing all together:

    function observe(channel, topic, data) {
    
        if (!(channel instanceof Ci.nsIChannel)) {
            return;
        }
        var win = getDOMWindowFromChannel(channel);
        if (!win) {
            return;
        }
        var topWin = getToplevelWindow(win);
        if (topWin.location.href.indexOf("chrome://browser/content/hiddenWindow") != 0) {
            return;
        }
        // do stuff, e.g.
        console.log(topWin.location.href);
    }
    
    Services.obs.addObserver(observe, "http-on-modify-request", false);
    

    It should be noted that not all requests are nsIChannel and not all nsIChannel actually have a DOMWindow or real loadGroup associated (e.g. background requests), hence all those try catch blocks.

    Also, don't forget to remove the observer again at some point, which I skipped. ;)

    And lastly, here is some code to actually test this (I ran the whole thing as a Scratchpad on an about:newtab tab, which happens to have chrome privileges just like add-ons):

    var hw = Services.appShell.hiddenDOMWindow;
    var iframe = hw.document.createElement("iframe");
    hw.document.documentElement.appendChild(iframe);
    var r = iframe.contentWindow.XMLHttpRequest();
    r.open("GET", "http://example.org/");
    r.send();