The content preferences code currently has a four piece architecture:
- a service that manages the database of prefs;
- a controller for each window that watches the browser’s location and notifies pref handlers when it changes;
- pref handlers that tweak the browser according to the user’s prefs;
- a sidebar housing controls for twiddling prefs.
In the first version of the extension, I designed these to support preferences like the text zoom pref. In v2 I redesigned them to improve that support and tried to generalize the API to work for other prefs.
But a single caller is a very limited perspective for designing an API (indeed, I’ve already found issues while getting a second pref up and running on it), so I’m keen to get feedback on how to make this work well for other prefs (in both core code and extensions).
Here’s how it works at the moment:
The Service (mozISitePrefService)
The service manages the database of prefs. Its API is pretty simple. It has four core methods, getPref, setPref, hasPref, and removePref:
mozISitePref getPref(in AUTF8String site, in AUTF8String key);
void setPref(in AUTF8String site, in AUTF8String key, in AUTF8String value);
boolean hasPref(in AUTF8String site, in AUTF8String key);
void removePref(in AUTF8String site, in AUTF8String key);
It also has a method, getPrefs, that gets all prefs for a site, and it has another method, getSiteForURI, that extracts a site identifier from a URI:
void getPrefs(in AUTF8String site,
out unsigned long prefCount,
[retval, array, size_is(prefCount)] out mozISitePref prefs);
AUTF8String getSiteForURI(in nsIURI uri);
Site identifiers are full domains by default (f.e. www.mozilla.org), but you can configure the extension to use base domains (f.e. mozilla.org), and perhaps we’ll cook up other options in the future.
The interface to a preference (mozISitePref) is very basic:
readonly attribute AUTF8String site;
readonly attribute AUTF8String key;
readonly attribute AUTF8String value;
The Controller (SitePref)
The controller’s job is to notify pref handlers when the browser location changes so those handlers can apply the user’s preferences for the new location. It has addObserver and removeObserver methods that handlers can use to register/unregister themselves for notifications:
addObserver(in AUTF8String key, in nsISupports observer)
removeObserver(in AUTF8String key, in nsISupports observer)
The methods take a pref key so the controller know what setting the handler cares about. When the location changes, the controller grabs the set of prefs for the new location and calls each observer’s onLocationChange method with the pref it cares about (if any):
onLocationChange(in nsISitePref pref)
The Pref Handlers
The pref handlers implement the aforementioned onLocationChange method and then do whatever they need to do to apply user preferences to the browser.
The sidebar provides a central location for site-specific preference controls. It’s an experiment in exposing these preferences in a place that is location-specific (unlike the Preferences dialog) and visible alongside the page being affected (unlike the View menu).
It doesn’t do much besides provide a place for pref controls to overlay themselves onto. Controls are responsible for updating themselves when the location changes. But the sidebar does define a gMainWindow shortcut for getting the browser window that the sidebar is embedded in.
Feedback
I’m interested in feedback on all aspects of this API. Here are the questions I know I should ask:
- Should pref values be typed?
We can’t store them typed in the database given the EAV model we’re using to store them (although we could change the model if it mattered enough), but we could convert them on their way in and out of the database and have type-specific methods for getting and setting them (à la nsIPrefBranch).
- Should pref handlers listen for location changes themselves and get their prefs directly from the service instead of having that work be mediated by the controller?
It’s faster for the controller to query once for all prefs, and it’s simpler on the pref handlers not to have to implement nsIWebProgressListener themselves, but it makes the architecture more complex, since it adds an extra layer between the service and the handlers.
- Is AUTF8String the right string type for site identifiers and keys?
And then there are all those questions I don’t know I should be asking (along with their answers), so clue me in!