Chapter 5: Let's build a Firefox extension

  • Revision slug: Firefox_addons_developer_guide/Let's_build_a_Firefox_extension
  • Revision title: Chapter 5: Let's build a Firefox extension
  • Revision id: 76702
  • Created:
  • Creator: Lebedel.delphine
  • Is current revision? No
  • Comment no wording changes

Revision Content

{{ Fx_minversion_header("3") }} {{ Draft() }}

The chapters so far have each focused on technologies in isolation—XUL, JavaScript, CSS, and XPCOM. In this chapter, we’ll discuss how to put them together to actually build an extension.

Setting up your development environment

Change your preferences for more efficient development

Before you get to work developing your extension, you’ll want to change some of your Firefox preferences. This isn’t a requirement for extension development, but I recommend it as a way to work more efficiently.

To make these changes, type about:config into Firefox’s location bar and open the preferences window; find the preferences listed in Table 1 and double-click on them to set them accordingly. Some of these preferences do not exist—to create them, right-click, select “New>Boolean”, and type in the name and set the value accordingly.

Install the DOM Inspector

The DOM Inspector is an extension that lets you examine HTML and XUL DOM tree structures, JavaScript objects and CSS properties, etc. This is not required for developing extensions, but it is handy to have around. If you don’t see “DOM Inspector” under the Tool menu in Firefox after trying to install it, you’ll need to re-install it. Select “custom install” from the “setup type” screen, and under “additional components,” select “DOM Inspector.” Install extensions for Firefox 3 from Mozilla Add-ons (https://addons.mozilla.org/).

Create a development profile

If you want to partition your everyday browsing environment from your development environment in Firefox, set up a second profile for development1.

Table 1: Preferences to set for developing extensions

Preference

Description

Value

nglayout.debug.disable_xul_cache

Ordinarily, Firefox will cache XUL documents after they have been read in once, to speed subsequent displays. Disabling this cache forces XUL documents to be reloaded any every time they are displayed

True

browser.dom.window.dump.enabled

Enables use of the dump method for debugging. Refer to the “JavaScript debugging methods” sidebar.

True

javascript.options.showInConsole

Outputs JavaScript errors to the error console

True

javascript.options.strict

Enforces strict error output from JavaScript

True

 

Developing extensions: What you need to know

Let’s delve into chrome, something you’ll need to know about in order to develop extensions.

Chrome

What is chrome?

“Chrome”is the word used to describe all the GUI structural elements that go into an XUL application. For example, in the Firefox browser window, everything other than the web page being displayed in the content area is chrome. Extensions also can be considered XUL applications with chrome.

Three kinds of packages make up chrome

The content package

This package is used to contain the main XUL and JavaScript source files. Most extensions consist of a single content package2

The locale package

This package is used to contain language data that can be translated. To make an extension’s GUI support multiple languages, you can include multiple locale packages, one for each language.

The skin package

This is used to include source files used as visual elements in the GUI, including style sheets and images. Most extensions include only one skin package, but you can include multiple skin packages to allow the GUI to change with different themes.

Chrome URL

Use a file called a “chrome manifest” to register chrome packages with Firefox and start using them. To register a package, you use a special URI scheme called a “Chrome URL” to represent the path to the file. Chrome URLs are structured as:

chrome://package name/package type/relative path to source file

For example, the Chrome URL for the Firefox browser window is as follows:

chrome://browser/content/browser.xul

Here, the package name is “browser”, and the file “browser.xul” is located inside the “content” package. Source files represented through chrome URLs run with UniversalXPConnect privileges; even if they haven’t been granted “use XPConnect as local file” privileges, as discussed in Chapter 4, they will be able to use XPCOM functions (Figure 1).

[figure]

Run with privileges

---

Register chrome

---

Extension

chrome manifest

Chrome

content package

locale package

skin package

[/figure]

Figure 1: Chrome packages and chrome registration

Cross-package overlays

The “overlay” technique introduced in Chapter 3 required the use of the xul-overlay instruction in the XUL document that is the overlay target. It is also possible to coerce an overlay without using the xul-overlay instruction in the XUL document—this is called a “cross-package overlay” (Figure 2). In fact, you need to use cross-package overlays to append buttons or menus to the Firefox browser window. Use the chrome manifest to invoke a cross-package overlay.

[figure]

Normal overlay

Adding the xul-overlay instruction to the target XUL document overlays it with another XUL document

--

Cross-package overlay

(all on one line)

Adding a cross manifest permits one XUL file to overlay another without any xul-overlay instruction.

[/figure]

Figure 2: Invoking a cross-package overlay

Conclusion

This has been a brief introduction to chrome that probably leaves a lot of unanswered questions. If you keep in mind the following three points at the very least, the next section, Developing a Simple Extension, should help flesh out your understanding.

1. An extension’s GUI, or chrome, can consist of three kinds of package: content, locale, and skin.

2. Use a cross-package overlay on top of browser.xul to add a button or menu item to the Firefox browser window.

3. The chrome manifest plays two important roles: it registers the contents of the chrome packages, and invokes the cross-package overlay.

Developing a Simple Extension: Hello World

In this section, we will write an extremely simple “hello world” extension that only displays the time.3

Phase 1: test install

Our first step will be to perform a test installation consisting of the minimum code needed to add a new menu item to the Tools menu (Figure 3).

Source file structure

First set up the work folders you’ll use to organize your extension’s source files. The folder can be wherever you want, but for the purposes of this guide, we’ll assume it’s at C:\extensions\helloworld . Create folders and files in your work folder as shown in Figure 4. The purpose of each file created during Phase 1 is explained in Table 2.

Create install manifest

Fill in the install.rdf file as shown in Listing 1.

Create the chrome manifest

Fill in the chrome.manifest file as shown in Listing 2.

Register the content package (content instruction)

Line 1, beginning content, contains the code used to register the chrome’s content package. Here, helloworld is a package name, and content/ is a relative path to the folder where the source file is stored. Registering the content package makes it so that the overlay.xul file in the content folder can be accessed using the chrome URL, chrome://helloworld/content/overlay.xul.

XUL cross-package overlay (overlay instruction)

Line 2, beginning overlay, contains the code used to invoke the cross-package overlay, overlaying the Firefox browser window, at chrome://browser/content/browser.xul with chrome://helloworld/content/overlay.xul.

Figure 3: Menu after Phase 1 complete

Figure 4: Folder structure used in Phase 1

Table 2: How files are used in Phase 1

File name

Role

install.rdf

Called the install manifest, this gives basic information about the extension, and is required in order for the extension to be installed in Firefox.

chrome.manifest

This is the chrome manifest described in the earlier section. Registers packages and invokes cross-package overlays.

overly.xul

XUL file that will be overlaid on the Firefox browser window, adding buttons, menu items, etc.

clock.xul

clock.js

The XUL to display a clock in the window, and the JavaScript to control its operation (these files will be used in Phase 2).

Finding overlay merge points

The next step is to find the “merge points” where the overlay document will insert its content into the base document. There are a number of ways to go about this; here, we’ll use the DOM Inspector (Figure 5).

1. Open the DOM Inspector by selecting the Tools > DOM Inspector menu item.

2. Type chrome://browser/content/browser.xul into the URL input field at the top, then click the Inspect button. A browser pane will appear in the lower part of the window.

3. Click the spots numbered 1 and 2 in Figure 5, in that order; the menu element (3) will then be selected.

4. Expand spot 3; this will disclose the area numbered 4, with several menuitem elements. The menupop element 4 contains the merge point where you’re going to be adding a new menu item; you can see that it has the id menu_ToolsPopup.

5. Look through the menuitem elements within this menupop element and determine where you want to add your new element. For the time being, let’s locate it in the spot numbered 5.

Figure 5: Finding merge points for overlays using the DOM Inspector

Listing 1: Content for install.rdf

<?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">

/* Unique ID for extension.

Can be in e-mail address format or GUID format */

<em:id>helloworld@xuldev.org</em:id>

/* Indicates that this add-on is an extension */

<em:type>2</em:type>

/* Extension name displayed in Add-on Manager */

<em:name>Hello, World!</em:name>

/* Extension version number.

There is a version numbering scheme you must follow */

<em:version>0.1</em:version>

/* Brief description displayed in Add-on Manager */

<em:description>My first extension.</em:description>

/* Name of extension’s primary developer. Change to your name */

<em:creator>Gomita</em:creator>

/* Web page address through which extension is distributed */

<em:homepageURL>http://www.xuldev.org/helloworld/</em:homepageURL>

/* This section gives details of the target application

for the extension--in this case, Firefox 2 */

<em:targetApplication>

<Description>

<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>

<em:minVersion>2.0</em:minVersion>

<em:maxVersion>2.0.0.*</em:maxVersion>

</Description>

</em:targetApplication>

</Description>

</RDF>

Listing 2: Content for chrome.manifest

content helloworld content/

overlay chrome://browser/content/browser.xulchrome://helloworld/content/overlay.xul

Putting overlays on the browser window

Now that you know the merge point for your overlay, you can create the overlay.xul file that will insert it (Listing 3). In line 4, we identify the merge point as the menupop element; in lines 5–7, we have the code that adds the new menu item. The insertbefore attribute determines where the element gets added.

Test install and operational check

This brings us to the point where we finally install our Hello World extension. Using the normal XPI-style installer would just be added trouble in this case, so we won’t use it. For source files under development, we do test installs.

Placing pointer files

First, navigate to the profile folder of the currently active profile4, and open the extensions folder within it. Create a new file there with the name you used as the Hello World extension ID in the install manifest, helloworld@xuldev.org. Set the contents of that file to be the full path to the work folder for your extension source files, C:\extensions\helloworld.

Listing 3: Additional content for overlay.xul

1. <?xml version="1.0"?>

2. <overlay id="helloworldOverlay"

3: xmlns="http://www.mozilla.org/keymaster/gat...re.is.only.xul">

4: <menupopup id="menu_ToolsPopup">

5: <menuitem id="helloworldMenuitem"

6: label="Hello, World!"

7: insertbefore="sanitizeSeparator" />

8: </menupopup>

9: </overlay>

Listing 4: Additional content for overlay.xul

oncommand="window.openDialog('chrome://helloworld/content/clock.xu...rscreen,modal');"

 

Phase 2: Adding a function to display the time

In Phase 2, we will make it so that selecting the Hello World menu item we created in Phase 1 will display a window with the time (Figure 6).

Adding an event handler

First, we add an event handler to the menu item that will open the window (Listing 4).

Clock handler

Create the window that displays the clock (Listing 5) and its controller (Listing 6). We’ve already covered the dialog element in Chapter 3. In this case, we only want to display an OK button, so we set the buttons attribute to accept. Within the window are a label element and textbox element, wrapped in a hbox element so that they are arranged horizontally.

Operations check

Perform an operations check to make sure that your changes to the source file are correct. At this point, it’s important that you disabled the XUL cache as discussed in the earlier section, “Setting up your development environment.” Assuming you’ve done that, you’ll be able to confirm the changes to overlay.xul and clock.xul without the bother of relaunching Firefox or reinstalling the extension. If the browser.xul file, which is the target of the overlay in overlay.xul is being read in again, the changes will be reflected, and you’ll be able to see the changes by opening a new browser window. The files clock.xul and clock.js will be read in every time you open the clock window, so just re-opening the clock window once will suffice to check them. During development, it’s important to minimize the number of steps between revising a source file and running an operations check against those changes. The sidebar “Operations checks on revised source files” discusses the general procedure to follow from source-file revision to operations check.

Figure 6: Clock window produced by Phase 2

Listing 5: Content for clock.xul

<?xml version="1.0"?>

<?xml-stylesheet href="chrome://global/skin/"?>

<dialog id="clockDialog"

xmlns="http://www.mozilla.org/keymaster

/gatekeeper/there.is.only.xul"

title="Clock"

buttons="accept"

onload="initClock();">

<script type="application/x-javascript"

src="chrome://helloworld/content/clock.js" />

<hbox align="center">

<label value="Current Time:" />

<textbox id="currentTime" />

</hbox>

</dialog>

Listing 6: content for clock.js

function initClock() {

showCurrentTime();

window.setInterval(showCurrentTime, 1000);

}

function showCurrentTime() {

var textbox = document.getElementById("currentTime");

textbox.value = new Date().toLocaleTimeString();

textbox.select();

}

What if something’s not working right?

If your extension isn’t working according to plan, first see if there’s an error being displayed on the Errors panel of the Error Console. If you set things up as recommended in the section “Setting up your development environment,” XUL and JavaScript errors will appear here. See the sidebar “JavaScript debugging techniques” for useful tips on that subject5 If Firefox freezes up, you’ll need to terminate the Firefox process from the task manager.

 

 

<meta content="text/html; charset=utf-8" http-equiv="CONTENT-TYPE"/> <title/> <meta content="OpenOffice.org 3.0 (Linux)" name="GENERATOR"/> <style type="text/css"> &lt;!-- @page { margin: 2cm } P { margin-bottom: 0cm } TD P { margin-bottom: 0cm } A.sdfootnoteanc { font-size: 57% } --&gt; </style>

Phase 3: Adding multilingual support

The clock window that we created in Phase 2 displays everything in English. In Phase 3, we’re going to add multilingual support, making it so that it users running Firefox in a Japanese environment will see “現在時刻,” and those running it in English will see “Current time” (Figure 7).

Figure 7: Clock window after Phase 3

Directory structure

To add multilingual support to this extension, we’ll add a locale package to the chrome. Create a new locale folder with subfolders and files within the helloworld folder, as shown in Figure 8. The purpose of each of these files is explained in Table 3.

[figure]

locale package

[/figure]

Figure 8: Directory structure with locale package added

Table 3: Files used in Phase 3

name

purpose

locale\en-US\clock.dtd

DTD definining entity references used in clock.xul (for English).

locale\ja-JP\clock.dtd

DTD definining entity references used in clock.xul (for Japanese).

Adding locale packages

Registering locale packages (locale instruction)

The line beginning locale is used to register the chrome’s local package; helloworld is the package name, and locale/en-US/ and locale/ja-JP/ are the relative paths to the folders containing the source files; en-US and ja-JP indicate that those locale packages are for English and Japanese, respectively.

Replacing text with entity references in XUL

To make the extension support multiple languages, you will need to separate out all the hard-coded display text in clock.xul and move it to a DTD file. The DTD file is located inside the locale package, and the correct file is picked automatically to match the user’s language preferences. Edit clock.xul to match Listing 8. Add the DOCTYPE declaration that references the DTD file, and replace “clock” and “current time” with entity references.

Create the DTD that defines the entity references

Now create the DTD file that gets referred to by clock.xul (Listing 9). The clock.dtd file inside the ja-JP folder must be encoded as UTF-8.

Operations check

This brings us to the operations check. Because we’ve changed the chrome manifest, we need to relaunch Firefox once, as discussed in the sidebar “Operations checks on revised source files.” So relaunch Firefox and display the clock window to make sure it is working correctly. Then type about:config into the location bar, and change the value of general.useragent.locale from en to ja, and re-display the clock window to confirm that it appears in Japanese now.

Listing 7: Additional content for chrome.manifest

locale helloworld en-US locale/en-US/

locale helloworld ja-JP locale/ja-JP/

Listing 8: Revisions to clock.xul

1: <?xml version="1.0"?>

2: <?xml-stylesheet href="chrome://global/skin/"?>

3: <!DOCTYPE dialog SYSTEM "chrome://helloworld/locale/clock.dtd">

4: <dialog id="clockDialog"

5: xmlns="http://www.mozilla.org/keymaster/gat...re.is.only.xul"

6: title="&helloworld.clock;"

7: buttons="accept"

8: onload="initClock();">

9: <script type="application/x-javascript" src="chrome://helloworld/content/clock.js" />

10: <hbox align="center">

11: <label value="&helloworld.currentTime;:" />

12: <textbox id="currentTimeTextbox" />

13: </hbox>

14: </dialog>

Listing 9: Content for clock.dtd

locale\en-US\clock.dtd:

<!ENTITY helloworld.clock "Clock">

<!ENTITY helloworld.currentTime "Current Time">

locale\ja-JP\clock.dtd:

<!ENTITY helloworld.clock "時計">

<!ENTITY helloworld.currentTime "現在時刻">

 

Phase 4: Adding a toolbar button

In Phase 4, we’ll use graphics and style sheets to create a toolbar button that will open the clock window (Figure 9).

Directory structure

The style sheet and graphics files will be located inside the chrome’s skin package. The extension will actually work fine even if these are located in the content package, but putting them in the skin package has the benefit of making it possible to design GUIs that match specific Firefox themes, and allowing theme developers to create special visual effects for extensions.

First, add the skin directory and its subordinate files to your helloworld directory as shown in Figure 10. The purpose of each of these files is explained in Table 4. Download the files icon.png and icon-small.png from the resources website and place them appropriately.

Figure 9: Toolbar after Phase 4

[figure]

skin package

[/figure]

Figure 10: Directory structure with skin package added

Add the skin package

Update chrome.manifest with the contents of Listing 10.

Register skin package (skin instruction)

Line 1 beginning skin is used to register the skin package; helloworld is the package name; skin/classic/ is the relative path to the folder containing the source files; classic/1.0 indicates that this skin package is meant for the Firefox standard theme.

Cross-package overlays with style sheets (style instruction)

In Phase 1, we used the overlay instruction to invoke a cross-package overlay with XUL; to do this with a style sheet, we use the style instruction. Lines 2-3 create overlays on the browser window and the “customize toolbar” window.

Add the toolbar button

To add the toolbar button to the browser window, update overlay.xul as shown in Listing 11. In order to make it so that users can relocate the button where they like, you will add a new toolbarbutton element, using the special element toolbarpalette with the id BrowserToolbarPalette as the merge point. You’ll also add a new command element, so that a click on the toolbar button will share its process with the menu item.

Create the style sheet

Update the file overlay.css with the contents of Listing 12. Lines 1–3 define the icon image to use with full-sized icons, and lines 4–6 define the icon image to use when small toolbar icons have been selected.

Operations check

Since we have edited the chrome manifest, as we did in Phase 3, we need to relaunch Firefox. Check to make sure that, when you click on the “customize toolbar” button at the top-right of the window that the new toolbar icon appears in the resulting window. Drag it onto the toolbar, and click it to make sure it works correctly.

Table 4: Files used in Phase 4

name

purpose

icon.png

The full-size toolbar button icon file

icon-small.png

the small toolbar button icon file

overlay.css

Style sheet that defines the toolbar buttons to use. Overlays both the browser window and the “customize toolbar” window.

Listing 10: Additional content for chrome.manifest

1. skin helloworld classic/1.0 skin/classic/

2. style chrome://browser/content/browser.xul chrome://helloworld/skin/overlay.css

3. style chrome://global/content/customizeToolbar.xul chrome://helloworld/skin/overlay.css

Listing 11: Revisions to overlay.xul

<?xml version="1.0"?>

<overlay id="helloworldOverlay"

xmlns="http://www.mozilla.org/keymaster/gatekeeper/

there.is.only.xul">

<commandset id="mainCommandSet">

<command id="helloworldCommand"

oncommand="window.openDialog(

'chrome://helloworld/content/clock.xul',

'Clock','chrome,centerscreen,modal');" />

