简体   繁体   中英

Why in Firefox Addon the imported objects can not access the importers' imported modules?

For clarification, please consider the following simplified example:

one.js

Components.utils.import('resource://gre/modules/Services.jsm');

let obj = {

  init: function() {

   Components.utils.import('chrome://myaddon/modules/two.jsm', this);
  }

  // code here has access to Services.jsm
}

two.js

this.EXPORTED_SYMBOLS = ['abc'];

this.abc = {

  // abc is imported into obj()
  // however as part of obj (), abc{} does not have access to Services.jsm
}

I know that this is how it works but the question is why?
Result is that for example Services.jsm has to be imparted in every module.
Although Firefox caches the modules and there isn't much of a performance difference, I would like to know if the repeated importation can be avoided?

Like @felix-kling already mentioned, that's due to module level isolation and it makes a lot of sense if you think about it. If it was otherwise not only Services would be seen by other modules, but also abc .

There is another important reason, though: Since JS code modules are initiated once and cached after that, what would happen if you imported two.jsm twice, once from a module already having imported Services.jsm and once from another module not having done so? Now two.jsm "seeing" Services would depend on which of the other modules was imported first! Which would be extremely nasty.

In that context, your comment about "abc is imported into obj()" is wrong. Your code actually imports abc into the top-level scope. Cu.import will always import into the top-level scope, unless you explicitly specify another scope to import to.

"abc" in this; // false
"abc" in obj; // false
obj.init();
"abc" in this; // true
"abc" in obj; // false!

If you wanted to import two.jsm into obj , you'd need to call Cu.import with a second argument.

let obj = {
  init: function() {
   Components.utils.import('chrome://myaddon/modules/two.jsm', this);
  }
};
"abc" in this; // false
"abc" in obj; // false
obj.init();
"abc" in this; // false
"abc" in obj; // true

But that does not affect the visibility of Services , of course.

It would be helpful I guess, if Cu.import just auto-imported some modules you'd import anyway, such as Services.jsm and XPCOMUtils.jsm . But this does not and likely will not ever happen due to legacy reasons and backward-compatibility constraints. (Eg I had code break that imported const {Promise} = Cu.import(..., {}); because ES6 added a default Promise global...; that kind of backward-compatibility issues/constraints).

Alternatives?

Well, the obvious one is not to use Cu.import for your own stuff, but use something else. A bunch of add-ons, incl. all SDK add-ons of course, have their own CommonJS-style require() implementation.

  • You can actually re-use the SDK loader, without using the SDK, or with only using selected parts of the SDK if you like. See the loader documentation. I know that Erik creates a loader in the otherwse non-SDK Scriptish add-on.
  • You can write your own custom loader based on the subscript loader and maybe Sandbox . Eg I did so in my extSDK boilerplate (all global symbols in loader.jsm == loader.jsm::exports will be visible to each require d module).

But doing so may require a quite bit of extra work, extra knowledge, and effort to port existing JS code modules to require() based modules.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM