Chapter 16. Writing an Extension

Firefox uses extensions to enhance its functionality. An extension is a package consisting of chrome modifications and executable content (usually in JavaScript). Extensions are installed using the Extensions Manager, found on the Tools menu.

Extensions provide varied functionality enhancements, variety, and versatility. There are navigation extensions, humor extensions, searching extensions, and so on—in all, there are about 20 categories of extensions for Firefox. The actual number of possible extensions is impossible to estimate. The Mozilla website has perhaps 500 extensions for all its products, with many more posted on its developer’s website.

In this chapter we will cover tools and extensions for extension development. We’ll also discuss the Extensible User Interface Language (XUL), JavaScript, and resources for extension developers.

Setting Up the Development Environment

First, the suggestions in this section are not absolute. You can install and configure Firefox differently and still be a successful extension developer. However, my experience has taught me that these steps are very useful in starting extension development.

I recommend a new, clean installation of Firefox. If Firefox is already installed, I recommend uninstalling it and checking to make sure the Firefox program folder has been deleted.

Next, delete your profiles! Back them up if you want, so you can later reinstall them, but for now, you will want a clean profile.

After you have uninstalled Firefox and cleared all your profiles, reinstall Firefox.

Now, your Firefox installation will have all components from the same installation source, thereby avoiding corrupt installation files. As well, because the profiles will be clean, you will be starting out clean.

After you have installed Firefox, launch it. Firefox will go to the default home page. In the Location bar, type about:config to go to the preferences configuration page. You will change several preferences to values that improve your development environment.

Tip

Generally, the only extensions you want installed in Firefox when developing are those that are specifically intended to assist you in creating your extension. Delete any other extensions.

Preferences for Extension Developers

In the about:config window, check the following preferences. Set them as indicated, and then restart Firefox. (Restarting ensures that your changes have taken effect.)

If you determine any preferences are missing during the Firefox installation, add them using about:config. Check the following preferences:

  • nglayout.debug.disable_xul_cache = true—Setting this preference to true turns off the XUL cache. Any XUL changes made will not require that you restart Firefox.

  • browser.dom.window.dump.enabled = true—Setting this preference to true causes the dump() statement to print to the JavaScript Console. You must restart Firefox with the –console option to use this preference. Try creating a shortcut icon on your desktop with the –console option added to the command line (see Figure 16.1).

    Set your Firefox –console option as shown.

    Figure 16.1. Set your Firefox –console option as shown.

  • javascript.options.showInConsole = true—This preference enables the JavaScript Console to log chrome errors.

  • javascript.options.strict = true—With this preference, warnings are also sent to the JavaScript Console. This can generate more messages because warnings for all the extensions are sent to the JavaScript Console, not just those generated by your extension.

The two JavaScript preferences require that you run Firefox with the console for them to be useful. To start Firefox with the console active, modify your desktop shortcut to Firefox as shown in Figure 16.1. Add –console after the final quotation mark in the Target string. If you add it before the final quotation mark, it will not work.

Caution

Of all these settings, nglayout.debug.disable_xul_cache is the most vital. Failing to disable the XUL cache can result in some very strange behavior when modifying your extension.

Now, start Firefox one final time. Check about:config to be sure your four development preferences are correctly set. Switch to the console and see what it looks like. Figure 16.2 shows the Firefox console during a run of Firefox.

The console is a separate window that looks a bit like a command session.

Figure 16.2. The console is a separate window that looks a bit like a command session.

Note

When javascript.options.strict=true is set, the console displays in addition to errors generated by your code errors that are generated by other code (such as other extensions). This is a good reason to not have any unnecessary extensions loaded while developing your extension.

Tools for Extension Development

There are several useful development tools, most of which are implemented as Firefox extensions.

Prior to starting development, go to Mozilla’s Firefox extensions page and see which developer extensions are currently available. Check this page frequently because the list changes often.

The following section contains information on the Extension Developer extension. This particular extension, designed for extension developers, can save you a great deal of time if used properly.

The Extension Developer Extension

One of the most useful tools for extension development is the Extension Developer. This tool was written by Ted Mielczarek and can be downloaded at http://ted.mielczarek.org/code/mozilla/extensiondev/.

Extension Developer installs a new pop-up menu under Firefox’s Tools menu selection. The eight choices in this menu are

  • Extension Builder

  • Toggle Debugging Prefs

  • JavaScript Shell

  • JavaScript Environment

  • JavaScript Injector

  • HTML Editor

  • XUL Editor

  • Reload All Chrome

You can quickly try each of these options, as described in the following.

Extension Builder

The Extension Builder is the most powerful and useful feature of Extension Developer. Shown in Figure 16.3 is the dialog box that appears when this feature is selected.

One useful part of Extension Builder is its capability to edit the install.rdf file automatically.

Figure 16.3. One useful part of Extension Builder is its capability to edit the install.rdf file automatically.

Other features of the Extension Builder include the capability to build the extension, install it, determine its folder location, and show its installed folder.

The install.rdf editor is able to read the extension’s install.rdf file. All extensions must have an install.rdf file (this file tells Firefox all about the extension).

Toggle Debugging Prefs

In the previous section, “Preferences for Extension Developers,” you set some preferences when developing extensions. The Extension Developer’s menu item Toggle Debugging Prefs toggles three of the four recommended extension building preferences:

  • nglayout.debug.disable_xul_cache

  • browser.dom.window.dump.enabled

  • javascript.options.showInConsole

Being able to toggle these preferences is important because, when they are set, Firefox performance can be seriously compromised.

The one preference that is not toggled is javascript.options.strict. If your extension uses JavaScript and you are encountering problems, manually enable javascript.options.strict preference, as earlier described.

JavaScript Shell

Sometimes the JavaScript Console’s one-line JavaScript evaluator is not sufficient for testing pieces of JavaScript code. The JavaScript Shell interface can be very useful in such cases.

In Figure 16.4, the JavaScript Shell was started. The first six lines in the window are welcome text, complete with some links (including Math, help, and enumerate Windows()).

The variable MyVar is assigned a string; then MyVar's contents are displayed in an Alert box.

Figure 16.4. The variable MyVar is assigned a string; then MyVar's contents are displayed in an Alert box.

