Search code examples
iframefirefox-addonxulonloaddomcontentloaded

Load multiple pages in a hidden iframe from a xul-based firefox extension


From a xul-based firefox addon, I need to:

  1. programmatically create an invisible iframe (once)
  2. reuse it to load multiple URLs as the addon runs
  3. access the returned HTML after each URL loads

Problem: I can only get the first page-load for any created iframe to trigger an 'onload' or 'DOMContentLoaded' event. For subsequent URLs, there is no event triggered.

Note: I'm also fine with using the hiddenDOMWindow itself if this is possible...

Code:

var urls = ['http://en.wikipedia.org/wiki/Internet', 'http://en.wikipedia.org/wiki/IPv4', 'http://en.wikipedia.org/wiki/Multicast' ];

visitPage(urls.pop());

function visitPage(url) {

    var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

    var hiddenWindow = Components.classes["@mozilla.org/appshell/appShellService;1"].getService
        (Components.interfaces.nsIAppShellService).hiddenDOMWindow;

    var doc = hiddenWindow.document, iframe = doc.getElementById("my-iframe");

    if (!iframe) 
    {
        iframe = doc.createElement("iframe");
        //OR: iframe = doc.createElementNS(XUL_NS,"iframe");

        iframe.setAttribute("id", "my-iframe");
        iframe.setAttribute('style', 'display: none');

        iframe.addEventListener("DOMContentLoaded", function (e) { 
            dump('DOMContentLoaded: '+e.originalTarget.location.href);
            visitPage(urls.pop()); 
        });

        doc.documentElement.appendChild(iframe);
    }

    iframe.src = url;    
}

Solution

  • There are some traps:

    • The hiddenWindow differs between platforms. It is XUL on Mac, and HTML else.
    • You should use .setAttribute("src", url); to reliably navigate.

    The following works for me (Mac, Win7):

    var urls = [
        'http://en.wikipedia.org/wiki/Internet',
        'http://en.wikipedia.org/wiki/IPv4',
        'http://en.wikipedia.org/wiki/Multicast'
    ];
    
    var hiddenWindow = Components.classes["@mozilla.org/appshell/appShellService;1"].
                       getService(Components.interfaces.nsIAppShellService).
                       hiddenDOMWindow;
    
    function visitPage(url) {
        var iframe = hiddenWindow.document.getElementById("my-iframe");
        if (!iframe) {
            // Always use html. The hidden window might be XUL (Mac)
            // or just html (other platforms).
            iframe = hiddenWindow.document.
                     createElementNS("http://www.w3.org/1999/xhtml", "iframe");
            iframe.setAttribute("id", "my-iframe");
            iframe.addEventListener("DOMContentLoaded", function (e) {
                console.log("DOMContentLoaded: " +
                            e.originalTarget.location);
                var u = urls.pop();
                // Make sure there actually was something left to load.
                if (u) {
                    visitPage(u);
                }
            });
            hiddenWindow.document.documentElement.appendChild(iframe);
        }
        // Use .setAttribute() to reliably navigate the iframe.
        iframe.setAttribute("src", url);
    }
    
    visitPage(urls.pop());
    

    Don't reload the hiddenWindow itself, or you will break lots of other code.