David Madore's WebLog: How to add a personal menu to Firefox

[Index of all entries / Index de toutes les entréesLatest entries / Dernières entréesXML (RSS 1.0) • Recent comments / Commentaires récents]

↓Entry #1570 [older| permalink|newer] / ↓Entrée #1570 [précédente| permalien|suivante] ↓

(Friday)

How to add a personal menu to Firefox

(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 the chrome/content/ subdirectory (i.e., ~/firefox/mymenu/chrome/content/). This is important, because residing in the chrome:// 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 their chrome:// URL by typing it directly in the URL box (or linking to them, or such things), you need to add contentaccessible=yes at the end of the first line.
  • The chrome://browser/content/browser.xul file shall be modified by the overlay found in chrome://mymenu/content/mymenu.xul (and which, according to the previous line, will reside in ~/firefox/mymenu/chrome/content/mymenu.xul). This is important, because chrome://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 is chrome://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).

↑Entry #1570 [older| permalink|newer] / ↑Entrée #1570 [précédente| permalien|suivante] ↑

[Index of all entries / Index de toutes les entréesLatest entries / Dernières entréesXML (RSS 1.0) • Recent comments / Commentaires récents]