The steps to display the Alert box shown in Figure 16.4 are shown here:

  1. On the seventh line, MyVar = "This is an alert message!" is the first line typed into JavaScript Shell’s window.

  2. The JavaScript Shell responds by printing the new value of MyVar, in the eight line.

  3. In the final line, alert(MyVar) is typed and Enter is pressed. The Alert box is displayed, using the text you saved in MyVar.

If you click the help link (end of the third line), a short but useful help page is displayed. After using this small window for a while, you might find that it is indispensable when developing simple JavaScript code.

JavaScript Environment

The JavaScript Environment is similar in concept to the JavaScript Shell. The most important difference is that the JavaScript Environment allows you to develop more complex JavaScript code.

The JavaScript Environment window is shown in Figure 16.5. In this example, I’ve written a simple JavaScript program that prints the squares of numbers between zero and five. (When you start the JavaScript Environment, you will see the inspiration for this program.) To make this example a bit more than trivial, I’ve created a function to do the square function.

In the JavaScript Development Environment window, the top half is the window where you enter your code.

Figure 16.5. In the JavaScript Development Environment window, the top half is the window where you enter your code.

Here's my sample code:

print("Squares of numbers 0 through 5:");
for (i = 0; i <= 5; ++i)
  print(SquareIt(i));

function SquareIt(a) {return(a * a);}

This small JavaScript program created the results shown in Figure 16.6.

The browser window shows the actual code; the JavaScript Injector window shows the file being executed; and the Alert window shows the results.

Figure 16.6. The browser window shows the actual code; the JavaScript Injector window shows the file being executed; and the Alert window shows the results.

JavaScript Injector

The JavaScript Injector enables the user to execute JavaScript that has been saved in a file. This allows easy testing of complete JavaScript files without having to cut and paste.

Shown in Figure 16.6 is Firefox with a small sample JavaScript program. This program is similar to the one in the previous example; however, the previous example’s print() statements have been replaced with alert() statements. This was done because the JavaScript Injector runs the JavaScript program in a true JavaScript environment, whereas the other two examples have a text output area available.

In the end, all three of these JavaScript tools are very useful.

Tip

Don’t forget that the JavaScript Console is available when these JavaScript tools are used. For example, if you use the JavaScript Injector and the code doesn’t execute properly, check the JavaScript Console for any error messages.

HTML Editor

Much like the JavaScript Shell, the HTML Editor has a window with two sections. In the upper section you enter your HTML code, and in the bottom, as you are working, you will see the rendered results of the HTML.

This small tool supports clipboard copy and paste, letting you easily move HTML code into and out of the HTML Editor. (This is important because there is no file Open, Save, or Close command.)

Figure 16.7 shows the HTML Editor with a small piece of HTML that was lifted from my cooking web page.

This HTML was created completely in the Real-time HTML Editor. If you are a guy and think you can’t cook, think again.

Figure 16.7. This HTML was created completely in the Real-time HTML Editor. If you are a guy and think you can’t cook, think again.

The Real-time HTML Editor gives you immediate feedback by showing what the HTML code will look like in a browser window.

XUL Editor

When you find that JavaScript won’t do everything you want (and JavaScript is limited, intentionally), the next avenue to greater functionality is XUL. XUL allows for easy development of platform-independent user interfaces, although Java and JavaScript must still be relied on for much of the underlying functionality.

In Firefox, click Edit in the menu for an example of a menu using XUL. It is not a part of Windows. Firefox doesn’t use the Windows GUI interface except for client output. Everything you see in the Firefox window—the menu, the toolbar(s), the status bar, and so on—is a creation of Firefox. Most Windows applications use Windows to display titles, menus, toolbars, and the like. That is fine for those applications, but Firefox was designed to be platform independent.

Someday most applications will use XUL—a victory for developers who don’t want to be tied down to a specific operating system. The XUL Editor is shown in Figure 16.8. This code example is a bit more complex than our previous JavaScript examples, but it does more. Though not obvious, our XUL example uses JavaScript.

XUL frees you from the operating system, just like the operating system frees you from the hardware.

Figure 16.8. XUL frees you from the operating system, just like the operating system frees you from the hardware.

To be more readable, this simple bit of XUL code is shown in Listing 16.1. In this code, several items are highlighted in bold; these are areas that deserve closer inspection.

Example 16.1. The Sample XUL Code from Figure 16.8

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window id="yourwindow" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<label value="This is our XUL example of a menu."/>

<toolbox flex="1">
  <menubar id="My-menubar">
    <menu id="filemenu" label="File">
      <menupopup id="filepopup">
        <menuitem label="New" oncommand="alert('You clicked on File, New in the menu')," />
        <menuitem label="Open"/>
        <menuitem label="Save"/>
        <menuseparator/>
        <menuitem label="Exit"/>
      </menupopup>
    </menu>
    <menu id="editmenu" label="Edit">
      <menupopup id="editpopup">
        <menuitem label="Cut"/>
        <menuitem label="Copy"/>
        <menuitem label="Paste"/>
        <menuseparator/>
        <menuitem label="Undo"/>
        <menuitem label="Redo"/>
      </menupopup>
    </menu>
  </menubar>
</toolbox>

</window>

In Listing 16.1, we have added code to the file menu’s New command. Added is an oncommand attribute. The data assigned to the oncommand attribute is what will happen when the user selects (clicks) this item. In this example, "alert('You clicked on File, New in the menu')," is JavaScript code. I enclosed the entire item in double quotation marks. Usually, a text literal passed to the JavaScript alert() function is enclosed in double quotation marks, but because this entire item is in double quotation marks, I instead used single quotation marks for the alert()’s parameter.

After you get used to using XUL, you will find it easy to create user interface functionality using a combination of XUL and JavaScript.

Reload All Chrome

Chrome is the look and feel of an application’s user interface. Colors, button shapes, fonts, sizes, and so on are all specified as parts of chrome. Chrome is always affected by themes and, to a lesser extent, by extensions. This lesser extent is typically parts of chrome that are directly part of the extension’s functionality (such as added dialog boxes, menu items, and so forth).

