summaryrefslogtreecommitdiff
path: root/components/preferences
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2021-11-19 08:23:01 -0500
committerMatt A. Tobin <email@mattatobin.com>2021-11-19 08:23:01 -0500
commit410d228eaa229067de8bb3a359e0ca80a4dd6066 (patch)
treea5b29d0e6df6da569f6c84a8b155405714f4597e /components/preferences
parent0caddc34f86c28aeeafb1d967872eca008db1974 (diff)
downloadaura-central-410d228eaa229067de8bb3a359e0ca80a4dd6066.tar.gz
Issue %3005 - Move modules/libpref to components/preferences
- greprefs is now in system/
Diffstat (limited to 'components/preferences')
-rw-r--r--components/preferences/moz.build26
-rw-r--r--components/preferences/public/nsIPrefBranch.idl425
-rw-r--r--components/preferences/public/nsIPrefBranch2.idl16
-rw-r--r--components/preferences/public/nsIPrefBranchInternal.idl17
-rw-r--r--components/preferences/public/nsIPrefLocalizedString.idl64
-rw-r--r--components/preferences/public/nsIPrefService.idl160
-rw-r--r--components/preferences/public/nsIRelativeFilePref.idl69
-rw-r--r--components/preferences/src/Preferences.cpp1988
-rw-r--r--components/preferences/src/Preferences.h408
-rw-r--r--components/preferences/src/nsPrefBranch.cpp943
-rw-r--r--components/preferences/src/nsPrefBranch.h269
-rw-r--r--components/preferences/src/nsPrefsFactory.cpp54
-rw-r--r--components/preferences/src/prefapi.cpp1005
-rw-r--r--components/preferences/src/prefapi.h258
-rw-r--r--components/preferences/src/prefapi_private_data.h40
-rw-r--r--components/preferences/src/prefread.cpp657
-rw-r--r--components/preferences/src/prefread.h118
17 files changed, 6517 insertions, 0 deletions
diff --git a/components/preferences/moz.build b/components/preferences/moz.build
index 635fa39c9..4bd147384 100644
--- a/components/preferences/moz.build
+++ b/components/preferences/moz.build
@@ -3,4 +3,30 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+include('/ipc/chromium/chromium-config.mozbuild')
+
+DEFINES['OS_ARCH'] = CONFIG['OS_ARCH']
+DEFINES['MOZ_WIDGET_TOOLKIT'] = CONFIG['MOZ_WIDGET_TOOLKIT']
+
+XPIDL_SOURCES += [
+ 'public/nsIPrefBranch.idl',
+ 'public/nsIPrefBranch2.idl',
+ 'public/nsIPrefBranchInternal.idl',
+ 'public/nsIPrefLocalizedString.idl',
+ 'public/nsIPrefService.idl',
+ 'public/nsIRelativeFilePref.idl',
+]
+
+EXPORTS.mozilla += ['src/Preferences.h']
+
+SOURCES += [
+ 'src/nsPrefBranch.cpp',
+ 'src/nsPrefsFactory.cpp',
+ 'src/prefapi.cpp',
+ 'src/Preferences.cpp',
+ 'src/prefread.cpp',
+]
+
+XPIDL_MODULE = 'pref'
+FINAL_LIBRARY = 'xul'
JAR_MANIFESTS += ['jar.mn'] \ No newline at end of file
diff --git a/components/preferences/public/nsIPrefBranch.idl b/components/preferences/public/nsIPrefBranch.idl
new file mode 100644
index 000000000..900806b42
--- /dev/null
+++ b/components/preferences/public/nsIPrefBranch.idl
@@ -0,0 +1,425 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIObserver;
+
+/**
+ * The nsIPrefBranch interface is used to manipulate the preferences data. This
+ * object may be obtained from the preferences service (nsIPrefService) and
+ * used to get and set default and/or user preferences across the application.
+ *
+ * This object is created with a "root" value which describes the base point in
+ * the preferences "tree" from which this "branch" stems. Preferences are
+ * accessed off of this root by using just the final portion of the preference.
+ * For example, if this object is created with the root "browser.startup.",
+ * the preferences "browser.startup.page", "browser.startup.homepage",
+ * and "browser.startup.homepage_override" can be accessed by simply passing
+ * "page", "homepage", or "homepage_override" to the various Get/Set methods.
+ *
+ * @see nsIPrefService
+ */
+
+[scriptable, uuid(55d25e49-793f-4727-a69f-de8b15f4b985)]
+interface nsIPrefBranch : nsISupports
+{
+
+ /**
+ * Values describing the basic preference types.
+ *
+ * @see getPrefType
+ */
+ const long PREF_INVALID = 0;
+ const long PREF_STRING = 32;
+ const long PREF_INT = 64;
+ const long PREF_BOOL = 128;
+
+ /**
+ * Called to get the root on which this branch is based, such as
+ * "browser.startup."
+ */
+ readonly attribute string root;
+
+ /**
+ * Called to determine the type of a specific preference.
+ *
+ * @param aPrefName The preference to get the type of.
+ *
+ * @return long A value representing the type of the preference. This
+ * value will be PREF_STRING, PREF_INT, or PREF_BOOL.
+ */
+ long getPrefType(in string aPrefName);
+
+ /**
+ * Called to get the state of an individual boolean preference.
+ *
+ * @param aPrefName The boolean preference to get the state of.
+ * @param aDefaultValue The value to return if the preference is not set.
+ *
+ * @return boolean The value of the requested boolean preference.
+ *
+ * @see setBoolPref
+ */
+ [optional_argc,binaryname(GetBoolPrefWithDefault)]
+ boolean getBoolPref(in string aPrefName, [optional] in boolean aDefaultValue);
+ [noscript,binaryname(GetBoolPref)]
+ boolean getBoolPrefXPCOM(in string aPrefName);
+
+ /**
+ * Called to set the state of an individual boolean preference.
+ *
+ * @param aPrefName The boolean preference to set the state of.
+ * @param aValue The boolean value to set the preference to.
+ *
+ * @throws Error if setting failed or the preference has a default
+ value of a type other than boolean.
+ *
+ * @see getBoolPref
+ */
+ void setBoolPref(in string aPrefName, in boolean aValue);
+
+ /**
+ * Called to get the state of an individual floating-point preference.
+ * "Floating point" preferences are really string preferences that
+ * are converted to floating point numbers.
+ *
+ * @param aPrefName The floating point preference to get the state of.
+ * @param aDefaultValue The value to return if the preference is not set.
+ *
+ * @return float The value of the requested floating point preference.
+ *
+ * @see setCharPref
+ */
+ [optional_argc,binaryname(GetFloatPrefWithDefault)]
+ float getFloatPref(in string aPrefName, [optional] in float aDefaultValue);
+ [noscript,binaryname(GetFloatPref)]
+ float getFloatPrefXPCOM(in string aPrefName);
+
+ /**
+ * Called to get the state of an individual string preference.
+ *
+ * @param aPrefName The string preference to retrieve.
+ * @param aDefaultValue The string to return if the preference is not set.
+ *
+ * @return string The value of the requested string preference.
+ *
+ * @see setCharPref
+ */
+ [optional_argc,binaryname(GetCharPrefWithDefault)]
+ string getCharPref(in string aPrefName, [optional] in string aDefaultValue);
+ [noscript,binaryname(GetCharPref)]
+ string getCharPrefXPCOM(in string aPrefName);
+
+ /**
+ * Called to set the state of an individual string preference.
+ *
+ * @param aPrefName The string preference to set.
+ * @param aValue The string value to set the preference to.
+ *
+ * @throws Error if setting failed or the preference has a default
+ value of a type other than string.
+ *
+ * @see getCharPref
+ */
+ void setCharPref(in string aPrefName, in string aValue);
+
+ /**
+ * Called to get the state of an individual integer preference.
+ *
+ * @param aPrefName The integer preference to get the value of.
+ * @param aDefaultValue The value to return if the preference is not set.
+ *
+ * @return long The value of the requested integer preference.
+ *
+ * @see setIntPref
+ */
+ [optional_argc,binaryname(GetIntPrefWithDefault)]
+ long getIntPref(in string aPrefName, [optional] in long aDefaultValue);
+ [noscript,binaryname(GetIntPref)]
+ long getIntPrefXPCOM(in string aPrefName);
+
+ /**
+ * Called to set the state of an individual integer preference.
+ *
+ * @param aPrefName The integer preference to set the value of.
+ * @param aValue The integer value to set the preference to.
+ *
+ * @throws Error if setting failed or the preference has a default
+ value of a type other than integer.
+ *
+ * @see getIntPref
+ */
+ void setIntPref(in string aPrefName, in long aValue);
+
+ /**
+ * Called to get the state of an individual complex preference. A complex
+ * preference is a preference which represents an XPCOM object that can not
+ * be easily represented using a standard boolean, integer or string value.
+ *
+ * @param aPrefName The complex preference to get the value of.
+ * @param aType The XPCOM interface that this complex preference
+ * represents. Interfaces currently supported are:
+ * - nsIFile
+ * - nsISupportsString (UniChar)
+ * - nsIPrefLocalizedString (Localized UniChar)
+ * @param aValue The XPCOM object into which to the complex preference
+ * value should be retrieved.
+ *
+ * @throws Error The value does not exist or is the wrong type.
+ *
+ * @see setComplexValue
+ */
+ void getComplexValue(in string aPrefName, in nsIIDRef aType,
+ [iid_is(aType), retval] out nsQIResult aValue);
+
+ /**
+ * Called to set the state of an individual complex preference. A complex
+ * preference is a preference which represents an XPCOM object that can not
+ * be easily represented using a standard boolean, integer or string value.
+ *
+ * @param aPrefName The complex preference to set the value of.
+ * @param aType The XPCOM interface that this complex preference
+ * represents. Interfaces currently supported are:
+ * - nsIFile
+ * - nsISupportsString (UniChar)
+ * - nsIPrefLocalizedString (Localized UniChar)
+ * @param aValue The XPCOM object from which to set the complex preference
+ * value.
+ *
+ * @throws Error if setting failed or the value is the wrong type.
+ *
+ * @see getComplexValue
+ */
+ void setComplexValue(in string aPrefName, in nsIIDRef aType, in nsISupports aValue);
+
+ /**
+ * Called to clear a user set value from a specific preference. This will, in
+ * effect, reset the value to the default value. If no default value exists
+ * the preference will cease to exist.
+ *
+ * @param aPrefName The preference to be cleared.
+ *
+ * @note
+ * This method does nothing if this object is a default branch.
+ */
+ void clearUserPref(in string aPrefName);
+
+ /**
+ * Called to lock a specific preference. Locking a preference will cause the
+ * preference service to always return the default value regardless of
+ * whether there is a user set value or not.
+ *
+ * @param aPrefName The preference to be locked.
+ *
+ * @note
+ * This method can be called on either a default or user branch but, in
+ * effect, always operates on the default branch.
+ *
+ * @throws Error The preference does not exist or an error occurred.
+ *
+ * @see unlockPref
+ */
+ void lockPref(in string aPrefName);
+
+ /**
+ * Called to check if a specific preference has a user value associated to
+ * it.
+ *
+ * @param aPrefName The preference to be tested.
+ *
+ * @note
+ * This method can be called on either a default or user branch but, in
+ * effect, always operates on the user branch.
+ *
+ * @note
+ * If a preference was manually set to a value that equals the default value,
+ * then the preference no longer has a user set value, i.e. it is
+ * considered reset to its default value.
+ * In particular, this method will return false for such a preference and
+ * the preference will not be saved to a file by nsIPrefService.savePrefFile.
+ *
+ * @return boolean true The preference has a user set value.
+ * false The preference only has a default value.
+ */
+ boolean prefHasUserValue(in string aPrefName);
+
+ /**
+ * Called to check if a specific preference is locked. If a preference is
+ * locked calling its Get method will always return the default value.
+ *
+ * @param aPrefName The preference to be tested.
+ *
+ * @note
+ * This method can be called on either a default or user branch but, in
+ * effect, always operates on the default branch.
+ *
+ * @return boolean true The preference is locked.
+ * false The preference is not locked.
+ *
+ * @see lockPref
+ * @see unlockPref
+ */
+ boolean prefIsLocked(in string aPrefName);
+
+ /**
+ * Called to unlock a specific preference. Unlocking a previously locked
+ * preference allows the preference service to once again return the user set
+ * value of the preference.
+ *
+ * @param aPrefName The preference to be unlocked.
+ *
+ * @note
+ * This method can be called on either a default or user branch but, in
+ * effect, always operates on the default branch.
+ *
+ * @throws Error The preference does not exist or an error occurred.
+ *
+ * @see lockPref
+ */
+ void unlockPref(in string aPrefName);
+
+
+ /**
+ * Called to remove all of the preferences referenced by this branch.
+ *
+ * @param aStartingAt The point on the branch at which to start the deleting
+ * preferences. Pass in "" to remove all preferences
+ * referenced by this branch.
+ *
+ * @note
+ * This method can be called on either a default or user branch but, in
+ * effect, always operates on both.
+ *
+ * @throws Error The preference(s) do not exist or an error occurred.
+ */
+ void deleteBranch(in string aStartingAt);
+
+ /**
+ * Returns an array of strings representing the child preferences of the
+ * root of this branch.
+ *
+ * @param aStartingAt The point on the branch at which to start enumerating
+ * the child preferences. Pass in "" to enumerate all
+ * preferences referenced by this branch.
+ * @param aCount Receives the number of elements in the array.
+ * @param aChildArray Receives the array of child preferences.
+ *
+ * @note
+ * This method can be called on either a default or user branch but, in
+ * effect, always operates on both.
+ *
+ * @throws Error The preference(s) do not exist or an error occurred.
+ */
+ void getChildList(in string aStartingAt,
+ [optional] out unsigned long aCount,
+ [array, size_is(aCount), retval] out string aChildArray);
+
+ /**
+ * Called to reset all of the preferences referenced by this branch to their
+ * default values.
+ *
+ * @param aStartingAt The point on the branch at which to start the resetting
+ * preferences to their default values. Pass in "" to
+ * reset all preferences referenced by this branch.
+ *
+ * @note
+ * This method can be called on either a default or user branch but, in
+ * effect, always operates on the user branch.
+ *
+ * @throws Error The preference(s) do not exist or an error occurred.
+ */
+ void resetBranch(in string aStartingAt);
+
+ /**
+ * Add a preference change observer. On preference changes, the following
+ * arguments will be passed to the nsIObserver.observe() method:
+ * aSubject - The nsIPrefBranch object (this)
+ * aTopic - The string defined by NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
+ * aData - The name of the preference which has changed, relative to
+ * the |root| of the aSubject branch.
+ *
+ * aSubject.get*Pref(aData) will get the new value of the modified
+ * preference. For example, if your observer is registered with
+ * addObserver("bar.", ...) on a branch with root "foo.", modifying
+ * the preference "foo.bar.baz" will trigger the observer, and aData
+ * parameter will be "bar.baz".
+ *
+ * @param aDomain The preference on which to listen for changes. This can
+ * be the name of an entire branch to observe.
+ * e.g. Holding the "root" prefbranch and calling
+ * addObserver("foo.bar.", ...) will observe changes to
+ * foo.bar.baz and foo.bar.bzip
+ * @param aObserver The object to be notified if the preference changes.
+ * @param aHoldWeak true Hold a weak reference to |aObserver|. The object
+ * must implement the nsISupportsWeakReference
+ * interface or this will fail.
+ * false Hold a strong reference to |aObserver|.
+ *
+ * @note
+ * Registering as a preference observer can open an object to potential
+ * cyclical references which will cause memory leaks. These cycles generally
+ * occur because an object both registers itself as an observer (causing the
+ * branch to hold a reference to the observer) and holds a reference to the
+ * branch object for the purpose of getting/setting preference values. There
+ * are 3 approaches which have been implemented in an attempt to avoid these
+ * situations.
+ * 1) The nsPrefBranch object supports nsISupportsWeakReference. Any consumer
+ * may hold a weak reference to it instead of a strong one.
+ * 2) The nsPrefBranch object listens for xpcom-shutdown and frees all of the
+ * objects currently in its observer list. This ensures that long lived
+ * objects (services for example) will be freed correctly.
+ * 3) The observer can request to be held as a weak reference when it is
+ * registered. This insures that shorter lived objects (say one tied to an
+ * open window) will not fall into the cyclical reference trap.
+ *
+ * @note
+ * The list of registered observers may be changed during the dispatch of
+ * nsPref:changed notification. However, the observers are not guaranteed
+ * to be notified in any particular order, so you can't be sure whether the
+ * added/removed observer will be called during the notification when it
+ * is added/removed.
+ *
+ * @note
+ * It is possible to change preferences during the notification.
+ *
+ * @note
+ * It is not safe to change observers during this callback in Gecko
+ * releases before 1.9. If you want a safe way to remove a pref observer,
+ * please use an nsITimer.
+ *
+ * @see nsIObserver
+ * @see removeObserver
+ */
+ void addObserver(in string aDomain, in nsIObserver aObserver,
+ in boolean aHoldWeak);
+
+ /**
+ * Remove a preference change observer.
+ *
+ * @param aDomain The preference which is being observed for changes.
+ * @param aObserver An observer previously registered with addObserver().
+ *
+ * @note
+ * Note that you must call removeObserver() on the same nsIPrefBranch
+ * instance on which you called addObserver() in order to remove aObserver;
+ * otherwise, the observer will not be removed.
+ *
+ * @see nsIObserver
+ * @see addObserver
+ */
+ void removeObserver(in string aDomain, in nsIObserver aObserver);
+};
+
+
+%{C++
+
+#define NS_PREFBRANCH_CONTRACTID "@mozilla.org/preferencesbranch;1"
+/**
+ * Notification sent when a preference changes.
+ */
+#define NS_PREFBRANCH_PREFCHANGE_TOPIC_ID "nsPref:changed"
+
+%}
diff --git a/components/preferences/public/nsIPrefBranch2.idl b/components/preferences/public/nsIPrefBranch2.idl
new file mode 100644
index 000000000..f1087c92d
--- /dev/null
+++ b/components/preferences/public/nsIPrefBranch2.idl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIPrefBranch.idl"
+
+/**
+ * An empty interface to provide backwards compatibility for existing code.
+ *
+ * @see nsIPrefBranch
+ */
+[scriptable, uuid(8892016d-07f7-4530-b5c1-d73dfcde4a1c)]
+interface nsIPrefBranch2 : nsIPrefBranch
+{
+};
diff --git a/components/preferences/public/nsIPrefBranchInternal.idl b/components/preferences/public/nsIPrefBranchInternal.idl
new file mode 100644
index 000000000..476e6a59f
--- /dev/null
+++ b/components/preferences/public/nsIPrefBranchInternal.idl
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIPrefBranch2.idl"
+
+/**
+ * An empty interface to provide backwards compatibility for existing code that
+ * bsmedberg didn't want to break all at once. Don't use me!
+ *
+ * @status NON-FROZEN interface WHICH WILL PROBABLY GO AWAY.
+ */
+
+[scriptable, uuid(355bd1e9-248a-438b-809d-e0db1b287882)]
+interface nsIPrefBranchInternal : nsIPrefBranch2
+{
+};
diff --git a/components/preferences/public/nsIPrefLocalizedString.idl b/components/preferences/public/nsIPrefLocalizedString.idl
new file mode 100644
index 000000000..f99d60f6f
--- /dev/null
+++ b/components/preferences/public/nsIPrefLocalizedString.idl
@@ -0,0 +1,64 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * The nsIPrefLocalizedString interface is simply a wrapper interface for
+ * nsISupportsString so the preferences service can have a unique identifier
+ * to distinguish between requests for normal wide strings (nsISupportsString)
+ * and "localized" wide strings, which get their default values from properites
+ * files.
+ *
+ * @see nsIPrefBranch
+ * @see nsISupportsString
+ */
+
+[scriptable, uuid(ae419e24-1dd1-11b2-b39a-d3e5e7073802)]
+interface nsIPrefLocalizedString : nsISupports
+{
+ /**
+ * Provides access to string data stored in this property.
+ *
+ * @throws Error An error occurred.
+ */
+ attribute wstring data;
+
+ /**
+ * Used to retrieve the contents of this object into a wide string.
+ *
+ * @return wstring The string containing the data stored within this object.
+ */
+ wstring toString();
+
+ /**
+ * Used to set the contents of this object.
+ *
+ * @param length The length of the string. This value should not include
+ * space for the null terminator, nor should it account for the
+ * size of a character. It should only be the number of
+ * characters for which there is space in the string.
+ * @param data The string data to be stored.
+ *
+ * @note
+ * This makes a copy of the string argument passed in.
+ */
+ void setDataWithLength(in unsigned long length,
+ [size_is(length)] in wstring data);
+};
+
+%{C++
+
+#define NS_PREFLOCALIZEDSTRING_CID \
+ { /* {064d9cee-1dd2-11b2-83e3-d25ab0193c26} */ \
+ 0x064d9cee, \
+ 0x1dd2, \
+ 0x11b2, \
+ { 0x83, 0xe3, 0xd2, 0x5a, 0xb0, 0x19, 0x3c, 0x26 } \
+ }
+
+#define NS_PREFLOCALIZEDSTRING_CONTRACTID "@mozilla.org/pref-localizedstring;1"
+
+%}
diff --git a/components/preferences/public/nsIPrefService.idl b/components/preferences/public/nsIPrefService.idl
new file mode 100644
index 000000000..0db401996
--- /dev/null
+++ b/components/preferences/public/nsIPrefService.idl
@@ -0,0 +1,160 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIPrefBranch.idl"
+
+%{C++
+struct PrefTuple;
+#include "nsTArrayForwardDeclare.h"
+%}
+
+[ptr] native nsPreferencesArrayPtr(nsTArray<PrefTuple>);
+[ptr] native nsPreferencePtr(PrefTuple);
+[ptr] native nsPreferencePtrConst(const PrefTuple);
+
+interface nsIFile;
+
+/**
+ * The nsIPrefService interface is the main entry point into the back end
+ * preferences management library. The preference service is directly
+ * responsible for the management of the preferences files and also facilitates
+ * access to the preference branch object which allows the direct manipulation
+ * of the preferences themselves.
+ *
+ * @see nsIPrefBranch
+ */
+
+[scriptable, uuid(1f84fd56-3956-40df-b86a-1ea01402ee96)]
+interface nsIPrefService : nsISupports
+{
+ /**
+ * Called to read in the preferences specified in a user preference file.
+ *
+ * @param aFile The file to be read.
+ *
+ * @note
+ * If nullptr is passed in for the aFile parameter the default preferences
+ * file(s) [prefs.js, user.js] will be read and processed.
+ *
+ * @throws Error File failed to read or contained invalid data.
+ *
+ * @see savePrefFile
+ * @see nsIFile
+ */
+ void readUserPrefs(in nsIFile aFile);
+
+ /**
+ * Called to completely flush and re-initialize the preferences system.
+ *
+ * @throws Error The preference service failed to restart correctly.
+ */
+ void resetPrefs();
+
+ /**
+ * Called to reset all preferences with user set values back to the
+ * application default values.
+ */
+ void resetUserPrefs();
+
+ /**
+ * Called to write current preferences state to a file.
+ *
+ * @param aFile The file to be written.
+ *
+ * @note
+ * If nullptr is passed in for the aFile parameter the preference data is
+ * written out to the current preferences file (usually prefs.js.)
+ *
+ * @throws Error File failed to write.
+ *
+ * @see readUserPrefs
+ * @see nsIFile
+ */
+ void savePrefFile(in nsIFile aFile);
+
+ /**
+ * Call to get a Preferences "Branch" which accesses user preference data.
+ * Using a Set method on this object will always create or set a user
+ * preference value. When using a Get method a user set value will be
+ * returned if one exists, otherwise a default value will be returned.
+ *
+ * @param aPrefRoot The preference "root" on which to base this "branch".
+ * For example, if the root "browser.startup." is used, the
+ * branch will be able to easily access the preferences
+ * "browser.startup.page", "browser.startup.homepage", or
+ * "browser.startup.homepage_override" by simply requesting
+ * "page", "homepage", or "homepage_override". nullptr or ""
+ * may be used to access to the entire preference "tree".
+ *
+ * @return nsIPrefBranch The object representing the requested branch.
+ *
+ * @see getDefaultBranch
+ */
+ nsIPrefBranch getBranch(in string aPrefRoot);
+
+ /**
+ * Call to get a Preferences "Branch" which accesses only the default
+ * preference data. Using a Set method on this object will always create or
+ * set a default preference value. When using a Get method a default value
+ * will always be returned.
+ *
+ * @param aPrefRoot The preference "root" on which to base this "branch".
+ * For example, if the root "browser.startup." is used, the
+ * branch will be able to easily access the preferences
+ * "browser.startup.page", "browser.startup.homepage", or
+ * "browser.startup.homepage_override" by simply requesting
+ * "page", "homepage", or "homepage_override". nullptr or ""
+ * may be used to access to the entire preference "tree".
+ *
+ * @note
+ * Few consumers will want to create default branch objects. Many of the
+ * branch methods do nothing on a default branch because the operations only
+ * make sense when applied to user set preferences.
+ *
+ * @return nsIPrefBranch The object representing the requested default branch.
+ *
+ * @see getBranch
+ */
+ nsIPrefBranch getDefaultBranch(in string aPrefRoot);
+
+ /**
+ * The preference service is 'dirty' if there are changes to user preferences
+ * that have not been written to disk
+ */
+ readonly attribute boolean dirty;
+};
+
+%{C++
+
+#define NS_PREFSERVICE_CID \
+ { /* {1cd91b88-1dd2-11b2-92e1-ed22ed298000} */ \
+ 0x91ca2441, \
+ 0x050f, \
+ 0x4f7c, \
+ { 0x9d, 0xf8, 0x75, 0xb4, 0x0e, 0xa4, 0x01, 0x56 } \
+ }
+
+#define NS_PREFSERVICE_CONTRACTID "@mozilla.org/preferences-service;1"
+
+/**
+ * Notification sent before reading the default user preferences files.
+ */
+#define NS_PREFSERVICE_READ_TOPIC_ID "prefservice:before-read-userprefs"
+
+/**
+ * Notification sent when resetPrefs has been called, but before the actual
+ * reset process occurs.
+ */
+#define NS_PREFSERVICE_RESET_TOPIC_ID "prefservice:before-reset"
+
+/**
+ * Notification sent when after reading app-provided default
+ * preferences, but before user profile override defaults or extension
+ * defaults are loaded.
+ */
+#define NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID "prefservice:after-app-defaults"
+
+%}
diff --git a/components/preferences/public/nsIRelativeFilePref.idl b/components/preferences/public/nsIRelativeFilePref.idl
new file mode 100644
index 000000000..4b86e3755
--- /dev/null
+++ b/components/preferences/public/nsIRelativeFilePref.idl
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+interface nsIFile;
+
+/**
+ * The nsIRelativeFilePref interface is a wrapper for an nsIFile and
+ * and a directory service key. When used as a pref value, it stores a
+ * relative path to the file from the location pointed to by the directory
+ * service key. The path has the same syntax across all platforms.
+ *
+ * @see nsIPrefBranch::getComplexValue
+ * @see nsIPrefBranch::setComplexValue
+ *
+ */
+
+[scriptable, uuid(2f977d4e-5485-11d4-87e2-0010a4e75ef2)]
+interface nsIRelativeFilePref : nsISupports
+{
+ /**
+ * file
+ *
+ * The file whose location is stored or retrieved.
+ */
+ attribute nsIFile file;
+
+ /**
+ * relativeToKey
+ *
+ * A directory service key for the directory
+ * from which the file path is relative.
+ */
+ attribute ACString relativeToKey;
+
+};
+
+%{C++
+
+#define NS_RELATIVEFILEPREF_CID \
+ { /* {2f977d4f-5485-11d4-87e2-0010a4e75ef2} */ \
+ 0x2f977d4f, \
+ 0x5485, \
+ 0x11d4, \
+ { 0x87, 0xe2, 0x00, 0x10, 0xa4, 0xe7, 0x5e, 0xf2 } \
+ }
+
+#define NS_RELATIVEFILEPREF_CONTRACTID "@mozilla.org/pref-relativefile;1"
+
+#include "nsComponentManagerUtils.h"
+
+inline nsresult
+NS_NewRelativeFilePref(nsIFile* aFile, const nsACString& relativeToKey, nsIRelativeFilePref** result)
+{
+ nsresult rv;
+ nsCOMPtr<nsIRelativeFilePref> local(do_CreateInstance(NS_RELATIVEFILEPREF_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) return rv;
+
+ (void)local->SetFile(aFile);
+ (void)local->SetRelativeToKey(relativeToKey);
+
+ *result = local;
+ NS_ADDREF(*result);
+ return NS_OK;
+}
+
+%}
diff --git a/components/preferences/src/Preferences.cpp b/components/preferences/src/Preferences.cpp
new file mode 100644
index 000000000..5f17125da
--- /dev/null
+++ b/components/preferences/src/Preferences.cpp
@@ -0,0 +1,1988 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/ContentChild.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/UniquePtrExtensions.h"
+
+#include "nsXULAppAPI.h"
+
+#include "mozilla/Preferences.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDataHashtable.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsICategoryManager.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsNetUtil.h"
+#include "nsIFile.h"
+#include "nsIInputStream.h"
+#include "nsIObserverService.h"
+#include "nsIOutputStream.h"
+#include "nsISafeOutputStream.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIStringEnumerator.h"
+#include "nsIZipReader.h"
+#include "nsPrefBranch.h"
+#include "nsXPIDLString.h"
+#include "nsCRT.h"
+#include "nsCOMArray.h"
+#include "nsXPCOMCID.h"
+#include "nsAutoPtr.h"
+#include "nsPrintfCString.h"
+
+#include "nsQuickSort.h"
+#include "PLDHashTable.h"
+
+#include "prefapi.h"
+#include "prefread.h"
+#include "prefapi_private_data.h"
+
+#include "mozilla/Omnijar.h"
+#include "nsZipArchive.h"
+
+#include "nsTArray.h"
+#include "nsRefPtrHashtable.h"
+#include "nsIMemoryReporter.h"
+#include "nsThreadUtils.h"
+
+#ifdef DEBUG
+#define ENSURE_MAIN_PROCESS(message, pref) do { \
+ if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
+ nsPrintfCString msg("ENSURE_MAIN_PROCESS failed. %s %s", message, pref); \
+ NS_WARNING(msg.get()); \
+ return NS_ERROR_NOT_AVAILABLE; \
+ } \
+} while (0);
+#else
+#define ENSURE_MAIN_PROCESS(message, pref) \
+ if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
+ return NS_ERROR_NOT_AVAILABLE; \
+ }
+#endif
+
+class PrefCallback;
+
+namespace mozilla {
+
+// Definitions
+#define INITIAL_PREF_FILES 10
+static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
+
+void
+Preferences::DirtyCallback()
+{
+ if (gHashTable && sPreferences && !sPreferences->mDirty) {
+ sPreferences->mDirty = true;
+ }
+}
+
+// Prototypes
+static nsresult openPrefFile(nsIFile* aFile);
+static nsresult pref_InitInitialObjects(void);
+static nsresult pref_LoadPrefsInDirList(const char *listId);
+static nsresult ReadExtensionPrefs(nsIFile *aFile);
+
+static const char kChannelPref[] = "app.update.channel";
+
+static const char kPrefFileHeader[] =
+ "# Mozilla User Preferences"
+ NS_LINEBREAK
+ NS_LINEBREAK
+ "/* Do not edit this file."
+ NS_LINEBREAK
+ " *"
+ NS_LINEBREAK
+ " * If you make changes to this file while the application is running,"
+ NS_LINEBREAK
+ " * the changes will be overwritten when the application exits."
+ NS_LINEBREAK
+ " *"
+ NS_LINEBREAK
+ " * To make a manual change to preferences, you can visit the URL about:config"
+ NS_LINEBREAK
+ " */"
+ NS_LINEBREAK
+ NS_LINEBREAK;
+
+Preferences* Preferences::sPreferences = nullptr;
+nsIPrefBranch* Preferences::sRootBranch = nullptr;
+nsIPrefBranch* Preferences::sDefaultRootBranch = nullptr;
+bool Preferences::sShutdown = false;
+
+class ValueObserverHashKey : public PLDHashEntryHdr {
+public:
+ typedef ValueObserverHashKey* KeyType;
+ typedef const ValueObserverHashKey* KeyTypePointer;
+
+ static const ValueObserverHashKey* KeyToPointer(ValueObserverHashKey *aKey)
+ {
+ return aKey;
+ }
+
+ static PLDHashNumber HashKey(const ValueObserverHashKey *aKey)
+ {
+ PLDHashNumber hash = HashString(aKey->mPrefName);
+ hash = AddToHash(hash, aKey->mMatchKind);
+ return AddToHash(hash, aKey->mCallback);
+ }
+
+ ValueObserverHashKey(const char *aPref, PrefChangedFunc aCallback, Preferences::MatchKind aMatchKind) :
+ mPrefName(aPref), mCallback(aCallback), mMatchKind(aMatchKind) { }
+
+ explicit ValueObserverHashKey(const ValueObserverHashKey *aOther) :
+ mPrefName(aOther->mPrefName),
+ mCallback(aOther->mCallback),
+ mMatchKind(aOther->mMatchKind)
+ { }
+
+ bool KeyEquals(const ValueObserverHashKey *aOther) const
+ {
+ return mCallback == aOther->mCallback &&
+ mPrefName == aOther->mPrefName &&
+ mMatchKind == aOther->mMatchKind;
+ }
+
+ ValueObserverHashKey *GetKey() const
+ {
+ return const_cast<ValueObserverHashKey*>(this);
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+ nsCString mPrefName;
+ PrefChangedFunc mCallback;
+ Preferences::MatchKind mMatchKind;
+};
+
+class ValueObserver final : public nsIObserver,
+ public ValueObserverHashKey
+{
+ ~ValueObserver() {
+ Preferences::RemoveObserver(this, mPrefName.get());
+ }
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ ValueObserver(const char *aPref, PrefChangedFunc aCallback, Preferences::MatchKind aMatchKind)
+ : ValueObserverHashKey(aPref, aCallback, aMatchKind) { }
+
+ void AppendClosure(void *aClosure) {
+ mClosures.AppendElement(aClosure);
+ }
+
+ void RemoveClosure(void *aClosure) {
+ mClosures.RemoveElement(aClosure);
+ }
+
+ bool HasNoClosures() {
+ return mClosures.Length() == 0;
+ }
+
+ nsTArray<void*> mClosures;
+};
+
+NS_IMPL_ISUPPORTS(ValueObserver, nsIObserver)
+
+NS_IMETHODIMP
+ValueObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
+ "invalid topic");
+ NS_ConvertUTF16toUTF8 data(aData);
+ if (mMatchKind == Preferences::ExactMatch && !mPrefName.EqualsASCII(data.get())) {
+ return NS_OK;
+ }
+ for (uint32_t i = 0; i < mClosures.Length(); i++) {
+ mCallback(data.get(), mClosures.ElementAt(i));
+ }
+
+ return NS_OK;
+}
+
+struct CacheData {
+ void* cacheLocation;
+ union {
+ bool defaultValueBool;
+ int32_t defaultValueInt;
+ uint32_t defaultValueUint;
+ float defaultValueFloat;
+ };
+};
+
+static nsTArray<nsAutoPtr<CacheData> >* gCacheData = nullptr;
+static nsRefPtrHashtable<ValueObserverHashKey,
+ ValueObserver>* gObserverTable = nullptr;
+
+#ifdef DEBUG
+static bool
+HaveExistingCacheFor(void* aPtr)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (gCacheData) {
+ for (size_t i = 0, count = gCacheData->Length(); i < count; ++i) {
+ if ((*gCacheData)[i]->cacheLocation == aPtr) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void
+AssertNotAlreadyCached(const char* aPrefType,
+ const char* aPref,
+ void* aPtr)
+{
+ if (HaveExistingCacheFor(aPtr)) {
+ fprintf_stderr(stderr,
+ "Attempt to add a %s pref cache for preference '%s' at address '%p'"
+ "was made. However, a pref was already cached at this address.\n",
+ aPrefType, aPref, aPtr);
+ MOZ_ASSERT(false, "Should not have an existing pref cache for this address");
+ }
+}
+#endif
+
+static void
+ReportToConsole(const char* aMessage, int aLine, bool aError)
+{
+ nsPrintfCString message("** Preference parsing %s (line %d) = %s **\n",
+ (aError ? "error" : "warning"), aLine, aMessage);
+ nsPrefBranch::ReportToConsole(NS_ConvertUTF8toUTF16(message.get()));
+}
+
+// Although this is a member of Preferences, it measures sPreferences and
+// several other global structures.
+/* static */ int64_t
+Preferences::SizeOfIncludingThisAndOtherStuff(mozilla::MallocSizeOf aMallocSizeOf)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), 0);
+
+ size_t n = aMallocSizeOf(sPreferences);
+ if (gHashTable) {
+ // pref keys are allocated in a private arena, which we count elsewhere.
+ // pref stringvals are allocated out of the same private arena.
+ n += gHashTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ }
+ if (gCacheData) {
+ n += gCacheData->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ for (uint32_t i = 0, count = gCacheData->Length(); i < count; ++i) {
+ n += aMallocSizeOf((*gCacheData)[i]);
+ }
+ }
+ if (gObserverTable) {
+ n += gObserverTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ for (auto iter = gObserverTable->Iter(); !iter.Done(); iter.Next()) {
+ n += iter.Key()->mPrefName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ n += iter.Data()->mClosures.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+ }
+ if (sRootBranch) {
+ n += reinterpret_cast<nsPrefBranch*>(sRootBranch)->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ if (sDefaultRootBranch) {
+ n += reinterpret_cast<nsPrefBranch*>(sDefaultRootBranch)->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ n += pref_SizeOfPrivateData(aMallocSizeOf);
+ return n;
+}
+
+class PreferenceServiceReporter final : public nsIMemoryReporter
+{
+ ~PreferenceServiceReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+
+protected:
+ static const uint32_t kSuspectReferentCount = 1000;
+};
+
+NS_IMPL_ISUPPORTS(PreferenceServiceReporter, nsIMemoryReporter)
+
+MOZ_DEFINE_MALLOC_SIZE_OF(PreferenceServiceMallocSizeOf)
+
+NS_IMETHODIMP
+PreferenceServiceReporter::CollectReports(
+ nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
+{
+ MOZ_COLLECT_REPORT(
+ "explicit/preferences", KIND_HEAP, UNITS_BYTES,
+ Preferences::SizeOfIncludingThisAndOtherStuff(PreferenceServiceMallocSizeOf),
+ "Memory used by the preferences system.");
+
+ nsPrefBranch* rootBranch =
+ static_cast<nsPrefBranch*>(Preferences::GetRootBranch());
+ if (!rootBranch) {
+ return NS_OK;
+ }
+
+ size_t numStrong = 0;
+ size_t numWeakAlive = 0;
+ size_t numWeakDead = 0;
+ nsTArray<nsCString> suspectPreferences;
+ // Count of the number of referents for each preference.
+ nsDataHashtable<nsCStringHashKey, uint32_t> prefCounter;
+
+ for (auto iter = rootBranch->mObservers.Iter(); !iter.Done(); iter.Next()) {
+ nsAutoPtr<PrefCallback>& callback = iter.Data();
+ nsPrefBranch* prefBranch = callback->GetPrefBranch();
+ const char* pref = prefBranch->getPrefName(callback->GetDomain().get());
+
+ if (callback->IsWeak()) {
+ nsCOMPtr<nsIObserver> callbackRef = do_QueryReferent(callback->mWeakRef);
+ if (callbackRef) {
+ numWeakAlive++;
+ } else {
+ numWeakDead++;
+ }
+ } else {
+ numStrong++;
+ }
+
+ nsDependentCString prefString(pref);
+ uint32_t oldCount = 0;
+ prefCounter.Get(prefString, &oldCount);
+ uint32_t currentCount = oldCount + 1;
+ prefCounter.Put(prefString, currentCount);
+
+ // Keep track of preferences that have a suspiciously large number of
+ // referents (a symptom of a leak).
+ if (currentCount == kSuspectReferentCount) {
+ suspectPreferences.AppendElement(prefString);
+ }
+ }
+
+ for (uint32_t i = 0; i < suspectPreferences.Length(); i++) {
+ nsCString& suspect = suspectPreferences[i];
+ uint32_t totalReferentCount = 0;
+ prefCounter.Get(suspect, &totalReferentCount);
+
+ nsPrintfCString suspectPath("preference-service-suspect/"
+ "referent(pref=%s)", suspect.get());
+
+ aHandleReport->Callback(
+ /* process = */ EmptyCString(),
+ suspectPath, KIND_OTHER, UNITS_COUNT, totalReferentCount,
+ NS_LITERAL_CSTRING(
+ "A preference with a suspiciously large number referents (symptom of a "
+ "leak)."),
+ aData);
+ }
+
+ MOZ_COLLECT_REPORT(
+ "preference-service/referent/strong", KIND_OTHER, UNITS_COUNT,
+ numStrong,
+ "The number of strong referents held by the preference service.");
+
+ MOZ_COLLECT_REPORT(
+ "preference-service/referent/weak/alive", KIND_OTHER, UNITS_COUNT,
+ numWeakAlive,
+ "The number of weak referents held by the preference service that are "
+ "still alive.");
+
+ MOZ_COLLECT_REPORT(
+ "preference-service/referent/weak/dead", KIND_OTHER, UNITS_COUNT,
+ numWeakDead,
+ "The number of weak referents held by the preference service that are "
+ "dead.");
+
+ return NS_OK;
+}
+
+namespace {
+class AddPreferencesMemoryReporterRunnable : public Runnable
+{
+ NS_IMETHOD Run() override
+ {
+ return RegisterStrongMemoryReporter(new PreferenceServiceReporter());
+ }
+};
+} // namespace
+
+// static
+Preferences*
+Preferences::GetInstanceForService()
+{
+ if (sPreferences) {
+ NS_ADDREF(sPreferences);
+ return sPreferences;
+ }
+
+ NS_ENSURE_TRUE(!sShutdown, nullptr);
+
+ sRootBranch = new nsPrefBranch("", false);
+ NS_ADDREF(sRootBranch);
+ sDefaultRootBranch = new nsPrefBranch("", true);
+ NS_ADDREF(sDefaultRootBranch);
+
+ sPreferences = new Preferences();
+ NS_ADDREF(sPreferences);
+
+ if (NS_FAILED(sPreferences->Init())) {
+ // The singleton instance will delete sRootBranch and sDefaultRootBranch.
+ NS_RELEASE(sPreferences);
+ return nullptr;
+ }
+
+ gCacheData = new nsTArray<nsAutoPtr<CacheData> >();
+
+ gObserverTable = new nsRefPtrHashtable<ValueObserverHashKey, ValueObserver>();
+
+ // Preferences::GetInstanceForService() can be called from GetService(), and
+ // RegisterStrongMemoryReporter calls GetService(nsIMemoryReporter). To
+ // avoid a potential recursive GetService() call, we can't register the
+ // memory reporter here; instead, do it off a runnable.
+ RefPtr<AddPreferencesMemoryReporterRunnable> runnable =
+ new AddPreferencesMemoryReporterRunnable();
+ NS_DispatchToMainThread(runnable);
+
+ NS_ADDREF(sPreferences);
+ return sPreferences;
+}
+
+// static
+bool
+Preferences::IsServiceAvailable()
+{
+ return !!sPreferences;
+}
+
+// static
+bool
+Preferences::InitStaticMembers()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!sShutdown && !sPreferences) {
+ nsCOMPtr<nsIPrefService> prefService =
+ do_GetService(NS_PREFSERVICE_CONTRACTID);
+ }
+
+ return sPreferences != nullptr;
+}
+
+// static
+void
+Preferences::Shutdown()
+{
+ if (!sShutdown) {
+ sShutdown = true; // Don't create the singleton instance after here.
+
+ // Don't set sPreferences to nullptr here. The instance may be grabbed by
+ // other modules. The utility methods of Preferences should be available
+ // until the singleton instance actually released.
+ if (sPreferences) {
+ sPreferences->Release();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+/*
+ * Constructor/Destructor
+ */
+
+Preferences::Preferences()
+ : mDirty(false)
+{
+}
+
+Preferences::~Preferences()
+{
+ NS_ASSERTION(sPreferences == this, "Isn't this the singleton instance?");
+
+ delete gObserverTable;
+ gObserverTable = nullptr;
+
+ delete gCacheData;
+ gCacheData = nullptr;
+
+ NS_RELEASE(sRootBranch);
+ NS_RELEASE(sDefaultRootBranch);
+
+ sPreferences = nullptr;
+
+ PREF_Cleanup();
+}
+
+
+/*
+ * nsISupports Implementation
+ */
+
+NS_IMPL_ADDREF(Preferences)
+NS_IMPL_RELEASE(Preferences)
+
+NS_INTERFACE_MAP_BEGIN(Preferences)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrefService)
+ NS_INTERFACE_MAP_ENTRY(nsIPrefService)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIPrefBranch)
+ NS_INTERFACE_MAP_ENTRY(nsIPrefBranch2)
+ NS_INTERFACE_MAP_ENTRY(nsIPrefBranchInternal)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+
+/*
+ * nsIPrefService Implementation
+ */
+
+nsresult
+Preferences::Init()
+{
+ nsresult rv;
+
+ PREF_SetDirtyCallback(&DirtyCallback);
+ PREF_Init();
+
+ rv = pref_InitInitialObjects();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ using mozilla::dom::ContentChild;
+ if (XRE_IsContentProcess()) {
+ InfallibleTArray<PrefSetting> prefs;
+ ContentChild::GetSingleton()->SendReadPrefsArray(&prefs);
+
+ // Store the array
+ for (uint32_t i = 0; i < prefs.Length(); ++i) {
+ pref_SetPref(prefs[i]);
+ }
+ return NS_OK;
+ }
+
+ nsXPIDLCString lockFileName;
+ /*
+ * The following is a small hack which will allow us to only load the library
+ * which supports the netscape.cfg file if the preference is defined. We
+ * test for the existence of the pref, set in the all.js (mozilla) or
+ * all-ns.js (netscape 6), and if it exists we startup the pref config
+ * category which will do the rest.
+ */
+
+ rv = PREF_CopyCharPref("general.config.filename", getter_Copies(lockFileName), false);
+ if (NS_SUCCEEDED(rv))
+ NS_CreateServicesFromCategory("pref-config-startup",
+ static_cast<nsISupports *>(static_cast<void *>(this)),
+ "pref-config-startup");
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (!observerService)
+ return NS_ERROR_FAILURE;
+
+ rv = observerService->AddObserver(this, "profile-before-change", true);
+
+ observerService->AddObserver(this, "load-extension-defaults", true);
+ observerService->AddObserver(this, "suspend_process_notification", true);
+
+ return(rv);
+}
+
+// static
+nsresult
+Preferences::ResetAndReadUserPrefs()
+{
+ sPreferences->ResetUserPrefs();
+ return sPreferences->ReadUserPrefs(nullptr);
+}
+
+NS_IMETHODIMP
+Preferences::Observe(nsISupports *aSubject, const char *aTopic,
+ const char16_t *someData)
+{
+ if (XRE_IsContentProcess())
+ return NS_ERROR_NOT_AVAILABLE;
+
+ nsresult rv = NS_OK;
+
+ if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
+ rv = SavePrefFile(nullptr);
+ } else if (!strcmp(aTopic, "load-extension-defaults")) {
+ pref_LoadPrefsInDirList(NS_EXT_PREFS_DEFAULTS_DIR_LIST);
+ } else if (!nsCRT::strcmp(aTopic, "reload-default-prefs")) {
+ // Reload the default prefs from file.
+ pref_InitInitialObjects();
+ } else if (!nsCRT::strcmp(aTopic, "suspend_process_notification")) {
+ // Our process is being suspended. The OS may wake our process later,
+ // or it may kill the process. In case our process is going to be killed
+ // from the suspended state, we save preferences before suspending.
+ rv = SavePrefFile(nullptr);
+ }
+ return rv;
+}
+
+
+NS_IMETHODIMP
+Preferences::ReadUserPrefs(nsIFile *aFile)
+{
+ if (XRE_IsContentProcess()) {
+ NS_ERROR("cannot load prefs from content process");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult rv;
+
+ if (nullptr == aFile) {
+ rv = UseDefaultPrefFile();
+ // A user pref file is optional.
+ // Ignore all errors related to it, so we retain 'rv' value :-|
+ (void) UseUserPrefFile();
+
+ NotifyServiceObservers(NS_PREFSERVICE_READ_TOPIC_ID);
+ } else {
+ rv = ReadAndOwnUserPrefFile(aFile);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+Preferences::ResetPrefs()
+{
+ if (XRE_IsContentProcess()) {
+ NS_ERROR("cannot reset prefs from content process");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ NotifyServiceObservers(NS_PREFSERVICE_RESET_TOPIC_ID);
+ PREF_CleanupPrefs();
+
+ PREF_Init();
+
+ return pref_InitInitialObjects();
+}
+
+NS_IMETHODIMP
+Preferences::ResetUserPrefs()
+{
+ if (XRE_IsContentProcess()) {
+ NS_ERROR("cannot reset user prefs from content process");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ PREF_ClearAllUserPrefs();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Preferences::SavePrefFile(nsIFile *aFile)
+{
+ if (XRE_IsContentProcess()) {
+ NS_ERROR("cannot save pref file from content process");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return SavePrefFileInternal(aFile);
+}
+
+static nsresult
+ReadExtensionPrefs(nsIFile *aFile)
+{
+ nsresult rv;
+ nsCOMPtr<nsIZipReader> reader = do_CreateInstance(kZipReaderCID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = reader->Open(aFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIUTF8StringEnumerator> files;
+ rv = reader->FindEntries(nsDependentCString("defaults/preferences/*.(J|j)(S|s)$"),
+ getter_AddRefs(files));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char buffer[4096];
+
+ bool more;
+ while (NS_SUCCEEDED(rv = files->HasMore(&more)) && more) {
+ nsAutoCString entry;
+ rv = files->GetNext(entry);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInputStream> stream;
+ rv = reader->GetInputStream(entry, getter_AddRefs(stream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint64_t avail;
+ uint32_t read;
+
+ PrefParseState ps;
+ PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr);
+ while (NS_SUCCEEDED(rv = stream->Available(&avail)) && avail) {
+ rv = stream->Read(buffer, 4096, &read);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Pref stream read failed");
+ break;
+ }
+
+ PREF_ParseBuf(&ps, buffer, read);
+ }
+ PREF_FinalizeParseState(&ps);
+ }
+ return rv;
+}
+
+void
+Preferences::SetPreference(const PrefSetting& aPref)
+{
+ pref_SetPref(aPref);
+}
+
+void
+Preferences::GetPreference(PrefSetting* aPref)
+{
+ PrefHashEntry *entry = pref_HashTableLookup(aPref->name().get());
+ if (!entry)
+ return;
+
+ if (pref_EntryHasAdvisablySizedValues(entry)) {
+ pref_GetPrefFromEntry(entry, aPref);
+ }
+}
+
+void
+Preferences::GetPreferences(InfallibleTArray<PrefSetting>* aPrefs)
+{
+ aPrefs->SetCapacity(gHashTable->Capacity());
+ for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<PrefHashEntry*>(iter.Get());
+
+ if (!pref_EntryHasAdvisablySizedValues(entry)) {
+ continue;
+ }
+
+ dom::PrefSetting *pref = aPrefs->AppendElement();
+ pref_GetPrefFromEntry(entry, pref);
+ }
+}
+
+NS_IMETHODIMP
+Preferences::GetBranch(const char *aPrefRoot, nsIPrefBranch **_retval)
+{
+ nsresult rv;
+
+ if ((nullptr != aPrefRoot) && (*aPrefRoot != '\0')) {
+ // TODO: - cache this stuff and allow consumers to share branches (hold weak references I think)
+ RefPtr<nsPrefBranch> prefBranch = new nsPrefBranch(aPrefRoot, false);
+ prefBranch.forget(_retval);
+ rv = NS_OK;
+ } else {
+ // special case caching the default root
+ nsCOMPtr<nsIPrefBranch> root(sRootBranch);
+ root.forget(_retval);
+ rv = NS_OK;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+Preferences::GetDefaultBranch(const char *aPrefRoot, nsIPrefBranch **_retval)
+{
+ if (!aPrefRoot || !aPrefRoot[0]) {
+ nsCOMPtr<nsIPrefBranch> root(sDefaultRootBranch);
+ root.forget(_retval);
+ return NS_OK;
+ }
+
+ // TODO: - cache this stuff and allow consumers to share branches (hold weak references I think)
+ RefPtr<nsPrefBranch> prefBranch = new nsPrefBranch(aPrefRoot, true);
+ if (!prefBranch)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ prefBranch.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Preferences::GetDirty(bool *_retval) {
+ *_retval = mDirty;
+ return NS_OK;
+}
+
+nsresult
+Preferences::NotifyServiceObservers(const char *aTopic)
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (!observerService)
+ return NS_ERROR_FAILURE;
+
+ nsISupports *subject = (nsISupports *)((nsIPrefService *)this);
+ observerService->NotifyObservers(subject, aTopic, nullptr);
+
+ return NS_OK;
+}
+
+nsresult
+Preferences::UseDefaultPrefFile()
+{
+ nsCOMPtr<nsIFile> aFile;
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_PREFS_50_FILE, getter_AddRefs(aFile));
+
+ if (NS_SUCCEEDED(rv)) {
+ rv = ReadAndOwnUserPrefFile(aFile);
+ // Most likely cause of failure here is that the file didn't
+ // exist, so save a new one. mUserPrefReadFailed will be
+ // used to catch an error in actually reading the file.
+ if (NS_FAILED(rv)) {
+ if (NS_FAILED(SavePrefFileInternal(aFile)))
+ NS_ERROR("Failed to save new shared pref file");
+ else
+ rv = NS_OK;
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+Preferences::UseUserPrefFile()
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIFile> aFile;
+ nsDependentCString prefsDirProp(NS_APP_PREFS_50_DIR);
+
+ rv = NS_GetSpecialDirectory(prefsDirProp.get(), getter_AddRefs(aFile));
+ if (NS_SUCCEEDED(rv) && aFile) {
+ rv = aFile->AppendNative(NS_LITERAL_CSTRING("user.js"));
+ if (NS_SUCCEEDED(rv)) {
+ bool exists = false;
+ aFile->Exists(&exists);
+ if (exists) {
+ rv = openPrefFile(aFile);
+ } else {
+ rv = NS_ERROR_FILE_NOT_FOUND;
+ }
+ }
+ }
+ return rv;
+}
+
+nsresult
+Preferences::MakeBackupPrefFile(nsIFile *aFile)
+{
+ // Example: this copies "prefs.js" to "Invalidprefs.js" in the same directory.
+ // "Invalidprefs.js" is removed if it exists, prior to making the copy.
+ nsAutoString newFilename;
+ nsresult rv = aFile->GetLeafName(newFilename);
+ NS_ENSURE_SUCCESS(rv, rv);
+ newFilename.Insert(NS_LITERAL_STRING("Invalid"), 0);
+ nsCOMPtr<nsIFile> newFile;
+ rv = aFile->GetParent(getter_AddRefs(newFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = newFile->Append(newFilename);
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool exists = false;
+ newFile->Exists(&exists);
+ if (exists) {
+ rv = newFile->Remove(false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ rv = aFile->CopyTo(nullptr, newFilename);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
+}
+
+nsresult
+Preferences::ReadAndOwnUserPrefFile(nsIFile *aFile)
+{
+ NS_ENSURE_ARG(aFile);
+
+ if (mCurrentFile == aFile)
+ return NS_OK;
+ mCurrentFile = aFile;
+
+ nsresult rv = NS_OK;
+ bool exists = false;
+ mCurrentFile->Exists(&exists);
+ if (exists) {
+ rv = openPrefFile(mCurrentFile);
+ if (NS_FAILED(rv)) {
+ // Save a backup copy of the current (invalid) prefs file, since all prefs
+ // from the error line to the end of the file will be lost (bug 361102).
+ // TODO we should notify the user about it (bug 523725).
+ MakeBackupPrefFile(mCurrentFile);
+ }
+ } else {
+ rv = NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ return rv;
+}
+
+nsresult
+Preferences::SavePrefFileInternal(nsIFile *aFile)
+{
+ if (nullptr == aFile) {
+ // the mDirty flag tells us if we should write to mCurrentFile
+ // we only check this flag when the caller wants to write to the default
+ if (!mDirty) {
+ return NS_OK;
+ }
+
+ // It's possible that we never got a prefs file.
+ nsresult rv = NS_OK;
+ if (mCurrentFile)
+ rv = WritePrefFile(mCurrentFile);
+
+ return rv;
+ } else {
+ return WritePrefFile(aFile);
+ }
+}
+
+nsresult
+Preferences::WritePrefFile(nsIFile* aFile)
+{
+ nsCOMPtr<nsIOutputStream> outStreamSink;
+ nsCOMPtr<nsIOutputStream> outStream;
+ uint32_t writeAmount;
+ nsresult rv;
+
+ if (!gHashTable)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // execute a "safe" save by saving through a tempfile
+ rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStreamSink),
+ aFile,
+ -1,
+ 0600);
+ if (NS_FAILED(rv))
+ return rv;
+ rv = NS_NewBufferedOutputStream(getter_AddRefs(outStream), outStreamSink, 4096);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // get the lines that we're supposed to be writing to the file
+ uint32_t prefCount;
+ UniquePtr<char*[]> valueArray = pref_savePrefs(gHashTable, &prefCount);
+
+ /* Sort the preferences to make a readable file on disk */
+ NS_QuickSort(valueArray.get(), prefCount, sizeof(char *),
+ pref_CompareStrings, nullptr);
+
+ // write out the file header
+ outStream->Write(kPrefFileHeader, sizeof(kPrefFileHeader) - 1, &writeAmount);
+
+ for (uint32_t valueIdx = 0; valueIdx < prefCount; valueIdx++) {
+ char*& pref = valueArray[valueIdx];
+ MOZ_ASSERT(pref);
+ outStream->Write(pref, strlen(pref), &writeAmount);
+ outStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &writeAmount);
+ free(pref);
+ pref = nullptr;
+ }
+
+ // tell the safe output stream to overwrite the real prefs file
+ // (it'll abort if there were any errors during writing)
+ nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outStream);
+ NS_ASSERTION(safeStream, "expected a safe output stream!");
+ if (safeStream) {
+ rv = safeStream->Finish();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("failed to save prefs file! possible data loss");
+ return rv;
+ }
+ }
+
+ mDirty = false;
+ return NS_OK;
+}
+
+static nsresult openPrefFile(nsIFile* aFile)
+{
+ nsCOMPtr<nsIInputStream> inStr;
+
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), aFile);
+ if (NS_FAILED(rv))
+ return rv;
+
+ int64_t fileSize64;
+ rv = aFile->GetFileSize(&fileSize64);
+ if (NS_FAILED(rv))
+ return rv;
+ NS_ENSURE_TRUE(fileSize64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
+
+ uint32_t fileSize = (uint32_t)fileSize64;
+ auto fileBuffer = MakeUniqueFallible<char[]>(fileSize);
+ if (fileBuffer == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PrefParseState ps;
+ PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr);
+
+ // Read is not guaranteed to return a buf the size of fileSize,
+ // but usually will.
+ nsresult rv2 = NS_OK;
+ uint32_t offset = 0;
+ for (;;) {
+ uint32_t amtRead = 0;
+ rv = inStr->Read(fileBuffer.get(), fileSize, &amtRead);
+ if (NS_FAILED(rv) || amtRead == 0)
+ break;
+ if (!PREF_ParseBuf(&ps, fileBuffer.get(), amtRead))
+ rv2 = NS_ERROR_FILE_CORRUPTED;
+ offset += amtRead;
+ if (offset == fileSize) {
+ break;
+ }
+ }
+
+ PREF_FinalizeParseState(&ps);
+
+ return NS_FAILED(rv) ? rv : rv2;
+}
+
+/*
+ * some stuff that gets called from Pref_Init()
+ */
+
+static int
+pref_CompareFileNames(nsIFile* aFile1, nsIFile* aFile2, void* /*unused*/)
+{
+ nsAutoCString filename1, filename2;
+ aFile1->GetNativeLeafName(filename1);
+ aFile2->GetNativeLeafName(filename2);
+
+ return Compare(filename2, filename1);
+}
+
+/**
+ * Load default pref files from a directory. The files in the
+ * directory are sorted reverse-alphabetically; a set of "special file
+ * names" may be specified which are loaded after all the others.
+ */
+static nsresult
+pref_LoadPrefsInDir(nsIFile* aDir, char const *const *aSpecialFiles, uint32_t aSpecialFilesCount)
+{
+ nsresult rv, rv2;
+ bool hasMoreElements;
+
+ nsCOMPtr<nsISimpleEnumerator> dirIterator;
+
+ // this may fail in some normal cases, such as embedders who do not use a GRE
+ rv = aDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
+ if (NS_FAILED(rv)) {
+ // If the directory doesn't exist, then we have no reason to complain. We
+ // loaded everything (and nothing) successfully.
+ if (rv == NS_ERROR_FILE_NOT_FOUND || rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
+ rv = NS_OK;
+ return rv;
+ }
+
+ rv = dirIterator->HasMoreElements(&hasMoreElements);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMArray<nsIFile> prefFiles(INITIAL_PREF_FILES);
+ nsCOMArray<nsIFile> specialFiles(aSpecialFilesCount);
+ nsCOMPtr<nsIFile> prefFile;
+
+ while (hasMoreElements && NS_SUCCEEDED(rv)) {
+ nsAutoCString leafName;
+
+ nsCOMPtr<nsISupports> supports;
+ rv = dirIterator->GetNext(getter_AddRefs(supports));
+ prefFile = do_QueryInterface(supports);
+ if (NS_FAILED(rv)) {
+ break;
+ }
+
+ prefFile->GetNativeLeafName(leafName);
+ NS_ASSERTION(!leafName.IsEmpty(), "Failure in default prefs: directory enumerator returned empty file?");
+
+ // Skip non-js files
+ if (StringEndsWith(leafName, NS_LITERAL_CSTRING(".js"),
+ nsCaseInsensitiveCStringComparator())) {
+ bool shouldParse = true;
+ // separate out special files
+ for (uint32_t i = 0; i < aSpecialFilesCount; ++i) {
+ if (leafName.Equals(nsDependentCString(aSpecialFiles[i]))) {
+ shouldParse = false;
+ // special files should be process in order; we put them into
+ // the array by index; this can make the array sparse
+ specialFiles.ReplaceObjectAt(prefFile, i);
+ }
+ }
+
+ if (shouldParse) {
+ prefFiles.AppendObject(prefFile);
+ }
+ }
+
+ rv = dirIterator->HasMoreElements(&hasMoreElements);
+ }
+
+ if (prefFiles.Count() + specialFiles.Count() == 0) {
+ NS_WARNING("No default pref files found.");
+ if (NS_SUCCEEDED(rv)) {
+ rv = NS_SUCCESS_FILE_DIRECTORY_EMPTY;
+ }
+ return rv;
+ }
+
+ prefFiles.Sort(pref_CompareFileNames, nullptr);
+
+ uint32_t arrayCount = prefFiles.Count();
+ uint32_t i;
+ for (i = 0; i < arrayCount; ++i) {
+ rv2 = openPrefFile(prefFiles[i]);
+ if (NS_FAILED(rv2)) {
+ NS_ERROR("Default pref file not parsed successfully.");
+ rv = rv2;
+ }
+ }
+
+ arrayCount = specialFiles.Count();
+ for (i = 0; i < arrayCount; ++i) {
+ // this may be a sparse array; test before parsing
+ nsIFile* file = specialFiles[i];
+ if (file) {
+ rv2 = openPrefFile(file);
+ if (NS_FAILED(rv2)) {
+ NS_ERROR("Special default pref file not parsed successfully.");
+ rv = rv2;
+ }
+ }
+ }
+
+ return rv;
+}
+
+static nsresult pref_LoadPrefsInDirList(const char *listId)
+{
+ nsresult rv;
+ nsCOMPtr<nsIProperties> dirSvc(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsISimpleEnumerator> list;
+ dirSvc->Get(listId,
+ NS_GET_IID(nsISimpleEnumerator),
+ getter_AddRefs(list));
+ if (!list)
+ return NS_OK;
+
+ bool hasMore;
+ while (NS_SUCCEEDED(list->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsISupports> elem;
+ list->GetNext(getter_AddRefs(elem));
+ if (!elem)
+ continue;
+
+ nsCOMPtr<nsIFile> path = do_QueryInterface(elem);
+ if (!path)
+ continue;
+
+ nsAutoCString leaf;
+ path->GetNativeLeafName(leaf);
+
+ // Do we care if a file provided by this process fails to load?
+ if (Substring(leaf, leaf.Length() - 4).EqualsLiteral(".xpi"))
+ ReadExtensionPrefs(path);
+ else
+ pref_LoadPrefsInDir(path, nullptr, 0);
+ }
+ return NS_OK;
+}
+
+static nsresult pref_ReadPrefFromJar(nsZipArchive* jarReader, const char *name)
+{
+ nsZipItemPtr<char> manifest(jarReader, name, true);
+ NS_ENSURE_TRUE(manifest.Buffer(), NS_ERROR_NOT_AVAILABLE);
+
+ PrefParseState ps;
+ PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr);
+ PREF_ParseBuf(&ps, manifest, manifest.Length());
+ PREF_FinalizeParseState(&ps);
+
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------------------------
+// Initialize default preference JavaScript buffers from
+// appropriate TEXT resources
+//----------------------------------------------------------------------------------------
+static nsresult pref_InitInitialObjects()
+{
+ nsresult rv;
+
+ // In omni.jar case, we load the following prefs:
+ // - jar:$gre/omni.jar!/goanna.js
+ // - jar:$gre/omni.jar!/defaults/pref/*.js
+ // In non omni.jar case, we load:
+ // - $gre/goanna.js
+ //
+ // In both cases, we also load:
+ // - $gre/defaults/pref/*.js
+ // This is kept for bug 591866 (channel-prefs.js should not be in omni.jar)
+ // on $app == $gre case ; we load all files instead of channel-prefs.js only
+ // to have the same behaviour as $app != $gre, where this is required as
+ // a supported location for GRE preferences.
+ //
+ // When $app != $gre, we additionally load, in omni.jar case:
+ // - jar:$app/omni.jar!/defaults/preferences/*.js
+ // - $app/defaults/preferences/*.js
+ // and in non omni.jar case:
+ // - $app/defaults/preferences/*.js
+ // When $app == $gre, we additionally load, in omni.jar case:
+ // - jar:$gre/omni.jar!/defaults/preferences/*.js
+ // Thus, in omni.jar case, we always load app-specific default preferences
+ // from omni.jar, whether or not $app == $gre.
+
+ nsZipFind *findPtr;
+ nsAutoPtr<nsZipFind> find;
+ nsTArray<nsCString> prefEntries;
+ const char *entryName;
+ uint16_t entryNameLen;
+
+ RefPtr<nsZipArchive> jarReader = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
+ if (jarReader) {
+ // Load jar:$gre/omni.jar!/goanna.js
+ rv = pref_ReadPrefFromJar(jarReader, "goanna.js");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Load jar:$gre/omni.jar!/defaults/pref/*.js
+ rv = jarReader->FindInit("defaults/pref/*.js$", &findPtr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ find = findPtr;
+ while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
+ prefEntries.AppendElement(Substring(entryName, entryNameLen));
+ }
+
+ prefEntries.Sort();
+ for (uint32_t i = prefEntries.Length(); i--; ) {
+ rv = pref_ReadPrefFromJar(jarReader, prefEntries[i].get());
+ if (NS_FAILED(rv))
+ NS_WARNING("Error parsing preferences.");
+ }
+ } else {
+ // Load $gre/goanna.js
+ nsCOMPtr<nsIFile> greprefsFile;
+ rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greprefsFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = greprefsFile->AppendNative(NS_LITERAL_CSTRING("goanna.js"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = openPrefFile(greprefsFile);
+ if (NS_FAILED(rv))
+ NS_WARNING("Error parsing GRE default preferences. Is this an old-style embedding app?");
+ }
+
+ // Load $gre/defaults/pref/*.js
+ nsCOMPtr<nsIFile> defaultPrefDir;
+
+ rv = NS_GetSpecialDirectory(NS_APP_PREF_DEFAULTS_50_DIR, getter_AddRefs(defaultPrefDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /* these pref file names should not be used: we process them after all other application pref files for backwards compatibility */
+ static const char* specialFiles[] = {
+#if defined(XP_WIN)
+ "winpref.js"
+#elif defined(XP_UNIX)
+ "unix.js"
+#endif
+ };
+
+ rv = pref_LoadPrefsInDir(defaultPrefDir, specialFiles, ArrayLength(specialFiles));
+ if (NS_FAILED(rv))
+ NS_WARNING("Error parsing application default preferences.");
+
+ // Load jar:$app/omni.jar!/defaults/preferences/*.js
+ // or jar:$gre/omni.jar!/defaults/preferences/*.js.
+ RefPtr<nsZipArchive> appJarReader = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
+ // GetReader(mozilla::Omnijar::APP) returns null when $app == $gre, in which
+ // case we look for app-specific default preferences in $gre.
+ if (!appJarReader)
+ appJarReader = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
+ if (appJarReader) {
+ rv = appJarReader->FindInit("defaults/preferences/*.js$", &findPtr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ find = findPtr;
+ prefEntries.Clear();
+ while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
+ prefEntries.AppendElement(Substring(entryName, entryNameLen));
+ }
+ prefEntries.Sort();
+ for (uint32_t i = prefEntries.Length(); i--; ) {
+ rv = pref_ReadPrefFromJar(appJarReader, prefEntries[i].get());
+ if (NS_FAILED(rv))
+ NS_WARNING("Error parsing preferences.");
+ }
+ }
+
+ rv = pref_LoadPrefsInDirList(NS_APP_PREFS_DEFAULTS_DIR_LIST);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_CreateServicesFromCategory(NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID,
+ nullptr, NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID);
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (!observerService)
+ return NS_ERROR_FAILURE;
+
+ observerService->NotifyObservers(nullptr, NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID, nullptr);
+
+ return pref_LoadPrefsInDirList(NS_EXT_PREFS_DEFAULTS_DIR_LIST);
+}
+
+
+/******************************************************************************
+ *
+ * static utilities
+ *
+ ******************************************************************************/
+
+// static
+nsresult
+Preferences::GetBool(const char* aPref, bool* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_GetBoolPref(aPref, aResult, false);
+}
+
+// static
+nsresult
+Preferences::GetInt(const char* aPref, int32_t* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_GetIntPref(aPref, aResult, false);
+}
+
+// static
+nsresult
+Preferences::GetFloat(const char* aPref, float* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ nsAutoCString result;
+ nsresult rv = PREF_CopyCharPref(aPref, getter_Copies(result), false);
+ if (NS_SUCCEEDED(rv)) {
+ *aResult = result.ToFloat(&rv);
+ }
+
+ return rv;
+}
+
+// static
+nsAdoptingCString
+Preferences::GetCString(const char* aPref)
+{
+ nsAdoptingCString result;
+ PREF_CopyCharPref(aPref, getter_Copies(result), false);
+ return result;
+}
+
+// static
+nsAdoptingString
+Preferences::GetString(const char* aPref)
+{
+ nsAdoptingString result;
+ GetString(aPref, &result);
+ return result;
+}
+
+// static
+nsresult
+Preferences::GetCString(const char* aPref, nsACString* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ nsAutoCString result;
+ nsresult rv = PREF_CopyCharPref(aPref, getter_Copies(result), false);
+ if (NS_SUCCEEDED(rv)) {
+ *aResult = result;
+ }
+ return rv;
+}
+
+// static
+nsresult
+Preferences::GetString(const char* aPref, nsAString* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ nsAutoCString result;
+ nsresult rv = PREF_CopyCharPref(aPref, getter_Copies(result), false);
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF8toUTF16(result, *aResult);
+ }
+ return rv;
+}
+
+// static
+nsAdoptingCString
+Preferences::GetLocalizedCString(const char* aPref)
+{
+ nsAdoptingCString result;
+ GetLocalizedCString(aPref, &result);
+ return result;
+}
+
+// static
+nsAdoptingString
+Preferences::GetLocalizedString(const char* aPref)
+{
+ nsAdoptingString result;
+ GetLocalizedString(aPref, &result);
+ return result;
+}
+
+// static
+nsresult
+Preferences::GetLocalizedCString(const char* aPref, nsACString* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ nsAutoString result;
+ nsresult rv = GetLocalizedString(aPref, &result);
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF16toUTF8(result, *aResult);
+ }
+ return rv;
+}
+
+// static
+nsresult
+Preferences::GetLocalizedString(const char* aPref, nsAString* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ nsCOMPtr<nsIPrefLocalizedString> prefLocalString;
+ nsresult rv = sRootBranch->GetComplexValue(aPref,
+ NS_GET_IID(nsIPrefLocalizedString),
+ getter_AddRefs(prefLocalString));
+ if (NS_SUCCEEDED(rv)) {
+ NS_ASSERTION(prefLocalString, "Succeeded but the result is NULL");
+ prefLocalString->GetData(getter_Copies(*aResult));
+ }
+ return rv;
+}
+
+// static
+nsresult
+Preferences::GetComplex(const char* aPref, const nsIID &aType, void** aResult)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return sRootBranch->GetComplexValue(aPref, aType, aResult);
+}
+
+// static
+nsresult
+Preferences::SetCString(const char* aPref, const char* aValue)
+{
+ ENSURE_MAIN_PROCESS("Cannot SetCString from content process:", aPref);
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_SetCharPref(aPref, aValue, false);
+}
+
+// static
+nsresult
+Preferences::SetCString(const char* aPref, const nsACString &aValue)
+{
+ ENSURE_MAIN_PROCESS("Cannot SetCString from content process:", aPref);
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_SetCharPref(aPref, PromiseFlatCString(aValue).get(), false);
+}
+
+// static
+nsresult
+Preferences::SetString(const char* aPref, const char16ptr_t aValue)
+{
+ ENSURE_MAIN_PROCESS("Cannot SetString from content process:", aPref);
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_SetCharPref(aPref, NS_ConvertUTF16toUTF8(aValue).get(), false);
+}
+
+// static
+nsresult
+Preferences::SetString(const char* aPref, const nsAString &aValue)
+{
+ ENSURE_MAIN_PROCESS("Cannot SetString from content process:", aPref);
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_SetCharPref(aPref, NS_ConvertUTF16toUTF8(aValue).get(), false);
+}
+
+// static
+nsresult
+Preferences::SetBool(const char* aPref, bool aValue)
+{
+ ENSURE_MAIN_PROCESS("Cannot SetBool from content process:", aPref);
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_SetBoolPref(aPref, aValue, false);
+}
+
+// static
+nsresult
+Preferences::SetInt(const char* aPref, int32_t aValue)
+{
+ ENSURE_MAIN_PROCESS("Cannot SetInt from content process:", aPref);
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_SetIntPref(aPref, aValue, false);
+}
+
+// static
+nsresult
+Preferences::SetFloat(const char* aPref, float aValue)
+{
+ return SetCString(aPref, nsPrintfCString("%f", aValue).get());
+}
+
+// static
+nsresult
+Preferences::SetComplex(const char* aPref, const nsIID &aType,
+ nsISupports* aValue)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return sRootBranch->SetComplexValue(aPref, aType, aValue);
+}
+
+// static
+nsresult
+Preferences::ClearUser(const char* aPref)
+{
+ ENSURE_MAIN_PROCESS("Cannot ClearUser from content process:", aPref);
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_ClearUserPref(aPref);
+}
+
+// static
+bool
+Preferences::HasUserValue(const char* aPref)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), false);
+ return PREF_HasUserPref(aPref);
+}
+
+// static
+int32_t
+Preferences::GetType(const char* aPref)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), nsIPrefBranch::PREF_INVALID);
+ int32_t result;
+ return NS_SUCCEEDED(sRootBranch->GetPrefType(aPref, &result)) ?
+ result : nsIPrefBranch::PREF_INVALID;
+}
+
+// static
+nsresult
+Preferences::AddStrongObserver(nsIObserver* aObserver,
+ const char* aPref)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return sRootBranch->AddObserver(aPref, aObserver, false);
+}
+
+// static
+nsresult
+Preferences::AddWeakObserver(nsIObserver* aObserver,
+ const char* aPref)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return sRootBranch->AddObserver(aPref, aObserver, true);
+}
+
+// static
+nsresult
+Preferences::RemoveObserver(nsIObserver* aObserver,
+ const char* aPref)
+{
+ if (!sPreferences && sShutdown) {
+ return NS_OK; // Observers have been released automatically.
+ }
+ NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
+ return sRootBranch->RemoveObserver(aPref, aObserver);
+}
+
+// static
+nsresult
+Preferences::AddStrongObservers(nsIObserver* aObserver,
+ const char** aPrefs)
+{
+ for (uint32_t i = 0; aPrefs[i]; i++) {
+ nsresult rv = AddStrongObserver(aObserver, aPrefs[i]);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+}
+
+// static
+nsresult
+Preferences::AddWeakObservers(nsIObserver* aObserver,
+ const char** aPrefs)
+{
+ for (uint32_t i = 0; aPrefs[i]; i++) {
+ nsresult rv = AddWeakObserver(aObserver, aPrefs[i]);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+}
+
+// static
+nsresult
+Preferences::RemoveObservers(nsIObserver* aObserver,
+ const char** aPrefs)
+{
+ if (!sPreferences && sShutdown) {
+ return NS_OK; // Observers have been released automatically.
+ }
+ NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
+
+ for (uint32_t i = 0; aPrefs[i]; i++) {
+ nsresult rv = RemoveObserver(aObserver, aPrefs[i]);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+}
+
+// static
+nsresult
+Preferences::RegisterCallback(PrefChangedFunc aCallback,
+ const char* aPref,
+ void* aClosure,
+ MatchKind aMatchKind)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+
+ ValueObserverHashKey hashKey(aPref, aCallback, aMatchKind);
+ RefPtr<ValueObserver> observer;
+ gObserverTable->Get(&hashKey, getter_AddRefs(observer));
+ if (observer) {
+ observer->AppendClosure(aClosure);
+ return NS_OK;
+ }
+
+ observer = new ValueObserver(aPref, aCallback, aMatchKind);
+ observer->AppendClosure(aClosure);
+ nsresult rv = AddStrongObserver(observer, aPref);
+ NS_ENSURE_SUCCESS(rv, rv);
+ gObserverTable->Put(observer, observer);
+ return NS_OK;
+}
+
+// static
+nsresult
+Preferences::RegisterCallbackAndCall(PrefChangedFunc aCallback,
+ const char* aPref,
+ void* aClosure,
+ MatchKind aMatchKind)
+{
+ nsresult rv = RegisterCallback(aCallback, aPref, aClosure, aMatchKind);
+ if (NS_SUCCEEDED(rv)) {
+ (*aCallback)(aPref, aClosure);
+ }
+ return rv;
+}
+
+// static
+nsresult
+Preferences::UnregisterCallback(PrefChangedFunc aCallback,
+ const char* aPref,
+ void* aClosure,
+ MatchKind aMatchKind)
+{
+ if (!sPreferences && sShutdown) {
+ return NS_OK; // Observers have been released automatically.
+ }
+ NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
+
+ ValueObserverHashKey hashKey(aPref, aCallback, aMatchKind);
+ RefPtr<ValueObserver> observer;
+ gObserverTable->Get(&hashKey, getter_AddRefs(observer));
+ if (!observer) {
+ return NS_OK;
+ }
+
+ observer->RemoveClosure(aClosure);
+ if (observer->HasNoClosures()) {
+ // Delete the callback since its list of closures is empty.
+ gObserverTable->Remove(observer);
+ }
+ return NS_OK;
+}
+
+static void BoolVarChanged(const char* aPref, void* aClosure)
+{
+ CacheData* cache = static_cast<CacheData*>(aClosure);
+ *((bool*)cache->cacheLocation) =
+ Preferences::GetBool(aPref, cache->defaultValueBool);
+}
+
+// static
+nsresult
+Preferences::AddBoolVarCache(bool* aCache,
+ const char* aPref,
+ bool aDefault)
+{
+ NS_ASSERTION(aCache, "aCache must not be NULL");
+#ifdef DEBUG
+ AssertNotAlreadyCached("bool", aPref, aCache);
+#endif
+ *aCache = GetBool(aPref, aDefault);
+ CacheData* data = new CacheData();
+ data->cacheLocation = aCache;
+ data->defaultValueBool = aDefault;
+ gCacheData->AppendElement(data);
+ return RegisterCallback(BoolVarChanged, aPref, data, ExactMatch);
+}
+
+static void IntVarChanged(const char* aPref, void* aClosure)
+{
+ CacheData* cache = static_cast<CacheData*>(aClosure);
+ *((int32_t*)cache->cacheLocation) =
+ Preferences::GetInt(aPref, cache->defaultValueInt);
+}
+
+// static
+nsresult
+Preferences::AddIntVarCache(int32_t* aCache,
+ const char* aPref,
+ int32_t aDefault)
+{
+ NS_ASSERTION(aCache, "aCache must not be NULL");
+#ifdef DEBUG
+ AssertNotAlreadyCached("int", aPref, aCache);
+#endif
+ *aCache = Preferences::GetInt(aPref, aDefault);
+ CacheData* data = new CacheData();
+ data->cacheLocation = aCache;
+ data->defaultValueInt = aDefault;
+ gCacheData->AppendElement(data);
+ return RegisterCallback(IntVarChanged, aPref, data, ExactMatch);
+}
+
+static void UintVarChanged(const char* aPref, void* aClosure)
+{
+ CacheData* cache = static_cast<CacheData*>(aClosure);
+ *((uint32_t*)cache->cacheLocation) =
+ Preferences::GetUint(aPref, cache->defaultValueUint);
+}
+
+// static
+nsresult
+Preferences::AddUintVarCache(uint32_t* aCache,
+ const char* aPref,
+ uint32_t aDefault)
+{
+ NS_ASSERTION(aCache, "aCache must not be NULL");
+#ifdef DEBUG
+ AssertNotAlreadyCached("uint", aPref, aCache);
+#endif
+ *aCache = Preferences::GetUint(aPref, aDefault);
+ CacheData* data = new CacheData();
+ data->cacheLocation = aCache;
+ data->defaultValueUint = aDefault;
+ gCacheData->AppendElement(data);
+ return RegisterCallback(UintVarChanged, aPref, data, ExactMatch);
+}
+
+template <MemoryOrdering Order>
+static void AtomicUintVarChanged(const char* aPref, void* aClosure)
+{
+ CacheData* cache = static_cast<CacheData*>(aClosure);
+ *((Atomic<uint32_t, Order>*)cache->cacheLocation) =
+ Preferences::GetUint(aPref, cache->defaultValueUint);
+}
+
+template <MemoryOrdering Order>
+// static
+nsresult
+Preferences::AddAtomicUintVarCache(Atomic<uint32_t, Order>* aCache,
+ const char* aPref,
+ uint32_t aDefault)
+{
+ NS_ASSERTION(aCache, "aCache must not be NULL");
+#ifdef DEBUG
+ AssertNotAlreadyCached("uint", aPref, aCache);
+#endif
+ *aCache = Preferences::GetUint(aPref, aDefault);
+ CacheData* data = new CacheData();
+ data->cacheLocation = aCache;
+ data->defaultValueUint = aDefault;
+ gCacheData->AppendElement(data);
+ return RegisterCallback(AtomicUintVarChanged<Order>, aPref, data, ExactMatch);
+}
+
+// Since the definition of this template function is not in a header file,
+// we need to explicitly specify the instantiations that are required.
+// Currently only the order=Relaxed variant is needed.
+template
+nsresult Preferences::AddAtomicUintVarCache(Atomic<uint32_t,Relaxed>*,
+ const char*, uint32_t);
+
+static void FloatVarChanged(const char* aPref, void* aClosure)
+{
+ CacheData* cache = static_cast<CacheData*>(aClosure);
+ *((float*)cache->cacheLocation) =
+ Preferences::GetFloat(aPref, cache->defaultValueFloat);
+}
+
+// static
+nsresult
+Preferences::AddFloatVarCache(float* aCache,
+ const char* aPref,
+ float aDefault)
+{
+ NS_ASSERTION(aCache, "aCache must not be NULL");
+#ifdef DEBUG
+ AssertNotAlreadyCached("float", aPref, aCache);
+#endif
+ *aCache = Preferences::GetFloat(aPref, aDefault);
+ CacheData* data = new CacheData();
+ data->cacheLocation = aCache;
+ data->defaultValueFloat = aDefault;
+ gCacheData->AppendElement(data);
+ return RegisterCallback(FloatVarChanged, aPref, data, ExactMatch);
+}
+
+// static
+nsresult
+Preferences::GetDefaultBool(const char* aPref, bool* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_GetBoolPref(aPref, aResult, true);
+}
+
+// static
+nsresult
+Preferences::GetDefaultInt(const char* aPref, int32_t* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return PREF_GetIntPref(aPref, aResult, true);
+}
+
+// static
+nsresult
+Preferences::GetDefaultCString(const char* aPref, nsACString* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ nsAutoCString result;
+ nsresult rv = PREF_CopyCharPref(aPref, getter_Copies(result), true);
+ if (NS_SUCCEEDED(rv)) {
+ *aResult = result;
+ }
+ return rv;
+}
+
+// static
+nsresult
+Preferences::GetDefaultString(const char* aPref, nsAString* aResult)
+{
+ NS_PRECONDITION(aResult, "aResult must not be NULL");
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ nsAutoCString result;
+ nsresult rv = PREF_CopyCharPref(aPref, getter_Copies(result), true);
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF8toUTF16(result, *aResult);
+ }
+ return rv;
+}
+
+// static
+nsresult
+Preferences::GetDefaultLocalizedCString(const char* aPref,
+ nsACString* aResult)
+{
+ nsAutoString result;
+ nsresult rv = GetDefaultLocalizedString(aPref, &result);
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF16toUTF8(result, *aResult);
+ }
+ return rv;
+}
+
+// static
+nsresult
+Preferences::GetDefaultLocalizedString(const char* aPref,
+ nsAString* aResult)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ nsCOMPtr<nsIPrefLocalizedString> prefLocalString;
+ nsresult rv =
+ sDefaultRootBranch->GetComplexValue(aPref,
+ NS_GET_IID(nsIPrefLocalizedString),
+ getter_AddRefs(prefLocalString));
+ if (NS_SUCCEEDED(rv)) {
+ NS_ASSERTION(prefLocalString, "Succeeded but the result is NULL");
+ prefLocalString->GetData(getter_Copies(*aResult));
+ }
+ return rv;
+}
+
+// static
+nsAdoptingString
+Preferences::GetDefaultString(const char* aPref)
+{
+ nsAdoptingString result;
+ GetDefaultString(aPref, &result);
+ return result;
+}
+
+// static
+nsAdoptingCString
+Preferences::GetDefaultCString(const char* aPref)
+{
+ nsAdoptingCString result;
+ PREF_CopyCharPref(aPref, getter_Copies(result), true);
+ return result;
+}
+
+// static
+nsAdoptingString
+Preferences::GetDefaultLocalizedString(const char* aPref)
+{
+ nsAdoptingString result;
+ GetDefaultLocalizedString(aPref, &result);
+ return result;
+}
+
+// static
+nsAdoptingCString
+Preferences::GetDefaultLocalizedCString(const char* aPref)
+{
+ nsAdoptingCString result;
+ GetDefaultLocalizedCString(aPref, &result);
+ return result;
+}
+
+// static
+nsresult
+Preferences::GetDefaultComplex(const char* aPref, const nsIID &aType,
+ void** aResult)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
+ return sDefaultRootBranch->GetComplexValue(aPref, aType, aResult);
+}
+
+// static
+int32_t
+Preferences::GetDefaultType(const char* aPref)
+{
+ NS_ENSURE_TRUE(InitStaticMembers(), nsIPrefBranch::PREF_INVALID);
+ int32_t result;
+ return NS_SUCCEEDED(sDefaultRootBranch->GetPrefType(aPref, &result)) ?
+ result : nsIPrefBranch::PREF_INVALID;
+}
+
+} // namespace mozilla
+
+#undef ENSURE_MAIN_PROCESS
diff --git a/components/preferences/src/Preferences.h b/components/preferences/src/Preferences.h
new file mode 100644
index 000000000..255d2a8d2
--- /dev/null
+++ b/components/preferences/src/Preferences.h
@@ -0,0 +1,408 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Preferences_h
+#define mozilla_Preferences_h
+
+#ifndef MOZILLA_INTERNAL_API
+#error "This header is only usable from within libxul (MOZILLA_INTERNAL_API)."
+#endif
+
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefBranchInternal.h"
+#include "nsIObserver.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsWeakReference.h"
+#include "mozilla/MemoryReporting.h"
+
+class nsIFile;
+class nsAdoptingString;
+class nsAdoptingCString;
+
+#ifndef have_PrefChangedFunc_typedef
+typedef void (*PrefChangedFunc)(const char *, void *);
+#define have_PrefChangedFunc_typedef
+#endif
+
+namespace mozilla {
+
+namespace dom {
+class PrefSetting;
+} // namespace dom
+
+class Preferences final : public nsIPrefService,
+ public nsIObserver,
+ public nsIPrefBranchInternal,
+ public nsSupportsWeakReference
+{
+public:
+ typedef mozilla::dom::PrefSetting PrefSetting;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIPREFSERVICE
+ NS_FORWARD_NSIPREFBRANCH(sRootBranch->)
+ NS_DECL_NSIOBSERVER
+
+ Preferences();
+
+ nsresult Init();
+
+ /**
+ * Returns true if the Preferences service is available, false otherwise.
+ */
+ static bool IsServiceAvailable();
+
+ /**
+ * Reset loaded user prefs then read them
+ */
+ static nsresult ResetAndReadUserPrefs();
+
+ /**
+ * Returns the singleton instance which is addreffed.
+ */
+ static Preferences* GetInstanceForService();
+
+ /**
+ * Finallizes global members.
+ */
+ static void Shutdown();
+
+ /**
+ * Returns shared pref service instance
+ * NOTE: not addreffed.
+ */
+ static nsIPrefService* GetService()
+ {
+ NS_ENSURE_TRUE(InitStaticMembers(), nullptr);
+ return sPreferences;
+ }
+
+ /**
+ * Returns shared pref branch instance.
+ * NOTE: not addreffed.
+ */
+ static nsIPrefBranch* GetRootBranch()
+ {
+ NS_ENSURE_TRUE(InitStaticMembers(), nullptr);
+ return sRootBranch;
+ }
+
+ /**
+ * Returns shared default pref branch instance.
+ * NOTE: not addreffed.
+ */
+ static nsIPrefBranch* GetDefaultRootBranch()
+ {
+ NS_ENSURE_TRUE(InitStaticMembers(), nullptr);
+ return sDefaultRootBranch;
+ }
+
+ /**
+ * Gets int or bool type pref value with default value if failed to get
+ * the pref.
+ */
+ static bool GetBool(const char* aPref, bool aDefault = false)
+ {
+ bool result = aDefault;
+ GetBool(aPref, &result);
+ return result;
+ }
+
+ static int32_t GetInt(const char* aPref, int32_t aDefault = 0)
+ {
+ int32_t result = aDefault;
+ GetInt(aPref, &result);
+ return result;
+ }
+
+ static uint32_t GetUint(const char* aPref, uint32_t aDefault = 0)
+ {
+ uint32_t result = aDefault;
+ GetUint(aPref, &result);
+ return result;
+ }
+
+ static float GetFloat(const char* aPref, float aDefault = 0)
+ {
+ float result = aDefault;
+ GetFloat(aPref, &result);
+ return result;
+ }
+
+ /**
+ * Gets char type pref value directly. If failed, the get() of result
+ * returns nullptr. Even if succeeded but the result was empty string, the
+ * get() does NOT return nullptr. So, you can check whether the method
+ * succeeded or not by:
+ *
+ * nsAdoptingString value = Prefereces::GetString("foo.bar");
+ * if (!value) {
+ * // failed
+ * }
+ *
+ * Be aware. If you wrote as:
+ *
+ * nsAutoString value = Preferences::GetString("foo.bar");
+ * if (!value.get()) {
+ * // the condition is always FALSE!!
+ * }
+ *
+ * The value.get() doesn't return nullptr. You must use nsAdoptingString
+ * when you need to check whether it was failure or not.
+ */
+ static nsAdoptingCString GetCString(const char* aPref);
+ static nsAdoptingString GetString(const char* aPref);
+ static nsAdoptingCString GetLocalizedCString(const char* aPref);
+ static nsAdoptingString GetLocalizedString(const char* aPref);
+
+ /**
+ * Gets int, float, or bool type pref value with raw return value of
+ * nsIPrefBranch.
+ *
+ * @param aPref A pref name.
+ * @param aResult Must not be nullptr. The value is never modified
+ * when these methods fail.
+ */
+ static nsresult GetBool(const char* aPref, bool* aResult);
+ static nsresult GetInt(const char* aPref, int32_t* aResult);
+ static nsresult GetFloat(const char* aPref, float* aResult);
+ static nsresult GetUint(const char* aPref, uint32_t* aResult)
+ {
+ int32_t result;
+ nsresult rv = GetInt(aPref, &result);
+ if (NS_SUCCEEDED(rv)) {
+ *aResult = static_cast<uint32_t>(result);
+ }
+ return rv;
+ }
+
+ /**
+ * Gets string type pref value with raw return value of nsIPrefBranch.
+ *
+ * @param aPref A pref name.
+ * @param aResult Must not be nullptr. The value is never modified
+ * when these methods fail.
+ */
+ static nsresult GetCString(const char* aPref, nsACString* aResult);
+ static nsresult GetString(const char* aPref, nsAString* aResult);
+ static nsresult GetLocalizedCString(const char* aPref, nsACString* aResult);
+ static nsresult GetLocalizedString(const char* aPref, nsAString* aResult);
+
+ static nsresult GetComplex(const char* aPref, const nsIID &aType,
+ void** aResult);
+
+ /**
+ * Sets various type pref values.
+ */
+ static nsresult SetBool(const char* aPref, bool aValue);
+ static nsresult SetInt(const char* aPref, int32_t aValue);
+ static nsresult SetUint(const char* aPref, uint32_t aValue)
+ {
+ return SetInt(aPref, static_cast<int32_t>(aValue));
+ }
+ static nsresult SetFloat(const char* aPref, float aValue);
+ static nsresult SetCString(const char* aPref, const char* aValue);
+ static nsresult SetCString(const char* aPref, const nsACString &aValue);
+ static nsresult SetString(const char* aPref, const char16ptr_t aValue);
+ static nsresult SetString(const char* aPref, const nsAString &aValue);
+
+ static nsresult SetComplex(const char* aPref, const nsIID &aType,
+ nsISupports* aValue);
+
+ /**
+ * Clears user set pref.
+ */
+ static nsresult ClearUser(const char* aPref);
+
+ /**
+ * Whether the pref has a user value or not.
+ */
+ static bool HasUserValue(const char* aPref);
+
+ /**
+ * Gets the type of the pref.
+ */
+ static int32_t GetType(const char* aPref);
+
+ /**
+ * Adds/Removes the observer for the root pref branch.
+ * The observer is referenced strongly if AddStrongObserver is used. On the
+ * other hand, it is referenced weakly, if AddWeakObserver is used.
+ * See nsIPrefBranch.idl for details.
+ */
+ static nsresult AddStrongObserver(nsIObserver* aObserver, const char* aPref);
+ static nsresult AddWeakObserver(nsIObserver* aObserver, const char* aPref);
+ static nsresult RemoveObserver(nsIObserver* aObserver, const char* aPref);
+
+ /**
+ * Adds/Removes two or more observers for the root pref branch.
+ * Pass to aPrefs an array of const char* whose last item is nullptr.
+ */
+ static nsresult AddStrongObservers(nsIObserver* aObserver,
+ const char** aPrefs);
+ static nsresult AddWeakObservers(nsIObserver* aObserver,
+ const char** aPrefs);
+ static nsresult RemoveObservers(nsIObserver* aObserver,
+ const char** aPrefs);
+
+ /**
+ * Registers/Unregisters the callback function for the aPref.
+ *
+ * Pass ExactMatch for aMatchKind to only get callbacks for
+ * exact matches and not prefixes.
+ */
+ enum MatchKind {
+ PrefixMatch,
+ ExactMatch,
+ };
+ static nsresult RegisterCallback(PrefChangedFunc aCallback,
+ const char* aPref,
+ void* aClosure = nullptr,
+ MatchKind aMatchKind = PrefixMatch);
+ static nsresult UnregisterCallback(PrefChangedFunc aCallback,
+ const char* aPref,
+ void* aClosure = nullptr,
+ MatchKind aMatchKind = PrefixMatch);
+ // Like RegisterCallback, but also calls the callback immediately for
+ // initialization.
+ static nsresult RegisterCallbackAndCall(PrefChangedFunc aCallback,
+ const char* aPref,
+ void* aClosure = nullptr,
+ MatchKind aMatchKind = PrefixMatch);
+
+ /**
+ * Adds the aVariable to cache table. aVariable must be a pointer for a
+ * static variable. The value will be modified when the pref value is
+ * changed but note that even if you modified it, the value isn't assigned to
+ * the pref.
+ */
+ static nsresult AddBoolVarCache(bool* aVariable,
+ const char* aPref,
+ bool aDefault = false);
+ static nsresult AddIntVarCache(int32_t* aVariable,
+ const char* aPref,
+ int32_t aDefault = 0);
+ static nsresult AddUintVarCache(uint32_t* aVariable,
+ const char* aPref,
+ uint32_t aDefault = 0);
+ template <MemoryOrdering Order>
+ static nsresult AddAtomicUintVarCache(Atomic<uint32_t, Order>* aVariable,
+ const char* aPref,
+ uint32_t aDefault = 0);
+ static nsresult AddFloatVarCache(float* aVariable,
+ const char* aPref,
+ float aDefault = 0.0f);
+
+ /**
+ * Gets the default bool, int or uint value of the pref.
+ * The result is raw result of nsIPrefBranch::Get*Pref().
+ * If the pref could have any value, you needed to use these methods.
+ * If not so, you could use below methods.
+ */
+ static nsresult GetDefaultBool(const char* aPref, bool* aResult);
+ static nsresult GetDefaultInt(const char* aPref, int32_t* aResult);
+ static nsresult GetDefaultUint(const char* aPref, uint32_t* aResult)
+ {
+ return GetDefaultInt(aPref, reinterpret_cast<int32_t*>(aResult));
+ }
+
+ /**
+ * Gets the default bool, int or uint value of the pref directly.
+ * You can set an invalid value of the pref to aFailedResult. If these
+ * methods failed to get the default value, they would return the
+ * aFailedResult value.
+ */
+ static bool GetDefaultBool(const char* aPref, bool aFailedResult)
+ {
+ bool result;
+ return NS_SUCCEEDED(GetDefaultBool(aPref, &result)) ? result :
+ aFailedResult;
+ }
+ static int32_t GetDefaultInt(const char* aPref, int32_t aFailedResult)
+ {
+ int32_t result;
+ return NS_SUCCEEDED(GetDefaultInt(aPref, &result)) ? result : aFailedResult;
+ }
+ static uint32_t GetDefaultUint(const char* aPref, uint32_t aFailedResult)
+ {
+ return static_cast<uint32_t>(
+ GetDefaultInt(aPref, static_cast<int32_t>(aFailedResult)));
+ }
+
+ /**
+ * Gets the default value of the char type pref.
+ * If the get() of the result returned nullptr, that meant the value didn't
+ * have default value.
+ *
+ * See the comment at definition at GetString() and GetCString() for more
+ * details of the result.
+ */
+ static nsAdoptingString GetDefaultString(const char* aPref);
+ static nsAdoptingCString GetDefaultCString(const char* aPref);
+ static nsAdoptingString GetDefaultLocalizedString(const char* aPref);
+ static nsAdoptingCString GetDefaultLocalizedCString(const char* aPref);
+
+ static nsresult GetDefaultCString(const char* aPref, nsACString* aResult);
+ static nsresult GetDefaultString(const char* aPref, nsAString* aResult);
+ static nsresult GetDefaultLocalizedCString(const char* aPref,
+ nsACString* aResult);
+ static nsresult GetDefaultLocalizedString(const char* aPref,
+ nsAString* aResult);
+
+ static nsresult GetDefaultComplex(const char* aPref, const nsIID &aType,
+ void** aResult);
+
+ /**
+ * Gets the type of the pref.
+ */
+ static int32_t GetDefaultType(const char* aPref);
+
+ // Used to synchronise preferences between chrome and content processes.
+ static void GetPreferences(InfallibleTArray<PrefSetting>* aPrefs);
+ static void GetPreference(PrefSetting* aPref);
+ static void SetPreference(const PrefSetting& aPref);
+
+ static int64_t SizeOfIncludingThisAndOtherStuff(mozilla::MallocSizeOf aMallocSizeOf);
+
+ static void DirtyCallback();
+
+protected:
+ virtual ~Preferences();
+
+ nsresult NotifyServiceObservers(const char *aSubject);
+ /**
+ * Reads the default pref file or, if that failed, try to save a new one.
+ *
+ * @return NS_OK if either action succeeded,
+ * or the error code related to the read attempt.
+ */
+ nsresult UseDefaultPrefFile();
+ nsresult UseUserPrefFile();
+ nsresult ReadAndOwnUserPrefFile(nsIFile *aFile);
+ nsresult ReadAndOwnSharedUserPrefFile(nsIFile *aFile);
+ nsresult SavePrefFileInternal(nsIFile* aFile);
+ nsresult WritePrefFile(nsIFile* aFile);
+ nsresult MakeBackupPrefFile(nsIFile *aFile);
+
+private:
+ nsCOMPtr<nsIFile> mCurrentFile;
+ bool mDirty;
+
+ static Preferences* sPreferences;
+ static nsIPrefBranch* sRootBranch;
+ static nsIPrefBranch* sDefaultRootBranch;
+ static bool sShutdown;
+
+ /**
+ * Init static members. TRUE if it succeeded. Otherwise, FALSE.
+ */
+ static bool InitStaticMembers();
+};
+
+} // namespace mozilla
+
+#endif // mozilla_Preferences_h
diff --git a/components/preferences/src/nsPrefBranch.cpp b/components/preferences/src/nsPrefBranch.cpp
new file mode 100644
index 000000000..6107e64d8
--- /dev/null
+++ b/components/preferences/src/nsPrefBranch.cpp
@@ -0,0 +1,943 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ContentChild.h"
+#include "nsXULAppAPI.h"
+
+#include "nsPrefBranch.h"
+#include "nsILocalFile.h" // nsILocalFile used for backwards compatibility
+#include "nsIObserverService.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIDirectoryService.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsXPIDLString.h"
+#include "nsPrintfCString.h"
+#include "nsIStringBundle.h"
+#include "prefapi.h"
+#include "PLDHashTable.h"
+
+#include "nsCRT.h"
+#include "mozilla/Services.h"
+
+#include "prefapi_private_data.h"
+
+#include "nsIConsoleService.h"
+
+#ifdef DEBUG
+#define ENSURE_MAIN_PROCESS(message, pref) do { \
+ if (GetContentChild()) { \
+ nsPrintfCString msg("ENSURE_MAIN_PROCESS failed. %s %s", message, pref); \
+ NS_ERROR(msg.get()); \
+ return NS_ERROR_NOT_AVAILABLE; \
+ } \
+} while (0);
+#else
+#define ENSURE_MAIN_PROCESS(message, pref) \
+ if (GetContentChild()) { \
+ return NS_ERROR_NOT_AVAILABLE; \
+ }
+#endif
+
+using mozilla::dom::ContentChild;
+
+static ContentChild*
+GetContentChild()
+{
+ if (XRE_IsContentProcess()) {
+ ContentChild* cpc = ContentChild::GetSingleton();
+ if (!cpc) {
+ NS_RUNTIMEABORT("Content Protocol is NULL! We're going to crash!");
+ }
+ return cpc;
+ }
+ return nullptr;
+}
+
+/*
+ * Constructor/Destructor
+ */
+
+nsPrefBranch::nsPrefBranch(const char *aPrefRoot, bool aDefaultBranch)
+{
+ mPrefRoot = aPrefRoot;
+ mPrefRootLength = mPrefRoot.Length();
+ mIsDefault = aDefaultBranch;
+ mFreeingObserverList = false;
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ ++mRefCnt; // Our refcnt must be > 0 when we call this, or we'll get deleted!
+ // add weak so we don't have to clean up at shutdown
+ observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
+ --mRefCnt;
+ }
+}
+
+nsPrefBranch::~nsPrefBranch()
+{
+ freeObserverList();
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService)
+ observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+}
+
+
+/*
+ * nsISupports Implementation
+ */
+
+NS_IMPL_ADDREF(nsPrefBranch)
+NS_IMPL_RELEASE(nsPrefBranch)
+
+NS_INTERFACE_MAP_BEGIN(nsPrefBranch)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrefBranch)
+ NS_INTERFACE_MAP_ENTRY(nsIPrefBranch)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIPrefBranch2, !mIsDefault)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIPrefBranchInternal, !mIsDefault)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+
+/*
+ * nsIPrefBranch Implementation
+ */
+
+NS_IMETHODIMP nsPrefBranch::GetRoot(char **aRoot)
+{
+ NS_ENSURE_ARG_POINTER(aRoot);
+ mPrefRoot.Truncate(mPrefRootLength);
+ *aRoot = ToNewCString(mPrefRoot);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrefBranch::GetPrefType(const char *aPrefName, int32_t *_retval)
+{
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ switch (PREF_GetPrefType(pref)) {
+ case PrefType::String:
+ *_retval = PREF_STRING;
+ break;
+ case PrefType::Int:
+ *_retval = PREF_INT;
+ break;
+ case PrefType::Bool:
+ *_retval = PREF_BOOL;
+ break;
+ case PrefType::Invalid:
+ default:
+ *_retval = PREF_INVALID;
+ break;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrefBranch::GetBoolPrefWithDefault(const char *aPrefName,
+ bool aDefaultValue,
+ uint8_t _argc, bool *_retval)
+{
+ nsresult rv = GetBoolPref(aPrefName, _retval);
+
+ if (NS_FAILED(rv) && _argc == 1) {
+ *_retval = aDefaultValue;
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPrefBranch::GetBoolPref(const char *aPrefName, bool *_retval)
+{
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ return PREF_GetBoolPref(pref, _retval, mIsDefault);
+}
+
+NS_IMETHODIMP nsPrefBranch::SetBoolPref(const char *aPrefName, bool aValue)
+{
+ ENSURE_MAIN_PROCESS("Cannot SetBoolPref from content process:", aPrefName);
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ return PREF_SetBoolPref(pref, aValue, mIsDefault);
+}
+
+NS_IMETHODIMP nsPrefBranch::GetFloatPrefWithDefault(const char *aPrefName,
+ float aDefaultValue,
+ uint8_t _argc, float *_retval)
+{
+ nsresult rv = GetFloatPref(aPrefName, _retval);
+
+ if (NS_FAILED(rv) && _argc == 1) {
+ *_retval = aDefaultValue;
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPrefBranch::GetFloatPref(const char *aPrefName, float *_retval)
+{
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ nsAutoCString stringVal;
+ nsresult rv = GetCharPref(pref, getter_Copies(stringVal));
+ if (NS_SUCCEEDED(rv)) {
+ *_retval = stringVal.ToFloat(&rv);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPrefBranch::GetCharPrefWithDefault(const char *aPrefName,
+ const char *aDefaultValue,
+ uint8_t _argc, char **_retval)
+{
+ nsresult rv = GetCharPref(aPrefName, _retval);
+
+ if (NS_FAILED(rv) && _argc == 1) {
+ NS_ENSURE_ARG(aDefaultValue);
+ *_retval = NS_strdup(aDefaultValue);
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPrefBranch::GetCharPref(const char *aPrefName, char **_retval)
+{
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ return PREF_CopyCharPref(pref, _retval, mIsDefault);
+}
+
+NS_IMETHODIMP nsPrefBranch::SetCharPref(const char *aPrefName, const char *aValue)
+{
+ nsresult rv = CheckSanityOfStringLength(aPrefName, aValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return SetCharPrefInternal(aPrefName, aValue);
+}
+
+nsresult nsPrefBranch::SetCharPrefInternal(const char *aPrefName, const char *aValue)
+
+{
+ ENSURE_MAIN_PROCESS("Cannot SetCharPref from content process:", aPrefName);
+ NS_ENSURE_ARG(aPrefName);
+ NS_ENSURE_ARG(aValue);
+ const char *pref = getPrefName(aPrefName);
+ return PREF_SetCharPref(pref, aValue, mIsDefault);
+}
+
+NS_IMETHODIMP nsPrefBranch::GetIntPrefWithDefault(const char *aPrefName,
+ int32_t aDefaultValue,
+ uint8_t _argc, int32_t *_retval)
+{
+ nsresult rv = GetIntPref(aPrefName, _retval);
+
+ if (NS_FAILED(rv) && _argc == 1) {
+ *_retval = aDefaultValue;
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPrefBranch::GetIntPref(const char *aPrefName, int32_t *_retval)
+{
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ return PREF_GetIntPref(pref, _retval, mIsDefault);
+}
+
+NS_IMETHODIMP nsPrefBranch::SetIntPref(const char *aPrefName, int32_t aValue)
+{
+ ENSURE_MAIN_PROCESS("Cannot SetIntPref from content process:", aPrefName);
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ return PREF_SetIntPref(pref, aValue, mIsDefault);
+}
+
+NS_IMETHODIMP nsPrefBranch::GetComplexValue(const char *aPrefName, const nsIID & aType, void **_retval)
+{
+ NS_ENSURE_ARG(aPrefName);
+
+ nsresult rv;
+ nsXPIDLCString utf8String;
+
+ // we have to do this one first because it's different than all the rest
+ if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
+ nsCOMPtr<nsIPrefLocalizedString> theString(do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) return rv;
+
+ const char *pref = getPrefName(aPrefName);
+ bool bNeedDefault = false;
+
+ if (mIsDefault) {
+ bNeedDefault = true;
+ } else {
+ // if there is no user (or locked) value
+ if (!PREF_HasUserPref(pref) && !PREF_PrefIsLocked(pref)) {
+ bNeedDefault = true;
+ }
+ }
+
+ // if we need to fetch the default value, do that instead, otherwise use the
+ // value we pulled in at the top of this function
+ if (bNeedDefault) {
+ nsXPIDLString utf16String;
+ rv = GetDefaultFromPropertiesFile(pref, getter_Copies(utf16String));
+ if (NS_SUCCEEDED(rv)) {
+ theString->SetData(utf16String.get());
+ }
+ } else {
+ rv = GetCharPref(aPrefName, getter_Copies(utf8String));
+ if (NS_SUCCEEDED(rv)) {
+ theString->SetData(NS_ConvertUTF8toUTF16(utf8String).get());
+ }
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ theString.forget(reinterpret_cast<nsIPrefLocalizedString**>(_retval));
+ }
+
+ return rv;
+ }
+
+ // if we can't get the pref, there's no point in being here
+ rv = GetCharPref(aPrefName, getter_Copies(utf8String));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // also check nsILocalFile, for backwards compatibility
+ if (aType.Equals(NS_GET_IID(nsIFile)) || aType.Equals(NS_GET_IID(nsILocalFile))) {
+ if (GetContentChild()) {
+ NS_ERROR("cannot get nsIFile pref from content process");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+
+ if (NS_SUCCEEDED(rv)) {
+ rv = file->SetPersistentDescriptor(utf8String);
+ if (NS_SUCCEEDED(rv)) {
+ file.forget(reinterpret_cast<nsIFile**>(_retval));
+ return NS_OK;
+ }
+ }
+ return rv;
+ }
+
+ if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
+ if (GetContentChild()) {
+ NS_ERROR("cannot get nsIRelativeFilePref from content process");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsACString::const_iterator keyBegin, strEnd;
+ utf8String.BeginReading(keyBegin);
+ utf8String.EndReading(strEnd);
+
+ // The pref has the format: [fromKey]a/b/c
+ if (*keyBegin++ != '[')
+ return NS_ERROR_FAILURE;
+ nsACString::const_iterator keyEnd(keyBegin);
+ if (!FindCharInReadable(']', keyEnd, strEnd))
+ return NS_ERROR_FAILURE;
+ nsAutoCString key(Substring(keyBegin, keyEnd));
+
+ nsCOMPtr<nsIFile> fromFile;
+ nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+ rv = directoryService->Get(key.get(), NS_GET_IID(nsIFile), getter_AddRefs(fromFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIFile> theFile;
+ rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(theFile));
+ if (NS_FAILED(rv))
+ return rv;
+ rv = theFile->SetRelativeDescriptor(fromFile, Substring(++keyEnd, strEnd));
+ if (NS_FAILED(rv))
+ return rv;
+ nsCOMPtr<nsIRelativeFilePref> relativePref;
+ rv = NS_NewRelativeFilePref(theFile, key, getter_AddRefs(relativePref));
+ if (NS_FAILED(rv))
+ return rv;
+
+ relativePref.forget(reinterpret_cast<nsIRelativeFilePref**>(_retval));
+ return NS_OK;
+ }
+
+ if (aType.Equals(NS_GET_IID(nsISupportsString))) {
+ nsCOMPtr<nsISupportsString> theString(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
+
+ if (NS_SUCCEEDED(rv)) {
+ // Debugging to see why we end up with very long strings here with
+ // some addons, see bug 836263.
+ nsAutoString wdata;
+ if (!AppendUTF8toUTF16(utf8String, wdata, mozilla::fallible)) {
+ NS_RUNTIMEABORT("bug836263");
+ }
+ theString->SetData(wdata);
+ theString.forget(reinterpret_cast<nsISupportsString**>(_retval));
+ }
+ return rv;
+ }
+
+ NS_WARNING("nsPrefBranch::GetComplexValue - Unsupported interface type");
+ return NS_NOINTERFACE;
+}
+
+nsresult nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName, const char* aValue) {
+ if (!aValue) {
+ return NS_OK;
+ }
+ return CheckSanityOfStringLength(aPrefName, strlen(aValue));
+}
+
+nsresult nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName, const nsAString& aValue) {
+ return CheckSanityOfStringLength(aPrefName, aValue.Length());
+}
+
+nsresult nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName, const uint32_t aLength) {
+ if (aLength > MAX_PREF_LENGTH) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ if (aLength <= MAX_ADVISABLE_PREF_LENGTH) {
+ return NS_OK;
+ }
+ nsresult rv;
+ nsCOMPtr<nsIConsoleService> console = do_GetService("@mozilla.org/consoleservice;1", &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ nsAutoCString message(nsPrintfCString("Warning: attempting to write %d bytes to preference %s. This is bad "
+ "for general performance and memory usage. Such an amount of data "
+ "should rather be written to an external file. This preference will "
+ "not be sent to any content processes.",
+ aLength,
+ getPrefName(aPrefName)));
+ rv = console->LogStringMessage(NS_ConvertUTF8toUTF16(message).get());
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+/*static*/
+void nsPrefBranch::ReportToConsole(const nsAString& aMessage)
+{
+ nsresult rv;
+ nsCOMPtr<nsIConsoleService> console = do_GetService("@mozilla.org/consoleservice;1", &rv);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ nsAutoString message(aMessage);
+ console->LogStringMessage(message.get());
+}
+
+NS_IMETHODIMP nsPrefBranch::SetComplexValue(const char *aPrefName, const nsIID & aType, nsISupports *aValue)
+{
+ ENSURE_MAIN_PROCESS("Cannot SetComplexValue from content process:", aPrefName);
+ NS_ENSURE_ARG(aPrefName);
+
+ nsresult rv = NS_NOINTERFACE;
+
+ // also check nsILocalFile, for backwards compatibility
+ if (aType.Equals(NS_GET_IID(nsIFile)) || aType.Equals(NS_GET_IID(nsILocalFile))) {
+ nsCOMPtr<nsIFile> file = do_QueryInterface(aValue);
+ if (!file)
+ return NS_NOINTERFACE;
+ nsAutoCString descriptorString;
+
+ rv = file->GetPersistentDescriptor(descriptorString);
+ if (NS_SUCCEEDED(rv)) {
+ rv = SetCharPrefInternal(aPrefName, descriptorString.get());
+ }
+ return rv;
+ }
+
+ if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
+ nsCOMPtr<nsIRelativeFilePref> relFilePref = do_QueryInterface(aValue);
+ if (!relFilePref)
+ return NS_NOINTERFACE;
+
+ nsCOMPtr<nsIFile> file;
+ relFilePref->GetFile(getter_AddRefs(file));
+ if (!file)
+ return NS_NOINTERFACE;
+ nsAutoCString relativeToKey;
+ (void) relFilePref->GetRelativeToKey(relativeToKey);
+
+ nsCOMPtr<nsIFile> relativeToFile;
+ nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+ rv = directoryService->Get(relativeToKey.get(), NS_GET_IID(nsIFile), getter_AddRefs(relativeToFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString relDescriptor;
+ rv = file->GetRelativeDescriptor(relativeToFile, relDescriptor);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString descriptorString;
+ descriptorString.Append('[');
+ descriptorString.Append(relativeToKey);
+ descriptorString.Append(']');
+ descriptorString.Append(relDescriptor);
+ return SetCharPrefInternal(aPrefName, descriptorString.get());
+ }
+
+ if (aType.Equals(NS_GET_IID(nsISupportsString))) {
+ nsCOMPtr<nsISupportsString> theString = do_QueryInterface(aValue);
+
+ if (theString) {
+ nsString wideString;
+
+ rv = theString->GetData(wideString);
+ if (NS_SUCCEEDED(rv)) {
+ // Check sanity of string length before any lengthy conversion
+ rv = CheckSanityOfStringLength(aPrefName, wideString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = SetCharPrefInternal(aPrefName, NS_ConvertUTF16toUTF8(wideString).get());
+ }
+ }
+ return rv;
+ }
+
+ if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
+ nsCOMPtr<nsIPrefLocalizedString> theString = do_QueryInterface(aValue);
+
+ if (theString) {
+ nsXPIDLString wideString;
+
+ rv = theString->GetData(getter_Copies(wideString));
+ if (NS_SUCCEEDED(rv)) {
+ // Check sanity of string length before any lengthy conversion
+ rv = CheckSanityOfStringLength(aPrefName, wideString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = SetCharPrefInternal(aPrefName, NS_ConvertUTF16toUTF8(wideString).get());
+ }
+ }
+ return rv;
+ }
+
+ NS_WARNING("nsPrefBranch::SetComplexValue - Unsupported interface type");
+ return NS_NOINTERFACE;
+}
+
+NS_IMETHODIMP nsPrefBranch::ClearUserPref(const char *aPrefName)
+{
+ ENSURE_MAIN_PROCESS("Cannot ClearUserPref from content process:", aPrefName);
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ return PREF_ClearUserPref(pref);
+}
+
+NS_IMETHODIMP nsPrefBranch::PrefHasUserValue(const char *aPrefName, bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ *_retval = PREF_HasUserPref(pref);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrefBranch::LockPref(const char *aPrefName)
+{
+ ENSURE_MAIN_PROCESS("Cannot LockPref from content process:", aPrefName);
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ return PREF_LockPref(pref, true);
+}
+
+NS_IMETHODIMP nsPrefBranch::PrefIsLocked(const char *aPrefName, bool *_retval)
+{
+ ENSURE_MAIN_PROCESS("Cannot check PrefIsLocked from content process:", aPrefName);
+ NS_ENSURE_ARG_POINTER(_retval);
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ *_retval = PREF_PrefIsLocked(pref);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrefBranch::UnlockPref(const char *aPrefName)
+{
+ ENSURE_MAIN_PROCESS("Cannot UnlockPref from content process:", aPrefName);
+ NS_ENSURE_ARG(aPrefName);
+ const char *pref = getPrefName(aPrefName);
+ return PREF_LockPref(pref, false);
+}
+
+NS_IMETHODIMP nsPrefBranch::ResetBranch(const char *aStartingAt)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsPrefBranch::DeleteBranch(const char *aStartingAt)
+{
+ ENSURE_MAIN_PROCESS("Cannot DeleteBranch from content process:", aStartingAt);
+ NS_ENSURE_ARG(aStartingAt);
+ const char *pref = getPrefName(aStartingAt);
+ return PREF_DeleteBranch(pref);
+}
+
+NS_IMETHODIMP nsPrefBranch::GetChildList(const char *aStartingAt, uint32_t *aCount, char ***aChildArray)
+{
+ char **outArray;
+ int32_t numPrefs;
+ int32_t dwIndex;
+ AutoTArray<nsCString, 32> prefArray;
+
+ NS_ENSURE_ARG(aStartingAt);
+ NS_ENSURE_ARG_POINTER(aCount);
+ NS_ENSURE_ARG_POINTER(aChildArray);
+
+ *aChildArray = nullptr;
+ *aCount = 0;
+
+ // this will contain a list of all the pref name strings
+ // allocate on the stack for speed
+
+ const char* parent = getPrefName(aStartingAt);
+ size_t parentLen = strlen(parent);
+ for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<PrefHashEntry*>(iter.Get());
+ if (strncmp(entry->key, parent, parentLen) == 0) {
+ prefArray.AppendElement(entry->key);
+ }
+ }
+
+ // now that we've built up the list, run the callback on
+ // all the matching elements
+ numPrefs = prefArray.Length();
+
+ if (numPrefs) {
+ outArray = (char **)moz_xmalloc(numPrefs * sizeof(char *));
+ if (!outArray)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ for (dwIndex = 0; dwIndex < numPrefs; ++dwIndex) {
+ // we need to lop off mPrefRoot in case the user is planning to pass this
+ // back to us because if they do we are going to add mPrefRoot again.
+ const nsCString& element = prefArray[dwIndex];
+ outArray[dwIndex] = (char *)nsMemory::Clone(
+ element.get() + mPrefRootLength, element.Length() - mPrefRootLength + 1);
+
+ if (!outArray[dwIndex]) {
+ // we ran out of memory... this is annoying
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(dwIndex, outArray);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ *aChildArray = outArray;
+ }
+ *aCount = numPrefs;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrefBranch::AddObserver(const char *aDomain, nsIObserver *aObserver, bool aHoldWeak)
+{
+ PrefCallback *pCallback;
+ const char *pref;
+
+ NS_ENSURE_ARG(aDomain);
+ NS_ENSURE_ARG(aObserver);
+
+ // hold a weak reference to the observer if so requested
+ if (aHoldWeak) {
+ nsCOMPtr<nsISupportsWeakReference> weakRefFactory = do_QueryInterface(aObserver);
+ if (!weakRefFactory) {
+ // the caller didn't give us a object that supports weak reference... tell them
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Construct a PrefCallback with a weak reference to the observer.
+ pCallback = new PrefCallback(aDomain, weakRefFactory, this);
+
+ } else {
+ // Construct a PrefCallback with a strong reference to the observer.
+ pCallback = new PrefCallback(aDomain, aObserver, this);
+ }
+
+ if (mObservers.Get(pCallback)) {
+ NS_WARNING("Ignoring duplicate observer.");
+ delete pCallback;
+ return NS_OK;
+ }
+
+ mObservers.Put(pCallback, pCallback);
+
+ // We must pass a fully qualified preference name to the callback
+ // aDomain == nullptr is the only possible failure, and we trapped it with
+ // NS_ENSURE_ARG above.
+ pref = getPrefName(aDomain);
+ PREF_RegisterCallback(pref, NotifyObserver, pCallback);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrefBranch::RemoveObserver(const char *aDomain, nsIObserver *aObserver)
+{
+ NS_ENSURE_ARG(aDomain);
+ NS_ENSURE_ARG(aObserver);
+
+ nsresult rv = NS_OK;
+
+ // If we're in the middle of a call to freeObserverList, don't process this
+ // RemoveObserver call -- the observer in question will be removed soon, if
+ // it hasn't been already.
+ //
+ // It's important that we don't touch mObservers in any way -- even a Get()
+ // which returns null might cause the hashtable to resize itself, which will
+ // break the iteration in freeObserverList.
+ if (mFreeingObserverList)
+ return NS_OK;
+
+ // Remove the relevant PrefCallback from mObservers and get an owning
+ // pointer to it. Unregister the callback first, and then let the owning
+ // pointer go out of scope and destroy the callback.
+ PrefCallback key(aDomain, aObserver, this);
+ nsAutoPtr<PrefCallback> pCallback;
+ mObservers.RemoveAndForget(&key, pCallback);
+ if (pCallback) {
+ // aDomain == nullptr is the only possible failure, trapped above
+ const char *pref = getPrefName(aDomain);
+ rv = PREF_UnregisterCallback(pref, NotifyObserver, pCallback);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPrefBranch::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
+{
+ // watch for xpcom shutdown and free our observers to eliminate any cyclic references
+ if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ freeObserverList();
+ }
+ return NS_OK;
+}
+
+/* static */
+void nsPrefBranch::NotifyObserver(const char *newpref, void *data)
+{
+ PrefCallback *pCallback = (PrefCallback *)data;
+
+ nsCOMPtr<nsIObserver> observer = pCallback->GetObserver();
+ if (!observer) {
+ // The observer has expired. Let's remove this callback.
+ pCallback->GetPrefBranch()->RemoveExpiredCallback(pCallback);
+ return;
+ }
+
+ // remove any root this string may contain so as to not confuse the observer
+ // by passing them something other than what they passed us as a topic
+ uint32_t len = pCallback->GetPrefBranch()->GetRootLength();
+ nsAutoCString suffix(newpref + len);
+
+ observer->Observe(static_cast<nsIPrefBranch *>(pCallback->GetPrefBranch()),
+ NS_PREFBRANCH_PREFCHANGE_TOPIC_ID,
+ NS_ConvertASCIItoUTF16(suffix).get());
+}
+
+size_t
+nsPrefBranch::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+ n += mPrefRoot.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ return n;
+}
+
+void nsPrefBranch::freeObserverList(void)
+{
+ // We need to prevent anyone from modifying mObservers while we're iterating
+ // over it. In particular, some clients will call RemoveObserver() when
+ // they're removed and destructed via the iterator; we set
+ // mFreeingObserverList to keep those calls from touching mObservers.
+ mFreeingObserverList = true;
+ for (auto iter = mObservers.Iter(); !iter.Done(); iter.Next()) {
+ nsAutoPtr<PrefCallback>& callback = iter.Data();
+ nsPrefBranch *prefBranch = callback->GetPrefBranch();
+ const char *pref = prefBranch->getPrefName(callback->GetDomain().get());
+ PREF_UnregisterCallback(pref, nsPrefBranch::NotifyObserver, callback);
+ iter.Remove();
+ }
+ mFreeingObserverList = false;
+}
+
+void
+nsPrefBranch::RemoveExpiredCallback(PrefCallback *aCallback)
+{
+ NS_PRECONDITION(aCallback->IsExpired(), "Callback should be expired.");
+ mObservers.Remove(aCallback);
+}
+
+nsresult nsPrefBranch::GetDefaultFromPropertiesFile(const char *aPrefName, char16_t **return_buf)
+{
+ nsresult rv;
+
+ // the default value contains a URL to a .properties file
+
+ nsXPIDLCString propertyFileURL;
+ rv = PREF_CopyCharPref(aPrefName, getter_Copies(propertyFileURL), true);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ if (!bundleService)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle(propertyFileURL,
+ getter_AddRefs(bundle));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // string names are in unicode
+ nsAutoString stringId;
+ stringId.AssignASCII(aPrefName);
+
+ return bundle->GetStringFromName(stringId.get(), return_buf);
+}
+
+const char *nsPrefBranch::getPrefName(const char *aPrefName)
+{
+ NS_ASSERTION(aPrefName, "null pref name!");
+
+ // for speed, avoid strcpy if we can:
+ if (mPrefRoot.IsEmpty())
+ return aPrefName;
+
+ // isn't there a better way to do this? this is really kind of gross.
+ mPrefRoot.Truncate(mPrefRootLength);
+ mPrefRoot.Append(aPrefName);
+ return mPrefRoot.get();
+}
+
+//----------------------------------------------------------------------------
+// nsPrefLocalizedString
+//----------------------------------------------------------------------------
+
+nsPrefLocalizedString::nsPrefLocalizedString()
+{
+}
+
+nsPrefLocalizedString::~nsPrefLocalizedString()
+{
+}
+
+
+/*
+ * nsISupports Implementation
+ */
+
+NS_IMPL_ADDREF(nsPrefLocalizedString)
+NS_IMPL_RELEASE(nsPrefLocalizedString)
+
+NS_INTERFACE_MAP_BEGIN(nsPrefLocalizedString)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrefLocalizedString)
+ NS_INTERFACE_MAP_ENTRY(nsIPrefLocalizedString)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsString)
+NS_INTERFACE_MAP_END
+
+nsresult nsPrefLocalizedString::Init()
+{
+ nsresult rv;
+ mUnicodeString = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsPrefLocalizedString::GetData(char16_t **_retval)
+{
+ nsAutoString data;
+
+ nsresult rv = GetData(data);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = ToNewUnicode(data);
+ if (!*_retval)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrefLocalizedString::SetData(const char16_t *aData)
+{
+ if (!aData)
+ return SetData(EmptyString());
+ return SetData(nsDependentString(aData));
+}
+
+NS_IMETHODIMP
+nsPrefLocalizedString::SetDataWithLength(uint32_t aLength,
+ const char16_t *aData)
+{
+ if (!aData)
+ return SetData(EmptyString());
+ return SetData(Substring(aData, aLength));
+}
+
+//----------------------------------------------------------------------------
+// nsRelativeFilePref
+//----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(nsRelativeFilePref, nsIRelativeFilePref)
+
+nsRelativeFilePref::nsRelativeFilePref()
+{
+}
+
+nsRelativeFilePref::~nsRelativeFilePref()
+{
+}
+
+NS_IMETHODIMP nsRelativeFilePref::GetFile(nsIFile **aFile)
+{
+ NS_ENSURE_ARG_POINTER(aFile);
+ *aFile = mFile;
+ NS_IF_ADDREF(*aFile);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRelativeFilePref::SetFile(nsIFile *aFile)
+{
+ mFile = aFile;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRelativeFilePref::GetRelativeToKey(nsACString& aRelativeToKey)
+{
+ aRelativeToKey.Assign(mRelativeToKey);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRelativeFilePref::SetRelativeToKey(const nsACString& aRelativeToKey)
+{
+ mRelativeToKey.Assign(aRelativeToKey);
+ return NS_OK;
+}
+
+#undef ENSURE_MAIN_PROCESS
diff --git a/components/preferences/src/nsPrefBranch.h b/components/preferences/src/nsPrefBranch.h
new file mode 100644
index 000000000..37cf5c2c4
--- /dev/null
+++ b/components/preferences/src/nsPrefBranch.h
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsPrefBranch_h
+#define nsPrefBranch_h
+
+#include "nsCOMPtr.h"
+#include "nsIObserver.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefBranchInternal.h"
+#include "nsIPrefLocalizedString.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIRelativeFilePref.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsWeakReference.h"
+#include "nsClassHashtable.h"
+#include "nsCRT.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/MemoryReporting.h"
+
+namespace mozilla {
+class PreferenceServiceReporter;
+} // namespace mozilla
+
+class nsPrefBranch;
+
+class PrefCallback : public PLDHashEntryHdr {
+ friend class mozilla::PreferenceServiceReporter;
+
+ public:
+ typedef PrefCallback* KeyType;
+ typedef const PrefCallback* KeyTypePointer;
+
+ static const PrefCallback* KeyToPointer(PrefCallback *aKey)
+ {
+ return aKey;
+ }
+
+ static PLDHashNumber HashKey(const PrefCallback *aKey)
+ {
+ uint32_t hash = mozilla::HashString(aKey->mDomain);
+ return mozilla::AddToHash(hash, aKey->mCanonical);
+ }
+
+
+ public:
+ // Create a PrefCallback with a strong reference to its observer.
+ PrefCallback(const char *aDomain, nsIObserver *aObserver,
+ nsPrefBranch *aBranch)
+ : mDomain(aDomain),
+ mBranch(aBranch),
+ mWeakRef(nullptr),
+ mStrongRef(aObserver)
+ {
+ MOZ_COUNT_CTOR(PrefCallback);
+ nsCOMPtr<nsISupports> canonical = do_QueryInterface(aObserver);
+ mCanonical = canonical;
+ }
+
+ // Create a PrefCallback with a weak reference to its observer.
+ PrefCallback(const char *aDomain,
+ nsISupportsWeakReference *aObserver,
+ nsPrefBranch *aBranch)
+ : mDomain(aDomain),
+ mBranch(aBranch),
+ mWeakRef(do_GetWeakReference(aObserver)),
+ mStrongRef(nullptr)
+ {
+ MOZ_COUNT_CTOR(PrefCallback);
+ nsCOMPtr<nsISupports> canonical = do_QueryInterface(aObserver);
+ mCanonical = canonical;
+ }
+
+ // Copy constructor needs to be explicit or the linker complains.
+ explicit PrefCallback(const PrefCallback *&aCopy)
+ : mDomain(aCopy->mDomain),
+ mBranch(aCopy->mBranch),
+ mWeakRef(aCopy->mWeakRef),
+ mStrongRef(aCopy->mStrongRef),
+ mCanonical(aCopy->mCanonical)
+ {
+ MOZ_COUNT_CTOR(PrefCallback);
+ }
+
+ ~PrefCallback()
+ {
+ MOZ_COUNT_DTOR(PrefCallback);
+ }
+
+ bool KeyEquals(const PrefCallback *aKey) const
+ {
+ // We want to be able to look up a weakly-referencing PrefCallback after
+ // its observer has died so we can remove it from the table. Once the
+ // callback's observer dies, its canonical pointer is stale -- in
+ // particular, we may have allocated a new observer in the same spot in
+ // memory! So we can't just compare canonical pointers to determine
+ // whether aKey refers to the same observer as this.
+ //
+ // Our workaround is based on the way we use this hashtable: When we ask
+ // the hashtable to remove a PrefCallback whose weak reference has
+ // expired, we use as the key for removal the same object as was inserted
+ // into the hashtable. Thus we can say that if one of the keys' weak
+ // references has expired, the two keys are equal iff they're the same
+ // object.
+
+ if (IsExpired() || aKey->IsExpired())
+ return this == aKey;
+
+ if (mCanonical != aKey->mCanonical)
+ return false;
+
+ return mDomain.Equals(aKey->mDomain);
+ }
+
+ PrefCallback *GetKey() const
+ {
+ return const_cast<PrefCallback*>(this);
+ }
+
+ // Get a reference to the callback's observer, or null if the observer was
+ // weakly referenced and has been destroyed.
+ already_AddRefed<nsIObserver> GetObserver() const
+ {
+ if (!IsWeak()) {
+ nsCOMPtr<nsIObserver> copy = mStrongRef;
+ return copy.forget();
+ }
+
+ nsCOMPtr<nsIObserver> observer = do_QueryReferent(mWeakRef);
+ return observer.forget();
+ }
+
+ const nsCString& GetDomain() const
+ {
+ return mDomain;
+ }
+
+ nsPrefBranch* GetPrefBranch() const
+ {
+ return mBranch;
+ }
+
+ // Has this callback's weak reference died?
+ bool IsExpired() const
+ {
+ if (!IsWeak())
+ return false;
+
+ nsCOMPtr<nsIObserver> observer(do_QueryReferent(mWeakRef));
+ return !observer;
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+ private:
+ nsCString mDomain;
+ nsPrefBranch *mBranch;
+
+ // Exactly one of mWeakRef and mStrongRef should be non-null.
+ nsWeakPtr mWeakRef;
+ nsCOMPtr<nsIObserver> mStrongRef;
+
+ // We need a canonical nsISupports pointer, per bug 578392.
+ nsISupports *mCanonical;
+
+ bool IsWeak() const
+ {
+ return !!mWeakRef;
+ }
+};
+
+class nsPrefBranch final : public nsIPrefBranchInternal,
+ public nsIObserver,
+ public nsSupportsWeakReference
+{
+ friend class mozilla::PreferenceServiceReporter;
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPREFBRANCH
+ NS_DECL_NSIPREFBRANCH2
+ NS_DECL_NSIOBSERVER
+
+ nsPrefBranch(const char *aPrefRoot, bool aDefaultBranch);
+
+ int32_t GetRootLength() { return mPrefRootLength; }
+
+ nsresult RemoveObserverFromMap(const char *aDomain, nsISupports *aObserver);
+
+ static void NotifyObserver(const char *newpref, void *data);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+ static void ReportToConsole(const nsAString& aMessage);
+
+protected:
+ virtual ~nsPrefBranch();
+
+ nsPrefBranch() /* disallow use of this constructer */
+ : mPrefRootLength(0)
+ , mIsDefault(false)
+ , mFreeingObserverList(false)
+ {}
+
+ nsresult GetDefaultFromPropertiesFile(const char *aPrefName, char16_t **return_buf);
+ // As SetCharPref, but without any check on the length of |aValue|
+ nsresult SetCharPrefInternal(const char *aPrefName, const char *aValue);
+ // Reject strings that are more than 1Mb, warn if strings are more than 16kb
+ nsresult CheckSanityOfStringLength(const char* aPrefName, const nsAString& aValue);
+ nsresult CheckSanityOfStringLength(const char* aPrefName, const char* aValue);
+ nsresult CheckSanityOfStringLength(const char* aPrefName, const uint32_t aLength);
+ void RemoveExpiredCallback(PrefCallback *aCallback);
+ const char *getPrefName(const char *aPrefName);
+ void freeObserverList(void);
+
+private:
+ int32_t mPrefRootLength;
+ nsCString mPrefRoot;
+ bool mIsDefault;
+
+ bool mFreeingObserverList;
+ nsClassHashtable<PrefCallback, PrefCallback> mObservers;
+};
+
+
+class nsPrefLocalizedString final : public nsIPrefLocalizedString,
+ public nsISupportsString
+{
+public:
+ nsPrefLocalizedString();
+
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_NSISUPPORTSSTRING(mUnicodeString->)
+ NS_FORWARD_NSISUPPORTSPRIMITIVE(mUnicodeString->)
+
+ nsresult Init();
+
+private:
+ virtual ~nsPrefLocalizedString();
+
+ NS_IMETHOD GetData(char16_t**) override;
+ NS_IMETHOD SetData(const char16_t* aData) override;
+ NS_IMETHOD SetDataWithLength(uint32_t aLength, const char16_t *aData) override;
+
+ nsCOMPtr<nsISupportsString> mUnicodeString;
+};
+
+
+class nsRelativeFilePref : public nsIRelativeFilePref
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIRELATIVEFILEPREF
+
+ nsRelativeFilePref();
+
+private:
+ virtual ~nsRelativeFilePref();
+
+ nsCOMPtr<nsIFile> mFile;
+ nsCString mRelativeToKey;
+};
+
+#endif
diff --git a/components/preferences/src/nsPrefsFactory.cpp b/components/preferences/src/nsPrefsFactory.cpp
new file mode 100644
index 000000000..35a9885b9
--- /dev/null
+++ b/components/preferences/src/nsPrefsFactory.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsPrefBranch.h"
+#include "prefapi.h"
+
+using namespace mozilla;
+
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(Preferences,
+ Preferences::GetInstanceForService)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrefLocalizedString, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsRelativeFilePref)
+
+static NS_DEFINE_CID(kPrefServiceCID, NS_PREFSERVICE_CID);
+static NS_DEFINE_CID(kPrefLocalizedStringCID, NS_PREFLOCALIZEDSTRING_CID);
+static NS_DEFINE_CID(kRelativeFilePrefCID, NS_RELATIVEFILEPREF_CID);
+
+static mozilla::Module::CIDEntry kPrefCIDs[] = {
+ { &kPrefServiceCID, true, nullptr, PreferencesConstructor },
+ { &kPrefLocalizedStringCID, false, nullptr, nsPrefLocalizedStringConstructor },
+ { &kRelativeFilePrefCID, false, nullptr, nsRelativeFilePrefConstructor },
+ { nullptr }
+};
+
+static mozilla::Module::ContractIDEntry kPrefContracts[] = {
+ { NS_PREFSERVICE_CONTRACTID, &kPrefServiceCID },
+ { NS_PREFLOCALIZEDSTRING_CONTRACTID, &kPrefLocalizedStringCID },
+ { NS_RELATIVEFILEPREF_CONTRACTID, &kRelativeFilePrefCID },
+ // compatibility for extension that uses old service
+ { "@mozilla.org/preferences;1", &kPrefServiceCID },
+ { nullptr }
+};
+
+static void
+UnloadPrefsModule()
+{
+ Preferences::Shutdown();
+}
+
+static const mozilla::Module kPrefModule = {
+ mozilla::Module::kVersion,
+ kPrefCIDs,
+ kPrefContracts,
+ nullptr,
+ nullptr,
+ nullptr,
+ UnloadPrefsModule
+};
+
+NSMODULE_DEFN(nsPrefModule) = &kPrefModule;
diff --git a/components/preferences/src/prefapi.cpp b/components/preferences/src/prefapi.cpp
new file mode 100644
index 000000000..8aaf4077b
--- /dev/null
+++ b/components/preferences/src/prefapi.cpp
@@ -0,0 +1,1005 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+#include "prefapi.h"
+#include "prefapi_private_data.h"
+#include "prefread.h"
+#include "MainThreadUtils.h"
+#include "nsReadableUtils.h"
+#include "nsCRT.h"
+
+#define PL_ARENA_CONST_ALIGN_MASK 3
+#include "plarena.h"
+
+#ifdef _WIN32
+ #include "windows.h"
+#endif /* _WIN32 */
+
+#include "plstr.h"
+#include "PLDHashTable.h"
+#include "plbase64.h"
+#include "mozilla/Logging.h"
+#include "prprf.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/PContent.h"
+#include "nsQuickSort.h"
+#include "nsString.h"
+#include "nsPrintfCString.h"
+#include "prlink.h"
+
+using namespace mozilla;
+
+static void
+clearPrefEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
+{
+ PrefHashEntry *pref = static_cast<PrefHashEntry *>(entry);
+ if (pref->prefFlags.IsTypeString())
+ {
+ if (pref->defaultPref.stringVal)
+ PL_strfree(pref->defaultPref.stringVal);
+ if (pref->userPref.stringVal)
+ PL_strfree(pref->userPref.stringVal);
+ }
+ // don't need to free this as it's allocated in memory owned by
+ // gPrefNameArena
+ pref->key = nullptr;
+ memset(entry, 0, table->EntrySize());
+}
+
+static bool
+matchPrefEntry(const PLDHashEntryHdr* entry, const void* key)
+{
+ const PrefHashEntry *prefEntry =
+ static_cast<const PrefHashEntry*>(entry);
+
+ if (prefEntry->key == key) return true;
+
+ if (!prefEntry->key || !key) return false;
+
+ const char *otherKey = reinterpret_cast<const char*>(key);
+ return (strcmp(prefEntry->key, otherKey) == 0);
+}
+
+PLDHashTable* gHashTable;
+static PLArenaPool gPrefNameArena;
+
+static struct CallbackNode* gCallbacks = nullptr;
+static bool gIsAnyPrefLocked = false;
+// These are only used during the call to pref_DoCallback
+static bool gCallbacksInProgress = false;
+static bool gShouldCleanupDeadNodes = false;
+
+
+static PLDHashTableOps pref_HashTableOps = {
+ PLDHashTable::HashStringKey,
+ matchPrefEntry,
+ PLDHashTable::MoveEntryStub,
+ clearPrefEntry,
+ nullptr,
+};
+
+// PR_ALIGN_OF_WORD is only defined on some platforms. ALIGN_OF_WORD has
+// already been defined to PR_ALIGN_OF_WORD everywhere
+#ifndef PR_ALIGN_OF_WORD
+#define PR_ALIGN_OF_WORD PR_ALIGN_OF_POINTER
+#endif
+
+// making PrefName arena 8k for nice allocation
+#define PREFNAME_ARENA_SIZE 8192
+
+#define WORD_ALIGN_MASK (PR_ALIGN_OF_WORD - 1)
+
+// sanity checking
+#if (PR_ALIGN_OF_WORD & WORD_ALIGN_MASK) != 0
+#error "PR_ALIGN_OF_WORD must be a power of 2!"
+#endif
+
+// equivalent to strdup() - does no error checking,
+// we're assuming we're only called with a valid pointer
+static char *ArenaStrDup(const char* str, PLArenaPool* aArena)
+{
+ void* mem;
+ uint32_t len = strlen(str);
+ PL_ARENA_ALLOCATE(mem, aArena, len+1);
+ if (mem)
+ memcpy(mem, str, len+1);
+ return static_cast<char*>(mem);
+}
+
+static PrefsDirtyFunc gDirtyCallback = nullptr;
+
+inline void MakeDirtyCallback()
+{
+ // Right now the callback function is always set, so we don't need
+ // to complicate the code to cover the scenario where we set the callback
+ // after we've already tried to make it dirty. If this assert triggers
+ // we will add that code.
+ MOZ_ASSERT(gDirtyCallback);
+ if (gDirtyCallback) {
+ gDirtyCallback();
+ }
+}
+
+void PREF_SetDirtyCallback(PrefsDirtyFunc aFunc)
+{
+ gDirtyCallback = aFunc;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type);
+/* -- Privates */
+struct CallbackNode {
+ char* domain;
+ // If someone attempts to remove the node from the callback list while
+ // pref_DoCallback is running, |func| is set to nullptr. Such nodes will
+ // be removed at the end of pref_DoCallback.
+ PrefChangedFunc func;
+ void* data;
+ struct CallbackNode* next;
+};
+
+/* -- Prototypes */
+static nsresult pref_DoCallback(const char* changed_pref);
+
+enum {
+ kPrefSetDefault = 1,
+ kPrefForceSet = 2,
+ kPrefStickyDefault = 4,
+};
+static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags);
+
+#define PREF_HASHTABLE_INITIAL_LENGTH 1024
+
+void PREF_Init()
+{
+ if (!gHashTable) {
+ gHashTable = new PLDHashTable(&pref_HashTableOps,
+ sizeof(PrefHashEntry),
+ PREF_HASHTABLE_INITIAL_LENGTH);
+
+ PL_INIT_ARENA_POOL(&gPrefNameArena, "PrefNameArena",
+ PREFNAME_ARENA_SIZE);
+ }
+}
+
+/* Frees the callback list. */
+void PREF_Cleanup()
+{
+ NS_ASSERTION(!gCallbacksInProgress,
+ "PREF_Cleanup was called while gCallbacksInProgress is true!");
+ struct CallbackNode* node = gCallbacks;
+ struct CallbackNode* next_node;
+
+ while (node)
+ {
+ next_node = node->next;
+ PL_strfree(node->domain);
+ free(node);
+ node = next_node;
+ }
+ gCallbacks = nullptr;
+
+ PREF_CleanupPrefs();
+}
+
+/* Frees up all the objects except the callback list. */
+void PREF_CleanupPrefs()
+{
+ if (gHashTable) {
+ delete gHashTable;
+ gHashTable = nullptr;
+ PL_FinishArenaPool(&gPrefNameArena);
+ }
+}
+
+// note that this appends to aResult, and does not assign!
+static void str_escape(const char * original, nsAFlatCString& aResult)
+{
+ /* JavaScript does not allow quotes, slashes, or line terminators inside
+ * strings so we must escape them. ECMAScript defines four line
+ * terminators, but we're only worrying about \r and \n here. We currently
+ * feed our pref script to the JS interpreter as Latin-1 so we won't
+ * encounter \u2028 (line separator) or \u2029 (paragraph separator).
+ *
+ * WARNING: There are hints that we may be moving to storing prefs
+ * as utf8. If we ever feed them to the JS compiler as UTF8 then
+ * we'll have to worry about the multibyte sequences that would be
+ * interpreted as \u2028 and \u2029
+ */
+ const char *p;
+
+ if (original == nullptr)
+ return;
+
+ /* Paranoid worst case all slashes will free quickly */
+ for (p=original; *p; ++p)
+ {
+ switch (*p)
+ {
+ case '\n':
+ aResult.AppendLiteral("\\n");
+ break;
+
+ case '\r':
+ aResult.AppendLiteral("\\r");
+ break;
+
+ case '\\':
+ aResult.AppendLiteral("\\\\");
+ break;
+
+ case '\"':
+ aResult.AppendLiteral("\\\"");
+ break;
+
+ default:
+ aResult.Append(*p);
+ break;
+ }
+ }
+}
+
+/*
+** External calls
+*/
+nsresult
+PREF_SetCharPref(const char *pref_name, const char *value, bool set_default)
+{
+ if ((uint32_t)strlen(value) > MAX_PREF_LENGTH) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ PrefValue pref;
+ pref.stringVal = (char*)value;
+
+ return pref_HashPref(pref_name, pref, PrefType::String, set_default ? kPrefSetDefault : 0);
+}
+
+nsresult
+PREF_SetIntPref(const char *pref_name, int32_t value, bool set_default)
+{
+ PrefValue pref;
+ pref.intVal = value;
+
+ return pref_HashPref(pref_name, pref, PrefType::Int, set_default ? kPrefSetDefault : 0);
+}
+
+nsresult
+PREF_SetBoolPref(const char *pref_name, bool value, bool set_default)
+{
+ PrefValue pref;
+ pref.boolVal = value;
+
+ return pref_HashPref(pref_name, pref, PrefType::Bool, set_default ? kPrefSetDefault : 0);
+}
+
+enum WhichValue { DEFAULT_VALUE, USER_VALUE };
+static nsresult
+SetPrefValue(const char* aPrefName, const dom::PrefValue& aValue,
+ WhichValue aWhich)
+{
+ bool setDefault = (aWhich == DEFAULT_VALUE);
+ switch (aValue.type()) {
+ case dom::PrefValue::TnsCString:
+ return PREF_SetCharPref(aPrefName, aValue.get_nsCString().get(),
+ setDefault);
+ case dom::PrefValue::Tint32_t:
+ return PREF_SetIntPref(aPrefName, aValue.get_int32_t(),
+ setDefault);
+ case dom::PrefValue::Tbool:
+ return PREF_SetBoolPref(aPrefName, aValue.get_bool(),
+ setDefault);
+ default:
+ MOZ_CRASH();
+ }
+}
+
+nsresult
+pref_SetPref(const dom::PrefSetting& aPref)
+{
+ const char* prefName = aPref.name().get();
+ const dom::MaybePrefValue& defaultValue = aPref.defaultValue();
+ const dom::MaybePrefValue& userValue = aPref.userValue();
+
+ nsresult rv;
+ if (defaultValue.type() == dom::MaybePrefValue::TPrefValue) {
+ rv = SetPrefValue(prefName, defaultValue.get_PrefValue(), DEFAULT_VALUE);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ if (userValue.type() == dom::MaybePrefValue::TPrefValue) {
+ rv = SetPrefValue(prefName, userValue.get_PrefValue(), USER_VALUE);
+ } else {
+ rv = PREF_ClearUserPref(prefName);
+ }
+
+ // NB: we should never try to clear a default value, that doesn't
+ // make sense
+
+ return rv;
+}
+
+UniquePtr<char*[]>
+pref_savePrefs(PLDHashTable* aTable, uint32_t* aPrefCount)
+{
+ // This function allocates the entries in the savedPrefs array it returns.
+ // It is the callers responsibility to go through the array and free
+ // all of them. The aPrefCount entries will be non-null. Any end padding
+ // is an implementation detail and may change.
+ MOZ_ASSERT(aPrefCount);
+ auto savedPrefs = MakeUnique<char*[]>(aTable->EntryCount());
+
+ // This is not necessary, but leaving it in for now
+ memset(savedPrefs.get(), 0, aTable->EntryCount() * sizeof(char*));
+
+ int32_t j = 0;
+ for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
+ auto pref = static_cast<PrefHashEntry*>(iter.Get());
+
+ nsAutoCString prefValue;
+ nsAutoCString prefPrefix;
+ prefPrefix.AssignLiteral("user_pref(\"");
+
+ // where we're getting our pref from
+ PrefValue* sourcePref;
+
+ if (pref->prefFlags.HasUserValue() &&
+ (pref_ValueChanged(pref->defaultPref,
+ pref->userPref,
+ pref->prefFlags.GetPrefType()) ||
+ !(pref->prefFlags.HasDefault()) ||
+ pref->prefFlags.HasStickyDefault())) {
+ sourcePref = &pref->userPref;
+ } else {
+ // do not save default prefs that haven't changed
+ continue;
+ }
+
+ // strings are in quotes!
+ if (pref->prefFlags.IsTypeString()) {
+ prefValue = '\"';
+ str_escape(sourcePref->stringVal, prefValue);
+ prefValue += '\"';
+
+ } else if (pref->prefFlags.IsTypeInt()) {
+ prefValue.AppendInt(sourcePref->intVal);
+
+ } else if (pref->prefFlags.IsTypeBool()) {
+ prefValue = (sourcePref->boolVal) ? "true" : "false";
+ }
+
+ nsAutoCString prefName;
+ str_escape(pref->key, prefName);
+
+ savedPrefs[j++] = ToNewCString(prefPrefix +
+ prefName +
+ NS_LITERAL_CSTRING("\", ") +
+ prefValue +
+ NS_LITERAL_CSTRING(");"));
+ }
+ *aPrefCount = j;
+
+ return savedPrefs;
+}
+
+bool
+pref_EntryHasAdvisablySizedValues(PrefHashEntry* aHashEntry)
+{
+ if (aHashEntry->prefFlags.GetPrefType() != PrefType::String) {
+ return true;
+ }
+
+ char* stringVal;
+ if (aHashEntry->prefFlags.HasDefault()) {
+ stringVal = aHashEntry->defaultPref.stringVal;
+ if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
+ return false;
+ }
+ }
+
+ if (aHashEntry->prefFlags.HasUserValue()) {
+ stringVal = aHashEntry->userPref.stringVal;
+ if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void
+GetPrefValueFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref,
+ WhichValue aWhich)
+{
+ PrefValue* value;
+ dom::PrefValue* settingValue;
+ if (aWhich == USER_VALUE) {
+ value = &aHashEntry->userPref;
+ aPref->userValue() = dom::PrefValue();
+ settingValue = &aPref->userValue().get_PrefValue();
+ } else {
+ value = &aHashEntry->defaultPref;
+ aPref->defaultValue() = dom::PrefValue();
+ settingValue = &aPref->defaultValue().get_PrefValue();
+ }
+
+ switch (aHashEntry->prefFlags.GetPrefType()) {
+ case PrefType::String:
+ *settingValue = nsDependentCString(value->stringVal);
+ return;
+ case PrefType::Int:
+ *settingValue = value->intVal;
+ return;
+ case PrefType::Bool:
+ *settingValue = !!value->boolVal;
+ return;
+ default:
+ MOZ_CRASH();
+ }
+}
+
+void
+pref_GetPrefFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref)
+{
+ aPref->name() = aHashEntry->key;
+ if (aHashEntry->prefFlags.HasDefault()) {
+ GetPrefValueFromEntry(aHashEntry, aPref, DEFAULT_VALUE);
+ } else {
+ aPref->defaultValue() = null_t();
+ }
+ if (aHashEntry->prefFlags.HasUserValue()) {
+ GetPrefValueFromEntry(aHashEntry, aPref, USER_VALUE);
+ } else {
+ aPref->userValue() = null_t();
+ }
+
+ MOZ_ASSERT(aPref->defaultValue().type() == dom::MaybePrefValue::Tnull_t ||
+ aPref->userValue().type() == dom::MaybePrefValue::Tnull_t ||
+ (aPref->defaultValue().get_PrefValue().type() ==
+ aPref->userValue().get_PrefValue().type()));
+}
+
+
+int
+pref_CompareStrings(const void *v1, const void *v2, void *unused)
+{
+ char *s1 = *(char**) v1;
+ char *s2 = *(char**) v2;
+
+ if (!s1)
+ {
+ if (!s2)
+ return 0;
+ else
+ return -1;
+ }
+ else if (!s2)
+ return 1;
+ else
+ return strcmp(s1, s2);
+}
+
+bool PREF_HasUserPref(const char *pref_name)
+{
+ if (!gHashTable)
+ return false;
+
+ PrefHashEntry *pref = pref_HashTableLookup(pref_name);
+ return pref && pref->prefFlags.HasUserValue();
+}
+
+nsresult
+PREF_CopyCharPref(const char *pref_name, char ** return_buffer, bool get_default)
+{
+ if (!gHashTable)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ char* stringVal;
+ PrefHashEntry* pref = pref_HashTableLookup(pref_name);
+
+ if (pref && (pref->prefFlags.IsTypeString())) {
+ if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
+ stringVal = pref->defaultPref.stringVal;
+ } else {
+ stringVal = pref->userPref.stringVal;
+ }
+
+ if (stringVal) {
+ *return_buffer = NS_strdup(stringVal);
+ rv = NS_OK;
+ }
+ }
+ return rv;
+}
+
+nsresult PREF_GetIntPref(const char *pref_name,int32_t * return_int, bool get_default)
+{
+ if (!gHashTable)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ PrefHashEntry* pref = pref_HashTableLookup(pref_name);
+ if (pref && (pref->prefFlags.IsTypeInt())) {
+ if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
+ int32_t tempInt = pref->defaultPref.intVal;
+ /* check to see if we even had a default */
+ if (!pref->prefFlags.HasDefault()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ *return_int = tempInt;
+ } else {
+ *return_int = pref->userPref.intVal;
+ }
+ rv = NS_OK;
+ }
+ return rv;
+}
+
+nsresult PREF_GetBoolPref(const char *pref_name, bool * return_value, bool get_default)
+{
+ if (!gHashTable)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ PrefHashEntry* pref = pref_HashTableLookup(pref_name);
+ //NS_ASSERTION(pref, pref_name);
+ if (pref && (pref->prefFlags.IsTypeBool())) {
+ if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
+ bool tempBool = pref->defaultPref.boolVal;
+ /* check to see if we even had a default */
+ if (pref->prefFlags.HasDefault()) {
+ *return_value = tempBool;
+ rv = NS_OK;
+ }
+ } else {
+ *return_value = pref->userPref.boolVal;
+ rv = NS_OK;
+ }
+ }
+ return rv;
+}
+
+nsresult
+PREF_DeleteBranch(const char *branch_name)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ int len = (int)strlen(branch_name);
+
+ if (!gHashTable)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ /* The following check insures that if the branch name already has a "."
+ * at the end, we don't end up with a "..". This fixes an incompatibility
+ * between nsIPref, which needs the period added, and nsIPrefBranch which
+ * does not. When nsIPref goes away this function should be fixed to
+ * never add the period at all.
+ */
+ nsAutoCString branch_dot(branch_name);
+ if ((len > 1) && branch_name[len - 1] != '.')
+ branch_dot += '.';
+
+ /* Delete a branch. Used for deleting mime types */
+ const char *to_delete = branch_dot.get();
+ MOZ_ASSERT(to_delete);
+ len = strlen(to_delete);
+ for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<PrefHashEntry*>(iter.Get());
+
+ /* note if we're deleting "ldap" then we want to delete "ldap.xxx"
+ and "ldap" (if such a leaf node exists) but not "ldap_1.xxx" */
+ if (PL_strncmp(entry->key, to_delete, (uint32_t) len) == 0 ||
+ (len-1 == (int)strlen(entry->key) &&
+ PL_strncmp(entry->key, to_delete, (uint32_t)(len-1)) == 0)) {
+ iter.Remove();
+ }
+ }
+
+ MakeDirtyCallback();
+ return NS_OK;
+}
+
+
+nsresult
+PREF_ClearUserPref(const char *pref_name)
+{
+ if (!gHashTable)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ PrefHashEntry* pref = pref_HashTableLookup(pref_name);
+ if (pref && pref->prefFlags.HasUserValue()) {
+ pref->prefFlags.SetHasUserValue(false);
+
+ if (!pref->prefFlags.HasDefault()) {
+ gHashTable->RemoveEntry(pref);
+ }
+
+ pref_DoCallback(pref_name);
+ MakeDirtyCallback();
+ }
+ return NS_OK;
+}
+
+nsresult
+PREF_ClearAllUserPrefs()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!gHashTable)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ std::vector<std::string> prefStrings;
+ for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
+ auto pref = static_cast<PrefHashEntry*>(iter.Get());
+
+ if (pref->prefFlags.HasUserValue()) {
+ prefStrings.push_back(std::string(pref->key));
+
+ pref->prefFlags.SetHasUserValue(false);
+ if (!pref->prefFlags.HasDefault()) {
+ iter.Remove();
+ }
+ }
+ }
+
+ for (std::string& prefString : prefStrings) {
+ pref_DoCallback(prefString.c_str());
+ }
+
+ MakeDirtyCallback();
+ return NS_OK;
+}
+
+nsresult PREF_LockPref(const char *key, bool lockit)
+{
+ if (!gHashTable)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ PrefHashEntry* pref = pref_HashTableLookup(key);
+ if (!pref)
+ return NS_ERROR_UNEXPECTED;
+
+ if (lockit) {
+ if (!pref->prefFlags.IsLocked()) {
+ pref->prefFlags.SetLocked(true);
+ gIsAnyPrefLocked = true;
+ pref_DoCallback(key);
+ }
+ } else {
+ if (pref->prefFlags.IsLocked()) {
+ pref->prefFlags.SetLocked(false);
+ pref_DoCallback(key);
+ }
+ }
+ return NS_OK;
+}
+
+/*
+ * Hash table functions
+ */
+static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type)
+{
+ bool changed = true;
+ switch(type) {
+ case PrefType::String:
+ if (oldValue.stringVal && newValue.stringVal) {
+ changed = (strcmp(oldValue.stringVal, newValue.stringVal) != 0);
+ }
+ break;
+ case PrefType::Int:
+ changed = oldValue.intVal != newValue.intVal;
+ break;
+ case PrefType::Bool:
+ changed = oldValue.boolVal != newValue.boolVal;
+ break;
+ case PrefType::Invalid:
+ default:
+ changed = false;
+ break;
+ }
+ return changed;
+}
+
+/*
+ * Overwrite the type and value of an existing preference. Caller must
+ * ensure that they are not changing the type of a preference that has
+ * a default value.
+ */
+static PrefTypeFlags pref_SetValue(PrefValue* existingValue, PrefTypeFlags flags,
+ PrefValue newValue, PrefType newType)
+{
+ if (flags.IsTypeString() && existingValue->stringVal) {
+ PL_strfree(existingValue->stringVal);
+ }
+ flags.SetPrefType(newType);
+ if (flags.IsTypeString()) {
+ MOZ_ASSERT(newValue.stringVal);
+ existingValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : nullptr;
+ }
+ else {
+ *existingValue = newValue;
+ }
+ return flags;
+}
+
+PrefHashEntry* pref_HashTableLookup(const char *key)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return static_cast<PrefHashEntry*>(gHashTable->Search(key));
+}
+
+nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!gHashTable)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ auto pref = static_cast<PrefHashEntry*>(gHashTable->Add(key, fallible));
+ if (!pref)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // new entry, better initialize
+ if (!pref->key) {
+
+ // initialize the pref entry
+ pref->prefFlags.Reset().SetPrefType(type);
+ pref->key = ArenaStrDup(key, &gPrefNameArena);
+ memset(&pref->defaultPref, 0, sizeof(pref->defaultPref));
+ memset(&pref->userPref, 0, sizeof(pref->userPref));
+ } else if (pref->prefFlags.HasDefault() && !pref->prefFlags.IsPrefType(type)) {
+ NS_WARNING(nsPrintfCString("Trying to overwrite value of default pref %s with the wrong type!", key).get());
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ bool valueChanged = false;
+ if (flags & kPrefSetDefault) {
+ if (!pref->prefFlags.IsLocked()) {
+ /* ?? change of semantics? */
+ if (pref_ValueChanged(pref->defaultPref, value, type) ||
+ !pref->prefFlags.HasDefault()) {
+ pref->prefFlags = pref_SetValue(&pref->defaultPref, pref->prefFlags, value, type).SetHasDefault(true);
+ if (flags & kPrefStickyDefault) {
+ pref->prefFlags.SetHasStickyDefault(true);
+ }
+ if (!pref->prefFlags.HasUserValue()) {
+ valueChanged = true;
+ }
+ }
+ // What if we change the default to be the same as the user value?
+ // Should we clear the user value?
+ }
+ } else {
+ /* If new value is same as the default value and it's not a "sticky"
+ pref, then un-set the user value.
+ Otherwise, set the user value only if it has changed */
+ if ((pref->prefFlags.HasDefault()) &&
+ !(pref->prefFlags.HasStickyDefault()) &&
+ !pref_ValueChanged(pref->defaultPref, value, type) &&
+ !(flags & kPrefForceSet)) {
+ if (pref->prefFlags.HasUserValue()) {
+ /* XXX should we free a user-set string value if there is one? */
+ pref->prefFlags.SetHasUserValue(false);
+ if (!pref->prefFlags.IsLocked()) {
+ MakeDirtyCallback();
+ valueChanged = true;
+ }
+ }
+ } else if (!pref->prefFlags.HasUserValue() ||
+ !pref->prefFlags.IsPrefType(type) ||
+ pref_ValueChanged(pref->userPref, value, type) ) {
+ pref->prefFlags = pref_SetValue(&pref->userPref, pref->prefFlags, value, type).SetHasUserValue(true);
+ if (!pref->prefFlags.IsLocked()) {
+ MakeDirtyCallback();
+ valueChanged = true;
+ }
+ }
+ }
+
+ if (valueChanged) {
+ return pref_DoCallback(key);
+ }
+ return NS_OK;
+}
+
+size_t
+pref_SizeOfPrivateData(MallocSizeOf aMallocSizeOf)
+{
+ size_t n = PL_SizeOfArenaPoolExcludingPool(&gPrefNameArena, aMallocSizeOf);
+ for (struct CallbackNode* node = gCallbacks; node; node = node->next) {
+ n += aMallocSizeOf(node);
+ n += aMallocSizeOf(node->domain);
+ }
+ return n;
+}
+
+PrefType
+PREF_GetPrefType(const char *pref_name)
+{
+ if (gHashTable) {
+ PrefHashEntry* pref = pref_HashTableLookup(pref_name);
+ if (pref) {
+ return pref->prefFlags.GetPrefType();
+ }
+ }
+ return PrefType::Invalid;
+}
+
+/* -- */
+
+bool
+PREF_PrefIsLocked(const char *pref_name)
+{
+ bool result = false;
+ if (gIsAnyPrefLocked && gHashTable) {
+ PrefHashEntry* pref = pref_HashTableLookup(pref_name);
+ if (pref && pref->prefFlags.IsLocked()) {
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+/* Adds a node to the beginning of the callback list. */
+void
+PREF_RegisterCallback(const char *pref_node,
+ PrefChangedFunc callback,
+ void * instance_data)
+{
+ NS_PRECONDITION(pref_node, "pref_node must not be nullptr");
+ NS_PRECONDITION(callback, "callback must not be nullptr");
+
+ struct CallbackNode* node = (struct CallbackNode*) malloc(sizeof(struct CallbackNode));
+ if (node)
+ {
+ node->domain = PL_strdup(pref_node);
+ node->func = callback;
+ node->data = instance_data;
+ node->next = gCallbacks;
+ gCallbacks = node;
+ }
+ return;
+}
+
+/* Removes |node| from gCallbacks list.
+ Returns the node after the deleted one. */
+struct CallbackNode*
+pref_RemoveCallbackNode(struct CallbackNode* node,
+ struct CallbackNode* prev_node)
+{
+ NS_PRECONDITION(!prev_node || prev_node->next == node, "invalid params");
+ NS_PRECONDITION(prev_node || gCallbacks == node, "invalid params");
+
+ NS_ASSERTION(!gCallbacksInProgress,
+ "modifying the callback list while gCallbacksInProgress is true");
+
+ struct CallbackNode* next_node = node->next;
+ if (prev_node)
+ prev_node->next = next_node;
+ else
+ gCallbacks = next_node;
+ PL_strfree(node->domain);
+ free(node);
+ return next_node;
+}
+
+/* Deletes a node from the callback list or marks it for deletion. */
+nsresult
+PREF_UnregisterCallback(const char *pref_node,
+ PrefChangedFunc callback,
+ void * instance_data)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+ struct CallbackNode* node = gCallbacks;
+ struct CallbackNode* prev_node = nullptr;
+
+ while (node != nullptr)
+ {
+ if ( node->func == callback &&
+ node->data == instance_data &&
+ strcmp(node->domain, pref_node) == 0)
+ {
+ if (gCallbacksInProgress)
+ {
+ // postpone the node removal until after
+ // gCallbacks enumeration is finished.
+ node->func = nullptr;
+ gShouldCleanupDeadNodes = true;
+ prev_node = node;
+ node = node->next;
+ }
+ else
+ {
+ node = pref_RemoveCallbackNode(node, prev_node);
+ }
+ rv = NS_OK;
+ }
+ else
+ {
+ prev_node = node;
+ node = node->next;
+ }
+ }
+ return rv;
+}
+
+static nsresult pref_DoCallback(const char* changed_pref)
+{
+ nsresult rv = NS_OK;
+ struct CallbackNode* node;
+
+ bool reentered = gCallbacksInProgress;
+ gCallbacksInProgress = true;
+ // Nodes must not be deleted while gCallbacksInProgress is true.
+ // Nodes that need to be deleted are marked for deletion by nulling
+ // out the |func| pointer. We release them at the end of this function
+ // if we haven't reentered.
+
+ for (node = gCallbacks; node != nullptr; node = node->next)
+ {
+ if ( node->func &&
+ PL_strncmp(changed_pref,
+ node->domain,
+ strlen(node->domain)) == 0 )
+ {
+ (*node->func) (changed_pref, node->data);
+ }
+ }
+
+ gCallbacksInProgress = reentered;
+
+ if (gShouldCleanupDeadNodes && !gCallbacksInProgress)
+ {
+ struct CallbackNode* prev_node = nullptr;
+ node = gCallbacks;
+
+ while (node != nullptr)
+ {
+ if (!node->func)
+ {
+ node = pref_RemoveCallbackNode(node, prev_node);
+ }
+ else
+ {
+ prev_node = node;
+ node = node->next;
+ }
+ }
+ gShouldCleanupDeadNodes = false;
+ }
+
+ return rv;
+}
+
+void PREF_ReaderCallback(void *closure,
+ const char *pref,
+ PrefValue value,
+ PrefType type,
+ bool isDefault,
+ bool isStickyDefault)
+
+{
+ uint32_t flags = 0;
+ if (isDefault) {
+ flags |= kPrefSetDefault;
+ if (isStickyDefault) {
+ flags |= kPrefStickyDefault;
+ }
+ } else {
+ flags |= kPrefForceSet;
+ }
+ pref_HashPref(pref, value, type, flags);
+}
diff --git a/components/preferences/src/prefapi.h b/components/preferences/src/prefapi.h
new file mode 100644
index 000000000..9ef014ada
--- /dev/null
+++ b/components/preferences/src/prefapi.h
@@ -0,0 +1,258 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+// <pre>
+*/
+#ifndef PREFAPI_H
+#define PREFAPI_H
+
+#include "nscore.h"
+#include "PLDHashTable.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// 1 MB should be enough for everyone.
+static const uint32_t MAX_PREF_LENGTH = 1 * 1024 * 1024;
+// Actually, 4kb should be enough for everyone.
+static const uint32_t MAX_ADVISABLE_PREF_LENGTH = 4 * 1024;
+
+typedef union
+{
+ char* stringVal;
+ int32_t intVal;
+ bool boolVal;
+} PrefValue;
+
+/*
+// <font color=blue>
+// The Init function initializes the preference context and creates
+// the preference hashtable.
+// </font>
+*/
+void PREF_Init();
+
+/*
+// Cleanup should be called at program exit to free the
+// list of registered callbacks.
+*/
+void PREF_Cleanup();
+void PREF_CleanupPrefs();
+
+/*
+// <font color=blue>
+// Preference flags, including the native type of the preference. Changing any of these
+// values will require modifying the code inside of PrefTypeFlags class.
+// </font>
+*/
+
+enum class PrefType {
+ Invalid = 0,
+ String = 1,
+ Int = 2,
+ Bool = 3,
+};
+
+// Keep the type of the preference, as well as the flags guiding its behaviour.
+class PrefTypeFlags
+{
+public:
+ PrefTypeFlags() : mValue(AsInt(PrefType::Invalid)) {}
+ explicit PrefTypeFlags(PrefType aType) : mValue(AsInt(aType)) {}
+ PrefTypeFlags& Reset() { mValue = AsInt(PrefType::Invalid); return *this; }
+
+ bool IsTypeValid() const { return !IsPrefType(PrefType::Invalid); }
+ bool IsTypeString() const { return IsPrefType(PrefType::String); }
+ bool IsTypeInt() const { return IsPrefType(PrefType::Int); }
+ bool IsTypeBool() const { return IsPrefType(PrefType::Bool); }
+ bool IsPrefType(PrefType type) const { return GetPrefType() == type; }
+
+ PrefTypeFlags& SetPrefType(PrefType aType) {
+ mValue = mValue - AsInt(GetPrefType()) + AsInt(aType);
+ return *this;
+ }
+ PrefType GetPrefType() const {
+ return (PrefType)(mValue & (AsInt(PrefType::String) |
+ AsInt(PrefType::Int) |
+ AsInt(PrefType::Bool)));
+ }
+
+ bool HasDefault() const { return mValue & PREF_FLAG_HAS_DEFAULT; }
+ PrefTypeFlags& SetHasDefault(bool aSetOrUnset) { return SetFlag(PREF_FLAG_HAS_DEFAULT, aSetOrUnset); }
+
+ bool HasStickyDefault() const { return mValue & PREF_FLAG_STICKY_DEFAULT; }
+ PrefTypeFlags& SetHasStickyDefault(bool aSetOrUnset) { return SetFlag(PREF_FLAG_STICKY_DEFAULT, aSetOrUnset); }
+
+ bool IsLocked() const { return mValue & PREF_FLAG_LOCKED; }
+ PrefTypeFlags& SetLocked(bool aSetOrUnset) { return SetFlag(PREF_FLAG_LOCKED, aSetOrUnset); }
+
+ bool HasUserValue() const { return mValue & PREF_FLAG_USERSET; }
+ PrefTypeFlags& SetHasUserValue(bool aSetOrUnset) { return SetFlag(PREF_FLAG_USERSET, aSetOrUnset); }
+
+private:
+ static uint16_t AsInt(PrefType aType) { return (uint16_t)aType; }
+
+ PrefTypeFlags& SetFlag(uint16_t aFlag, bool aSetOrUnset) {
+ mValue = aSetOrUnset ? mValue | aFlag : mValue & ~aFlag;
+ return *this;
+ }
+
+ // Pack both the value of type (PrefType) and flags into the same int. This is why
+ // the flag enum starts at 4, as PrefType occupies the bottom two bits.
+ enum {
+ PREF_FLAG_LOCKED = 4,
+ PREF_FLAG_USERSET = 8,
+ PREF_FLAG_CONFIG = 16,
+ PREF_FLAG_REMOTE = 32,
+ PREF_FLAG_LILOCAL = 64,
+ PREF_FLAG_HAS_DEFAULT = 128,
+ PREF_FLAG_STICKY_DEFAULT = 256,
+ };
+ uint16_t mValue;
+};
+
+struct PrefHashEntry : PLDHashEntryHdr
+{
+ PrefTypeFlags prefFlags; // This field goes first to minimize struct size on 64-bit.
+ const char *key;
+ PrefValue defaultPref;
+ PrefValue userPref;
+};
+
+/*
+// <font color=blue>
+// Set the various types of preferences. These functions take a dotted
+// notation of the preference name (e.g. "browser.startup.homepage").
+// Note that this will cause the preference to be saved to the file if
+// it is different from the default. In other words, these are used
+// to set the _user_ preferences.
+//
+// If set_default is set to true however, it sets the default value.
+// This will only affect the program behavior if the user does not have a value
+// saved over it for the particular preference. In addition, these will never
+// be saved out to disk.
+//
+// Each set returns PREF_VALUECHANGED if the user value changed
+// (triggering a callback), or PREF_NOERROR if the value was unchanged.
+// </font>
+*/
+nsresult PREF_SetCharPref(const char *pref,const char* value, bool set_default = false);
+nsresult PREF_SetIntPref(const char *pref,int32_t value, bool set_default = false);
+nsresult PREF_SetBoolPref(const char *pref,bool value, bool set_default = false);
+
+bool PREF_HasUserPref(const char* pref_name);
+
+/*
+// <font color=blue>
+// Get the various types of preferences. These functions take a dotted
+// notation of the preference name (e.g. "browser.startup.homepage")
+//
+// They also take a pointer to fill in with the return value and return an
+// error value. At the moment, this is simply an int but it may
+// be converted to an enum once the global error strategy is worked out.
+//
+// They will perform conversion if the type doesn't match what was requested.
+// (if it is reasonably possible)
+// </font>
+*/
+nsresult PREF_GetIntPref(const char *pref,
+ int32_t * return_int, bool get_default);
+nsresult PREF_GetBoolPref(const char *pref, bool * return_val, bool get_default);
+/*
+// <font color=blue>
+// These functions are similar to the above "Get" version with the significant
+// difference that the preference module will alloc the memory (e.g. XP_STRDUP) and
+// the caller will need to be responsible for freeing it...
+// </font>
+*/
+nsresult PREF_CopyCharPref(const char *pref, char ** return_buf, bool get_default);
+/*
+// <font color=blue>
+// bool function that returns whether or not the preference is locked and therefore
+// cannot be changed.
+// </font>
+*/
+bool PREF_PrefIsLocked(const char *pref_name);
+
+/*
+// <font color=blue>
+// Function that sets whether or not the preference is locked and therefore
+// cannot be changed.
+// </font>
+*/
+nsresult PREF_LockPref(const char *key, bool lockIt);
+
+PrefType PREF_GetPrefType(const char *pref_name);
+
+/*
+ * Delete a branch of the tree
+ */
+nsresult PREF_DeleteBranch(const char *branch_name);
+
+/*
+ * Clears the given pref (reverts it to its default value)
+ */
+nsresult PREF_ClearUserPref(const char *pref_name);
+
+/*
+ * Clears all user prefs
+ */
+nsresult PREF_ClearAllUserPrefs();
+
+
+/*
+// <font color=blue>
+// The callback function will get passed the pref_node which triggered the call
+// and the void * instance_data which was passed to the register callback function.
+// Return a non-zero result (nsresult) to pass an error up to the caller.
+// </font>
+*/
+/* Temporarily conditionally compile PrefChangedFunc typedef.
+** During migration from old libpref to nsIPref we need it in
+** both header files. Eventually prefapi.h will become a private
+** file. The two types need to be in sync for now. Certain
+** compilers were having problems with multiple definitions.
+*/
+#ifndef have_PrefChangedFunc_typedef
+typedef void (*PrefChangedFunc) (const char *, void *);
+#define have_PrefChangedFunc_typedef
+#endif
+
+/*
+// <font color=blue>
+// Register a callback. This takes a node in the preference tree and will
+// call the callback function if anything below that node is modified.
+// Unregister returns PREF_NOERROR if a callback was found that
+// matched all the parameters; otherwise it returns PREF_ERROR.
+// </font>
+*/
+void PREF_RegisterCallback(const char* domain,
+ PrefChangedFunc callback, void* instance_data );
+nsresult PREF_UnregisterCallback(const char* domain,
+ PrefChangedFunc callback, void* instance_data );
+
+/*
+ * Used by nsPrefService as the callback function of the 'pref' parser
+ */
+void PREF_ReaderCallback( void *closure,
+ const char *pref,
+ PrefValue value,
+ PrefType type,
+ bool isDefault,
+ bool isStickyDefault);
+
+
+/*
+ * Callback whenever we change a preference
+ */
+typedef void (*PrefsDirtyFunc) ();
+void PREF_SetDirtyCallback(PrefsDirtyFunc);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/components/preferences/src/prefapi_private_data.h b/components/preferences/src/prefapi_private_data.h
new file mode 100644
index 000000000..f1fa68fdc
--- /dev/null
+++ b/components/preferences/src/prefapi_private_data.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Data shared between prefapi.c and nsPref.cpp */
+
+#ifndef prefapi_private_data_h
+#define prefapi_private_data_h
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
+
+extern PLDHashTable* gHashTable;
+
+namespace mozilla {
+namespace dom {
+class PrefSetting;
+} // namespace dom
+} // namespace mozilla
+
+mozilla::UniquePtr<char*[]>
+pref_savePrefs(PLDHashTable* aTable, uint32_t* aPrefCount);
+
+nsresult
+pref_SetPref(const mozilla::dom::PrefSetting& aPref);
+
+int pref_CompareStrings(const void *v1, const void *v2, void* unused);
+PrefHashEntry* pref_HashTableLookup(const char *key);
+
+bool
+pref_EntryHasAdvisablySizedValues(PrefHashEntry* aHashEntry);
+
+void pref_GetPrefFromEntry(PrefHashEntry *aHashEntry,
+ mozilla::dom::PrefSetting* aPref);
+
+size_t
+pref_SizeOfPrivateData(mozilla::MallocSizeOf aMallocSizeOf);
+
+#endif
diff --git a/components/preferences/src/prefread.cpp b/components/preferences/src/prefread.cpp
new file mode 100644
index 000000000..605dcaac6
--- /dev/null
+++ b/components/preferences/src/prefread.cpp
@@ -0,0 +1,657 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "prefread.h"
+#include "nsString.h"
+#include "nsUTF8Utils.h"
+
+#ifdef TEST_PREFREAD
+#include <stdio.h>
+#define NS_WARNING(_s) printf(">>> " _s "!\n")
+#define NS_NOTREACHED(_s) NS_WARNING(_s)
+#else
+#include "nsDebug.h" // for NS_WARNING
+#endif
+
+/* pref parser states */
+enum {
+ PREF_PARSE_INIT,
+ PREF_PARSE_MATCH_STRING,
+ PREF_PARSE_UNTIL_NAME,
+ PREF_PARSE_QUOTED_STRING,
+ PREF_PARSE_UNTIL_COMMA,
+ PREF_PARSE_UNTIL_VALUE,
+ PREF_PARSE_INT_VALUE,
+ PREF_PARSE_COMMENT_MAYBE_START,
+ PREF_PARSE_COMMENT_BLOCK,
+ PREF_PARSE_COMMENT_BLOCK_MAYBE_END,
+ PREF_PARSE_ESC_SEQUENCE,
+ PREF_PARSE_HEX_ESCAPE,
+ PREF_PARSE_UTF16_LOW_SURROGATE,
+ PREF_PARSE_UNTIL_OPEN_PAREN,
+ PREF_PARSE_UNTIL_CLOSE_PAREN,
+ PREF_PARSE_UNTIL_SEMICOLON,
+ PREF_PARSE_UNTIL_EOL
+};
+
+#define UTF16_ESC_NUM_DIGITS 4
+#define HEX_ESC_NUM_DIGITS 2
+#define BITS_PER_HEX_DIGIT 4
+
+static const char kUserPref[] = "user_pref";
+static const char kPref[] = "pref";
+static const char kPrefSticky[] = "sticky_pref";
+static const char kTrue[] = "true";
+static const char kFalse[] = "false";
+
+/**
+ * pref_GrowBuf
+ *
+ * this function will increase the size of the buffer owned
+ * by the given pref parse state. We currently use a simple
+ * doubling algorithm, but the only hard requirement is that
+ * it increase the buffer by at least the size of the ps->esctmp
+ * buffer used for escape processing (currently 6 bytes).
+ *
+ * this buffer is used to store partial pref lines. it is
+ * freed when the parse state is destroyed.
+ *
+ * @param ps
+ * parse state instance
+ *
+ * this function updates all pointers that reference an
+ * address within lb since realloc may relocate the buffer.
+ *
+ * @return false if insufficient memory.
+ */
+static bool
+pref_GrowBuf(PrefParseState *ps)
+{
+ int bufLen, curPos, valPos;
+
+ bufLen = ps->lbend - ps->lb;
+ curPos = ps->lbcur - ps->lb;
+ valPos = ps->vb - ps->lb;
+
+ if (bufLen == 0)
+ bufLen = 128; /* default buffer size */
+ else
+ bufLen <<= 1; /* double buffer size */
+
+#ifdef TEST_PREFREAD
+ fprintf(stderr, ">>> realloc(%d)\n", bufLen);
+#endif
+
+ ps->lb = (char*) realloc(ps->lb, bufLen);
+ if (!ps->lb)
+ return false;
+
+ ps->lbcur = ps->lb + curPos;
+ ps->lbend = ps->lb + bufLen;
+ ps->vb = ps->lb + valPos;
+
+ return true;
+}
+
+/**
+ * Report an error or a warning. If not specified, just dump to stderr.
+ */
+static void
+pref_ReportParseProblem(PrefParseState& ps, const char* aMessage, int aLine, bool aError)
+{
+ if (ps.reporter) {
+ ps.reporter(aMessage, aLine, aError);
+ } else {
+ printf_stderr("**** Preference parsing %s (line %d) = %s **\n",
+ (aError ? "error" : "warning"), aLine, aMessage);
+ }
+}
+
+/**
+ * pref_DoCallback
+ *
+ * this function is called when a complete pref name-value pair has
+ * been extracted from the input data.
+ *
+ * @param ps
+ * parse state instance
+ *
+ * @return false to indicate a fatal error.
+ */
+static bool
+pref_DoCallback(PrefParseState *ps)
+{
+ PrefValue value;
+
+ switch (ps->vtype) {
+ case PrefType::String:
+ value.stringVal = ps->vb;
+ break;
+ case PrefType::Int:
+ if ((ps->vb[0] == '-' || ps->vb[0] == '+') && ps->vb[1] == '\0') {
+ pref_ReportParseProblem(*ps, "invalid integer value", 0, true);
+ NS_WARNING("malformed integer value");
+ return false;
+ }
+ value.intVal = atoi(ps->vb);
+ break;
+ case PrefType::Bool:
+ value.boolVal = (ps->vb == kTrue);
+ break;
+ default:
+ break;
+ }
+ (*ps->reader)(ps->closure, ps->lb, value, ps->vtype, ps->fdefault,
+ ps->fstickydefault);
+ return true;
+}
+
+void
+PREF_InitParseState(PrefParseState *ps, PrefReader reader,
+ PrefParseErrorReporter reporter, void *closure)
+{
+ memset(ps, 0, sizeof(*ps));
+ ps->reader = reader;
+ ps->closure = closure;
+ ps->reporter = reporter;
+}
+
+void
+PREF_FinalizeParseState(PrefParseState *ps)
+{
+ if (ps->lb)
+ free(ps->lb);
+}
+
+/**
+ * Pseudo-BNF
+ * ----------
+ * function = LJUNK function-name JUNK function-args
+ * function-name = "user_pref" | "pref" | "sticky_pref"
+ * function-args = "(" JUNK pref-name JUNK "," JUNK pref-value JUNK ")" JUNK ";"
+ * pref-name = quoted-string
+ * pref-value = quoted-string | "true" | "false" | integer-value
+ * JUNK = *(WS | comment-block | comment-line)
+ * LJUNK = *(WS | comment-block | comment-line | bcomment-line)
+ * WS = SP | HT | LF | VT | FF | CR
+ * SP = <US-ASCII SP, space (32)>
+ * HT = <US-ASCII HT, horizontal-tab (9)>
+ * LF = <US-ASCII LF, linefeed (10)>
+ * VT = <US-ASCII HT, vertical-tab (11)>
+ * FF = <US-ASCII FF, form-feed (12)>
+ * CR = <US-ASCII CR, carriage return (13)>
+ * comment-block = <C/C++ style comment block>
+ * comment-line = <C++ style comment line>
+ * bcomment-line = <bourne-shell style comment line>
+ */
+bool
+PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen)
+{
+ const char *end;
+ char c;
+ char udigit;
+ int state;
+
+ // The line number is currently only used for the error/warning reporting.
+ int lineNum = 0;
+
+ state = ps->state;
+ for (end = buf + bufLen; buf != end; ++buf) {
+ c = *buf;
+ if (c == '\r' || c == '\n' || c == 0x1A) {
+ lineNum ++;
+ }
+
+ switch (state) {
+ /* initial state */
+ case PREF_PARSE_INIT:
+ if (ps->lbcur != ps->lb) { /* reset state */
+ ps->lbcur = ps->lb;
+ ps->vb = nullptr;
+ ps->vtype = PrefType::Invalid;
+ ps->fdefault = false;
+ ps->fstickydefault = false;
+ }
+ switch (c) {
+ case '/': /* begin comment block or line? */
+ state = PREF_PARSE_COMMENT_MAYBE_START;
+ break;
+ case '#': /* accept shell style comments */
+ state = PREF_PARSE_UNTIL_EOL;
+ break;
+ case 'u': /* indicating user_pref */
+ case 's': /* indicating sticky_pref */
+ case 'p': /* indicating pref */
+ if (c == 'u') {
+ ps->smatch = kUserPref;
+ } else if (c == 's') {
+ ps->smatch = kPrefSticky;
+ } else {
+ ps->smatch = kPref;
+ }
+ ps->sindex = 1;
+ ps->nextstate = PREF_PARSE_UNTIL_OPEN_PAREN;
+ state = PREF_PARSE_MATCH_STRING;
+ break;
+ /* else skip char */
+ }
+ break;
+
+ /* string matching */
+ case PREF_PARSE_MATCH_STRING:
+ if (c == ps->smatch[ps->sindex++]) {
+ /* if we've matched all characters, then move to next state. */
+ if (ps->smatch[ps->sindex] == '\0') {
+ state = ps->nextstate;
+ ps->nextstate = PREF_PARSE_INIT; /* reset next state */
+ }
+ /* else wait for next char */
+ }
+ else {
+ pref_ReportParseProblem(*ps, "non-matching string", lineNum, true);
+ NS_WARNING("malformed pref file");
+ return false;
+ }
+ break;
+
+ /* quoted string parsing */
+ case PREF_PARSE_QUOTED_STRING:
+ /* we assume that the initial quote has already been consumed */
+ if (ps->lbcur == ps->lbend && !pref_GrowBuf(ps))
+ return false; /* out of memory */
+ if (c == '\\')
+ state = PREF_PARSE_ESC_SEQUENCE;
+ else if (c == ps->quotechar) {
+ *ps->lbcur++ = '\0';
+ state = ps->nextstate;
+ ps->nextstate = PREF_PARSE_INIT; /* reset next state */
+ }
+ else
+ *ps->lbcur++ = c;
+ break;
+
+ /* name parsing */
+ case PREF_PARSE_UNTIL_NAME:
+ if (c == '\"' || c == '\'') {
+ ps->fdefault = (ps->smatch == kPref || ps->smatch == kPrefSticky);
+ ps->fstickydefault = (ps->smatch == kPrefSticky);
+ ps->quotechar = c;
+ ps->nextstate = PREF_PARSE_UNTIL_COMMA; /* return here when done */
+ state = PREF_PARSE_QUOTED_STRING;
+ }
+ else if (c == '/') { /* allow embedded comment */
+ ps->nextstate = state; /* return here when done with comment */
+ state = PREF_PARSE_COMMENT_MAYBE_START;
+ }
+ else if (!isspace(c)) {
+ pref_ReportParseProblem(*ps, "need space, comment or quote", lineNum, true);
+ NS_WARNING("malformed pref file");
+ return false;
+ }
+ break;
+
+ /* parse until we find a comma separating name and value */
+ case PREF_PARSE_UNTIL_COMMA:
+ if (c == ',') {
+ ps->vb = ps->lbcur;
+ state = PREF_PARSE_UNTIL_VALUE;
+ }
+ else if (c == '/') { /* allow embedded comment */
+ ps->nextstate = state; /* return here when done with comment */
+ state = PREF_PARSE_COMMENT_MAYBE_START;
+ }
+ else if (!isspace(c)) {
+ pref_ReportParseProblem(*ps, "need space, comment or comma", lineNum, true);
+ NS_WARNING("malformed pref file");
+ return false;
+ }
+ break;
+
+ /* value parsing */
+ case PREF_PARSE_UNTIL_VALUE:
+ /* the pref value type is unknown. so, we scan for the first
+ * character of the value, and determine the type from that. */
+ if (c == '\"' || c == '\'') {
+ ps->vtype = PrefType::String;
+ ps->quotechar = c;
+ ps->nextstate = PREF_PARSE_UNTIL_CLOSE_PAREN;
+ state = PREF_PARSE_QUOTED_STRING;
+ }
+ else if (c == 't' || c == 'f') {
+ ps->vb = (char *) (c == 't' ? kTrue : kFalse);
+ ps->vtype = PrefType::Bool;
+ ps->smatch = ps->vb;
+ ps->sindex = 1;
+ ps->nextstate = PREF_PARSE_UNTIL_CLOSE_PAREN;
+ state = PREF_PARSE_MATCH_STRING;
+ }
+ else if (isdigit(c) || (c == '-') || (c == '+')) {
+ ps->vtype = PrefType::Int;
+ /* write c to line buffer... */
+ if (ps->lbcur == ps->lbend && !pref_GrowBuf(ps))
+ return false; /* out of memory */
+ *ps->lbcur++ = c;
+ state = PREF_PARSE_INT_VALUE;
+ }
+ else if (c == '/') { /* allow embedded comment */
+ ps->nextstate = state; /* return here when done with comment */
+ state = PREF_PARSE_COMMENT_MAYBE_START;
+ }
+ else if (!isspace(c)) {
+ pref_ReportParseProblem(*ps, "need value, comment or space", lineNum, true);
+ NS_WARNING("malformed pref file");
+ return false;
+ }
+ break;
+ case PREF_PARSE_INT_VALUE:
+ /* grow line buffer if necessary... */
+ if (ps->lbcur == ps->lbend && !pref_GrowBuf(ps))
+ return false; /* out of memory */
+ if (isdigit(c))
+ *ps->lbcur++ = c;
+ else {
+ *ps->lbcur++ = '\0'; /* stomp null terminator; we are done. */
+ if (c == ')')
+ state = PREF_PARSE_UNTIL_SEMICOLON;
+ else if (c == '/') { /* allow embedded comment */
+ ps->nextstate = PREF_PARSE_UNTIL_CLOSE_PAREN;
+ state = PREF_PARSE_COMMENT_MAYBE_START;
+ }
+ else if (isspace(c))
+ state = PREF_PARSE_UNTIL_CLOSE_PAREN;
+ else {
+ pref_ReportParseProblem(*ps, "while parsing integer", lineNum, true);
+ NS_WARNING("malformed pref file");
+ return false;
+ }
+ }
+ break;
+
+ /* comment parsing */
+ case PREF_PARSE_COMMENT_MAYBE_START:
+ switch (c) {
+ case '*': /* comment block */
+ state = PREF_PARSE_COMMENT_BLOCK;
+ break;
+ case '/': /* comment line */
+ state = PREF_PARSE_UNTIL_EOL;
+ break;
+ default:
+ /* pref file is malformed */
+ pref_ReportParseProblem(*ps, "while parsing comment", lineNum, true);
+ NS_WARNING("malformed pref file");
+ return false;
+ }
+ break;
+ case PREF_PARSE_COMMENT_BLOCK:
+ if (c == '*')
+ state = PREF_PARSE_COMMENT_BLOCK_MAYBE_END;
+ break;
+ case PREF_PARSE_COMMENT_BLOCK_MAYBE_END:
+ switch (c) {
+ case '/':
+ state = ps->nextstate;
+ ps->nextstate = PREF_PARSE_INIT;
+ break;
+ case '*': /* stay in this state */
+ break;
+ default:
+ state = PREF_PARSE_COMMENT_BLOCK;
+ }
+ break;
+
+ /* string escape sequence parsing */
+ case PREF_PARSE_ESC_SEQUENCE:
+ /* not necessary to resize buffer here since we should be writing
+ * only one character and the resize check would have been done
+ * for us in the previous state */
+ switch (c) {
+ case '\"':
+ case '\'':
+ case '\\':
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'x': /* hex escape -- always interpreted as Latin-1 */
+ case 'u': /* UTF16 escape */
+ ps->esctmp[0] = c;
+ ps->esclen = 1;
+ ps->utf16[0] = ps->utf16[1] = 0;
+ ps->sindex = (c == 'x' ) ?
+ HEX_ESC_NUM_DIGITS :
+ UTF16_ESC_NUM_DIGITS;
+ state = PREF_PARSE_HEX_ESCAPE;
+ continue;
+ default:
+ pref_ReportParseProblem(*ps, "preserving unexpected JS escape sequence",
+ lineNum, false);
+ NS_WARNING("preserving unexpected JS escape sequence");
+ /* Invalid escape sequence so we do have to write more than
+ * one character. Grow line buffer if necessary... */
+ if ((ps->lbcur+1) == ps->lbend && !pref_GrowBuf(ps))
+ return false; /* out of memory */
+ *ps->lbcur++ = '\\'; /* preserve the escape sequence */
+ break;
+ }
+ *ps->lbcur++ = c;
+ state = PREF_PARSE_QUOTED_STRING;
+ break;
+
+ /* parsing a hex (\xHH) or utf16 escape (\uHHHH) */
+ case PREF_PARSE_HEX_ESCAPE:
+ if ( c >= '0' && c <= '9' )
+ udigit = (c - '0');
+ else if ( c >= 'A' && c <= 'F' )
+ udigit = (c - 'A') + 10;
+ else if ( c >= 'a' && c <= 'f' )
+ udigit = (c - 'a') + 10;
+ else {
+ /* bad escape sequence found, write out broken escape as-is */
+ pref_ReportParseProblem(*ps, "preserving invalid or incomplete hex escape",
+ lineNum, false);
+ NS_WARNING("preserving invalid or incomplete hex escape");
+ *ps->lbcur++ = '\\'; /* original escape slash */
+ if ((ps->lbcur + ps->esclen) >= ps->lbend && !pref_GrowBuf(ps))
+ return false;
+ for (int i = 0; i < ps->esclen; ++i)
+ *ps->lbcur++ = ps->esctmp[i];
+
+ /* push the non-hex character back for re-parsing. */
+ /* (++buf at the top of the loop keeps this safe) */
+ --buf;
+ state = PREF_PARSE_QUOTED_STRING;
+ continue;
+ }
+
+ /* have a digit */
+ ps->esctmp[ps->esclen++] = c; /* preserve it */
+ ps->utf16[1] <<= BITS_PER_HEX_DIGIT;
+ ps->utf16[1] |= udigit;
+ ps->sindex--;
+ if (ps->sindex == 0) {
+ /* have the full escape. Convert to UTF8 */
+ int utf16len = 0;
+ if (ps->utf16[0]) {
+ /* already have a high surrogate, this is a two char seq */
+ utf16len = 2;
+ }
+ else if (0xD800 == (0xFC00 & ps->utf16[1])) {
+ /* a high surrogate, can't convert until we have the low */
+ ps->utf16[0] = ps->utf16[1];
+ ps->utf16[1] = 0;
+ state = PREF_PARSE_UTF16_LOW_SURROGATE;
+ break;
+ }
+ else {
+ /* a single utf16 character */
+ ps->utf16[0] = ps->utf16[1];
+ utf16len = 1;
+ }
+
+ /* actual conversion */
+ /* make sure there's room, 6 bytes is max utf8 len (in */
+ /* theory; 4 bytes covers the actual utf16 range) */
+ if (ps->lbcur+6 >= ps->lbend && !pref_GrowBuf(ps))
+ return false;
+
+ ConvertUTF16toUTF8 converter(ps->lbcur);
+ converter.write(ps->utf16, utf16len);
+ ps->lbcur += converter.Size();
+ state = PREF_PARSE_QUOTED_STRING;
+ }
+ break;
+
+ /* looking for beginning of utf16 low surrogate */
+ case PREF_PARSE_UTF16_LOW_SURROGATE:
+ if (ps->sindex == 0 && c == '\\') {
+ ++ps->sindex;
+ }
+ else if (ps->sindex == 1 && c == 'u') {
+ /* escape sequence is correct, now parse hex */
+ ps->sindex = UTF16_ESC_NUM_DIGITS;
+ ps->esctmp[0] = 'u';
+ ps->esclen = 1;
+ state = PREF_PARSE_HEX_ESCAPE;
+ }
+ else {
+ /* didn't find expected low surrogate. Ignore high surrogate
+ * (it would just get converted to nothing anyway) and start
+ * over with this character */
+ --buf;
+ if (ps->sindex == 1)
+ state = PREF_PARSE_ESC_SEQUENCE;
+ else
+ state = PREF_PARSE_QUOTED_STRING;
+ continue;
+ }
+ break;
+
+ /* function open and close parsing */
+ case PREF_PARSE_UNTIL_OPEN_PAREN:
+ /* tolerate only whitespace and embedded comments */
+ if (c == '(')
+ state = PREF_PARSE_UNTIL_NAME;
+ else if (c == '/') {
+ ps->nextstate = state; /* return here when done with comment */
+ state = PREF_PARSE_COMMENT_MAYBE_START;
+ }
+ else if (!isspace(c)) {
+ pref_ReportParseProblem(*ps, "need space, comment or open parentheses",
+ lineNum, true);
+ NS_WARNING("malformed pref file");
+ return false;
+ }
+ break;
+ case PREF_PARSE_UNTIL_CLOSE_PAREN:
+ /* tolerate only whitespace and embedded comments */
+ if (c == ')') {
+ state = PREF_PARSE_UNTIL_SEMICOLON;
+ } else if (c == '/') {
+ ps->nextstate = state; /* return here when done with comment */
+ state = PREF_PARSE_COMMENT_MAYBE_START;
+ } else if (!isspace(c)) {
+ pref_ReportParseProblem(*ps, "need space, comment or closing parentheses",
+ lineNum, true);
+ NS_WARNING("malformed pref file");
+ return false;
+ }
+ break;
+
+ /* function terminator ';' parsing */
+ case PREF_PARSE_UNTIL_SEMICOLON:
+ /* tolerate only whitespace and embedded comments */
+ if (c == ';') {
+ if (!pref_DoCallback(ps))
+ return false;
+ state = PREF_PARSE_INIT;
+ }
+ else if (c == '/') {
+ ps->nextstate = state; /* return here when done with comment */
+ state = PREF_PARSE_COMMENT_MAYBE_START;
+ }
+ else if (!isspace(c)) {
+ pref_ReportParseProblem(*ps, "need space, comment or semicolon",
+ lineNum, true);
+ NS_WARNING("malformed pref file");
+ return false;
+ }
+ break;
+
+ /* eol parsing */
+ case PREF_PARSE_UNTIL_EOL:
+ /* need to handle mac, unix, or dos line endings.
+ * PREF_PARSE_INIT will eat the next \n in case
+ * we have \r\n. */
+ if (c == '\r' || c == '\n' || c == 0x1A) {
+ state = ps->nextstate;
+ ps->nextstate = PREF_PARSE_INIT; /* reset next state */
+ }
+ break;
+ }
+ }
+ ps->state = state;
+ return true;
+}
+
+#ifdef TEST_PREFREAD
+
+static void
+pref_reader(void *closure,
+ const char *pref,
+ PrefValue val,
+ PrefType type,
+ bool defPref)
+{
+ printf("%spref(\"%s\", ", defPref ? "" : "user_", pref);
+ switch (type) {
+ case PREF_STRING:
+ printf("\"%s\");\n", val.stringVal);
+ break;
+ case PREF_INT:
+ printf("%i);\n", val.intVal);
+ break;
+ case PREF_BOOL:
+ printf("%s);\n", val.boolVal == false ? "false" : "true");
+ break;
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ PrefParseState ps;
+ char buf[4096]; /* i/o buffer */
+ FILE *fp;
+ int n;
+
+ if (argc == 1) {
+ printf("usage: prefread file.js\n");
+ return -1;
+ }
+
+ fp = fopen(argv[1], "r");
+ if (!fp) {
+ printf("failed to open file\n");
+ return -1;
+ }
+
+ PREF_InitParseState(&ps, pref_reader, nullptr, nullptr);
+
+ while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
+ PREF_ParseBuf(&ps, buf, n);
+
+ PREF_FinalizeParseState(&ps);
+
+ fclose(fp);
+ return 0;
+}
+
+#endif /* TEST_PREFREAD */
diff --git a/components/preferences/src/prefread.h b/components/preferences/src/prefread.h
new file mode 100644
index 000000000..2a09b30b6
--- /dev/null
+++ b/components/preferences/src/prefread.h
@@ -0,0 +1,118 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prefread_h__
+#define prefread_h__
+
+#include "prefapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Callback function used to notify consumer of preference name value pairs.
+ * The pref name and value must be copied by the implementor of the callback
+ * if they are needed beyond the scope of the callback function.
+ *
+ * @param closure
+ * user data passed to PREF_InitParseState
+ * @param pref
+ * preference name
+ * @param val
+ * preference value
+ * @param type
+ * preference type (PREF_STRING, PREF_INT, or PREF_BOOL)
+ * @param defPref
+ * preference type (true: default, false: user preference)
+ * @param stickyPref
+ * default preference marked as a "sticky" pref
+ */
+typedef void (*PrefReader)(void *closure,
+ const char *pref,
+ PrefValue val,
+ PrefType type,
+ bool defPref,
+ bool stickyPref);
+
+/**
+ * Report any errors or warnings we encounter during parsing.
+ */
+typedef void (*PrefParseErrorReporter)(const char* message, int line, bool error);
+
+/* structure fields are private */
+typedef struct PrefParseState {
+ PrefReader reader;
+ PrefParseErrorReporter reporter;
+ void *closure;
+ int state; /* PREF_PARSE_... */
+ int nextstate; /* sometimes used... */
+ const char *smatch; /* string to match */
+ int sindex; /* next char of smatch to check */
+ /* also, counter in \u parsing */
+ char16_t utf16[2]; /* parsing UTF16 (\u) escape */
+ int esclen; /* length in esctmp */
+ char esctmp[6]; /* raw escape to put back if err */
+ char quotechar; /* char delimiter for quotations */
+ char *lb; /* line buffer (only allocation) */
+ char *lbcur; /* line buffer cursor */
+ char *lbend; /* line buffer end */
+ char *vb; /* value buffer (ptr into lb) */
+ PrefType vtype; /* PREF_STRING,INT,BOOL */
+ bool fdefault; /* true if (default) pref */
+ bool fstickydefault; /* true if (sticky) pref */
+} PrefParseState;
+
+/**
+ * PREF_InitParseState
+ *
+ * Called to initialize a PrefParseState instance.
+ *
+ * @param ps
+ * PrefParseState instance.
+ * @param reader
+ * PrefReader callback function, which will be called once for each
+ * preference name value pair extracted.
+ * @param reporter
+ * PrefParseErrorReporter callback function, which will be called if we
+ * encounter any errors (stop) or warnings (continue) during parsing.
+ * @param closure
+ * PrefReader closure.
+ */
+void PREF_InitParseState(PrefParseState *ps, PrefReader reader,
+ PrefParseErrorReporter reporter, void *closure);
+
+/**
+ * PREF_FinalizeParseState
+ *
+ * Called to release any memory in use by the PrefParseState instance.
+ *
+ * @param ps
+ * PrefParseState instance.
+ */
+void PREF_FinalizeParseState(PrefParseState *ps);
+
+/**
+ * PREF_ParseBuf
+ *
+ * Called to parse a buffer containing some portion of a preference file. This
+ * function may be called repeatedly as new data is made available. The
+ * PrefReader callback function passed PREF_InitParseState will be called as
+ * preference name value pairs are extracted from the data.
+ *
+ * @param ps
+ * PrefParseState instance. Must have been initialized.
+ * @param buf
+ * Raw buffer containing data to be parsed.
+ * @param bufLen
+ * Length of buffer.
+ *
+ * @return false if buffer contains malformed content.
+ */
+bool PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* prefread_h__ */