fune/dom/tests/browser/browser_localStorage_snapshotting_e10s.js
2019-01-22 21:26:02 +00:00

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