Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.
From Firefox 53 onwards, no new legacy add-ons will be accepted on addons.mozilla.org (AMO).
From Firefox 57 onwards, WebExtensions will be the only supported extension type, and Firefox will not load other types.
Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to WebExtensions if they can. See the "Compatibility Milestones" document for more.
A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.
This page contains small, self-explanatory code snippets.
System info
Operating system detection
// Returns "WINNT" on Windows Vista, XP, 2000, and NT systems; // "Linux" on GNU/Linux; and "Darwin" on Mac OS X. var osString = Services.appinfo.OS;
Detecting the host application and version
// Get the name of the application running us Services.appinfo.name; // Returns "Firefox" for Firefox Services.appinfo.version; // Returns "2.0.0.1" for Firefox version 2.0.0.1
Retrieving the version of an extension as specified in the extension's install.rdf
Components.utils.import("resource://gre/modules/AddonManager.jsm"); AddonManager.getAddonByID("extension-guid@example.org", function(addon) { // This is an asynchronous callback function that might not be called immediately alert("My extension's version is " + addon.version); });
Restarting Firefox/Thunderbird/SeaMonkey_2.0
- For Firefox 3 see
onWizardFinish
around here: http://mxr.mozilla.org/seamonkey/sou...pdates.js#1639 - For Firefox 2 see around here: http://mxr.mozilla.org/mozilla1.8/so...pdates.js#1631
bugĀ 338039 tracks improving this situation by providing a simple method to restart the application.
Example for Firefox:
Services.startup.quit(Services.startup.eForceQuit|Services.startup.eRestart);
Mouse and keyboard
Detecting mouse wheel events
When scrolling the mouse wheel on an element, the DOMMouseScroll
event fires. event.detail
contains the number of lines to scroll. This event is Mozilla-only; other browsers may support window.onmousewheel
.
<div id="scrollArea" style="overflow: scroll; height: 6em; width: 10em;"> This is the scrolling area. This is the scrolling area. This is the scrolling area. This is the scrolling area. This is the scrolling area. This is the scrolling area. This is the scrolling area. This is the scrolling area. This is the scrolling area. This is the scrolling area. This is the scrolling area. This is the scrolling area. </div> <script type="text/javascript"> var elm = document.getElementById("scrollArea"); elm.addEventListener("DOMMouseScroll", function scroll(event){ //event.detail is positive for a downward scroll, negative for an upward scroll alert("scrolling " + event.detail + " lines"); }, false); </script>
If you do not receive a DOMMouseScroll
event while holding any of the modifier keys (Ctrl,Shift,Alt,Meta) you should check the mousewheel.withcontrolkey.action
and related preferences. The meaning of the action preference is shown in the following table
mousewheel.withXXXkey.action | Result |
0 | Scroll by lines. If set, you will receive DOMMouseScroll events. |
1 | Scroll by page. |
2 | Move around in the history. If set, you will not receive DOMMouseScroll events. |
3 | Change text size. If set, you will not receive DOMMouseScroll events. |
You can listen to mouse wheel click events just like normal click events (via mousedown
and mouseup
events). When the mouse wheel is clicked, event.button
will be equal to 1.
Simulating mouse and key events
https://developer.mozilla.org/samples/domref/dispatchEvent.html
Also, new in Firefox 3 / Gecko 1.9:
var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); utils.sendMouseEvent("mousedown", 10, 10, 0, 1, 0); utils.sendMouseEvent("mouseup", 10, 10, 0, 1, 0);
Getting the currently selected text
From browser.xul
overlay context:
var selectedText = document.commandDispatcher.focusedWindow.getSelection().toString();
or:
content.getSelection(); // |window| object is implied; i.e., window.content.getSelection()
or:
getBrowserSelection(); // |window| object is implied; i.e., window.getBrowserSelection()
This final option massages the selection to remove leading and trailing whitespace. It also collapses consecutive whitespaces into a single whitespace, and returns at most 150 characters, making it ideal to display text in context menus. It is defined at http://mxr.mozilla.org/mozilla-central/source/browser/base/content/browser.js#4620. Note that it returns the empty string (""), not false
, when nothing is selected. getBrowserSelection()
is not implemented on some platforms, such as Thunderbird and Songbird.
See also http://mxr.mozilla.org/seamonkey/source/browser/base/content/browser.js#4674
Discovering which element in the loaded document has focus
// focusedControl stores the focused field, or null if there is none. // This excludes textareas for simplicity, but onPageLoad() can easily be // modified to cycle through textareas as well. Further enhancements would include // handling for fields dynamically added to the page (e.g., by page javascript). var focusedControl; window.addEventListener("load", function(e) { onWindowLoad(e); }, false); function onWindowLoad() { gBrowser.addEventListener("load", onPageLoad, true); } function onPageLoad() { pageDoc = document.commandDispatcher.focusedWindow.document; var inputList = pageDoc.getElementsByTagName('input'); for (var i=1; i<inputList.length; i++) { inputList.item(i). addEventListener("focus", function(e) {onFocusInput(e);}, false); inputList.item(i). addEventListener("blur", function(e) {onBlurInput(e);}, false); } } function onFocusInput(focusEvent) { focusedControl = focusEvent.originalTarget; } function onBlurInput(blurEvent) { focusedControl = null; }
or
var element = document.commandDispatcher.focusedElement;
Inserting text at the cursor
function insertText(element, snippet) { var selectionEnd = element.selectionStart + snippet.length; var currentValue = element.value; var beforeText = currentValue.substring(0, element.selectionStart); var afterText = currentValue.substring(element.selectionEnd, currentValue.length); element.value = beforeText + snippet + afterText; element.focus(); //put the cursor after the inserted text element.setSelectionRange(selectionEnd, selectionEnd); } insertText(document.getElementById("example"), "the text to be inserted");
Disabling JavaScript programmatically
// Disable JS in the currently active tab from the context of browser.xul gBrowser.docShell.allowJavascript = false;
If this isn't your browser, you should save the value and restore it when finished. If you wish to block selected scripts based on their URI, implement nsIContentPolicy
.
Using string bundles from JavaScript
Assuming the extension has myext.properties with name/value pairs such as:
invalid.url=The speficied URL, %S, is invalid. That was attempt number %S.
These properties can be accessed from JavaScript in the following manner:
var common = { _bundle: Components.classes["@mozilla.org/intl/stringbundle;1"] .getService(Components.interfaces.nsIStringBundleService) .createBundle("chrome://myext/locale/myext.properties"), getLocalizedMessage: function(msg) { return this._bundle.GetStringFromName(msg); } }; alert(common.getLocalizedMessage("invalid.url"))
Another similar alternative (using both GetStringFromName
and formatStringFromName
), is:
var fcBundle = Components.classes["@mozilla.org/intl/stringbundle;1"] .getService(Components.interfaces.nsIStringBundleService) .createBundle("chrome://myext/locale/myext.properties"); function getStr(msg, args){ //get localised message if (args){ args = Array.prototype.slice.call(arguments, 1); return fcBundle.formatStringFromName(msg,args,args.length); } else { return fcBundle.GetStringFromName(msg); } } /* Usage */ alert(getStr("invalid.url", "http://bad/url/", "3")); //for message with parameters alert(getStr("invalid.url")); //for message without parameters
Getting postData of a webpage
First, you need to get the browser you want, and its historySession
.
var Ci = Components.interfaces; var Cc = Components.classes; //assume you can not get the main window object directly, if you can, just use it var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); var mainWindow = wm.getMostRecentWindow("navigator:browser"); //get sessionHistory from the current selected tab var history = mainWindow.gBrowser.selectedBrowser.webNavigation.sessionHistory;
And then get the page you want, and it's postData
. This example obtains the post data of the last page.
var postdata = history.getEntryAtIndex(history.index-1,false).QueryInterface(Ci.nsISHEntry).postData;
If you got here all by yourself, your problem must be at reading the postData, because it's a nsIInputStream
object, whose available function always returns 0. And if you read it directly, you always got nothing. And here's how it's done
postdata.QueryInterface(Ci.nsISeekableStream).seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream); stream.setInputStream(postdata); var postBytes = stream.readByteArray(stream.available()); var poststr = String.fromCharCode.apply(null, postBytes); //Do anything to your poststr alert(poststr);
Getting a string from the input stream is made somewhat simpler in Firefox 4, by the addition of NetUtil.readInputStreamToString()
Getting postData of a request before the request is sent
The above code will get the postdata for a page that has already loaded. To see what the postdata looks like before the request is even sent, use the 'http-on-modify-request' observer topic:
observerService.addObserver(observer, 'http-on-modify-request', false);
where "observer" is an object that has a method "observe":
function observe(subject, topic, data) { subject.QueryInterface(Components.interfaces.nsIUploadChannel); postData = subject.uploadStream; }
Here again, postData is not a string, but an nsIInputStream, so you can use the last code snippet of the previous section to get the data as a string. However, if you are not going to cancel the request, you need to "rewind" it by calling:
postdata.QueryInterface(Ci.nsISeekableStream).seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
Adding custom certificates to a XULRunner application
You need to ship a XULRunner application with your own SSL certificates? For Firefox, you could use the Client Customization Kit (CCK) to create an extension that does exactly that. To get the same feature for your XULRunner application, follow those steps:
First, put the certificates you want to ship into the content/certs
folder of your application.
Then create a XPCOM service that adds the certificates inside that directory to the certs database on application startup. Put this code in the components/certsService.js
file:
const Cc = Components.classes; const Ci = Components.interfaces; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); const gObserver = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService); const gIOService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); function CertsService() {} CertsService.prototype = { observe: function(aSubject, aTopic, aData) { switch(aTopic) { case "app-startup": gObserver.addObserver(this,"xpcom-shutdown",false); gObserver.addObserver(this,"final-ui-startup",false); break; case "xpcom-shutdown": gObserver.removeObserver(this,"final-ui-startup"); gObserver.removeObserver(this,"xpcom-shutdown"); break; case "final-ui-startup": this.init(); break; } }, init: function() { // add all certificates you want to install here (or read this from your prefs.js ...) var certificates = "root.crt,user.crt"; var certs = certificates.split(','); for (var i=0; i<certs.length; i++) { this.addCertificate(certs[i], 'C,c,c'); } }, addCertificate: function(CertName, CertTrust) { var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(Ci.nsIX509CertDB2); var scriptableStream=Cc["@mozilla.org/scriptableinputstream;1"].getService(Ci.nsIScriptableInputStream); var channel = gIOService.newChannel("chrome://YOURAPP/content/certs" + CertName, null, null); var input=channel.open(); scriptableStream.init(input); var certfile=scriptableStream.read(input.available()); scriptableStream.close(); input.close(); var beginCert = "-----BEGIN CERTIFICATE-----"; var endCert = "-----END CERTIFICATE-----"; certfile = certfile.replace(/[\r\n]/g, ""); var begin = certfile.indexOf(beginCert); var end = certfile.indexOf(endCert); var cert = certfile.substring(begin + beginCert.length, end); certDB.addCertFromBase64(cert, CertTrust, ""); }, classDescription: "Certificate Service", contractID: "@mozilla.org/certs-service;2", classID: Components.ID("{e9d2d37c-bf25-4e37-82a1-16b8fa089939}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), _xpcom_categories: [{ category: "app-startup", service: true }] } function NSGetModule(compMgr, fileSpec) { return XPCOMUtils.generateModule([CertsService]); }
You need to delete your existing profile, otherwise the XPCOM service is not used. See How to Build an XPCOM Component in Javascript for details. Now are your certificates installed on every application startup. Unfortunately that seems to be the only working solution, apart from modifying the Mozilla source code and recompiling.
Generating Random Bytes
Useful snippet for generating random bytes that can, for example be used as a good source for cryptographic entropy.
const NOB = 128; // number of bytes var buffer = ''; var prng = Components.classes['@mozilla.org/security/random-generator;1']; var bytebucket = prng.getService(Components.interfaces.nsIRandomGenerator).generateRandomBytes(NOB, buffer);
Detecting full screen mode on/off
It works for that global 'window' object at least.
window.addEventListener('fullscreen', function(){ alert('fullscreen mode on or off') }, false)
Getting addon install path
First one using addon manager:
Components.utils.import('resource://gre/modules/AddonManager.jsm'); AddonManager.getAddonById('example@addon', function(addon) { let path = addon.getResourceURI().path; // something like /home/username/.... });
example@addon is extension id.
Another one is to utilize bootstrap.js startup function data argument:
function startup(data, reason) { // some code ... yourModule(data.installPath.path); // some code ... }