Hack 89. Build an Installable Theme
Build an installable theme that changes the way Firefox windows look. This hack describes how to make, bundle, and offer a new theme for installation. Firefox's theming system is not as comprehensive as some generic GUI skin engines. For example, it can't completely reorganize the Firefox user interface as flexibly as WinAMP's themes can. Firefox themes are restricted to effects that are possible within CSS. However, those effects are expanding in number as CSS becomes more popular. 8.7.1. Getting the Content TogetherBefore building the theme's bundle, make sure you have the content ready. 8.7.1.1 Do the creative bitNo one can give you a formula for creativity, and this hack isn't going to try. Before you can assemble the theme, you have to have a creative starting point, a plan, and a design. That should lead to a set of prepared images and some color scheme information. 8.7.1.2 Do the systematic bitYour images and colors must be tied to CSS styles before they will have any effect. That means studying the CSS selector sites (tag names, attribute names, IDs, and classes) and structures (tag hierarchies) implicit in the XUL content to which your theme will apply. That, in turn, means studying the browser package as a bare minimum. Once that's done, you build the stylesheets that tie the images and colors to the content. Those stylesheets generally match the names of the sheets used in the windows to be styled. So, if this reference appears in an XUL document, you have the opportunity of supplying a stylesheet named special/decorations.css that will affect that window: <?xml-stylesheet href="chrome://browser/skin/special/decorations.css"?> This one-to-one match between theme sheets and referenced sheets can be distorted by two mechanisms. One mechanism is to use the contents.rdf file [Section 8.7.2, later in this hack]. The other way is to use the @import CSS directive. For example, most theme sheets start by importing the global theme: @import url("chrome://global/skin/"); You can import whatever other sheets you see fit. You can, for example, leave the stylesheet empty except for an @import statement that includes some completely foreign sheet. In general terms, though, it makes sense to follow the pattern of sheet names implied by the XUL you're seeking to theme up. 8.7.1.3 Collect together skinsThe stylesheets and images must be collected together into skins. Each theme provides one skin per supported package. A skin is just a group of files in a common directory, possibly with some subdirectories. At a minimum, you should create skins for the browser, global, and mozapps packages. In an ideal world, you would also create skins for every package ever developedpast, present, and future. Since that's not feasible, there is a fallback mechanism. Nearly all XUL windows include the special global package's skin. The global skin is important, because it is the fallback resource for all packages that you don't explicitly write skins for. All well-written XUL documents know that the global skin is the first thing they should include. It's up to the theme designer to ensure that the global skin contains enough resources to support fallback. You should put all your generic images here and refer to these images from the other, more specialized skins that you create. 8.7.2. Building the Theme JAR FileThere are three different ways to deliver a theme:
Each approach requires different packaging of the skin information. 8.7.2.1 Build an install-bundled themeThis approach requires that the JAR file contain the skin information in the standard, inverted hierarchy [Hack #86] : skin/theme-name/package-name/skin-file The JAR file must also include a contents.rdf file for each skin: skin/theme-name/package-name/contents.rdf This contents.rdf file should match this example exactly, also covered in [Hack #86] . We've just spaced it out a little more for discussion and included is a skin overlay [Hack #87] . <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:skin:root"> <RDF:li resource="urn:mozilla:skin:theme-name/1.0" /> </RDF:Seq> <RDF:Description about="urn:mozilla:skin:theme-name/1.0"> <chrome:packages> <RDF:Seq about="urn:mozilla:skin:theme-name/1.0:packages"> <RDF:li resource="urn:mozilla:skin:theme-name/1.0:package-name"/> </RDF:Seq> </chrome:packages> </RDF:Description> <RDF:Description about="urn:mozilla:skin:theme-name/1.0:package-name" chrome:skinVersion="1.5"/> <!-- optional forced inclusion of one skin stylesheet into one page --> <RDF:Seq about="urn:mozilla:stylesheets"> <RDF:li resource="chrome://package-name/content/some-file.xul"/> </RDF:Seq> <RDF:Seq about="chrome://package-name/content/some-file.xul"> <RDF:li>chrome://package-name/skin/extra-styles.css</RDF:li> </RDF:Seq> </RDF:RDF> The skin overlay is the last chunk of code. Since this is package registration, not extension registration, only attributes that describe packages are understood. Almost identical copies of this file can be drawn from most skins, with just the names changed. Since the theme is bundled with the install, the install.js script at the top of the install bundle must also be updated so that it knows to register the additional skins. 8.7.2.2 Build skins into downloadable extensionsThis case is the same as the install-bundled case, except that the skin content is combined with locale and extension content in the one JAR file, which is then made into an extension XPI file [Hack #88] . To use this approach is to make a skin masquerade as a full extension. That's entirely possible, but the price is that Firefox expects that the extension's package will have content. Even if there are no content files, the package must still be registered using: <RDF:Seq about="urn:mozilla:package:root">
<RDF:li resource="urn:mozilla:package:
package-name" />
</RDF:Seq> 8.7.2.3 Build skins into complete and separately downloadable themesThe format of the required JAR file in this case is different than in the previous two cases. It is a simplified format, and there is no need for an XPI file at any stage. The word skin and the theme name is redundant, since the theme bundle holds exactly one theme, so paths to skin files inside the JAR need only be constructed like this: package-name/skin-file At the top level, the JAR file must contain these additional files: contents.rdf install.rdf preview.png icon.png The two .png files and some information in the contents.rdf file are used in the Theme Manager dialog box to preview and describe the theme. See any downloadable theme for an example. The files can be any raster image format the Firefox understands. There is only one contents.rdf file in the JAR archive; it aggregates all the content of the various contents.rdf files that would otherwise appear one-per-skin in these locations: package-name/contents.rdf Finally, the install.rdf file contains the product registration information [Hack #88] for the theme. Some of this is also visible in the Theme Manager. 8.7.3. Installation SupportTo install a downloadable theme, use the same web page scripts that extensions require [Hack #88] . Because themes have a very regular and predictable format, there is no special install.js file requiredjust make sure the XPI file is delivered with the right content type. Firefox reads the downloaded theme, unpacks the JAR, and automatically deploys all of the pieces into the right spots. Alternatively, you can trigger XPInstall the old-fashioned way with a script and a raw JAR file: <script> function get_theme( ) { InstallTrigger.installChrome( InstallTrigger.SKIN, 'http://mydomain.com/themes/mytheme.jar', 'myTheme v0.1'); } </script> <a title="Install my cool theme" href="javascript:get_theme( )"> Install myTheme v0.1 now! </a> |