</commandset>

<toolbarpalette id="BrowserToolbarPalette">

<toolbarbutton id="helloworldButton"

label="Hello, World!"

class="toolbarbutton-1"

command="helloworldCommand" />

</toolbarpalette>

<menupopup id="menu_ToolsPopup">

<menuitem id="helloworldMenuitem"

label="Hello, World!"

insertbefore="sanitizeSeparator"

command="helloworldCommand" />

</menupopup>

</overlay>

Listing 12: Content for overlay.css

#helloworldButton {

list-style-image:

url(chrome://helloworld/skin/icon.png);

}

toolbar[iconsize="small"] #helloworldButton {

list-style-image:

url(chrome://helloworld/skin/icon-small.png);

}

 

Phase 5: XPI packaging

We actually completed our Hello World extension in Phase 4, but you can’t distribute the source files in this state to other users. So we need to create an xpi-formatted installer.

XPI file directory structure

XPI is basically just a zip archive, but the directory structure inside an xpi file is generally different from your development directory structure (Figure 11).

Packaging procedure

To package your extension as an xpi while preserving the directory structure of the source files you used during development, make the changes shown in Figure 11, as described below.

1. Create a new chrome directory inside the helloworld directory.

2. Zip the content, locale, and skin directories1, rename the resulting archive helloworld.jar, and place it inside the chrome directory you created in Step 1.

3. Copy chrome.manifest and rename the backup chrome.manifest.bak.

4. Change the content of chrome.manifest to be in line with Listing 13.

5. Zip install.rdf, chrome.manifest, and the chrome folder together, and rename the resulting archive helloworld.xpi.

That completes the process of XPI packaging. One point you need to take special note of is that in Step 4, you updated chrome.manifest so that the files it refers to have paths pointing inside the zip archive.

In order to go back into development mode after you’ve finished packaging the extension as an xpi, you need to revert the chrome.manifest you edited in Step 4 to point to the backup files in Step 3. If you forget to do this, any subsequent changes you make to your source files during development will not be reflected. You may want to write a batch file to automate this task.

There’s a simpler way to package an extensions as an xpi. Create a zip archive containing install.rdf, chrome.manifest, content, locale, and skin. Name this helloworld.xpi.

This omits creating the archive of the chrome packages called helloworld.jar. This kind of xpi file will run fine, but will slow down Firefox’s launch time.

Figure 11: Directory structure inside an xpi file

Listing 13: Revisions to chrome.manifest

content helloworld jar:chrome/helloworld.jar!/content/

overlay chrome://browser/content/browser.xul chrome://helloworld/content/overlay.xul

locale helloworld en-US jar:chrome/helloworld.jar!/locale/en-US/

locale helloworld ja-JP jar:chrome/helloworld.jar!/locale/ja-JP/

skin helloworld classic/1.0 jar:chrome/helloworld.jar!/skin/classic/

style chrome://browser/content/browser.xul chrome://helloworld/skin/overlay.css

style chrome://global/content/customizeToolbar.xul chrome://helloworld/skin/overlay.css

 

XPI operations check

After you create the xpi file, make sure it works. Since you’ve already got the development version of the Hello World extension running as a test install on your current profile, restart Firefox under a different profile (see footnote 1). Drag and drop the xpi file onto your browser window to start the installation.

This completes the explanation of how to create and package a simple “hello world” extension.

 

Developing practical extensions:
A session-management extension

In this section, we will create an extension that uses one of the new features in Firefox 2: the session store API. This will allow the user to save and restore session snapshots (browser window states) at any time.

 

Phase 1: test install

Figure 12 shows what the interface for the session-management extension will look like. Under the Tool menu, the Session Store submenu contains two items—Save Session and Clear Sessions; in between them is a list of previously saved sessions that you can restore, most recent first.

As discussed in the sidebar “The Session Store API,” sessions are represented as JSON character strings. Sessions are stored in a subdirectory of the profile directory (called “sessionstore”), with each session stored as a text file in it (Figure 13).

Source file structure

Figure 14 shows the directory structure for this project.

Creating install manifest

Create your work folder, and create an install manifest with the contents of Listing 1, but in this case, change em:id to sessionstore@xuldev.org and em:name to Session Store.

Creating chrome manifest

Create a chrome manifest with the contents of Listing 2.

Finding overlay merge points

Use the same overlay merge point and element to add on to as you did for the Hello World extension.

Browser window overlay

Create a file that will overlay the browser window, overlay.xul, with the contents from Listing 14.

Test install

Perform a test install using pointer files as you did in the previous section. Afterwards, you should see a Session Store menu item under the Tools menu.

Figure 12: Session-management extension interface

[figure]

Firefox << Save session Saved session directory

>> Restore session

Sessions saved under file names that indicate time of snapshot.

[/figure]

Figure 13: Schematic of session saving and restoring

Figure 14: Source file directory structure

Listing 14: Content for overlay.xul

<?xml version="1.0"?>

<overlay id="sessionstoreOverlay"

xmlns="http://www.mozilla.org/keymaster/

gatekeeper/there.is.only.xul">

<script type="application/x-javascript"

src="chrome://sessionstore/content/overlay.js" />

<menupopup id="menu_ToolsPopup">

<menu label="Session Store"

insertbefore="sanitizeSeparator">

<menupopup>

<menuitem label="Save Session" />

<menuseparator />

<!-- Dynamically generated menu items go here -->

<menuseparator />

<menuitem label="Clear Sessions" />

</menupopup>

</menu>

</menupopup>

</overlay>

Phase 2: Implement functionality

In Phase 2, we’ll use JavaScript to implement the session save and restore functions using the session store API.

Figure out JavaScript skeleton

Decide on method names and variable names for each of the processes you’ll need to implement these features, and put them into an overlay.js file. Follow the code in Listing 15.

Wrap methods and variables as properties of objects

You’ll note that in Listing 15, I’ve defined a lot of different methods and variables as properties of a single object, gSessionStore. Take note that when using an extension to create an overlay on the browser window, any global variables and functions defined within JavaScript that get opened as overlays on top of browser.xul all become properties of the browser.xul window object. This means that all those variables and functions get mixed up with those defined by Firefox itself and any other extensions that are running. Wrapping everything in one object is a good way to isolate extensions from each other and keep them from trampling each others’ toes. Defining a class would be another good way.

Initializing when opening browser window

With extensions, there are a lot of situations where you want to perform some kind of initialization when the browser window opens. To do that, add an event listener for the load event that targets the window object. In Listing 15, an init method runs when the window opens, and an uninit method runs when the window closes.

Listing 15: Content for overlay.js

var gSessionStore = {

// Directory to save sessions (nsILocalFile)

_dir: null,

// Initialization

init: function() { },

// uninitialization

uninit: function() { },

// Save session (event handler)

save: function(event) { },

// Restore session (event handler)

restore: function(event) { },

// Delete session (event handler)

clear: function(event) { },

// Dynamically generate menu items (event handler)

createMenu: function(event) { },

// Read file

_readFile: function(aFile) { },

// Write file

_writeFile: function(aFile, aData) { },

};

