www.roachfiend.com

Creating Firefox extensions


This article, while immensely enjoyable, has been updated. The newer version lies here: http://roachfiend.com/archives/2004/12/08/how-to-create-firefox-extensions/


Contents

  1. Learn By Example
  2. Dissecting the Bug
  3. Re-configuring your extension's installation
  4. Chrome is more than a shiny bumper
  5. Skin that cat
  6. Pack it up and try it out
  7. An easier way to re-build
  8. My Firefox got completely hosed up
  9. Ensure server compatibility
  10. Additonal help and information

Learn by example

Everyone has a good idea at one time or another to implement a new feature in a web browser. Well, with the goodness that is Mozilla Firefox, now you can do just that. You need to have a vague understanding of XUL and Javascript, but you certainly don't need to be a master of either. When I started, I knew nothing about either one, really. I had seen some bookmarklets here and there, and tried to figure out just how they worked. Well, that's how I made my first extension, BugMeNot.

I found bugmenot.com by chance one day, and it certainly made browsing web sites that required registration much easier. They even had a bookmarklet that would work "on the fly", more or less. I liked using it, but I'd prefer to have it within easier reach, say, a right click menu that I could tap at any time. Unfortunately, I had no idea how to do this, so I scoured ways of adding items to a right-click menu.

Xul Planet had a nice little tutorial which showed me the basics of creating a menu. The problem was that I wasn't making a new window, I wanted to add an item to a pre-existing menu. This gave me a good grounding in how XUL works, though. Mozilla also has a very handy DOM Window Interface reference.

So basically I needed to figure out exactly how extensions are packaged up, and how they work. Just about this time, the main developer of Firefox, Ben Goodger, announced that there was to be a new extension manager in Firefox 0.9, which would entail the use of a different packaging system. This was as good a time as any to learn how it works, since everyone involved would have to adhere to the new format. So, Ben wrote a guide to packaging extensions which would work for the new browser builds.


Back to top

Dissecting the Bug

Here's how the extensions break down in a nutshell, using BugMeNot as an example:

(You can download it here, just right-click and save, then you can follow along.)

bugmenot.xpi is the packaged extension. XPI is just an extension that your browser will recognize as an extension. In reality, it's just a zipped up file. So you can rename the XPI to ZIP or even JAR if you want, then open it up using an archive program, like 7-Zip or WinRAR. So, once that's opened up, you'll see:

  • chrome
  • default
  • install.js
  • install.rdf

Two folders and two files. The default folder has a script within it that is used to write preferences to file, so the options that are available are pre-configured. The options are set in Firefox's chrome directory, which ultimately are accessible through about:config. install.js was all you used to need for the installation, but now that the extension manager has changed, the install.rdf is used instead. Now, the install.js is used purely for earlier versions of Firefox/bird, Mozilla, and Netscape. If you want to make this extension solely for 0.9+ versions of Firefox, then you can omit this file if you'd like. I tend to keep it in because it only take a second to make, and assures a wide audience compatibility.

If you open up install.js, you'll see that it's very basic, in terms of what you need to modify to make your own install script:

// --- Editable items begin ---
extFullName: 'BugMeNot', // The name displayed to the user (don't include the version)
extShortName: 'bugmenot', // The leafname of the JAR file (without the .jar part)
extVersion: '0.5',
extAuthor: 'Eric Hamiter',
extLocaleNames: null, // e.g. ['en-US', 'en-GB']
extSkinNames: null, // e.g. ['classic', 'modern']
extPostInstallMessage: 'Success! Please restart your browser to finish the installation.'
// Set to null for no post-install message
// --- Editable items end ---
		

So all the hard work is done for you. I'm not going post the rest of the code, but you'll see quite a lengthy amount of work that is fully automated.

Now if you open install.rdf. you'll see this:

<?xml version="1.0"?>

<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">

	<Description about="urn:mozilla:install-manifest">

		<em:id>{987311C6-B504-4aa2-90BF-60CC49808D42}</em:id>
		<em:name>BugMeNot</em:name>
		<em:version>0.5</em:version>
		<em:description>Bypass compulsory web registration with the context menu
		via www.bugmenot.com.</em:description>
		<em:creator>Eric Hamiter</em:creator>
		<em:contributor>Michael Lidman</em:contributor>
		<em:contributor>Joseph Wain</em:contributor>
		<em:homepageURL>http://extensions.roachfiend.com</em:homepageURL>
		<em:iconURL>chrome://bugmenot/skin/bugmenot.png</em:iconURL>
		<em:aboutURL>chrome://bugmenot/content/about.xul</em:aboutURL>
		<em:file>
			<Description about="urn:mozilla:extension:file:bugmenot.jar">
				<em:package>content/bugmenot/</em:package>
				<em:skin>skin/classic/bugmenot/</em:skin>
			</Description>
		</em:file>

		<em:targetApplication>
			<Description>
				<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
				<em:minVersion>0.7</em:minVersion>
				<em:maxVersion>0.9+</em:maxVersion>
			</Description>
		</em:targetApplication>

	</Description>

