Education/Learning/Preferences
Contents
Introduction
Mozilla applications like Firefox or Thunderbird are highly configurable via a preference system. Developing new features, or fixing bugs in existing code, often means identifying and honouring the wishes of the user, as expressed in various preferences. When you're working on a piece of code in JavaScript or C++, you need to know how to determine the value of preferences at run-time.
This discussion will take a real example from Firefox and show how the preferences is accessed by the user, implemented in terms of UI, and then used by C++ code in order to determine run-time behaviour. All code samples below are taken from the following revision of mozilla-central: http://hg.mozilla.org/mozilla-central/file/0cd41f599080/
A Firefox Preference
When you close your browser, what should happen to all the private data that was stored during your various sessions? Should things like cookies and other data remain? Should they be deleted? It's not something that Firefox determines at compile time. Instead there is a preference so the user can decide.
The easiest way to change this behaviour is to open the Firefox Preferences dialog (Tools > Options on Windows, Firefox > Preferences on Mac) and navigate to the Privacy panel. There, under Private Data, you'll find the following checkbox:
Always clear my private data when I close Firefox
Now that we have the UI string, we can go looking for the actual preference and code to deal with it.
Finding a Preference
Using MXR we can search for this UI string, and find the DTD file where it is defined (Mozilla localizes all strings in the UI). A quick search reveals the following entity in privacy.dtd:
44 <!ENTITY alwaysClear.label "Always clear my private data when I close &brandShortName;">
Now we have an entity name vs. a localized string (i.e., alwaysClear.label) to search for within the XUL code. Another search reveals the use of this entity in privacy.xul:
178 <checkbox id="alwaysClear" flex="1" 179 preference="privacy.sanitize.sanitizeOnShutdown" 180 label="&alwaysClear.label;" 181 accesskey="&alwaysClear.accesskey;"/>
This is the checkbox we saw in the UI, and also the next bit of information we need to continue our search: the name of the preference itself:
179 preference="privacy.sanitize.sanitizeOnShutdown"
Preferences are named using this dot notation, which denotes a tree structure (i.e., privacy can (and does) have many related children beneath it). At this point we can go and look at the current value of the preference by name using the browser itself. In your address bar, enter the following location:
about:config
This gives a complete list of the preferences currently set for the browser. Obviously there are many more than have visible UI in the preferences dialog. You can add preferences here simply by right-clicking and selecting New.
To locate a preference, begin typing its name: privacy.sanitize.sanitizeOnShutdown. If you want to alter the value of this preference, simply double-click on it.
Accessing the preference value from code
Looking around users of this preference in the code reveals a few interesting things. First, we see the default value being set in firefox.js:
411 pref("privacy.sanitize.sanitizeOnShutdown", false);
We also see how the value of this preference influences what happens on shutdown in nsNavHistoryExpire::OnQuit():
91 // Sanitization preferences 92 #define PREF_SANITIZE_ON_SHUTDOWN "privacy.sanitize.sanitizeOnShutdown" ... 213 nsCOMPtr<nsIPrefBranch> prefs(do_GetService("@mozilla.org/preferences-service;1")); 214 PRBool sanitizeOnShutdown, sanitizeHistory; 215 prefs->GetBoolPref(PREF_SANITIZE_ON_SHUTDOWN, &sanitizeOnShutdown); 216 prefs->GetBoolPref(PREF_SANITIZE_ITEM_HISTORY, &sanitizeHistory); 217 if (sanitizeHistory && sanitizeOnShutdown) 218 return;
This is a common pattern seen all over the Mozilla source. It begins with a define for the preference name, which will be used by nsIPrefBranch to get the desired value.
Next, we see XPCOM code to get the Preferences Service using the nsIPrefBranch interface. Using nsIPrefBranch's GetBoolPref method, a PRBool is populated with the value needed.
Other Resources
There are many good resources available for further reading on the subject of Preferences:
- A Brief Guide to Mozilla Preferences
- The Mozilla Preference System
- Preference Coding Examples
- Preference XUL Reference
Also remember that the source code is filled with great examples, like the one above.