summaryrefslogtreecommitdiffstats
path: root/modules/libpref/test/unit
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/libpref/test/unit/data/testParser.js98
-rw-r--r--modules/libpref/test/unit/data/testPref.js6
-rw-r--r--modules/libpref/test/unit/data/testPrefLocked.js2
-rw-r--r--modules/libpref/test/unit/data/testPrefLockedUser.js3
-rw-r--r--modules/libpref/test/unit/data/testPrefSticky.js2
-rw-r--r--modules/libpref/test/unit/data/testPrefStickyUser.js5
-rw-r--r--modules/libpref/test/unit/data/testPrefUTF8.js6
-rw-r--r--modules/libpref/test/unit/extdata/testExt.js2
-rw-r--r--modules/libpref/test/unit/head_libPrefs.js37
-rw-r--r--modules/libpref/test/unit/test_bug1354613.js21
-rw-r--r--modules/libpref/test/unit/test_bug345529.js25
-rw-r--r--modules/libpref/test/unit/test_bug506224.js25
-rw-r--r--modules/libpref/test/unit/test_bug577950.js17
-rw-r--r--modules/libpref/test/unit/test_bug790374.js50
-rw-r--r--modules/libpref/test/unit/test_changeType.js164
-rw-r--r--modules/libpref/test/unit/test_defaultValues.js59
-rw-r--r--modules/libpref/test/unit/test_dirtyPrefs.js69
-rw-r--r--modules/libpref/test/unit/test_libPrefs.js450
-rw-r--r--modules/libpref/test/unit/test_locked_file_prefs.js51
-rw-r--r--modules/libpref/test/unit/test_parsePrefs.js136
-rw-r--r--modules/libpref/test/unit/test_parser.js116
-rw-r--r--modules/libpref/test/unit/test_stickyprefs.js196
-rw-r--r--modules/libpref/test/unit/test_warnings.js62
-rw-r--r--modules/libpref/test/unit/xpcshell.ini28
-rw-r--r--modules/libpref/test/unit_ipc/test_existing_prefs.js20
-rw-r--r--modules/libpref/test/unit_ipc/test_initial_prefs.js14
-rw-r--r--modules/libpref/test/unit_ipc/test_large_pref.js104
-rw-r--r--modules/libpref/test/unit_ipc/test_locked_prefs.js39
-rw-r--r--modules/libpref/test/unit_ipc/test_observed_prefs.js12
-rw-r--r--modules/libpref/test/unit_ipc/test_sharedMap.js362
-rw-r--r--modules/libpref/test/unit_ipc/test_sharedMap_static_prefs.js76
-rw-r--r--modules/libpref/test/unit_ipc/test_update_prefs.js34
-rw-r--r--modules/libpref/test/unit_ipc/test_user_default_prefs.js72
-rw-r--r--modules/libpref/test/unit_ipc/xpcshell.ini14
34 files changed, 2377 insertions, 0 deletions
diff --git a/modules/libpref/test/unit/data/testParser.js b/modules/libpref/test/unit/data/testParser.js
new file mode 100644
index 0000000000..be29430b2a
--- /dev/null
+++ b/modules/libpref/test/unit/data/testParser.js
@@ -0,0 +1,98 @@
+// Note: this file tests only valid syntax (of default pref files, not user
+// pref files). See modules/libpref/test/gtest/Parser.cpp for tests of invalid
+// syntax.
+
+#
+# comment
+ # comment £
+//
+// comment
+ // comment £
+/**/
+ /* comment £ */
+/* comment
+ * and
+some
+ # more
+ // comment */
+// comment /*
+# comment /*
+/* /* /* /* /* ...no nesting of C-style comments... */
+
+// The following four lines have whitespace: \t, \v, \f, \r
+
+
+
+
+
+// This comment has the same whitespace:
+# This comment has other stuff: \n \r \t \v \f \r \a \b \? \' \" \\ \@
+/* This comment has more stuff: \x61 \u0061 \u1234 \uXYZ */
+
+/*0*/ pref /*1*/ ( /*2*/ "comment1" /*3*/ , /*4*/ true /*5*/ ) /*6*/ ; /*7*/
+
+pref # foo
+( // foo
+"comment2" /*
+foo
+*/,/*foo*/
+true#foo
+)//
+; /*7*/
+
+pref
+ (
+ "spaced-out"
+ ,
+ true
+ )
+ ;
+
+pref("pref", true);
+sticky_pref("sticky_pref", true);
+user_pref("user_pref", true);
+pref("sticky_pref2", true, sticky);
+pref("locked_pref", true, locked);
+pref("locked_sticky_pref", true, locked, sticky,sticky,
+ locked, locked, locked);
+
+pref("bool.true", true);
+pref("bool.false", false);
+
+pref("int.0", 0);
+pref("int.1", 1);
+pref("int.123", 123);
+pref("int.+234", +234);
+pref("int.+ 345", + 345);
+// Note that both the prefname and value have tabs in them
+pref("int.-0", -0);
+pref("int.-1", -1);
+pref("int.- /* hmm */ 456", - /* hmm */ 456);
+pref("int.-\n567", -
+567);
+pref("int.INT_MAX-1", 2147483646);
+pref("int.INT_MAX", 2147483647);
+pref("int.INT_MIN+2", -2147483646);
+pref("int.INT_MIN+1", -2147483647);
+pref("int.INT_MIN", -2147483648);
+//pref("int.overflow.max", 2147483648); // overflows to -2147483648
+//pref("int.overflow.min", -2147483649); // overflows to 2147483647
+//pref("int.overflow.other", 4000000000); // overflows to -294967296
+//pref("int.overflow.another", 5000000000000000); // overflows to 937459712
+
+pref("string.empty", "");
+pref("string.abc", "abc");
+pref("string.long", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+pref('string.single-quotes', '"abc"');
+pref("string.double-quotes", "'abc'");
+pref("string.weird-chars", "  ");
+pref("string.escapes", "\" \' \\ \n \r");
+pref("string.x-escapes1", "Mozilla0\x4d\x6F\x7a\x69\x6c\x6C\x610");
+pref("string.x-escapes2", "A\x41 A_umlaut\xc4 y_umlaut\xff");
+pref("string.u-escapes1", "A\u0041 A_umlaut\u00c4 y_umlaut\u00ff0");
+pref("string.u-escapes2", "S_acute\u015a y_grave\u1Ef3");
+// CYCLONE is 0x1f300, GRINNING FACE is 0x1f600. We have to represent them via
+// surrogate pairs.
+pref("string.u-surrogates",
+ "cyclone\uD83C\uDF00 grinning_face\uD83D\uDE00");
+
diff --git a/modules/libpref/test/unit/data/testPref.js b/modules/libpref/test/unit/data/testPref.js
new file mode 100644
index 0000000000..0864e7ce8b
--- /dev/null
+++ b/modules/libpref/test/unit/data/testPref.js
@@ -0,0 +1,6 @@
+user_pref("testPref.bool1", true);
+user_pref("testPref.bool2", false);
+user_pref("testPref.int1", 23);
+user_pref("testPref.int2", -1236);
+user_pref("testPref.char1", "_testPref");
+user_pref("testPref.char2", "älskar"); \ No newline at end of file
diff --git a/modules/libpref/test/unit/data/testPrefLocked.js b/modules/libpref/test/unit/data/testPrefLocked.js
new file mode 100644
index 0000000000..001b65122b
--- /dev/null
+++ b/modules/libpref/test/unit/data/testPrefLocked.js
@@ -0,0 +1,2 @@
+pref("testPref.unlocked.int", 333);
+pref("testPref.locked.int", 444, locked);
diff --git a/modules/libpref/test/unit/data/testPrefLockedUser.js b/modules/libpref/test/unit/data/testPrefLockedUser.js
new file mode 100644
index 0000000000..4da40b7abc
--- /dev/null
+++ b/modules/libpref/test/unit/data/testPrefLockedUser.js
@@ -0,0 +1,3 @@
+// testPrefLocked.js defined this pref as a locked pref.
+// Changing a locked pref has no effect.
+user_pref("testPref.locked.int", 555);
diff --git a/modules/libpref/test/unit/data/testPrefSticky.js b/modules/libpref/test/unit/data/testPrefSticky.js
new file mode 100644
index 0000000000..ef1a02adfd
--- /dev/null
+++ b/modules/libpref/test/unit/data/testPrefSticky.js
@@ -0,0 +1,2 @@
+pref("testPref.unsticky.bool", true);
+pref("testPref.sticky.bool", false, sticky);
diff --git a/modules/libpref/test/unit/data/testPrefStickyUser.js b/modules/libpref/test/unit/data/testPrefStickyUser.js
new file mode 100644
index 0000000000..d929c670ad
--- /dev/null
+++ b/modules/libpref/test/unit/data/testPrefStickyUser.js
@@ -0,0 +1,5 @@
+// testPrefSticky.js defined this pref as sticky. Once a sticky
+// pref has been changed, it's written as a user_pref().
+// So this test file reflects that scenario.
+// Note the default in testPrefSticky.js is also false.
+user_pref("testPref.sticky.bool", false);
diff --git a/modules/libpref/test/unit/data/testPrefUTF8.js b/modules/libpref/test/unit/data/testPrefUTF8.js
new file mode 100644
index 0000000000..a409c4eb72
--- /dev/null
+++ b/modules/libpref/test/unit/data/testPrefUTF8.js
@@ -0,0 +1,6 @@
+user_pref("testPref.bool1", true);
+user_pref("testPref.bool2", false);
+user_pref("testPref.int1", 23);
+user_pref("testPref.int2", -1236);
+user_pref("testPref.char1", "_testPref");
+user_pref("testPref.char2", "älskar");
diff --git a/modules/libpref/test/unit/extdata/testExt.js b/modules/libpref/test/unit/extdata/testExt.js
new file mode 100644
index 0000000000..17c6849692
--- /dev/null
+++ b/modules/libpref/test/unit/extdata/testExt.js
@@ -0,0 +1,2 @@
+pref("testPref.bool2", true);
+pref("testExtPref.bool", true);
diff --git a/modules/libpref/test/unit/head_libPrefs.js b/modules/libpref/test/unit/head_libPrefs.js
new file mode 100644
index 0000000000..ef7044a641
--- /dev/null
+++ b/modules/libpref/test/unit/head_libPrefs.js
@@ -0,0 +1,37 @@
+/* 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/. */
+
+const NS_APP_USER_PROFILE_50_DIR = "ProfD";
+
+function do_check_throws(f, result, stack) {
+ if (!stack) {
+ stack = Components.stack.caller;
+ }
+
+ try {
+ f();
+ } catch (exc) {
+ equal(exc.result, result, "Correct exception was thrown");
+ return;
+ }
+ ok(false, "expected result " + result + ", none thrown");
+}
+
+// Register current test directory as provider for the profile directory.
+var provider = {
+ getFile(prop, persistent) {
+ persistent.value = true;
+ if (prop == NS_APP_USER_PROFILE_50_DIR) {
+ return Services.dirsvc.get("CurProcD", Ci.nsIFile);
+ }
+ throw Components.Exception(
+ "Tried to get test directory '" + prop + "'",
+ Cr.NS_ERROR_FAILURE
+ );
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]),
+};
+Services.dirsvc
+ .QueryInterface(Ci.nsIDirectoryService)
+ .registerProvider(provider);
diff --git a/modules/libpref/test/unit/test_bug1354613.js b/modules/libpref/test/unit/test_bug1354613.js
new file mode 100644
index 0000000000..e609c6805a
--- /dev/null
+++ b/modules/libpref/test/unit/test_bug1354613.js
@@ -0,0 +1,21 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+function run_test() {
+ const BRANCH = "foo.";
+ const PREF_NAME = "testPref";
+ const FULL_PREF_NAME = BRANCH + PREF_NAME;
+
+ const FLOAT = 9.674;
+ const FUDGE = 0.001;
+
+ let prefs = Services.prefs.getDefaultBranch(null);
+
+ /* Test with a non-default branch */
+ prefs.setCharPref(FULL_PREF_NAME, FLOAT);
+ let pb = Services.prefs.getBranch(BRANCH);
+
+ let floatPref = pb.getFloatPref(PREF_NAME);
+ Assert.ok(FLOAT + FUDGE >= floatPref);
+ Assert.ok(FLOAT - FUDGE <= floatPref);
+}
diff --git a/modules/libpref/test/unit/test_bug345529.js b/modules/libpref/test/unit/test_bug345529.js
new file mode 100644
index 0000000000..05fce35f57
--- /dev/null
+++ b/modules/libpref/test/unit/test_bug345529.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Regression test for bug 345529 - crash removing an observer during an
+// nsPref:changed notification.
+function run_test() {
+ const PREF_NAME = "testPref";
+
+ var observer = {
+ QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+
+ observe: function observe(aSubject, aTopic, aState) {
+ Services.prefs.removeObserver(PREF_NAME, observer);
+ },
+ };
+ Services.prefs.addObserver(PREF_NAME, observer);
+
+ Services.prefs.setCharPref(PREF_NAME, "test0");
+ // This second call isn't needed on a clean profile: it makes sure
+ // the observer gets called even if the pref already had the value
+ // "test0" before this test.
+ Services.prefs.setCharPref(PREF_NAME, "test1");
+
+ Assert.ok(true);
+}
diff --git a/modules/libpref/test/unit/test_bug506224.js b/modules/libpref/test/unit/test_bug506224.js
new file mode 100644
index 0000000000..033c5acc6e
--- /dev/null
+++ b/modules/libpref/test/unit/test_bug506224.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+function run_test() {
+ const PREF_NAME = "testPref";
+
+ var prefs = Services.prefs.getDefaultBranch(null);
+ var userprefs = Services.prefs.getBranch(null);
+
+ prefs.setCharPref(PREF_NAME, "test0");
+ prefs.lockPref(PREF_NAME);
+ Assert.equal("test0", userprefs.getCharPref(PREF_NAME));
+ Assert.equal(false, userprefs.prefHasUserValue(PREF_NAME));
+
+ var file = do_get_profile();
+ file.append("prefs.js");
+ Services.prefs.savePrefFile(file);
+
+ prefs.unlockPref(PREF_NAME);
+ prefs.setCharPref(PREF_NAME, "test1");
+ Services.prefs.readUserPrefsFromFile(file);
+
+ Assert.equal("test1", userprefs.getCharPref(PREF_NAME));
+ Assert.equal(false, userprefs.prefHasUserValue(PREF_NAME));
+}
diff --git a/modules/libpref/test/unit/test_bug577950.js b/modules/libpref/test/unit/test_bug577950.js
new file mode 100644
index 0000000000..f9106a349f
--- /dev/null
+++ b/modules/libpref/test/unit/test_bug577950.js
@@ -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/. */
+
+function run_test() {
+ var observer = {
+ QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+
+ observe: function observe(aSubject, aTopic, aState) {
+ // Don't do anything.
+ },
+ };
+
+ /* Set the same pref twice. This shouldn't leak. */
+ Services.prefs.addObserver("UserPref.nonexistent.setIntPref", observer);
+ Services.prefs.addObserver("UserPref.nonexistent.setIntPref", observer);
+}
diff --git a/modules/libpref/test/unit/test_bug790374.js b/modules/libpref/test/unit/test_bug790374.js
new file mode 100644
index 0000000000..d4271a7ff7
--- /dev/null
+++ b/modules/libpref/test/unit/test_bug790374.js
@@ -0,0 +1,50 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+function run_test() {
+ const PREF_NAME = "testPref";
+
+ var prefs = Services.prefs.getDefaultBranch(null);
+ var userprefs = Services.prefs.getBranch(null);
+
+ /* First, test to make sure we can parse a float from a string properly. */
+ prefs.setCharPref(PREF_NAME, "9.674");
+ prefs.lockPref(PREF_NAME);
+ var myFloat = 9.674;
+ var fudge = 0.001;
+ var floatPref = userprefs.getFloatPref(PREF_NAME);
+ Assert.ok(myFloat + fudge >= floatPref);
+ Assert.ok(myFloat - fudge <= floatPref);
+
+ /* Now test some failure conditions. */
+ prefs.unlockPref(PREF_NAME);
+ prefs.setCharPref(PREF_NAME, "");
+ prefs.lockPref(PREF_NAME);
+ do_check_throws(function () {
+ userprefs.getFloatPref(PREF_NAME);
+ }, Cr.NS_ERROR_ILLEGAL_VALUE);
+
+ prefs.unlockPref(PREF_NAME);
+ prefs.setCharPref(PREF_NAME, "18.0a1");
+ prefs.lockPref(PREF_NAME);
+
+ do_check_throws(function () {
+ userprefs.getFloatPref(PREF_NAME);
+ }, Cr.NS_ERROR_ILLEGAL_VALUE);
+
+ prefs.unlockPref(PREF_NAME);
+ prefs.setCharPref(PREF_NAME, "09.25.2012");
+ prefs.lockPref(PREF_NAME);
+
+ do_check_throws(function () {
+ userprefs.getFloatPref(PREF_NAME);
+ }, Cr.NS_ERROR_ILLEGAL_VALUE);
+
+ prefs.unlockPref(PREF_NAME);
+ prefs.setCharPref(PREF_NAME, "aString");
+ prefs.lockPref(PREF_NAME);
+
+ do_check_throws(function () {
+ userprefs.getFloatPref(PREF_NAME);
+ }, Cr.NS_ERROR_ILLEGAL_VALUE);
+}
diff --git a/modules/libpref/test/unit/test_changeType.js b/modules/libpref/test/unit/test_changeType.js
new file mode 100644
index 0000000000..94f3a84f13
--- /dev/null
+++ b/modules/libpref/test/unit/test_changeType.js
@@ -0,0 +1,164 @@
+/* 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/. */
+
+/* Tests for changing the type of a preference (bug 985998) */
+
+function run_test() {
+ var ps = Services.prefs;
+
+ let defaultBranch = ps.getDefaultBranch("");
+ let userBranch = ps.getBranch("");
+
+ // Prefs that only have a default value -- we can't change their type.
+ defaultBranch.setBoolPref("TypeTest.default.bool", true);
+ defaultBranch.setIntPref("TypeTest.default.int", 23);
+ defaultBranch.setCharPref("TypeTest.default.char", "hey");
+
+ Assert.equal(userBranch.getBoolPref("TypeTest.default.bool"), true);
+ Assert.equal(userBranch.getIntPref("TypeTest.default.int"), 23);
+ Assert.equal(userBranch.getCharPref("TypeTest.default.char"), "hey");
+
+ // Prefs that only have a user value -- we can change their type, but only
+ // when we set the user value.
+ userBranch.setBoolPref("TypeTest.user.bool", false);
+ userBranch.setIntPref("TypeTest.user.int", 24);
+ userBranch.setCharPref("TypeTest.user.char", "hi");
+
+ Assert.equal(userBranch.getBoolPref("TypeTest.user.bool"), false);
+ Assert.equal(userBranch.getIntPref("TypeTest.user.int"), 24);
+ Assert.equal(userBranch.getCharPref("TypeTest.user.char"), "hi");
+
+ // Prefs that have both a default and a user value -- we can't change their
+ // type.
+ defaultBranch.setBoolPref("TypeTest.both.bool", true);
+ userBranch.setBoolPref("TypeTest.both.bool", false);
+ defaultBranch.setIntPref("TypeTest.both.int", 25);
+ userBranch.setIntPref("TypeTest.both.int", 26);
+ defaultBranch.setCharPref("TypeTest.both.char", "yo");
+ userBranch.setCharPref("TypeTest.both.char", "ya");
+
+ Assert.equal(userBranch.getBoolPref("TypeTest.both.bool"), false);
+ Assert.equal(userBranch.getIntPref("TypeTest.both.int"), 26);
+ Assert.equal(userBranch.getCharPref("TypeTest.both.char"), "ya");
+
+ // We only have a default value, and we try to set a default value of a
+ // different type --> fails.
+ do_check_throws(function () {
+ defaultBranch.setCharPref("TypeTest.default.bool", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setIntPref("TypeTest.default.bool", 5);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setCharPref("TypeTest.default.int", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setBoolPref("TypeTest.default.int", true);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setBoolPref("TypeTest.default.char", true);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setIntPref("TypeTest.default.char", 6);
+ }, Cr.NS_ERROR_UNEXPECTED);
+
+ // We only have a default value, and we try to set a user value of a
+ // different type --> fails.
+ do_check_throws(function () {
+ userBranch.setCharPref("TypeTest.default.bool", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ userBranch.setIntPref("TypeTest.default.bool", 5);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ userBranch.setCharPref("TypeTest.default.int", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ userBranch.setBoolPref("TypeTest.default.int", true);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ userBranch.setBoolPref("TypeTest.default.char", true);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ userBranch.setIntPref("TypeTest.default.char", 6);
+ }, Cr.NS_ERROR_UNEXPECTED);
+
+ // We only have a user value, and we try to set a default value of a
+ // different type --> fails.
+ do_check_throws(function () {
+ defaultBranch.setCharPref("TypeTest.user.bool", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setIntPref("TypeTest.user.bool", 5);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setCharPref("TypeTest.user.int", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setBoolPref("TypeTest.user.int", true);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setBoolPref("TypeTest.user.char", true);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setIntPref("TypeTest.user.char", 6);
+ }, Cr.NS_ERROR_UNEXPECTED);
+
+ // We only have a user value, and we try to set a user value of a
+ // different type --> SUCCEEDS.
+ userBranch.setCharPref("TypeTest.user.bool", "boo");
+ Assert.equal(userBranch.getCharPref("TypeTest.user.bool"), "boo");
+ userBranch.setIntPref("TypeTest.user.bool", 5);
+ Assert.equal(userBranch.getIntPref("TypeTest.user.bool"), 5);
+ userBranch.setCharPref("TypeTest.user.int", "boo");
+ Assert.equal(userBranch.getCharPref("TypeTest.user.int"), "boo");
+ userBranch.setBoolPref("TypeTest.user.int", true);
+ Assert.equal(userBranch.getBoolPref("TypeTest.user.int"), true);
+ userBranch.setBoolPref("TypeTest.user.char", true);
+ Assert.equal(userBranch.getBoolPref("TypeTest.user.char"), true);
+ userBranch.setIntPref("TypeTest.user.char", 6);
+ Assert.equal(userBranch.getIntPref("TypeTest.user.char"), 6);
+
+ // We have both a default value and user value, and we try to set a default
+ // value of a different type --> fails.
+ do_check_throws(function () {
+ defaultBranch.setCharPref("TypeTest.both.bool", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setIntPref("TypeTest.both.bool", 5);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setCharPref("TypeTest.both.int", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setBoolPref("TypeTest.both.int", true);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setBoolPref("TypeTest.both.char", true);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ defaultBranch.setIntPref("TypeTest.both.char", 6);
+ }, Cr.NS_ERROR_UNEXPECTED);
+
+ // We have both a default value and user value, and we try to set a user
+ // value of a different type --> fails.
+ do_check_throws(function () {
+ userBranch.setCharPref("TypeTest.both.bool", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ userBranch.setIntPref("TypeTest.both.bool", 5);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ userBranch.setCharPref("TypeTest.both.int", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ userBranch.setBoolPref("TypeTest.both.int", true);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ userBranch.setBoolPref("TypeTest.both.char", true);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ userBranch.setIntPref("TypeTest.both.char", 6);
+ }, Cr.NS_ERROR_UNEXPECTED);
+}
diff --git a/modules/libpref/test/unit/test_defaultValues.js b/modules/libpref/test/unit/test_defaultValues.js
new file mode 100644
index 0000000000..905675a1cf
--- /dev/null
+++ b/modules/libpref/test/unit/test_defaultValues.js
@@ -0,0 +1,59 @@
+/* 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/. */
+
+/* Tests for providing a default value to get{Bool,Char,Float,Int}Pref */
+
+function run_test() {
+ const ps = Services.prefs;
+ let prefName = "test.default.values.bool";
+ do_check_throws(function () {
+ ps.getBoolPref(prefName);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ strictEqual(ps.getBoolPref(prefName, false), false);
+ strictEqual(ps.getBoolPref(prefName, true), true);
+ ps.setBoolPref(prefName, true);
+ strictEqual(ps.getBoolPref(prefName), true);
+ strictEqual(ps.getBoolPref(prefName, false), true);
+ strictEqual(ps.getBoolPref(prefName, true), true);
+
+ prefName = "test.default.values.char";
+ do_check_throws(function () {
+ ps.getCharPref(prefName);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ strictEqual(ps.getCharPref(prefName, ""), "");
+ strictEqual(ps.getCharPref(prefName, "string"), "string");
+ ps.setCharPref(prefName, "foo");
+ strictEqual(ps.getCharPref(prefName), "foo");
+ strictEqual(ps.getCharPref(prefName, "string"), "foo");
+
+ prefName = "test.default.values.string";
+ do_check_throws(function () {
+ ps.getCharPref(prefName);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ strictEqual(ps.getStringPref(prefName, ""), "");
+ strictEqual(ps.getStringPref(prefName, "éèçàê€"), "éèçàê€");
+ ps.setStringPref(prefName, "éèçàê€");
+ strictEqual(ps.getStringPref(prefName), "éèçàê€");
+ strictEqual(ps.getStringPref(prefName, "string"), "éèçàê€");
+
+ prefName = "test.default.values.float";
+ do_check_throws(function () {
+ ps.getFloatPref(prefName);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ strictEqual(ps.getFloatPref(prefName, 3.5), 3.5);
+ strictEqual(ps.getFloatPref(prefName, 0), 0);
+ ps.setCharPref(prefName, 1.75);
+ strictEqual(ps.getFloatPref(prefName), 1.75);
+ strictEqual(ps.getFloatPref(prefName, 3.5), 1.75);
+
+ prefName = "test.default.values.int";
+ do_check_throws(function () {
+ ps.getIntPref(prefName);
+ }, Cr.NS_ERROR_UNEXPECTED);
+ strictEqual(ps.getIntPref(prefName, 3), 3);
+ strictEqual(ps.getIntPref(prefName, 0), 0);
+ ps.setIntPref(prefName, 42);
+ strictEqual(ps.getIntPref(prefName), 42);
+ strictEqual(ps.getIntPref(prefName, 3), 42);
+}
diff --git a/modules/libpref/test/unit/test_dirtyPrefs.js b/modules/libpref/test/unit/test_dirtyPrefs.js
new file mode 100644
index 0000000000..e107bef363
--- /dev/null
+++ b/modules/libpref/test/unit/test_dirtyPrefs.js
@@ -0,0 +1,69 @@
+/* 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/. */
+
+/* Tests for handling of the preferences 'dirty' flag (bug 985998) */
+
+function run_test() {
+ const ps = Services.prefs;
+
+ let defaultBranch = ps.getDefaultBranch("");
+ let userBranch = ps.getBranch("");
+
+ let prefFile = do_get_profile();
+ prefFile.append("prefs.js");
+
+ //* *************************************************************************//
+ // prefs are not dirty after a write
+ ps.savePrefFile(null);
+ Assert.ok(!ps.dirty);
+
+ // set a new a user value, we should become dirty
+ userBranch.setBoolPref("DirtyTest.new.bool", true);
+ Assert.ok(ps.dirty);
+ ps.savePrefFile(null);
+ // Overwrite a pref with the same value => not dirty
+ userBranch.setBoolPref("DirtyTest.new.bool", true);
+ Assert.ok(!ps.dirty);
+
+ // Repeat for the other two types
+ userBranch.setIntPref("DirtyTest.new.int", 1);
+ Assert.ok(ps.dirty);
+ ps.savePrefFile(null);
+ // Overwrite a pref with the same value => not dirty
+ userBranch.setIntPref("DirtyTest.new.int", 1);
+ Assert.ok(!ps.dirty);
+
+ userBranch.setCharPref("DirtyTest.new.char", "oop");
+ Assert.ok(ps.dirty);
+ ps.savePrefFile(null);
+ // Overwrite a pref with the same value => not dirty
+ userBranch.setCharPref("DirtyTest.new.char", "oop");
+ Assert.ok(!ps.dirty);
+
+ // change *type* of a user value -> dirty
+ userBranch.setBoolPref("DirtyTest.new.char", false);
+ Assert.ok(ps.dirty);
+ ps.savePrefFile(null);
+
+ // Set a default pref => not dirty (defaults don't go into prefs.js)
+ defaultBranch.setBoolPref("DirtyTest.existing.bool", true);
+ Assert.ok(!ps.dirty);
+ // Fail to change type of a pref with default value -> not dirty
+ do_check_throws(function () {
+ userBranch.setCharPref("DirtyTest.existing.bool", "boo");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ Assert.ok(!ps.dirty);
+
+ // Set user value same as default, not dirty
+ userBranch.setBoolPref("DirtyTest.existing.bool", true);
+ Assert.ok(!ps.dirty);
+ // User value different from default, dirty
+ userBranch.setBoolPref("DirtyTest.existing.bool", false);
+ Assert.ok(ps.dirty);
+ ps.savePrefFile(null);
+ // Back to default value, dirty again
+ userBranch.setBoolPref("DirtyTest.existing.bool", true);
+ Assert.ok(ps.dirty);
+ ps.savePrefFile(null);
+}
diff --git a/modules/libpref/test/unit/test_libPrefs.js b/modules/libpref/test/unit/test_libPrefs.js
new file mode 100644
index 0000000000..93651568be
--- /dev/null
+++ b/modules/libpref/test/unit/test_libPrefs.js
@@ -0,0 +1,450 @@
+/* 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/. */
+
+// It is necessary to manually disable `xpc::IsInAutomation` since
+// `resetPrefs` will flip the preference to re-enable `once`-synced
+// preference change assertions, and also change the value of those
+// preferences.
+Services.prefs.setBoolPref(
+ "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer",
+ false
+);
+
+const PREF_INVALID = 0;
+const PREF_BOOL = 128;
+const PREF_INT = 64;
+const PREF_STRING = 32;
+
+const MAX_PREF_LENGTH = 1 * 1024 * 1024;
+
+function makeList(a) {
+ var o = {};
+ for (var i = 0; i < a.length; i++) {
+ o[a[i]] = "";
+ }
+ return o;
+}
+
+function run_test() {
+ const ps = Services.prefs;
+
+ //* *************************************************************************//
+ // Nullsafety
+
+ do_check_throws(function () {
+ ps.getPrefType(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.getBoolPref(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.setBoolPref(null, false);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.getIntPref(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.setIntPref(null, 0);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.getCharPref(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.setCharPref(null, null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.getStringPref(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.setStringPref(null, null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.clearUserPref(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.prefHasUserValue(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.lockPref(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.prefIsLocked(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.unlockPref(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.deleteBranch(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+ do_check_throws(function () {
+ ps.getChildList(null);
+ }, Cr.NS_ERROR_INVALID_ARG);
+
+ //* *************************************************************************//
+ // Nonexisting user preferences
+
+ Assert.equal(ps.prefHasUserValue("UserPref.nonexistent.hasUserValue"), false);
+ ps.clearUserPref("UserPref.nonexistent.clearUserPref"); // shouldn't throw
+ Assert.equal(
+ ps.getPrefType("UserPref.nonexistent.getPrefType"),
+ PREF_INVALID
+ );
+ Assert.equal(ps.root, "");
+
+ // bool...
+ do_check_throws(function () {
+ ps.getBoolPref("UserPref.nonexistent.getBoolPref");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ ps.setBoolPref("UserPref.nonexistent.setBoolPref", false);
+ Assert.equal(ps.getBoolPref("UserPref.nonexistent.setBoolPref"), false);
+
+ // int...
+ do_check_throws(function () {
+ ps.getIntPref("UserPref.nonexistent.getIntPref");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ ps.setIntPref("UserPref.nonexistent.setIntPref", 5);
+ Assert.equal(ps.getIntPref("UserPref.nonexistent.setIntPref"), 5);
+
+ // char
+ do_check_throws(function () {
+ ps.getCharPref("UserPref.nonexistent.getCharPref");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ ps.setCharPref("UserPref.nonexistent.setCharPref", "_test");
+ Assert.equal(ps.getCharPref("UserPref.nonexistent.setCharPref"), "_test");
+
+ //* *************************************************************************//
+ // Existing user Prefs and data integrity test (round-trip match)
+
+ ps.setBoolPref("UserPref.existing.bool", true);
+ ps.setIntPref("UserPref.existing.int", 23);
+ ps.setCharPref("UserPref.existing.char", "hey");
+
+ // getPref should return the pref value
+ Assert.equal(ps.getBoolPref("UserPref.existing.bool"), true);
+ Assert.equal(ps.getIntPref("UserPref.existing.int"), 23);
+ Assert.equal(ps.getCharPref("UserPref.existing.char"), "hey");
+
+ // setPref should not complain and should change the value of the pref
+ ps.setBoolPref("UserPref.existing.bool", false);
+ Assert.equal(ps.getBoolPref("UserPref.existing.bool"), false);
+ ps.setIntPref("UserPref.existing.int", 24);
+ Assert.equal(ps.getIntPref("UserPref.existing.int"), 24);
+ ps.setCharPref("UserPref.existing.char", "hej då!");
+ Assert.equal(ps.getCharPref("UserPref.existing.char"), "hej då!");
+
+ // prefHasUserValue should return true now
+ Assert.ok(ps.prefHasUserValue("UserPref.existing.bool"));
+ Assert.ok(ps.prefHasUserValue("UserPref.existing.int"));
+ Assert.ok(ps.prefHasUserValue("UserPref.existing.char"));
+
+ // clearUserPref should remove the pref
+ ps.clearUserPref("UserPref.existing.bool");
+ Assert.ok(!ps.prefHasUserValue("UserPref.existing.bool"));
+ ps.clearUserPref("UserPref.existing.int");
+ Assert.ok(!ps.prefHasUserValue("UserPref.existing.int"));
+ ps.clearUserPref("UserPref.existing.char");
+ Assert.ok(!ps.prefHasUserValue("UserPref.existing.char"));
+
+ //* *************************************************************************//
+ // Large value test
+
+ let largeStr = new Array(MAX_PREF_LENGTH + 1).join("x");
+ ps.setCharPref("UserPref.large.char", largeStr);
+ largeStr += "x";
+ do_check_throws(function () {
+ ps.setCharPref("UserPref.large.char", largeStr);
+ }, Cr.NS_ERROR_ILLEGAL_VALUE);
+
+ //* *************************************************************************//
+ // getPrefType test
+
+ // bool...
+ ps.setBoolPref("UserPref.getPrefType.bool", true);
+ Assert.equal(ps.getPrefType("UserPref.getPrefType.bool"), PREF_BOOL);
+
+ // int...
+ ps.setIntPref("UserPref.getPrefType.int", -234);
+ Assert.equal(ps.getPrefType("UserPref.getPrefType.int"), PREF_INT);
+
+ // char...
+ ps.setCharPref("UserPref.getPrefType.char", "testing1..2");
+ Assert.equal(ps.getPrefType("UserPref.getPrefType.char"), PREF_STRING);
+
+ //* *************************************************************************//
+ // getBranch tests
+
+ Assert.equal(ps.root, "");
+
+ // bool ...
+ ps.setBoolPref("UserPref.root.boolPref", true);
+ let pb_1 = ps.getBranch("UserPref.root.");
+ Assert.equal(pb_1.getBoolPref("boolPref"), true);
+ let pb_2 = ps.getBranch("UserPref.root.boolPref");
+ Assert.equal(pb_2.getBoolPref(""), true);
+ pb_2.setBoolPref(".anotherPref", false);
+ let pb_3 = ps.getBranch("UserPref.root.boolPre");
+ Assert.equal(pb_3.getBoolPref("f.anotherPref"), false);
+
+ // int ...
+ ps.setIntPref("UserPref.root.intPref", 23);
+ pb_1 = ps.getBranch("UserPref.root.");
+ Assert.equal(pb_1.getIntPref("intPref"), 23);
+ pb_2 = ps.getBranch("UserPref.root.intPref");
+ Assert.equal(pb_2.getIntPref(""), 23);
+ pb_2.setIntPref(".anotherPref", 69);
+ pb_3 = ps.getBranch("UserPref.root.intPre");
+ Assert.equal(pb_3.getIntPref("f.anotherPref"), 69);
+
+ // char...
+ ps.setCharPref("UserPref.root.charPref", "_char");
+ pb_1 = ps.getBranch("UserPref.root.");
+ Assert.equal(pb_1.getCharPref("charPref"), "_char");
+ pb_2 = ps.getBranch("UserPref.root.charPref");
+ Assert.equal(pb_2.getCharPref(""), "_char");
+ pb_2.setCharPref(".anotherPref", "_another");
+ pb_3 = ps.getBranch("UserPref.root.charPre");
+ Assert.equal(pb_3.getCharPref("f.anotherPref"), "_another");
+
+ //* *************************************************************************//
+ // getChildlist tests
+
+ // get an already set prefBranch
+ let pb1 = ps.getBranch("UserPref.root.");
+ let prefList = pb1.getChildList("");
+ Assert.equal(prefList.length, 6);
+
+ // check for specific prefs in the array : the order is not important
+ Assert.ok("boolPref" in makeList(prefList));
+ Assert.ok("intPref" in makeList(prefList));
+ Assert.ok("charPref" in makeList(prefList));
+ Assert.ok("boolPref.anotherPref" in makeList(prefList));
+ Assert.ok("intPref.anotherPref" in makeList(prefList));
+ Assert.ok("charPref.anotherPref" in makeList(prefList));
+
+ //* *************************************************************************//
+ // Default branch tests
+
+ // bool...
+ pb1 = ps.getDefaultBranch("");
+ pb1.setBoolPref("DefaultPref.bool", true);
+ Assert.equal(pb1.getBoolPref("DefaultPref.bool"), true);
+ Assert.ok(!pb1.prefHasUserValue("DefaultPref.bool"));
+ ps.setBoolPref("DefaultPref.bool", false);
+ Assert.ok(pb1.prefHasUserValue("DefaultPref.bool"));
+ Assert.equal(ps.getBoolPref("DefaultPref.bool"), false);
+
+ // int...
+ pb1 = ps.getDefaultBranch("");
+ pb1.setIntPref("DefaultPref.int", 100);
+ Assert.equal(pb1.getIntPref("DefaultPref.int"), 100);
+ Assert.ok(!pb1.prefHasUserValue("DefaultPref.int"));
+ ps.setIntPref("DefaultPref.int", 50);
+ Assert.ok(pb1.prefHasUserValue("DefaultPref.int"));
+ Assert.equal(ps.getIntPref("DefaultPref.int"), 50);
+
+ // char...
+ pb1 = ps.getDefaultBranch("");
+ pb1.setCharPref("DefaultPref.char", "_default");
+ Assert.equal(pb1.getCharPref("DefaultPref.char"), "_default");
+ Assert.ok(!pb1.prefHasUserValue("DefaultPref.char"));
+ ps.setCharPref("DefaultPref.char", "_user");
+ Assert.ok(pb1.prefHasUserValue("DefaultPref.char"));
+ Assert.equal(ps.getCharPref("DefaultPref.char"), "_user");
+
+ //* *************************************************************************//
+ // pref Locking/Unlocking tests
+
+ // locking and unlocking a nonexistent pref should throw
+ do_check_throws(function () {
+ ps.lockPref("DefaultPref.nonexistent");
+ }, Cr.NS_ERROR_ILLEGAL_VALUE);
+ do_check_throws(function () {
+ ps.unlockPref("DefaultPref.nonexistent");
+ }, Cr.NS_ERROR_ILLEGAL_VALUE);
+
+ // getting a locked pref branch should return the "default" value
+ Assert.ok(!ps.prefIsLocked("DefaultPref.char"));
+ ps.lockPref("DefaultPref.char");
+ Assert.equal(ps.getCharPref("DefaultPref.char"), "_default");
+ Assert.ok(ps.prefIsLocked("DefaultPref.char"));
+
+ // getting an unlocked pref branch should return the "user" value
+ ps.unlockPref("DefaultPref.char");
+ Assert.equal(ps.getCharPref("DefaultPref.char"), "_user");
+ Assert.ok(!ps.prefIsLocked("DefaultPref.char"));
+
+ // setting the "default" value to a user pref branch should
+ // make prefHasUserValue return false (documented behavior)
+ ps.setCharPref("DefaultPref.char", "_default");
+ Assert.ok(!pb1.prefHasUserValue("DefaultPref.char"));
+
+ // unlocking and locking multiple times shouldn't throw
+ ps.unlockPref("DefaultPref.char");
+ ps.lockPref("DefaultPref.char");
+ ps.lockPref("DefaultPref.char");
+
+ //* *************************************************************************//
+ // resetBranch test
+
+ // NOT IMPLEMENTED YET in module/libpref. So we're not testing !
+ // uncomment the following if resetBranch ever gets implemented.
+ /* ps.resetBranch("DefaultPref");
+ do_check_eq(ps.getBoolPref("DefaultPref.bool"), true);
+ do_check_eq(ps.getIntPref("DefaultPref.int"), 100);
+ do_check_eq(ps.getCharPref("DefaultPref.char"), "_default");*/
+
+ //* *************************************************************************//
+ // deleteBranch tests
+
+ // TODO : Really, this should throw!, by documentation.
+ // do_check_throws(function() {
+ // ps.deleteBranch("UserPref.nonexistent.deleteBranch");}, Cr.NS_ERROR_UNEXPECTED);
+
+ ps.deleteBranch("DefaultPref");
+ let pb = ps.getBranch("DefaultPref");
+ pb1 = ps.getDefaultBranch("DefaultPref");
+
+ // getting prefs on deleted user branches should throw
+ do_check_throws(function () {
+ pb.getBoolPref("DefaultPref.bool");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ pb.getIntPref("DefaultPref.int");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ pb.getCharPref("DefaultPref.char");
+ }, Cr.NS_ERROR_UNEXPECTED);
+
+ // getting prefs on deleted default branches should throw
+ do_check_throws(function () {
+ pb1.getBoolPref("DefaultPref.bool");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ pb1.getIntPref("DefaultPref.int");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ pb1.getCharPref("DefaultPref.char");
+ }, Cr.NS_ERROR_UNEXPECTED);
+
+ //* *************************************************************************//
+ // savePrefFile & readPrefFile tests
+
+ // set some prefs
+ ps.setBoolPref("ReadPref.bool", true);
+ ps.setIntPref("ReadPref.int", 230);
+ ps.setCharPref("ReadPref.char", "hello");
+
+ // save those prefs in a file
+ let savePrefFile = do_get_cwd();
+ savePrefFile.append("data");
+ savePrefFile.append("savePref.js");
+
+ if (savePrefFile.exists()) {
+ savePrefFile.remove(false);
+ }
+ savePrefFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+ ps.savePrefFile(savePrefFile);
+ ps.resetPrefs();
+
+ // load a preexisting pref file
+ let prefFile = do_get_file("data/testPref.js");
+ ps.readUserPrefsFromFile(prefFile);
+
+ // former prefs should have been replaced/lost
+ do_check_throws(function () {
+ pb.getBoolPref("ReadPref.bool");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ pb.getIntPref("ReadPref.int");
+ }, Cr.NS_ERROR_UNEXPECTED);
+ do_check_throws(function () {
+ pb.getCharPref("ReadPref.char");
+ }, Cr.NS_ERROR_UNEXPECTED);
+
+ // loaded prefs should read ok.
+ pb = ps.getBranch("testPref.");
+ Assert.equal(pb.getBoolPref("bool1"), true);
+ Assert.equal(pb.getBoolPref("bool2"), false);
+ Assert.equal(pb.getIntPref("int1"), 23);
+ Assert.equal(pb.getIntPref("int2"), -1236);
+ Assert.equal(pb.getCharPref("char1"), "_testPref");
+ Assert.equal(pb.getCharPref("char2"), "älskar");
+
+ // loading our former savePrefFile should allow us to read former prefs
+
+ // Hack alert: on Windows nsLocalFile caches the size of savePrefFile from
+ // the .create() call above as 0. We call .exists() to reset the cache.
+ savePrefFile.exists();
+
+ ps.readUserPrefsFromFile(savePrefFile);
+ // cleanup the file now we don't need it
+ savePrefFile.remove(false);
+ Assert.equal(ps.getBoolPref("ReadPref.bool"), true);
+ Assert.equal(ps.getIntPref("ReadPref.int"), 230);
+ Assert.equal(ps.getCharPref("ReadPref.char"), "hello");
+
+ // ... and still be able to access "prior-to-readUserPrefs" preferences
+ Assert.equal(pb.getBoolPref("bool1"), true);
+ Assert.equal(pb.getBoolPref("bool2"), false);
+ Assert.equal(pb.getIntPref("int1"), 23);
+
+ //* *************************************************************************//
+ // preference Observers
+
+ class PrefObserver {
+ /**
+ * Creates and registers a pref observer.
+ *
+ * @param prefBranch The preference branch instance to observe.
+ * @param expectedName The pref name we expect to receive.
+ * @param expectedValue The int pref value we expect to receive.
+ */
+ constructor(prefBranch, expectedName, expectedValue) {
+ this.pb = prefBranch;
+ this.name = expectedName;
+ this.value = expectedValue;
+
+ prefBranch.addObserver(expectedName, this);
+ }
+
+ QueryInterface(aIID) {
+ if (aIID.equals(Ci.nsIObserver) || aIID.equals(Ci.nsISupports)) {
+ return this;
+ }
+ throw Components.Exception("", Cr.NS_NOINTERFACE);
+ }
+
+ observe(aSubject, aTopic, aState) {
+ Assert.equal(aTopic, "nsPref:changed");
+ Assert.equal(aState, this.name);
+ Assert.equal(this.pb.getIntPref(aState), this.value);
+ pb.removeObserver(aState, this);
+
+ // notification received, we may go on...
+ do_test_finished();
+ }
+ }
+
+ // Indicate that we'll have 3 more async tests pending so that they all
+ // actually get a chance to run.
+ do_test_pending();
+ do_test_pending();
+ do_test_pending();
+
+ let observer = new PrefObserver(ps, "ReadPref.int", 76);
+ ps.setIntPref("ReadPref.int", 76);
+
+ // removed observer should not fire
+ ps.removeObserver("ReadPref.int", observer);
+ ps.setIntPref("ReadPref.int", 32);
+
+ // let's test observers once more with a non-root prefbranch
+ pb = ps.getBranch("ReadPref.");
+ observer = new PrefObserver(pb, "int", 76);
+ ps.setIntPref("ReadPref.int", 76);
+
+ // Let's try that again with different pref.
+ observer = new PrefObserver(pb, "another_int", 76);
+ ps.setIntPref("ReadPref.another_int", 76);
+}
diff --git a/modules/libpref/test/unit/test_locked_file_prefs.js b/modules/libpref/test/unit/test_locked_file_prefs.js
new file mode 100644
index 0000000000..29b5270df5
--- /dev/null
+++ b/modules/libpref/test/unit/test_locked_file_prefs.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// This file tests the `locked` attribute in default pref files.
+
+const ps = Services.prefs;
+
+// It is necessary to manually disable `xpc::IsInAutomation` since
+// `resetPrefs` will flip the preference to re-enable `once`-synced
+// preference change assertions, and also change the value of those
+// preferences.
+Services.prefs.setBoolPref(
+ "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer",
+ false
+);
+
+add_test(function notChangedFromAPI() {
+ ps.resetPrefs();
+ ps.readDefaultPrefsFromFile(do_get_file("data/testPrefLocked.js"));
+ Assert.strictEqual(ps.getIntPref("testPref.unlocked.int"), 333);
+ Assert.strictEqual(ps.getIntPref("testPref.locked.int"), 444);
+
+ // Unlocked pref: can set the user value, which is used upon reading.
+ ps.setIntPref("testPref.unlocked.int", 334);
+ Assert.ok(ps.prefHasUserValue("testPref.unlocked.int"), "has a user value");
+ Assert.strictEqual(ps.getIntPref("testPref.unlocked.int"), 334);
+
+ // Locked pref: can set the user value, but the default value is used upon
+ // reading.
+ ps.setIntPref("testPref.locked.int", 445);
+ Assert.ok(ps.prefHasUserValue("testPref.locked.int"), "has a user value");
+ Assert.strictEqual(ps.getIntPref("testPref.locked.int"), 444);
+
+ // After unlocking, the user value is used.
+ ps.unlockPref("testPref.locked.int");
+ Assert.ok(ps.prefHasUserValue("testPref.locked.int"), "has a user value");
+ Assert.strictEqual(ps.getIntPref("testPref.locked.int"), 445);
+
+ run_next_test();
+});
+
+add_test(function notChangedFromUserPrefs() {
+ ps.resetPrefs();
+ ps.readDefaultPrefsFromFile(do_get_file("data/testPrefLocked.js"));
+ ps.readUserPrefsFromFile(do_get_file("data/testPrefLockedUser.js"));
+
+ Assert.strictEqual(ps.getIntPref("testPref.unlocked.int"), 333);
+ Assert.strictEqual(ps.getIntPref("testPref.locked.int"), 444);
+
+ run_next_test();
+});
diff --git a/modules/libpref/test/unit/test_parsePrefs.js b/modules/libpref/test/unit/test_parsePrefs.js
new file mode 100644
index 0000000000..53bb55dc7e
--- /dev/null
+++ b/modules/libpref/test/unit/test_parsePrefs.js
@@ -0,0 +1,136 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+"use strict";
+
+// // This undoes some of the configuration from
+// Services.dirsvc
+// .QueryInterface(Ci.nsIDirectoryService)
+// .unregisterProvider(provider);
+
+class PrefObserver {
+ constructor() {
+ this.events = [];
+ }
+
+ onStringPref(...args) {
+ // // What happens with an exception here?
+ // throw new Error(`foo ${args[1]}`);
+ this.events.push(["string", ...args]);
+ }
+
+ onIntPref(...args) {
+ this.events.push(["int", ...args]);
+ }
+
+ onBoolPref(...args) {
+ this.events.push(["bool", ...args]);
+ }
+
+ onError(message) {
+ this.events.push(["error", message]);
+ }
+}
+
+const TESTS = {
+ "data/testPrefSticky.js": [
+ ["bool", "Default", "testPref.unsticky.bool", true, false, false],
+ ["bool", "Default", "testPref.sticky.bool", false, true, false],
+ ],
+ "data/testPrefStickyUser.js": [
+ ["bool", "User", "testPref.sticky.bool", false, false, false],
+ ],
+ "data/testPrefLocked.js": [
+ ["int", "Default", "testPref.unlocked.int", 333, false, false],
+ ["int", "Default", "testPref.locked.int", 444, false, true],
+ ],
+ // data/testPrefLockedUser is ASCII.
+ "data/testPrefLockedUser.js": [
+ ["int", "User", "testPref.locked.int", 555, false, false],
+ ],
+ // data/testPref is ISO-8859.
+ "data/testPref.js": [
+ ["bool", "User", "testPref.bool1", true, false, false],
+ ["bool", "User", "testPref.bool2", false, false, false],
+ ["int", "User", "testPref.int1", 23, false, false],
+ ["int", "User", "testPref.int2", -1236, false, false],
+ ["string", "User", "testPref.char1", "_testPref", false, false],
+ ["string", "User", "testPref.char2", "älskar", false, false],
+ ],
+ // data/testParsePrefs is data/testPref.js as UTF-8.
+ "data/testPrefUTF8.js": [
+ ["bool", "User", "testPref.bool1", true, false, false],
+ ["bool", "User", "testPref.bool2", false, false, false],
+ ["int", "User", "testPref.int1", 23, false, false],
+ ["int", "User", "testPref.int2", -1236, false, false],
+ ["string", "User", "testPref.char1", "_testPref", false, false],
+ // Observe that this is the ISO-8859/Latin-1 encoding of the UTF-8 bytes.
+ // (Note that this source file is encoded as UTF-8.) This appears to just
+ // be how libpref handles this case. This test serves as documentation of
+ // this infelicity.
+ ["string", "User", "testPref.char2", "älskar", false, false],
+ ],
+};
+
+add_task(async function test_success() {
+ for (let [path, expected] of Object.entries(TESTS)) {
+ let prefObserver = new PrefObserver();
+
+ let prefsFile = do_get_file(path);
+ let data = await IOUtils.read(prefsFile.path);
+
+ Services.prefs.parsePrefsFromBuffer(data, prefObserver, path);
+ Assert.deepEqual(
+ prefObserver.events,
+ expected,
+ `Observations from ${path} are as expected`
+ );
+ }
+});
+
+add_task(async function test_exceptions() {
+ const { AddonTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/AddonTestUtils.sys.mjs"
+ );
+
+ let s = `user_pref("testPref.bool1", true);
+ user_pref("testPref.bool2", false);
+ user_pref("testPref.int1", 23);
+ user_pref("testPref.int2", -1236);
+ `;
+
+ let onErrorCount = 0;
+ let addPrefCount = 0;
+
+ let marker = "2fc599a1-433b-4de7-bd63-3e69c3bbad5b";
+ let addPref = (...args) => {
+ addPrefCount += 1;
+ throw new Error(`${marker}${JSON.stringify(args)}${marker}`);
+ };
+
+ let { messages } = await AddonTestUtils.promiseConsoleOutput(() => {
+ Services.prefs.parsePrefsFromBuffer(new TextEncoder().encode(s), {
+ onStringPref: addPref,
+ onIntPref: addPref,
+ onBoolPref: addPref,
+ onError(message) {
+ onErrorCount += 1;
+ console.error(message);
+ },
+ });
+ });
+
+ Assert.equal(addPrefCount, 4);
+ Assert.equal(onErrorCount, 0);
+
+ // This is fragile but mercifully simple.
+ Assert.deepEqual(
+ messages.map(m => JSON.parse(m.message.split(marker)[1])),
+ [
+ ["User", "testPref.bool1", true, false, false],
+ ["User", "testPref.bool2", false, false, false],
+ ["User", "testPref.int1", 23, false, false],
+ ["User", "testPref.int2", -1236, false, false],
+ ]
+ );
+});
diff --git a/modules/libpref/test/unit/test_parser.js b/modules/libpref/test/unit/test_parser.js
new file mode 100644
index 0000000000..0b37eef47d
--- /dev/null
+++ b/modules/libpref/test/unit/test_parser.js
@@ -0,0 +1,116 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// It is necessary to manually disable `xpc::IsInAutomation` since
+// `resetPrefs` will flip the preference to re-enable `once`-synced
+// preference change assertions, and also change the value of those
+// preferences.
+Services.prefs.setBoolPref(
+ "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer",
+ false
+);
+
+function run_test() {
+ const ps = Services.prefs;
+
+ ps.resetPrefs();
+ ps.readDefaultPrefsFromFile(do_get_file("data/testParser.js"));
+
+ Assert.equal(ps.getBoolPref("comment1"), true);
+ Assert.equal(ps.getBoolPref("comment2"), true);
+ Assert.equal(ps.getBoolPref("spaced-out"), true);
+
+ Assert.equal(ps.getBoolPref("pref"), true);
+ Assert.equal(ps.getBoolPref("sticky_pref"), true);
+ Assert.equal(ps.getBoolPref("user_pref"), true);
+ Assert.equal(ps.getBoolPref("sticky_pref2"), true);
+ Assert.equal(ps.getBoolPref("locked_pref"), true);
+ Assert.equal(ps.getBoolPref("locked_sticky_pref"), true);
+ Assert.equal(ps.prefIsLocked("locked_pref"), true);
+ Assert.equal(ps.prefIsLocked("locked_sticky_pref"), true);
+
+ Assert.equal(ps.getBoolPref("bool.true"), true);
+ Assert.equal(ps.getBoolPref("bool.false"), false);
+
+ Assert.equal(ps.getIntPref("int.0"), 0);
+ Assert.equal(ps.getIntPref("int.1"), 1);
+ Assert.equal(ps.getIntPref("int.123"), 123);
+ Assert.equal(ps.getIntPref("int.+234"), 234);
+ Assert.equal(ps.getIntPref("int.+ 345"), 345);
+ Assert.equal(ps.getIntPref("int.-0"), -0);
+ Assert.equal(ps.getIntPref("int.-1"), -1);
+ Assert.equal(ps.getIntPref("int.- /* hmm */\t456"), -456);
+ Assert.equal(ps.getIntPref("int.-\n567"), -567);
+ Assert.equal(ps.getIntPref("int.INT_MAX-1"), 2147483646);
+ Assert.equal(ps.getIntPref("int.INT_MAX"), 2147483647);
+ Assert.equal(ps.getIntPref("int.INT_MIN+2"), -2147483646);
+ Assert.equal(ps.getIntPref("int.INT_MIN+1"), -2147483647);
+ Assert.equal(ps.getIntPref("int.INT_MIN"), -2147483648);
+
+ Assert.equal(ps.getCharPref("string.empty"), "");
+ Assert.equal(ps.getCharPref("string.abc"), "abc");
+ Assert.equal(
+ ps.getCharPref("string.long"),
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ );
+ Assert.equal(ps.getCharPref("string.single-quotes"), '"abc"');
+ Assert.equal(ps.getCharPref("string.double-quotes"), "'abc'");
+ Assert.equal(
+ ps.getCharPref("string.weird-chars"),
+ "\x0d \x09 \x0b \x0c \x06 \x16"
+ );
+ Assert.equal(ps.getCharPref("string.escapes"), "\" ' \\ \n \r");
+
+ // This one is ASCII, so we can use getCharPref() and getStringPref
+ // interchangeably.
+ Assert.equal(
+ ps.getCharPref("string.x-escapes1"),
+ "Mozilla0\x4d\x6F\x7a\x69\x6c\x6C\x610"
+ );
+ Assert.equal(ps.getStringPref("string.x-escapes1"), "Mozilla0Mozilla0");
+
+ // This one has chars with value > 127, so it's not valid UTF8, so we can't
+ // use getStringPref on it.
+ Assert.equal(
+ ps.getCharPref("string.x-escapes2"),
+ "AA A_umlaut\xc4 y_umlaut\xff"
+ );
+
+ // The following strings use \uNNNN escapes, which are UTF16 code points.
+ // libpref stores them internally as UTF8 byte sequences. In each case we get
+ // the string in two ways:
+ // - getStringPref() interprets it as UTF8, which is then converted to UTF16
+ // in JS. I.e. code points that are multiple bytes in UTF8 become a single
+ // 16-bit char in JS (except for the non-BMP chars, which become a 16-bit
+ // surrogate pair).
+ // - getCharPref() interprets it as Latin1, which is then converted to UTF16
+ // in JS. I.e. code points that are multiple bytes in UTF8 become multiple
+ // 16-bit chars in JS.
+
+ Assert.equal(
+ ps.getStringPref("string.u-escapes1"),
+ "A\u0041 A_umlaut\u00c4 y_umlaut\u00ff0"
+ );
+ Assert.equal(
+ ps.getCharPref("string.u-escapes1"),
+ "A\x41 A_umlaut\xc3\x84 y_umlaut\xc3\xbf0"
+ );
+
+ Assert.equal(
+ ps.getStringPref("string.u-escapes2"),
+ "S_acute\u015a y_grave\u1Ef3"
+ );
+ Assert.equal(
+ ps.getCharPref("string.u-escapes2"),
+ "S_acute\xc5\x9a y_grave\xe1\xbb\xb3"
+ );
+
+ Assert.equal(
+ ps.getStringPref("string.u-surrogates"),
+ "cyclone\uD83C\uDF00 grinning_face\uD83D\uDE00"
+ );
+ Assert.equal(
+ ps.getCharPref("string.u-surrogates"),
+ "cyclone\xF0\x9F\x8C\x80 grinning_face\xF0\x9F\x98\x80"
+ );
+}
diff --git a/modules/libpref/test/unit/test_stickyprefs.js b/modules/libpref/test/unit/test_stickyprefs.js
new file mode 100644
index 0000000000..d344d208ab
--- /dev/null
+++ b/modules/libpref/test/unit/test_stickyprefs.js
@@ -0,0 +1,196 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+const ps = Services.prefs;
+
+// It is necessary to manually disable `xpc::IsInAutomation` since
+// `resetPrefs` will flip the preference to re-enable `once`-synced
+// preference change assertions, and also change the value of those
+// preferences.
+Services.prefs.setBoolPref(
+ "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer",
+ false
+);
+
+// A little helper to reset the service and load one pref file.
+function resetAndLoadDefaults() {
+ ps.resetPrefs();
+ ps.readDefaultPrefsFromFile(do_get_file("data/testPrefSticky.js"));
+}
+
+// A little helper to reset the service and load two pref files.
+function resetAndLoadAll() {
+ ps.resetPrefs();
+ ps.readDefaultPrefsFromFile(do_get_file("data/testPrefSticky.js"));
+ ps.readUserPrefsFromFile(do_get_file("data/testPrefStickyUser.js"));
+}
+
+// A little helper that saves the current state to a file in the profile
+// dir, then resets the service and re-reads the file it just saved.
+// Used to test what gets actually written - things the pref service decided
+// not to write don't exist at all after this call.
+function saveAndReload() {
+ let file = do_get_profile();
+ file.append("prefs.js");
+ ps.savePrefFile(file);
+
+ // Now reset the pref service and re-read what we saved.
+ ps.resetPrefs();
+
+ // Hack alert: on Windows nsLocalFile caches the size of savePrefFile from
+ // the .create() call above as 0. We call .exists() to reset the cache.
+ file.exists();
+
+ ps.readUserPrefsFromFile(file);
+}
+
+// A sticky pref should not be written if the value is unchanged.
+add_test(function notWrittenWhenUnchanged() {
+ resetAndLoadDefaults();
+ Assert.strictEqual(ps.getBoolPref("testPref.unsticky.bool"), true);
+ Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false);
+
+ // write prefs - but we haven't changed the sticky one, so it shouldn't be written.
+ saveAndReload();
+ // sticky should not have been written to the new file.
+ try {
+ ps.getBoolPref("testPref.sticky.bool");
+ Assert.ok(false, "expected failure reading this pref");
+ } catch (ex) {
+ Assert.ok(ex, "exception reading regular pref");
+ }
+ run_next_test();
+});
+
+// Loading a sticky `pref` then a `user_pref` for the same pref means it should
+// always be written.
+add_test(function writtenOnceLoadedWithoutChange() {
+ // Load the same pref file *as well as* a pref file that has a user_pref for
+ // our sticky with the default value. It should be re-written without us
+ // touching it.
+ resetAndLoadAll();
+ // reset and re-read what we just wrote - it should be written.
+ saveAndReload();
+ Assert.strictEqual(
+ ps.getBoolPref("testPref.sticky.bool"),
+ false,
+ "user_pref was written with default value"
+ );
+ run_next_test();
+});
+
+// If a sticky pref is explicicitly changed, even to the default, it is written.
+add_test(function writtenOnceLoadedWithChangeNonDefault() {
+ // Load the same pref file *as well as* a pref file that has a user_pref for
+ // our sticky - then change the pref. It should be written.
+ resetAndLoadAll();
+ // Set a new val and check we wrote it.
+ ps.setBoolPref("testPref.sticky.bool", false);
+ saveAndReload();
+ Assert.strictEqual(
+ ps.getBoolPref("testPref.sticky.bool"),
+ false,
+ "user_pref was written with custom value"
+ );
+ run_next_test();
+});
+
+// If a sticky pref is changed to the non-default value, it is written.
+add_test(function writtenOnceLoadedWithChangeNonDefault() {
+ // Load the same pref file *as well as* a pref file that has a user_pref for
+ // our sticky - then change the pref. It should be written.
+ resetAndLoadAll();
+ // Set a new val and check we wrote it.
+ ps.setBoolPref("testPref.sticky.bool", true);
+ saveAndReload();
+ Assert.strictEqual(
+ ps.getBoolPref("testPref.sticky.bool"),
+ true,
+ "user_pref was written with custom value"
+ );
+ run_next_test();
+});
+
+// Test that prefHasUserValue always returns true whenever there is a sticky
+// value, even when that value matches the default. This is mainly for
+// about:config semantics - prefs with a sticky value always remain bold and
+// always offer "reset" (which fully resets and drops the sticky value as if
+// the pref had never changed.)
+add_test(function hasUserValue() {
+ // sticky pref without user value.
+ resetAndLoadDefaults();
+ Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false);
+ Assert.ok(
+ !ps.prefHasUserValue("testPref.sticky.bool"),
+ "should not initially reflect a user value"
+ );
+
+ ps.setBoolPref("testPref.sticky.bool", false);
+ Assert.ok(
+ ps.prefHasUserValue("testPref.sticky.bool"),
+ "should reflect a user value after set to default"
+ );
+
+ ps.setBoolPref("testPref.sticky.bool", true);
+ Assert.ok(
+ ps.prefHasUserValue("testPref.sticky.bool"),
+ "should reflect a user value after change to non-default"
+ );
+
+ ps.clearUserPref("testPref.sticky.bool");
+ Assert.ok(
+ !ps.prefHasUserValue("testPref.sticky.bool"),
+ "should reset to no user value"
+ );
+ ps.setBoolPref("testPref.sticky.bool", false, "expected default");
+
+ // And make sure the pref immediately reflects a user value after load.
+ resetAndLoadAll();
+ Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false);
+ Assert.ok(
+ ps.prefHasUserValue("testPref.sticky.bool"),
+ "should have a user value when loaded value is the default"
+ );
+ run_next_test();
+});
+
+// Test that clearUserPref removes the "sticky" value.
+add_test(function clearUserPref() {
+ // load things such that we have a sticky value which is the same as the
+ // default.
+ resetAndLoadAll();
+ ps.clearUserPref("testPref.sticky.bool");
+
+ // Once we save prefs the sticky pref should no longer be written.
+ saveAndReload();
+ try {
+ ps.getBoolPref("testPref.sticky.bool");
+ Assert.ok(false, "expected failure reading this pref");
+ } catch (ex) {
+ Assert.ok(ex, "pref doesn't have a sticky value");
+ }
+ run_next_test();
+});
+
+// Test that a pref observer gets a notification fired when a sticky pref
+// has it's value changed to the same value as the default. The reason for
+// this behaviour is that later we might have other code that cares about a
+// pref being sticky (IOW, we notify due to the "state" of the pref changing
+// even if the value has not)
+add_test(function observerFires() {
+ // load things so there's no sticky value.
+ resetAndLoadDefaults();
+
+ function observe(subject, topic, data) {
+ Assert.equal(data, "testPref.sticky.bool");
+ ps.removeObserver("testPref.sticky.bool", observe);
+ run_next_test();
+ }
+ ps.addObserver("testPref.sticky.bool", observe);
+
+ ps.setBoolPref(
+ "testPref.sticky.bool",
+ ps.getBoolPref("testPref.sticky.bool")
+ );
+ // and the observer will fire triggering the next text.
+});
diff --git a/modules/libpref/test/unit/test_warnings.js b/modules/libpref/test/unit/test_warnings.js
new file mode 100644
index 0000000000..a6782f5c59
--- /dev/null
+++ b/modules/libpref/test/unit/test_warnings.js
@@ -0,0 +1,62 @@
+/* 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/. */
+
+function makeBuffer(length) {
+ return new Array(length + 1).join("x");
+}
+
+/**
+ * @resolves |true| if execution proceeded without warning,
+ * |false| if there was a warning.
+ */
+function checkWarning(pref, buffer) {
+ return new Promise(resolve => {
+ let complete = false;
+ let listener = {
+ observe(event) {
+ let message = event.message;
+ if (
+ !(
+ message.startsWith("Warning: attempting to write") &&
+ message.includes(pref)
+ )
+ ) {
+ return;
+ }
+ if (complete) {
+ return;
+ }
+ complete = true;
+ info("Warning while setting " + pref);
+ Services.console.unregisterListener(listener);
+ resolve(true);
+ },
+ };
+ do_timeout(1000, function () {
+ if (complete) {
+ return;
+ }
+ complete = true;
+ info("No warning while setting " + pref);
+ Services.console.unregisterListener(listener);
+ resolve(false);
+ });
+ Services.console.registerListener(listener);
+ Services.prefs.setCharPref(pref, buffer);
+ });
+}
+
+add_task(async function () {
+ // Simple change, shouldn't cause a warning
+ info("Checking that a simple change doesn't cause a warning");
+ let buf = makeBuffer(100);
+ let warned = await checkWarning("string.accept", buf);
+ Assert.ok(!warned);
+
+ // Large change, should cause a warning
+ info("Checking that a large change causes a warning");
+ buf = makeBuffer(32 * 1024);
+ warned = await checkWarning("string.warn", buf);
+ Assert.ok(warned);
+});
diff --git a/modules/libpref/test/unit/xpcshell.ini b/modules/libpref/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..0778b8659f
--- /dev/null
+++ b/modules/libpref/test/unit/xpcshell.ini
@@ -0,0 +1,28 @@
+[DEFAULT]
+head = head_libPrefs.js
+support-files =
+ data/testPref.js
+ extdata/testExt.js
+
+[test_warnings.js]
+[test_bug345529.js]
+[test_bug506224.js]
+[test_bug577950.js]
+[test_bug790374.js]
+[test_stickyprefs.js]
+skip-if = (os == "win" && socketprocess_networking)
+support-files = data/testPrefSticky.js data/testPrefStickyUser.js
+[test_locked_file_prefs.js]
+skip-if = (os == "win" && socketprocess_networking)
+support-files = data/testPrefLocked.js data/testPrefLockedUser.js
+[test_changeType.js]
+[test_defaultValues.js]
+[test_dirtyPrefs.js]
+[test_libPrefs.js]
+[test_bug1354613.js]
+[test_parser.js]
+support-files = data/testParser.js
+[test_parsePrefs.js]
+support-files =
+ data/testPrefUTF8.js
+head =
diff --git a/modules/libpref/test/unit_ipc/test_existing_prefs.js b/modules/libpref/test/unit_ipc/test_existing_prefs.js
new file mode 100644
index 0000000000..4efce784b0
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/test_existing_prefs.js
@@ -0,0 +1,20 @@
+function isParentProcess() {
+ let appInfo = Cc["@mozilla.org/xre/app-info;1"];
+ return (
+ !appInfo ||
+ Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT
+ );
+}
+
+function run_test() {
+ if (!isParentProcess()) {
+ do_load_child_test_harness();
+
+ var pb = Services.prefs;
+ pb.setBoolPref("Test.IPC.bool.new", true);
+ pb.setIntPref("Test.IPC.int.new", 23);
+ pb.setCharPref("Test.IPC.char.new", "hey");
+
+ run_test_in_child("test_observed_prefs.js");
+ }
+}
diff --git a/modules/libpref/test/unit_ipc/test_initial_prefs.js b/modules/libpref/test/unit_ipc/test_initial_prefs.js
new file mode 100644
index 0000000000..9c5e8991cf
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/test_initial_prefs.js
@@ -0,0 +1,14 @@
+function isParentProcess() {
+ return Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+}
+
+function run_test() {
+ if (!isParentProcess()) {
+ const pb = Services.prefs;
+ pb.setBoolPref("Test.IPC.bool", true);
+ pb.setIntPref("Test.IPC.int", 23);
+ pb.setCharPref("Test.IPC.char", "hey");
+
+ run_test_in_child("test_existing_prefs.js");
+ }
+}
diff --git a/modules/libpref/test/unit_ipc/test_large_pref.js b/modules/libpref/test/unit_ipc/test_large_pref.js
new file mode 100644
index 0000000000..79c1330c36
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/test_large_pref.js
@@ -0,0 +1,104 @@
+/* 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/. */
+
+// Large preferences should not be set in the child process.
+// Non-string preferences are not tested here, because their behavior
+// should not be affected by this filtering.
+//
+
+function isParentProcess() {
+ return Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+}
+
+function makeBuffer(length) {
+ let string = "x";
+ while (string.length < length) {
+ string = string + string;
+ }
+ if (string.length > length) {
+ string = string.substring(length - string.length);
+ }
+ return string;
+}
+
+// from prefapi.h
+const MAX_ADVISABLE_PREF_LENGTH = 4 * 1024;
+
+const largeString = makeBuffer(MAX_ADVISABLE_PREF_LENGTH + 1);
+const smallString = makeBuffer(4);
+
+const testValues = [
+ { name: "None", value: undefined },
+ { name: "Small", value: smallString },
+ { name: "Large", value: largeString },
+];
+
+function prefName(def, user) {
+ return "Test.IPC.default" + def.name + "User" + user.name;
+}
+
+function expectedPrefValue(def, user) {
+ if (user.value) {
+ return user.value;
+ }
+ return def.value;
+}
+
+function run_test() {
+ const pb = Services.prefs;
+ let defaultBranch = pb.getDefaultBranch("");
+
+ let isParent = isParentProcess();
+ if (isParent) {
+ // Preferences with large values will still appear in the shared memory
+ // snapshot that we share with all processes. They should not, however, be
+ // sent with the list of changes on top of the snapshot.
+ //
+ // So, make sure we've generated the initial snapshot before we set the
+ // preference values by launching a child process with an empty test.
+ sendCommand("");
+
+ // Set all combinations of none, small and large, for default and user prefs.
+ for (let def of testValues) {
+ for (let user of testValues) {
+ let currPref = prefName(def, user);
+ if (def.value) {
+ defaultBranch.setCharPref(currPref, def.value);
+ }
+ if (user.value) {
+ pb.setCharPref(currPref, user.value);
+ }
+ }
+ }
+
+ run_test_in_child("test_large_pref.js");
+ }
+
+ // Check that each preference is set or not set, as appropriate.
+ for (let def of testValues) {
+ for (let user of testValues) {
+ if (!def.value && !user.value) {
+ continue;
+ }
+ let pref_name = prefName(def, user);
+ if (isParent || (def.name != "Large" && user.name != "Large")) {
+ Assert.equal(pb.getCharPref(pref_name), expectedPrefValue(def, user));
+ } else {
+ // This is the child, and either the default or user value is
+ // large, so the preference should not be set.
+ let prefExists;
+ try {
+ let val = pb.getCharPref(pref_name);
+ prefExists = val.length > 128;
+ } catch (e) {
+ prefExists = false;
+ }
+ ok(
+ !prefExists,
+ "Pref " + pref_name + " should not be set in the child"
+ );
+ }
+ }
+ }
+}
diff --git a/modules/libpref/test/unit_ipc/test_locked_prefs.js b/modules/libpref/test/unit_ipc/test_locked_prefs.js
new file mode 100644
index 0000000000..037427e495
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/test_locked_prefs.js
@@ -0,0 +1,39 @@
+/* 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/. */
+
+// Locked status should be communicated to children.
+
+function isParentProcess() {
+ return Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+}
+
+function run_test() {
+ const pb = Services.prefs;
+
+ let bprefname = "Test.IPC.locked.bool";
+ let iprefname = "Test.IPC.locked.int";
+ let sprefname = "Test.IPC.locked.string";
+
+ let isParent = isParentProcess();
+ if (isParent) {
+ pb.setBoolPref(bprefname, true);
+ pb.lockPref(bprefname);
+
+ pb.setIntPref(iprefname, true);
+ pb.lockPref(iprefname);
+
+ pb.setStringPref(sprefname, true);
+ pb.lockPref(sprefname);
+ pb.unlockPref(sprefname);
+
+ run_test_in_child("test_locked_prefs.js");
+ }
+
+ ok(pb.prefIsLocked(bprefname), bprefname + " should be locked in the child");
+ ok(pb.prefIsLocked(iprefname), iprefname + " should be locked in the child");
+ ok(
+ !pb.prefIsLocked(sprefname),
+ sprefname + " should be unlocked in the child"
+ );
+}
diff --git a/modules/libpref/test/unit_ipc/test_observed_prefs.js b/modules/libpref/test/unit_ipc/test_observed_prefs.js
new file mode 100644
index 0000000000..9f6984d555
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/test_observed_prefs.js
@@ -0,0 +1,12 @@
+function isParentProcess() {
+ return Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+}
+
+function run_test() {
+ if (!isParentProcess()) {
+ const pb = Services.prefs;
+ Assert.equal(pb.getBoolPref("Test.IPC.bool.new"), true);
+ Assert.equal(pb.getIntPref("Test.IPC.int.new"), 23);
+ Assert.equal(pb.getCharPref("Test.IPC.char.new"), "hey");
+ }
+}
diff --git a/modules/libpref/test/unit_ipc/test_sharedMap.js b/modules/libpref/test/unit_ipc/test_sharedMap.js
new file mode 100644
index 0000000000..c9a001d7a0
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/test_sharedMap.js
@@ -0,0 +1,362 @@
+/* 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/. */
+"use strict";
+
+// This file tests the functionality of the preference service when using a
+// shared memory snapshot. In this configuration, a snapshot of the initial
+// state of the preferences database is made when we first spawn a child
+// process, and changes after that point are stored as entries in a dynamic hash
+// table, on top of the snapshot.
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+let contentPage;
+
+const { prefs } = Services;
+const defaultPrefs = prefs.getDefaultBranch("");
+
+const FRAME_SCRIPT_INIT = `
+ var { prefs } = Services;
+ var defaultPrefs = prefs.getDefaultBranch("");
+`;
+
+function try_(fn) {
+ try {
+ return fn();
+ } catch (e) {
+ return undefined;
+ }
+}
+
+function getPref(pref) {
+ let flags = {
+ locked: try_(() => prefs.prefIsLocked(pref)),
+ hasUser: try_(() => prefs.prefHasUserValue(pref)),
+ };
+
+ switch (prefs.getPrefType(pref)) {
+ case prefs.PREF_INT:
+ return {
+ ...flags,
+ type: "Int",
+ user: try_(() => prefs.getIntPref(pref)),
+ default: try_(() => defaultPrefs.getIntPref(pref)),
+ };
+ case prefs.PREF_BOOL:
+ return {
+ ...flags,
+ type: "Bool",
+ user: try_(() => prefs.getBoolPref(pref)),
+ default: try_(() => defaultPrefs.getBoolPref(pref)),
+ };
+ case prefs.PREF_STRING:
+ return {
+ ...flags,
+ type: "String",
+ user: try_(() => prefs.getStringPref(pref)),
+ default: try_(() => defaultPrefs.getStringPref(pref)),
+ };
+ }
+ return {};
+}
+
+function getPrefs(prefNames) {
+ let result = {};
+ for (let pref of prefNames) {
+ result[pref] = getPref(pref);
+ }
+ result.childList = prefs.getChildList("");
+ return result;
+}
+
+function checkPref(
+ pref,
+ proc,
+ val,
+ type,
+ userVal,
+ defaultVal,
+ expectedFlags = {}
+) {
+ info(`Check "${pref}" ${proc} value`);
+
+ equal(val.type, type, `Expected type for "${pref}"`);
+ equal(val.user, userVal, `Expected user value for "${pref}"`);
+
+ // We only send changes to the content process when they'll make a visible
+ // difference, so ignore content process default values when we have a defined
+ // user value.
+ if (proc !== "content" || val.user === undefined) {
+ equal(val.default, defaultVal, `Expected default value for "${pref}"`);
+ }
+
+ for (let [flag, value] of Object.entries(expectedFlags)) {
+ equal(val[flag], value, `Expected ${flag} value for "${pref}"`);
+ }
+}
+
+function getPrefList() {
+ return prefs.getChildList("");
+}
+
+const TESTS = {
+ "exists.thenDoesNot": {
+ beforeContent(PREF) {
+ prefs.setBoolPref(PREF, true);
+
+ ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`);
+ },
+ contentStartup(PREF, val, childList) {
+ ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`);
+ ok(childList.includes(PREF), `Child list includes "${PREF}"`);
+
+ prefs.clearUserPref(PREF);
+ ok(
+ !getPrefList().includes(PREF),
+ `Parent list doesn't include "${PREF}"`
+ );
+ },
+ contentUpdate1(PREF, val, childList) {
+ ok(
+ !getPrefList().includes(PREF),
+ `Parent list doesn't include "${PREF}"`
+ );
+ ok(!childList.includes(PREF), `Child list doesn't include "${PREF}"`);
+
+ prefs.setCharPref(PREF, "foo");
+ ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`);
+ checkPref(PREF, "parent", getPref(PREF), "String", "foo");
+ },
+ contentUpdate2(PREF, val, childList) {
+ ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`);
+ ok(childList.includes(PREF), `Child list includes "${PREF}"`);
+
+ checkPref(PREF, "parent", getPref(PREF), "String", "foo");
+ checkPref(PREF, "child", val, "String", "foo");
+ },
+ },
+ "doesNotExists.thenDoes": {
+ contentStartup(PREF, val, childList) {
+ ok(
+ !getPrefList().includes(PREF),
+ `Parent list doesn't include "${PREF}"`
+ );
+ ok(!childList.includes(PREF), `Child list doesn't include "${PREF}"`);
+
+ prefs.setIntPref(PREF, 42);
+ ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`);
+ },
+ contentUpdate1(PREF, val, childList) {
+ ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`);
+ ok(childList.includes(PREF), `Child list includes "${PREF}"`);
+
+ checkPref(PREF, "parent", getPref(PREF), "Int", 42);
+ checkPref(PREF, "child", val, "Int", 42);
+ },
+ },
+};
+
+const PREFS = [
+ { type: "Bool", values: [true, false, true] },
+ { type: "Int", values: [24, 42, 73] },
+ { type: "String", values: ["meh", "hem", "hrm"] },
+];
+
+for (let { type, values } of PREFS) {
+ let set = `set${type}Pref`;
+
+ function prefTest(opts) {
+ function check(
+ pref,
+ proc,
+ val,
+ {
+ expectedVal,
+ defaultVal = undefined,
+ expectedDefault = defaultVal,
+ expectedFlags = {},
+ }
+ ) {
+ checkPref(
+ pref,
+ proc,
+ val,
+ type,
+ expectedVal,
+ expectedDefault,
+ expectedFlags
+ );
+ }
+
+ function updatePref(
+ PREF,
+ { userVal = undefined, defaultVal = undefined, flags = {} }
+ ) {
+ info(`Update "${PREF}"`);
+ if (userVal !== undefined) {
+ prefs[set](PREF, userVal);
+ }
+ if (defaultVal !== undefined) {
+ defaultPrefs[set](PREF, defaultVal);
+ }
+ if (flags.locked === true) {
+ prefs.lockPref(PREF);
+ } else if (flags.locked === false) {
+ prefs.unlockPref(PREF);
+ }
+ }
+
+ return {
+ beforeContent(PREF) {
+ updatePref(PREF, opts.initial);
+ check(PREF, "parent", getPref(PREF), opts.initial);
+ },
+ contentStartup(PREF, contentVal) {
+ check(PREF, "content", contentVal, opts.initial);
+ check(PREF, "parent", getPref(PREF), opts.initial);
+
+ updatePref(PREF, opts.change1);
+ check(PREF, "parent", getPref(PREF), opts.change1);
+ },
+ contentUpdate1(PREF, contentVal) {
+ check(PREF, "content", contentVal, opts.change1);
+ check(PREF, "parent", getPref(PREF), opts.change1);
+
+ if (opts.change2) {
+ updatePref(PREF, opts.change2);
+ check(PREF, "parent", getPref(PREF), opts.change2);
+ }
+ },
+ contentUpdate2(PREF, contentVal) {
+ if (opts.change2) {
+ check(PREF, "content", contentVal, opts.change2);
+ check(PREF, "parent", getPref(PREF), opts.change2);
+ }
+ },
+ };
+ }
+
+ for (let i of [0, 1]) {
+ let userVal = values[i];
+ let defaultVal = values[+!i];
+
+ TESTS[`type.${type}.${i}.default`] = prefTest({
+ initial: { defaultVal, expectedVal: defaultVal },
+ change1: { defaultVal: values[2], expectedVal: values[2] },
+ });
+
+ TESTS[`type.${type}.${i}.user`] = prefTest({
+ initial: { userVal, expectedVal: userVal },
+ change1: { defaultVal: values[2], expectedVal: userVal },
+ change2: {
+ userVal: values[2],
+ expectedDefault: values[2],
+ expectedVal: values[2],
+ },
+ });
+
+ TESTS[`type.${type}.${i}.both`] = prefTest({
+ initial: { userVal, defaultVal, expectedVal: userVal },
+ change1: { defaultVal: values[2], expectedVal: userVal },
+ change2: {
+ userVal: values[2],
+ expectedDefault: values[2],
+ expectedVal: values[2],
+ },
+ });
+
+ TESTS[`type.${type}.${i}.both.thenLock`] = prefTest({
+ initial: { userVal, defaultVal, expectedVal: userVal },
+ change1: {
+ expectedDefault: defaultVal,
+ expectedVal: defaultVal,
+ flags: { locked: true },
+ expectFlags: { locked: true },
+ },
+ });
+
+ TESTS[`type.${type}.${i}.both.thenUnlock`] = prefTest({
+ initial: {
+ userVal,
+ defaultVal,
+ expectedVal: defaultVal,
+ flags: { locked: true },
+ expectedFlags: { locked: true },
+ },
+ change1: {
+ expectedDefault: defaultVal,
+ expectedVal: userVal,
+ flags: { locked: false },
+ expectFlags: { locked: false },
+ },
+ });
+
+ TESTS[`type.${type}.${i}.both.locked`] = prefTest({
+ initial: {
+ userVal,
+ defaultVal,
+ expectedVal: defaultVal,
+ flags: { locked: true },
+ expectedFlags: { locked: true },
+ },
+ change1: {
+ userVal: values[2],
+ expectedDefault: defaultVal,
+ expectedVal: defaultVal,
+ expectedFlags: { locked: true },
+ },
+ change2: {
+ defaultVal: values[2],
+ expectedDefault: defaultVal,
+ expectedVal: defaultVal,
+ expectedFlags: { locked: true },
+ },
+ });
+ }
+}
+
+add_task(async function test_sharedMap_prefs() {
+ let prefValues = {};
+
+ async function runChecks(op) {
+ for (let [pref, ops] of Object.entries(TESTS)) {
+ if (ops[op]) {
+ info(`Running ${op} for "${pref}"`);
+ await ops[op](
+ pref,
+ prefValues[pref] || undefined,
+ prefValues.childList || undefined
+ );
+ }
+ }
+ }
+
+ await runChecks("beforeContent");
+
+ contentPage = await XPCShellContentUtils.loadContentPage("about:blank", {
+ remote: true,
+ });
+ registerCleanupFunction(() => contentPage.close());
+
+ contentPage.addFrameScriptHelper(FRAME_SCRIPT_INIT);
+ contentPage.addFrameScriptHelper(try_);
+ contentPage.addFrameScriptHelper(getPref);
+
+ let prefNames = Object.keys(TESTS);
+ prefValues = await contentPage.legacySpawn(prefNames, getPrefs);
+
+ await runChecks("contentStartup");
+
+ prefValues = await contentPage.legacySpawn(prefNames, getPrefs);
+
+ await runChecks("contentUpdate1");
+
+ prefValues = await contentPage.legacySpawn(prefNames, getPrefs);
+
+ await runChecks("contentUpdate2");
+});
diff --git a/modules/libpref/test/unit_ipc/test_sharedMap_static_prefs.js b/modules/libpref/test/unit_ipc/test_sharedMap_static_prefs.js
new file mode 100644
index 0000000000..a8181bf2cb
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/test_sharedMap_static_prefs.js
@@ -0,0 +1,76 @@
+/* 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/. */
+"use strict";
+
+// Tests that static preferences in the content process
+// correctly handle values which are different from their
+// statically-defined defaults.
+//
+// Since we can't access static preference values from JS, this tests relies on
+// assertions in debug builds to detect mismatches. The default and user
+// values of two preferences are changed (respectively) before a content
+// process is started. Once the content process is launched, the
+// preference service asserts that the values stored in all static prefs
+// match their current values as known to the preference service. If
+// there's a mismatch, the shell will crash, and the test will fail.
+//
+// For sanity, we also check that the dynamically retrieved preference
+// values in the content process match our expectations, though this is
+// not strictly part of the test.
+
+const PREF1_NAME = "dom.webcomponents.shadowdom.report_usage";
+const PREF1_VALUE = false;
+
+const PREF2_NAME = "dom.mutation-events.cssom.disabled";
+const PREF2_VALUE = true;
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+const { prefs } = Services;
+const defaultPrefs = prefs.getDefaultBranch("");
+
+add_task(async function test_sharedMap_static_prefs() {
+ equal(
+ prefs.getBoolPref(PREF1_NAME),
+ PREF1_VALUE,
+ `Expected initial value for ${PREF1_NAME}`
+ );
+ equal(
+ prefs.getBoolPref(PREF2_NAME),
+ PREF2_VALUE,
+ `Expected initial value for ${PREF2_NAME}`
+ );
+
+ defaultPrefs.setBoolPref(PREF1_NAME, !PREF1_VALUE);
+ prefs.setBoolPref(PREF2_NAME, !PREF2_VALUE);
+
+ equal(
+ prefs.getBoolPref(PREF1_NAME),
+ !PREF1_VALUE,
+ `Expected updated value for ${PREF1_NAME}`
+ );
+ equal(
+ prefs.getBoolPref(PREF2_NAME),
+ !PREF2_VALUE,
+ `Expected updated value for ${PREF2_NAME}`
+ );
+
+ let contentPage = await XPCShellContentUtils.loadContentPage("about:blank", {
+ remote: true,
+ });
+ registerCleanupFunction(() => contentPage.close());
+
+ /* eslint-disable no-shadow */
+ let values = await contentPage.spawn([[PREF1_NAME, PREF2_NAME]], prefs => {
+ return prefs.map(pref => Services.prefs.getBoolPref(pref));
+ });
+ /* eslint-enable no-shadow */
+
+ equal(values[0], !PREF1_VALUE, `Expected content value for ${PREF1_NAME}`);
+ equal(values[1], !PREF2_VALUE, `Expected content value for ${PREF2_NAME}`);
+});
diff --git a/modules/libpref/test/unit_ipc/test_update_prefs.js b/modules/libpref/test/unit_ipc/test_update_prefs.js
new file mode 100644
index 0000000000..51e2c458c5
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/test_update_prefs.js
@@ -0,0 +1,34 @@
+function isParentProcess() {
+ return Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+}
+
+function run_test() {
+ if (isParentProcess()) {
+ do_load_child_test_harness();
+
+ var pb = Services.prefs;
+
+ // these prefs are set after the child has been created.
+ pb.setBoolPref("Test.IPC.bool.new", true);
+ pb.setIntPref("Test.IPC.int.new", 23);
+ pb.setCharPref("Test.IPC.char.new", "hey");
+
+ run_test_in_child("test_observed_prefs.js", testPrefClear);
+ }
+}
+
+function testPrefClear() {
+ var pb = Services.prefs;
+ pb.clearUserPref("Test.IPC.bool.new");
+
+ sendCommand(
+ 'var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);\n' +
+ 'pb.prefHasUserValue("Test.IPC.bool.new");\n',
+ checkWasCleared
+ );
+}
+
+function checkWasCleared(existsStr) {
+ Assert.equal(existsStr, "false");
+ do_test_finished();
+}
diff --git a/modules/libpref/test/unit_ipc/test_user_default_prefs.js b/modules/libpref/test/unit_ipc/test_user_default_prefs.js
new file mode 100644
index 0000000000..3307522513
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/test_user_default_prefs.js
@@ -0,0 +1,72 @@
+const pb = Services.prefs;
+
+// This pref is chosen somewhat arbitrarily --- we just need one
+// that's guaranteed to have a default value.
+const kPrefName = "intl.accept_languages"; // of type char, which we
+// assume below
+var initialValue = null;
+
+function check_child_pref_info_eq(continuation) {
+ sendCommand(
+ 'var pb = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);\n' +
+ // Returns concatenation "[value],[isUser]"
+ 'pb.getCharPref("' +
+ kPrefName +
+ '")+ "," +' +
+ 'pb.prefHasUserValue("' +
+ kPrefName +
+ '");',
+ function (info) {
+ let [value, isUser] = info.split(",");
+ Assert.equal(pb.getCharPref(kPrefName), value);
+ Assert.equal(pb.prefHasUserValue(kPrefName), isUser == "true");
+ continuation();
+ }
+ );
+}
+
+function run_test() {
+ // We finish in clean_up()
+ do_test_pending();
+
+ initialValue = pb.getCharPref(kPrefName);
+
+ test_user_setting();
+}
+
+function test_user_setting() {
+ // We rely on setting this before the content process starts up.
+ // When it starts up, it should recognize this as a user pref, not
+ // a default pref.
+ pb.setCharPref(kPrefName, "i-imaginarylanguage");
+ // NB: processing of the value-change notification in the child
+ // process triggered by the above set happens-before the remaining
+ // code here
+ check_child_pref_info_eq(function () {
+ Assert.equal(pb.prefHasUserValue(kPrefName), true);
+
+ test_cleared_is_default();
+ });
+}
+
+function test_cleared_is_default() {
+ pb.clearUserPref(kPrefName);
+ // NB: processing of the value-change notification in the child
+ // process triggered by the above set happens-before the remaining
+ // code here
+ check_child_pref_info_eq(function () {
+ Assert.equal(pb.prefHasUserValue(kPrefName), false);
+
+ clean_up();
+ });
+}
+
+function clean_up() {
+ pb.setCharPref(kPrefName, initialValue);
+ // NB: processing of the value-change notification in the child
+ // process triggered by the above set happens-before the remaining
+ // code here
+ check_child_pref_info_eq(function () {
+ do_test_finished();
+ });
+}
diff --git a/modules/libpref/test/unit_ipc/xpcshell.ini b/modules/libpref/test/unit_ipc/xpcshell.ini
new file mode 100644
index 0000000000..e2a444f774
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/xpcshell.ini
@@ -0,0 +1,14 @@
+[DEFAULT]
+head =
+skip-if = toolkit == 'android'
+
+[test_existing_prefs.js]
+[test_initial_prefs.js]
+[test_large_pref.js]
+[test_locked_prefs.js]
+[test_observed_prefs.js]
+[test_update_prefs.js]
+[test_sharedMap.js]
+[test_sharedMap_static_prefs.js]
+skip-if = !debug # Relies on debug assertions to catch failure cases.
+[test_user_default_prefs.js]