</RDF>
		

Back to top

Re-configuring your extension's installation

Ok, whoa.. what is all this crap? The first thing you'll see is the <em:id> tag. This is your very own generated id that will separate your extension from anyone elses. The way to make it is to use a program called guidgen, brought to us by Microsoft. How deliciously ironic. Or am I misuing the term irony here? Whatever. So download that, then you'll see this when you run it:

GUIID Screenshot

So choose 4. Registry Format, then hit New GUID a few times for good measure, then Copy. That's it, now your new spiffy id is in your clipboard. Replace the old one with this, and you're set.

Name, version, description, creator, and homepageURL are all self-explanatory. The iconURL and aboutURL are what shows up if someone right-clicks your extension and chooses "About Extension...". You can leave these blank, it's not mandatory, but it's nice to have a little flash every now and then.

Underneath file, this is standard stuff. Just replace all instances of "bugmenot" with your extension name. This is where the installation will try and find your files and folders. If you have any icons, you'll include the skin folder. Again, it's not mandatory.

Target application is what you're gearing this for. The ec8030f7... is unique to Firefox, so leave that alone. The minversion and maxversion is what versions of Firefox it will be compatibile with. There was a big stink about this recently, since the developers introduced 0.9.1, shortly after telling us to make sure and only put a maxVersion of 0.9. This does not compute. So I recently modified mine to go to 0.9+, which will hopefully take care of all versions to 1.0. We'll see.


Back to top

Chrome is more than a shiny bumper

Ok, now open up the chrome folder. In there you'll find another archived file, bugmenot.jar. Open it up and extract the files. You'll now have content and skin folders. Let's explore content first. In there, we have a bugmenot folder, and under that, these files:

  • about.xul
  • bugmenotOverlay.xul
  • contents.rdf

about.xul is the file you see when you click "About BugMeNot..." in the extensions menu. It's pretty self-explanatory, and you'll see that a nice man named Jed Brown wrote the template for it, so all the hard work has been done for you. Again. So just fill out the info, and that's it.

bugmenotOverlay.xul is what makes things happen when you right-click. It's the brains behind the outfit, so to speak. And you'll be amazed at how simple it is.

<?xml version="1.0"?>

<overlay id="BugMeNotOverlay"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<script type="application/x-javascript" src="chrome://bugmenot/content/">

</script>

<popup id="contentAreaContextMenu">
  <menuitem id="bugmenot" label="BugMeNot" accesskey="N" insertafter="context-stop" oncommand="BugMeNot();"/>
</popup>

</overlay>
		

So all it says to do is to insert the javascript file, which is called bugmenotOverlay.js and to create a context menu entry called BugMeNot. accesskey="N" makes it look like BugMeNot, since "N" was the first letter that wasn't taken by any other options. insertafter="context-stop" places the option directly underneath the Stop label. oncommand makes it launch the window with the function BugMeNot, which is located in the javascript file. It's basically a really quick chain reaction.

The javascript file has become pretty complex since the release of version 0.4; now it has the ability to copy and insert text on-the-fly, use regular expressions, and self-close its pop-up window, which is a little complex for this tutorial, so we'll focus on the the rest of the packaging. Go ahead and look through the script if you'd like, and you might be able to piece together what makes it work.

Now for contents.rdf. This is the file that tells the browser where to store this overlay information. Here's what it looks like:

<?xml version="1.0"?>

<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:chrome="http://www.mozilla.org/rdf/chrome#">

	<RDF:Seq RDF:about="urn:mozilla:package:root">
		<RDF:li RDF:resource="urn:mozilla:package:bugmenot"/>
	</RDF:Seq>

	<RDF:Seq RDF:about="urn:mozilla:overlays">
		<RDF:li RDF:resource="chrome://browser/content/browser.xul"/>
		<RDF:li RDF:resource="chrome://navigator/content/navigator.xul"/>
	</RDF:Seq>

	<RDF:Seq RDF:about="chrome://browser/content/browser.xul">
		<RDF:li>chrome://bugmenot/content/bugmenotOverlay.xul</RDF:li>
	</RDF:Seq>

	<RDF:Seq about="chrome://navigator/content/navigator.xul">
		<RDF:li>chrome://bugmenot/content/bugmenotOverlay.xul</RDF:li>
	</RDF:Seq>

	<RDF:Description RDF:about="urn:mozilla:package:bugmenot"
		chrome:displayName="BugMeNot 0.5"
		chrome:author="Eric Hamiter"
		chrome:authorURL="mailto:ehamiter@gmail.com"
		chrome:name="bugmenot"
		chrome:extension="true"
		chrome:description="Bypass compulsory web registration with the
		context menu via www.bugmenot.com.">
	</RDF:Description>

