Dev : Using preferences: Difference between revisions

From MozillaZine Knowledge Base
Jump to navigationJump to search
m (s/depreciated/deprecated/)
m (recategorizing + restyling)
Line 8: Line 8:
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.
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 <tt>nsIPrefService</tt>, <tt>nsIPrefBranch</tt> and <tt>nsIPrefBranchInternal</tt>. 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.
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 <tt>nsIPref</tt> interface. Despite it being used in some places, it is '''deprecated''' and should not be used.
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 <tt>nsIPrefBranch</tt>, either QI the pref service (that will give you the root branch) or call <tt>nsIPrefService.getBranch()</tt> to get a sub-branch.
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:
Here are two examples:
Line 28: Line 28:


==Simple types==
==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 <tt>nsIPrefBranch</tt> that read and write them: <tt>getBoolPref</tt>, <tt>setBoolPref</tt>, <tt>getCharPref</tt>, <tt>setCharPref</tt>, <tt>getIntPref</tt> and <tt>setIntPref</tt>. Using them is as easy as:
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>
<pre>
// prefs is an nsIPrefBranch.
// prefs is an nsIPrefBranch.
Line 37: Line 37:


==Complex types==
==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 <tt>nsILocalFile</tt> and <tt>nsISupportsString</tt> objects in preferences (as strings &mdash; note that from the preferences system's POV, complex values have a <tt>nsIPrefBranch.PREF_STRING</tt> type.)
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 <tt>nsIPrefBranch</tt> methods implementing the concept &mdash; <tt>setComplexValue</tt> and <tt>getComplexValue</tt>. 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:
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,  
<pre>void getComplexValue(in string aPrefName, in nsIIDRef aType,  
[iid_is(aType), retval] out nsQIResult aValue);
[iid_is(aType), retval] out nsQIResult aValue);
Line 45: Line 45:
void setComplexValue(in string aPrefName, in nsIIDRef aType, in nsISupports aValue);</pre>
void setComplexValue(in string aPrefName, in nsIIDRef aType, in nsISupports aValue);</pre>


