forked from mirrors/gecko-dev
		
	 a3846471d6
			
		
	
	
		a3846471d6
		
	
	
	
	
		
			
			Differential Revision: https://phabricator.services.mozilla.com/D14018 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			372 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			372 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const HELPER_PAGE_URL =
 | |
|   "http://example.com/browser/dom/tests/browser/page_localstorage_snapshotting_e10s.html";
 | |
| const HELPER_PAGE_ORIGIN = "http://example.com/";
 | |
| 
 | |
| /* import-globals-from helper_localStorage_e10s.js */
 | |
| 
 | |
| let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 | |
| Services.scriptloader.loadSubScript(testDir + "/helper_localStorage_e10s.js",
 | |
|                                     this);
 | |
| 
 | |
| function clearOrigin() {
 | |
|   let principal =
 | |
|     Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
 | |
|       HELPER_PAGE_ORIGIN);
 | |
|   let request =
 | |
|     Services.qms.clearStoragesForPrincipal(principal, "default", "ls");
 | |
|   let promise = new Promise(resolve => {
 | |
|     request.callback = () => {
 | |
|       resolve();
 | |
|     };
 | |
|   });
 | |
|   return promise;
 | |
| }
 | |
| 
 | |
| async function applyMutations(knownTab, mutations) {
 | |
|   await ContentTask.spawn(
 | |
|     knownTab.tab.linkedBrowser,
 | |
|     mutations,
 | |
|     function(mutations) {
 | |
|       return content.wrappedJSObject.applyMutations(Cu.cloneInto(mutations,
 | |
|                                                                  content));
 | |
|     });
 | |
| }
 | |
| 
 | |
