1

From reading examples, this seems like it should be quite easy. Here's my code:

rhkTest = {
  onPageLoad: function(event) {
    var doc = event.originalTarget;
    var wnd = doc.defaultView;
    var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
      .getService(Components.interfaces.mozIJSSubScriptLoader);
    loader.loadSubScript("chrome://rhkoshi-extension/content/testfoo.js", wnd);
    alert('typeof(wnd) = ' + typeof(wnd));
    alert('typeof(wnd.window.rhk_test1) = ' + typeof(wnd.window.rhk_test1));
    alert('typeof(wnd.window.rhk_test2) = ' + typeof(wnd.window.rhk_test2));
    alert('typeof(wnd.window.rhk_test3) = ' + typeof(wnd.window.rhk_test3));
    alert('typeof(wnd.rhk_test1) = ' + typeof(wnd.rhk_test1));
    alert('typeof(wnd.rhk_test2) = ' + typeof(wnd.rhk_test2));
    alert('typeof(wnd.rhk_test3) = ' + typeof(wnd.rhk_test3));
    alert('typeof(rhk_test1) = ' + typeof(rhk_test1));
    alert('typeof(rhk_test2) = ' + typeof(rhk_test2));
    alert('typeof(rhk_test3) = ' + typeof(rhk_test3));
    alert('typeof(this.rhk_test1) = ' + typeof(this.rhk_test1));
    alert('typeof(this.rhk_test2) = ' + typeof(this.rhk_test2));
    alert('typeof(this.rhk_test3) = ' + typeof(this.rhk_test3));
    alert('typeof(window.rhk_test1) = ' + typeof(window.rhk_test1));
    alert('typeof(window.rhk_test2) = ' + typeof(window.rhk_test2));
    alert('typeof(window.rhk_test3) = ' + typeof(window.rhk_test3));
  },
  onLoad: function(event) {
    var appcontent = document.getElementById("appcontent");
    if (appcontent) {
      appcontent.addEventListener("DOMContentLoaded", rhkTest.onPageLoad, true);
    }
  },
};  

window.addEventListener("load", function(e) { rhkTest.onLoad(e); }, false);

where testfoo.js contains:

window.rhk_test1 = 'testval1';
rhk_test2 = 'testval2';
var rhk_test3 = 'testval3';

window.alert('Running testfoo.js');

I get the alert "Running testfoo.js", so the file is found and executed. I also get an alert noting that wnd is an "object" (as expected -- it's initialized elsewhere). However, all the other alerts show "undefined" for the various typeof() calls. Naturally, I don't expect all of these to have values, but I was hoping that at least one of them might show something.

What happened to my values? Aren't they supposed to be in properties of wnd?

I'm running Firefox 19.0 on Windows 7 (if that matters).

Rick Koshi
  • 945
  • 1
  • 11
  • 21

2 Answers2

3

It's actually a bit complicated and depends on what wnd is. You can use a regular object as the scope for the subscript:

var myScope = this;
var subscriptScope = {};
loader.loadSubScript("testfoo.js", subscriptScope);

However, the scopes aren't completely isolated then. Here some examples for the code in testfoo.js:

var foo = "bar;      // Creates subscriptScope.foo
xyz = 1;             // Creates myScope.xyz
function dummy() {}  // Creates subscriptScope.dummy
var outer = myScope; // Assigns a value from outer scope

So while variables and functions of the subscript are defined in its scope, it still has access to the scope of the script that loaded it - undeclared variables will still be created in the outer scope and the outer scope will be searched for variables that cannot be resolved in the subscript scope.

If you really want to isolate the subscript then you should use a "proper" global object like a window object or a sandbox:

var myScope = this;
var subscriptScope = new Components.utils.Sandbox("about:blank");
loader.loadSubScript("testfoo.js", subscriptScope);

Now testfoo.js won't be able to access the outer scope any more:

var foo = "bar;      // Creates subscriptScope.foo
xyz = 1;             // Creates subscriptScope.xyz
function dummy() {}  // Creates subscriptScope.dummy
var outer = myScope; // Exception: myScope is not defined

Note that there is nothing special about the window variable - if you want the subscript to have that variable you have to define it in the scope object, e.g.:

subscriptScope.window = subscriptScope;

The scope object itself can be accessed as this at the top level of the subscript.

Wladimir Palant
  • 56,865
  • 12
  • 98
  • 126
  • Thank you for your detailed answer. Unfortunately, my results don't seem to match what I'd expect, given what you've said. I have updated my question to reflect a more complete example. As you can see, wnd is a window object, hopefully "proper" by your meaning. I've added more tests for where the data might have gone, but I'm still coming up empty. – Rick Koshi Mar 04 '13 at 13:36
1

Note: See Wladimir Palant's answer for a more general overview.

In the case of my example, I was actually using the function correctly, with more-or-less reasonable expectations. The problem was caused by Firefox's XPCNativeWrapper functionality.

I'm still struggling to understand the details, but in a nutshell, my wnd object was being wrapped in an XPCNativeWrapper, and testfoo.js was considered a "protected" script, which meant that I could not see any changes made to the object once control passed back from testfoo.js.

The solution is to unwrap the object before (or after -- that works too) passing it to the subscript, as follows:

var wnd = XPCNativeWrapper.unwrap(doc.defaultView);

As soon as I did that, the variables/properties modified by testfoo.js became visible to the parent script.

I do not yet understand the full infrastructure well enough to know whether this is "safe". After all, this is a security mechanism designed to protect my extension from malicious code on the browser page.

I am also seeing some problems with passing objects into methods defined by the subscript (not shown in my example). For that, I will make a separate question.

Long story short: Unwrapping is a (probably "the") solution for the immediate problem as posed in my question, but does not appear to be a complete solution to the difficulties I was having that engendered the question in the first place.

Rick Koshi
  • 945
  • 1
  • 11
  • 21