test_sharedMap.js (9978B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 "use strict"; 5 6 // This file tests the functionality of the preference service when using a 7 // shared memory snapshot. In this configuration, a snapshot of the initial 8 // state of the preferences database is made when we first spawn a child 9 // process, and changes after that point are stored as entries in a dynamic hash 10 // table, on top of the snapshot. 11 12 const { XPCShellContentUtils } = ChromeUtils.importESModule( 13 "resource://testing-common/XPCShellContentUtils.sys.mjs" 14 ); 15 16 XPCShellContentUtils.init(this); 17 18 let contentPage; 19 20 const { prefs } = Services; 21 const defaultPrefs = prefs.getDefaultBranch(""); 22 23 const FRAME_SCRIPT_INIT = ` 24 var { prefs } = Services; 25 var defaultPrefs = prefs.getDefaultBranch(""); 26 `; 27 28 function try_(fn) { 29 try { 30 return fn(); 31 } catch (e) { 32 return undefined; 33 } 34 } 35 36 function getPref(pref) { 37 let flags = { 38 locked: try_(() => prefs.prefIsLocked(pref)), 39 hasUser: try_(() => prefs.prefHasUserValue(pref)), 40 }; 41 42 switch (prefs.getPrefType(pref)) { 43 case prefs.PREF_INT: 44 return { 45 ...flags, 46 type: "Int", 47 user: try_(() => prefs.getIntPref(pref)), 48 default: try_(() => defaultPrefs.getIntPref(pref)), 49 }; 50 case prefs.PREF_BOOL: 51 return { 52 ...flags, 53 type: "Bool", 54 user: try_(() => prefs.getBoolPref(pref)), 55 default: try_(() => defaultPrefs.getBoolPref(pref)), 56 }; 57 case prefs.PREF_STRING: 58 return { 59 ...flags, 60 type: "String", 61 user: try_(() => prefs.getStringPref(pref)), 62 default: try_(() => defaultPrefs.getStringPref(pref)), 63 }; 64 } 65 return {}; 66 } 67 68 function getPrefs(prefNames) { 69 let result = {}; 70 for (let pref of prefNames) { 71 result[pref] = getPref(pref); 72 } 73 result.childList = prefs.getChildList(""); 74 return result; 75 } 76 77 function checkPref( 78 pref, 79 proc, 80 val, 81 type, 82 userVal, 83 defaultVal, 84 expectedFlags = {} 85 ) { 86 info(`Check "${pref}" ${proc} value`); 87 88 equal(val.type, type, `Expected type for "${pref}"`); 89 equal(val.user, userVal, `Expected user value for "${pref}"`); 90 91 // We only send changes to the content process when they'll make a visible 92 // difference, so ignore content process default values when we have a defined 93 // user value. 94 if (proc !== "content" || val.user === undefined) { 95 equal(val.default, defaultVal, `Expected default value for "${pref}"`); 96 } 97 98 for (let [flag, value] of Object.entries(expectedFlags)) { 99 equal(val[flag], value, `Expected ${flag} value for "${pref}"`); 100 } 101 } 102 103 function getPrefList() { 104 return prefs.getChildList(""); 105 } 106 107 const TESTS = { 108 "exists.thenDoesNot": { 109 beforeContent(PREF) { 110 prefs.setBoolPref(PREF, true); 111 112 ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`); 113 }, 114 contentStartup(PREF, val, childList) { 115 ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`); 116 ok(childList.includes(PREF), `Child list includes "${PREF}"`); 117 118 prefs.clearUserPref(PREF); 119 ok( 120 !getPrefList().includes(PREF), 121 `Parent list doesn't include "${PREF}"` 122 ); 123 }, 124 contentUpdate1(PREF, val, childList) { 125 ok( 126 !getPrefList().includes(PREF), 127 `Parent list doesn't include "${PREF}"` 128 ); 129 ok(!childList.includes(PREF), `Child list doesn't include "${PREF}"`); 130 131 prefs.setCharPref(PREF, "foo"); 132 ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`); 133 checkPref(PREF, "parent", getPref(PREF), "String", "foo"); 134 }, 135 contentUpdate2(PREF, val, childList) { 136 ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`); 137 ok(childList.includes(PREF), `Child list includes "${PREF}"`); 138 139 checkPref(PREF, "parent", getPref(PREF), "String", "foo"); 140 checkPref(PREF, "child", val, "String", "foo"); 141 }, 142 }, 143 "doesNotExists.thenDoes": { 144 contentStartup(PREF, val, childList) { 145 ok( 146 !getPrefList().includes(PREF), 147 `Parent list doesn't include "${PREF}"` 148 ); 149 ok(!childList.includes(PREF), `Child list doesn't include "${PREF}"`); 150 151 prefs.setIntPref(PREF, 42); 152 ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`); 153 }, 154 contentUpdate1(PREF, val, childList) { 155 ok(getPrefList().includes(PREF), `Parent list includes "${PREF}"`); 156 ok(childList.includes(PREF), `Child list includes "${PREF}"`); 157 158 checkPref(PREF, "parent", getPref(PREF), "Int", 42); 159 checkPref(PREF, "child", val, "Int", 42); 160 }, 161 }, 162 }; 163 164 const PREFS = [ 165 { type: "Bool", values: [true, false, true] }, 166 { type: "Int", values: [24, 42, 73] }, 167 { type: "String", values: ["meh", "hem", "hrm"] }, 168 ]; 169 170 for (let { type, values } of PREFS) { 171 let set = `set${type}Pref`; 172 173 function prefTest(opts) { 174 function check( 175 pref, 176 proc, 177 val, 178 { 179 expectedVal, 180 defaultVal = undefined, 181 expectedDefault = defaultVal, 182 expectedFlags = {}, 183 } 184 ) { 185 checkPref( 186 pref, 187 proc, 188 val, 189 type, 190 expectedVal, 191 expectedDefault, 192 expectedFlags 193 ); 194 } 195 196 function updatePref( 197 PREF, 198 { userVal = undefined, defaultVal = undefined, flags = {} } 199 ) { 200 info(`Update "${PREF}"`); 201 if (userVal !== undefined) { 202 prefs[set](PREF, userVal); 203 } 204 if (defaultVal !== undefined) { 205 defaultPrefs[set](PREF, defaultVal); 206 } 207 if (flags.locked === true) { 208 prefs.lockPref(PREF); 209 } else if (flags.locked === false) { 210 prefs.unlockPref(PREF); 211 } 212 } 213 214 return { 215 beforeContent(PREF) { 216 updatePref(PREF, opts.initial); 217 check(PREF, "parent", getPref(PREF), opts.initial); 218 }, 219 contentStartup(PREF, contentVal) { 220 check(PREF, "content", contentVal, opts.initial); 221 check(PREF, "parent", getPref(PREF), opts.initial); 222 223 updatePref(PREF, opts.change1); 224 check(PREF, "parent", getPref(PREF), opts.change1); 225 }, 226 contentUpdate1(PREF, contentVal) { 227 check(PREF, "content", contentVal, opts.change1); 228 check(PREF, "parent", getPref(PREF), opts.change1); 229 230 if (opts.change2) { 231 updatePref(PREF, opts.change2); 232 check(PREF, "parent", getPref(PREF), opts.change2); 233 } 234 }, 235 contentUpdate2(PREF, contentVal) { 236 if (opts.change2) { 237 check(PREF, "content", contentVal, opts.change2); 238 check(PREF, "parent", getPref(PREF), opts.change2); 239 } 240 }, 241 }; 242 } 243 244 for (let i of [0, 1]) { 245 let userVal = values[i]; 246 let defaultVal = values[+!i]; 247 248 TESTS[`type.${type}.${i}.default`] = prefTest({ 249 initial: { defaultVal, expectedVal: defaultVal }, 250 change1: { defaultVal: values[2], expectedVal: values[2] }, 251 }); 252 253 TESTS[`type.${type}.${i}.user`] = prefTest({ 254 initial: { userVal, expectedVal: userVal }, 255 change1: { defaultVal: values[2], expectedVal: userVal }, 256 change2: { 257 userVal: values[2], 258 expectedDefault: values[2], 259 expectedVal: values[2], 260 }, 261 }); 262 263 TESTS[`type.${type}.${i}.both`] = prefTest({ 264 initial: { userVal, defaultVal, expectedVal: userVal }, 265 change1: { defaultVal: values[2], expectedVal: userVal }, 266 change2: { 267 userVal: values[2], 268 expectedDefault: values[2], 269 expectedVal: values[2], 270 }, 271 }); 272 273 TESTS[`type.${type}.${i}.both.thenLock`] = prefTest({ 274 initial: { userVal, defaultVal, expectedVal: userVal }, 275 change1: { 276 expectedDefault: defaultVal, 277 expectedVal: defaultVal, 278 flags: { locked: true }, 279 expectFlags: { locked: true }, 280 }, 281 }); 282 283 TESTS[`type.${type}.${i}.both.thenUnlock`] = prefTest({ 284 initial: { 285 userVal, 286 defaultVal, 287 expectedVal: defaultVal, 288 flags: { locked: true }, 289 expectedFlags: { locked: true }, 290 }, 291 change1: { 292 expectedDefault: defaultVal, 293 expectedVal: userVal, 294 flags: { locked: false }, 295 expectFlags: { locked: false }, 296 }, 297 }); 298 299 TESTS[`type.${type}.${i}.both.locked`] = prefTest({ 300 initial: { 301 userVal, 302 defaultVal, 303 expectedVal: defaultVal, 304 flags: { locked: true }, 305 expectedFlags: { locked: true }, 306 }, 307 change1: { 308 userVal: values[2], 309 expectedDefault: defaultVal, 310 expectedVal: defaultVal, 311 expectedFlags: { locked: true }, 312 }, 313 change2: { 314 defaultVal: values[2], 315 expectedDefault: defaultVal, 316 expectedVal: defaultVal, 317 expectedFlags: { locked: true }, 318 }, 319 }); 320 } 321 } 322 323 add_task(async function test_sharedMap_prefs() { 324 let prefValues = {}; 325 326 async function runChecks(op) { 327 for (let [pref, ops] of Object.entries(TESTS)) { 328 if (ops[op]) { 329 info(`Running ${op} for "${pref}"`); 330 await ops[op]( 331 pref, 332 prefValues[pref] || undefined, 333 prefValues.childList || undefined 334 ); 335 } 336 } 337 } 338 339 await runChecks("beforeContent"); 340 341 contentPage = await XPCShellContentUtils.loadContentPage("about:blank", { 342 remote: true, 343 }); 344 registerCleanupFunction(() => contentPage.close()); 345 346 contentPage.addFrameScriptHelper(FRAME_SCRIPT_INIT); 347 contentPage.addFrameScriptHelper(try_); 348 contentPage.addFrameScriptHelper(getPref); 349 350 let prefNames = Object.keys(TESTS); 351 prefValues = await contentPage.legacySpawn(prefNames, getPrefs); 352 353 await runChecks("contentStartup"); 354 355 prefValues = await contentPage.legacySpawn(prefNames, getPrefs); 356 357 await runChecks("contentUpdate1"); 358 359 prefValues = await contentPage.legacySpawn(prefNames, getPrefs); 360 361 await runChecks("contentUpdate2"); 362 });