How to pack a Firefox extension from scratch

1.8k views Asked by At

I am new to Firefox extensions and i would like you to help me pack one extension that i build and send it to some friends to test it.

My Extension is about to "block" some URLs. Which means that if someone tries to join "facebook.com" my extension should redirect him to "www.google.com"

the code is below.

const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import('resource://gre/modules/Services.jsm');
var urls_block = [ 
    //If URLs contain any of these elements they will be blocked or redirected,
    //  your choice based on code in observer line 17
    'www.facebook.com',
     'www.apple.com'
];
var redir_obj = {
    'www.facebook.com': 'http://www.google.com/',
    'www.apple.com': 'http://www.samsung.com'
}
var observers = {
    'http-on-modify-request': {
        observe: function (aSubject, aTopic, aData) {
            console.info('http-on-modify-request: aSubject = ' 
                          + aSubject + ' | aTopic = ' + aTopic + ' | aData = ' + aData);
            var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
            var requestUrl = httpChannel.URI.spec.toLowerCase();
            for (var i=0; i<urls_block.length; i++) {
                if (requestUrl.indexOf(urls_block[i]) > -1) {
                    //httpChannel.cancel(Cr.NS_BINDING_ABORTED); //this aborts the load
                    //Can redirect with this next line, if don't want to redirect and
                    //  just block, then comment this line and uncomment the line above:
                    httpChannel.redirectTo(Services.io.newURI(redir_obj[urls_block[i]],
                                               null, null));
                    break;
                }
            }
        },
        reg: function () {
            Services.obs.addObserver(observers['http-on-modify-request'],
                                           'http-on-modify-request', false);
        },
        unreg: function () {
            Services.obs.removeObserver(observers['http-on-modify-request'], 
                                           'http-on-modify-request');
        }
    }
};
function install() {}
function uninstall() {}
function startup() {
    for (var o in observers) {
        observers[o].reg();
    }
}

function shutdown(aData, aReason) {
    if (aReason == APP_SHUTDOWN) return;

    for (var o in observers) {
        observers[o].unreg();
    }
}

Big thanks to @Noitidart for his enormous help.

So i want to pack this code for Firefox Extension. Could someone show me how to do it or any example?

Thanks a lot for your time helping me here.

1

There are 1 answers

2
Makyen On BEST ANSWER

At a minimum, you will need to create an install.rdf file and a chrome.manifest file. Go through those links, you are going to need to make some choices (e.g. what to call your extension, an <em:id>, etc.).

In addition, it appears that you are making a bootstrap/restartless add-on and should call the file containing the code you included in the question: bootstrap.js

.xpi file format (Extension Packaging):

The .xpi files that are used as containers for Mozilla (Firefox, Thunderbird, etc.) extensions are merely zip compressed archives that have had the file extension changed to .xpi. The files start in the root directory of the zip compressed archive (i.e. there is first level directory to contain the files). The files must either be uncompressed, or compressed using the "Deflate" algorithm. Using other compression algorithms will result in your .xpi file not loading and a popup being shown that the add-on is corrupt.

The contents of the archive could be only a few files to any number of files. At a minimum, you have an install.rdf and a chrome.manifest file. There will almost always be at least one additional file (if not many additional files).

My very simple Bootstrap/Restartless extension, Print Button is Print (changes the print button to print instead of print preview), has the following structure:

Archive contains:
  bootstrap.js
  chrome/
  chrome/content/
  chrome/content/options.xul
  chrome/skin/
  chrome/skin/printer-typeC128.png
  chrome/skin/printer-typeC32.png
  chrome/skin/printer-typeC48.png
  chrome/skin/printer-typeC64.png
  chrome.manifest
  install.rdf
  license.txt
Total 12 entries (42360 bytes)
  • There are the required install.rdf and chrome.manifest files.
  • The file bootstrap.js is required for Bootstrap/Restartless extensions. It contains the code that is run when the extension is installed, removed, enabled, disabled, or upon Firefox startup or shutdown. This extension is simple enough such that all the JavaScript code is contained in bootstrap.js.
  • There is a file chrome/content/options.xul which is a XUL definition of the options dialog.
  • The license.txt just explains that the extension was released under the Mozilla Public License, v2.0.
  • The .png files are the icon for this extension at various resolutions.