window.addEventListener("load", function(){

gSessionStore.init(); }, false);

window.addEventListener("unload", function(){

gSessionStore.uninit(); }, false);

Listing 16: Revisions to overlay.xul

<menupopup onpopupshowing="gSessionStore.createMenu(event);"

oncommand="gSessionStore.restore(event);">

<menuitem label="Save Session" oncommand="gSessionStore.save(event);" />

<menuseparator />

<!-- Dynamically generated menu items go here -->

<menuseparator />

<menuitem label="Clear Sessions" oncommand="gSessionStore.clear(event);" />

</menupopup>

Adding event handlers

Add the four event handlers you defined in overlay.js to overlay.xul (Listing 16). This takes advantage of event bubbling2, appending the restore event handler to the menupopup element one layer above the menuitem element. In the code for the save and restore event handlers, we will actually need blocks to prevent event bubbling.

Implementing methods

Now we’re going to actually implement the methods we defined in the gSessionStore object, one at a time. In the interest of space, I’m not including all the code here, but you can download it from the following URL: http://www.xuldev.org/samples/sd/

init method

The init method is as shown in Listing 17. The init method first gets the sessionstore directory inside the profile directory using nslLocalFile, which we’ll refer to through the variable _dir in subsequent lines (lines 3–6); if the directory doesn’t exist, we’ll create it (lines 7–8)3.

uninit method

Listing 18 gives the code for the uninit method. This just discards the variable _dir that was created by the init method. This process actually isn’t important.

save method

Listing 19 gives the code for the save method. In line 3, you see the block that prevents the bubbling that would result in this being called by the oncommand handler in the menupop element, which is the parent of the menuitem element. In lines 4–6, we use the nslSessionStore interface’s getBrowserState method to get a JSON text-string representation of the state s of all currently open browser windows. In lines 7–9, we create the target file’s nslFile object by duplicating the _dir property. In line 10, we use the _writefile method (which we’ll get to shortly) to write the JSON string representing the session to the file.

restore method

Listing 20 shows the event handler for the dynamically generated menu items, which were created using the createMenu method (which we’ll get to shortly). Lines 3–6 get the file name from the special attribute fileName that was added by the createMenu method, and read in the JSON text string from that file using the _readFile method (which we’ll get to shortly). Lines 7–9 use the nslSessionStore interface’s setWindowState method to restore the session, using the current window as an origin point. Session restoration overwrites any currently open tabs.

clear method

This uses the directoryEntries property of the nslFile interface to delete all files within the directory you got earlier. Refer to the section “Traversing folders” in Chapter 4.

_readFile method

Reads the nslFile object passed as a parameter, and returns its contents as a text string. When the file is read in, its contents are converted from UTF-8 to Unicode.

_writeFile method

Creates a new file for the nslFile object passed as a parameter, and writes the text string, also passed as a parameter. Upon writing, the text is converted from Unicode to UTF-8.

createMenu method

This event handler is called when the Session Store submenu opens. First, it deletes the menuitem elements that were dynamically generated the last time the submenu opened, and which are still left over. Next, it dynamically generates menuitem elements based on the names of all the files in the session-storage directory, and inserts them into the menu.

Operations check

This completes the functional implementation. Open a new window and make sure that all three functions—saving a session, restoring a session, and deleting sessions—all work correctly.

Listing 17: Content for init method

1: init: function()

2: {

3: var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]

4: .getService(Components.interfaces.nsIProperties);

5: this._dir = dirSvc.get("ProfD", Components.interfaces.nsILocalFile);

6: this._dir.append("sessionstore");

7: if (!this._dir.exists())

8: this._dir.create(this._dir.DIRECTORY_TYPE, 0700);

9: },

Listing 18: Content for uninit method

1: uninit: function()

2: {

3: this._dir = null;

4: },

Listing 19: Content for save method

1: save: function(event)

2: {

3: event.stopPropagation();

4: var ss = Components.classes["@mozilla.org/browser/sessionstore;1"]

5: .getService(Components.interfaces.nsISessionStore);

6: var state = ss.getBrowserState();

7: var fileName = "session_" + Date.now() + ".js";

8: var file = this._dir.clone();

9: file.append(fileName);

10: this._writeFile(file, state);

11: },

Listing 20: Content for restore method

1: restore: function(event)

2: {

3: var fileName = event.target.getAttribute("fileName");

4: var file = this._dir.clone();

5: file.append(fileName);

6: var state = this._readFile(file);

7: var ss = Components.classes["@mozilla.org/browser/sessionstore;1"]

8: .getService(Components.interfaces.nsISessionStore);

9: ss.setWindowState(window, state, false);

10: },

Phase 3: Creating a preference panel

You don’t need to use ini files, registries, or anything like that to give users options they can set through a preference panel. Firefox lets you read and write preference values using handy XPCOM services and create preference panels with interface widgets. In Phase 3, we’ll try this out (Figure 15).

Figure 15: Finished preference panel

Directory structure

Figure 16 shows the directory structure we’ll be using in Phase 3. Table 5 explains the new files being used.

Table 5: Files used in Phase 3

name

purpose

sessionstore-prefs.js

File sets defaults for preferences. This should always be located inside defaults\preferences

prefs.xul

The preference panel

 

Figure 16: Source file structure

Deciding the format for your preferences

Next you need to determine the names, types, and values for your preferences (Table 6). Each preference should be prefixed with something that uniquely identifies your extension—in this case, we’ll use extensions.sessionstore. .

Table 6: Preference formats

Preference name

Type

Value

extensions.sessionstore.warnOnClear

Boolean

Activated when deleting sessions. If true, causes a confirmation dialog to appear; if false, deletes immediately.

extensions.sessionstore.replaceTabs

Integer

Activated when restoring sessions.

0: Leave current tabs

1: Overwrite current tabs

2: Ask each time

Defaults definition file

Listing 21 gives the contents for the default preferences, as described in Table 6. Save this in defaults\preferences\sessionstoreprefs.js.

Creating the preference panel

Create prefs.xul with the contents from Listing 22. The prefwindow element is a special root element for creating preference panels, with the prefpane element subordinate to it. You can create an interface similar to Firefox’s options window by inserting multiple prefpane elements, which allows users to switch between panels by clicking buttons. The preference element creates a preference value that can be written and read by the preference panel, with the preference name indicated by the name attribute, and the type by the type attribute. Furthermore, by matching the preference element’s id to the preference attribute of a checkbox or radiogroup, you can specify the interface widget that will be used to set your preferences. Add the contents of Listing 23 to your install manifest to make it so that the Add-on Manager can open your preference panel.

Listing 21: Content for sessionstore-prefs.js

pref("extensions.sessionstore.warnOnClear", true);

pref("extensions.sessionstore.replaceTabs", 2);

Listing 22: Content for prefs.xul

<?xml version="1.0"?>

<?xml-stylesheet href="chrome://global/skin/"

type="text/css"?>

<prefwindow id="sessionstorePrefs"

xmlns="http://www.mozilla.org/keymaster/

gatekeeper/there.is.only.xul"

title="Session Store Preferences">

<prefpane>

<preferences>

<preference id="extensions.sessionstore.warnOnClear"

name="extensions.sessionstore.warnOnClear"

type="bool" />

<preference id="extensions.sessionstore.replaceTabs"

name="extensions.sessionstore.replaceTabs"

type="int" />

</preferences>

<checkbox label="Confirm before clearing sessions"

preference="extensions.sessionstore.warnOnClear" />

<groupbox>

<caption label="When restoring session:" />

<radiogroup

preference="extensions.sessionstore.replaceTabs">

<radio value="0" label="Keep current tabs" />

<radio value="1" label="Replace current tabs" />

<radio value="2" label="Ask me every time" />

</radiogroup>

</groupbox>

</prefpane>

</prefwindow>

Listing 23: Additional content for install.rdf

<em:optionsURL>chrome://sessionstore/content/prefs.xul</em:optionsURL>

Changing behavior through preference values

In Listing 24, we add a preference that allows a confirmation dialog to appear when we delete sessions. When we get the preference value from JavaScript, we take advantage the XPCOM nslPrefService interface, using the getBoolPref and getIntPref methods as appropriate to the data type. In Listing 24, we get the nslPrefBranch object that lets us read and write all preferences beginning with the prefix extensions.sessionstore., which is what lets us use the getBoolPref method to get the value.

Operations check

We’ve revised the install manifest, so we need to reinstall the extension (see the sidebar “Operations checks on revised source files”). Once you’ve reinstalled it, first open about:config, and confirm that the previous preference values are set. Next, select Session Store from the Add-on Manager and click the “Preferences” button to bring up the preference panel. Try changing the preferences, and see if those changes are reflected when you restore or delete sessions.

Phase 4: XPI packaging

This procedure is unchanged from Phase 5 in the “Developing a Simple Extension” section, but take note of everything within the defaults folder.

Conclusion

We’ve looked at how to build extensions at an extremely basic level in this chapter, and there are a lot of other interesting features to explore in extensions and XUL. But I hope that having gotten your feet wet with extensions development, you’ll want to dive in now.

Sidebar: Operations checks on revised source files

In the interest of developing extensions efficiently, you want to minimize the number of steps between revising a source file and running an operations check on it. For each type of source file, there’s a different procedure to follow. The following all assumes that you’ve disabled the XUL cache as discussed in the section “Setting up your development environment.”

XUL that opens new windows

Confirm changes by closing and reopening window. If the XUL is displaying as a sidebar, reopen the sidebar. Likewise with any JavaScript, DTDs, or style sheets referenced by the XUL.

XUL overlaying other windows

You need to force the overlay-target XUL to reload. If it’s the browser window that’s the target, you can simply open a new browser window to confirm the changes.

properties file

If you make changes to a properties file in the locale package, those changes will not be reflected in Firefox until you relaunch it.

XPCOM components inside the components directory

You will need to delete the files compreg.dat and xpti.dat inside the profile folder and relaunch Firefox.

Default preferences file inside the defaults\preferences directory

You will need to relaunch Firefox.

chrome.manifest

You will need to relaunch Firefox.

install.rdf

You will need to temporarily uninstall the extension and then reinstall it. In practice, what you’ll do is remove the pointer file, relaunch Firefox, return the pointer file, and relaunch Firefox again. On a Linux system, use the touch command on the directory pointed at by the pointer file to change its “last changed” date, and avoid the relaunching.

Sidebar: The session store API

The session store API4 is one of the new developer-oriented features in Firefox 2. Using the various methods in the nslSessionStore interface, which is an XPCOM service, you can easily save and restore session states. Functions in Firefox that let you reopen the last closed tab, or restore to your previous state after a crash, are all implemented through this API.

We used two methods in the nslSessionStore interface in this project. Let’s look at how they work.

getBrowserState()

Returns a JSON-formatted5 text string representing the state of every currently open browser window. This string includes extensive information regarding every open window and tab, the forward/back history for each tab, page scroll position, text zoom, contents of form elements, etc.

setWindowState(aWindow, aState, aOverwrite)

Restores the browser window indicated by the parameter aWindow to the state indicated by aState. If aOverwrite is true, the currently open tab is overwritten; otherwise, a new tab is created for the restored content.

Sidebar: JavaScript debugging methods

alert

The easiest way to debug javascvript is using the window.alert method to display variables in a dialog (Listing A). Except for asynchronous processes, all processing stops while the dialog is up, so this technique is useful when you want to pin down a value that can vary during a process.

dump

In a Windows environment, you can launch Firefox with the -console argument, which opens a dump console. Using the dump method, you can send text to the dump console (Listing B). The text output being sent to the console does not suspend any other processes, and this is most useful when there’s a high volume of messages being output. In order to use the dump method most effectively, you’ll need to follow the recommendations in “Setting up your development environment.” Note that in a Windows environment, Japanese text sent to the dump console will get corrupted.

error console

The nslConsoleService interface

Using the logStringMessage method of the nslConsoleService interface, which is an XPCOM service, you can output text to the “message” panel of the error console. Japanese text will appear correctly here. If you are going to be issuing messages to the error console frequently, it’s handy to define a function like that in Listing C.

Components.utils.reportError method

Another way to output text to the same error console is with the Components.utils.reportError method. This will send its output to the “error” panel of the error console.

Listing A: Check the value of variable foo

alert(foo);

Listing B: List the properties of obj

for (var i in obj) {

dump(i + " : " + obj[i] + "\n");

}

Listing C: Function to output messages to the error console

function log(aText) {

var console = Components.classes["@mozilla.org/consoleservice;1"]

.getService(Components.interfaces.nsIConsoleService);

console.logStringMessage(aText);

}

Sidebar: Where can I learn more about the XPCOM interface?

The Mozilla Development Center has extensive documentation on the nslSessionStore interface we’ve discussed in this section6. In general, details (IDL) on the XPCOM interface are available from XULPlanet7 and the Mozilla Cross-Reference8, which includes source code with full-text searching available.

 

1In Windows XP, you can use the “compress folder” command to create a zip archive, but I recommend installing a tool like 7-Zip that lets you specify the compression ratio. It’s OK to have a low compression ratio when you are creating the jar file, and a high compression ratio when creating the xpi file.

