Writing your Mozilla Firefox browser extension

  • Tutorial
So, after updating Firefox to version 19 , the beloved Yandex.Bar extension completely fell off. I will not forget to recall that Yandex.Bar was replaced by Yandex.Elements , which were liked a little more than anyone, and therefore received their well-deserved 2 out of 5 balls .

Why didn’t you like it? Replaced the address bar, it became inconvenient to view mail, replaced bookmarks and removed the address bar corrector (under the pretext of installing Punto Switcher, which may be good for an ordinary employee, but not for a programmer. Therefore, it was deleted almost immediately as it was installed. Yes and if it could be customized, then all the same the desire was gone).

A little later, it was decided to create their own similar extension, which will include such buns as viewing mail and address bar corrector. Well, if not you, so who is the other?

The first thing it was decided not to create your bike and resurrect Yandex.Bar, which did not want to work in version 19 of the browser. On the Internet, they suggested that the extension is a regular zip archive. They opened, looked, were horrified and closed. It was not possible to resurrect, even with all desire.

Then we go to the developer's center: builder.addons.mozilla.org . I preferred to use the web editor, although in some places it sometimes didn’t work very smoothly. After looking at other extensions, borrowing the code and a little understanding of the whole point of sowing the device, it all started first with a wall-hung machine and ended with a file.

The builder includes 3 sections: this is a section with scripts (Lib), a section with downloadable content (pictures, styles and scripts) and a section with ready-made libraries (Libraries)

By the way, here is the documentation: addons.mozilla.org/en-US/developers / docs / sdk / latest , solidly written.
The extension starts by loading the main.js file .
The function is called: exports.main .

Example main.js file:
const tabs = require("tabs");
exports.main = function (options) {
    tabs.on("ready", function(tab){
        contentScript: "document.addEventListener('click', function(e) { \
				var target = e.target; \
				if(target.tagName == 'A') { \
                    var mail_to = target.href.match(/^mailto:(.*)/i); \
					if(mail_to != null) { \
						e.preventDefault(); \
						var form = document.createElement('form'); \
						form.setAttribute('action','http://mail.yandex.ru/neo2/#compose/mailto=' + mail_to[1]); \
						form.setAttribute('target','_blank'); \
						document.getElementsByTagName('body')[0].appendChild(form); \
						form.submit(); \
						form.parentNode.removeChild(form); \
					} \
				} \
			}, false);"

What kind of magic happens in this code?

The first step is to connect the tabs module .
In this case, it serves to add your JavaScript code to the browser page.
Those. what we have: when an onready document event occurs, any JavaScript code is added to the body of the document. In this example, a link handler is added, for which the address begins with mailto .

Okay, let's do something more complicated. Add your button to the top bar!
Again, we won’t build bicycles, but with a clear conscience, we’ll take the ready-made Toolbar Button Complete library .

It also has an example of adding a button to a browser bar. I think you should not dump it here, because there is a lot of code.
So, there is a button, an icon has been set, everything seems to be fine, but not very good. How was it in Yandex.Bar? Oh yes, opposite the icon, there was also a counter of unread messages.
Here I found out several ways to add a counter:
  • universal but lighter (using styles)
  • not too universal, but not as simple as the first (using canvas)

The second way, however, was found by typing on the Internet. But I took the first one.
We know that the top bar is the same set of elements with their own classes, identifiers, properties and ways of working with them.

By typing type:
for(var val in document.getElementById('yandex-menu')) {

it was found that the methods are exactly the same as those that we usually use when working with site elements. But I note that by standard the browser does not know what document or window is in extensions (and there are still differences).

Example solution:
var wuntils = require('sdk/window/utils');
var window = wuntils.getMostRecentBrowserWindow();
var document = window.document;

I note that the development of the builder does not stand still and if earlier the way to get the active window was like this :
var winUtils = require("window-utils");
for (window in winUtils.windowIterator()) {
   if ("chrome://browser/content/browser.xul" != window.location)  return;
   console.log("An open window! " + window.location);
Now everything is much easier (I gave the example above).

Well, having talked a little about the features, I will return to adding a counter for the button.
Smart people suggested that by standard, the style of the label field of a button is display: none; , so somehow I needed to inject my css code into a bar. The solution, as it turned out, is not complicated (I advise you to wrap it in a file that will be included as needed):
const { Cc, Ci } = require('chrome');
const { when: unload } = require('sdk/system/unload');
var ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
/* Helper that registers style sheets and remembers to unregister on unload */
exports.addXULStylesheet = function addXULStylesheet(url) {
    var uri = newURI(url);
	var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
	sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
	unload(function () {
		if (sss.sheetRegistered(uri, sss.USER_SHEET)) {
			sss.unregisterSheet(uri, sss.USER_SHEET);
    return sss;
function newURI(uriStr, base) {
	try {
		var baseURI = base ? ios.newURI(base, null, null) : null;
		return ios.newURI(uriStr, null, baseURI);
	catch (e) {
		if (e.result === chrome.Cr.NS_ERROR_MALFORMED_URI) {
			throw new Error("malformed URI: " + uriStr);
		} else if (e.result === chrome.Cr.NS_ERROR_FAILURE ||
			e.result === chrome.Cr.NS_ERROR_ILLEGAL_VALUE) {
			throw new Error("invalid URI: " + uriStr);
	return null;

And we add something like to the exprorts.main function (although you can add it anywhere):

Do not forget to create stylesheet.css file in the content .

My file contains something like the following:
#yandex-mail {
    min-width: 16px;
#yandex-mail .toolbarbutton-text { 
    float: right !important;
    display: inline-block !important;
    font-size: 13px;
    background: url(data:image/png;base64,iVB.............OCYII=) no-repeat left center;
#yandex-mail .toolbarbutton-icon { 
    display: none;

Why do we hide the icon and add the background? This is because if this is not done, then the blocks are always displayed as display: block , no matter what values ​​I set (by the way, can anyone know on this topic?) Therefore, I have to be so cunning.

Also faced with the issue of downloading content from other sites and parsing xml.
I quickly figured out the first one, I don’t have to go far: Request
But I had to tinker with the second one.

As we know, you can get dom xml of a document using several functions:
  • XMLHttpRequest - dropped because gave a cross-domain request error (maybe I did something wrong?)
  • DOMParser - but here, too, had to tinker

What is the actual fuss: as with getting the window , so here:
var {Cc, Ci} = require("chrome");
var parser = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
var dom = parser.parseFromString(xmlPrepare (text), "application/xml");

This is how creating extensions for Firefox is no different from creating plugins for jQuery :)

By the way, the final creation for this day: CustomYandexBar , is still under review. Sources , they have a lot of useful things.

If someone doesn’t like that I use “their” pictures, brand or the like. - write.

Also popular now: