![]() |
Configurable Chromeby Dave Hyatt (hyatt@netscape.com)Last Modified 4/7/99 What is Chrome?The chrome is that part of the application window that lies outside of a window's content area. Toolbars, menu bars, progress bars, and window title bars are all examples of elements that are typically part of the chrome.1. Chrome ProvidersA supplier of chrome for a given window type (e.g., for the navigator window) is called a chrome provider. There are four types of chrome providers: skin providers, content providers, platform providers, and localization providers. A skin provider is responsible for providing a complete set of files that describe the visual appearance of the chrome. Typically a skin provider will provide CSS files and images. A content provider is responsible for providing a complete set of files for defining the structure of the chrome (e.g., the actual contents, such as menu items and toolbar buttons). The platform provider provides all of the files necessary to distinguish certain platforms. The platform provider typically provides both appearance and structure information. The localization provider is responsible for providing the actual string resources that entities in the XUL expand into. The four providers work together to supply a complete set of chrome for a particular window, from the images on the toolbar buttons to the files that describe the text, contents and appearance of the window itself. The main source file for a window description comes from the content provider, and it can be any file type viewable from within Mozilla. It will typically be a XUL file, since XUL is designed for describing the contents of windows and dialogs. Since anyone can write a XUL file and supply images, anyone can be a chrome provider for a particular window. For example, NetCenter could be the user's chrome provider for the messenger window, but another site like MozillaZine could be the user's preferred chrome provider for the navigator window. Even within a particular window, the user may choose different providers for portions of the chrome. For example, the user might (for the navigator window) be using the MozillaZine skin, NetCenter's content, the BeOS platform, and surfing the Web in Klingon. It's all up to the user what to choose. Because the files that come from the providers can live anywhere (e.g., on the user's local hard drive or on a remote site), the chrome itself can reside on multiple web sites and can be downloaded dynamically, thus allowing providers to supply new services and commands as part of the chrome, and to update the user interface without having to make any change in the source code of the application. This feature is referred to as downloadable chrome. 2. The Chrome RegistryThe Mozilla client maintains a table known as the chrome registry that provides mappings from window types (a string identifier) to information about each of the four providers for the window types. For each provider type, a unique name is specified for the current provider, a base directory at which all of the files are located is specified, as well as the location of the main file to load for the provider type. In addition a URL to an archive (e.g., a JAR file) can be specified. The archive is optional, but it is required in order to prevent remote chrome files from being downloaded one at a time (which could adversely impact the time from the window open to finished chrome display). The preferred naming convention to use if you have a window of type foo is to use Foo (first letter capitalized) as your window type name, foo.xul as your initial source file name, foo.css as your initial CSS file name, fooContent.jar as the archive for all structural chrome information, and fooSkin.jar as the archive for all visual chrome information, fooPlatform.jar for all platform chrome information, and fooLocale.jar as the archive for localization information. The registry file is called registry.rdf and it resides in the user's profile directory (or in the user's .netscape directory on UNIX). Example registry.rdf file: <r:RDF xmlns:r="http://www.w3.org/TR/WD-rdf-syntax#" <content>
<r:Description r:ID="chrome://navigator/content/">
<base>resource:/res/samples/</base>
<main>navigator.xul</main>
</r:Description>
</content>
... locale and platform entries similar to skin and content ...
</chrome>
... other chrome entries for communicator, messenger, composer, etc. ...
</r:RDF>
A registry entry can also contain a list of other resources within the registry that it is dependent on. This provides the client chrome cache (see below) with the hint that it needs to know which files are involved for a particular window. This will typically be used when all windows are loading from some common base, e.g., in order, for example, to ensure that a skin from the common base could be applied to all windows. This chrome registry is configurable and persistent, and thus a user can swap in a different entry for any item in the table. For example, the user could decide to retain his chrome content, but to drop in a new skin, e.g., the MozillaZine skin. The registry could simply be edited, and the URL for the skin file could be changed to point to the location of the new file to use. 3. Chrome URLsNew windows can be opened via JavaScript. For example, a new window can simply be opened by passing in the URL of a XUL file, e.g., window.openChromeWindow("MyNewWindowName", "http://www.foopy.com/myCoolXUL.xul"); The file will then be loaded just as a normal Web page might load. To open a window of a particular type, e.g., a new navigator window, however, we don't want to give an explicit URL in the open call, since we don't know who the user might have as a content provider for the navigator window. If we were to hardcode a URL in our file, then we would be choosing a specific structure and content for the navigator window, one that might conflict with what the user has chosen. Therefore rather than passing in the URL of a XUL file, the page designer should use a window type instead, specified using a chrome URL. window.open("MyNewBrowserWindow", "chrome://navigator/"); When a URL of the form chrome://WindowType/" is encountered, the default XUL file for that WindowType is loaded by looking up the entry in the chrome registry. Note that chrome://WindowType/ and chrome://WindowType/content/ are synonymous. Any content files for a particular window type can be referenced using chrome content URLS, with the assumption that chrome://WindowType/content/ refers to the base directory specified for content in the chrome registry. 4. SkinsCSS is the preferred mechanism whereby look and feel can be abstracted from the structure described in the XUL file. In typical HTML, the inclusion of a CSS files is sufficient to bring in a distinct look to apply to the file. The rub is that the URL of the CSS file has to be changed if the user wants the look to change. We cannot specify an explicit CSS file in the XUL file. If we did so, we would be hardcoding the particular skin that would be applied to the window content, and we want to allow for customization of skins without forcing the user to edit the XUL file (or to generate a new XUL file). We achieve this separation by specifying a chrome URL that can be used to access the files provided by the skin provider. When a URL of the form chrome://WindowType/skin/ is encountered, the default CSS file for that WindowType is loaded from the skin provider. Any files from the skin provider can be referenced using chrome URLS, with the assumption that chrome://WindowType/skin/ refers to the base directory of the skin provider as specified in the chrome registry. Example: Suppose that the MozillaZine skin archive, navigatorSkin.jar contains an image called throbberBusy.gif in an images subdirectory. This resource can be referenced using the following URL: chrome://navigator/skin/images/throbberBusy.gif 5. Platform-Specific ChromeThe default looks and feels as well as some of the XUL content will vary from platform to platform. For example, the Mac has items in the Apple menu and its OK and CANCEL buttons aren't in the same positions within dialogs as on Windows. It will be necessary to ship with a different set of XUL fragments and CSS files on each platform. This is handled by abstracting out platform-specific fragments and styles into separate entries in the chrome registry. On the different platforms, the entries in the chrome registry will be different. Platform-specific CSS and XUL can then be loaded using chrome URLs without the app developer having to know which platform he/she is developing on. 6. Localization of ChromeFor different languages, the text that appears on toolbar buttons, in tooltips, in dialog controls, and elsewhere within the window will be language-specific. XUL writers should be able to refer to DTDs (for entity expansion) and other specific information without having to know which language the user currently has selected. Again, this can be accomplished through different entries in the chrome registry. 7. The Chrome CacheWhen the user opens up a new application window, the chrome should be immediately accessible, regardless of whether or not it originally came from a remote site. In order to accomplish this, all files referenced using chrome URLs reside in a special chrome cache. This cache is contained in a subdirectory of the main installation directory called chrome. Note that this means that the cache is user-independent, which allows all of the chrome to be accessed by all users (without duplicating the UI files across multiple user caches). The chrome directory contains a subdirectory for each window type. Within each window type directory there are four subdirectories. The first is skin, in which all skin files reside. There is also a subdirectory called content, in which all of the content files reside. The remaining two directories, platform and locale, are for the platform-specific and locale-specific files. Within each provider type directory is a subdirectory for each named provider that is currently being observed by a user. The cache is smart enough to remove any provider directories that are no longer in use by users. All chrome URLs reference the cache. No URLs are ever fetched remotely when a chrome URL is requested. The updating instead happens at some later point through the use of a background thread. When Mozilla open a particular window type for the first time in a session, a background thread pings all of the remote files found in the chrome cache for that window type to see if any of them are dirty (this is why it's advisable to put all chrome in JAR files). Upon discovery of a dirty file, the Mozilla client makes a randomized decision about when it should really download the new file (ranging from immediately to up to two weeks in the future). The randomized decision is the same for all files within the same window type (so that synchronization can be maintained when portions of the chrome for a window need to be updated simultaneously). The background thread then checks to see if any of its files need to be downloaded (based on a randomized decision it already made some time in the past), and it then updates the files. The changes do not happen dynamically but will take effect the next time the user opens the window. This update happens automatically. The user is not prompted; the chrome simply updates. The randomized decision about when to update is necessary to enable NetCenter (with its enormous user base) to have appropriately staggered updates when a chrome change is made. (Otherwise millions of users would be hitting the server more or less within hours of one another.) When a chrome URL is specified, the client does the following:
Directory Structure Outline: [Mozilla]
[chrome]
[navigator]
[skin]
[default]
navigatorSkin.jar
... other files bundled with navigator ...
... other skins used by users for navigator ...
[content]
[default]
navigatorContent.jar
... other files bundled with navigator ...
[platform]
[default]
... optional JAR file and other files ...
[locale]
[default]
... optional JAR file and other files ...
... subdirectories for other specific window types ...
8. Local Annotation of ChromeSome changes to the chrome's content and to the chrome's skin are made using local annotations. These local annotations are profile-specific, and are written into RDF's local store and serialized as .RDF files (so that the user can easily edit or blow away local annotations). The user never makes local annotations explicitly. They happen as a side effect of certain UI gestures. For example, if the user removes the Back button from the command toolbar, then an annotation is made that the back button is no longer present on the user's toolbars. If the user resizes a window, then an annotation is made to remember the new size of the window. If the user hides columns in the bookmarks tree view or collapses a toolbar, annotations are made for that user only. When and how to annotate downloaded chrome is up to the XUL writers. The XUL language itself contains policies for describing whether or not changes to a particular AOM tree are persistent or non-persistent. Only persistent changes result in local annotations being made to the chrome. Policies for Fixing Mistakes - Nodes can be deleted from toolbars or properties of nodes can be changed that render the view unusable. It is important therefore to provide the user with a way to recover if he/she makes a mistake. The first line of defense for deletion is to bring up a dialog that double-checks the deletion request. No item should be deleted without first prompting the user. Next, all changes should be reversible using the Undo system. Finally, there should be a blanket reset option for the chrome that returns it to the state specified in the original files. 9. Modifying the Chrome through JavaScriptA script writer can modify the chrome in a number of different ways. First, the script writer can use normal DOM APIs to add or remove content from XUL. If the changes made are persistent, then local annotations (see section 8 above) will be made to the user's chrome. For example, a script might collapse a toolbar or change the name of a toolbar button. These changes would result in local annotations. These local annotations can also be made by speaking the lower-level RDF APIs. A script writer can change the user's skin, content, platform, or locale by using the lower-level RDF APIs to manipulate the chrome registry. A sample code snippet below shows how to change the chrome registry to apply a different skin to the navigator window. [Note: right now to get a handle to RDF from within a XUL document, you can just say document.rdf. This will eventually go away, to be replaced with the proper mechanism for obtaining the rdf service.] function applySkin(baseDirectory, skinFileName)
{
var ds = document.rdf.GetDataSource("rdf:chrome");
// For M4 builds, use this line instead.
// var ds = document.rdf.GetDataSource("resource:/chrome/registry.rdf");
var sourceNode = document.rdf.GetResource("chrome://navigator/skin/");
var baseArc = document.rdf.GetResource("http://chrome.mozilla.org/rdf#base");
var mainArc = document.rdf.GetResource("http://chrome.mozilla.org/rdf#main");
// Get the old targets
var oldBaseTarget = ds.GetTarget(sourceNode, baseArc, true);
var oldMainTarget = ds.GetTarget(sourceNode, mainArc, true);
// Get the new targets
var newBaseTarget = document.rdf.GetLiteral(baseDirectory);
var newMainTarget = document.rdf.GetLiteral(skinFileName);
// Unassert the old relationships
ds.Unassert(sourceNode, baseArc, oldBaseTarget);
ds.Unassert(sourceNode, mainArc, oldMainTarget);
// Assert the new relationships (note that we want a reassert rather than
// an unassert followed by an assert, once reassert is implemented)
ds.Assert(sourceNode, baseArc, newBaseTarget);
ds.Assert(sourceNode, mainArc, newMainTarget);
// Flush the modified data source to disk
// (Note: crashes in M4 builds, so don't use Flush() until fix checked in)
ds.Flush();
// Open up a new window to see your new chrome, since changes aren't yet dynamically
// applied to the current window
}
Note that scripts that manipulate the chrome registry will have to be signed, and they won't be allowed to make the change automatically. There will ultimately be a higher-level API for accomplishing this (so that an intimate knowledge of RDF isn't required to accomplish this). [TODO: Define higher-level API. :) ] 10. ThemesA theme is an RDF file that specifies its own chrome registry. When a theme is installed, all of the entries contained in the theme's chrome registry are copied into the user's chrome registry. This allows chrome writers to specify a theme for several different window types and provider types at once and have the theme be installed in one fell swoop onto the user's machine. [TODO: Discuss this in more detail. Define API for theme manipulation.] |
|
|
Copyright © 1998-1999 The Mozilla Organization.
Last modified April 8, 1999. |
|