Dev : Using preferences: Difference between revisions

From MozillaZine Knowledge Base
Jump to navigationJump to search
m (cat redirect)
 
(5 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{extdev}}
Moved [http://developer.mozilla.org/en/docs/Code_snippets:Preferences here]


This article is about using Mozilla Preferences system. Information here applies to the Mozilla Suite, Firefox, Thunderbird and possibly other Mozilla-based applications. Intended audience is Mozilla extension developers who wish to learn details about using preferences in Mozilla. <!-- Read about preferences in general [[??? | here]]. -->
[[Category:Redirects]]
 
If you haven't yet, read other documents about Mozilla preferences on XUL Planet and on mozilla.org (links below in [[#Resources|Resources section]]).
 
'''Note:''' This article doesn't cover all available methods for manipulating preferences yet, please refer to XulPlanet XPCOM reference pages listed in [[#Resources|Resources section]] for the complete list of methods. The interfaces dealing with preferences are fairly well documented, so using the methods not documented here should be easy.
 
==XPCOM interfaces for preferences system==
Mozilla exposes its preferences system through a few XPCOM interfaces. Look in the [[#Resources|Resources section]] below for the link to list of preferences-related interfaces.
 
Three most used interfaces are <code>nsIPrefService</code>, <code>nsIPrefBranch</code> and <code>nsIPrefBranchInternal</code>. First two are frozen and will not change, last one is &ldquo;internal&rdquo;, but is very useful because it allows you to set up prefs observers &mdash; see below for an example.
 
It's worth noting that there also is an <code>nsIPref</code> interface. Despite it being used in some places, it is '''deprecated''' and should not be used.
 
Preferences service is instantiated exactly as any other service. (See [http://xulplanet.com/references/xpcomref/creatingcomps.html Creating XPCOM Components] document at XUL Planet for details.) To get an <code>nsIPrefBranch</code>, either QI the pref service (that will give you the root branch) or call <code>nsIPrefService.getBranch()</code> to get a sub-branch.
 
Here are two examples:
<pre>
// Get the root branch
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                    getService(Components.interfaces.nsIPrefBranch);
</pre>
<pre>
// Get the "extensions.myext." branch
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                    getService(Components.interfaces.nsIPrefService);
prefs = prefs.getBranch("extensions.myext.");
</pre>
 
==Simple types==
There are three types of preferences: '''string''', '''integer''' and '''boolean'''. Each entry in preferences database (prefs.js) has one of those types. There are six methods of <code>nsIPrefBranch</code> that read and write them: <code>getBoolPref</code>, <code>setBoolPref</code>, <code>getCharPref</code>, <code>setCharPref</code>, <code>getIntPref</code> and <code>setIntPref</code>. Using them is as easy as:
<pre>
// prefs is an nsIPrefBranch.
// Look in the above section for examples of getting one.
var value = prefs.getBoolPref("accessibility.typeaheadfind"); // get a pref
prefs.setBoolPref("accessibility.typeaheadfind", !value); // set a pref
</pre>
 
==Complex types==
As noted in previous section, each entry in prefs database (prefs.js) must have a string, an integer or a boolean value. However, there is a concept of '''complex types''', which makes it easier for developers to save and load <code>nsILocalFile</code> and <code>nsISupportsString</code> objects in preferences (as strings &mdash; note that from the preferences system's POV, complex values have a <code>nsIPrefBranch.PREF_STRING</code> type.)
 
There are two <code>nsIPrefBranch</code> methods implementing the concept &mdash; <code>setComplexValue</code> and <code>getComplexValue</code>. You can look up their implementation in [http://lxr.mozilla.org/mozilla/source/modules/libpref/src/nsPrefBranch.cpp#221 nsPrefBranch.cpp]. Here is the IDL definition:
<pre>void getComplexValue(in string aPrefName, in nsIIDRef aType,
[iid_is(aType), retval] out nsQIResult aValue);
 
void setComplexValue(in string aPrefName, in nsIIDRef aType, in nsISupports aValue);</pre>
 
As you can see, both of them take <code>aType</code> parameter. It can have the following values <small>(to be precise, you should pass <code>Components.interfaces.nsIWhatever</code> instead of just <code>nsIWhatever</code>, which is undefined):</small>
*<code>[[#nsISupportsString|nsISupportsString]]</code> &mdash; used to handle unicode strings in preferences. Use this when the preference value may contain non-ASCII characters (for example, user's name).
*<code>[[#nsIPrefLocalizedString|nsIPrefLocalizedString]]</code> &mdash; almost same as <code>nsISupportsString</code>, but it is handled differently in <code>getComplexValue</code> when there's no user value for given preference, see below for details.
*[[Dev : Extensions : Example Code : File IO|<code>nsILocalFile</code> and <code>nsIRelativeFilePref</code>]] &mdash; store paths in preferences. <code>nsILocalFile</code> is used to store absolute paths, while <code>nsIRelativeFilePref</code> is used to store paths relative to one of &ldquo;special&rdquo; directories, like the profile folder.
 
===nsISupportsString===
As noted above, this is used to handle unicode strings in preferences. Example:
<pre>
// prefs is an nsIPrefBranch
 
// Example 1: getting unicode value
var value = prefs.getComplexValue("preference.with.non.ascii.value",
      Components.interfaces.nsISupportsString).data;
 
// Example 2: setting unicode value
var str = Components.classes["@mozilla.org/supports-string;1"]
      .createInstance(Components.interfaces.nsISupportsString);
str.data = "some non-ascii text";
prefs.setComplexValue("preference.with.non.ascii.value",
      Components.interfaces.nsISupportsString, str);
</pre>
 
===nsIPrefLocalizedString===
Another complex type supported by Mozilla is <code>nsIPrefLocalizedString</code>. It is similar to <code>nsISupportsString</code>, the only difference is that when there is no user value, <code>getComplexValue</code> gets the default value from a locale file (thus making the default value localizable).
 
It's easier to explain this on example. Let's say you want to make the default value for <code>extensions.myext.welcomemessage</code> preference localizable. You should do the following:
<ol>
<li>Add this line to some <code>.properties</code> file (for all of your locales), say to <code>chrome://myext/locale/defaults.properties</code>:
<pre>extensions.myext.welcomemessage=Localized default value</pre></li>
 
<li>Add the default value for <code>extensions.myext.welcomemessage</code>, pointing to that properties file, by adding the following line to your file with ''default preferences'' (see below).
<pre>pref("extensions.myext.welcomemessage", "chrome://myext/locale/defaults.properties");</pre>
</li>
 
<li>Read the preference with <code>getComplexValue</code>, passing <code>nsIPrefLocalizedString</code> as <code>aType</code>:
<pre>
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
      getService(Components.interfaces.nsIPrefService);
var branch = prefs.getBranch("extensions.myext.");
var value = branch.getComplexValue("welcomemessage",
      Components.interfaces.nsIPrefLocalizedString).data;
</pre></li>
</ol>
 
The code in step 3 will read the default value from <code>chrome://myext/locale/defaults.properties</code> when no user value is set, and will behave exactly the same as if <code>nsISupportsString</code> was passed as <code>aType</code> otherwise.
 
 
Setting <code>nsIPrefLocalizedString</code> preferences is similar to setting <code>nsISupportsString</code>:
var pls = Components.classes["@mozilla.org/pref-localizedstring;1"]
                    .createInstance(Components.interfaces.nsIPrefLocalizedString);
pls.data = val;
prefs.setComplexValue("preference.with.non.ascii.value",
                      Components.interfaces.nsIPrefLocalizedString, pls);
 
===nsILocalFile and nsIRelativeFilePref===
<!-- Leave this section to have nice TOC -->
Please see the [[Dev : Extensions : Example Code : File IO|File IO article]] for details on <code>nsILocalFile</code> and <code>nsIRelativeFilePref</code>.
 
==Default preferences==
<!-- someone should reword this section -->
Each preference may have up to two values &mdash; '''current''' and '''default'''. That means there are two &ldquo;pref trees&rdquo; &mdash; current and default, &mdash; and each of them may or may not have a value for preference in question.
 
You can see the list of preferences in [[about:config]] (where available). Preferences that have user value are bold, those that don't have a user value are printed in normal font.
 
You can get both trees using <code>nsIPrefService.getBranch()</code> and <code>nsIPrefService.getDefaultBranch()</code> functions. See below for details.
 
===What effect do default preferences have on various ''get'' methods===
When one of ''get'' methods of <code>nsIPrefBranch</code> (assuming it's a branch of the tree with current values) is called, it does the following:
#Checks whether the &ldquo;current&rdquo; tree has a value for the pref and whether the pref is locked.
#If there's a value of correct type (f.e. <code>getBoolValue</code> expects a value of type <code>nsIPrefBranch.PREF_BOOL</code>), and the preference is not locked, the method returns that value.
#If there's a value of incorrect type and the pref is not locked, an exception is thrown (<code>NS_ERROR_UNEXPECTED</code>)
#If the preference is locked or if there is no value for that preference in &ldquo;current&rdquo; tree, the ''get'' method checks the default tree.
#If there's a value of expected type in the &ldquo;default&rdquo; tree, it is returned (with the only exception, <code>getComplexValue</code> with <code>aType</code> parameter = <code>nsIPrefLocalizedString</code>, [[#nsIPrefLocalizedString|described above]])
#Otherwise an exception is thrown (<code>NS_ERROR_UNEXPECTED</code>).
 
If the branch is from the &ldquo;default&rdquo; tree, the ''get'' method doesn't check the tree with current values at all.
 
<small>(This is not exactly how it's coded in <code>libpref</code>, but it's equivalent)</small>
 
===Where are the default values read from===
*All Mozilla-based applications read <code>(application directory)/defaults/pref/*.js</code> <!-- (xxx are non-.js files read?) -->.
*In addition to that, recent versions of Toolkit applications (Firefox 1.0, Thunderbird 1.0 and the like; '''not''' Mozilla Suite) read extension defaults&mdash;usually <code>(profile folder)/extensions/(ID)/defaults/preferences/</code>
 
These files use simple JS-like syntax. To add a default value for a preference, you should add a line like this to your default preferences file:
pref("extensions.infolister.hide_menu_item", false);
 
===How to install extension's defaults files===
For Mozilla, copy them to <code>(appdir)/defaults/pref</code> in your [[Install.js|install script]]. (See [[Mozilla Default Prefs|this]] page for a profile directory based solution).
 
For Firefox/Thunderbird, just put them in <code>myext.xpi/defaults/preferences/</code>. They will be copied and registered with the prefs system automatically.
 
==More about preferences "branches"==
Preferences names consist of a few strings separated with dots, and related prefs usually share the same prefix. For example, most accessibility preferences in Mozilla start with "accessibility."
 
This means that all existing preferences can be imagined as if they were in a tree, like this:
+
|
+-- accessibility
|        |
|        +-- typeaheadfind
|        |        |
|        |        +-- autostart (''accessibility.typeaheadfind.autostart'')
|        |        |
|        |        +-- enablesound (''accessibility.typeaheadfind.enablesound'')
|        |
|        +-- usebrailledisplay (''accessibility.usebrailledisplay'')
|
+-- extensions
          |
          +-- lastAppVersion (''extensions.lastAppVersion'')
 
This is the metaphor behind <code>nsIPref'''Branch'''</code>. However, you should be aware of the fact that Mozilla preferences system doesn't treat dots in a special way. For example this code will also read the value of <code>''accessibility.typeaheadfind.enablesound''</code> preference:
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                    getService(Components.interfaces.nsIPrefService);
var branch = prefs.getBranch("acce");
var enablesound = branch.getBoolPref("ssibility.typeaheadfind.enablesound");
 
This is the reason why you should usually pass strings ending with a dot to <code>getBranch()</code>, like <code>prefs.getBranch("accessibility'''.'''")</code>.
 
Another caveat you should be aware of is that <code>nsIPrefBranch.getChildList("")</code> returns an array of preferences names that start with that branch's <code>root</code>, for example
var branch = prefs.getBranch("accessibility.");
var children = branch.getChildList("", {});
will return these items (for the example tree above): <code>"typeaheadfind.autostart", "typeaheadfind.enablesound", "usebrailledisplay"</code>, -- not just direct children (<code>"typeaheadfind"</code> and <code>"usebrailledisplay"</code>), as you might have expected.
 
==Using preferences observers==
You can use <code>nsIPrefBranchInternal</code> interface to &ldquo;listen&rdquo; to changes to preferences in a certain branch.
 
: <small>'''Note:''' During Gecko 1.8 development, <code>nsIPrefBranchInternal</code> was renamed to <code>nsIPrefBranch2</code> [https://bugzilla.mozilla.org/show_bug.cgi?id=281414] and was frozen. <code>nsIPrefBranchInternal</code> name is still supported in Gecko 1.8, so this is what you should use in extensions that need to be compatible with Gecko 1.7 ''and'' Gecko 1.8 (Firefox 1.0/1.5). For newer extensions use <code>nsIPrefBranch2</code>.</small>
 
Here's an example:
 
<pre>
var myPrefObserver =
{
  register: function()
  {
    var prefService = Components.classes["@mozilla.org/preferences-service;1"]
                                .getService(Components.interfaces.nsIPrefService);
    this._branch = prefService.getBranch("extensions.myextension.");
    this._branch.QueryInterface(Components.interfaces.nsIPrefBranch2);
    this._branch.addObserver("", this, false);
  },
 
  unregister: function()
  {
    if(!this._branch) return;
    this._branch.removeObserver("", this);
  },
 
  observe: function(aSubject, aTopic, aData)
  {
    if(aTopic != "nsPref:changed") return;
    // aSubject is the nsIPrefBranch we're observing (after appropriate QI)
    // aData is the name of the pref that's been changed (relative to aSubject)
    switch (aData) {
      case "pref1":
        // extensions.myextension.pref1 was changed
        break;
      case "pref2":
        // extensions.myextension.pref2 was changed
        break;
    }
  }
}
myPrefObserver.register();
</pre>
 
[http://lxr.mozilla.org/seamonkey/source/modules/libpref/public/nsIPrefBranch2.idl nsIPrefBranch2.idl] has more documentation.
 
==Using prefHasUserValue==
 
prefHasUserValue(''preference'') checks whether the preference has been changes from the default value to another one (by an user or an extension), if so it will return true, else false. in addition prefHasUserValue can be also used to check whether a preference exists. if calling a preference e.g. by getCharPref which was not defined previously the system will throw an exception (which of course can be handled by try...catch (which is not a nice solution to check whether a preference exists)). Using prefHasUserValue will return false if the preference was not defined before. This can be used for example to initialize an extension (see code snippet below).
<pre>
...
var QuickMenuMC_pref = Components.classes["@mozilla.org/preferences-service;1"]
                              .getService(Components.interfaces.nsIPrefBranch);
...
if(QuickMenuMC_pref.prefHasUserValue("QuickMenuMC.FolderLabel")){
...
}
</pre>
 
==Using preferences in extensions==
If you're writing your extension for one of Toolkit applications (Firefox, Thunderbird, Nvu), it's recommended to provide default values for your extension's preferences (see above for information on how to do it). It has the following benefits:
* You don't have to duplicate default values in various parts of your code.
* The code reading preferences is simplified, you don't need to worry about get* methods throw an exception, because under normal circumstances they won't do it.
 
== JavaScript wrappers for preferences system ==
There are a few JavaScript wrappers to make your life easier: see
[[Dev_:_Extensions_:_Example_Code_:_Javascript_Preferences_Class|this]], [http://mozilla.doslash.org/prefutils this], and the <code>nsPreferences</code> wrapper included with Firefox and Thunderbird (<tt>chrome://global/content/nsUserSettings.js</tt>).
 
==Resources==
*Other documentation on preferences
**[http://www.mozilla.org/catalog/end-user/customizing/briefprefs.html A Brief Guide to Mozilla Preferences] &mdash; describes preferences system from user's / administrator's POV.
**[http://www.xulplanet.com/tutorials/xulqa/q_prefs.html XUL Planet's article on preferences] &mdash; an explanation of the preferences system with simple examples. A must read.
*Mozilla XPCOM interfaces of the preferences system.
**[http://xulplanet.com/references/xpcomref/group_Preferences.html#Preferences Complete list]
**Most used interfaces (these are frozen and will not change): <code>[http://xulplanet.com/references/xpcomref/ifaces/nsIPrefBranch.html nsIPrefBranch]</code> and <code>[http://xulplanet.com/references/xpcomref/ifaces/nsIPrefService.html nsIPrefService]</code>
**<code>[http://xulplanet.com/references/xpcomref/ifaces/nsIPrefBranchInternal.html nsIPrefBranchInternal]</code> interface (in Gecko 1.8 it is called <code>nsIPrefBranch2</code> and is frozen).
 
*[http://lxr.mozilla.org/mozilla/source/modules/libpref/ LXR pages for libpref], the module implementing preferences system.
*Javascript Preferences wrappers: [[Dev : Extensions : Example Code : Javascript Preferences Class |  one]], [http://mozilla.doslash.org/prefutils two].
 
[[Category:Example code|Using preferences]] [[Category:XPCOM example code|Using preferences]]

Latest revision as of 21:31, 22 January 2007

Moved here