test_prefs_store.js (14045B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 const { PromiseTestUtils } = ChromeUtils.importESModule( 5 "resource://testing-common/PromiseTestUtils.sys.mjs" 6 ); 7 PromiseTestUtils.allowMatchingRejectionsGlobally( 8 /Unable to arm timer, the object has been finalized\./ 9 ); 10 PromiseTestUtils.allowMatchingRejectionsGlobally( 11 /IOUtils\.profileBeforeChange getter: IOUtils: profileBeforeChange phase has already finished/ 12 ); 13 14 const { PrefRec, getPrefsGUIDForTest } = ChromeUtils.importESModule( 15 "resource://services-sync/engines/prefs.sys.mjs" 16 ); 17 const PREFS_GUID = getPrefsGUIDForTest(); 18 const { Service } = ChromeUtils.importESModule( 19 "resource://services-sync/service.sys.mjs" 20 ); 21 22 const DEFAULT_THEME_ID = "default-theme@mozilla.org"; 23 const COMPACT_THEME_ID = "firefox-compact-light@mozilla.org"; 24 25 AddonTestUtils.init(this); 26 AddonTestUtils.createAppInfo( 27 "xpcshell@tests.mozilla.org", 28 "XPCShell", 29 "1", 30 "1.9.2" 31 ); 32 AddonTestUtils.overrideCertDB(); 33 34 let prefsWithUserValue = new Set(); 35 add_setup(function record_prefsWithUserValue() { 36 for (const pref of Services.prefs.getChildList("")) { 37 if (Services.prefs.prefHasUserValue(pref)) { 38 prefsWithUserValue.add(pref); 39 } 40 } 41 }); 42 43 function clearValues() { 44 for (const pref of Services.prefs.getChildList("")) { 45 if (!prefsWithUserValue.has(pref)) { 46 Services.prefs.clearUserPref(pref); 47 } 48 } 49 } 50 51 add_task(async function run_test() { 52 _("Test fixtures."); 53 // Part of this test ensures the default theme, via the preference 54 // extensions.activeThemeID, is synced correctly - so we do a little 55 // addons initialization to allow this to work. 56 57 // Enable application scopes to ensure the builtin theme is going to 58 // be installed as part of the the addon manager startup. 59 Services.prefs.setIntPref( 60 "extensions.enabledScopes", 61 AddonManager.SCOPE_APPLICATION 62 ); 63 await AddonTestUtils.promiseStartupManager(); 64 65 // Install another built-in theme. 66 await AddonManager.installBuiltinAddon("resource://builtin-themes/light/"); 67 68 const defaultThemeAddon = await AddonManager.getAddonByID(DEFAULT_THEME_ID); 69 ok(defaultThemeAddon, "Got an addon wrapper for the default theme"); 70 71 const otherThemeAddon = await AddonManager.getAddonByID(COMPACT_THEME_ID); 72 ok(otherThemeAddon, "Got an addon wrapper for the compact theme"); 73 74 await otherThemeAddon.enable(); 75 76 // read our custom prefs file before doing anything. 77 Services.prefs.readDefaultPrefsFromFile( 78 do_get_file("prefs_test_prefs_store.js") 79 ); 80 81 let engine = Service.engineManager.get("prefs"); 82 let store = engine._store; 83 try { 84 _("Expect the compact light theme to be active"); 85 Assert.strictEqual( 86 Services.prefs.getStringPref("extensions.activeThemeID"), 87 COMPACT_THEME_ID 88 ); 89 90 _("The GUID corresponds to XUL App ID."); 91 let allIDs = await store.getAllIDs(); 92 let ids = Object.keys(allIDs); 93 Assert.equal(ids.length, 1); 94 Assert.equal(ids[0], PREFS_GUID); 95 Assert.ok(allIDs[PREFS_GUID]); 96 97 Assert.ok(await store.itemExists(PREFS_GUID)); 98 Assert.equal(false, await store.itemExists("random-gibberish")); 99 100 _("Unknown prefs record is created as deleted."); 101 let record = await store.createRecord("random-gibberish", "prefs"); 102 Assert.ok(record.deleted); 103 104 _("Prefs record contains only prefs that should be synced."); 105 record = await store.createRecord(PREFS_GUID, "prefs"); 106 Assert.strictEqual(record.value["testing.int"], 123); 107 Assert.strictEqual(record.value["testing.string"], "ohai"); 108 Assert.strictEqual(record.value["testing.bool"], true); 109 // non-existing prefs get null as the value 110 Assert.strictEqual(record.value["testing.nonexistent"], null); 111 // as do prefs that have a default value. 112 Assert.strictEqual(record.value["testing.default"], null); 113 Assert.strictEqual(record.value["testing.turned.off"], undefined); 114 Assert.strictEqual(record.value["testing.not.turned.on"], undefined); 115 116 _("Prefs record contains the correct control prefs."); 117 // All control prefs which have the default value and where the pref 118 // itself is synced should appear, but with null as the value. 119 Assert.strictEqual( 120 record.value["services.sync.prefs.sync.testing.int"], 121 null 122 ); 123 Assert.strictEqual( 124 record.value["services.sync.prefs.sync.testing.string"], 125 null 126 ); 127 Assert.strictEqual( 128 record.value["services.sync.prefs.sync.testing.bool"], 129 null 130 ); 131 Assert.strictEqual( 132 record.value["services.sync.prefs.sync.testing.dont.change"], 133 null 134 ); 135 Assert.strictEqual( 136 record.value["services.sync.prefs.sync.testing.nonexistent"], 137 null 138 ); 139 Assert.strictEqual( 140 record.value["services.sync.prefs.sync.testing.default"], 141 null 142 ); 143 144 // but this control pref has a non-default value so that value is synced. 145 Assert.strictEqual( 146 record.value["services.sync.prefs.sync.testing.turned.off"], 147 false 148 ); 149 150 _("Unsyncable prefs are treated correctly."); 151 // Prefs we consider unsyncable (since they are URLs that won't be stable on 152 // another firefox) shouldn't be included - neither the value nor the 153 // control pref should appear. 154 Assert.strictEqual(record.value["testing.unsynced.url"], undefined); 155 Assert.strictEqual( 156 record.value["services.sync.prefs.sync.testing.unsynced.url"], 157 undefined 158 ); 159 // Other URLs with user prefs should be synced, though. 160 Assert.strictEqual( 161 record.value["testing.synced.url"], 162 "https://www.example.com" 163 ); 164 Assert.strictEqual( 165 record.value["services.sync.prefs.sync.testing.synced.url"], 166 null 167 ); 168 169 _("Update some prefs, including one that's to be reset/deleted."); 170 // This pref is not going to be reset or deleted as there's no "control pref" 171 // in either the incoming record or locally. 172 Services.prefs.setStringPref( 173 "testing.deleted-without-control-pref", 174 "I'm deleted-without-control-pref" 175 ); 176 // Another pref with only a local control pref. 177 Services.prefs.setStringPref( 178 "testing.deleted-with-local-control-pref", 179 "I'm deleted-with-local-control-pref" 180 ); 181 Services.prefs.setBoolPref( 182 "services.sync.prefs.sync.testing.deleted-with-local-control-pref", 183 true 184 ); 185 // And a pref without a local control pref but one that's incoming. 186 Services.prefs.setStringPref( 187 "testing.deleted-with-incoming-control-pref", 188 "I'm deleted-with-incoming-control-pref" 189 ); 190 record = new PrefRec("prefs", PREFS_GUID); 191 record.value = { 192 "extensions.activeThemeID": DEFAULT_THEME_ID, 193 "testing.int": 42, 194 "testing.string": "im in ur prefs", 195 "testing.bool": false, 196 "testing.deleted-without-control-pref": null, 197 "testing.deleted-with-local-control-pref": null, 198 "testing.deleted-with-incoming-control-pref": null, 199 "services.sync.prefs.sync.testing.deleted-with-incoming-control-pref": true, 200 "testing.somepref": "im a new pref from other device", 201 "services.sync.prefs.sync.testing.somepref": true, 202 // Pretend some a stale remote client is overwriting it with a value 203 // we consider unsyncable. 204 "testing.synced.url": "blob:ebeb707a-502e-40c6-97a5-dd4bda901463", 205 // Make sure we can replace the unsynced URL with a valid URL. 206 "testing.unsynced.url": "https://www.example.com/2", 207 // Make sure our "master control pref" is ignored. 208 "services.sync.prefs.dangerously_allow_arbitrary": true, 209 "services.sync.prefs.sync.services.sync.prefs.dangerously_allow_arbitrary": true, 210 }; 211 212 const onceAddonEnabled = AddonTestUtils.promiseAddonEvent("onEnabled"); 213 214 await store.update(record); 215 Assert.strictEqual(Services.prefs.getIntPref("testing.int"), 42); 216 Assert.strictEqual( 217 Services.prefs.getStringPref("testing.string"), 218 "im in ur prefs" 219 ); 220 Assert.strictEqual(Services.prefs.getBoolPref("testing.bool"), false); 221 Assert.strictEqual( 222 Services.prefs.getStringPref("testing.deleted-without-control-pref"), 223 "I'm deleted-without-control-pref" 224 ); 225 Assert.strictEqual( 226 Services.prefs.getPrefType("testing.deleted-with-local-control-pref"), 227 Ci.nsIPrefBranch.PREF_INVALID 228 ); 229 Assert.strictEqual( 230 Services.prefs.getStringPref( 231 "testing.deleted-with-incoming-control-pref" 232 ), 233 "I'm deleted-with-incoming-control-pref" 234 ); 235 Assert.strictEqual( 236 Services.prefs.getStringPref("testing.dont.change"), 237 "Please don't change me." 238 ); 239 Assert.strictEqual( 240 Services.prefs.getPrefType("testing.somepref"), 241 Ci.nsIPrefBranch.PREF_INVALID 242 ); 243 Assert.strictEqual( 244 Services.prefs.getStringPref("testing.synced.url"), 245 "https://www.example.com" 246 ); 247 Assert.strictEqual( 248 Services.prefs.getStringPref("testing.unsynced.url"), 249 "https://www.example.com/2" 250 ); 251 Assert.strictEqual( 252 Svc.PrefBranch.getPrefType("prefs.sync.testing.somepref"), 253 Ci.nsIPrefBranch.PREF_INVALID 254 ); 255 Assert.strictEqual( 256 Services.prefs.getBoolPref( 257 "services.sync.prefs.dangerously_allow_arbitrary" 258 ), 259 false 260 ); 261 Assert.strictEqual( 262 Services.prefs.getPrefType( 263 "services.sync.prefs.sync.services.sync.prefs.dangerously_allow_arbitrary" 264 ), 265 Ci.nsIPrefBranch.PREF_INVALID 266 ); 267 268 await onceAddonEnabled; 269 ok( 270 !defaultThemeAddon.userDisabled, 271 "the default theme should have been enabled" 272 ); 273 ok( 274 otherThemeAddon.userDisabled, 275 "the compact theme should have been disabled" 276 ); 277 278 _("Only the current app's preferences are applied."); 279 record = new PrefRec("prefs", "some-fake-app"); 280 record.value = { 281 "testing.int": 98, 282 }; 283 await store.update(record); 284 Assert.equal(Services.prefs.getIntPref("testing.int"), 42); 285 } finally { 286 clearValues(); 287 } 288 }); 289 290 add_task(async function test_dangerously_allow() { 291 _("services.sync.prefs.dangerously_allow_arbitrary"); 292 // Bug 1538015 added a capability to "dangerously allow" arbitrary prefs. 293 // Bug 1854698 removed that capability but did keep the fact we never 294 // sync the pref which enabled the "dangerous" behaviour, just incase someone 295 // tries to sync it back to a profile which *does* support that pref. 296 Services.prefs.readDefaultPrefsFromFile( 297 do_get_file("prefs_test_prefs_store.js") 298 ); 299 300 let engine = Service.engineManager.get("prefs"); 301 let store = engine._store; 302 try { 303 // an incoming record with our old "dangerous" pref. 304 let record = new PrefRec("prefs", PREFS_GUID); 305 record.value = { 306 "services.sync.prefs.dangerously_allow_arbitrary": true, 307 "services.sync.prefs.sync.services.sync.prefs.dangerously_allow_arbitrary": true, 308 }; 309 await store.update(record); 310 Assert.strictEqual( 311 Services.prefs.getBoolPref( 312 "services.sync.prefs.dangerously_allow_arbitrary" 313 ), 314 false 315 ); 316 Assert.strictEqual( 317 Services.prefs.getPrefType( 318 "services.sync.prefs.sync.services.sync.prefs.dangerously_allow_arbitrary" 319 ), 320 Ci.nsIPrefBranch.PREF_INVALID 321 ); 322 } finally { 323 clearValues(); 324 } 325 }); 326 327 add_task(async function test_incoming_sets_seen() { 328 _("Test the sync-seen allow-list"); 329 330 let engine = Service.engineManager.get("prefs"); 331 let store = engine._store; 332 333 Services.prefs.readDefaultPrefsFromFile( 334 do_get_file("prefs_test_prefs_store.js") 335 ); 336 const defaultValue = "the value"; 337 Assert.equal(Services.prefs.getStringPref("testing.seen"), defaultValue); 338 339 let record = await store.createRecord(PREFS_GUID, "prefs"); 340 // Haven't seen a non-default value before, so remains null. 341 Assert.strictEqual(record.value["testing.seen"], null); 342 343 // pretend an incoming record with the default value - it might not be 344 // the default everywhere, so we treat it specially. 345 record = new PrefRec("prefs", PREFS_GUID); 346 record.value = { 347 "testing.seen": defaultValue, 348 }; 349 await store.update(record); 350 // Our special control value should now be set. 351 Assert.strictEqual( 352 Services.prefs.getBoolPref("services.sync.prefs.sync-seen.testing.seen"), 353 true 354 ); 355 // It's still the default value, so the value is not considered changed 356 Assert.equal(Services.prefs.prefHasUserValue("testing.seen"), false); 357 358 // But now that special control value is set, the record always contains the value. 359 record = await store.createRecord(PREFS_GUID, "prefs"); 360 Assert.strictEqual(record.value["testing.seen"], defaultValue); 361 }); 362 363 add_task(async function test_outgoing_when_changed() { 364 _("Test the 'seen' pref is set first sync of non-default value"); 365 366 let engine = Service.engineManager.get("prefs"); 367 let store = engine._store; 368 clearValues(); 369 370 Services.prefs.readDefaultPrefsFromFile( 371 do_get_file("prefs_test_prefs_store.js") 372 ); 373 const defaultValue = "the value"; 374 Assert.equal(Services.prefs.getStringPref("testing.seen"), defaultValue); 375 376 let record = await store.createRecord(PREFS_GUID, "prefs"); 377 // Haven't seen a non-default value before, so remains null. 378 Assert.strictEqual(record.value["testing.seen"], null); 379 380 // Change the value. 381 Services.prefs.setStringPref("testing.seen", "new value"); 382 record = await store.createRecord(PREFS_GUID, "prefs"); 383 // creating the record toggled that "seen" pref. 384 Assert.strictEqual( 385 Services.prefs.getBoolPref("services.sync.prefs.sync-seen.testing.seen"), 386 true 387 ); 388 Assert.strictEqual(Services.prefs.getStringPref("testing.seen"), "new value"); 389 390 // Resetting the pref does not change that seen value. 391 Services.prefs.clearUserPref("testing.seen"); 392 Assert.strictEqual( 393 Services.prefs.getStringPref("testing.seen"), 394 defaultValue 395 ); 396 397 record = await store.createRecord(PREFS_GUID, "prefs"); 398 Assert.strictEqual( 399 Services.prefs.getBoolPref("services.sync.prefs.sync-seen.testing.seen"), 400 true 401 ); 402 });