Since we're talking restartless add-ons... A lot of restartless add-ons use unload
and unloadWindow
helper functions, to make it easier to implement shutdown
properly and also help with stuff like addEventListener
, so bear with me for a bit.
The helpers - unload
First, unload
is a helper function that you pass another function to, that will be run upon shutdown
(or can be called manually). Most implementations are extremely similar to this:
var unloaders = []; // Keeps track of unloader functions.
function unload(fn) {
if (typeof(fn) != "function") {
throw new Error("unloader is not a function");
}
unloaders.push(fn);
return function() {
try {
fn();
}
catch (ex) {
Cu.reportError("unloader threw " + fn.toSource());
Cu.reportError(ex);
}
unloaders = unloaders.filter(function(c) { return c != fn; });
};
}
You'd then hook up shutdown
to do the right thing:
function shutdown() {
...
for (let i = unloaders.length - 1; i >= 0; --i) {
try {
unloaders[i]();
}
catch (ex) {
Cu.reportError("unloader threw on shutdown " + fn.toSource());
Cu.reportError(ex);
}
}
unloaders.length = 0;
}
Using unload
Now you can do stuff like:
function startup() {
setupSomething();
unload(removeSomething);
setupSomethingElse();
var manualRemove = unload(removeSomethingElse);
...
if (condition) {
manualRemove();
}
}
The helpers - unloadWindow
You'll usually want to create a second function unloadWindow
to unload stuff when either your add-on is shut down or the window gets closed, whatever happens first. Not removing stuff when the window gets closed can be very tricky, and create Zombie compartments of your bootstrap.js
and/or code modules very easily (this is from experience writing and reviewing restartless add-ons).
function unloadWindow(window, fn) {
let handler = unload(function() {
window.removeEventListener('unload', handler, false);
try {
fn();
}
catch (ex) {
Cu.reportError("window unloader threw " + fn.toSource());
Cu.reportError(ex);
}
});
window.addEventListener('unload', handler, false);
};
(Some people might want to "optimize" this, as to have only one "unload"
handler, but usually you only have so unloadWindow
calls that it won't matter.)
Putting it all together
Now you can .bind
stuff and do whatever and let the the unloader closures keep track of it. Also, you can use this to keep your shut down code next to your initialization code, which might increase readability.
function setupWindow(window, document) {
var bound = this.contextPopupShowing.bind(this);
contextMenu.addEventListener('popupshowing', bound, false);
unloadWindow(window, function() {
contextMenu.removeEventListener('popupshowing', bound, false);
});
// Or stuff like
var element = document.createElement(...);
contextMenu.appendChild(element);
unloadWindow(window, function() {
contextMenu.removeChild(element);
});
// Or just combine the above into a single unloader
unloadWindow(window, function() {
contextMenu.removeEventListener('popupshowing', bound, false);
contextMenu.removeChild(element);
});
}