Blog Archives

Inserting JavaScript/CSS in a target page from a Firefox Extension

When you’re working with Firefox extensions you often want to act on the target page, accessing elements or even changing HTML, but JavaScript libraries and jQuery plugins assume that they are always working on the current scope’s global objects.

So every time you find yourself wanting to act on variables like window, document and jQuery you run into a problem.  You could insert the module into the target window using a script tag, but that may conflict with the target window’s JS.

A better solution is to load another instance of these libraries in your own scope tied to the target window’s global variables. One approach is to eval the window inside a context that has remapped the window and document element.

The following code demonstrates this given a passed target window.


createTargetScripts: function(targetWindow) {
   // Create the scripts we will use on the target window, we create them in a closure so they will
   // have the target window and document (but our privilege, and the target objects they use will actually be wrappers).

   (function() {
      var window = targetWindow;
      var document = targetWindow.document;
      eval(evalStringForURL('libs/jquery.js'));
      var jQuery = targetWindow.jQuery;          // Add jQuery to local scope for jQuery plugins
      eval(evalStringForURL('libs/jquery.tmpl-master0405.js'));
      eval(evalStringForURL('libs/jquery.qtip.js'));

   })();

   // Even though the jQuery is on the wrapper, that wrapper may be used by other extensions
   targetWindow.targetJQ = targetWindow.jQuery.noConflict(true);

   } ,
   readURLSync: function(url) {
      var data = myExtensionJQ.ajax({
         url: url,
         async: false,
         dataType: "text",
         mimeType: 'text/plain; charset=x-user-defined'
         }).responseText;
      return data;
   },

   // Add sourceURL so debuggers will know what source file to use
   evalStringForURL: function(url) {
      var evalStr = this.readURLSync(url);
      var sourceURLStr = "//@ sourceURL=" + url;
      return evalStr + sourceURLStr;
   },

For CSS you can do even better. Instead of inserting the CSS into the target page you can load it as a “user sheet,” making it available in all pages. If you want to override existing styles in the target page you just use a “!” in your style. Here is the code:


var sss = Components.classes["@mozilla.org/content/style-sheet-service;1"]
   .getService(Components.interfaces.nsIStyleSheetService);
var ios = Components.classes["@mozilla.org/network/io-service;1"]
   .getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(url, null, null);
   sss.loadAndRegisterSheet(uri, sss.USER_SHEET);

FYI, Another Firefox-specific approach for loading the JS libraries with a different context is to use loadSubScript, but a bug in Firefox 4 broke this. I’ve documented the bug at https://bugzilla.mozilla.org/show_bug.cgi?id=653926. Unfortunately, you can’t actually view the bug report – it was restricted because it exposes a potential Firefox security vulnerability.

Update 6/15/11

Apparently the bug I mentioned above exposes a severe security vulnerability since I was awarded a Firefox Security Bug Bounty Award. See [post: Firefox Security Bug Bounty…].

Advertisement

Adding a little jQuery/Sizzle to FirePath for Firebug

FirePath is a very cool Firebug plugin that adds the ability to search the HTML tree using XPath or CSS Selectors. It filters the actual Firebug DOM tree so you can then act on that element in all the normal Firebug ways (view DOM, inspect, etc).

Here is a screenshot:

Unfortunately FirePath only supports native CSS Selectors, and most of the world (including me) actually uses jQuery’s Sizzle which gives us some really key additions like :not, :contains, and :first.

So I did a little extension hacking and added Sizzle support to FirePath. You can find the patch for it where I submitted it to the FirePath author at http://code.google.com/p/firepath/issues/detail?id=22.

Update 3/22/2011

I guess I’m not the only one who wanted to use jQuery/Sizzle. The patch was accepted and FirePath with jQuery/Sizzle is now available in Version 0.9.6.1!

%d bloggers like this: