Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* 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(
);
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");
});