</RDF:RDF>
		

You'll notice the address chrome://browser/content/browser.xul up there. This is mozilla's internal frame of reference. browser is the actual browser, and navigator works for non-Firefox builds, like Netscape or Mozilla. The only part you'd need to modify is the descriptions. The rest of it just implements the extension into the browsers.


Back to top

Skin that cat

Now let's backtrack to the skin folder. In it, we'll find a few more folders: classic and bugmenot. This is just the traditional layout, and if it ain't broke, then hey, don't fix it. In bugmenot, we find three files: bugemnot.png, bugemnotb.png, and a contents.rdf file.

bugemnot.png:

BugMeNot small icon

bugemnotb.png:

BugMeNot large icon

These are called from about.xul mentioned previously, for use in the extension menu and the about menu. contents.rdf simply maps out the paths to the skin files, so you don't need to change anything there.


Back to top

Pack it up and try it out

So now that you see how the files work, and where they're packaged, you can modify them to your whims, and try out new things. Once you modify them, just pack them up in reverse order. Using your archive program, you would navigate back up to the chrome folder, and add content and skin into a zipped archive, then rename it to extension.jar. After that, navigate up another folder, and add chrome, install.rdf, and install.js into another zipped archive, then rename it to extension.xpi.

You're ready to test it out in your browser now. Open up Firefox, and hit CTRL-O, or Open File. Load up your xpi file, and say yes to the installation. Restart Firefox, and hopefully you'll see your new extension in the menu, and it does whatever you had hoped it would do.


Back to top

An easier way to re-build

After a while, it gets tiresome to select your files, your folder, archive them, rename them, move them, delete them, rename them... you get my point. If you use 7-Zip's command line, you can have this all fully automated. Here's what you need to do:

Copy C:\Program Files\7-Zip\7z.exe to C:\WINDOWS\system32 (This will put 7z.exe in your system's path, which will make it accessible from the command prompt).

It's a good practice to build your extensions somewhere far away from random scripts and clutter, so create a new folder somewhere and call it whatever your extension is named. Make sure it matches the internal .jar file that you previously referenced in your install.rdf file. You can always rename the final xpi to something more intricate afterward, but for packaging, it's best to keep it simple.

Copy the following script and paste it in a text editor and save it as build.bat in your newly-made folder:

set x=%cd%
md build\chrome
cd chrome
7z a -tzip %x%.jar * -r -mx=0
move %x%.jar ..\build\chrome
cd ..
copy *.rdf build
copy *.js build
xcopy /S defaults build\defaults\
cd build
7z a -tzip %x%.xpi * -r -mx=9
move %x%.xpi ..\
cd ..
rd build /s/q
		

Now, you can build or modify your extensions easily. Just use the new folder as your base of creation, so that would contain the install files and chrome folder. Whenever you want to create your new file, just double-click build.bat, and your new extension will pop out in the same folder. Each time you use the build.bat script, it will delete your old file and create a new one.


Back to top

My Firefox just got completely hosed up

Worst case scenario: upon restarting Firefox, it hangs with a "Firefox is still installing an extension, this may take a minute...". This means you borked it up somehow. Don't panic! A super easy way of uninstalling it without hosing the rest of your shit up is as follows:

Start » Program Files » Mozilla Firefox » Mozilla Firefox (Safe Mode)

Then go to Tools » Extensions » [right-click on your extension] » Uninstall

Restart Firefox, and it'll be gone. Then modify your files and try again.


Back to top

Ensure server compatibility

If it works, and you want to put it on your web server, but find out that it won't install directly, and your browser is treating it as "Save File As.." then you need to configure your mime types or modify your .htaccess file. I'm not going to go into detail about those if you're not sure what they are, that's what Google is for. But if you have no problem modifying these files, here's the information you need to add:

If you're modifying mime types:

application/x-xpinstall .xpi

or this for .htaccess:

AddType application/x-xpinstall .xpi

And you should be set.


Back to top

Additional help and information

If you want to take a look at any other files I've created, they're on my main extensions page, and I'll list them directly here for convenience as well:

They're both about as simple as this one. If you'd like to create your own links menu, then Goon Menu is good to learn from. And if you'd like to put a certain snippet of Javascript on every page, Allow Right-Click is the way to go.

The Mozillazine extensions forum is a great place to learn more about creating extensions. They're under heavy loads from time to time, so they might be down when you read this, but try back again if they are, because they have a lot of useful information there.

Loads of other useful extensions can also be found at Mozilla Update, and there's another place called My Extensions Mirror that has forums as well as tons of extensions.

Well, that's it. Hopefully this has been helpful to at least a few ambitious people, as well as a look into what goes into making one for the non-technical types. So get off your ass and make something useful!

Back to top