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