Sunday, February 1, 2009

Problems with the Mozilla Component.utils.Sandbox

Writing a Firefox extension can be time consuming for the first time, the documentation is more reference oriented, more oriented to C++ than the JavaScript version, but it is the worst when there are bugs and you waste hours of work. That is what happened to me with the Sandbox APIs of Firefox 3. After talking to members of the FireBug extension on its Google groups forum, I found out that I am not alone with Sandbox frustrations.

The Sandbox

The sandbox, introduced in full in FF3, is a container that allows extension developers to run code in lower security. This is helpful for interacting with browser web pages or for running code from a server. The Sandbox API Documentation is pretty light, and gives no useful examples for real-world applications. The primary use of the sandbox is probably to work with pages, but they show no example on how to do that.

My Problem

When I was working on an extension I write for work, I found that I had some extremely peculiar results. As an illustration, lets say there are two types of JavaScript objects in a page that you want to work with. The first, is a single instance object that looks up other JS objects, and the second, a 'normal' JS object that we want to create. So for my example, lets say we want to lookup an object, pass that object into a constructor and then run a method on that new object:

  var obj = TheRegistry.lookupObject('customId1');
  var ref = new ObjectReference(obj);
  ref.save();

Now, I want to run this code in my extension, so I setup the sandbox and execute the code like this:

  var evalInSandbox = function(win, script, vars)
  {
    win = getUnwrapped(win);
    var sandbox = new Components.utils.Sandbox(win);
    sandbox.window = win;
    sandbox.document = sandbox.window.document;
    sandbox.XPathResult = Components.interfaces.nsIDOMXPathResult;
    sandbox.__proto__ = win;
    if (vars)
    {
      for (var prop in vars)
      {
        sandbox[prop] = vars[prop];
      }
    }
    return Components.utils.evalInSandbox(
      "(function() { " + script + "\n })();", sandbox);
  };
  
  var getUnwrapped = function(obj)
  {
    while (obj.wrappedJSObject)
    {
      obj = obj.wrappedJSObject;
    }
    return obj;
  };
  
  var doSomething = function(window)
  {
    evalInSandbox(window,
      "var obj = TheRegistry.lookupObject('customId1');"+
      "var ref = new ObjectReference(obj);"+
      "ref.save();");
  }

What I was surprised to find out is that ref had no 'save' method, but I knew that was not the case. Looking further into it, ref == obj. What the heck?! I tried wrapping the code with with (window) { ... }, using new window.ObjectReference(obj), but nothing would work. Finally I gave up on the code and used a FireBug technique to inject a script tag into the page's DOM.

Wrap Up

I wrote this article, not to teach, but hopefully to save other people some time so if the google 'mozilla sandbox bugs' or something similar, they may find this. Hopefully Mozilla will fix the sandbox and improve its documentation, but until then, watch out for bugs, and stay away from creating page objects using the 'new' keyword inside of the sandbox, it just doesn't work.

BTW, the google group discussion I mentioned can be found here.

1 comment:

bakey said...

thank you for your share.I am looking for the document of the sandbox.but the data is extraordinary few.