When Firefox loads, it loads the chrome. From then until Firefox closes, Firefox does not change the chrome. When a new theme is selected, Firefox implements the theme when it restarts. The same is true in regard to extensions—extensions are loaded and configured at startup, not during the normal operation of Firefox.

The Extension Developer extension forces Firefox to reload the chrome without a restart. The concept is that this makes developing extensions (and themes) much faster. At least that is the idea.

My experience has been that developing extensions can be a risky process. Not only can you load the extension you are developing and have Firefox crash, but you also can damage the profile. In fact, it is too painfully easy to corrupt a profile when developing extensions.

If you are willing to take the risk that a reloading of the chrome might not resolve some problems your extension has caused but that you might not realize have occurred), you should try the Reload All Chrome option.

Debugging Consoles

Two consoles are available to extension developers. The first console is the Firefox Console, which receives messages relating to errors in Firefox. The second console, the JavaScript Console, specifically displays errors relating to JavaScript. Both are vital to creating extensions.

The Firefox Console

The Firefox Console is a separate window started when the Firefox command contains the switch –console.

The Firefox Console displays the streams stdout and stderr in a simple, display-only window (refer to Figure 16.2). Both stdout (which is short for standard output) and stderr (which is short for standard error) are terms that are inherited from the C and Unix days.

Note

Both stdout and stderr are from pre-Windows days. Back when we used command prompts, characters, terminals, and graphics were what we put on the wall and called art. Ah, the good old command prompt days….

It was common to redirect either (or both) to files, but in today’s Windows GUI environments, we don’t have a console or any way to easily grab this valuable information. The Firefox Console solves this problem in a crude but usable way.

stdout is the nonerror output from a number of I/O statements. stderr is error output, either specifically written to stderr by the program or from runtime functionality’s error trapping routines.

The JavaScript Console

The JavaScript Console serves a function very similar to that of the Firefox Console. The JavaScript Console receives error message information from JavaScript about errors and other problems that occur in JavaScript code.

Unlike the Firefox Console, though, the JavaScript Console has some management tools. There are controls to restrict how much information is displayed (All, Errors, and Warnings and Messages) and a Clear button. Plus, the JavaScript Console allows copying information about an error to the Windows clipboard so it can be pasted into other applications if necessary.