As you can see, both of them take <tt>aType</tt> parameter. It can have the following values <small>(to be precise, you should pass <tt>Components.interfaces.nsIWhatever</tt> instead of just <tt>nsIWhatever</tt>, which is undefined):</small>
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>
*<tt>[[#nsISupportsString|nsISupportsString]]</tt> &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>[[#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).
*<tt>[[#nsIPrefLocalizedString|nsIPrefLocalizedString]]</tt> &mdash; almost same as <tt>nsISupportsString</tt>, but it is handled differently in <tt>getComplexValue</tt> when there's no user value for given preference, see below for details.
*<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|<tt>nsILocalFile</tt> and <tt>nsIRelativeFilePref</tt>]] &mdash; store paths in preferences. <tt>nsILocalFile</tt> is used to store absolute paths, while <tt>nsIRelativeFilePref</tt> is used to store paths relative to one of &ldquo;special&rdquo; directories, like the profile folder.
*[[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===
===nsISupportsString===
Line 68: Line 68:


===nsIPrefLocalizedString===
===nsIPrefLocalizedString===
Another complex type supported by Mozilla is <tt>nsIPrefLocalizedString</tt>. It is similar to <tt>nsISupportsString</tt>, the only difference is that when there is no user value, <tt>getComplexValue</tt> gets the default value from a locale file (thus making the default value localizable).
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 <tt>extensions.myext.welcomemessage</tt> preference localizable. You should do the following:
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>
<ol>
<li>Add this line to some <tt>.properties</tt> file (for all of your locales), say to <tt>chrome://myext/locale/defaults.properties</tt>:
<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>
<pre>extensions.myext.welcomemessage=Localized default value</pre></li>


<li>Add the default value for <tt>extensions.myext.welcomemessage</tt>, pointing to that properties file, by adding the following line to your file with ''default preferences'' (see below).
<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>
<pre>pref("extensions.myext.welcomemessage", "chrome://myext/locale/defaults.properties");</pre>
</li>
</li>


<li>Read the preference with <tt>getComplexValue</tt>, passing <tt>nsIPrefLocalizedString</tt> as <tt>aType</tt>:
<li>Read the preference with <code>getComplexValue</code>, passing <code>nsIPrefLocalizedString</code> as <code>aType</code>:
<pre>
<pre>
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
Line 89: Line 89:
</ol>
</ol>


The code in step 3 will read the default value from <tt>chrome://myext/locale/defaults.properties</tt> when no user value is set, and will behave exactly the same as if <tt>nsISupportsString</tt> was passed as <tt>aType</tt> otherwise.
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.


===nsILocalFile and nsIRelativeFilePref===
===nsILocalFile and nsIRelativeFilePref===
<!-- Leave this section to have nice TOC -->
<!-- Leave this section to have nice TOC -->
Please see the [[Dev : Extensions : Example Code : File IO|File IO article]] for details on <tt>nsILocalFile</tt> and <tt>nsIRelativeFilePref</tt>.
Please see the [[Dev : Extensions : Example Code : File IO|File IO article]] for details on <code>nsILocalFile</code> and <code>nsIRelativeFilePref</code>.


==Default preferences==
==Default preferences==
Line 102: Line 102:


===What effect do default preferences have on various ''get'' methods===
===What effect do default preferences have on various ''get'' methods===
When one of ''get'' methods of <tt>nsIPrefBranch</tt> (assuming it's a branch of the tree with current values) is called, it does the following:
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.
#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. <tt>getBoolValue</tt> expects a value of type <tt>nsIPrefBranch.PREF_BOOL</tt>), and the preference is not locked, the method returns that value.
#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 (<tt>NS_ERROR_UNEXPECTED</tt>)
#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 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, <tt>getComplexValue</tt> with <tt>aType</tt> parameter = <tt>nsIPrefLocalizedString</tt>, [[#nsIPrefLocalizedString|described above]])
#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 (<tt>NS_ERROR_UNEXPECTED</tt>).
#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.
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 <tt>libpref</tt>, but it's equivalent)</small>
<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===
===Where are the default values read from===
*All Mozilla-based applications read <tt>(application directory)/defaults/pref/*.js</tt> <!-- (xxx are non-.js files read?) -->.
*All Mozilla-based applications read <code>(application directory)/defaults/pref/*.js</code> <!-- (xxx are non-.js files read?) -->.
* In addition to that, the recent versions of new toolkit applications (Firefox 1.0, Thunderbird 1.0 and the like; '''not''' Mozilla Suite) read files listed in <tt>(profile folder)/defaults.ini</tt> &mdash; usually <tt>(profile folder)/extensions/{guid}/defaults/preferences/</tt>
* In addition to that, the recent versions of new toolkit applications (Firefox 1.0, Thunderbird 1.0 and the like; '''not''' Mozilla Suite) read files listed in <code>(profile folder)/defaults.ini</code> &mdash; usually <code>(profile folder)/extensions/{guid}/defaults/preferences/</code>


===How to install extension's defaults files===
===How to install extension's defaults files===
For Mozilla, copy them to <tt>(appdir)/defaults/pref</tt> in your [[Install.js|install script]].
For Mozilla, copy them to <code>(appdir)/defaults/pref</code> in your [[Install.js|install script]].


For Firefox/Thunderbird, just put them in <tt>myext.xpi/defaults/preferences/</tt>. They will be copied and registered in <tt>defaults.ini</tt> automatically.
For Firefox/Thunderbird, just put them in <code>myext.xpi/defaults/preferences/</code>. They will be copied and registered in <code>defaults.ini</code> automatically.


==Using preferences observers==
==Using preferences observers==
You can use <tt>nsIPrefBranchInternal</tt> interface to &ldquo;listen&rdquo; to changes to preferences in a certain branch. Here's an example (note, this code hasn't actually been tested, so it may contain typos and other errors):
You can use <code>nsIPrefBranchInternal</code> interface to &ldquo;listen&rdquo; to changes to preferences in a certain branch. Here's an example (note, this code hasn't actually been tested, so it may contain typos and other errors):


<pre>var myPrefObserver =
<pre>var myPrefObserver =
Line 169: Line 169:
*Mozilla XPCOM interfaces of the preferences system.
*Mozilla XPCOM interfaces of the preferences system.
**[http://xulplanet.com/references/xpcomref/group_Preferences.html#Preferences Complete list]
**[http://xulplanet.com/references/xpcomref/group_Preferences.html#Preferences Complete list]
**Most used interfaces (these are frozen and will not change): [http://xulplanet.com/references/xpcomref/ifaces/nsIPrefBranch.html <tt>nsIPrefBranch</tt>] and [http://xulplanet.com/references/xpcomref/ifaces/nsIPrefService.html <tt>nsIPrefService</tt>]
**Most used interfaces (these are frozen and will not change): [http://xulplanet.com/references/xpcomref/ifaces/nsIPrefBranch.html <code>nsIPrefBranch</code>] and [http://xulplanet.com/references/xpcomref/ifaces/nsIPrefService.html <code>nsIPrefService</code>]
*[http://lxr.mozilla.org/mozilla/source/modules/libpref/ LXR pages for <tt>libpref</tt>], the module implementing preferences system.
*[http://lxr.mozilla.org/mozilla/source/modules/libpref/ LXR pages for <code>libpref</code>], the module implementing preferences system.


[[Category:Development|Using preferences]] [[Category:Example code|Using preferences]]
[[Category:Example code|Using preferences]] [[Category:XPCOM example code|Using preferences]]

Revision as of 00:14, 6 March 2005

This page is part of the extension development documentation project.

Ask your questions in MozillaZine Forums. Also try browsing example code.

Note: development documentation is in process of being moved to Mozilla Development Center (MDC).

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.

If you haven't yet, read other documents about Mozilla preferences on XUL Planet and on mozilla.org (links below in Resources section).

XPCOM interfaces for preferences system

Mozilla exposes its preferences system through a few XPCOM interfaces. Look in the Resources section below for the link to list of preferences-related interfaces.

Three most used interfaces are nsIPrefService, nsIPrefBranch and nsIPrefBranchInternal. First two are frozen and will not change, last one is “internal”, but is very useful because it allows you to set up prefs observers — see below for an example.

It's worth noting that there also is an nsIPref 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 Creating XPCOM Components document at XUL Planet for details.) To get an nsIPrefBranch, either QI the pref service (that will give you the root branch) or call nsIPrefService.getBranch() to get a sub-branch.

Here are two examples:

// Get the root branch
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                    getService(Components.interfaces.nsIPrefBranch);
// Get the "extensions.myext." branch
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                    getService(Components.interfaces.nsIPrefService);
prefs = prefs.getBranch("extensions.myext.");

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 nsIPrefBranch that read and write them: getBoolPref, setBoolPref, getCharPref, setCharPref, getIntPref and setIntPref. Using them is as easy as:

// 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

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 nsILocalFile and nsISupportsString objects in preferences (as strings — note that from the preferences system's POV, complex values have a nsIPrefBranch.PREF_STRING type.)

There are two nsIPrefBranch methods implementing the concept — setComplexValue and getComplexValue. You can look up their implementation in nsPrefBranch.cpp. Here is the IDL definition:

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);

As you can see, both of them take aType parameter. It can have the following values (to be precise, you should pass Components.interfaces.nsIWhatever instead of just nsIWhatever, which is undefined):

  • nsISupportsString — used to handle unicode strings in preferences. Use this when the preference value may contain non-ASCII characters (for example, user's name).
  • nsIPrefLocalizedString — almost same as nsISupportsString, but it is handled differently in getComplexValue when there's no user value for given preference, see below for details.
  • nsILocalFile and nsIRelativeFilePref — store paths in preferences. nsILocalFile is used to store absolute paths, while nsIRelativeFilePref is used to store paths relative to one of “special” directories, like the profile folder.

nsISupportsString

As noted above, this is used to handle unicode strings in preferences. Example:

// 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);

nsIPrefLocalizedString

Another complex type supported by Mozilla is nsIPrefLocalizedString. It is similar to nsISupportsString, the only difference is that when there is no user value, getComplexValue 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 extensions.myext.welcomemessage preference localizable. You should do the following:

  1. Add this line to some .properties file (for all of your locales), say to chrome://myext/locale/defaults.properties:
    extensions.myext.welcomemessage=Localized default value
  2. Add the default value for extensions.myext.welcomemessage, pointing to that properties file, by adding the following line to your file with default preferences (see below).
    pref("extensions.myext.welcomemessage", "chrome://myext/locale/defaults.properties");
  3. Read the preference with getComplexValue, passing nsIPrefLocalizedString as aType:
    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;
    

The code in step 3 will read the default value from chrome://myext/locale/defaults.properties when no user value is set, and will behave exactly the same as if nsISupportsString was passed as aType otherwise.

nsILocalFile and nsIRelativeFilePref

Please see the File IO article for details on nsILocalFile and nsIRelativeFilePref.

Default preferences

Each preference may have up to two values — current and default. That means there are two “pref trees” — current and default, — 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.

What effect do default preferences have on various get methods

When one of get methods of nsIPrefBranch (assuming it's a branch of the tree with current values) is called, it does the following:

  1. Checks whether the “current” tree has a value for the pref and whether the pref is locked.
  2. If there's a value of correct type (f.e. getBoolValue expects a value of type nsIPrefBranch.PREF_BOOL), and the preference is not locked, the method returns that value.
  3. If there's a value of incorrect type and the pref is not locked, an exception is thrown (NS_ERROR_UNEXPECTED)
  4. If the preference is locked or if there is no value for that preference in “current” tree, the get method checks the default tree.
  5. If there's a value of expected type in the “default” tree, it is returned (with the only exception, getComplexValue with aType parameter = nsIPrefLocalizedString, described above)
  6. Otherwise an exception is thrown (NS_ERROR_UNEXPECTED).

If the branch is from the “default” tree, the get method doesn't check the tree with current values at all.

(This is not exactly how it's coded in libpref, but it's equivalent)

Where are the default values read from

  • All Mozilla-based applications read (application directory)/defaults/pref/*.js .
  • In addition to that, the recent versions of new toolkit applications (Firefox 1.0, Thunderbird 1.0 and the like; not Mozilla Suite) read files listed in (profile folder)/defaults.ini — usually (profile folder)/extensions/{guid}/defaults/preferences/

How to install extension's defaults files

For Mozilla, copy them to (appdir)/defaults/pref in your install script.

For Firefox/Thunderbird, just put them in myext.xpi/defaults/preferences/. They will be copied and registered in defaults.ini automatically.

Using preferences observers

You can use nsIPrefBranchInternal interface to “listen” to changes to preferences in a certain branch. Here's an example (note, this code hasn't actually been tested, so it may contain typos and other errors):

var myPrefObserver =
{
  register: function()
  {
    var prefService = Components.classes["@mozilla.org/preferences-service;1"].
      getService(Components.interfaces.nsIPrefService);
    this._branch = prefService.getBranch("extensions.myextension.");

    var pbi = this._branch.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
    pbi.addObserver("", this, false);
  },

  unregister: function()
  {
    if(!this._branch) return;

    var pbi = this._branch.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
    pbi.removeObserver("", this);
  },

  observe: function(aSubject, aTopic, aData)
  {
    if(aTopic != "nsPref:changed") return;
    // aSubject is the nsIPrefBranch we're observing
    switch (aData) {
      case "pref1":
        // extensions.myextension.pref1 was changed
        break;
      case "pref2":
        // extensions.myextension.pref2 was changed
        break;
    }
  }
}
myPrefListener.register();

Resources