The install.rdf file for Print Button is Print is (all instances of PrintButtonIsPrint should be changed to something for your extension which you define in your chrome.manifest file; All of them you could just delete from the instal.rdf file if you wanted to as you have no options dialog, or icons defined (yet).):

<?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>[email protected]</em:id> <!-- MUST be unique to your extension. -->
    <em:version>1.0.1</em:version>
    <em:type>2</em:type>
    <em:name>Print Button is Print</em:name> <!-- Should be unique to your extension. -->
    <em:bootstrap>true</em:bootstrap> <!-- Indicate that the extension is restartless -->
    <em:unpack>false</em:unpack>
    <em:description>Makes the Print Button print the page instead of presenting a print preview. Adds the option of using shift-left-click and/or ctrl-left-click for Print Preview (both enabled by default).</em:description>
    <em:creator>Makyen</em:creator>
    <!-- No about.
    <em:aboutURL>chrome://PrintButtonIsPrint/content/about.xul</em:aboutURL>
    -->
    <em:optionsURL>chrome://PrintButtonIsPrint/content/options.xul</em:optionsURL>
    <em:iconURL>chrome://PrintButtonIsPrint/skin/printer-typeC48.png</em:iconURL> 
    <em:icon64URL>chrome://PrintButtonIsPrint/skin/printer-typeC64.png</em:icon64URL> 
<!--Firefox-->
    <em:targetApplication>
        <Description>
            <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
            <em:minVersion>29.0</em:minVersion>
            <em:maxVersion>37.*</em:maxVersion>
        </Description>
    </em:targetApplication>
    </Description>
</RDF>

The chrome.manifest is (both instances of PrintButtonIsPrint should be changed to something for your extension):

content      PrintButtonIsPrint                                         chrome/content/
skin         PrintButtonIsPrint               classic/1.0               chrome/skin/

To create the .xpi file I use a batch file, which uses a combination of DOS and Unix/Linux (actually Cygwin) commands:

mkxpi.bat:

rm -f [email protected]
zip -1 -r [email protected] * [email protected]
pause

This removes any old version of the .xpi file. It then creates a new .xpi file using, -1, minimal compression (speed of access is more important than saving space) and containing all files and subdirectories ** but ignoring all the files in the xpi.ignore text file [email protected]. Ignoring files is used because I have other things in the directory (e.g. .git directory, .bak files auto-created from editor, etc.). Once the .xpi file is created the script executes pause so that I can verify which files were included, that there were no errors, etc. instead of just having the window disappear and assuming that everything is fine.

My xpi.ignore file is a bit long, as it accumulates cruft from various projects and is rarely cleaned out:

*.com
*.class
*.dll
*.exe
*.o
*.so
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
*.log
*.sql
*.sqlite
*.svg
*/.DS_Store
*/.DS_Store?
*/._*
._*
*/.Spotlight-V100
.Spotlight-V100
*/.Trashes
.Trashes
*/ehthumbs.db
*/Thumbs.db
*.ORIG
*.bak
*OLD*
OLD/*
*/OLD/*
*.OLD
*.OLD[0-9]
*/OLD/*
*/OLD[0-9]/*
*.unknown
*.unknown[0-9]
*.updated
*.updated[0-9]
*/Copy *
*/OLD
*/OLD*
*/OLD[0-9]
*/OLD[0-9][0-9]
*/test/*
*/not in xpi/*
*/tmp
*.tmp
*/foo
*.foo
*checkpoint
.git
*/.git
.gitignore
*/.gitignore
xpi.ignore
mkclean.bat
mkclean.bat.DONTRUN
mkxpi.bat
*.xpi
*/devtools-toolbox-window.ico
*/devtools-webconsole.ico
*/JSConsoleWindow.ico
*/main-window.ico
*/places.ico
*/viewSource.ico

Installing extensions:

As to installing extensions (i.e. the .xpi file), it can be a simple matter of dragging and dropping it onto a Firefox window running the profile in which you desire it installed. For development/testing, you can have the extension be in a directory on your local drive by using a Firefox extension proxy file (create a file named as the extension's <em:id> in the profile's extensions directory containing one line with the complete path to the directory containing the extension's files). Depending on what your goal is (one profile, all profiles, all users, which OS, etc.), there are other options as to how to install extensions.

This answer was mostly copied from my answer here.