0

I'm developping a Firefox based on jQuery as described in this Answer here.

After implementing the example provided in the answer, eveything works fine, but the problem is the code between Firefox Tabs is somehow linked, and example.doc always refers to the last opened tab.

  • Opened tab1 : the plugin-example has been added and to the current page.
  • this.doc refers to tab1.
  • Oepened tab2: the plugin-example has been added to to current page (tab2).
  • this.doc now refers to tab2
  • back to viewing tab1 : this.doc still refers to tab1.
  • clicking on plugin-example on tab1 will act on the plugin-example in tab2 instead.

How can I make my code independent between tabs?

Here is an excrept from the code:

(function() {
    jQuery.noConflict();
    $ = function(selector,context) { 
        return new jQuery.fn.init(selector,context||example.doc); 
    };
    $.fn = $.prototype = jQuery.fn;

    example = new function(){};

    example.run = function(doc,aEvent) {
        if (doc.getElementById("plugin-example")) return;
        this.doc = doc;
        this.main = main = $('<div id="plugin-example">').appendTo(doc.body).html('Example Loaded!');
        this.main.click(function() { //<--- added this function
                example.main.html(example.doc.location.href);
        });
        main.css({ 
            background:'#FFF',color:'#000',position:'absolute',top:0,left:0,padding:8
        });
    };
    // Bind Plugin
    var delay = function(aEvent) { 
        var doc = aEvent.originalTarget; setTimeout(function() { 
            example.run(doc,aEvent); 
        }, 1); 
     };
    var load = function() { 
        gBrowser.addEventListener("DOMContentLoaded", delay, true); 
    };
    window.addEventListener("pageshow", load, false);

})();
Community
  • 1
  • 1
Majid Laissi
  • 19,188
  • 19
  • 68
  • 105

1 Answers1

2

Your code (overlay script) will only run once per window, not once per tab. So there is only one example instance per window. And hence example.doc will be set to whatever dispatched DOMContentLoaded last.

Your function should properly close over the document and avoid global state. This is who I would write it (then again, I would avoid jquery (in add-ons) like the plague...)

// Use strict mode in particular to avoid implicitly var declarations
(function() {
  "use strict";

  // Main runner function for each content window.
  // Similar to SDK page-mod, but without the security boundaries.
  function run(window, document) {
    // jquery setup. per https://stackoverflow.com/a/496970/484441
    $ = function(selector,context) {
      return new jq.fn.init(selector,context || document); 
    };
    $.fn = $.prototype = jq.fn;

    if (document.getElementById("my-example-addon-container"))  {
      return;
    }
    let main = $('<div id="my-example-addon-container">');
    main.appendTo(document.body).text('Example Loaded!');
    main.click(function() { //<--- added this function
      main.text(document.location.href);
    });
    main.css({
      background:'#FFF',color:'#000',position:'absolute',top:0,left:0,padding:8
    });
  };

  const log = Components.utils.reportError.bind(Components.utils);

  // Do not conflict with other add-ons using jquery.
  const jq = jQuery.noConflict(true);

  gBrowser.addEventListener("DOMContentLoaded", function load(evt) {
    try {
      // Call run with this == window ;)
      let doc = evt.target.ownerDocument || evt.target;
      if (!doc.location.href.startsWith("http")) {
        // Do not even attempt to interact with non-http(s)? sites.
        return;
      }
      run.call(doc.defaultView, doc.defaultView, doc);
    }
    catch (ex) {
      log(ex);
    }
  }, true);
})();

Here is a complete add-on as a gist. Just drop in a copy of jquery and it should be good to go.

PS: Reposted this at in the jquery in extensions question

Community
  • 1
  • 1
nmaier
  • 32,336
  • 5
  • 63
  • 78
  • Would it be more robust to check if the target document is `instanceof HTMLDocument`? Checking just if it has been served over `http(s)` will also catch other types of documents (e.g. Image, SVG). Of course under circumstances this might desirable, but most probably not. – paa May 22 '14 at 11:09
  • @paa Well, there might be also `chrome://` `HTMLDocument`s (which might be privileged), so at the very least I'd filter protocols. "Flat" images loaded in a tab also have an HTML container FWIW, and IIRC some other stuff as well... Also `DOMContentLoaded` should only fire if there is actually some kind of DOM. But ultimatively yeah, `instanceof HTMLDocument` might be a good *additional* check. – nmaier May 22 '14 at 16:42