summaryrefslogtreecommitdiffstats
path: root/modules/libpref/test/unit_ipc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /modules/libpref/test/unit_ipc
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-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
10 files changed, 747 insertions, 0 deletions
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]