Reputation: 9485
I am new to extension development. I am trying to trigger an action on a keydown
event in my bootstrapped Firefox extension, but it doesn't seem to work. Am I missing something?
Here is my bootstrap.js code:
Cu.import("resource://gre/modules/Services.jsm");
function watchWindows(callback) {
function watcher(window) {
try {
let {documentElement} = window.document;
if (documentElement.getAttribute("windowtype") == "navigator:browser")
callback(window);
}
catch(ex) {}
}
function runOnLoad(window) {
window.addEventListener("load", function runOnce() {
window.removeEventListener("load", runOnce, false);
watcher(window);
}, false);
}
// Add functionality to existing windows
let windows = Services.wm.getEnumerator(null);
while (windows.hasMoreElements()) {
// Only run the watcher immediately if the window is completely loaded
let window = windows.getNext();
if (window.document.readyState == "complete")
watcher(window);
// Wait for the window to load before continuing
else
runOnLoad(window);
}
// Watch for new browser windows opening then wait for it to load
function windowWatcher(subject, topic) {
if (topic == "domwindowopened")
runOnLoad(subject);
}
Services.ww.registerNotification(windowWatcher);
// Make sure to stop watching for windows if we're unloading
unload(function() Services.ww.unregisterNotification(windowWatcher));
}
/**
* Save callbacks to run when unloading. Optionally scope the callback to a
* container, e.g., window. Provide a way to run all the callbacks.
*
* @usage unload(): Run all callbacks and release them.
*
* @usage unload(callback): Add a callback to run on unload.
* @param [function] callback: 0-parameter function to call on unload.
* @return [function]: A 0-parameter function that undoes adding the callback.
*
* @usage unload(callback, container) Add a scoped callback to run on unload.
* @param [function] callback: 0-parameter function to call on unload.
* @param [node] container: Remove the callback when this container unloads.
* @return [function]: A 0-parameter function that undoes adding the callback.
*/
function unload(callback, container) {
// Initialize the array of unloaders on the first usage
let unloaders = unload.unloaders;
if (unloaders == null)
unloaders = unload.unloaders = [];
// Calling with no arguments runs all the unloader callbacks
if (callback == null) {
unloaders.slice().forEach(function(unloader) unloader());
unloaders.length = 0;
return;
}
// The callback is bound to the lifetime of the container if we have one
if (container != null) {
// Remove the unloader when the container unloads
container.addEventListener("unload", removeUnloader, false);
// Wrap the callback to additionally remove the unload listener
let origCallback = callback;
callback = function() {
container.removeEventListener("unload", removeUnloader, false);
origCallback();
}
}
// Wrap the callback in a function that ignores failures
function unloader() {
try {
callback();
}
catch(ex) {}
}
unloaders.push(unloader);
// Provide a way to remove the unloader
function removeUnloader() {
let index = unloaders.indexOf(unloader);
if (index != -1)
unloaders.splice(index, 1);
}
return removeUnloader;
}
/* library */
const Utils = (function() {
const sbService = Cc['@mozilla.org/intl/stringbundle;1']
.getService(Ci.nsIStringBundleService);
const windowMediator = Cc['@mozilla.org/appshell/window-mediator;1']
.getService(Ci.nsIWindowMediator);
let setAttrs = function(widget, attrs) {
for (let [key, value] in Iterator(attrs)) {
widget.setAttribute(key, value);
}
};
let getMostRecentWindow = function(winType) {
return windowMediator.getMostRecentWindow(winType);
};
let exports = {
setAttrs: setAttrs,
getMostRecentWindow: getMostRecentWindow,
};
return exports;
})();
let ResponseManager = (function() {
const obsService = Cc['@mozilla.org/observer-service;1']
.getService(Ci.nsIObserverService);
const RESPONSE_TOPIC = 'http-on-examine-response';
let observers = [];
let addObserver = function(observer) {
try {
obsService.addObserver(observer, RESPONSE_TOPIC, false);
} catch(error) {
trace(error);
}
observers.push(observers);
};
let removeObserver = function(observer) {
try {
obsService.removeObserver(observer, RESPONSE_TOPIC, false);
} catch(error) {
trace(error);
}
};
let destory = function() {
for (let observer of observers) {
removeObserver(observer);
}
observers = null;
};
let exports = {
addObserver: addObserver,
removeObserver: removeObserver,
destory: destory,
};
return exports;
})();
/* main */
let ReDisposition = function() {
let respObserver;
respObserver = {
observing: false,
observe: function(subject, topic, data) {
try {
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
this.override(channel);
} catch(error) {
trace(error);
}
},
start: function() {
if (!this.observing) {
ResponseManager.addObserver(this);
this.observing = true;
}
},
stop: function() {
if (this.observing) {
ResponseManager.removeObserver(this);
this.observing = false;
}
},
refresh: function() {
this.start();
},
re: /^\s*attachment/i,
re2: /^\s*text/i,
override: function(channel) {
if(disablePlugin)
{
try {
let contentHeader;
contentHeader = channel.getResponseHeader('Content-Type');
if (this.re2.test(contentHeader))
return;
channel.setResponseHeader('Content-Disposition', "attachment", false);
return;
}
catch(error) {
return;
}
}
// check if have header
let header;
try {
header = channel.getResponseHeader('Content-Disposition');
} catch(error) {
return;
}
// override to inline
if (this.re.test(header)) {
channel.setResponseHeader('Content-Disposition', header.replace(this.re, "inline"), false);
return;
}
}
};
let initialize = function() {
respObserver.refresh();
};
let destory = function() {
respObserver.stop();
};
let exports = {
initialize: initialize,
destory: destory,
}
return exports;
};
/* bootstrap entry points */
let reDisposition;
var disablePlugin = false;
let install = function(data, reason) {};
let uninstall = function(data, reason) {};
let startup = function(data, reason) {
reDisposition = ReDisposition();
reDisposition.initialize();
function onwindow(window) {
function onkeydown(e) {
if (e.keyCode == 70)
{
disablePlugin = true;
}
else
{
disablePlugin = false;
}
}
function onkeyup(e) {
disablePlugin = false;
}
// Bootstrapped add-ons need to clean up after themselves!
function onunload() {
window.removeEventListener("keydown", onkeypress);
window.removeEventListener("keyup", onkeypress);
}
window.addEventListener("keydown", onkeydown);
window.addEventListener("keyup", onkeyup);
unload(onunload, window);
}
watchWindows(onwindow);
};
let shutdown = function(data, reason) {
reDisposition.destory();
};
Upvotes: 3
Views: 522
Reputation: 33212
There is no window
in a bootstrap.js
scope. bootstrap.js
is essentially a standalone code module (that is running in a Sandbox, but that's just an implementation detail). It will run only once per application, not once per window.
When developing bootstrapped add-ons, you have to enumerate existing and watch for new windows yourself and "attach" to them as needed. See e.g. the watchWindows
boilerplate.
Here is an example. It assumes you're using the watchWindows
boilerplate:
function startup(data, reason) {
// Callback function called for each browser window (browser.xul)
function onwindow(window) {
function onkeydown(e) {
// XXX: Your code here.
}
// Bootstrapped add-ons need to clean up after themselves!
function onunload() {
window.removeEventListener("keydown", onkeypress);
}
window.addEventListener("keydown", onkeydown);
unload(onunload, window);
}
// Will also fire for existing windows once and upon each new window.
watchWindows(onwindow);
}
Developing bootstrapped add-ons isn't really something I'd recommend when you're new to Firefox add-ons, XUL/XPCOM and the mozilla platform in general. You probably should check out XUL overlay-based add-ons or the Add-on SDK.
Upvotes: 3