1# libpref
2libpref is a generic key/value store that is used to implement *prefs*, a term
3that encompasses a variety of things.
4
5- Feature enable/disable flags (e.g. `dom.IntersectionObserver.enabled`,
6  `xpinstall.signatures.required`).
7- User preferences (e.g. things set from `about:preferences`)
8- Internal application parameters (e.g.
9  `javascript.options.mem.nursery.max_kb`).
10- Testing and debugging flags (e.g. `network.dns.native-is-localhost`).
11- Things that might need locking in an enterprise installation.
12- Application data (e.g.
13  `browser.onboarding.tour.onboarding-tour-addons.completed`,
14  `services.sync.clients.lastSync`).
15- A cheap and dirty form of IPC(!) (some devtools prefs).
16
17Some of these (particularly the last two) are not an ideal use of libpref.
18
19The C++ API is in the `Preferences` class. The XPIDL API is in the
20`nsIPrefService` and `nsIPrefBranch` interfaces.
21
22## High-level design
23
24### Keys
25Keys (a.k.a. *pref names*) are 8-bit strings, and ASCII in practice. The
26convention is to use a dotted segmented form, e.g. `foo.bar.baz`, but the
27segments have no built-in meaning.
28
29Naming is inconsistent, e.g. segments have various forms: `foo_bar`,
30`foo-bar`, `fooBar`, etc. Pref names for feature flags are likewise
31inconsistent: `foo.enabled`, `foo.enable`, `foo.disable`, `fooEnabled`,
32`enable-foo`, `foo.enabled.bar`, etc.
33
34The grouping of prefs into families, via pref name segments, is ad hoc. Some of
35these families are closely related, e.g. there are many font prefs that are
36present for every language script.
37
38Some prefs only make sense when considered in combination with other prefs.
39
40Many pref names are known at compile time, but some are computed at runtime.
41
42### Basic values
43The basic types of pref values are bools, 32-bit ints, and 8-bit C strings.
44
45Strings are used to encode many types of data: identifiers, alphanumeric IDs,
46UUIDs, SHA1 hashes, CSS color hex values, large integers that don't fit into
4732-bit ints (e.g. timestamps), directory names, URLs, comma-separated lists,
48space-separated lists, JSON blobs, etc. There is a 1 MiB length limit on string
49values; longer strings are rejected outright.
50
51**Problem:** The C string encoding is unclear; some API functions deal with
52unrestricted 8-bit strings (i.e. Latin1), but some require UTF-8.
53
54There is some API support for faking floats, by converting them from/to strings when getting/setting.
55
56**Problem:** confusion between ints and floats can lead to bugs.
57
58Each pref consists of a default value and/or a user value. Default values can
59be initialized from file at startup, and can be added and modified at runtime
60via the API. User values can be initialized from file at startup, and can be
61added, modified and removed at runtime via the API and `about:config`.
62
63If both values are present the user value takes precedence for most operations,
64though there are operations that specifically work on the default value.
65
66If a user value is set to the same value as the default value, the user value
67is removed, unless the pref is marked as *sticky* at startup.
68
69**Problem:** it would be better to have a clear notion of "reset to default",
70at least for prefs that have a default value.
71
72Prefs can be locked. This prevents them from being given a user value, or
73hides the existing user value if there is one.
74
75### Complex values
76There is API support for some complex values.
77
78`nsIFile` objects are handled by storing the filename as a string, similar to
79how floats are faked by storing them as strings.
80
81`nsIPrefLocalizedString` objects are ones for which the default value
82specifies a properties file that contains an entry whose name matches the
83prefname. When gotten, the value from that entry is put into the user value.
84When set, the given value just overwrites the user value, like a string pref.
85
86**Problem:** this is weird and unlike all the other pref types.
87
88`nsIRelativeFilePref` objects are only used in comm-central.
89
90### Pref Branches
91XPIDL-based access to prefs is via `nsIPrefBranch`/`nsPrefBranch`, which
92lets you specify a branch of the pref tree (e.g. `font.`) and pref names work
93relative to that point.
94
95This API can be used from C++, but for C++ code there is also direct access
96through the `Preferences` class, which uses absolute pref names.
97
98### Threads
99For the most part, all the basic API functions only work on the main thread.
100However, there are two exceptions to this.
101
102The narrow exception is that the Servo traversal thread is allowed to get pref
103values. This only occurs when the main thread is paused, which makes it safe.
104(Note: [bug 1474789](https://bugzilla.mozilla.org/show_bug.cgi?id=1474789)
105indicates that this may not be true.)
106
107The broad exception is that static prefs can have a cached copy of a pref value
108that can be accessed from other threads. See below.
109
110### Notifications
111There is a notification API for being told when a pref's value changes. C++
112code can register a callback function and JS code can register an observer (via
113`nsIObserver`, which requires XPCOM). In both cases, the registered entity
114will be notified when the value of the named pref value changes, or when the
115value of any pref matching a given prefix changes. E.g. all font pref changes
116can be observed by adding a `font.` prefix-matching observer.
117
118See also the section on static prefs below.
119
120### Static prefs
121There is a special kind of pref called a static pref. Static prefs are defined
122in `StaticPrefList.yaml`.
123
124If a static pref is defined in both `StaticPrefList.yaml` and a pref data
125file, the latter definition will take precedence. A pref shouldn't appear in
126both `StaticPrefList.yaml` and `all.js`, but it may make sense for a pref
127to appear in both `StaticPrefList.yaml` and an app-specific pref data file
128such as `firefox.js`.
129
130Each static pref has a *mirror* kind.
131
132* `always`: A C++ *mirror variable* is associated with the pref. The variable
133  is always kept in sync with the pref value. This kind is common.
134* `once`: A C++ mirror variable is associated with the pref. The variable is
135  synced once with the pref's value at startup, and then does not change. This
136  kind is less common, and mostly used for graphics prefs.
137* `never`: No C++ mirror variable is associated with the pref. This is much
138  like a normal pref.
139
140An `always` or `once` static pref can only be used for prefs with
141bool/int/float values, not strings or complex values.
142
143Each mirror variable is read-only, accessible via a getter function.
144
145Mirror variables have two benefits. First, they allow C++ and Rust code to get
146the pref value directly from the variable instead of requiring a slow hash
147table lookup, which is important for prefs that are consulted frequently.
148Second, they allow C++ and Rust code to get the pref value off the main thread.
149The mirror variable must have an atomic type if it is read off the main thread,
150and assertions ensure this.
151
152Note that mirror variables could be implemented via vanilla callbacks without
153API support, except for one detail: libpref gives their callbacks higher
154priority than normal callbacks, ensuring that any static pref will be
155up-to-date if read by a normal callback.
156
157**Problem:** It is not clear what should happen to a static pref's mirror
158variable if the pref is deleted? Currently there is a missing
159`NotifyCallbacks()` call so the mirror variable keeps its value from before
160the deletion. The cleanest solution is probably to disallow static prefs from
161being deleted.
162
163### Loading and Saving
164Default pref values are initialized from various pref data files. Notable ones
165include:
166
167- `modules/libpref/init/all.js`, used by all products;
168- `browser/app/profile/firefox.js`, used by Firefox desktop;
169- `mobile/android/app/mobile.js`, used by Firefox mobile;
170- `mail/app/profile/all-thunderbird.js`, used by Thunderbird (in comm-central);
171- `suite/browser/browser-prefs.js`, used by SeaMonkey (in comm-central).
172
173In release builds these are all put into `omni.ja`.
174
175User pref values are initialized from `prefs.js` and (if present)
176`user.js`, in the user's profile. This only happens once, in the parent
177process. Note that `prefs.js` is managed by Firefox, and regularly
178overwritten. `user.js` is created and managed by the user, and Firefox only
179reads it.
180
181These files are not JavaScript; the `.js` suffix is present for historical
182reasons. They are read by a custom parser within libpref.
183
184User pref file syntax is slightly more restrictive than default pref file
185syntax. In user pref files `user_pref` definitions are allowed but `pref` and
186`sticky_pref` definitions are not, and attributes (such as `locked`) are not
187allowed.
188
189**Problem:** geckodriver has a separate prefs parser in the mozprofile crate.
190
191**Problem:** there is no versioning of these files, for either the syntax or
192the data. This makes changing the file format difficult.
193
194There are API functions to save modified prefs, either synchronously or
195asynchronously (via an off-main-thread runnable), either to the default file
196(`prefs.js`) or to a named file. When saving to the default file, no action
197will take place if no prefs have been modified.
198
199Also, whenever a pref is modified, we wait 500ms and then automatically do an
200off-main-thread save to `prefs.js`. This provides an approximation of
201[durability](https://en.wikipedia.org/wiki/ACID#Durability), but it is still
202possible for something to go wrong (e.g. a parent process crash) and end up
203with recently changed prefs not being saved. (If such a thing happens, it
204compromises [atomicity](https://en.wikipedia.org/wiki/ACID#Atomicity), i.e. a
205sequence of multiple related pref changes might only get partially written.)
206
207Only prefs whose values have changed from the default are saved to `prefs.js.`
208
209**Problem:** Each time prefs are saved, the entire file is overwritten -- 10s
210or even 100s of KiBs -- even if only a single value has changed. This happens
211at least every 5 minutes, due to sync. Furthermore, various prefs are changed
212during and shortly after startup, which can result in 10s of MiBs of disk
213activity.
214
215### about:support
216about:support contains an "Important Modified Preferences" table. It contains
217all prefs that (a) have had their value changed from the default, and (b) whose
218prefix match a whitelist in `Troubleshoot.jsm`. The whitelist matching is to
219avoid exposing pref values that might be privacy-sensitive.
220
221**Problem:** The whitelist of prefixes is specified separately from the prefs
222themselves. Having an attribute on a pref definition would be better.
223
224### Sync
225On desktop, a pref is synced onto a device via Sync if there is an
226accompanying `services.sync.prefs.sync.`-prefixed pref. I.e. the pref
227`foo.bar` is synced if the pref `services.sync.prefs.sync.foo.bar` exists
228and is true.
229
230Previously, one could push prefs onto a device even if a local
231`services.sync.prefs.sync.`-prefixed pref was not present; however this
232behavior changed in [bug 1538015](https://bugzilla.mozilla.org/show_bug.cgi?id=1538015)
233to require the local prefixed pref to be present. The old (insecure) behavior
234can be re-enabled by setting a single pref `services.sync.prefs.dangerously_allow_arbitrary`
235to true on the target browser - subsequently any pref can be pushed there by
236creating a *remote* `services.sync.prefs.sync.`-prefixed pref.
237
238In practice, only a small subset of prefs (about 70) have a `services.sync.prefs.sync.`-prefixed
239pref by default.
240
241**Problem:** This is gross. An attribute on the pref definition would be
242better, but it might be hard to change that at this point.
243
244The number of synced prefs is small because prefs are synced across versions;
245any pref whose meaning might change shouldn't be synced. Also, we don't sync
246prefs that may differ across different devices (such as a desktop machine
247vs. a notebook).
248
249Prefs are not synced on mobile.
250
251### Rust
252Static prefs mirror variables can be accessed from Rust code via the
253`static_prefs::pref!` macro. Other prefs currently cannot be accessed. Parts
254of libpref's C++ API could be made accessible to Rust code fairly
255straightforwardly via C bindings, either hand-made or generated.
256
257### Cost of a pref
258The cost of a single pref is low, but the cost of several thousand prefs is
259reasonably high, and includes the following.
260
261- Parsing and initializing at startup.
262- IPC costs at startup and on pref value changes.
263- Disk writing costs of pref value changes, especially during startup.
264- Memory usage for storing the prefs, callbacks and observers, and C++ mirror
265  variables.
266- Complexity: most pref combinations are untested. Some can be set to a bogus
267  value by a curious user, which can have [serious effects](https://rejzor.wordpress.com/2015/06/14/improve-firefox-html5-video-playback-performance/)
268  (read the comments). Prefs can also have bugs. Real-life examples include
269  mistyped prefnames, `all.js` entries with incorrect types (e.g. confusing
270  int vs. float), both of which mean changing the pref value via about:config
271  or the API would have no effect (see [bug 1414150](https://bugzilla.mozilla.org/show_bug.cgi?id=1414150) for examples of
272  both).
273- Sync cost, for synced prefs.
274
275### Guidelines
276We have far too many prefs. This is at least partly because we have had, for a
277long time, a culture of "when in doubt, add a pref". Also, we don't have any
278system — either technical or cultural — for removing unnecessary prefs. See
279[bug 90440] (https://bugzilla.mozilla.org/show_bug.cgi?id=90440) for a pref
280that was unused for 17 years.
281
282In short, prefs are Firefox's equivalent of the Windows Registry: a dumping
283ground for anything and everything. We should have guidelines for when to add a
284pref.
285
286Here are some good reasons to add a pref.
287
288- *A user may genuinely want to change it.* E.g. it controls a feature that is
289  adjustable in about:preferences.
290- *To enable/disable new features.* Once a feature is mature, consider removing
291  the pref. A pref expiry mechanism would help with this.
292- *For certain testing/debugging flags.* Ideally, these would not be visible in
293  about:config.
294
295Here are some less good reasons to add a pref.
296
297- *I'm not confident about this numeric parameter (cache size, timeout, etc.)*
298  Get confident! In practice, few if any users will change it. Adding a pref
299  doesn't absolve you of the responsibility of finding a good default. Then
300  make it a code constant.
301- *I need to experiment with different parameters during development.* This is
302  reasonable, but consider removing the pref before landing or once the feature
303  has matured. An expiry mechanism would help with this.
304- *I sometimes fiddle with this value for debugging or testing.*
305  Is it worth exposing it to the whole world to save yourself a recompile every
306  once in a while? Consider making it a code constant.
307- *Different values are needed on different platforms.* This can be done in
308  other ways, e.g. `#ifdef` in C++ code.
309
310These guidelines do not consider application data prefs (i.e. ones that
311typically don't have a default value). They are quite different from the other
312kinds. They arguably shouldn't prefs at all, and should be stored via some
313other mechanism.
314
315## Low-level details
316The key idea is that the prefs database consists of two pieces. The first is an
317initial snapshot of pref values that is created when the first child process is
318created. This snapshot is stored in immutable, shared memory, and shared by all
319processes.
320
321Pref value changes that occur after this point are stored in a second hash
322table. Each process has its own copy of this hash table. When pref values
323change in the parent process, it performs IPC to inform child processes about
324the changes, so they can update their copy.
325
326The motivation for this design is memory usage. It's not tenable for every
327child process to have a full copy of the prefs database.
328
329Not all child processes need access to prefs. Those that do include web content
330processes, the GPU process, and the RDD process.
331
332### Parent process startup
333The parent process initially has only a hash table.
334
335Early in startup, the parent process loads all of the static prefs and default
336prefs (mainly from `omni.ja`) into that hash table. The parent process also
337registers C++ mirror variables for static prefs, initializes them, and
338registers callbacks so they will be updated appropriately for all subsequent
339updates.
340
341Slightly later in startup, the parent process loads all user prefs files,
342mainly from the profile directory.
343
344When the first getter for a `once` static pref is called, all the `once`
345static prefs have their mirror variables set and special frozen prefs are put
346into the hash table. These frozen prefs are copies of the `once` prefs that
347are given `$$$` prefixes and suffixes on their names. They are also marked
348specially so they are ignored for all cases except when starting a new child
349process. They exist so that all child processes can be given the same `once`
350values as the parent process.
351
352### Child process startup (parent side)
353When the first child process is created, the parent process serializes its hash
354table into a shared, immutable snapshot. This snapshot is stored in a shared
355memory region managed by a `SharedPrefMap` instance. The parent process then
356clears the hash table. The hash table is subsequently used only to store
357changed pref values.
358
359When any child process is created, the parent process serializes all pref
360values present in the hash table (i.e. those that have changed since the
361snapshot was made) and stores them in a second, short-lived shared memory
362region. This represents the set of changes the child process needs to apply on
363top of the snapshot, and allows it to build a hash table which should exactly
364match the parent's.
365
366The parent process passes two file descriptors to the child process, one for
367each region of memory. The snapshot is the same for all child processes.
368
369### Child process startup (child side)
370Early in child process startup, the prefs service maps in and deserializes both
371shared memory regions sent from the parent process, but defers further
372initialization until requested by XPCOM initialization. Once that happens,
373mirror variables are initialized for static prefs, but no default values are
374set in the hash table, and no prefs files are loaded.
375
376Once the mirror variables have been initialized, we dispatch pref change
377callbacks for any prefs in the shared snapshot which have user values or are
378locked. This causes the mirror variables to be updated.
379
380After that, the changed pref values received from the parent process (via
381`changedPrefsFd`) are added to the prefs database. Their values override the
382values in the snapshot, and pref change callbacks are dispatched for them as
383appropriate. `once` mirror variable are initialized from the special frozen
384pref values.
385
386### Pref lookups
387Each prefs database has both a hash table and a shared memory snapshot. A given
388pref may have an entry in either or both of these. If a pref exists in both,
389the hash table entry takes precedence.
390
391For pref lookups, the hash table is checked first, followed by the shared
392snapshot. The entry in the hash table may have the type `None`, in which case
393the pref is treated as if it did not exist. The entry in the static snapshot
394never has the type `None`.
395
396For pref enumeration, both maps are enumerated, starting with the hash table.
397While iterating over the hash table, any entry with the type `None` is
398skipped. While iterating over the shared snapshot, any entry which also exists
399in the hash table is skipped. The combined result of the two iterations
400represents the full contents of the prefs database.
401
402### Pref changes
403Pref changes can only be initiated in the parent process. All API methods that
404modify prefs fail noisily (with `NS_ERROR`) if run outside the parent
405process.
406
407Pref changes that happen before the initial snapshot have been made are simple,
408and take place in the hash table. There is no shared snapshot to update, and no
409child processes to synchronize with.
410
411Once a snapshot has been created, any changes need to happen in the hash table.
412
413If an entry for a changed pref already exists in the hash table, that entry can
414be updated directly. Likewise for prefs that do not exist in either the hash
415table or the shared snapshot: a new hash table entry can be created.
416
417More care is needed when a changed pref exists in the snapshot but not in the
418hash table. In that case, we create a hash table entry with the same values as
419the snapshot entry, and then update it... but *only* if the changes will have
420an effect. If a caller attempts to set a pref to its existing value, we do not
421want to waste memory creating an unnecessary hash table entry.
422
423Content processes must be told about any visible pref value changes. (A change
424to a default value that is hidden by a user value is unimportant.) When this
425happens, `ContentParent` detects the change (via an observer).  It checks the
426pref name against a small blacklist of prefixes that child processes should not
427care about (this is an optimization to reduce IPC rather than a
428capabilities/security consideration), and for string prefs it also checks the
429value(s) don't exceed 4 KiB. If the checks pass, it sends an IPC message
430(`PreferenceUpdate`) to the child process, and the child process updates
431the pref (default and user value) accordingly.
432
433**Problem:** The blacklist of prefixes is specified separately from the prefs
434themselves. Having an attribute on a pref definition would be better.
435
436**Problem:** The 4 KiB limit can lead to inconsistencies between the parent
437process and child processes. E.g. see
438[bug 1303051](https://bugzilla.mozilla.org/show_bug.cgi?id=1303051#c28).
439
440### Pref deletions
441Pref deletion is more complicated. If a pref to be deleted exists only in the
442hash table of the parent process, its entry can simply be removed. If it exists
443in the shared snapshot, however, its hash table entry needs to be kept (or
444created), and its type changed to `None`. The presence of this entry masks
445the snapshot entry, causing it to be ignored by pref enumerators.
446