(I'm too busy to blog right now, so I'm just posting something I had written previously and was hoping to improve but didn't have the time to. I figured it was better to publish it as such—and rough at the edges—than to postpone indefinitely.)
Here's a quick HOWTO on how to add a personal menu to Firefox. The point, here, is not to publish a Firefox extension (there's already abundant doc on this subject) but, rather, to modify one's profile to personalize the menu system in a quick and (relatively) easy way. What follows should work on every operating system supported by Firefox, although I've only tested it on Unix; also, I will assume Firefox 3. I assume the reader has some basic knowledge of XML, and, of course, if you want to do anything useful in your menu you must know some JavaScript.
First, create a directory in which the menu files will reside. I
suggest something like ~/firefox/mymenu/
and I will
assume that name in what follows. It will be an easy thing to copy
the content of this directory to other machines to distribute the menu
extension on them; also, it will be relatively easy to turn this
working directory into a bona fide (i.e., .xpi
)
Firefox extension, although I won't cover this here.
Next, create a file called install.rdf
inside the
menu's working directory
(so, ~/firefox/mymenu/install.rdf
). Model it as
follows:
<?xml version="1.0" encoding="utf-8"?> <RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:NS1="http://www.mozilla.org/2004/em-rdf#"> <RDF:Description RDF:about="urn:mozilla:install-manifest"> <NS1:id>mymenu@local</NS1:id> <NS1:name>My Local Menu Extension</NS1:name> <NS1:version>0.0.0.0.0.1</NS1:version> <NS1:creator>John Doe User</NS1:creator> <NS1:description>A local menu loaded with goodies!</NS1:description> <NS1:targetApplication> <RDF:Description> <NS1:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</NS1:id> <!-- firefox --> <NS1:minVersion>3.0</NS1:minVersion> <NS1:maxVersion>3.0.*</NS1:maxVersion> </RDF:Description> </NS1:targetApplication> </RDF:Description> </RDF:RDF>
RDF is a bit arcane
(see here if you
want to learn more about it), but you don't need to understand it in
order to write this: basically, what matters is that this files
specifies a number of important properties of the extension we are
defining (yes, we are defining a Firefox extension, albeit
one which might remain purely local). The most important such
property is NS1:id
(actually the property's name is
really http://www.mozilla.org/2004/em-rdf#id
, but that
doesn't matter here), which specifies a unique identifier for the
extension: here I am calling it mymenu@local
, which is
fine if you don't expect it to ever become public—if you do,
it's probably better to call it something
like mymenu@mydomain.example.tld
where mydomain.example.tld
is some domain you own.
Another possibility is to use a UUID such
as {da0bfd29-39a2-44d5-8b58-8e9badbbec7a}
(you can use
the uuidgen
program to create one; remember to enclose it
in curly braces, as shown here), although UUIDs are
obviously more difficult to type and remember. Other properties
required in install.rdf
are NS1:name
(the
human-readable name of the extension, as it will appear in the
extension list) and NS1:version
(the extension's version
number; there
are sophisticated
rules to compare version numbers, but I suggest sticking to
something very simple, i.e., a period-separated list of numbers which
will be ordered component by component). The NS1:creator
(creator's name) and NS1:description
(one-line
description of the extension) properties are optional, but I suggest
filling them in anyway. Finally, NS1:targetApplication
indicates which programs the extension is made to work with:
here, {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
means Firefox
, and the NS1:minVersion
and NS1:maxVersion
specify the minimal and maximal
accepted versions of Firefox, here anything in the form 3.0 or
3.0.something will match (I suggest leaving it as such, and
increasing NS1:maxVersion
after testing with 3.1 when it
comes out). If you mean to make your menu also work for Thunderbird,
add another NS1:targetApplication
block (don't reuse the
previous one!) and
specify {3550f703-e582-4d05-9a08-453d09bdfdc6}
as NS1:id
. For more information about what can go in
the install.rdf
file,
see here
on developer.mozilla.org
and here
on mozillazine.org
.
After you've written the install.rdf
file, the other
important file which needs to be created
is chrome.manifest
(details
about it here). Create it in the same directory
(so ~/firefox/mymenu/chrome.manifest
) and model it as
follows:
content mymenu chrome/content/ overlay chrome://browser/content/browser.xul chrome://mymenu/content/mymenu.xul
What we're saying here is:
- The
chrome://mymenu/content/
URL shall now refer to the files in thechrome/content/
subdirectory (i.e.,~/firefox/mymenu/chrome/content/
). This is important, because residing in thechrome://
URL scheme gives the files extra privileges (the JavaScript they contain can freely access all Mozilla components, for example). If you want to be able to access the files with theirchrome://
URL by typing it directly in the URL box (or linking to them, or such things), you need to addcontentaccessible=yes
at the end of the first line. - The
chrome://browser/content/browser.xul
file shall be modified by the overlay found inchrome://mymenu/content/mymenu.xul
(and which, according to the previous line, will reside in~/firefox/mymenu/chrome/content/mymenu.xul
). This is important, becausechrome://browser/content/browser.xul
is the master URL for the Firefox browser (in a way, it is the browser), so to define a new menu you need to modify it by overlaying it. If you need to similarly overlay Thunderbird, the master URL ischrome://messenger/content/messenger.xul
. (More details about overlays can be found here and here.)
Now we need to create
the ~/firefox/mymenu/chrome/content/mymenu.xul
file with
the actual menu. Here is a template for it:
<?xml version="1.0" encoding="utf-8"?> <overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"> <script type="application/javascript"> // <![CDATA[ function StupidAlert() { alert("This is a stupid alert!"); } // ]]> </script> <menubar id="main-menubar"> <menu id="mymenu-menu" label="My Menu" accesskey="M"> <menupopup> <menuitem label="Nop" accesskey="N" /> <menuitem label="Stupid alert" accesskey="S" oncommand="StupidAlert()" /> </menupopup> </menu> </menubar> </overlay>
This is, of course, an utterly stupid example, merely intended to
demonstrate how the file can be written: here, we overlay the Firefox
main menubar (the main-menubar
is the key, here) with a
new menu which is labeled My Menu
(the first ‘M’
will be underlined and will serve as access key), which pops up with
two items: the first is labeled Nop
and does absolutely
nothing, and the second is labeled Stupid alert
and runs the
JavaScript function StupidAlert()
defined above (this is
contrary to Mozilla good practice which demands that JavaScript code
be separated from the XUL structure, and also that
user-readable labels be separated so they can be i13zed, but when
defining a personal menu one typically does not care about such
practices).
The language in which this file is written is an instance
of XML
called XUL:
to learn more about XUL you can start
with this
excellent tutorial, but you can also learn it the quick-and-dirty
way by trying to imitate the Firefox XUL itself, which
you can find in a file called browser.jar
(you can
extract it with unzip
) somewhere in your Firefox
installation directories (a typical directory under Linux would
be /usr/share/firefox/chrome/browser.jar
,
but YMMV). Similarly, if
you want your menu to do something useful, you need
some JavaScript,
and, more probably, you need some Mozilla-specific JavaScript: you'll
find many examples of
that here
(you might also
find this
page useful if you need to access Firefox windows or their
content).
But before you start coding away, there is one last thing that
needs to be done: now that a basic menu has been created
in ~/firefox/mymenu/
, you still need to tell Firefox
about its existence! To do that, stop all running Firefoxen, then go
in the extensions/
subdirectory inside your profile
directory (on Unix, that would typically
be ~/.mozilla/firefox/*.default/extensions/
), and create
a file called mymenu@local
(use the same name as
the NS1:id
of your extension, even if that means using
curly braces around a UUID) which just contains one
line:
~/firefox/mymenu
i.e., name of the directory in which your extension (and
importantly, the install.rdf
and chrome.manifest
files) resides. When Firefox is next
launched, it should tell you that a new extension has been installed
and the menu should be visible. After that, whenever you modify
something in the ~/firefox/mymenu/
directory, increment
the version number in install.rdf
and restart Firefox
(while this is not always strictly necessary, it is definitely good
practice to avoid confusion).