browser_localStorage_snapshotting.js (25633B)
1 const HELPER_PAGE_URL = 2 "https://example.com/browser/dom/tests/browser/page_localstorage_snapshotting.html"; 3 const HELPER_PAGE_ORIGIN = "https://example.com/"; 4 5 /* import-globals-from helper_localStorage.js */ 6 7 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/")); 8 Services.scriptloader.loadSubScript(testDir + "/helper_localStorage.js", this); 9 10 function clearOrigin() { 11 let principal = 12 Services.scriptSecurityManager.createContentPrincipalFromOrigin( 13 HELPER_PAGE_ORIGIN 14 ); 15 let request = Services.qms.clearStoragesForClient(principal, "ls", "default"); 16 let promise = new Promise(resolve => { 17 request.callback = () => { 18 resolve(); 19 }; 20 }); 21 return promise; 22 } 23 24 async function applyMutations(knownTab, mutations) { 25 await SpecialPowers.spawn( 26 knownTab.tab.linkedBrowser, 27 [mutations], 28 function (mutations) { 29 return content.wrappedJSObject.applyMutations( 30 Cu.cloneInto(mutations, content) 31 ); 32 } 33 ); 34 } 35 36 async function verifyState(knownTab, expectedState) { 37 let actualState = await SpecialPowers.spawn( 38 knownTab.tab.linkedBrowser, 39 [], 40 function () { 41 return content.wrappedJSObject.getState(); 42 } 43 ); 44 45 for (let [expectedKey, expectedValue] of Object.entries(expectedState)) { 46 ok(actualState.hasOwnProperty(expectedKey), "key present: " + expectedKey); 47 is(actualState[expectedKey], expectedValue, "value correct"); 48 } 49 for (let actualKey of Object.keys(actualState)) { 50 if (!expectedState.hasOwnProperty(actualKey)) { 51 ok(false, "actual state has key it shouldn't have: " + actualKey); 52 } 53 } 54 } 55 56 async function getKeys(knownTab) { 57 let keys = await SpecialPowers.spawn( 58 knownTab.tab.linkedBrowser, 59 [], 60 function () { 61 return content.wrappedJSObject.getKeys(); 62 } 63 ); 64 return keys; 65 } 66 67 async function beginExplicitSnapshot(knownTab) { 68 await SpecialPowers.spawn(knownTab.tab.linkedBrowser, [], function () { 69 return content.wrappedJSObject.beginExplicitSnapshot(); 70 }); 71 } 72 73 async function checkpointExplicitSnapshot(knownTab) { 74 await SpecialPowers.spawn(knownTab.tab.linkedBrowser, [], function () { 75 return content.wrappedJSObject.checkpointExplicitSnapshot(); 76 }); 77 } 78 79 async function endExplicitSnapshot(knownTab) { 80 await SpecialPowers.spawn(knownTab.tab.linkedBrowser, [], function () { 81 return content.wrappedJSObject.endExplicitSnapshot(); 82 }); 83 } 84 85 async function verifyHasSnapshot(knownTab, expectedHasSnapshot) { 86 let hasSnapshot = await SpecialPowers.spawn( 87 knownTab.tab.linkedBrowser, 88 [], 89 function () { 90 return content.wrappedJSObject.getHasSnapshot(); 91 } 92 ); 93 is(hasSnapshot, expectedHasSnapshot, "Correct has snapshot"); 94 } 95 96 async function verifySnapshotUsage(knownTab, expectedSnapshotUsage) { 97 let snapshotUsage = await SpecialPowers.spawn( 98 knownTab.tab.linkedBrowser, 99 [], 100 function () { 101 return content.wrappedJSObject.getSnapshotUsage(); 102 } 103 ); 104 is(snapshotUsage, expectedSnapshotUsage, "Correct snapshot usage"); 105 } 106 107 async function verifyParentState(expectedState) { 108 let principal = 109 Services.scriptSecurityManager.createContentPrincipalFromOrigin( 110 HELPER_PAGE_ORIGIN 111 ); 112 113 let actualState = await Services.domStorageManager.getState(principal); 114 115 for (let [expectedKey, expectedValue] of Object.entries(expectedState)) { 116 ok(actualState.hasOwnProperty(expectedKey), "key present: " + expectedKey); 117 is(actualState[expectedKey], expectedValue, "value correct"); 118 } 119 for (let actualKey of Object.keys(actualState)) { 120 if (!expectedState.hasOwnProperty(actualKey)) { 121 ok(false, "actual state has key it shouldn't have: " + actualKey); 122 } 123 } 124 } 125 126 // We spin up a ton of child processes. 127 requestLongerTimeout(6); 128 129 /** 130 * Verify snapshotting of our localStorage implementation in multi-e10s setup. 131 */ 132 add_task(async function () { 133 if (!Services.domStorageManager.nextGenLocalStorageEnabled) { 134 ok(true, "Test ignored when the next gen local storage is not enabled."); 135 return; 136 } 137 138 await SpecialPowers.pushPrefEnv({ 139 set: [ 140 // Enable LocalStorage's testing API so we can explicitly create 141 // snapshots when needed. 142 ["dom.storage.testing", true], 143 // Force multiple web and webIsolated content processes so that the 144 // multi-e10s logic works correctly. 145 ["dom.ipc.processCount", 8], 146 ["dom.ipc.processCount.webIsolated", 4], 147 ], 148 }); 149 150 // Ensure that there is no localstorage data by forcing the origin to be 151 // cleared prior to the start of our test.. 152 await clearOrigin(); 153 154 // - Open tabs. Don't configure any of them yet. 155 const knownTabs = new KnownTabs(); 156 const writerTab1 = await openTestTab( 157 HELPER_PAGE_URL, 158 "writer1", 159 knownTabs, 160 true 161 ); 162 const writerTab2 = await openTestTab( 163 HELPER_PAGE_URL, 164 "writer2", 165 knownTabs, 166 true 167 ); 168 const readerTab1 = await openTestTab( 169 HELPER_PAGE_URL, 170 "reader1", 171 knownTabs, 172 true 173 ); 174 const readerTab2 = await openTestTab( 175 HELPER_PAGE_URL, 176 "reader2", 177 knownTabs, 178 true 179 ); 180 181 const initialMutations = [ 182 [null, null], 183 ["key1", "initial1"], 184 ["key2", "initial2"], 185 ["key3", "initial3"], 186 ["key5", "initial5"], 187 ["key6", "initial6"], 188 ["key7", "initial7"], 189 ["key8", "initial8"], 190 ]; 191 192 const initialState = { 193 key1: "initial1", 194 key2: "initial2", 195 key3: "initial3", 196 key5: "initial5", 197 key6: "initial6", 198 key7: "initial7", 199 key8: "initial8", 200 }; 201 202 let sizeOfOneKey; 203 let sizeOfOneValue; 204 let sizeOfOneItem; 205 let sizeOfKeys = 0; 206 let sizeOfItems = 0; 207 208 let entries = Object.entries(initialState); 209 for (let i = 0; i < entries.length; i++) { 210 let entry = entries[i]; 211 let sizeOfKey = entry[0].length; 212 let sizeOfValue = entry[1].length; 213 let sizeOfItem = sizeOfKey + sizeOfValue; 214 if (i == 0) { 215 sizeOfOneKey = sizeOfKey; 216 sizeOfOneValue = sizeOfValue; 217 sizeOfOneItem = sizeOfItem; 218 } 219 sizeOfKeys += sizeOfKey; 220 sizeOfItems += sizeOfItem; 221 } 222 223 info("Size of one key is " + sizeOfOneKey); 224 info("Size of one value is " + sizeOfOneValue); 225 info("Size of one item is " + sizeOfOneItem); 226 info("Size of keys is " + sizeOfKeys); 227 info("Size of items is " + sizeOfItems); 228 229 const prefillValues = [ 230 // Zero prefill (prefill disabled) 231 0, 232 // Less than one key length prefill 233 sizeOfOneKey - 1, 234 // Greater than one key length and less than one item length prefill 235 sizeOfOneKey + 1, 236 // Precisely one item length prefill 237 sizeOfOneItem, 238 // Precisely two times one item length prefill 239 2 * sizeOfOneItem, 240 // Precisely size of keys prefill 241 sizeOfKeys, 242 // Less than size of keys plus one value length prefill 243 sizeOfKeys + sizeOfOneValue - 1, 244 // Precisely size of keys plus one value length prefill 245 sizeOfKeys + sizeOfOneValue, 246 // Greater than size of keys plus one value length and less than size of 247 // keys plus two times one value length prefill 248 sizeOfKeys + sizeOfOneValue + 1, 249 // Precisely size of keys plus two times one value length prefill 250 sizeOfKeys + 2 * sizeOfOneValue, 251 // Precisely size of keys plus three times one value length prefill 252 sizeOfKeys + 3 * sizeOfOneValue, 253 // Precisely size of keys plus four times one value length prefill 254 sizeOfKeys + 4 * sizeOfOneValue, 255 // Precisely size of keys plus five times one value length prefill 256 sizeOfKeys + 5 * sizeOfOneValue, 257 // Precisely size of keys plus six times one value length prefill 258 sizeOfKeys + 6 * sizeOfOneValue, 259 // Precisely size of items prefill 260 sizeOfItems, 261 // Unlimited prefill 262 -1, 263 ]; 264 265 for (let prefillValue of prefillValues) { 266 info("Setting prefill value to " + prefillValue); 267 268 await SpecialPowers.pushPrefEnv({ 269 set: [["dom.storage.snapshot_prefill", prefillValue]], 270 }); 271 272 const gradualPrefillValues = [ 273 // Zero gradual prefill 274 0, 275 // Less than one key length gradual prefill 276 sizeOfOneKey - 1, 277 // Greater than one key length and less than one item length gradual 278 // prefill 279 sizeOfOneKey + 1, 280 // Precisely one item length gradual prefill 281 sizeOfOneItem, 282 // Precisely two times one item length gradual prefill 283 2 * sizeOfOneItem, 284 // Precisely three times one item length gradual prefill 285 3 * sizeOfOneItem, 286 // Precisely four times one item length gradual prefill 287 4 * sizeOfOneItem, 288 // Precisely five times one item length gradual prefill 289 5 * sizeOfOneItem, 290 // Precisely six times one item length gradual prefill 291 6 * sizeOfOneItem, 292 // Precisely size of items prefill 293 sizeOfItems, 294 // Unlimited gradual prefill 295 -1, 296 ]; 297 298 for (let gradualPrefillValue of gradualPrefillValues) { 299 info("Setting gradual prefill value to " + gradualPrefillValue); 300 301 await SpecialPowers.pushPrefEnv({ 302 set: [["dom.storage.snapshot_gradual_prefill", gradualPrefillValue]], 303 }); 304 305 info("Stage 1"); 306 307 const setRemoveMutations1 = [ 308 ["key0", "setRemove10"], 309 ["key1", "setRemove11"], 310 ["key2", null], 311 ["key3", "setRemove13"], 312 ["key4", "setRemove14"], 313 ["key5", "setRemove15"], 314 ["key6", "setRemove16"], 315 ["key7", "setRemove17"], 316 ["key8", null], 317 ["key9", "setRemove19"], 318 ]; 319 320 const setRemoveState1 = { 321 key0: "setRemove10", 322 key1: "setRemove11", 323 key3: "setRemove13", 324 key4: "setRemove14", 325 key5: "setRemove15", 326 key6: "setRemove16", 327 key7: "setRemove17", 328 key9: "setRemove19", 329 }; 330 331 const setRemoveMutations2 = [ 332 ["key0", "setRemove20"], 333 ["key1", null], 334 ["key2", "setRemove22"], 335 ["key3", "setRemove23"], 336 ["key4", "setRemove24"], 337 ["key5", "setRemove25"], 338 ["key6", "setRemove26"], 339 ["key7", null], 340 ["key8", "setRemove28"], 341 ["key9", "setRemove29"], 342 ]; 343 344 const setRemoveState2 = { 345 key0: "setRemove20", 346 key2: "setRemove22", 347 key3: "setRemove23", 348 key4: "setRemove24", 349 key5: "setRemove25", 350 key6: "setRemove26", 351 key8: "setRemove28", 352 key9: "setRemove29", 353 }; 354 355 // Apply initial mutations using an explicit snapshot. The explicit 356 // snapshot here ensures that the parent process have received the 357 // changes. 358 await beginExplicitSnapshot(writerTab1); 359 await applyMutations(writerTab1, initialMutations); 360 await endExplicitSnapshot(writerTab1); 361 362 // Begin explicit snapshots in all tabs except readerTab2. All these tabs 363 // should see the initial state regardless what other tabs are doing. 364 await beginExplicitSnapshot(writerTab1); 365 await beginExplicitSnapshot(writerTab2); 366 await beginExplicitSnapshot(readerTab1); 367 368 // Apply first array of set/remove mutations in writerTab1 and end the 369 // explicit snapshot. This will trigger saving of values in other active 370 // snapshots. 371 await applyMutations(writerTab1, setRemoveMutations1); 372 await endExplicitSnapshot(writerTab1); 373 374 // Begin an explicit snapshot in readerTab2. writerTab1 already ended its 375 // explicit snapshot, so readerTab2 should see mutations done by 376 // writerTab1. 377 await beginExplicitSnapshot(readerTab2); 378 379 // Apply second array of set/remove mutations in writerTab2 and end the 380 // explicit snapshot. This will trigger saving of values in other active 381 // snapshots, but only if they haven't been saved already. 382 await applyMutations(writerTab2, setRemoveMutations2); 383 await endExplicitSnapshot(writerTab2); 384 385 // Verify state in readerTab1, it should match the initial state. 386 await verifyState(readerTab1, initialState); 387 await endExplicitSnapshot(readerTab1); 388 389 // Verify state in readerTab2, it should match the state after the first 390 // array of set/remove mutatations have been applied and "commited". 391 await verifyState(readerTab2, setRemoveState1); 392 await endExplicitSnapshot(readerTab2); 393 394 // Verify final state, it should match the state after the second array of 395 // set/remove mutation have been applied and "commited". An explicit 396 // snapshot is used. 397 await beginExplicitSnapshot(readerTab1); 398 await verifyState(readerTab1, setRemoveState2); 399 await endExplicitSnapshot(readerTab1); 400 401 info("Stage 2"); 402 403 const setRemoveClearMutations1 = [ 404 ["key0", "setRemoveClear10"], 405 ["key1", null], 406 [null, null], 407 ]; 408 409 const setRemoveClearState1 = {}; 410 411 const setRemoveClearMutations2 = [ 412 ["key8", null], 413 ["key9", "setRemoveClear29"], 414 [null, null], 415 ]; 416 417 const setRemoveClearState2 = {}; 418 419 // This is very similar to previous stage except that in addition to 420 // set/remove, the clear operation is involved too. 421 await beginExplicitSnapshot(writerTab1); 422 await applyMutations(writerTab1, initialMutations); 423 await endExplicitSnapshot(writerTab1); 424 425 await beginExplicitSnapshot(writerTab1); 426 await beginExplicitSnapshot(writerTab2); 427 await beginExplicitSnapshot(readerTab1); 428 429 await applyMutations(writerTab1, setRemoveClearMutations1); 430 await endExplicitSnapshot(writerTab1); 431 432 await beginExplicitSnapshot(readerTab2); 433 434 await applyMutations(writerTab2, setRemoveClearMutations2); 435 await endExplicitSnapshot(writerTab2); 436 437 await verifyState(readerTab1, initialState); 438 await endExplicitSnapshot(readerTab1); 439 440 await verifyState(readerTab2, setRemoveClearState1); 441 await endExplicitSnapshot(readerTab2); 442 443 await beginExplicitSnapshot(readerTab1); 444 await verifyState(readerTab1, setRemoveClearState2); 445 await endExplicitSnapshot(readerTab1); 446 447 info("Stage 3"); 448 449 const changeOrderMutations = [ 450 ["key1", null], 451 ["key2", null], 452 ["key3", null], 453 ["key5", null], 454 ["key6", null], 455 ["key7", null], 456 ["key8", null], 457 ["key8", "initial8"], 458 ["key7", "initial7"], 459 ["key6", "initial6"], 460 ["key5", "initial5"], 461 ["key3", "initial3"], 462 ["key2", "initial2"], 463 ["key1", "initial1"], 464 ]; 465 466 // Apply initial mutations using an explicit snapshot. The explicit 467 // snapshot here ensures that the parent process have received the 468 // changes. 469 await beginExplicitSnapshot(writerTab1); 470 await applyMutations(writerTab1, initialMutations); 471 await endExplicitSnapshot(writerTab1); 472 473 // Begin explicit snapshots in all tabs except writerTab2 which is not 474 // used in this stage. All these tabs should see the initial order 475 // regardless what other tabs are doing. 476 await beginExplicitSnapshot(readerTab1); 477 await beginExplicitSnapshot(writerTab1); 478 await beginExplicitSnapshot(readerTab2); 479 480 // Get all keys in readerTab1 and end the explicit snapshot. No mutations 481 // have been applied yet. 482 let tab1Keys = await getKeys(readerTab1); 483 await endExplicitSnapshot(readerTab1); 484 485 // Apply mutations that change the order of keys and end the explicit 486 // snapshot. The state is unchanged. This will trigger saving of key order 487 // in other active snapshots, but only if the order hasn't been saved 488 // already. 489 await applyMutations(writerTab1, changeOrderMutations); 490 await endExplicitSnapshot(writerTab1); 491 492 // Get all keys in readerTab2 and end the explicit snapshot. Change order 493 // mutations have been applied, but the order should stay unchanged. 494 let tab2Keys = await getKeys(readerTab2); 495 await endExplicitSnapshot(readerTab2); 496 497 // Verify the key order is the same. 498 is(tab2Keys.length, tab1Keys.length, "Correct keys length"); 499 for (let i = 0; i < tab2Keys.length; i++) { 500 is(tab2Keys[i], tab1Keys[i], "Correct key"); 501 } 502 503 // Verify final state, it should match the initial state since applied 504 // mutations only changed the key order. An explicit snapshot is used. 505 await beginExplicitSnapshot(readerTab1); 506 await verifyState(readerTab1, initialState); 507 await endExplicitSnapshot(readerTab1); 508 } 509 } 510 511 // - Clean up. 512 await cleanupTabs(knownTabs); 513 514 clearOrigin(); 515 }); 516 517 /** 518 * Verify that snapshots are able to work with negative usage. This can happen 519 * when there's an item stored in localStorage with given size and then two 520 * snaphots (created at the same time) mutate the item. The first one replases 521 * it with something bigger and the other one removes it. 522 */ 523 add_task(async function () { 524 if (!Services.domStorageManager.nextGenLocalStorageEnabled) { 525 ok(true, "Test ignored when the next gen local storage is not enabled."); 526 return; 527 } 528 529 await SpecialPowers.pushPrefEnv({ 530 set: [ 531 // Force multiple web and webIsolated content processes so that the 532 // multi-e10s logic works correctly. 533 ["dom.ipc.processCount", 4], 534 ["dom.ipc.processCount.webIsolated", 2], 535 // Disable snapshot peak usage pre-incrementation to make the testing 536 // easier. 537 ["dom.storage.snapshot_peak_usage.initial_preincrement", 0], 538 ["dom.storage.snapshot_peak_usage.reduced_initial_preincrement", 0], 539 ["dom.storage.snapshot_peak_usage.gradual_preincrement", 0], 540 ["dom.storage.snapshot_peak_usage.reuced_gradual_preincrement", 0], 541 // Enable LocalStorage's testing API so we can explicitly create 542 // snapshots when needed. 543 ["dom.storage.testing", true], 544 ], 545 }); 546 547 // Ensure that there is no localstorage data by forcing the origin to be 548 // cleared prior to the start of our test. 549 await clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN); 550 551 // Open tabs. Don't configure any of them yet. 552 const knownTabs = new KnownTabs(); 553 const writerTab1 = await openTestTab( 554 HELPER_PAGE_URL, 555 "writer1", 556 knownTabs, 557 true 558 ); 559 const writerTab2 = await openTestTab( 560 HELPER_PAGE_URL, 561 "writer2", 562 knownTabs, 563 true 564 ); 565 566 // Apply the initial mutation using an explicit snapshot. The explicit 567 // snapshot here ensures that the parent process have received the changes. 568 await beginExplicitSnapshot(writerTab1); 569 await applyMutations(writerTab1, [["key", "something"]]); 570 await endExplicitSnapshot(writerTab1); 571 572 // Begin explicit snapshots in both tabs. Both tabs should see the initial 573 // state. 574 await beginExplicitSnapshot(writerTab1); 575 await beginExplicitSnapshot(writerTab2); 576 577 // Apply the first mutation in writerTab1 and end the explicit snapshot. 578 await applyMutations(writerTab1, [["key", "somethingBigger"]]); 579 await endExplicitSnapshot(writerTab1); 580 581 // Apply the second mutation in writerTab2 and end the explicit snapshot. 582 await applyMutations(writerTab2, [["key", null]]); 583 await endExplicitSnapshot(writerTab2); 584 585 // Verify the final state, it should match the state after the second 586 // mutation has been applied and "commited". An explicit snapshot is used. 587 await beginExplicitSnapshot(writerTab1); 588 await verifyState(writerTab1, {}); 589 await endExplicitSnapshot(writerTab1); 590 591 // Clean up. 592 await cleanupTabs(knownTabs); 593 594 clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN); 595 }); 596 597 /** 598 * Verify that snapshot usage is correctly updated after each operation. 599 */ 600 add_task(async function () { 601 if (!Services.domStorageManager.nextGenLocalStorageEnabled) { 602 ok(true, "Test ignored when the next gen local storage is not enabled."); 603 return; 604 } 605 606 await SpecialPowers.pushPrefEnv({ 607 set: [ 608 // Force multiple web and webIsolated content processes so that the 609 // multi-e10s logic works correctly. 610 ["dom.ipc.processCount", 4], 611 ["dom.ipc.processCount.webIsolated", 2], 612 // Disable snapshot peak usage pre-incrementation to make the testing 613 // easier. 614 ["dom.storage.snapshot_peak_usage.initial_preincrement", 0], 615 ["dom.storage.snapshot_peak_usage.reduced_initial_preincrement", 0], 616 ["dom.storage.snapshot_peak_usage.gradual_preincrement", 0], 617 ["dom.storage.snapshot_peak_usage.reuced_gradual_preincrement", 0], 618 // Enable LocalStorage's testing API so we can explicitly create 619 // snapshots when needed. 620 ["dom.storage.testing", true], 621 ], 622 }); 623 624 // Ensure that there is no localstorage data by forcing the origin to be 625 // cleared prior to the start of our test. 626 await clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN); 627 628 // Open tabs. Don't configure any of them yet. 629 const knownTabs = new KnownTabs(); 630 const writerTab1 = await openTestTab( 631 HELPER_PAGE_URL, 632 "writer1", 633 knownTabs, 634 true 635 ); 636 const writerTab2 = await openTestTab( 637 HELPER_PAGE_URL, 638 "writer2", 639 knownTabs, 640 true 641 ); 642 643 // Apply the initial mutation using an explicit snapshot. The explicit 644 // snapshot here ensures that the parent process have received the changes. 645 await beginExplicitSnapshot(writerTab1); 646 await verifySnapshotUsage(writerTab1, 0); 647 await applyMutations(writerTab1, [["key", "something"]]); 648 await verifySnapshotUsage(writerTab1, 12); 649 await endExplicitSnapshot(writerTab1); 650 await verifyHasSnapshot(writerTab1, false); 651 652 // Begin an explicit snapshot in writerTab1 and apply the first mutatation 653 // in it. 654 await beginExplicitSnapshot(writerTab1); 655 await verifySnapshotUsage(writerTab1, 12); 656 await applyMutations(writerTab1, [["key", "somethingBigger"]]); 657 await verifySnapshotUsage(writerTab1, 18); 658 659 // Begin an explicit snapshot in writerTab2 and apply the second mutatation 660 // in it. 661 await beginExplicitSnapshot(writerTab2); 662 await verifySnapshotUsage(writerTab2, 18); 663 await applyMutations(writerTab2, [[null, null]]); 664 await verifySnapshotUsage(writerTab2, 6); 665 666 // End explicit snapshots in both tabs. 667 await endExplicitSnapshot(writerTab1); 668 await verifyHasSnapshot(writerTab1, false); 669 await endExplicitSnapshot(writerTab2); 670 await verifyHasSnapshot(writerTab2, false); 671 672 // Verify the final state, it should match the state after the second 673 // mutation has been applied and "commited". An explicit snapshot is used. 674 await beginExplicitSnapshot(writerTab1); 675 await verifySnapshotUsage(writerTab1, 0); 676 await verifyState(writerTab1, {}); 677 await endExplicitSnapshot(writerTab1); 678 await verifyHasSnapshot(writerTab1, false); 679 680 // Clean up. 681 await cleanupTabs(knownTabs); 682 683 clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN); 684 }); 685 686 /** 687 * Verify that datastore in the parent is correctly updated after a checkpoint. 688 */ 689 add_task(async function () { 690 if (!Services.domStorageManager.nextGenLocalStorageEnabled) { 691 ok(true, "Test ignored when the next gen local storage is not enabled."); 692 return; 693 } 694 695 await SpecialPowers.pushPrefEnv({ 696 set: [ 697 // Force multiple web and webIsolated content processes so that the 698 // multi-e10s logic works correctly. 699 ["dom.ipc.processCount", 4], 700 ["dom.ipc.processCount.webIsolated", 2], 701 // Enable LocalStorage's testing API so we can explicitly create 702 // snapshots when needed. 703 ["dom.storage.testing", true], 704 ], 705 }); 706 707 // Ensure that there is no localstorage data by forcing the origin to be 708 // cleared prior to the start of our test. 709 await clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN); 710 711 // Open tabs. Don't configure any of them yet. 712 const knownTabs = new KnownTabs(); 713 const writerTab1 = await openTestTab( 714 HELPER_PAGE_URL, 715 "writer1", 716 knownTabs, 717 true 718 ); 719 720 await verifyParentState({}); 721 722 // Apply the initial mutation using an explicit snapshot. The explicit 723 // snapshot here ensures that the parent process have received the changes. 724 await beginExplicitSnapshot(writerTab1); 725 await verifyParentState({}); 726 await applyMutations(writerTab1, [["key", "something"]]); 727 await verifyParentState({}); 728 await endExplicitSnapshot(writerTab1); 729 730 await verifyParentState({ key: "something" }); 731 732 // Begin an explicit snapshot in writerTab1, apply the first mutation in 733 // writerTab1 and checkpoint the explicit snapshot. 734 await beginExplicitSnapshot(writerTab1); 735 await verifyParentState({ key: "something" }); 736 await applyMutations(writerTab1, [["key", "somethingBigger"]]); 737 await verifyParentState({ key: "something" }); 738 await checkpointExplicitSnapshot(writerTab1); 739 740 await verifyParentState({ key: "somethingBigger" }); 741 742 // Apply the second mutation in writerTab1 and checkpoint the explicit 743 // snapshot. 744 await applyMutations(writerTab1, [["key", null]]); 745 await verifyParentState({ key: "somethingBigger" }); 746 await checkpointExplicitSnapshot(writerTab1); 747 748 await verifyParentState({}); 749 750 // Apply the third mutation in writerTab1 and end the explicit snapshot. 751 await applyMutations(writerTab1, [["otherKey", "something"]]); 752 await verifyParentState({}); 753 await endExplicitSnapshot(writerTab1); 754 755 await verifyParentState({ otherKey: "something" }); 756 757 // Verify the final state, it should match the state after the third mutation 758 // has been applied and "commited". An explicit snapshot is used. 759 await beginExplicitSnapshot(writerTab1); 760 await verifyParentState({ otherKey: "something" }); 761 await verifyState(writerTab1, { otherKey: "something" }); 762 await endExplicitSnapshot(writerTab1); 763 764 await verifyParentState({ otherKey: "something" }); 765 766 // Clean up. 767 await cleanupTabs(knownTabs); 768 769 clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN); 770 });