2An event generated by an element “bubbles up” to the root.

3See Code Snippets: File I/O-MDC (http://developer.mozilla.org/en/docs..._special_files) for techniques on getting special folders, like the profile folder.

5JSON is short for JavaScript Object Notation. It is a data format that can easily be read from and written to in JavaScript.

6nslSessionStore-MDC (http://developer.mozilla.org)

8Mozilla Cross-Reference (http://mxr.mozilla.org/)

Revision Source

<p style="text-align: justify;">{{ Fx_minversion_header("3") }} {{ Draft() }}</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">The chapters so far have each focused on technologies in isolation—XUL, JavaScript, CSS, and XPCOM. In this chapter, we’ll discuss how to put them together to actually build an extension.</p>
<h2 style="text-align: justify;"><font size="4"><em><strong>Setting up your development environment</strong></em></font></h2>
<h3 style="text-align: justify;"><font size="4"><em><strong>Change your preferences for more efficient development</strong></em></font></h3>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Before you get to work developing your extension, you’ll want to change some of your Firefox preferences. This isn’t a requirement for extension development, but I recommend it as a way to work more efficiently.</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">To make these changes, type <font size="2">about:config</font> into Firefox’s location bar and open the preferences window; find the preferences listed in Table 1 and double-click on them to set them accordingly. Some of these preferences do not exist—to create them, right-click, select “New&gt;Boolean”, and type in the name and set the value accordingly.</p>
<h3 style="text-align: justify;"><font size="4"><em><strong>Install the DOM Inspector</strong></em></font></h3>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">The DOM Inspector is an extension that lets you examine HTML and XUL DOM tree structures, JavaScript objects and CSS properties, etc. This is not required for developing extensions, but it is handy to have around. If you don’t see “DOM Inspector” under the Tool menu in Firefox after trying to install it, you’ll need to re-install it. Select “custom install” from the “setup type” screen, and under “additional components,” select “DOM Inspector.” Install extensions for Firefox 3 from Mozilla Add-ons (<a class=" link-https" href="https://addons.mozilla.org/" rel="freelink">https://addons.mozilla.org/</a>).</p>
<h3 style="text-align: justify;"><font size="4"><em><strong>Create a development profile</strong></em></font></h3>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">If you want to partition your everyday browsing environment from your development environment in Firefox, set up a second profile for development<sup><a class="sdfootnoteanc external" href="http://docs.google.com/Doc?id=dg533b6g_9dcsdjvgm&amp;invite=#sdfootnote1sym" name="sdfootnote1anc"><sup>1</sup></a></sup>.</p>
<p style="margin-bottom: 0.03in; page-break-after: avoid; text-align: justify;">Table 1: Preferences to set for developing extensions</p>
<table border="1" cellpadding="8" cellspacing="0" width="615"> <colgroup><col style="text-align: justify;" width="265"> <col style="text-align: justify;" width="250"> <col style="text-align: justify;" width="51"> </colgroup><tbody> <tr valign="top"> <td width="265"> <p class="western" style="text-align: justify;">Preference</p> </td> <td width="250"> <p class="western" style="text-align: justify;">Description</p> </td> <td width="51"> <p class="western" style="text-align: justify;">Value</p> </td> </tr> <tr valign="top"> <td width="265"> <p style="text-align: justify;"><font size="2">nglayout.debug.disable_xul_cache</font></p> </td> <td width="250"> <p class="western" style="text-align: justify;">Ordinarily, Firefox will cache XUL documents after they have been read in once, to speed subsequent displays. Disabling this cache forces XUL documents to be reloaded any every time they are displayed</p> </td> <td width="51"> <p class="western" style="text-align: justify;">True</p> </td> </tr> <tr valign="top"> <td width="265"> <p style="text-align: justify;"><font size="2">browser.dom.window.dump.enabled</font></p> </td> <td width="250"> <p class="western" style="text-align: justify;">Enables use of the dump method for debugging. Refer to the “JavaScript debugging methods” sidebar.</p> </td> <td width="51"> <p class="western" style="text-align: justify;">True</p> </td> </tr> <tr valign="top"> <td width="265"> <p style="text-align: justify;"><font size="2">javascript.options.showInConsole</font></p> </td> <td width="250"> <p class="western" style="text-align: justify;">Outputs JavaScript errors to the error console</p> </td> <td width="51"> <p class="western" style="text-align: justify;">True</p> </td> </tr> <tr valign="top"> <td width="265"> <p style="text-align: justify;"><font size="2">javascript.options.strict</font></p> </td> <td width="250"> <p class="western" style="text-align: justify;">Enforces strict error output from JavaScript</p> </td> <td width="51"> <p class="western" style="text-align: justify;">True</p> </td> </tr> </tbody>
</table>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;"> </p>
<h2 style="text-align: justify;"><font size="4"><em><strong>Developing extensions: What you need to know</strong></em></font></h2>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Let’s delve into chrome, something you’ll need to know about in order to develop extensions.</p>
<h3 style="text-align: justify;"><font size="4"><em><strong>Chrome</strong></em></font></h3>
<h4 style="text-align: justify;"><strong>What is chrome?</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">“Chrome”is the word used to describe all the GUI structural elements that go into an XUL application. For example, in the Firefox browser window, everything other than the web page being displayed in the content area is chrome. Extensions also can be considered XUL applications with chrome.</p>
<h4 style="text-align: justify;"><strong>Three kinds of packages make up chrome</strong></h4>
<h5 style="text-align: justify;"><em><strong>The content package</strong></em></h5>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">This package is used to contain the main XUL and JavaScript source files. Most extensions consist of a single content package<sup><a class="sdfootnoteanc external" href="http://docs.google.com/Doc?id=dg533b6g_9dcsdjvgm&amp;invite=#sdfootnote2sym" name="sdfootnote2anc"><sup>2</sup></a></sup></p>
<h5 style="text-align: justify;"><em><strong>The locale package</strong></em></h5>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">This package is used to contain language data that can be translated. To make an extension’s GUI support multiple languages, you can include multiple locale packages, one for each language.</p>
<h5 style="text-align: justify;"><em><strong>The skin package</strong></em></h5>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">This is used to include source files used as visual elements in the GUI, including style sheets and images. Most extensions include only one skin package, but you can include multiple skin packages to allow the GUI to change with different themes.</p>
<h4 style="text-align: justify;"><strong>Chrome URL</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Use a file called a “chrome manifest” to register chrome packages with Firefox and start using them. To register a package, you use a special URI scheme called a “Chrome URL” to represent the path to the file. Chrome URLs are structured as:</p>
<p style="margin-bottom: 0.17in; text-align: justify;"><font size="2"><a class=" external" href="chrome://" rel="freelink">chrome://</a><em>package name</em>/<em>package type</em>/<em>relative path to source file</em></font></p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">For example, the Chrome URL for the Firefox browser window is as follows:</p>
<p style="margin-bottom: 0.17in; text-align: justify;"><font size="2"><a class=" external" href="chrome://browser/content/browser.xul" rel="freelink">chrome://browser/content/browser.xul</a></font></p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Here, the package name is “browser”, and the file “browser.xul” is located inside the “content” package. Source files represented through chrome URLs run with UniversalXPConnect privileges; even if they haven’t been granted “use XPConnect as local file” privileges, as discussed in Chapter 4, they will be able to use XPCOM functions (Figure 1).</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">[figure]</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Run with privileges</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">---</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Register chrome</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">---</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Extension</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">chrome manifest</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Chrome</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">content package</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">locale package</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">skin package</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">[/figure]</p>
<p style="margin-bottom: 0.17in; text-align: justify;">Figure 1: Chrome packages and chrome registration</p>
<h3 style="text-align: justify;"><font size="4"><em><strong>Cross-package overlays</strong></em></font></h3>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">The “overlay” technique introduced in Chapter 3 required the use of the <font size="2">xul-overlay</font> instruction in the XUL document that is the overlay target. It is also possible to coerce an overlay without using the <font size="2">xul-overlay</font> instruction in the XUL document—this is called a “cross-package overlay” (Figure 2). In fact, you need to use cross-package overlays to append buttons or menus to the Firefox browser window. Use the chrome manifest to invoke a cross-package overlay.</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">[figure]</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Normal overlay</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Adding the <font size="2">xul-overlay</font> instruction to the target XUL document overlays it with another XUL document</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">--</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Cross-package overlay</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">(all on one line)</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Adding a cross manifest permits one XUL file to overlay another without any <font size="2">xul-overlay</font> instruction.</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">[/figure]</p>
<p style="margin-bottom: 0.17in; text-align: justify;">Figure 2: Invoking a cross-package overlay</p>
<h3 style="text-align: justify;"><font size="4"><em><strong>Conclusion</strong></em></font></h3>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">This has been a brief introduction to chrome that probably leaves a lot of unanswered questions. If you keep in mind the following three points at the very least, the next section, Developing a Simple Extension, should help flesh out your understanding.</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">1. An extension’s GUI, or chrome, can consist of three kinds of package: content, locale, and skin.</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">2. Use a cross-package overlay on top of browser.xul to add a button or menu item to the Firefox browser window.</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">3. The chrome manifest plays two important roles: it registers the contents of the chrome packages, and invokes the cross-package overlay.</p>
<h2 style="text-align: justify;"><font size="4"><em><strong>Developing a Simple Extension: Hello World</strong></em></font></h2>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">In this section, we will write an extremely simple “hello world” extension that only displays the time.<sup><a class="sdfootnoteanc external" href="http://docs.google.com/Doc?id=dg533b6g_9dcsdjvgm&amp;invite=#sdfootnote3sym" name="sdfootnote3anc"><sup>3</sup></a></sup></p>
<h3 style="text-align: justify;"><font size="4"><em><strong>Phase 1: test install</strong></em></font></h3>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Our first step will be to perform a test installation consisting of the minimum code needed to add a new menu item to the Tools menu (Figure 3).</p>
<h4 style="text-align: justify;"><strong>Source file structure</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">First set up the work folders you’ll use to organize your extension’s source files. The folder can be wherever you want, but for the purposes of this guide, we’ll assume it’s at <font size="2">C:\extensions\helloworld</font> . Create folders and files in your work folder as shown in Figure 4. The purpose of each file created during Phase 1 is explained in Table 2.</p>
<h4 style="text-align: justify;"><strong>Create install manifest</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Fill in the install.rdf file as shown in Listing 1.</p>
<h4 style="text-align: justify;"><strong>Create the chrome manifest</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Fill in the chrome.manifest file as shown in Listing 2.</p>
<h5 style="text-align: justify;"><em><strong>Register the content package (content instruction)</strong></em></h5>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Line 1, beginning <font size="2">content</font>, contains the code used to register the chrome’s content package. Here, <font size="2">helloworld</font> is a package name, and <font size="2">content/</font> is a relative path to the folder where the source file is stored. Registering the content package makes it so that the overlay.xul file in the content folder can be accessed using the chrome URL, <font size="2"><a class=" external" href="chrome://helloworld/content/overlay.xul" rel="freelink">chrome://helloworld/content/overlay.xul</a></font>.</p>
<h5 style="text-align: justify;"><em><strong>XUL cross-package overlay (overlay instruction)</strong></em></h5>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Line 2, beginning <font size="2">overlay</font>, contains the code used to invoke the cross-package overlay, overlaying the Firefox browser window, at <font size="2"><a class=" external" href="chrome://browser/content/browser.xul" rel="freelink">chrome://browser/content/browser.xul</a></font> with <font size="2"><a class=" external" href="chrome://helloworld/content/overlay.xul" rel="freelink">chrome://helloworld/content/overlay.xul</a></font>.</p>
<p style="margin-bottom: 0.17in; text-align: justify;">Figure 3: Menu after Phase 1 complete</p>
<p style="margin-bottom: 0.17in; text-align: justify;">Figure 4: Folder structure used in Phase 1</p>
<p style="margin-bottom: 0.03in; page-break-after: avoid; text-align: justify;">Table 2: How files are used in Phase 1</p>
<table border="1" cellpadding="8" cellspacing="0" width="615"> <colgroup><col style="text-align: justify;" width="116"> <col style="text-align: justify;" width="466"> </colgroup><tbody> <tr valign="top"> <td width="116"> <p class="western" style="text-align: justify;">File name</p> </td> <td width="466"> <p class="western" style="text-align: justify;">Role</p> </td> </tr> <tr valign="top"> <td width="116"> <p class="western" style="text-align: justify;">install.rdf</p> </td> <td width="466"> <p class="western" style="text-align: justify;">Called the install manifest, this gives basic information about the extension, and is required in order for the extension to be installed in Firefox.</p> </td> </tr> <tr valign="top"> <td width="116"> <p class="western" style="text-align: justify;">chrome.manifest</p> </td> <td width="466"> <p class="western" style="text-align: justify;">This is the chrome manifest described in the earlier section. Registers packages and invokes cross-package overlays.</p> </td> </tr> <tr valign="top"> <td width="116"> <p class="western" style="text-align: justify;">overly.xul</p> </td> <td width="466"> <p class="western" style="text-align: justify;">XUL file that will be overlaid on the Firefox browser window, adding buttons, menu items, etc.</p> </td> </tr> <tr valign="top"> <td width="116"> <p class="western" style="margin-bottom: 0in; text-align: justify;">clock.xul</p> <p class="western" style="text-align: justify;">clock.js</p> </td> <td width="466"> <p class="western" style="text-align: justify;">The XUL to display a clock in the window, and the JavaScript to control its operation (these files will be used in Phase 2).</p> </td> </tr> </tbody>
</table>
<h4 style="text-align: justify;"><strong>Finding overlay merge points</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">The next step is to find the “merge points” where the overlay document will insert its content into the base document. There are a number of ways to go about this; here, we’ll use the DOM Inspector (Figure 5).</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">1. Open the DOM Inspector by selecting the Tools &gt; DOM Inspector menu item.</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">2. Type <font size="2"><a class=" external" href="chrome://browser/content/browser.xul" rel="freelink">chrome://browser/content/browser.xul</a></font> into the URL input field at the top, then click the Inspect button. A browser pane will appear in the lower part of the window.</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">3. Click the spots numbered 1 and 2 in Figure 5, in that order; the menu element (3) will then be selected.</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">4. Expand spot 3; this will disclose the area numbered 4, with several <font size="2">menuitem</font> elements. The <font size="2">menupop</font> element 4 contains the merge point where you’re going to be adding a new menu item; you can see that it has the id <font size="2">menu_ToolsPopup</font>.</p>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">5. Look through the <font size="2">menuitem</font> elements within this <font size="2">menupop</font> element and determine where you want to add your new element. For the time being, let’s locate it in the spot numbered 5.</p>
<p style="margin-bottom: 0.17in; text-align: justify;">Figure 5: Finding merge points for overlays using the DOM Inspector</p>
<p style="margin-bottom: 0.03in; page-break-after: avoid; text-align: justify;">Listing 1: Content for install.rdf</p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;?xml version="1.0"?&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;RDF xmlns="<a class=" external" href="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rel="freelink">http://www.w3.org/1999/02/22-rdf-syntax-ns#</a>"</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">xmlns:em="<a class=" external" href="http://www.mozilla.org/2004/em-rdf#" rel="freelink">http://www.mozilla.org/2004/em-rdf#</a>"&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;Description about="urn:mozilla:install-manifest"&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">/* Unique ID for extension. </font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">Can be in e-mail address format or GUID format */</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:id&gt;<a class=" link-mailto" href="mailto:helloworld@xuldev.org" rel="freelink">helloworld@xuldev.org</a>&lt;/em:id&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">/* Indicates that this add-on is an extension */</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:type&gt;2&lt;/em:type&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">/* Extension name displayed in Add-on Manager */</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:name&gt;Hello, World!&lt;/em:name&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">/* Extension version number. </font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">There is a version numbering scheme you must follow */</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:version&gt;0.1&lt;/em:version&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">/* Brief description displayed in Add-on Manager */</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:description&gt;My first extension.&lt;/em:description&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">/* Name of extension’s primary developer. Change to your name */</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:creator&gt;Gomita&lt;/em:creator&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">/* Web page address through which extension is distributed */</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:homepageURL&gt;<a class=" external" href="http://www.xuldev.org/helloworld/" rel="freelink">http://www.xuldev.org/helloworld/</a>&lt;/em:homepageURL&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">/* This section gives details of the target application </font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">for the extension--in this case, Firefox 2 */</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:targetApplication&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;Description&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:id&gt;{ec8030f7-c20a-464f-9b0e-13a3a9e97384}&lt;/em:id&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:minVersion&gt;2.0&lt;/em:minVersion&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;em:maxVersion&gt;2.0.0.*&lt;/em:maxVersion&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;/Description&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;/em:targetApplication&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;/Description&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;/RDF&gt;</font></p>
<p style="margin-bottom: 0.03in; page-break-after: avoid; text-align: justify;">Listing 2: Content for chrome.manifest</p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">content helloworld content/</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">overlay <a class=" external" href="chrome://browser/content/browser.xul" rel="freelink">chrome://browser/content/browser.xul</a> ↩ <a class=" external" href="chrome://helloworld/content/overlay.xul" rel="freelink">chrome://helloworld/content/overlay.xul</a></font></p>
<h4 style="text-align: justify;"><strong>Putting overlays on the browser window</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Now that you know the merge point for your overlay, you can create the overlay.xul file that will insert it (Listing 3). In line 4, we identify the merge point as the <font size="2">menupop</font> element; in lines 5–7, we have the code that adds the new menu item. The <font size="2">insertbefore</font> attribute determines where the element gets added.</p>
<h4 style="text-align: justify;"><strong>Test install and operational check</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">This brings us to the point where we finally install our Hello World extension. Using the normal XPI-style installer would just be added trouble in this case, so we won’t use it. For source files under development, we do test installs.</p>
<h5 style="text-align: justify;"><em><strong>Placing pointer files</strong></em></h5>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">First, navigate to the profile folder of the currently active profile<sup><a class="sdfootnoteanc external" href="http://docs.google.com/Doc?id=dg533b6g_9dcsdjvgm&amp;invite=#sdfootnote4sym" name="sdfootnote4anc"><sup>4</sup></a></sup>, and open the extensions folder within it. Create a new file there with the name you used as the Hello World extension ID in the install manifest, <font size="2"><a class=" link-mailto" href="mailto:helloworld@xuldev.org" rel="freelink">helloworld@xuldev.org</a></font>. Set the contents of that file to be the full path to the work folder for your extension source files, <font size="2">C:\extensions\helloworld</font>.</p>
<p style="margin-bottom: 0.03in; page-break-after: avoid; text-align: justify;">Listing 3: Additional content for overlay.xul</p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">1. &lt;?xml version="1.0"?&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">2. &lt;overlay id="helloworldOverlay"</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">3: xmlns="<a class=" external" href="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" rel="freelink">http://www.mozilla.org/keymaster/gat...re.is.only.xul</a>"&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">4: &lt;menupopup id="menu_ToolsPopup"&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">5: &lt;menuitem id="helloworldMenuitem"</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">6: label="Hello, World!"</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">7: insertbefore="sanitizeSeparator" /&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">8: &lt;/menupopup&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">9: &lt;/overlay&gt;</font></p>
<p style="margin-bottom: 0.03in; page-break-after: avoid; text-align: justify;">Listing 4: Additional content for overlay.xul</p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">oncommand="window.openDialog('<a class=" external" href="chrome://helloworld/content/clock.xul','Clock','chrome,centerscreen,modal'" rel="freelink">chrome://helloworld/content/clock.xu...rscreen,modal'</a>);"</font></p>
<p style="text-align: justify;"> </p>
<h3 style="text-align: justify;"><font size="4"><em><strong>Phase 2: Adding a function to display the time</strong></em></font></h3>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">In Phase 2, we will make it so that selecting the Hello World menu item we created in Phase 1 will display a window with the time (Figure 6).</p>
<h4 style="text-align: justify;"><strong>Adding an event handler</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">First, we add an event handler to the menu item that will open the window (Listing 4).</p>
<h4 style="text-align: justify;"><strong>Clock handler</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Create the window that displays the clock (Listing 5) and its controller (Listing 6). We’ve already covered the <font size="2">dialog</font> element in Chapter 3. In this case, we only want to display an OK button, so we set the <font size="2">buttons</font> attribute to <font size="2">accept</font>. Within the window are a <font size="2">label</font> element and <font size="2">textbox</font> element, wrapped in a <font size="2">hbox</font> element so that they are arranged horizontally.</p>
<h4 style="text-align: justify;"><strong>Operations check</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">Perform an operations check to make sure that your changes to the source file are correct. At this point, it’s important that you disabled the XUL cache as discussed in the earlier section, “Setting up your development environment.” Assuming you’ve done that, you’ll be able to confirm the changes to overlay.xul and clock.xul without the bother of relaunching Firefox or reinstalling the extension. If the browser.xul file, which is the target of the overlay in overlay.xul is being read in again, the changes will be reflected, and you’ll be able to see the changes by opening a new browser window. The files clock.xul and clock.js will be read in every time you open the clock window, so just re-opening the clock window once will suffice to check them. During development, it’s important to minimize the number of steps between revising a source file and running an operations check against those changes. The sidebar “Operations checks on revised source files” discusses the general procedure to follow from source-file revision to operations check.</p>
<p style="margin-bottom: 0.17in; text-align: justify;">Figure 6: Clock window produced by Phase 2</p>
<p style="margin-bottom: 0.03in; page-break-after: avoid; text-align: justify;">Listing 5: Content for clock.xul</p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;?xml version="1.0"?&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;?xml-stylesheet href="<a class=" external" href="chrome://global/skin/" rel="freelink">chrome://global/skin/</a>"?&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;dialog id="clockDialog"</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">xmlns="<a class=" external" href="http://www.mozilla.org/keymaster" rel="freelink">http://www.mozilla.org/keymaster</a> ↩</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">/gatekeeper/there.is.only.xul"</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">title="Clock"</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">buttons="accept"</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">onload="initClock();"&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;script type="application/x-javascript"</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">src="<a class=" external" href="chrome://helloworld/content/clock.js" rel="freelink">chrome://helloworld/content/clock.js</a>" /&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;hbox align="center"&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;label value="Current Time:" /&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;textbox id="currentTime" /&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;/hbox&gt;</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">&lt;/dialog&gt;</font></p>
<p style="margin-bottom: 0.03in; page-break-after: avoid; text-align: justify;">Listing 6: content for clock.js</p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">function initClock() {</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">showCurrentTime();</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">window.setInterval(showCurrentTime, 1000);</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">}</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">function showCurrentTime() {</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">var textbox = document.getElementById("currentTime");</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">textbox.value = new Date().toLocaleTimeString();</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">textbox.select();</font></p>
<p style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1px; padding: 0in 0in 0in 0.06in; margin-left: 0.13in; margin-bottom: 0.03in; text-align: justify;"><font size="2">}</font></p>
<h4 style="text-align: justify;"><strong>What if something’s not working right?</strong></h4>
<p class="western" style="margin-bottom: 0.17in; text-align: justify;">If your extension isn’t working according to plan, first see if there’s an error being displayed on the Errors panel of the Error Console. If you set things up as recommended in the section “Setting up your development environment,” XUL and JavaScript errors will appear here. See the sidebar “JavaScript debugging techniques” for useful tips on that subject<sup><a class="sdfootnoteanc external" href="http://docs.google.com/Doc?id=dg533b6g_9dcsdjvgm&amp;invite=#sdfootnote5sym" name="sdfootnote5anc"><sup>5</sup></a></sup> If Firefox freezes up, you’ll need to terminate the Firefox process from the task manager.</p>
<p style="margin-top: 0.17in; margin-bottom: 0.04in; page-break-after: avoid; text-align: justify;"> </p>
<p> </p>
<p>&lt;meta content="text/html; charset=utf-8" http-equiv="CONTENT-TYPE"/&gt; &lt;title/&gt; &lt;meta content="OpenOffice.org 3.0 (Linux)" name="GENERATOR"/&gt; &lt;style type="text/css"&gt; &amp;lt;!-- @page { margin: 2cm } P { margin-bottom: 0cm } TD P { margin-bottom: 0cm } A.sdfootnoteanc { font-size: 57% } --&amp;gt; &lt;/style&gt;</p>
<h3><font size="4">Phase 3: Adding multilingual support</font></h3>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">The clock window that we created in Phase 2 displays everything in English. In Phase 3, we’re going to add multilingual support, making it so that it users running Firefox in a Japanese environment will see “<font face="DejaVu Sans"><font size="2">現在時刻</font></font>,” and those running it in English will see “Current time” (Figure 7).</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 7: Clock window after Phase 3</p>
<h4>Directory structure</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">To add multilingual support to this extension, we’ll add a <font size="2">locale</font> package to the chrome. Create a new locale folder with subfolders and files within the helloworld folder, as shown in Figure 8. The purpose of each of these files is explained in Table 3.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">[figure]</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">locale package</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">[/figure]</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 8: Directory structure with locale package added</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Table 3: Files used in Phase 3</p>
<table border="1" cellpadding="8" cellspacing="0" width="590"> <colgroup><col width="158"> <col width="398"> </colgroup><tbody> <tr valign="top"> <td width="158"> <p align="justify" style="font-style: normal; font-weight: normal;">name</p> </td> <td width="398"> <p align="justify" style="font-style: normal; font-weight: normal;">purpose</p> </td> </tr> <tr valign="top"> <td width="158"> <p align="justify" style="font-style: normal; font-weight: normal;">locale\en-US\clock.dtd</p> </td> <td width="398"> <p align="justify" style="font-style: normal; font-weight: normal;">DTD definining entity references used in clock.xul (for English).</p> </td> </tr> <tr valign="top"> <td width="158"> <p align="justify" style="font-style: normal; font-weight: normal;">locale\ja-JP\clock.dtd</p> </td> <td width="398"> <p align="justify" style="font-style: normal; font-weight: normal;">DTD definining entity references used in clock.xul (for Japanese).</p> </td> </tr> </tbody>
</table>
<h4>Adding locale packages</h4>
<h5>Registering locale packages (locale instruction)</h5>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">The line beginning <font size="2">locale</font> is used to register the chrome’s local package; <font size="2">helloworld</font> is the package name, and <font size="2">locale/en-US/</font> and <font size="2">locale/ja-JP/</font> are the relative paths to the folders containing the source files; <font size="2">en-US</font> and <font size="2">ja-JP</font> indicate that those locale packages are for English and Japanese, respectively.</p>
<h4>Replacing text with entity references in XUL</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">To make the extension support multiple languages, you will need to separate out all the hard-coded display text in clock.xul and move it to a DTD file. The DTD file is located inside the locale package, and the correct file is picked automatically to match the user’s language preferences. Edit clock.xul to match Listing 8. Add the DOCTYPE declaration that references the DTD file, and replace “clock” and “current time” with entity references.</p>
<h4>Create the DTD that defines the entity references</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Now create the DTD file that gets referred to by clock.xul (Listing 9). The clock.dtd file inside the ja-JP folder must be encoded as UTF-8.</p>
<h4>Operations check</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">This brings us to the operations check. Because we’ve changed the chrome manifest, we need to relaunch Firefox once, as discussed in the sidebar “Operations checks on revised source files.” So relaunch Firefox and display the clock window to make sure it is working correctly. Then type <font size="2">about:config</font> into the location bar, and change the value of general.useragent.locale from <font size="2">en</font> to <font size="2">ja</font>, and re-display the clock window to confirm that it appears in Japanese now.</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 7: Additional content for chrome.manifest</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">locale helloworld en-US locale/en-US/</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">locale helloworld ja-JP locale/ja-JP/</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 8: Revisions to clock.xul</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">1: &lt;?xml version="1.0"?&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">2: &lt;?xml-stylesheet href="<a class=" external" href="chrome://global/skin/" rel="freelink">chrome://global/skin/</a>"?&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">3: &lt;!DOCTYPE dialog SYSTEM "<a class=" external" href="chrome://helloworld/locale/clock.dtd" rel="freelink">chrome://helloworld/locale/clock.dtd</a>"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">4: &lt;dialog id="clockDialog"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">5: xmlns="<a class=" external" href="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" rel="freelink">http://www.mozilla.org/keymaster/gat...re.is.only.xul</a>"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">6: title="&amp;helloworld.clock;"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">7: buttons="accept"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">8: onload="initClock();"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">9: &lt;script type="application/x-javascript" src="<a class=" external" href="chrome://helloworld/content/clock.js" rel="freelink">chrome://helloworld/content/clock.js</a>" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">10: &lt;hbox align="center"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">11: &lt;label value="&amp;helloworld.currentTime;:" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">12: &lt;textbox id="currentTimeTextbox" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">13: &lt;/hbox&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">14: &lt;/dialog&gt;</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 9: Content for clock.dtd</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">locale\en-US\clock.dtd:</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;!ENTITY helloworld.clock "Clock"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;!ENTITY helloworld.currentTime "Current Time"&gt;</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">locale\ja-JP\clock.dtd:</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;!ENTITY helloworld.clock "</font><font face="DejaVu Sans"><font size="2">時計</font></font><font size="2">"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;!ENTITY helloworld.currentTime "</font><font face="DejaVu Sans"><font size="2">現在時刻</font></font><font size="2">"&gt;</font></p>
<p> </p>
<h3><font size="4">Phase 4: Adding a toolbar button</font></h3>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">In Phase 4, we’ll use graphics and style sheets to create a toolbar button that will open the clock window (Figure 9).</p>
<h4>Directory structure</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">The style sheet and graphics files will be located inside the chrome’s skin package. The extension will actually work fine even if these are located in the content package, but putting them in the skin package has the benefit of making it possible to design GUIs that match specific Firefox themes, and allowing theme developers to create special visual effects for extensions.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">First, add the <font size="2">skin</font> directory and its subordinate files to your <font size="2">helloworld</font> directory as shown in Figure 10. The purpose of each of these files is explained in Table 4. Download the files icon.png and icon-small.png from the resources website and place them appropriately.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 9: Toolbar after Phase 4</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">[figure]</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">skin package</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">[/figure]</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 10: Directory structure with skin package added</p>
<h4>Add the skin package</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Update chrome.manifest with the contents of Listing 10.</p>
<h5>Register skin package (skin instruction)</h5>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Line 1 beginning <font size="2">skin</font> is used to register the skin package; <font size="2">helloworld</font> is the package name; <font size="2">skin/classic/</font> is the relative path to the folder containing the source files; <font size="2">classic/1.0</font> indicates that this skin package is meant for the Firefox standard theme.</p>
<h5>Cross-package overlays with style sheets (style instruction)</h5>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">In Phase 1, we used the <font size="2">overlay</font> instruction to invoke a cross-package overlay with XUL; to do this with a style sheet, we use the <font size="2">style</font> instruction. Lines 2-3 create overlays on the browser window and the “customize toolbar” window.</p>
<h4>Add the toolbar button</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">To add the toolbar button to the browser window, update overlay.xul as shown in Listing 11. In order to make it so that users can relocate the button where they like, you will add a new <font size="2">toolbarbutton</font> element, using the special element <font size="2">toolbarpalette</font> with the id <font size="2">BrowserToolbarPalette</font> as the merge point. You’ll also add a new <font size="2">command</font> element, so that a click on the toolbar button will share its process with the menu item.</p>
<h4>Create the style sheet</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Update the file overlay.css with the contents of Listing 12. Lines 1–3 define the icon image to use with full-sized icons, and lines 4–6 define the icon image to use when small toolbar icons have been selected.</p>
<h4>Operations check</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Since we have edited the chrome manifest, as we did in Phase 3, we need to relaunch Firefox. Check to make sure that, when you click on the “customize toolbar” button at the top-right of the window that the new toolbar icon appears in the resulting window. Drag it onto the toolbar, and click it to make sure it works correctly.</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Table 4: Files used in Phase 4</p>
<table border="1" cellpadding="8" cellspacing="0" width="590"> <colgroup><col width="158"> <col width="398"> </colgroup><tbody> <tr valign="top"> <td width="158"> <p align="justify" style="font-style: normal; font-weight: normal;">name</p> </td> <td width="398"> <p align="justify" style="font-style: normal; font-weight: normal;">purpose</p> </td> </tr> <tr valign="top"> <td width="158"> <p align="justify" style="font-style: normal; font-weight: normal;">icon.png</p> </td> <td width="398"> <p align="justify" style="font-style: normal; font-weight: normal;">The full-size toolbar button icon file</p> </td> </tr> <tr valign="top"> <td width="158"> <p align="justify" style="font-style: normal; font-weight: normal;">icon-small.png</p> </td> <td width="398"> <p align="justify" style="font-style: normal; font-weight: normal;">the small toolbar button icon file</p> </td> </tr> <tr valign="top"> <td width="158"> <p align="justify" style="font-style: normal; font-weight: normal;">overlay.css</p> </td> <td width="398"> <p align="justify" style="font-style: normal; font-weight: normal;">Style sheet that defines the toolbar buttons to use. Overlays both the browser window and the “customize toolbar” window.</p> </td> </tr> </tbody>
</table>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 10: Additional content for chrome.manifest</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">1. skin helloworld classic/1.0 skin/classic/</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">2. style <a class=" external" href="chrome://browser/content/browser.xul" rel="freelink">chrome://browser/content/browser.xul</a> <a class=" external" href="chrome://helloworld/skin/overlay.css" rel="freelink">chrome://helloworld/skin/overlay.css</a></font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">3. style <a class=" external" href="chrome://global/content/customizeToolbar.xul" rel="freelink">chrome://global/content/customizeToolbar.xul</a> <a class=" external" href="chrome://helloworld/skin/overlay.css" rel="freelink">chrome://helloworld/skin/overlay.css</a></font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 11: Revisions to overlay.xul</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;?xml version="1.0"?&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;overlay id="helloworldOverlay"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">xmlns="<a class=" external" href="http://www.mozilla.org/keymaster/gatekeeper/" rel="freelink">http://www.mozilla.org/keymaster/gatekeeper/</a> ↩</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">there.is.only.xul"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;commandset id="mainCommandSet"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;command id="helloworldCommand"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">oncommand="window.openDialog(</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">'<a class=" external" href="chrome://helloworld/content/clock.xul" rel="freelink">chrome://helloworld/content/clock.xul</a>',</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">'Clock','chrome,centerscreen,modal');" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/commandset&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;toolbarpalette id="BrowserToolbarPalette"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;toolbarbutton id="helloworldButton"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">label="Hello, World!"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">class="toolbarbutton-1"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">command="helloworldCommand" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/toolbarpalette&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menupopup id="menu_ToolsPopup"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menuitem id="helloworldMenuitem"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">label="Hello, World!"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">insertbefore="sanitizeSeparator"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">command="helloworldCommand" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/menupopup&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/overlay&gt;</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 12: Content for overlay.css</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">#helloworldButton {</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">list-style-image:</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">url(<a class=" external" href="chrome://helloworld/skin/icon.png" rel="freelink">chrome://helloworld/skin/icon.png</a>);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">}</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">toolbar[iconsize="small"] #helloworldButton {</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">list-style-image:</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">url(<a class=" external" href="chrome://helloworld/skin/icon-small.png" rel="freelink">chrome://helloworld/skin/icon-small.png</a>);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">}</font></p>
<p> </p>
<h3><font size="4">Phase 5: XPI packaging</font></h3>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">We actually completed our Hello World extension in Phase 4, but you can’t distribute the source files in this state to other users. So we need to create an xpi-formatted installer.</p>
<h4>XPI file directory structure</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">XPI is basically just a zip archive, but the directory structure inside an xpi file is generally different from your development directory structure (Figure 11).</p>
<h4>Packaging procedure</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">To package your extension as an xpi while preserving the directory structure of the source files you used during development, make the changes shown in Figure 11, as described below.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">1. Create a new chrome directory inside the helloworld directory.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">2. Zip the content, locale, and skin directories<sup><a class="sdfootnoteanc" href="#sdfootnote1sym" name="sdfootnote1anc"><sup>1</sup></a></sup>, rename the resulting archive helloworld.jar, and place it inside the chrome directory you created in Step 1.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">3. Copy chrome.manifest and rename the backup chrome.manifest.bak.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">4. Change the content of chrome.manifest to be in line with Listing 13.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">5. Zip install.rdf, chrome.manifest, and the chrome folder together, and rename the resulting archive helloworld.xpi.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">That completes the process of XPI packaging. One point you need to take special note of is that in Step 4, you updated chrome.manifest so that the files it refers to have paths pointing inside the zip archive.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">In order to go back into development mode after you’ve finished packaging the extension as an xpi, you need to revert the chrome.manifest you edited in Step 4 to point to the backup files in Step 3. If you forget to do this, any subsequent changes you make to your source files during development will not be reflected. You may want to write a batch file to automate this task.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">There’s a simpler way to package an extensions as an xpi. Create a zip archive containing install.rdf, chrome.manifest, content, locale, and skin. Name this helloworld.xpi.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">This omits creating the archive of the chrome packages called helloworld.jar. This kind of xpi file will run fine, but will slow down Firefox’s launch time.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 11: Directory structure inside an xpi file</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 13: Revisions to chrome.manifest</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">content helloworld jar:chrome/helloworld.jar!/content/</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">overlay <a class=" external" href="chrome://browser/content/browser.xul" rel="freelink">chrome://browser/content/browser.xul</a> <a class=" external" href="chrome://helloworld/content/overlay.xul" rel="freelink">chrome://helloworld/content/overlay.xul</a></font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">locale helloworld en-US jar:chrome/helloworld.jar!/locale/en-US/</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">locale helloworld ja-JP jar:chrome/helloworld.jar!/locale/ja-JP/</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">skin helloworld classic/1.0 jar:chrome/helloworld.jar!/skin/classic/</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">style <a class=" external" href="chrome://browser/content/browser.xul" rel="freelink">chrome://browser/content/browser.xul</a> <a class=" external" href="chrome://helloworld/skin/overlay.css" rel="freelink">chrome://helloworld/skin/overlay.css</a></font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">style <a class=" external" href="chrome://global/content/customizeToolbar.xul" rel="freelink">chrome://global/content/customizeToolbar.xul</a> <a class=" external" href="chrome://helloworld/skin/overlay.css" rel="freelink">chrome://helloworld/skin/overlay.css</a></font></p>
<p> </p>
<h4>XPI operations check</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">After you create the xpi file, make sure it works. Since you’ve already got the development version of the Hello World extension running as a test install on your current profile, restart Firefox under a different profile (see footnote 1). Drag and drop the xpi file onto your browser window to start the installation.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">This completes the explanation of how to create and package a simple “hello world” extension.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;"> </p>
<h2><font size="4">Developing practical extensions:<br>
A session-management extension</font></h2>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">In this section, we will create an extension that uses one of the new features in Firefox 2: the session store API. This will allow the user to save and restore session snapshots (browser window states) at any time.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"> </p>
<h3><font size="4">Phase 1: test install</font></h3>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 12 shows what the interface for the session-management extension will look like. Under the Tool menu, the Session Store submenu contains two items—Save Session and Clear Sessions; in between them is a list of previously saved sessions that you can restore, most recent first.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">As discussed in the sidebar “The Session Store API,” sessions are represented as JSON character strings. Sessions are stored in a subdirectory of the profile directory (called “sessionstore”), with each session stored as a text file in it (Figure 13).</p>
<h4>Source file structure</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 14 shows the directory structure for this project.</p>
<h4>Creating install manifest</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Create your work folder, and create an install manifest with the contents of Listing 1, but in this case, change <font size="2">em:id</font> to <font size="2"><a class=" link-mailto" href="mailto:sessionstore@xuldev.org" rel="freelink">sessionstore@xuldev.org</a></font> and <font size="2">em:name</font> to <font size="2">Session Store</font>.</p>
<h4>Creating chrome manifest</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Create a chrome manifest with the contents of Listing 2.</p>
<h4>Finding overlay merge points</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Use the same overlay merge point and element to add on to as you did for the Hello World extension.</p>
<h4>Browser window overlay</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Create a file that will overlay the browser window, overlay.xul, with the contents from Listing 14.</p>
<h4>Test install</h4>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Perform a test install using pointer files as you did in the previous section. Afterwards, you should see a Session Store menu item under the Tools menu.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 12: Session-management extension interface</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">[figure]</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Firefox &lt;&lt; Save session Saved session directory</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">&gt;&gt; Restore session</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Sessions saved under file names that indicate time of snapshot.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">[/figure]</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 13: Schematic of session saving and restoring</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 14: Source file directory structure</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 14: Content for overlay.xul</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;?xml version="1.0"?&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;overlay id="sessionstoreOverlay"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">xmlns="<a class=" external" href="http://www.mozilla.org/keymaster/" rel="freelink">http://www.mozilla.org/keymaster/</a> ↩</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">gatekeeper/there.is.only.xul"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;script type="application/x-javascript"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">src="<a class=" external" href="chrome://sessionstore/content/overlay.js" rel="freelink">chrome://sessionstore/content/overlay.js</a>" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menupopup id="menu_ToolsPopup"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menu label="Session Store"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">insertbefore="sanitizeSeparator"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menupopup&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menuitem label="Save Session" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menuseparator /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;!-- Dynamically generated menu items go here --&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menuseparator /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menuitem label="Clear Sessions" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/menupopup&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/menu&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/menupopup&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/overlay&gt;</font></p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">Phase 2: Implement functionality</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">In Phase 2, we’ll use JavaScript to implement the session save and restore functions using the session store API.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Figure out JavaScript skeleton</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Decide on method names and variable names for each of the processes you’ll need to implement these features, and put them into an overlay.js file. Follow the code in Listing 15.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Wrap methods and variables as properties of objects</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">You’ll note that in Listing 15, I’ve defined a lot of different methods and variables as properties of a single object, <font size="2">gSessionStore</font>. Take note that when using an extension to create an overlay on the browser window, any global variables and functions defined within JavaScript that get opened as overlays on top of browser.xul all become properties of the browser.xul <font size="2">window</font> object. This means that all those variables and functions get mixed up with those defined by Firefox itself and any other extensions that are running. Wrapping everything in one object is a good way to isolate extensions from each other and keep them from trampling each others’ toes. Defining a class would be another good way.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Initializing when opening browser window</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">With extensions, there are a lot of situations where you want to perform some kind of initialization when the browser window opens. To do that, add an event listener for the <font size="2">load</font> event that targets the <font size="2">window</font> object. In Listing 15, an <font size="2">init</font> method runs when the window opens, and an <font size="2">uninit</font> method runs when the window closes.</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 15: Content for overlay.js</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">var gSessionStore = {</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">// Directory to save sessions (nsILocalFile)</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">_dir: null,</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">// Initialization</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">init: function() { },</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">// uninitialization</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">uninit: function() { },</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">// Save session (event handler)</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">save: function(event) { },</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">// Restore session (event handler)</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">restore: function(event) { },</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">// Delete session (event handler)</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">clear: function(event) { },</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">// Dynamically generate menu items (event handler)</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">createMenu: function(event) { },</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">// Read file</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">_readFile: function(aFile) { },</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">// Write file</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">_writeFile: function(aFile, aData) { },</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">};</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">window.addEventListener("load", function(){</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">gSessionStore.init(); }, false);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">window.addEventListener("unload", function(){</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">gSessionStore.uninit(); }, false);</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 16: Revisions to overlay.xul</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menupopup onpopupshowing="gSessionStore.createMenu(event);"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">oncommand="gSessionStore.restore(event);"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menuitem label="Save Session" oncommand="gSessionStore.save(event);" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menuseparator /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;!-- Dynamically generated menu items go here --&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menuseparator /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;menuitem label="Clear Sessions" oncommand="gSessionStore.clear(event);" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/menupopup&gt;</font></p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Adding event handlers</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Add the four event handlers you defined in overlay.js to overlay.xul (Listing 16). This takes advantage of event bubbling<sup><a class="sdfootnoteanc" href="#sdfootnote2sym" name="sdfootnote2anc"><sup>2</sup></a></sup>, appending the <font size="2">restore</font> event handler to the <font size="2">menupopup</font> element one layer above the <font size="2">menuitem</font> element. In the code for the <font size="2">save</font> and <font size="2">restore</font> event handlers, we will actually need blocks to prevent event bubbling.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Implementing methods</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Now we’re going to actually implement the methods we defined in the <font size="2">gSessionStore</font> object, one at a time. In the interest of space, I’m not including all the code here, but you can download it from the following URL: <a class=" external" href="http://www.xuldev.org/samples/sd/" rel="freelink">http://www.xuldev.org/samples/sd/</a></p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">init method</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">The <font size="2">init</font> method is as shown in Listing 17. The <font size="2">init</font> method first gets the <font size="2">sessionstore</font> directory inside the profile directory using nslLocalFile, which we’ll refer to through the variable <font size="2">_dir</font> in subsequent lines (lines 3–6); if the directory doesn’t exist, we’ll create it (lines 7–8)<sup><a class="sdfootnoteanc" href="#sdfootnote3sym" name="sdfootnote3anc"><sup>3</sup></a></sup>.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">uninit method</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Listing 18 gives the code for the <font size="2">uninit</font> method. This just discards the variable <font size="2">_dir</font> that was created by the <font size="2">init</font> method. This process actually isn’t important.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">save method</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Listing 19 gives the code for the save method. In line 3, you see the block that prevents the bubbling that would result in this being called by the <font size="2">oncommand</font> handler in the <font size="2">menupop</font> element, which is the parent of the <font size="2">menuitem</font> element. In lines 4–6, we use the <font size="2">nslSessionStore</font> interface’s <font size="2">getBrowserState</font> method to get a JSON text-string representation of the state s of all currently open browser windows. In lines 7–9, we create the target file’s <font size="2">nslFile</font> object by duplicating the <font size="2">_dir</font> property. In line 10, we use the <font size="2">_writefile</font> method (which we’ll get to shortly) to write the JSON string representing the session to the file.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">restore method</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Listing 20 shows the event handler for the dynamically generated menu items, which were created using the <font size="2">createMenu</font> method (which we’ll get to shortly). Lines 3–6 get the file name from the special attribute <font size="2">fileName</font> that was added by the <font size="2">createMenu</font> method, and read in the JSON text string from that file using the <font size="2">_readFile</font> method (which we’ll get to shortly). Lines 7–9 use the <font size="2">nslSessionStore</font> interface’s <font size="2">setWindowState</font> method to restore the session, using the current window as an origin point. Session restoration overwrites any currently open tabs.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">clear method</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">This uses the <font size="2">directoryEntries</font> property of the <font size="2">nslFile</font> interface to delete all files within the directory you got earlier. Refer to the section “Traversing folders” in Chapter 4.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">_readFile method</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Reads the <font size="2">nslFile</font> object passed as a parameter, and returns its contents as a text string. When the file is read in, its contents are converted from UTF-8 to Unicode.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">_writeFile method</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Creates a new file for the nslFile object passed as a parameter, and writes the text string, also passed as a parameter. Upon writing, the text is converted from Unicode to UTF-8.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">createMenu method</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">This event handler is called when the Session Store submenu opens. First, it deletes the <font size="2">menuitem</font> elements that were dynamically generated the last time the submenu opened, and which are still left over. Next, it dynamically generates <font size="2">menuitem</font> elements based on the names of all the files in the session-storage directory, and inserts them into the menu.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Operations check</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">This completes the functional implementation. Open a new window and make sure that all three functions—saving a session, restoring a session, and deleting sessions—all work correctly.</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 17: Content for init method</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">1: init: function()</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">2: {</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">3: var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">4: .getService(Components.interfaces.nsIProperties);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">5: this._dir = dirSvc.get("ProfD", Components.interfaces.nsILocalFile);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">6: this._dir.append("sessionstore");</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">7: if (!this._dir.exists())</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">8: this._dir.create(this._dir.DIRECTORY_TYPE, 0700);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">9: },</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 18: Content for uninit method</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">1: uninit: function()</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">2: {</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">3: this._dir = null;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">4: },</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 19: Content for save method</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">1: save: function(event)</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">2: {</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">3: event.stopPropagation();</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">4: var ss = Components.classes["@mozilla.org/browser/sessionstore;1"]</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">5: .getService(Components.interfaces.nsISessionStore);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">6: var state = ss.getBrowserState();</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">7: var fileName = "session_" + Date.now() + ".js";</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">8: var file = this._dir.clone();</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">9: file.append(fileName);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">10: this._writeFile(file, state);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">11: },</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 20: Content for restore method</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">1: restore: function(event)</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">2: {</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">3: var fileName = event.target.getAttribute("fileName");</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">4: var file = this._dir.clone();</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">5: file.append(fileName);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">6: var state = this._readFile(file);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">7: var ss = Components.classes["@mozilla.org/browser/sessionstore;1"]</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">8: .getService(Components.interfaces.nsISessionStore);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">9: ss.setWindowState(window, state, false);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">10: },</font></p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">Phase 3: Creating a preference panel</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">You don’t need to use ini files, registries, or anything like that to give users options they can set through a preference panel. Firefox lets you read and write preference values using handy XPCOM services and create preference panels with interface widgets. In Phase 3, we’ll try this out (Figure 15).</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 15: Finished preference panel</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Directory structure</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 16 shows the directory structure we’ll be using in Phase 3. Table 5 explains the new files being used.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Table 5: Files used in Phase 3</p>
<table border="1" cellpadding="8" cellspacing="0" width="590"> <colgroup><col width="158"> <col width="398"> </colgroup><tbody> <tr valign="top"> <td width="158"> <p align="justify" style="font-style: normal; font-weight: normal;">name</p> </td> <td width="398"> <p align="justify" style="font-style: normal; font-weight: normal;">purpose</p> </td> </tr> <tr valign="top"> <td width="158"> <p align="justify" style="font-style: normal; font-weight: normal;">sessionstore-prefs.js</p> </td> <td width="398"> <p align="justify" style="font-style: normal; font-weight: normal;">File sets defaults for preferences. This should always be located inside defaults\preferences</p> </td> </tr> <tr valign="top"> <td width="158"> <p align="justify" style="font-style: normal; font-weight: normal;">prefs.xul</p> </td> <td width="398"> <p align="justify" style="font-style: normal; font-weight: normal;">The preference panel</p> </td> </tr> </tbody>
</table>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;"> </p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Figure 16: Source file structure</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Deciding the format for your preferences</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Next you need to determine the names, types, and values for your preferences (Table 6). Each preference should be prefixed with something that uniquely identifies your extension—in this case, we’ll use <font size="2">extensions.sessionstore.</font> .</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Table 6: Preference formats</p>
<table border="1" cellpadding="8" cellspacing="0" width="590"> <colgroup><col width="276"> <col width="55"> <col width="209"> </colgroup><tbody> <tr valign="top"> <td width="276"> <p align="justify" style="font-style: normal; font-weight: normal;">Preference name</p> </td> <td width="55"> <p align="justify" style="font-style: normal; font-weight: normal;">Type</p> </td> <td width="209"> <p align="justify" style="font-style: normal; font-weight: normal;">Value</p> </td> </tr> <tr valign="top"> <td width="276"> <p align="justify" style="font-style: normal; font-weight: normal;"><font size="2">extensions.sessionstore.warnOnClear</font></p> </td> <td width="55"> <p align="justify" style="font-style: normal; font-weight: normal;">Boolean</p> </td> <td width="209"> <p align="justify" style="font-style: normal; font-weight: normal;">Activated when deleting sessions. If true, causes a confirmation dialog to appear; if false, deletes immediately.</p> </td> </tr> <tr valign="top"> <td width="276"> <p align="justify" style="font-style: normal; font-weight: normal;"><font size="2">extensions.sessionstore.replaceTabs</font></p> </td> <td width="55"> <p align="justify" style="font-style: normal; font-weight: normal;">Integer</p> </td> <td width="209"> <p align="justify" style="font-style: normal; font-weight: normal;">Activated when restoring sessions.</p> <p align="justify" style="font-style: normal; font-weight: normal;">0: Leave current tabs</p> <p align="justify" style="font-style: normal; font-weight: normal;">1: Overwrite current tabs</p> <p align="justify" style="font-style: normal; font-weight: normal;">2: Ask each time</p> </td> </tr> </tbody>
</table>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Defaults definition file</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Listing 21 gives the contents for the default preferences, as described in Table 6. Save this in defaults\preferences\sessionstoreprefs.js.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Creating the preference panel</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Create prefs.xul with the contents from Listing 22. The <font size="2">prefwindow</font> element is a special root element for creating preference panels, with the <font size="2">prefpane</font> element subordinate to it. You can create an interface similar to Firefox’s options window by inserting multiple <font size="2">prefpane</font> elements, which allows users to switch between panels by clicking buttons. The <font size="2">preference</font> element creates a preference value that can be written and read by the preference panel, with the preference name indicated by the <font size="2">name</font> attribute, and the type by the <font size="2">type</font> attribute. Furthermore, by matching the <font size="2">preference</font> element’s <font size="2">id</font> to the <font size="2">preference</font> attribute of a <font size="2">checkbox</font> or <font size="2">radiogroup</font>, you can specify the interface widget that will be used to set your preferences. Add the contents of Listing 23 to your install manifest to make it so that the Add-on Manager can open your preference panel.</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 21: Content for sessionstore-prefs.js</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">pref("extensions.sessionstore.warnOnClear", true);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">pref("extensions.sessionstore.replaceTabs", 2);</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 22: Content for prefs.xul</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;?xml version="1.0"?&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;?xml-stylesheet href="<a class=" external" href="chrome://global/skin/" rel="freelink">chrome://global/skin/</a>"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">type="text/css"?&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;prefwindow id="sessionstorePrefs"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">xmlns="<a class=" external" href="http://www.mozilla.org/keymaster/" rel="freelink">http://www.mozilla.org/keymaster/</a></font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">gatekeeper/there.is.only.xul"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">title="Session Store Preferences"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;prefpane&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;preferences&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;preference id="extensions.sessionstore.warnOnClear"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">name="extensions.sessionstore.warnOnClear"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">type="bool" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;preference id="extensions.sessionstore.replaceTabs"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">name="extensions.sessionstore.replaceTabs"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">type="int" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/preferences&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;checkbox label="Confirm before clearing sessions"</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">preference="extensions.sessionstore.warnOnClear" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;groupbox&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;caption label="When restoring session:" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;radiogroup</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">preference="extensions.sessionstore.replaceTabs"&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;radio value="0" label="Keep current tabs" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;radio value="1" label="Replace current tabs" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;radio value="2" label="Ask me every time" /&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/radiogroup&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/groupbox&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/prefpane&gt;</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;/prefwindow&gt;</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing 23: Additional content for install.rdf</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">&lt;em:optionsURL&gt;<a class=" external" href="chrome://sessionstore/content/prefs.xul" rel="freelink">chrome://sessionstore/content/prefs.xul</a>&lt;/em:optionsURL&gt;</font></p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Changing behavior through preference values</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">In Listing 24, we add a preference that allows a confirmation dialog to appear when we delete sessions. When we get the preference value from JavaScript, we take advantage the XPCOM <font size="2">nslPrefService</font> interface, using the <font size="2">getBoolPref</font> and <font size="2">getIntPref</font> methods as appropriate to the data type. In Listing 24, we get the <font size="2">nslPrefBranch</font> object that lets us read and write all preferences beginning with the prefix <font size="2">extensions.sessionstore.</font>, which is what lets us use the <font size="2">getBoolPref</font> method to get the value.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Operations check</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">We’ve revised the install manifest, so we need to reinstall the extension (see the sidebar “Operations checks on revised source files”). Once you’ve reinstalled it, first open about:config, and confirm that the previous preference values are set. Next, select Session Store from the Add-on Manager and click the “Preferences” button to bring up the preference panel. Try changing the preferences, and see if those changes are reflected when you restore or delete sessions.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">Phase 4: XPI packaging</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">This procedure is unchanged from Phase 5 in the “Developing a Simple Extension” section, but take note of everything within the defaults folder.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">Conclusion</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">We’ve looked at how to build extensions at an extremely basic level in this chapter, and there are a lot of other interesting features to explore in extensions and XUL. But I hope that having gotten your feet wet with extensions development, you’ll want to dive in now.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">Sidebar: Operations checks on revised source files</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">In the interest of developing extensions efficiently, you want to minimize the number of steps between revising a source file and running an operations check on it. For each type of source file, there’s a different procedure to follow. The following all assumes that you’ve disabled the XUL cache as discussed in the section “Setting up your development environment.”</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">XUL that opens new windows</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Confirm changes by closing and reopening window. If the XUL is displaying as a sidebar, reopen the sidebar. Likewise with any JavaScript, DTDs, or style sheets referenced by the XUL.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">XUL overlaying other windows</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">You need to force the overlay-target XUL to reload. If it’s the browser window that’s the target, you can simply open a new browser window to confirm the changes.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">properties file</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">If you make changes to a properties file in the locale package, those changes will not be reflected in Firefox until you relaunch it.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">XPCOM components inside the components directory</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">You will need to delete the files compreg.dat and xpti.dat inside the profile folder and relaunch Firefox.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">Default preferences file inside the defaults\preferences directory</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">You will need to relaunch Firefox.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">chrome.manifest</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">You will need to relaunch Firefox.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">install.rdf</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">You will need to temporarily uninstall the extension and then reinstall it. In practice, what you’ll do is remove the pointer file, relaunch Firefox, return the pointer file, and relaunch Firefox again. On a Linux system, use the <font size="2">touch</font> command on the directory pointed at by the pointer file to change its “last changed” date, and avoid the relaunching.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">Sidebar: The session store API</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">The session store API<sup><a class="sdfootnoteanc" href="#sdfootnote4sym" name="sdfootnote4anc"><sup>4</sup></a></sup> is one of the new developer-oriented features in Firefox 2. Using the various methods in the <font size="2">nslSessionStore</font> interface, which is an XPCOM service, you can easily save and restore session states. Functions in Firefox that let you reopen the last closed tab, or restore to your previous state after a crash, are all implemented through this API.</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">We used two methods in the <font size="2">nslSessionStore</font> interface in this project. Let’s look at how they work.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">getBrowserState()</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Returns a JSON-formatted<sup><a class="sdfootnoteanc" href="#sdfootnote5sym" name="sdfootnote5anc"><sup>5</sup></a></sup> text string representing the state of every currently open browser window. This string includes extensive information regarding every open window and tab, the forward/back history for each tab, page scroll position, text zoom, contents of form elements, etc.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">setWindowState(aWindow, aState, aOverwrite)</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Restores the browser window indicated by the parameter <font size="2">aWindow</font> to the state indicated by <font size="2">aState</font>. If <font size="2">aOverwrite</font> is true, the currently open tab is overwritten; otherwise, a new tab is created for the restored content.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">Sidebar: JavaScript debugging methods</font></p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">alert</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">The easiest way to debug javascvript is using the <font size="2">window.alert</font> method to display variables in a dialog (Listing A). Except for asynchronous processes, all processing stops while the dialog is up, so this technique is useful when you want to pin down a value that can vary during a process.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">dump</font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">In a Windows environment, you can launch Firefox with the <font size="2">-console</font> argument, which opens a dump console. Using the dump method, you can send text to the dump console (Listing B). The text output being sent to the console does not suspend any other processes, and this is most useful when there’s a high volume of messages being output. In order to use the dump method most effectively, you’ll need to follow the recommendations in “Setting up your development environment.” Note that in a Windows environment, Japanese text sent to the dump console will get corrupted.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4">error console</font></p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">The nslConsoleService interface</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Using the <font size="2">logStringMessage</font> method of the <font size="2">nslConsoleService</font> interface, which is an XPCOM service, you can output text to the “message” panel of the error console. Japanese text will appear correctly here. If you are going to be issuing messages to the error console frequently, it’s handy to define a function like that in Listing C.</p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Components.utils.reportError method</p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">Another way to output text to the same error console is with the <font size="2">Components.utils.reportError</font> method. This will send its output to the “error” panel of the error console.</p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing A: Check the value of variable foo</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">alert(foo);</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing B: List the properties of obj</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">for (var i in obj) {</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">dump(i + " : " + obj[i] + "\n");</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">}</font></p>
<p align="justify" style="margin-bottom: 0.08cm; font-style: normal; font-weight: normal; page-break-after: avoid;">Listing C: Function to output messages to the error console</p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">function log(aText) {</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">var console = Components.classes["@mozilla.org/consoleservice;1"]</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">.getService(Components.interfaces.nsIConsoleService);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">console.logStringMessage(aText);</font></p>
<p align="justify" style="border-style: none none none solid; border-color: -moz-use-text-color -moz-use-text-color -moz-use-text-color rgb(0, 0, 0); border-width: medium medium medium 1pt; padding: 0cm 0cm 0cm 0.15cm; margin-left: 0.33cm; margin-bottom: 0.08cm; font-style: normal; font-weight: normal;"><font size="2">}</font></p>
<p align="justify" style="margin-top: 0.43cm; margin-bottom: 0.1cm; font-style: normal; font-weight: normal; page-break-after: avoid;"><font size="4"><strong>Sidebar: Where can I learn more about the XPCOM interface?</strong></font></p>
<p align="justify" style="margin-bottom: 0.43cm; font-style: normal; font-weight: normal;">The Mozilla Development Center has extensive documentation on the nslSessionStore interface we’ve discussed in this section<sup><a class="sdfootnoteanc" href="#sdfootnote6sym" name="sdfootnote6anc"><sup>6</sup></a></sup>. In general, details (IDL) on the XPCOM interface are available from XULPlanet<sup><a class="sdfootnoteanc" href="#sdfootnote7sym" name="sdfootnote7anc"><sup>7</sup></a></sup> and the Mozilla Cross-Reference<sup><a class="sdfootnoteanc" href="#sdfootnote8sym" name="sdfootnote8anc"><sup>8</sup></a></sup>, which includes source code with full-text searching available.</p>
<p align="justify" style="font-style: normal; font-weight: normal;"> </p>
<div id="sdfootnote1">
<p style="margin-bottom: 0.43cm;"><a class="sdfootnotesym" href="#sdfootnote1anc" name="sdfootnote1sym">1</a>In Windows XP, you can use the “compress folder” command to create a zip archive, but I recommend installing a tool like 7-Zip that lets you specify the compression ratio. It’s OK to have a low compression ratio when you are creating the jar file, and a high compression ratio when creating the xpi file.</p>
</div>
<div id="sdfootnote2">
<p style="margin-bottom: 0.43cm;"><a class="sdfootnotesym" href="#sdfootnote2anc" name="sdfootnote2sym">2</a>An event generated by an element “bubbles up” to the root.</p>
</div>
<div id="sdfootnote3">
<p style="margin-bottom: 0.43cm;"><a class="sdfootnotesym" href="#sdfootnote3anc" name="sdfootnote3sym">3</a>See Code Snippets: File I/O-MDC (<a class=" external" href="http://developer.mozilla.org/en/docs/Code_snippets:File_I/O#Getting_special_files" rel="freelink">http://developer.mozilla.org/en/docs..._special_files</a>) for techniques on getting special folders, like the profile folder.</p>
</div>
<div id="sdfootnote4">
<p style="margin-bottom: 0.43cm;"><a class="sdfootnotesym" href="#sdfootnote4anc" name="sdfootnote4sym">4</a>See Session Store API-MDC (<a class=" external" href="http://developer.mozilla.org/en/Session_store_API" rel="freelink">http://developer.mozilla.org/en/Session_store_API</a>)</p>
</div>
<div id="sdfootnote5">
<p style="margin-bottom: 0.43cm;"><a class="sdfootnotesym" href="#sdfootnote5anc" name="sdfootnote5sym">5</a>JSON is short for JavaScript Object Notation. It is a data format that can easily be read from and written to in JavaScript.</p>
</div>
<div id="sdfootnote6">
<p style="margin-bottom: 0.43cm;"><a class="sdfootnotesym" href="#sdfootnote6anc" name="sdfootnote6sym">6</a>nslSessionStore-MDC (<a class=" external" href="http://developer.mozilla.org" rel="freelink">http://developer.mozilla.org</a>)</p>
</div>
<div id="sdfootnote7">
<p style="margin-bottom: 0.43cm;"><a class="sdfootnotesym" href="#sdfootnote7anc" name="sdfootnote7sym">7</a>XULPlanet (<a class=" external" href="http://www.xulplanet.com/" rel="freelink">http://www.xulplanet.com/</a>)</p>
</div>
<div id="sdfootnote8">
<p style="margin-bottom: 0.43cm;"><a class="sdfootnotesym" href="#sdfootnote8anc" name="sdfootnote8sym">8</a>Mozilla Cross-Reference (<a class=" external" href="http://mxr.mozilla.org/" rel="freelink">http://mxr.mozilla.org/</a>)</p>
</div>
Revert to this revision