In my add-on, I'm using XUL to display dialog windows because I can customize their appearance to suit the add-on's general style (like a custom titlebar
).
Using the migration guide, I'm able to do this easily. The thing is, I would like to call certain functions in the add-on's main
module from the XUL dialog.
After a bit of searching I found the loader
module, which seems to be able to do exactly what I want. But, I'm experiencing trouble in using it to access the main
module.
First, I tried the obvious approach as mentioned in the documentation;
xul_dialog.js:
let {Loader} = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js');
let loader = Loader.Loader({
paths: {
'toolkit/': 'resource://gre/modules/commonjs/toolkit/',
'': 'resource:///modules/',
'./': 'resource://<my-addon-name>/root/'
}
});
let main = Loader.main(loader, './main');
I got an error that './main'
was not found at resource://<my-addon-name>/root/
. Figuring that I was using the incorrect paths, I experimented a bit until I could remove all path associated errors.
xul_dialog.js:
let {Loader} = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js');
let loader = Loader.Loader({
paths: {
'toolkit/': 'resource://gre/modules/commonjs/toolkit/',
'': 'resource://gre/modules/commonjs/',
'./': 'resource://<my-addon-id>-at-jetpack/<my-addon-name>/lib/'
}
});
let main = Loader.main(loader, './main');
This time I got a rather confusing error at loader.js, line 279
.
Components is not available in this context.
Functionality provided by Components may be available in an SDK
module: https://jetpack.mozillalabs.com/sdk/latest/docs/
However, if you still need to import Components, you may use the
`chrome` module's properties for shortcuts to Component properties:
Shortcuts:
Cc = Components.classes
Ci = Components.interfaces
Cu = Components.utils
CC = Components.Constructor
Example:
let { Cc, Ci } = require('chrome');
I get the same error when I use Loader.Require(loader, {id: './main'})
instead of Loader.main
. I even tried passing Components
as globals
when instantiating the loader, but without much luck.
I'm fairly certain that I'm doing a lot of things wrong. I don't understand why I'm getting the error, even after spending quite a bit of time in loader.js
. Plus, I also think that there would be a better alternative than having to use the add-on id for the path to main.js
; hard-coding it like that doesn't seem right.
Any help would be really appreciated.
If it is your XUL dialog that should interact with your add-on, then please don't use the
Loader
stuff and in particular do not go theXPIProvider.bootstrapScopes
@paa suggested. While this might work (for now), it should be noted that it relies on tons of implementation details that are subject to change at any point making this solution extremely fragile.Instead there are a couple of other options (not an exhaustive list):
.openDialog
which supports passing arguments to the created window, and these arguments can even be objects and functions. Also, you can have the window dispatch (custom) events, and which your SDK part can listen to by callingaddEventListener
on thewindow
the.openDialog
call returns.AddonManager
because ofem:optionsURL
) then thensIObserverService
is another way to communicate. The window could e.g..notifyObservers
onDOMContentLoaded
containing a reference to itself. The SDK parts would just have to observe such notifications byaddObserver
.nsIWindowWatcher.registerNotification
and then injecting some API into e.g.browser.xul
windows byXPCNativeWrapper.unwrap(subject.QueryInterface(Ci.nsIDOMWindow)).myAddonAPI = something
.Be sure to handle unloading or your add-on well - it is still restartless -, i.e. reverse any changes you've done and remove any observers and so on again.
If you want to interact with SDK addons not created by you, then the
XPIProvider
route might be the only feasible. But still, it might be worth contacting the add-on author first asking for the addition of some public API instead of hacking deep into the AddonManager and SDK loader internals.PS:
Considering passing
require
or the global scope to your window viaopenDialog
if you want to. Get the global scope by placing this into yourmain.js
:What you have to do is to find a specific instance of
Loader
, not create a new one.At main.js
At xul.js (or whatever is the name of the xul dialog script)
Assuming there is a
foo
function at main.js, you can call it likeOf course don't expect things to work as if XUL and Add-on SDK actually blended into one thing.