For example, when a user changes the browser’s home page in the Options dialog, the new value is saved as a user preference... When the user modifies the default preference value or crea
Trang 1Figure 17-24 shows the dialog we have created.
F IGURE 17-24: A simple dialog
Let’s look at our dialog code more closely:
The dialogelement specifies that our XUL document is in fact a dialog
■The titleattribute specifies the dialog title
■The buttonsattribute specifies the comma-separated list of buttons that will appear in the dialog In our case, we want two buttons: OK and Cancel Notice that we specified only the wanted buttons and didn’t have to create the button ele-ments The buttons are created automatically, and their position and appearance are determined by the user’s operating system conventions
■The ondialogacceptand ondialogcancelattributes define functions that will be called when the user presses OK and Cancel, respectively
The scriptelement defines our JavaScript code Notice that while all our examples until now demonstrated the use of external JavaScript files, you can have your scripts embedded directly in the XUL document
A single labelelement is used to display a line of text Obviously, real dialogs often have more complex user interfaces
Once our dialog implementation is ready, we can add it to our chrome package Let’s name our dialog file testDialog.xuland add it to the siteleds package We can now open it using the
window.openDialogmethod like so:
window.openDialog(“chrome://siteleds/content/testDialog.xul”,
“_blank”,
“chrome”);
The first parameter specifies the URL of the dialog XUL file; the second, the name of the dialog The third parameter specifies some optional flags — the chromeflag means that the document is a chrome window and doesn’t need to be wrapped by a browser component, like
an HTML document, for example You can specify the modalflag to make the opened dialog modal
Preferences and Persistent Information
The preferences mechanism allows the browser to store user modifiable application settings For example, when a user changes the browser’s home page in the Options dialog, the new value is saved as a user preference
Trang 2user preference is browser.startup.homepage You can see each word in the preference
name as a branch For example, all browsing-related preferences are located under the browser
main branch, all the preferences that are related to the browser startup are located under the startup subbranch of the browser branch, and so on This way, all the user preferences can be viewed as a tree (see Figure 17-25) When a new component or an extension creates its own preferences, it should give them a unique main branch name to avoid conflicts For example, our sample extension might save and use a preference named siteleds.monitor.url
There is a convenient user interface for examining, modifying, and creating preferences You
can open it by typing about:config in your browser address bar.
There are three main preference data types: string, integer, and Boolean Also, each preference can have two optional values: default and current When the user modifies the default preference value or creates a new preference, the new value is saved as a current value and is highlighted in bold in about:config When the system tries to retrieve a preference value, it does the following:
1 Checks whether the preference has a current value and, if so, returns it.
2 If there is no current value, it checks whether there is a default value and, if there is, returns it.
3 If neither current nor default value can be found, an exception is thrown.
If you are trying to retrieve a preference of a specific type, and a preference having a different type is found, an exception is thrown For example, if you are trying to retrieve the string value
of the user home page preference (browser.startup.homepage ) and a Boolean value is found instead, the call will throw an exception
There are several XPCOM components and interfaces for working with preferences You can specify the preference names using these interfaces in two ways You can obtain an interface
to the root branch and specify the full preference names (such as browser.startup
homepage) Alternatively, you can get an interface to a specific subbranch, which will allow you to omit that branch prefix from the preference names For example, if you are working with the browser branch, you can use the startup.homepagestring to access the browser startup.homepagepreference
Here’s how to get an interface to the root branch:
var prefs = Components.classes[“@mozilla.org/preferences-service;1”].
getService(Components.interfaces.nsIPrefBranch);
After we have the root branch, we can access a preference by specifying its full name:
var homePage = prefs.getCharPref(“browser.startup.homepage”);
If we want to work with a specific subbranch, we can use the getBranchmethod of the
nsIPrefServiceinterface:
var prefs = Components.classes[“@mozilla.org/preferences-service;1”].
getService(Components.interfaces.nsIPrefService);
Trang 3F
Trang 4var homePage = prefsBranch.getCharPref(“startup.homepage”);
To modify a preference or create a new one, you can use one of the setCharPref,
setBoolPref, or setIntPrefmethods (for string, Boolean, and integer preferences, respectively) For example, the following changes the user’s home page preference (prefsBranchshould contain a reference to the browser branch):
prefsBranch.setCharPref(“startup.homepage”, “http://www.iosart.com/firefox/”);
To retrieve a preference value, you can use one of the getCharPref,getBoolPref, or
getIntPrefmethods
As mentioned earlier, the methods that retrieve preference values can throw exceptions if the preference is not found or has the wrong type You can use the prefHasUserValue and getPrefTypemethods of the nsIPrefBranch interface to make sure that the preference exists and has the expected type or you can wrap your preference retrieval calls in try/catch JavaScript blocks
A related Mozilla mechanism allows saving the state of XUL elements across browser sessions
For example, a dialog can remember its size, so if the user resizes it, the correct size will be retained even after the browser is restarted One way to accomplish this is to manually save the current state of the various elements as user preferences Mozilla has a persistence mechanism that greatly simplifies this task The following will make the size of a dialog persistent:
<dialog xmlns=”http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul”
id=”test-dialog”
title=”Test Dialog”
buttons=”accept,cancel”
ondialogaccept=”return dialogOK();”
ondialogcancel=”return dialogCancel();”
width=”400”
height=”200”
persist=”width height”>
We have added a new persistattribute to our dialogelement and specified a space-delimited list of element attributes that we want to be saved Now, each time these attributes change (the dialog is resized), their new values are saved by the browser Next time the dialog
is displayed, the widthand heightattributes will receive the saved values, rather than the initial ones
You can use the data persistence mechanism on any XUL element that has an idattribute
The mechanism is typically used to save element visibility, position, size, and so on, but you can make any attribute persistent, and any number of element attributes can be saved using this technique
Localized Strings in JavaScript
As mentioned in the previous sections, all the strings that are displayed to the user should be defined in a separate string table file, which will allow easy translation of the user interface You saw how this can be accomplished in XUL files using XML entities and DTD files
Trang 5Often, element labels and other displayed strings aren’t static; they can change during the pro-gram execution For example, a status bar can display many different messages, and the text of these messages is typically set by a JavaScript code A mechanism similar to XML entities is needed so the messages and strings that originate in JavaScript can be easily localized JavaScript isn’t an XML language Unlike XUL, it cannot use XML entities to specify string
variables Mozilla has an additional mechanism called property files that allows having variable
localizable strings in JavaScript Let’s extend our SiteLeds example to include this mechanism First, we define a property file that is located in the same directory as our siteledsOverlay.dtd file and contains all the UI strings that need to be accessed from JavaScript The contents of the siteledsOverlay.properties file are as follows:
pageModified=The monitored page was modified
pageError=There was an error retrieving the monitored page
Now we include the property file we have created in our XUL document (siteledsOverlay.xul) using a stringbundleelement:
.
<!DOCTYPE overlay SYSTEM “chrome://siteleds/locale/siteledsOverlay.dtd”>
<overlay id=”siteleds-overlay”
xmlns=”http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul”>
<script type=”application/x-javascript”
src=”chrome://siteleds/content/siteledsOverlay.js”/>
<stringbundle id=”siteleds-strings”
src=”chrome://siteleds/locale/siteledsOverlay.properties”/>
.
.
Finally, we can get a specific string from our JavaScript code by finding the stringbundle
element and calling its getStringmethod:
var stringBundle = document.getElementById(“siteleds-strings”);
var pageErrorString = stringBundle.getString(“pageError”);
alert(pageErrorString);
To translate the user interface, you will need to translate all the DTD and property files
Firefox Customization Options
This section shows some additional examples of how Firefox can be customized and enhanced using the extensions mechanism
Adding Main Menu and Context Menu Entries
An extension can add menu entries to the main Firefox menu and to the context menu of the browser content area (the place where the web pages are displayed)
Trang 6the help of the DOM Inspector, we can find out that the id of the Firefox Tools menupopup
element is menu_ToolsPopup We can add the following to our overlay:
<menupopup id=”menu_ToolsPopup”>
<menuitem id=”my-menu-item”
label=”My Menu Item”
accesskey=”y”
insertbefore=”menu_preferences”
oncommand=”alert(‘Testing 1 2 3’);”/>
</menupopup>
The new menu item is shown in Figure 17-26
F IGURE 17-26: The new menu item
You can control the exact position of the new menu item in the overlaid menu by specifying insertafter, insertbefore, or the position attributes of the new element
You can add menu items to the context area menu by overlaying the
contentAreaContextMenuelement:
<menupopup id=”contentAreaContextMenu”>
<menuitem id=”my-context-menu-item”
label=”My Context Menu Item”
accesskey=”y”
oncommand=”alert(‘Context Testing 1 2 3’);”/>
Trang 7You can dynamically determine whether to make your new menu item visible:
1 Add an initialization function that will be called when the Firefox window is loaded
along with your overlay:
window.addEventListener(“load”, initMyOverlay, false);
contentAreaContextMenumenu:
function initMyOverlay() { var contextMenu = document.getElementById(“contentAreaContextMenu”); contextMenu.addEventListener(“popupshowing”, myContextPopupshowing, false); }
This handler will be called every time the context menu is about to become visible
your menu item accordingly:
function myContextPopupshowing() { var contextMenuItem = document.getElementById(“my-context-menu-item”);
if (contextMenuItem) { contextMenuItem.hidden = !gContextMenu.isTextSelected;
} }
The preceding code shows our new menu item in the context menu only if some text is selected on the web page As you can see, we first find our menu item using the DOM
getElementByIdmethod We then determine whether some text is selected by exam-ining the isTextSelectedproperty of the global gContextMenuobject and hide our menu item if no text is selected The gContextMenuobject has several useful methods and properties that can help you determine whether your menu item is appro-priate for a given context Some examples follow:
■target: The element on which the user clicked to open the context menu
■isTextSelected: Determines whether there is some text selected on the web page
■onLink,onTextInput,onImage,onTextInput: Allow you to determine the type of element that the context menu was opened on
■linkText(),linkURL(): If onLinkis true, provides additional information about the link element that the context menu was opened on
Adding Keyboard Shortcuts
In XUL, shortcut keys are defined using the keyelement Several keyelements are typically grouped in a keysetelement The Firefox keyboard shortcuts are defined in a keyset ele-ment that has an id of mainKeyset You can overlay this element to create additional key-board shortcuts For example, you can add the following to your dynamic overlay:
Trang 8<key id=”my-key-test”
key=”T”
modifiers=”accel,shift”
oncommand=”alert(‘Testing 1 2 3’);”/>
</keyset>
When the user presses Ctrl+Shift+T (Cmd+Shift+T on Macintosh), the oncommandscript is executed See the keyelement documentation for further details on specifying shortcuts in Mozilla
When adding new global shortcut keys, you should verify that your keys aren’t conflicting with the existing shortcuts, defined either in Firefox itself or in other popular extensions It is always a good idea to implement some functionality in your extension that will let the user reconfigure the default extension shortcut keys
In addition, similar to text strings, it is recommended to define shortcut keys as XML entities rather than directly in the XUL file Shortcut keys often correspond to the name of the action (for example, Ctrl+S for Save) If the extension is translated into another language, the default short-cuts may no longer make sense If the shortshort-cuts are specified inside a DTD file along with the other strings, they can be easily modified to correspond to the translated name of the action
Adding Toolbars and Toolbar Buttons
A toolbar is created using the toolbarXUL element All Firefox toolbars are located inside a single toolboxelement The id of this element is navigator-toolbox, and by overlaying
it, we can add a custom browser toolbar, as demonstrated in the following example:
<toolbox id=”navigator-toolbox”>
<toolbar id=”my-test-toolbar”
class=”chromeclass-toolbar”
toolbarname=”My Test Toolbar”
accesskey=”T”
context=”toolbar-context-menu”
hidden=”false”
persist=”hidden”>
<toolbarbutton id=”my-toolbar-button-1”
tooltiptext=”First Button”
label=”Button 1”
oncommand=”alert(‘Testing Button 1’);”/>
<toolbarbutton id=”my-toolbar-button-2”
tooltiptext=”Second Button”
label=”Button 2”
oncommand=”alert(‘Testing Button 2’);”/>
</toolbar>
</toolbox>
Let’s take a closer look at what we have done:
We created a toolboxelement with navigator-toolboxid in our overlay The specified id attribute ensures that this toolbox will overlay the main Firefox toolbox and our toolbar will be added to the browser
Trang 9The toolbarelement defines our new toolbar Let’s examine its attributes:
■class: The chromeclass-toolbarclass specifies that the XUL element should be styled as a standard Firefox toolbar
■toolbarname: The name of the toolbar as it appears in the View ➪ Toolbars menu
■accesskey: The keyboard key that can be used to trigger the toolbar visibility in the Toolbars menu The specified letter will be underlined, similar to other menu keyboard shortcuts
■context: The id of the context menu that appears when the user right-clicks over the toolbar You can create your own custom menu or specify toolbar-context-menu, which is the id of the default Firefox context menu that allows toggling the visibility of the various toolbars
■hidden: The value of falsespecifies that the toolbar is initially visible
■persist: By setting this attribute to hidden, we are instructing the browser to remember the visibility state of our toolbar across sessions
Inside the toolbarelement, we have defined a couple of toolbar buttons As we men-tioned earlier, the toolbarbuttonelement is similar to a regular button but typically has a different style In addition to toolbar buttons, we can place any elements on our toolbar (checkboxes, text boxes, drop-down lists, and so on)
Figure 17-27 shows our new toolbar
F IGURE 17-27: A simple toolbar
If you want to add a single toolbar button rather than a complete toolbar, you must use a slightly different technique In Firefox, the user can customize a toolbar by choosing View ➪ Toolbars ➪ Customize and dragging the wanted toolbar buttons and other elements from the toolbar palette to the target location By adding our toolbar button to the customization palette, we can allow the user to later add this button to one of the toolbars
It is possible to add a toolbar button directly to one of the browser toolbars, rather than to the customization palette, but this requires a somewhat more complex technique
Trang 10main toolbarpaletteelement, which has an id of BrowserToolbarPalette:
<toolbarpalette id=”BrowserToolbarPalette”>
<toolbarbutton id=”my-toolbar-button-3”
class=”toolbarbutton-1”
tooltiptext=”Third Button”
label=”Button 3”
oncommand=”alert(‘Testing Button 3’);”/>
</toolbarpalette>
We have set the class of our toolbarbutton element to toolbarbutton-1 This ensures that the button will be displayed correctly in both Icons and Text toolbar modes
We now want to specify an icon for our toolbarbutton Because toolbars can have two sizes, big and small, we will have to define two icons for our toolbar button The big icon is
24 ×24 pixels, and the small one is 16 ×16 pixels
Let’s look at the style sheet that defines the toolbar button icon:
#my-toolbar-button-3 { list-style-image: url(“chrome://my-extension/skin/button_3_large.png”);
} toolbar[iconsize=”small”] #my-toolbar-button-3 { list-style-image: url(“chrome://my-extension/skin/button_3_small.png”);
}
As you can see, the iconsizeattribute of the parent toolbar element determines whether the small or the large icons are displayed
Changing the Appearance of Web Pages
An extension can modify the appearance of web pages that are loaded into the browser content area You can define an event handler function that will be called each time a new web page is loaded In this function, you can examine and modify the DOM structure of the loaded page
This is accomplished with the following steps:
1 Add an initialization function that will be called when the Firefox window is loaded
along with your overlay:
window.addEventListener(“load”, initMyOverlay, false);
2 In the initialization function, find the browser content element using its id
(appconten) and attach a loadevent handler to it:
function initMyOverlay() { var appContent = document.getElementById(“appcontent”);
appContent.addEventListener(“load”, myNewWebPageLoaded, true);