Search code examples
javascriptfirefoxgreasemonkeydomdocumentuserscripts

Injecting a document.write call from a Greasemonkey script causes nothing to load


The following injection works perfectly in Chrome, however in the latest Firefox (version 21), the documents stays "Connecting..." forever and not even its elements like <img> loads. (But if you click reload on them they show up fine...)

Screenshot:
Screenshot of problem

Script:

// ==UserScript==
// @name            Test
// @namespace       Test
// @author          Owyn
// @match           http://*
// ==/UserScript==

function inject(func) {
    var source = func.toString();
    var script = document.createElement('script');
    script.innerHTML = "("+ source +")()";
    document.body.appendChild(script);
}

function myf() {
  document.write('<html>TEST</html>');
}

inject(myf);

Any fixes, any workarounds? Should I contact Mozilla about a bug?

P.S. - I use document.write to remove all event listeners (unknown to me) and found no alternative to it yet.


Solution

  • The smartest/best(est) thing to do is to avoid document.write(). Use DOM techniques. Here's a complete script:

    // ==UserScript==
    // @name     _Replace a target page using DOM techniques
    // @include  http://YOUR_SERVER.COM/YOUR_PATH/*
    // @grant    GM_addStyle
    // ==/UserScript==
    /*- The @grant directive is needed to work around a design change
        introduced in GM 1.0.   It restores the sandbox.
    */
    var D       = document;
    var newDoc  = D.implementation.createHTMLDocument ("");
    
    D.replaceChild (
        D.importNode (newDoc.documentElement, true),
        D.documentElement
    );
    
    D.body.innerHTML = '<h1>TEST</h1>'
    



    Greasemonkey normally fires on DOMContentLoaded, not when the page has fully completed loading. Chrome fires later than that, by default.

    Anyway, at DOMContentLoaded, in Firefox; something is getting orphaned when you trash the current document. Hence the "throbber" is never stopped and some other cleanup is probably missed.

    This is probably a bug, but you shouldn't use document.write() anyway. Also, per the DOC, you should call document.close()

    A dirty workaround is to wait until the page has loaded before firing. So, instead of calling inject(myf); directly, call it like this:

    function myf () {
      document.write ('<html>TEST</html>');
      document.close ();
    }
    
    window.addEventListener ("load", function () { inject(myf); }, false);
    


    but use DOM methods instead! They'll be faster, and won't trip edge-case bugs and security violations.