| async function verifyState(knownTab, expectedState) {
 | |
|   let actualState = await ContentTask.spawn(
 | |
|     knownTab.tab.linkedBrowser,
 | |
|     {},
 | |
|     function() {
 | |
|       return content.wrappedJSObject.getState();
 | |
|     });
 | |
| 
 | |
|   for (let [expectedKey, expectedValue] of Object.entries(expectedState)) {
 | |
|     ok(actualState.hasOwnProperty(expectedKey), "key present: " + expectedKey);
 | |
|     is(actualState[expectedKey], expectedValue, "value correct");
 | |
|   }
 | |
|   for (let actualKey of Object.keys(actualState)) {
 | |
|     if (!expectedState.hasOwnProperty(actualKey)) {
 | |
|       ok(false, "actual state has key it shouldn't have: " + actualKey);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| async function getKeys(knownTab) {
 | |
|   let keys = await ContentTask.spawn(
 | |
|     knownTab.tab.linkedBrowser,
 | |
|     null,
 | |
|     function() {
 | |
|       return content.wrappedJSObject.getKeys();
 | |
|     });
 | |
|   return keys;
 | |
| }
 | |
| 
 | |
| async function beginExplicitSnapshot(knownTab) {
 | |
|   await ContentTask.spawn(
 | |
|     knownTab.tab.linkedBrowser,
 | |
|     null,
 | |
|     function() {
 | |
|       return content.wrappedJSObject.beginExplicitSnapshot();
 | |
|     });
 | |
| }
 | |
| 
 | |
| async function endExplicitSnapshot(knownTab) {
 | |
|   await ContentTask.spawn(
 | |
|     knownTab.tab.linkedBrowser,
 | |
|     null,
 | |
|     function() {
 | |
|       return content.wrappedJSObject.endExplicitSnapshot();
 | |
|     });
 | |
| }
 | |
| 
 | |
| // We spin up a ton of child processes.
 | |
| requestLongerTimeout(4);
 | |
| 
 | |
| /**
 | |
|  * Verify snapshotting of our localStorage implementation in multi-e10s setup.
 | |
|  */
 | |
| add_task(async function() {
 | |
|   if (!Services.lsm.nextGenLocalStorageEnabled) {
 | |
|     ok(true, "Test ignored when the next gen local storage is not enabled.");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   await SpecialPowers.pushPrefEnv({
 | |
|     set: [
 | |
|       // Enable LocalStorage's testing API so we can explicitly create
 | |
|       // snapshots when needed.
 | |
|       ["dom.storage.testing", true],
 | |
|     ],
 | |
|   });
 | |
| 
 | |
|   // Ensure that there is no localstorage data by forcing the origin to be
 | |
|   // cleared prior to the start of our test..
 | |
|   await clearOrigin();
 | |
| 
 | |
|   // - Open tabs.  Don't configure any of them yet.
 | |
|   const knownTabs = new KnownTabs();
 | |
|   const writerTab1 = await openTestTabInOwnProcess(HELPER_PAGE_URL, "writer1",
 | |
|     knownTabs);
 | |
|   const writerTab2 = await openTestTabInOwnProcess(HELPER_PAGE_URL, "writer2",
 | |
|     knownTabs);
 | |
|   const readerTab1 = await openTestTabInOwnProcess(HELPER_PAGE_URL, "reader1",
 | |
|     knownTabs);
 | |
|   const readerTab2 = await openTestTabInOwnProcess(HELPER_PAGE_URL, "reader2",
 | |
|     knownTabs);
 | |
| 
 | |
|   const initialMutations = [
 | |
|     [null, null],
 | |
|     ["key1", "initial1"],
 | |
|     ["key2", "initial2"],
 | |
|     ["key3", "initial3"],
 | |
|     ["key5", "initial5"],
 | |
|     ["key6", "initial6"],
 | |
|     ["key7", "initial7"],
 | |
|     ["key8", "initial8"],
 | |
|   ];
 | |
| 
 | |
|   const initialState = {
 | |
|     key1: "initial1",
 | |
|     key2: "initial2",
 | |
|     key3: "initial3",
 | |
|     key5: "initial5",
 | |
|     key6: "initial6",
 | |
|     key7: "initial7",
 | |
|     key8: "initial8",
 | |
|   };
 | |
| 
 | |
|   function getPartialPrefill() {
 | |
|     let size = 0;
 | |
|     let entries = Object.entries(initialState);
 | |
|     for (let i = 0; i < entries.length / 2; i++) {
 | |
|       let entry = entries[i];
 | |
|       size += entry[0].length + entry[1].length;
 | |
|     }
 | |
|     return size;
 | |
|   }
 | |
| 
 | |
|   const prefillValues = [
 | |
|     0,                   // no prefill
 | |
|     getPartialPrefill(), // partial prefill
 | |
|     -1,                  // full prefill
 | |
|   ];
 | |
| 
 | |
|   for (let prefillValue of prefillValues) {
 | |
|     info("Setting prefill value");
 | |
| 
 | |
|     await SpecialPowers.pushPrefEnv({
 | |
|       set: [
 | |
|         ["dom.storage.snapshot_prefill", prefillValue],
 | |
|       ],
 | |
|     });
 | |
| 
 | |
|     info("Stage 1");
 | |
| 
 | |
|     const setRemoveMutations1 = [
 | |
|       ["key0", "setRemove10"],
 | |
|       ["key1", "setRemove11"],
 | |
|       ["key2", null],
 | |
|       ["key3", "setRemove13"],
 | |
|       ["key4", "setRemove14"],
 | |
|       ["key5", "setRemove15"],
 | |
|       ["key6", "setRemove16"],
 | |
|       ["key7", "setRemove17"],
 | |
|       ["key8", null],
 | |
|       ["key9", "setRemove19"],
 | |
|     ];
 | |
| 
 | |
|     const setRemoveState1 = {
 | |
|       key0: "setRemove10",
 | |
|       key1: "setRemove11",
 | |
|       key3: "setRemove13",
 | |
|       key4: "setRemove14",
 | |
|       key5: "setRemove15",
 | |
|       key6: "setRemove16",
 | |
|       key7: "setRemove17",
 | |
|       key9: "setRemove19",
 | |
|     };
 | |
| 
 | |
|     const setRemoveMutations2 = [
 | |
|       ["key0", "setRemove20"],
 | |
|       ["key1", null],
 | |
|       ["key2", "setRemove22"],
 | |
|       ["key3", "setRemove23"],
 | |
|       ["key4", "setRemove24"],
 | |
|       ["key5", "setRemove25"],
 | |
|       ["key6", "setRemove26"],
 | |
|       ["key7", null],
 | |
|       ["key8", "setRemove28"],
 | |
|       ["key9", "setRemove29"],
 | |
|     ];
 | |
| 
 | |
|     const setRemoveState2 = {
 | |
|       key0: "setRemove20",
 | |
|       key2: "setRemove22",
 | |
|       key3: "setRemove23",
 | |
|       key4: "setRemove24",
 | |
|       key5: "setRemove25",
 | |
|       key6: "setRemove26",
 | |
|       key8: "setRemove28",
 | |
|       key9: "setRemove29",
 | |
|     };
 | |
| 
 | |
|     // Apply initial mutations using an explicit snapshot. The explicit
 | |
|     // snapshot here ensures that the parent process have received the changes.
 | |
|     await beginExplicitSnapshot(writerTab1);
 | |
|     await applyMutations(writerTab1, initialMutations);
 | |
|     await endExplicitSnapshot(writerTab1);
 | |
| 
 | |
|     // Begin explicit snapshots in all tabs except readerTab2. All these tabs
 | |
|     // should see the initial state regardless what other tabs are doing.
 | |
|     await beginExplicitSnapshot(writerTab1);
 | |
|     await beginExplicitSnapshot(writerTab2);
 | |
|     await beginExplicitSnapshot(readerTab1);
 | |
| 
 | |
|     // Apply first array of set/remove mutations in writerTab1 and end the
 | |
|     // explicit snapshot. This will trigger saving of values in other active
 | |
|     // snapshots.
 | |
|     await applyMutations(writerTab1, setRemoveMutations1);
 | |
|     await endExplicitSnapshot(writerTab1);
 | |
| 
 | |
|     // Begin an explicit snapshot in readerTab2. writerTab1 already ended its
 | |
|     // explicit snapshot, so readerTab2 should see mutations done by
 | |
|     // writerTab1.
 | |
|     await beginExplicitSnapshot(readerTab2);
 | |
| 
 | |
|     // Apply second array of set/remove mutations in writerTab2 and end the
 | |
|     // explicit snapshot. This will trigger saving of values in other active
 | |
|     // snapshots, but only if they haven't been saved already.
 | |
|     await applyMutations(writerTab2, setRemoveMutations2);
 | |
|     await endExplicitSnapshot(writerTab2);
 | |
| 
 | |
|     // Verify state in readerTab1, it should match the initial state.
 | |
|     await verifyState(readerTab1, initialState);
 | |
|     await endExplicitSnapshot(readerTab1);
 | |
| 
 | |
|     // Verify state in readerTab2, it should match the state after the first
 | |
|     // array of set/remove mutatations have been applied and "commited".
 | |
|     await verifyState(readerTab2, setRemoveState1);
 | |
|     await endExplicitSnapshot(readerTab2);
 | |
| 
 | |
|     // Verify final state, it should match the state after the second array of
 | |
|     // set/remove mutation have been applied and "commited". An explicit
 | |
|     // snapshot is used.
 | |
|     await beginExplicitSnapshot(readerTab1);
 | |
|     await verifyState(readerTab1, setRemoveState2);
 | |
|     await endExplicitSnapshot(readerTab1);
 | |
| 
 | |
|     info("Stage 2");
 | |
| 
 | |
|     const setRemoveClearMutations1 = [
 | |
|       ["key0", "setRemoveClear10"],
 | |
|       ["key1", null],
 | |
|       [null, null],
 | |
|     ];
 | |
| 
 | |
|     const setRemoveClearState1 = {
 | |
|     };
 | |
| 
 | |
|     const setRemoveClearMutations2 = [
 | |
|       ["key8", null],
 | |
|       ["key9", "setRemoveClear29"],
 | |
|       [null, null],
 | |
|     ];
 | |
| 
 | |
|     const setRemoveClearState2 = {
 | |
|     };
 | |
| 
 | |
|     // This is very similar to previous stage except that in addition to
 | |
|     // set/remove, the clear operation is involved too.
 | |
|     await beginExplicitSnapshot(writerTab1);
 | |
|     await applyMutations(writerTab1, initialMutations);
 | |
|     await endExplicitSnapshot(writerTab1);
 | |
| 
 | |
|     await beginExplicitSnapshot(writerTab1);
 | |
|     await beginExplicitSnapshot(writerTab2);
 | |
|     await beginExplicitSnapshot(readerTab1);
 | |
| 
 | |
|     await applyMutations(writerTab1, setRemoveClearMutations1);
 | |
|     await endExplicitSnapshot(writerTab1);
 | |
| 
 | |
|     await beginExplicitSnapshot(readerTab2);
 | |
| 
 | |
|     await applyMutations(writerTab2, setRemoveClearMutations2);
 | |
|     await endExplicitSnapshot(writerTab2);
 | |
| 
 | |
|     await verifyState(readerTab1, initialState);
 | |
|     await endExplicitSnapshot(readerTab1);
 | |
| 
 | |
|     await verifyState(readerTab2, setRemoveClearState1);
 | |
|     await endExplicitSnapshot(readerTab2);
 | |
| 
 | |
|     await beginExplicitSnapshot(readerTab1);
 | |
|     await verifyState(readerTab1, setRemoveClearState2);
 | |
|     await endExplicitSnapshot(readerTab1);
 | |
| 
 | |
|     info("Stage 3");
 | |
| 
 | |
|     const changeOrderMutations = [
 | |
|       ["key1", null],
 | |
|       ["key2", null],
 | |
|       ["key3", null],
 | |
|       ["key5", null],
 | |
|       ["key6", null],
 | |
|       ["key7", null],
 | |
|       ["key8", null],
 | |
|       ["key8", "initial8"],
 | |
|       ["key7", "initial7"],
 | |
|       ["key6", "initial6"],
 | |
|       ["key5", "initial5"],
 | |
|       ["key3", "initial3"],
 | |
|       ["key2", "initial2"],
 | |
|       ["key1", "initial1"],
 | |
|     ];
 | |
| 
 | |
|     // Apply initial mutations using an explicit snapshot. The explicit
 | |
|     // snapshot here ensures that the parent process have received the changes.
 | |
|     await beginExplicitSnapshot(writerTab1);
 | |
|     await applyMutations(writerTab1, initialMutations);
 | |
|     await endExplicitSnapshot(writerTab1);
 | |
| 
 | |
|     // Begin explicit snapshots in all tabs except writerTab2 which is not used
 | |
|     // in this stage. All these tabs should see the initial order regardless
 | |
|     // what other tabs are doing.
 | |
|     await beginExplicitSnapshot(readerTab1);
 | |
|     await beginExplicitSnapshot(writerTab1);
 | |
|     await beginExplicitSnapshot(readerTab2);
 | |
| 
 | |
|     // Get all keys in readerTab1 and end the explicit snapshot. No mutations
 | |
|     // have been applied yet.
 | |
|     let tab1Keys = await getKeys(readerTab1);
 | |
|     await endExplicitSnapshot(readerTab1);
 | |
| 
 | |
|     // Apply mutations that change the order of keys and end the explicit
 | |
|     // snapshot. The state is unchanged. This will trigger saving of key order
 | |
|     // in other active snapshots, but only if the order hasn't been saved
 | |
|     // already.
 | |
|     await applyMutations(writerTab1, changeOrderMutations);
 | |
|     await endExplicitSnapshot(writerTab1);
 | |
| 
 | |
|     // Get all keys in readerTab2 and end the explicit snapshot. Change order
 | |
|     // mutations have been applied, but the order should stay unchanged.
 | |
|     let tab2Keys = await getKeys(readerTab2);
 | |
|     await endExplicitSnapshot(readerTab2);
 | |
| 
 | |
|     // Verify the key order is the same.
 | |
|     is(tab2Keys.length, tab1Keys.length, "Correct keys length");
 | |
|     for (let i = 0; i < tab2Keys.length; i++) {
 | |
|       is(tab2Keys[i], tab1Keys[i], "Correct key");
 | |
|     }
 | |
| 
 | |
|     // Verify final state, it should match the initial state since applied
 | |
|     // mutations only changed the key order. An explicit snapshot is used.
 | |
|     await beginExplicitSnapshot(readerTab1);
 | |
|     await verifyState(readerTab1, initialState);
 | |
|     await endExplicitSnapshot(readerTab1);
 | |
|   }
 | |
| 
 | |
|   // - Clean up.
 | |
|   await cleanupTabs(knownTabs);
 | |
| 
 | |
|   clearOrigin();
 | |
| });
 |