Compare Revisions

Preferences

Revision 44011:

Revision 44011 by Db48x on

Revision 44012:

Revision 44012 by Db48x on

Title:
Preferences
Preferences
Slug:
Code_snippets/Preferences
Code_snippets/Preferences
Tags:
Extensions, Add-ons, "Code snippets"
Extensions, Add-ons, "Code snippets"
Content:

Revision 44011
Revision 44012
nn7    <p>
8      This article provides examples for extension developers tha
 >t wish to use the Mozilla preferences system. Information here ap
 >plies to the Mozilla Suite, Firefox, Thunderbird, and possibly ot
 >her Mozilla-based applications. For more details on preferences i
 >n Mozilla, see <a href="en/Preferences_System">Preferences System
 ></a>.
9    </p>
10    <p>
11      If you haven't yet, read other documents about Mozilla pref
 >erences on XULPlanet and on mozilla.org (links below in <a href="
 >#Resources">Resources section</a>).
12    </p>
13    <div class="note">
14      <b>Note:</b> This article doesn't cover all available metho
 >ds for manipulating preferences; please refer to the XULPlanet XP
 >COM reference pages listed in <a href="#Resources">Resources sect
 >ion</a> for the complete list of methods. The interfaces dealing 
 >with preferences are fairly well documented, so using the methods
 > not mentioned here should be straightforward.
15    </div>
16    <h3 name="XPCOM_interfaces_for_preferences_system">
17      XPCOM interfaces for preferences system
18    </h3>
19    <p>
20      Mozilla exposes its preferences system through a few XPCOM 
 >interfaces. Look in the <a href="#Resources">Resources section</a
 >> below for the link to list of preferences-related interfaces.
21    </p>
22    <p>
23      Three used interfaces are <code>nsIPrefService</code>, <cod
 >e>nsIPrefBranch</code> and <code>nsIPrefBranch2</code>. They are 
 >frozen and will not change.
24    </p>
25    <p>
26      It's worth noting that there also is an <code>nsIPref</code
 >> interface. Despite it being used in some places, it is <b>depre
 >cated</b> and should not be used.
27    </p>
28    <p>
29      The preferences service is instantiated in the same way you
 > instantiate any XPCOM service; see the article <a class="externa
 >l" href="http://xulplanet.com/references/xpcomref/creatingcomps.h
 >tml">Creating XPCOM Components</a> at XULPlanet for details.) To 
 >get an <code>nsIPrefBranch</code>, either <code>QueryInterface()<
 >/code> the pref service (that will give you the root branch) or c
 >all <code>nsIPrefService.getBranch()</code> to get a sub-branch.
30    </p>
31    <p>
32      Here are two examples:
33    </p>
34    <pre>
35// Get the root branch
36var prefs = Components.classes["@mozilla.org/preferences-service;
 >1"].
37                    getService(Components.interfaces.nsIPrefBranc
 >h);
38</pre>
39    <pre>
40// Get the "extensions.myext." branch
41var prefs = Components.classes["@mozilla.org/preferences-service;
 >1"].
42                    getService(Components.interfaces.nsIPrefServi
 >ce);
43prefs = prefs.getBranch("extensions.myext.");
44</pre>
45    <h3 name="Simple_types">
46      Simple types
47    </h3>
48    <p>
49      There are three types of preferences: <b>string</b>, <b>int
 >eger</b> and <b>boolean</b>. Each entry in the preferences databa
 >se (<tt>prefs.js</tt>) has one of those types. There are six meth
 >ods in <code>nsIPrefBranch</code> that read and write preferences
 >: <code>getBoolPref()</code>, <code>setBoolPref()</code>, <code>g
 >etCharPref()</code>, <code>setCharPref()</code>, <code>getIntPref
 >()</code> and <code>setIntPref()</code>. Using them is as easy as
 >:
50    </p>
51    <pre>
52// prefs is an nsIPrefBranch.
53// Look in the above section for examples of getting one.
54var value = prefs.getBoolPref("accessibility.typeaheadfind"); // 
 >get a pref
55prefs.setBoolPref("accessibility.typeaheadfind", !value); // set 
 >a pref
56</pre>
57    <h3 name="Complex_types">
58      Complex types
59    </h3>
60    <p>
61      As noted in previous section, each entry in the preferences
 > database (<tt>prefs.js</tt>) must have a string, an integer, or 
 >a boolean value. However, there is a concept of <b>complex types<
 >/b>, which makes it easier for developers to save and load <code>
 >nsILocalFile</code> and <code>nsISupportsString</code> objects in
 > preferences (as strings — note that from the preferences system'
 >s point of view, complex values have a <code>nsIPrefBranch.PREF_S
 >TRING</code> type.)
62    </p>
63    <p>
64      There are two <code>nsIPrefBranch</code> methods implementi
 >ng the concept — <code>setComplexValue()</code> and <code>getComp
 >lexValue()</code>. You can look up their implementations in {{tem
 >plate.Source("modules/libpref/src/nsPrefBranch.cpp#228", "nsPrefB
 >ranch.cpp")}}. Here are the IDL definitions:
65    </p>
66    <pre class="eval">
67void getComplexValue(in string aPrefName, in nsIIDRef aType, 
68                     [iid_is(aType), retval] out nsQIResult aValu
 >e);
69void setComplexValue(in string aPrefName, in nsIIDRef aType, in n
 >sISupports aValue);
70</pre>
71    <p>
72      As you can see, both of them take a parameter, <code>aType<
 >/code>, which can have one of the following values <small>(to be 
 >precise, you should pass <code>Components.interfaces.nsIWhatever<
 >/code> instead of just <code>nsIWhatever</code>, which is undefin
 >ed):</small>
73    </p>
74    <dl>
75      <dt>
76        <code><a href="#nsISupportsString">nsISupportsString</a><
 >/code>
77      </dt>
78      <dd>
79        Used to handle Unicode strings in preferences. Use this w
 >hen the preference value may contain non-ASCII characters (for ex
 >ample, a user's name).
80      </dd>
81      <dt>
82        <code><a href="#nsIPrefLocalizedString">nsIPrefLocalizedS
 >tring</a></code>
83      </dt>
84      <dd>
85        Almost the same as <code>nsISupportsString</code>, but it
 > is handled differently in <code>getComplexValue()</code> when th
 >ere's no user value for the given preference; see below for detai
 >ls.
86      </dd>
87      <dt>
88        <a href="en/Code_snippets/File_I%2f%2fO"><code>nsILocalFi
 >le</code> and <code>nsIRelativeFilePref</code></a>
89      </dt>
90      <dd>
91        Store paths in preferences. <code>nsILocalFile</code> is 
 >used to store absolute paths, while <code>nsIRelativeFilePref</co
 >de> is used to store paths relative to a "special" directory, suc
 >h as the profile folder.
92      </dd>
93    </dl>
94    <h4 name="nsISupportsString">
95      nsISupportsString
96    </h4>
97    <p>
98      As noted above, this is used to handle Unicode strings in p
 >references. Example:
99    </p>
100    <pre>
101// prefs is an nsIPrefBranch
102 
103// Example 1: getting Unicode value
104var value = prefs.getComplexValue("preference.with.non.ascii.valu
 >e",
105      Components.interfaces.nsISupportsString).data;
106 
107// Example 2: setting Unicode value
108var str = Components.classes["@mozilla.org/supports-string;1"]
109      .createInstance(Components.interfaces.nsISupportsString);
110str.data = "some non-ascii text";
111prefs.setComplexValue("preference.with.non.ascii.value", 
112      Components.interfaces.nsISupportsString, str);
113</pre>
114    <h4 name="nsIPrefLocalizedString">
115      nsIPrefLocalizedString
116    </h4>
117    <p>
118      Another complex type supported by Mozilla is <code>nsIPrefL
 >ocalizedString</code>. It is similar to <code>nsISupportsString</
 >code>, except that when there is no user value, <code>getComplexV
 >alue()</code> gets the default value from a locale file (thus mak
 >ing the default value localizable).
119    </p>
120    <p>
121      It's easier to explain this by example. Let's say you want 
 >to make the default value for <code>extensions.myext.welcomemessa
 >ge</code> preference localizable. You should do the following:
122    </p>
123    <ol>
124      <li>Add this line to some <code>.properties</code> file (fo
 >r all of your locales), say to <code>chrome://myext/locale/defaul
 >ts.properties</code>:
125        <pre>
126extensions.myext.welcomemessage=Localized default value
127</pre>
128      </li>
129      <li>Add the default value for <code>extensions.myext.welcom
 >emessage</code>, pointing to that properties file, by adding the 
 >following line to your file with <i>default preferences</i> (see 
 >below).
130        <pre>
131pref("extensions.myext.welcomemessage", "chrome://myext/locale/de
 >faults.properties");
132</pre>
133      </li>
134      <li>Read the preference with <code>getComplexValue</code>, 
 >passing <code>nsIPrefLocalizedString</code> as <code>aType</code>
 >:
135        <pre>
136var prefs = Components.classes["@mozilla.org/preferences-service;
 >1"].
137      getService(Components.interfaces.nsIPrefService);
138var branch = prefs.getBranch("extensions.myext.");
139var value = branch.getComplexValue("welcomemessage",
140      Components.interfaces.nsIPrefLocalizedString).data;
141</pre>
142      </li>
143    </ol>
144    <p>
145      The code in step 3 will read the default value from <tt>chr
 >ome://myext/locale/defaults.properties</tt> when no user value is
 > set, and will behave exactly the same as if <code>nsISupportsStr
 >ing</code> was passed as <code>aType</code> otherwise.
146    </p>
147    <p>
148      <br>
149      Setting <code>nsIPrefLocalizedString</code> preferences is 
 >similar to setting <code>nsISupportsString</code>:
150    </p>
151    <pre class="eval">
152var pls = Components.classes["@mozilla.org/pref-localizedstring;1
 >"]
153                    .createInstance(Components.interfaces.nsIPref
 >LocalizedString);
154pls.data = val;
155prefs.setComplexValue("preference.with.non.ascii.value", 
156                      Components.interfaces.nsIPrefLocalizedStrin
 >g, pls);
157</pre>
158    <h4 name="nsILocalFile_and_nsIRelativeFilePref">
159      nsILocalFile and nsIRelativeFilePref
160    </h4>
161    <p>
162      <span class="comment">Leave this section to have nice TOC</
 >span> Please see the <a href="en/Code_snippets/File_I%2f%2fO">Fil
 >e IO article</a> for details on <code>nsILocalFile</code> and <co
 >de>nsIRelativeFilePref</code>.
163    </p>
164    <h3 name="Default_preferences">
165      Default preferences
166    </h3>
167    <p>
168      <span class="comment">someone should reword this section</s
 >pan> Each preference may have up to two values — the <b>current</
 >b> value and the <b>default</b> value. That means there are two "
 >pref trees:" current and default, and each of them may or may not
 > have a value for the preference in question.
169    </p>
170    <p>
171      You can see the list of preferences in <a class="external" 
 >href="http://kb.mozillazine.org/About:config">about:config</a> (w
 >here available). Preferences that have a user value are bold, tho
 >se that don't have a user value are printed in normal font.
172    </p>
173    <p>
174      You can get both trees using the <code>nsIPrefService.getBr
 >anch()</code> and <code>nsIPrefService.getDefaultBranch()</code> 
 >functions. See below for details.
175    </p>
176    <h4 name="The_effect_of_default_preferences_on_get_methods">
177      The effect of default preferences on <code>get</code> metho
 >ds
178    </h4>
179    <p>
180      When one of the <code>get</code> methods of <code>nsIPrefBr
 >anch</code> (assuming it's a branch of the tree with current valu
 >es) is called, it does the following:
181    </p>
182    <ol>
183      <li>Checks whether the <b>current</b> tree has a value for 
 >the preference and whether or not the preference is locked.
184      </li>
185      <li>If there's a value of the correct type (for example, <c
 >ode>getBoolValue()</code> expects a value of type <code>nsIPrefBr
 >anch.PREF_BOOL</code>), and the preference is not locked, the met
 >hod returns that value.
186      </li>
187      <li>If there's a value of the wrong type and the preference
 > is not locked, an <code>NS_ERROR_UNEXPECTED</code> exception is 
 >thrown.
188      </li>
189      <li>If the preference is locked or if there is no value for
 > that preference in the <b>current</b> tree, the <code>get</code>
 > method checks the default tree.
190      </li>
191      <li>If there's a value of the expected type in the <b>defau
 >lt</b> tree, it is returned (with the only exception being that c
 >alling <code>getComplexValue()</code> with <code>aType</code> par
 >ameter specified as <code>nsIPrefLocalizedString</code>, <a href=
 >"#nsIPrefLocalizedString">described above</a>).
192      </li>
193      <li>Otherwise an <code>NS_ERROR_UNEXPECTED</code> exception
 > is thrown.
194      </li>
195    </ol>
196    <p>
197      If the branch is from the <b>default</b> tree, the <code>ge
 >t</code> method doesn't check the tree with current values at all
 >.
198    </p>
199    <p>
200      <small>(This is not exactly how it's coded in <code>libpref
 ></code>, but it's equivalent)</small>
201    </p>
202    <h4 name="Where_the_default_values_are_read_from">
203      Where the default values are read from
204    </h4>
205    <ul>
206      <li>All Mozilla-based applications read <tt>(application di
 >rectory)/defaults/pref/*.js</tt> <span class="comment">(xxx are n
 >on-.js files read?)</span>.
207      </li>
208      <li>In addition to that, recent versions of Toolkit applica
 >tions (Firefox 1.0, Thunderbird 1.0 and the like but <b>not</b> t
 >he Mozilla Suite) read extension defaults -- usually located in u
 >sually <code>(profile folder)/extensions/(ID)/defaults/preference
 >s/</code>
209      </li>
210    </ul>
211    <p>
212      These files use simple JavaScript-like syntax. To add a def
 >ault value for a preference, you should add a line like this to y
 >our default preferences file:
213    </p>
214    <pre class="eval">
215pref("extensions.extensionname.preferencename", false);
216</pre>
217    <h4 name="How_to_install_an_extension.27s_defaults_files">
218      How to install an extension's defaults files
219    </h4>
220    <p>
221      For Mozilla Suite (not Firefox and Thunderbird), copy them 
 >to <code>(appdir)/defaults/pref</code> in your <a href="en/Instal
 >l.js">install script</a>.
222    </p>
223    <p>
224      For Firefox/Thunderbird, just put them in <code>myext.xpi/d
 >efaults/preferences/</code>. They will be copied and registered w
 >ith the preferences system automatically.
225    </p>
226    <h3 name="More_about_preferences_.22branches.22">
227      More about preferences "branches"
228    </h3>
229    <p>
230      Preference names consist of a few strings separated with do
 >ts, and related preferences usually share the same prefix. For ex
 >ample, most accessibility preferences in Mozilla start with "acce
 >ssibility."
231    </p>
232    <p>
233      This means that all existing preferences can be imagined as
 > if they were in a tree, like this:
234    </p>
235    <pre class="eval">
236+
237|
238+-- accessibility
239|         |
240|         +-- typeaheadfind
241|         |         |
242|         |         +-- autostart (<i>accessibility.typeaheadfind
 >.autostart</i>)
243|         |         |
244|         |         +-- enablesound (<i>accessibility.typeaheadfi
 >nd.enablesound</i>)
245|         |
246|         +-- usebrailledisplay (<i>accessibility.usebrailledispl
 >ay</i>)
247|
248+-- extensions
249          |
250          +-- lastAppVersion (<i>extensions.lastAppVersion</i>)
251</pre>
252    <p>
253      This is the metaphor behind <code>nsIPref<b>Branch</b></cod
 >e>. However, you should be aware of the fact that the Mozilla pre
 >ferences system doesn't treat dots in a special way. For example 
 >this code will also read the value of <code><i>accessibility.type
 >aheadfind.enablesound</i></code> preference:
254    </p>
255    <pre class="eval">
256var prefs = Components.classes["@mozilla.org/preferences-service;
 >1"].
257                    getService(Components.interfaces.nsIPrefServi
 >ce);
258var branch = prefs.getBranch("acce");
259var enablesound = branch.getBoolPref("ssibility.typeaheadfind.ena
 >blesound");
260</pre>
261    <p>
262      This is the reason why you should usually pass strings endi
 >ng with a dot to <code>getBranch()</code>, like <code>prefs.getBr
 >anch("accessibility<b>.</b>")</code>.
263    </p>
264    <p>
265      Another caveat you should be aware of is that <code>nsIPref
 >Branch.getChildList("",{})</code> returns an array of preference 
 >names that start with that branch's <code>root</code>, for exampl
 >e
266    </p>
267    <pre class="eval">
268var branch = prefs.getBranch("accessibility.");
269var children = branch.getChildList("", {});
270</pre>
271    <p>
272      will return these items (for the example tree above): <code
 >>"typeaheadfind.autostart", "typeaheadfind.enablesound", and "use
 >brailledisplay"</code>, not just direct children (<code>"typeahea
 >dfind"</code> and <code>"usebrailledisplay"</code>), as you might
 > have expected.
273    </p>
n55      Here's the code I like to use, as it makes for much better n322      <a class="external" href="https://bugzilla.mozilla.org/show
>code reuse both within a project (because you can easily create m>_bug.cgi?id=281414">{{template.Source("modules/libpref/public/nsI
>ultiple listeners without duplicating the code) and across projec>PrefBranch2.idl", "nsIPrefBranch2.idl")}} has more documentation.
>ts (I put it in a common include file.)> For a working example, see the StockWatcher2 tutorial "Adding pr
 >eferences to an extension", linked below in the</a> <a href="#Res
 >ources">Resources section</a>.
323    </p>
324    <h3 name="Using_prefHasUserValue.28.29">
325      Using <code>prefHasUserValue()</code>
326    </h3>
56    </p>327    <p>
328      <code>nsIPrefBranch.prefHasUserValue(<i>preference</i>)</co
 >de> checks whether the preference has been changed from the defau
 >lt value. If so, it will return <code>true</code>, otherwise <cod
 >e>false</code>. In particular, when no default value exists for a
 > preference, <code>prefHasUserValue()</code> indicates whether a 
 >preference exists.
329    </p>
57    <pre>330    <p>
58function prefListener(branchName, func)331      Attempting to read nonexistent preference using one of <cod
 >e>get*Pref</code> methods will throw an exception. Using <code>pr
 >efHasUserValue()</code> lets you check if the preference exists b
 >efore attempting to read it. For example:
59{332    </p>
60    var prefService = Components.classes["@mozilla.org/preference333    <pre class="eval">
>s-service;1"] 
61                                .getService(Components.interfaces334if(prefs.prefHasUserValue("mypref")) {
>.nsIPrefService); 
62    var branch = prefService.getBranch(branchName);335  alert(prefs.getCharPref("mypref"));
63    branch.QueryInterface(Components.interfaces.nsIPrefBranch2);
64 
65    this.register = function()
66    {
67        branch.addObserver("", this, false);
68        branch.getChildList("", { })
69              .forEach(function (name) { func(branch, name); });
70    };
71 
72    this.unregister = function unregister()
73    {
74        if (branch)
75            branch.removeObserver("", this);
76    };
77 
78    this.observe = function(subject, topic, data)
79    {
80        if (topic == "nsPref:changed")
81            func(branch, data);
82    };
n84 n
85var myListener = new prefListener("extensions.myextension",
86                                  function(branch, name)
87                                  {
88                                      switch (name) 
89                                      {
90                                          case "pref1":
91                                              // extensions.myext
>ension.pref1 was changed 
92                                              break;
93                                          case "pref2":
94                                              // extensions.myext
>ension.pref2 was changed 
95                                              break;
96                                      }
97                                  });
t99    <p>t
100      <a class="external" href="https://bugzilla.mozilla.org/show
>_bug.cgi?id=281414">{{template.Source("modules/libpref/public/nsI 
>PrefBranch2.idl", "nsIPrefBranch2.idl")}} has more documentation. 
> For a working example, see the StockWatcher2 tutorial "Adding pr 
>eferences to an extension", linked below in the</a> <a href="#Res 
>ources">Resources section</a>. 
101    </p>338    <p>
339      Note that the <code>getCharPref()</code> call may throw eve
 >n if the preference exists, for example if it has a different typ
 >e.
340    </p>
341    <h3 name="Using_preferences_in_extensions">
342      Using preferences in extensions
343    </h3>
344    <p>
345      If you're writing your extension for one of the Toolkit app
 >lications (Firefox, Thunderbird, Nvu), you should provide default
 > values for your extension's preferences (see above for informati
 >on on how to do it). It has the following benefits:
346    </p>
347    <ul>
348      <li>You don't have to duplicate default values in various p
 >arts of your code.
349      </li>
350      <li>The code for reading preferences is simplified, since y
 >ou don't need to worry about the <code>get</code> methods throwin
 >g exceptions.
351      </li>
352    </ul>
353    <h3 name="JavaScript_wrappers_for_preferences_system">
354      JavaScript wrappers for preferences system
355    </h3>
356    <p>
357      There are a few JavaScript wrappers to make your life easie
 >r, such as the one found at http://mozilla.doslash.org/prefutils 
 >and the <code>nsPreferences</code> wrapper included with Firefox 
 >and Thunderbird (<tt>chrome://global/content/nsUserSettings.js</t
 >t>).
358    </p>
359    <h3 name="How_to_Save_Preferences">
360      How to Save Preferences
361    </h3>
362    <p>
363      Here is the secret recipe for saving preferences into the d
 >efault location:
364    </p>
365    <pre class="eval">
366var prefService = Components.classes["@mozilla.org/preferences-se
 >rvice;1"]
367                               .getService(Components.interfaces.
 >nsIPrefService);
368prefService.savePrefFile(null);
369</pre>
370    <h3 name="Resources">
371      Resources
372    </h3>
373    <ul>
374      <li>Other documentation on preferences
375        <ul>
376          <li>
377            <a href="en/Preferences_API">Preferences API</a>
378          </li>
379          <li>
380            <a class="external" href="http://www.mozilla.org/cata
 >log/end-user/customizing/briefprefs.html">A Brief Guide to Mozill
 >a Preferences</a> — describes preferences system from user's / ad
 >ministrator's POV.
381          </li>
382          <li>
383            <a class="external" href="http://www.xulplanet.com/tu
 >torials/xulqa/q_prefs.html">XUL Planet's article on preferences</
 >a> — an explanation of the preferences system with simple example
 >s. A must read.
384          </li>
385        </ul>
386      </li>
387      <li>Mozilla XPCOM interfaces of the preferences system.
388        <ul>
389          <li>
390            <a class="external" href="http://xulplanet.com/refere
 >nces/xpcomref/group_Preferences.html#Preferences">Complete list</
 >a>
391          </li>
392          <li>Most used interfaces (these are frozen and will not
 > change): <code><a class="external" href="http://xulplanet.com/re
 >ferences/xpcomref/ifaces/nsIPrefBranch.html">nsIPrefBranch</a></c
 >ode> and <code><a class="external" href="http://xulplanet.com/ref
 >erences/xpcomref/ifaces/nsIPrefService.html">nsIPrefService</a></
 >code>
393          </li>
394          <li>
395            <code><a class="external" href="http://xulplanet.com/
 >references/xpcomref/ifaces/nsIPrefBranch2.html">nsIPrefBranch2</a
 >></code> interface (before Gecko 1.8 it was called <code>nsIPrefB
 >ranchInternal</code>).
396          </li>
397        </ul>
398      </li>
399      <li>
400        <a href="en/Preferences_System">Preferences System</a> - 
 >an easy way to create a XUL Options window for your extension or 
 >application.
401      </li>
402      <li>{{template.Source("modules/libpref/", "LXR pages for li
 >bpref")}}, the source code module that implements the preferences
 > system.
403      </li>
404      <li>A JavaScript wrapper for preferences API: http://mozill
 >a.doslash.org/prefutils
405      </li>
406      <li>
407        <a class="external" href="http://developer.mozilla.org/en
 >/docs/Adding_preferences_to_an_extension">Adding preferences to a
 >n extension</a> — a simple tutorial with a working extension that
 > illustrates the use of preference observers
408      </li>
409    </ul>
410    <div class="noinclude"></div>{{ wiki.languages( { "fr": "fr/E
 >xtraits_de_code/Pr\u00e9f\u00e9rences", "ja": "ja/Code_snippets/P
 >references", "pl": "pl/Fragmenty_kodu/Preferencje" } ) }}

Back to History