The JavaScript Console can be displayed in a Firefox sidebar (see http://www.digitalmediaminute.com/article/1348/updated-open-firefox-javascript-console-in-a-sidebar), as a separate window (select JavaScript Console in the Tools menu), or in a tab (by creating a new tab and entering the following URI in the Location bar):

chrome://global/content/console.xul

Figure 16.9 shows the JavaScript Console displayed as a separate window (go to the Tools menu and click JavaScript Console).

The JavaScript Console; no matter how it’s displayed, it always looks the same.

Figure 16.9. The JavaScript Console; no matter how it’s displayed, it always looks the same.

At the top of the screen are the buttons that limit the display to either a severity level or all messages and a Clear button to clear the console’s display. Below these buttons is a small text area in which you can type a JavaScript statement and have it evaluated by either clicking the Evaluate button or pressing the Enter key.

Another powerful advantage of the JavaScript Console is that it provides information about errors in a consistent format. Each error has a severity level symbol (Error, Warning, or Message), an error description, an error source (usually a file), along with optional error-specific information.

The filename for the error is a link to that file. Click the filename, and the file is opened in a browser window.

Tip

Programmers love to indent their code using tab characters. (Indenting is vital to creating readable code.) I’m going to suggest that you not put tabs in any files you use when creating your extensions. Instead, use spaces to indent.

Many of the error-reporting tools display a pointer to the exact place in the file where the error occurred. This pointer might not reflect the correct position if there are tabs in the source line.

Multiple Instances of Firefox

When developing extensions and themes, it can be useful to have several copies of Firefox running at the same time.

However, if you attempt to run a second copy of Firefox, all that typically happens is that you are switched to the currently running copy—not what you want in this situation.

Mozilla has taken care of this problem. There is a way to force Firefox to open a second independent copy of itself on demand. And, this small feat of magic is easy to do.

Multiple instances of Firefox are allowed when the environment variable MOZ_NO_REMOTE exists and is set to a value of 1. You can do this in one of two ways. One way is to create a batch script file with the following two lines:

Set MOZ_NO_REMOTE=1
Firefox %1 %2 %3 %4 %5

Save this file as firefox2.bat (or any other name you find suitable, as long as you use .bat for the extension).

Next, run this batch file in a command prompt window (go to the Start menu and select All Programs, Accessories, Command Prompt). Or, from the Windows run command box (go to the Start menu and select Run), you can enter the following command:

Firefox2 –p my_second_profile

where my_second_profile is the name of a profile different from the profile the other copy of Firefox is running. If you fail to specify a profile that is not in use, Firefox might prompt for one.

Note

You can’t run two copies of Firefox using the same profile. Plan ahead and create a clone of your profile, so you won’t have to reinstall extensions and themes when using these techniques.

The second method is to set a permanent user environment variable, as the following steps show:

  1. Open the Windows Control panel.

  2. Double-click System.

  3. Click the Environment Variables button at the bottom (just above the OK button).

  4. In the top section, labeled User Variables for, click New (see Figure 16.10).

    Make sure the variable name is in uppercase and that the value is the number 1.

    Figure 16.10. Make sure the variable name is in uppercase and that the value is the number 1.

  5. In the Variable Name box, type MOZ_NO_REMOTE.

  6. In the Variable Value box, type 1.

  7. Click OK to save this variable; then click OK to close the Environment Variables window.

  8. Click OK to close the System window, and close the Windows Control panel.

After you finish adding the new environment variable, all that is necessary to start another copy of Firefox is to click the Firefox icon!

Understanding XML User Interface Language

Firefox makes extensive use of XUL. In the previous section, “Extension Developer,” the effect of a simple XUL fragment was shown. Of course, that example is not a full application, but it does show what XUL can do.

XUL Syntax

XML User Interface Language (XUL), which is based on XML, has a syntax that at first glance seems similar to HTML. This syntax is made up of events, widgets, tags, and data. There are some rules that are global and must be followed. Four basic rules are

  • Events and attributes must be in lowercase only. No mixed case, except for quoted strings, is allowed.

  • Strings must be double quoted.

  • Widgets must have close tags. Valid close tags are <tag>, </tag>, and <tag/>.

  • Attributes, when specified, must be assigned a value.

Comments

Comments in XUL and XML are begun with the characters <!-- and end with -->. The number of hyphens in a beginning or ending comment must be exactly two—no more, no less. Comments must not contain any double hyphens. A sample comment is shown here:

<!-- This is a valid comment -->

Processing Instructions

Processing instructions are lines that contain instructions for the application that is processing the XUL or XML. An example of a processing instruction is

<$xml version="1.0"$>

This instruction tells the application that the version of XML is 1.0.

XUL Example

Earlier in “XUL Editor,” you created a simple window with a menu. The code was shown previously in Listing 16.1. Now let’s take a closer look at this code, on a line-by-line basis:

<?xml version="1.0"?>

This is an application directive specifying the XML version.

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

This is another application directive specifying the stylesheet used.

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

This creates the window and gives it the ID "yourwindow". The XML name space (xmlns) is also specified.

<label value="This is our XUL example of a menu."/>

This creates a label at the top of the window. We specify the text in double quotation marks.

<toolbox flex="1">

Next, we create a toolbox that holds one or more toolbar widgets. The flex="1" part describes how unused space is divided between children.

<menubar id="My-menubar">

This creates a container that will hold menu items.

<menu id="filemenu" label="File">

This line creates a top-level menu item labeled File.

<menupopup id="filepopup">

Next, we create a pop-up menu (one that drops down, actually) that is displayed whenever the user clicks the top-level menu item, Files.

<menuitem label="New" oncommand="alert('You clicked on File, New in the menu')," />

Our first pop-up menu item is labeled New and has an action that takes place when the user clicks it. The action is a JavaScript alert() box displaying the text specified.

<menuitem label="Open"/>
<menuitem label="Save"/>

These are the two additional pop-up menu items. Neither has an action that will take place when it is clicked. Eventually, however, it will be necessary to write the handlers.

<menuseparator/>

A menu separator draws a line between pop-up menu items.

<menuitem label="Exit"/>

This is the last pop-up menu item.

</menupopup>

This line closes the pop-up menu.

</menu>

And this line closes the File menu.

<menu id="editmenu" label="Edit">

Next, we create a second top-level menu item named Edit.

<menupopup id="editpopup">
  <menuitem label="Cut"/>
  <menuitem label="Copy"/>
  <menuitem label="Paste"/>
  <menuseparator/>
  <menuitem label="Undo"/>
  <menuitem label="Redo"/>
</menupopup>

This adds a pop-up menu to our Edit menu. None of these items have any actions written for them yet.

</menu>

This ends the Edit menu.

</menubar>

And this ends the menu bar.

</toolbox>

Now we end the toolbox.

</window>

And finally we end the window definition. (A copy of this script is available on the book’s website at http://www.quepublishing.com/title/0789734583.)

The best source of reference information for XUL is http://www.xulplanet.com.

Working with JavaScript

Java or JavaScript? And, what’s the difference?

Java is a programming language developed and controlled by Sun Microsystems. It is proprietary, licensed, and readily available on a number of platforms.

JavaScript was developed by Netscape as a scripting language for its browsers.

The difference between Java and JavaScript is roughly the same as the difference between C/C++ and Java (or C/C++ and JavaScript). There are similar syntax requirements, and some of the names are the same (or sufficiently similar that it is easy to figure out what’s what). For many years, C and C++ have been the programming languages of choice for many applications and systems. Unix, Linux, and Windows contain extensive C and C++ code. Even Firefox has thousands of C and C++ source files.

JavaScript Reference

I’m including a simple JavaScript reference here. For a more complete reference, try http://wp.netscape.com/eng/mozilla/3.0/handbook/javascript/, which contains tutorial and reference sections. JavaScript supports operations (assignments, math, and so on), conditionals (if, loop, and so forth), objects, and functions. JavaScript statements are

  • break—This immediately ends a while or for loop.

  • comment—This inserts a comment into the JavaScript code.

  • continue—This ends the current iteration of a loop and begins a new iteration. Essentially, it skips following statements.

  • for—This is a basic looping structure in which the start, condition, and increment can be specified.

  • for...in—This iterates the variable for all properties in an object.

  • function—This creates a function that can be called from multiple locations. Optionally, it returns a value.

  • if...else—This is the basic conditional test block.

  • new—This creates a new array, Boolean, date, functions, math, number, or string object.

  • return—This returns from a function, optionally passing back a return value.

  • this—This is a reference to the current object.

  • var—This declares and initializes a variable. It is not an error to initialize a variable, but it is bad programming practice.

  • while—This creates a conditional loop structure that is executed as long as the condition is true.

  • with—This allows you to specify a default object for a collection of statements.

The Firefox window Object

The Firefox window object is used to create all windows in Firefox. They can be manipulated as a whole (such as applying a theme) or individually (adding a menu item or button to one window), as the developer desires.

Take a look at Firefox’s main window. It has a title bar, a menu bar, toolbars, scrollbars, status bars, and other features. When a window is created, its feature can be specified. Features are classified into three broad categories:

  • Position and size—Enable the specification of a window’s size and position.

  • Toolbars and chrome—Control which features (as mentioned previously) the window will have.

  • Window functionalities—Used to describe how the window will behave. These include resizing, scrollbars, and other similar functions.

A good reference on the windows object is at http://www.mozilla.org/docs/dom/domref/dom_window_ref.html.

Creating a Unique GUID

Each extension is ultimately identified by a complex number called a globally unique identifier (GUID). Any extension you write will need its own GUID, and it is best to not make one up. The reasons to have GUIDs are found in the history of software development and how components such as software interact with each other and the operating system.

As computers have become more complex, the use of names to describe unique content, features, and functionalities has become problematic. Names created by independent parties who were developing these features often were identical to names from other developers. Usually, this similarity was unintended, but the results were that Windows (and applications) often got confused as to which object was being referred to because two items had the same name.

As a solution, Microsoft created a methodology to ensure that every component had a unique name that would never be duplicated under normal circumstances. These identifiers were first used with Object Linking and Embedding (OLE) to allow objects to be embedded into documents. Each object needed a global identifier that never changed for that object (even between two computers) and was unique. Enter the GUID, a system that was initially set up by Microsoft and adopted by many others along the way.

A GUID is a 16-byte (128-bit) number. In reality, a GUID is divided into groups of 8 hexadecimal digits, 4 hexadecimal digits, 4 hexadecimal digits, 4 hexadecimal digits, and 12 hexadecimal digits. Neither the value nor the position of any part of the GUID is significant—nor is there any meaning for the groupings.

A GUID is generated, under optimal conditions, by taking the generating computer’s Ethernet or Token Ring hardware address (the NIC MAC address, a value that in itself is unique to this one computer), the current time, and a pseudorandom number. All three are mixed together with a bit of programming magic to create a GUID. We are guaranteed that no two computers will have the same NIC MAC address. Even if there were to be two identical MAC addresses, it is very unlikely that both would try to generate a GUID at the same time or that the pseudo number portion would be the same. This makes GUIDs sufficiently unique so that we will never have duplicate GUIDs. There are GUID generators that do not use the NIC’s MAC address; therefore, these GUIDs cannot be guaranteed to be unique. However, the likelihood of duplication is remote at best.

For example, Firefox’s GUID is {ec8030f7-c20a-464f-9b0e-13a3a9e97384}. Usually an application does not change GUIDs unless it changes sufficiently that compatibility issues would result from it being confused with earlier versions of the same application.

GUID generators are available online. The website at http://www.hoskinson.net/webservices/guidgeneratorclient.aspx creates all the GUIDs you might ever want. However, should you not trust an outside source to generate your GUIDs, you can get your own GUID generator from Microsoft (GUIDGEN.EXE is part of Visual Basic 6’s tools pack).

For the sample extension, GUIDGEN.EXE is used. Figure 16.11 shows the results of generating the GUID.

GUIDGEN can create GUIDs in four formats; we want ours in Registry Format.

Figure 16.11. GUIDGEN can create GUIDs in four formats; we want ours in Registry Format.

The GUID shown is the one used in this chapter’s example.

Firefox Command Options for Developers

Firefox offers a few command-line options. Some are useful for developers, and having shortcuts for these options can make the process of developing an extension much easier.

The following table lists Firefox startup options.

Option

Description

-console

Runs Firefox using the debugging console. The console provides error message output.

-contentLocale <locale>

Forces the locale to be the one specified in <locale>.

-CreateProfile <profile>

Creates a profile named <profile>.

-h or -help

Displays a list of Firefox command options.

-height <value>

Makes Firefox’s window height equal to <value>. Also see -width.

-install-global-extension </path/to/extension>

Tells Firefox to install the extension pointed to by </path/to/extension> as a global (available to all users) extension.

-install-global-theme </path/to/theme>

Tells Firefox to install the theme pointed to by </path/to/theme> as a global (available to all users) theme.

-p <profile name>

Starts Firefox with the specified profile.

-profile <path/to/profile>

Starts Firefox with the profile pointed to by <path/to/profile>.

-ProfileManager

Starts Firefox with the Profile Manager.

-ProfileWizard

Starts Firefox with the Profile Wizard.

-safe-mode

Starts Firefox with all extensions disabled. This is used to recover from an extension that keeps Firefox from starting.

-SelectProfile

Starts Firefox, first displaying the Profile Select dialog box.

-UILocale <locale>

Forces the Firefox user interface to be the one for the specified locale.

url

Starts Firefox and displays the URL provided. This option does not use a dash prefix.

-v or -version

Prints the Firefox version.

-width <value>

Makes Firefox’s window width equal to <value>. Also see -height.

Working Through an Extension Development Example

With the introduction of Firefox version 1.5, extension development has been made a bit easier. The contents.rdf files are gone, replaced by an easier-to-understand chrome.manifest file.

In the following paragraphs, you will build a simple extension that works with most versions of Firefox below version 1.5 and a second extension for Firefox version 1.5 and later. The basic functions of these two extensions are identical: You add a menu item and a call to a relatively trivial JavaScript piece of code. These examples have been kept as simple as possible, with the intention that they be easy to learn and implement. After you gain more experience, you can either add to these examples or create your own from scratch.

First, we will describe items that are common to all versions of Firefox. Following this, we will discuss the version-specific bits and pieces.

You will need a few helper batch files. One batch file will package your extension, creating your JavaScript Archive (JAR) contents file and the extension’s XPI installation file. This batch file will then call another batch file that will clear Firefox’s profile, restoring it to a known starting point. For extensions, the best starting point is with no extensions or themes installed.

Development Profile Save and Restore

First, start Firefox with the -profilemanager command-line option. This starts the Profile Manager. In the Profile Manager, create a new profile, naming it Testing Profile.

Next, set your development preferences as described previously in the section “Preferences for Extension Developers.” If you want to set a home page, do so now. After your preferences (and home page, if desired) are set, exit Firefox. Some developers might want to also load the Extension Developer extension; if you do, do this before exiting Firefox. Restart Firefox and ensure that your setup is what you want and that any necessary extensions are loaded (and functional). Then exit Firefox again.

Now you will create a backup copy of your profile.

The easiest way to back up your profile is to open a Windows Explorer window and navigate to Firefox’s profiles folder (for example, in Windows XP, it’s Documents and Settingsuser-idApplication DataMozillaFirefoxProfiles). Right-click the profile you created—there should be only one profile at this point if you have started with a clean Firefox installation and deleted any preexisting profiles. Next, drop it in the same folder and select Copy Here from the pop-up context menu Windows Explorer displays. You will then have a second copy of the profile with an additional extension of .bak added to the profile’s name. Make a note of this backup copy’s name. Now repeat the copy again and save the third copy with the extension .backup. This third copy is your emergency recovery copy.

Next, you need to configure a development folder.

The Development Folders

You must create a folder to hold your new extension. Although you probably will develop your extension by using an existing extension as a foundation to build upon, let’s take a look at what the folder structure for this sample extension looks like.

MyExtension’s Root Folder

Your new extension needs a name. I’ve picked the name MyExtension as the name for the extension. After you have a name for the extension picked out, the extension will need a home. The name for this top-level folder is not critical. Other subfolder names will be significant, but the topmost level folder can have any convenient name, usually the same name as your extension.

Note

There is nothing to say that your extension can not have more folders than those described here. However, most extensions have these two subfolders as a minimum. As well, none of the folder names are absolute—you can use other names if you want. The names given here are simply those used by many developers of Firefox extensions.

In My Documents, I created my MyExtension folder. In this folder we need two subfolders. The first is named content, and the second is called chrome. We now have these folders:

  • MyExtension

  • MyExtensioncontent

  • MyExtensionchrome

Contained in the root folder will be two or three project files: the install.rdf file, the extension’s .xpi file, and (for Firefox 1.5 and later) the chrome.manifest file.

Caution

Windows programmers are used to having filenames and folder names that are not case specific. So, for a Windows programmer, MyExtension is the same as myExtension and myextension.

However, Firefox might not always be so forgiving. A good rule of thumb is to be case conscious when working with Firefox extensions.

content

The content folder is where you will put your working copies of the extension’s files.

In MyExtension the three files in the contents folder are

  • contents.rdf (for versions of Firefox before 1.5)

  • myextension-Overlay.xul

  • myextensionOverlay.js

More complex extensions will have more files in the contents folder.

chrome

The chrome folder will hold the extension’s JAR file. Most extension developers use the chrome folder only to hold the JAR file, and nothing else. However, this name is significant to Firefox, so you should not put your JAR file anywhere else.

Extension Files

With the folders created, next you need a few files. Your extension is rather simple, so you only have a few files that must be created for it.

extension.XPI, the Extension Distribution File

All extensions are distributed using XPI files. The definition of an XPI file is that it is a Mozilla extension distribution file, although that tells little of what it really is.

XPI files are, for all intents and purposes, simply renamed Zip files. You can create them in several ways. My method uses the WinZip command-line interface driven by a batch file. (I will go more into depth regarding this batch file later in this chapter.)

Contained in the XPI file will be a minimum of two files:

  • The install.rdf file that describes to Firefox the extension

  • The extension’s JAR (JavaScript Archive) file

  • For versions of Firefox from 1.5 onward, the chrome.manifest file

More complex extensions might have other files that are used as part of the installation process. An example is the Google Bar extension that also has install.js and googlebar.js.

All the root files in the XPI file are used for the extension’s installation. The extension’s actual functionality is always contained within the JAR file.

The following paragraphs cover the contents of the XPI file for this chapter’s extension project (see Figure 16.12).

This extension has only install.rdf and the extension’s JAR file for contents.

Figure 16.12. This extension has only install.rdf and the extension’s JAR file for contents.

extension.jar, the Extension’s JAR File

Contained within the extension’s JAR file are all the files that make up the extension. Some of the files often found in a JAR file include XUL, RDF, JavaScript, and various image files.

The files contained in this extension’s JAR file are shown in Figure 16.13.

The JAR file contains the actual extension’s working code.

Figure 16.13. The JAR file contains the actual extension’s working code.

There will be two or three files in the JAR:

  • contents.rdf—This file contains information about the extension. This file is required, with this name. Contained in the contents.rdf file is an extension description and the extension’s internal name. This file also contains information regarding the extension’s XUL file. This file should be found only in extensions for Firefox versions 1.0.x and earlier.

  • myextension-Overlay.xul—Contained in the myextension-Overlay.xul file are instructions telling Firefox what functionality the extension will modify or enhance. This file’s name must match the name specified in the contents.rdf file.

  • myextensionOverlay.js—In this chapter’s extension, there is a simple JavaScript file. This file’s name is specified in the extension’s XUL file and might not be found in every extension.

Other Files for Extension Developers

A few other files can be used to streamline the extension packaging process. If you want to use it, the Extension Developer extension also provides this functionality.

The first supporting file is named makexpi.bat (see Listing 16.2). This is a command batch file that calls the command-line version of WinZip to create both the JAR file and the XPI file. Optionally, this batch file can do other cleanup tasks as well. Comments have been added to this file to make its function clearer.

Note

The makexpi.bat file depends on WinZip and the command-line interface. Both are available at the WinZip website (http://www.winzip.com). By default, WinZip and the command-line interface, wzzip.exe, are installed in C:program fileswinzip. If you want to use another Zip-type program, such as TUGZip, makexpi.bat must be modified accordingly.

Example 16.2. The makexpi.bat File

@echo off

If X%1 == Xhelp (
Echo makexpi [firefox]
Echo where specifying firefox builds for Firefox 1.1.x and later.
Echo if no parameter is specified this will build
Echo a Firefox version 1.0.x and earlier
Echo extension.
goto end
)

REM the debug.txt file holds the output from all the commands.
REM This keeps execution cleaner.
time /T >debug.txt

REM - Create the JAR file first. WinZip will add or create as necessary.

echo Refreshing the JAR file! >>debug.txt
REM This line is for versions of Firefox 1.0.x and earlier.

If X%1 == Xfirefox (
   "%programfiles%WinZipwzzip" -a -r -P chromemyextension.jar content
The makexpi.bat FilemyextensionOverlay.js contentmyextension-Overlay.xul >>debug.txt
) else (
   "%programfiles%WinZipwzzip" -a -r -P chromemyextension.jar content >>debug.txt
)

REM - Build the XPI file
echo Refreshing the XPI file! >>debug.txt

If X%1 == Xfirefox (
"%programfiles%WinZipwzzip" -a -r -P MyExtension.xpi chromemyextension.jar chrome
The makexpi.bat File.manifest install.rdf >>debug.txt
) else (
"%programfiles%WinZipwzzip" -a -r -P MyExtension.xpi chromemyextension.jar install.rdf
The makexpi.bat File >>debug.txt
)

REM - Tell when this build was made

echo Desplaying the timestamp! >>debug.txt
Echo Generate time was

type time.txt

REM - Restore profile to clean version

echo Refreshing the Firefox profile! >>debug.txt

call clearprofile.bat

Rem bailout on help and errors label
:end

In Listing 16.3, you must change the parts in bold to whatever filenames you want for your extension.

Example 16.3. The clearprofile.bat File

@echo off

Rem you *must* fix the profile name below (in bold) to match your profile name!

RD /s /q "%appdata%MozillaFirefoxProfilesjvaofcos.Testing Profile"
MD ""%appdata%MozillaFirefoxProfilesjvaofcos.Testing Profile"
xcopy /s /y ""%appdata%MozillaFirefoxProfilesjvaofcos.Testing Profile.bak" "%appdata%
The clearprofile.bat FileMozillaFirefoxProfilesjvaofcos.Testing Profile
" >>debug.txt

In Listing 16.3, you must change jvaofcos.Testing Profile to reflect the name of the Firefox profile you created for testing and debugging purposes.

An Extension for Firefox 1.0.x

Firefox 1.0.x represents versions of Firefox before 1.5. Some users have not switched to Firefox 1.5 due to concerns that their favorite extensions and themes will not be compatible with the newer version.

Firefox 1.0.x does not support the chrome.manifest format (we’ll talk about chrome.manifest later). Thus, any extension for these platforms require the more complex contents.rdf file. This makes these extensions slightly more complex.

First, for your extension, you will create some folders. The first folder will be the extension’s root folder. In the extension root folder, you will create two additional folders, chrome and content.

In this example, you have to create or modify four files: install.rdf, contents.rdf, myextension-Overlay.xul, and myextensionOverlay.js. I’ve listed each of these files in the following sections, showing the parts that are relatively generic and those areas that you’d rewrite or enhance for your extension.

install.rdf

The install.rdf file is the file Firefox uses to install the extension (see Listing 16.4). This file describes the extension and provides Firefox with the name and location for the extension’s JAR file.

Example 16.4. 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">
     <em:id>{71536D06-A8BD-411a-BC0C-15C1FDB7DF2A}</em:id>
     <em:version>0.1.05</em:version>
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>0.9</em:minVersion>
         <em:maxVersion>1.0</em:maxVersion>
       </Description>
     </em:targetApplication>
     <em:name>My Extension</em:name>
     <em:file>
       <Description about="urn:mozilla:extension:file:myextension.jar">
         <em:package>content/</em:package>
       </Description>
     </em:file>
   </Description>
</RDF>

In the install.rdf file, you must provide a new GUID for your extension. My GUID was 71536D06-A8BD-411a-BC0C-15C1FDB7DF2A, and it is in bold in the listing. Please don’t just change a few numbers trying to make your own GUID; use a generator as described previously to make a new GUID.

The minVersion and maxVersion need to be changed to reflect for which versions of Firefox your extension is designed. The defaults in Listing 16.4 are what you should use for versions of Firefox before 1.5.

The name My Extension should be changed to the name of your extension. Spaces are acceptable, and no quotation marks are needed.

The JAR filename, myextension, must be the same as the JAR file created by makexpi.bat.

Everything else in install.rdf should remain the same.

contents.rdf

The contents.rdf file needs some modifications, as shown in Listing 16.5.

Example 16.5. contents.rdf

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

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

 </RDF:Seq>

 <RDF:Description about="urn:mozilla:package:myextension"
   chrome:displayName="My Extension"
   chrome:author="Peter D. Hipson"
   chrome:authorURL="http://www.hipson.net"
   chrome:name="myextension"
   chrome:extension="true"
   chrome:settingsURL="chrome://myextension/content/settings/settings.xul"
   chrome:description="Add a menu item to Files in Firefox.">
 </RDF:Description>

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

 <!-- overlay information for Mozilla Firebird-->
 <RDF:Seq about="chrome://browser/content/browser.xul">
   <RDF:li>chrome://myextension/content/myextension-Overlay.xul</RDF:li>
 </RDF:Seq>
</RDF:RDF>

In Listing 16.5, the lines starting with chrome: should be changed as appropriate. The chrome:name value should match the name in chrome:settingsURL and in the chrome://myextension line.

The myextension-Overlay name should match the name of your extension’s XUL file. Mozilla conventions say that overlay should be part of the name.

myextension-Overlay.xul

You need to modify the myextension-Overlay.xul file as shown in Listing 16.6. This filename needs to match the name in the contents.rdf file (refer to Listing 16.5). This file is identical for all versions of Firefox.

Example 16.6. myextension-Overlay.xul

<?xml version="1.0"?>

<overlay id="MyExtensionOverlay"

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

// Include the Javascript

  <script type="application/x-javascript"
  src="chrome://myextension/content/myextensionOverlay.js">
  </script>

  <menupopup id="menu_FilePopup">
    <menuitem label="My Extension" position="1" oncommand= "myextensionmessage();" />
  </menupopup>
</overlay>

In the myextension-Overlay.xul file, you must give the overlay a name. Change MyExtensionOverlay to a name that suits your extension’s name.

In the chrome: line, the myextension should be changed to match the chrome:name in the contents.rdf file.

The menuitem line needs to reflect whatever chrome changes your extension is doing. I’m adding a menu item, but you might be doing something different. The myextensionmessage() call to JavaScript must match your JavaScript functions, if you use JavaScript, or whatever way you choose to add functionality to the menu item.

myextensionOverlay.js

The myextension.js file contains the JavaScript code to give my extension its functionality (see Listing 16.7). In the listing, you would rename it to match the name in the myextension.xul file. This extension is trivial—it just displays a message box using the JavaScript alert() function call. This file is the same for all versions of myextension.

Example 16.7. myextension.js

// An example of some JavaScript functionality.
// You could code virtually any functionalty that
// JavaScript supports here:

function myextensionmessage() {

    alert("         ...and, it worked!         ");
}

If you decide to use JavaScript in your extension, you could use this file to hold your JavaScript functionality. As extensions grow more complex, additional JavaScript source files might need to be added.

An Extension for Firefox 1.5

Firefox 1.5 and later versions are compatible with extensions written for earlier versions of Firefox. However, it is best to adhere to the new extension conventions as described here.

In this sample extension, both myextension.js and myextension-Overlay.xul won’t change based on versions of Firefox. For the contents of these two files, refer to the previous section.

In addition to these two files, two other files exist in the Firefox 1.5 version of this sample extension. One file (install.rdf) will be modified, whereas the other (chrome.manifest) will be added to your extension. The file contents.rdf is no longer used. If Firefox finds a contents.rdf file and does not find chrome.manifest (for example, an old extension written for earlier versions of Firefox), a chrome.manifest file is created from the contents.rdf file.

install.rdf

Because some of the changes to install.rdf will be new even to experienced extension writers, let’s cover them in detail. First, Listing 16.8 shows the new install.rdf file.

Most of the changes in install.rdf involve the addition of lines and some minor rearranging of content.

Example 16.8. 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">
    <em:id>{71536D06-A8BD-411a-BC0C-15C1FDB7DF2A}</em:id>
    <em:version>0.1.05</em:version>
    <em:targetApplication>
      <Description>
        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
        <em:minVersion>1.0</em:minVersion>
        <em:maxVersion>1.5+</em:maxVersion>
      </Description>
    </em:targetApplication>

    <!-- Front End MetaData -->
    <em:name>My Extension</em:name>
    <em:creator>Peter D. Hipson</em:creator>
    <em:description>Add a menu item to Files in Firefox. </em:description>
    <em:homepageURL>http://www.hipson.net</em:homepageURL>
    <em:updateURL>http://www.hipson.net</em:updateURL>

    <em:file>
      <Description about="urn:mozilla:extension:file:myextension.jar">
        <em:package>content/</em:package>
      </Description>
    </em:file>
  </Description>
</RDF>

In addition to the name and GUID changes outlined in Listing 16.8, in versions of Firefox earlier than 1.5, you need to make the following changes to your install.rdf file.

You should create a new section, preceded by the following comment line:

<!-- Front End MetaData -->

This comment indicates that data for your extension follows. Next, add four lines that take data that was in your contents.rdf file and place them in install.rdf. The format of the lines will change—we are only interested in the extension-specific data that has been highlighted in the listing.

The <em:name>My Extension</em:name> remains the same as the previous example:

<em:creator>Peter D. Hipson</em:creator>

The em:creator line takes the data that was in the contents.rdf file’s chrome:author line. No quotes are needed for this field:

<em:description>Add a menu item to Files in Firefox. </em:description>

The em:description line takes the description that was in the chrome:description line in the contents.rdf file:

<em:homepageURL> http://www.hipson.net</em:homepageURL>

The data in em:homepageURL is the chrome:authorURL data from contents.rdf:

<em:updateURL>http://www.hipson.net</em:updateURL>

The em:updateURL data was not present in the sample contents.rdf file and simply specifies where to look for updates to this extension.

chrome.manifest

The chrome.manifest file is new to Firefox 1.5 (see Listing 16.9). It is intended to replace the complex and often problematic contents.rdf file with something that’s easier to work with.

This file, in this example, has just two lines, making it considerably shorter than contents.rdf. The file is a simple text format, created with Notepad or your favorite text editor.

Example 16.9. chrome.manifest

overlay chrome://browser/content/browser.xul chrome://myextension/content
chrome.manifest/myextension-Overlay.xul
content myextension jar:chrome/myextension.jar!/content/

A chrome.manifest file has, at a minimum, the lines shown in Listing 16.9. Some additional lines might be present in a more complex extension’s chrome.manifest file.

These potential lines are listed in the following, along with a description. Compare these specification lines with the lines in Listing 16.9 to see how the parameters would change. Items in bold would change to match your extension.

The content package line describes the extension (packagename) and the location of the extension’s JAR (or other content) file:

content packagename path/to/files

In the chrome.manifest file, the path/to/files was jar:chrome/myextension.jar!/content/. The exclamation point after the JAR file’s extension is required, so don’t forget it.

The locale package line specifies the locale name and the path to that locale’s files. This line is required in chrome.manifest:

locale packagename localename path/to/files

An example of this line is locale myextension en-US jar:en-US.jar!/locale/en-US/myextension. In this example, en-US is the locale identifier for English in the United States. The en-US.jar file is the JAR file containing elements specific to the locale and the location in that JAR file of the necessary content. This file is optional in chrome.manifest.

The skin package line specifies the extension’s skin (think theme) location. Again, it includes the package name and a path to files that would be similar to that in the previous examples:

skin packagename skinname path/to/files

The XUL overlay package line specifies the extension’s XUL overlay’s location. There is a path to the chrome for this extension:

overlay chrome://file-to-overlay chrome://overlay-file

An example of this line from chrome.manifest is

overlay chrome://browser/content/browser.xul chrome://myextension/content
chrome.manifest/myextension-Overlay.xul

This code gives the information provided in the contents.rdf file’s overlay section.

Many extensions modify the Cascading Style Sheets (CSS) used by Firefox. The style overlay line provides this information in a format similar to the previous overlay line:

style chrome://file-to-style chrome://stylesheet-file

Firefox is a multiplatform product, meaning it will run on Windows, Linux, and Mac OS/X. If your extension is specific to one of these platforms, add the keyword platform at the end of the content lines.

More complex extensions have multiple occurrences of these lines in chrome.manifest. If you want to learn more about chrome.manifest, go to http://www.mozilla.org/xpfe/ConfigChromeSpec.html.

Extension Writing Secrets for Power Users

Here are a few ideas from the experts:

  • To prepare for extension developing, it is important to set up the development environment. A clean (new) installation of Firefox, with a clean (never used) profile, can make debugging much easier.

  • Some preferences make extension development much easier. These, used in conjunction with Firefox command-line options, make testing easier.

  • A number of extensions have been created just to help others create their extensions. One notable extension is the Extension Developer extension.

  • Firefox has a debugging console that is displayed when Firefox is started with the -console option.

  • Developers can set an environment variable to allow multiple instances of Firefox to run. Normally, Firefox runs only one instance (copy) at a time.

  • Programs and objects are uniquely identified using a GUID. There are programs to create GUIDs and websites that will create GUIDs online.

  • Understanding Firefox command options is important for developers. Having shortcut icons on the desktop preconfigured for these options lets you easily launch Firefox with an option.

  • Extensions have a basic arrangement and names for the extension’s folders. Following these conventions makes an extension’s development easier.

  • The chrome.manifest file is new to Firefox 1.5. This file is designed to replace the contents.rdf file with a file that is easier to work with.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.114.121