770 lines
25 KiB
JavaScript
770 lines
25 KiB
JavaScript
const HELPER_PAGE_URL =
|
|
"https://example.com/browser/dom/tests/browser/page_localstorage_snapshotting.html";
|
|
const HELPER_PAGE_ORIGIN = "https://example.com/";
|
|
|
|
/* import-globals-from helper_localStorage.js */
|
|
|
|
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
|
Services.scriptloader.loadSubScript(testDir + "/helper_localStorage.js", this);
|
|
|
|
function clearOrigin() {
|
|
let principal =
|
|
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
|
|
HELPER_PAGE_ORIGIN
|
|
);
|
|
let request = Services.qms.clearStoragesForClient(principal, "ls", "default");
|
|
let promise = new Promise(resolve => {
|
|
request.callback = () => {
|
|
resolve();
|
|
};
|
|
});
|
|
return promise;
|
|
}
|
|
|
|
async function applyMutations(knownTab, mutations) {
|
|
await SpecialPowers.spawn(
|
|
knownTab.tab.linkedBrowser,
|
|
[mutations],
|
|
function (mutations) {
|
|
return content.wrappedJSObject.applyMutations(
|
|
Cu.cloneInto(mutations, content)
|
|
);
|
|
}
|
|
);
|
|
}
|
|
|
|
async function verifyState(knownTab, expectedState) {
|
|
let actualState = await SpecialPowers.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 SpecialPowers.spawn(
|
|
knownTab.tab.linkedBrowser,
|
|
[],
|
|
function () {
|
|
return content.wrappedJSObject.getKeys();
|
|
}
|
|
);
|
|
return keys;
|
|
}
|
|
|
|
async function beginExplicitSnapshot(knownTab) {
|
|
await SpecialPowers.spawn(knownTab.tab.linkedBrowser, [], function () {
|
|
return content.wrappedJSObject.beginExplicitSnapshot();
|
|
});
|
|
}
|
|
|
|
async function checkpointExplicitSnapshot(knownTab) {
|
|
await SpecialPowers.spawn(knownTab.tab.linkedBrowser, [], function () {
|
|
return content.wrappedJSObject.checkpointExplicitSnapshot();
|
|
});
|
|
}
|
|
|
|
async function endExplicitSnapshot(knownTab) {
|
|
await SpecialPowers.spawn(knownTab.tab.linkedBrowser, [], function () {
|
|
return content.wrappedJSObject.endExplicitSnapshot();
|
|
});
|
|
}
|
|
|
|
async function verifyHasSnapshot(knownTab, expectedHasSnapshot) {
|
|
let hasSnapshot = await SpecialPowers.spawn(
|
|
knownTab.tab.linkedBrowser,
|
|
[],
|
|
function () {
|
|
return content.wrappedJSObject.getHasSnapshot();
|
|
}
|
|
);
|
|
is(hasSnapshot, expectedHasSnapshot, "Correct has snapshot");
|
|
}
|
|
|
|
async function verifySnapshotUsage(knownTab, expectedSnapshotUsage) {
|
|
let snapshotUsage = await SpecialPowers.spawn(
|
|
knownTab.tab.linkedBrowser,
|
|
[],
|
|
function () {
|
|
return content.wrappedJSObject.getSnapshotUsage();
|
|
}
|
|
);
|
|
is(snapshotUsage, expectedSnapshotUsage, "Correct snapshot usage");
|
|
}
|
|
|
|
async function verifyParentState(expectedState) {
|
|
let principal =
|
|
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
|
|
HELPER_PAGE_ORIGIN
|
|
);
|
|
|
|
let actualState = await Services.domStorageManager.getState(principal);
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// We spin up a ton of child processes.
|
|
requestLongerTimeout(6);
|
|
|
|
/**
|
|
* Verify snapshotting of our localStorage implementation in multi-e10s setup.
|
|
*/
|
|
add_task(async function () {
|
|
if (!Services.domStorageManager.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],
|
|
// Force multiple web and webIsolated content processes so that the
|
|
// multi-e10s logic works correctly.
|
|
["dom.ipc.processCount", 8],
|
|
["dom.ipc.processCount.webIsolated", 4],
|
|
],
|
|
});
|
|
|
|
// 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 openTestTab(
|
|
HELPER_PAGE_URL,
|
|
"writer1",
|
|
knownTabs,
|
|
true
|
|
);
|
|
const writerTab2 = await openTestTab(
|
|
HELPER_PAGE_URL,
|
|
"writer2",
|
|
knownTabs,
|
|
true
|
|
);
|
|
const readerTab1 = await openTestTab(
|
|
HELPER_PAGE_URL,
|
|
"reader1",
|
|
knownTabs,
|
|
true
|
|
);
|
|
const readerTab2 = await openTestTab(
|
|
HELPER_PAGE_URL,
|
|
"reader2",
|
|
knownTabs,
|
|
true
|
|
);
|
|
|
|
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",
|
|
};
|
|
|
|
let sizeOfOneKey;
|
|
let sizeOfOneValue;
|
|
let sizeOfOneItem;
|
|
let sizeOfKeys = 0;
|
|
let sizeOfItems = 0;
|
|
|
|
let entries = Object.entries(initialState);
|
|
for (let i = 0; i < entries.length; i++) {
|
|
let entry = entries[i];
|
|
let sizeOfKey = entry[0].length;
|
|
let sizeOfValue = entry[1].length;
|
|
let sizeOfItem = sizeOfKey + sizeOfValue;
|
|
if (i == 0) {
|
|
sizeOfOneKey = sizeOfKey;
|
|
sizeOfOneValue = sizeOfValue;
|
|
sizeOfOneItem = sizeOfItem;
|
|
}
|
|
sizeOfKeys += sizeOfKey;
|
|
sizeOfItems += sizeOfItem;
|
|
}
|
|
|
|
info("Size of one key is " + sizeOfOneKey);
|
|
info("Size of one value is " + sizeOfOneValue);
|
|
info("Size of one item is " + sizeOfOneItem);
|
|
info("Size of keys is " + sizeOfKeys);
|
|
info("Size of items is " + sizeOfItems);
|
|
|
|
const prefillValues = [
|
|
// Zero prefill (prefill disabled)
|
|
0,
|
|
// Less than one key length prefill
|
|
sizeOfOneKey - 1,
|
|
// Greater than one key length and less than one item length prefill
|
|
sizeOfOneKey + 1,
|
|
// Precisely one item length prefill
|
|
sizeOfOneItem,
|
|
// Precisely two times one item length prefill
|
|
2 * sizeOfOneItem,
|
|
// Precisely size of keys prefill
|
|
sizeOfKeys,
|
|
// Less than size of keys plus one value length prefill
|
|
sizeOfKeys + sizeOfOneValue - 1,
|
|
// Precisely size of keys plus one value length prefill
|
|
sizeOfKeys + sizeOfOneValue,
|
|
// Greater than size of keys plus one value length and less than size of
|
|
// keys plus two times one value length prefill
|
|
sizeOfKeys + sizeOfOneValue + 1,
|
|
// Precisely size of keys plus two times one value length prefill
|
|
sizeOfKeys + 2 * sizeOfOneValue,
|
|
// Precisely size of keys plus three times one value length prefill
|
|
sizeOfKeys + 3 * sizeOfOneValue,
|
|
// Precisely size of keys plus four times one value length prefill
|
|
sizeOfKeys + 4 * sizeOfOneValue,
|
|
// Precisely size of keys plus five times one value length prefill
|
|
sizeOfKeys + 5 * sizeOfOneValue,
|
|
// Precisely size of keys plus six times one value length prefill
|
|
sizeOfKeys + 6 * sizeOfOneValue,
|
|
// Precisely size of items prefill
|
|
sizeOfItems,
|
|
// Unlimited prefill
|
|
-1,
|
|
];
|
|
|
|
for (let prefillValue of prefillValues) {
|
|
info("Setting prefill value to " + prefillValue);
|
|
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["dom.storage.snapshot_prefill", prefillValue]],
|
|
});
|
|
|
|
const gradualPrefillValues = [
|
|
// Zero gradual prefill
|
|
0,
|
|
// Less than one key length gradual prefill
|
|
sizeOfOneKey - 1,
|
|
// Greater than one key length and less than one item length gradual
|
|
// prefill
|
|
sizeOfOneKey + 1,
|
|
// Precisely one item length gradual prefill
|
|
sizeOfOneItem,
|
|
// Precisely two times one item length gradual prefill
|
|
2 * sizeOfOneItem,
|
|
// Precisely three times one item length gradual prefill
|
|
3 * sizeOfOneItem,
|
|
// Precisely four times one item length gradual prefill
|
|
4 * sizeOfOneItem,
|
|
// Precisely five times one item length gradual prefill
|
|
5 * sizeOfOneItem,
|
|
// Precisely six times one item length gradual prefill
|
|
6 * sizeOfOneItem,
|
|
// Precisely size of items prefill
|
|
sizeOfItems,
|
|
// Unlimited gradual prefill
|
|
-1,
|
|
];
|
|
|
|
for (let gradualPrefillValue of gradualPrefillValues) {
|
|
info("Setting gradual prefill value to " + gradualPrefillValue);
|
|
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["dom.storage.snapshot_gradual_prefill", gradualPrefillValue]],
|
|
});
|
|
|
|
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();
|
|
});
|
|
|
|
/**
|
|
* Verify that snapshots are able to work with negative usage. This can happen
|
|
* when there's an item stored in localStorage with given size and then two
|
|
* snaphots (created at the same time) mutate the item. The first one replases
|
|
* it with something bigger and the other one removes it.
|
|
*/
|
|
add_task(async function () {
|
|
if (!Services.domStorageManager.nextGenLocalStorageEnabled) {
|
|
ok(true, "Test ignored when the next gen local storage is not enabled.");
|
|
return;
|
|
}
|
|
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [
|
|
// Force multiple web and webIsolated content processes so that the
|
|
// multi-e10s logic works correctly.
|
|
["dom.ipc.processCount", 4],
|
|
["dom.ipc.processCount.webIsolated", 2],
|
|
// Disable snapshot peak usage pre-incrementation to make the testing
|
|
// easier.
|
|
["dom.storage.snapshot_peak_usage.initial_preincrement", 0],
|
|
["dom.storage.snapshot_peak_usage.reduced_initial_preincrement", 0],
|
|
["dom.storage.snapshot_peak_usage.gradual_preincrement", 0],
|
|
["dom.storage.snapshot_peak_usage.reuced_gradual_preincrement", 0],
|
|
// 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 clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
|
|
|
|
// Open tabs. Don't configure any of them yet.
|
|
const knownTabs = new KnownTabs();
|
|
const writerTab1 = await openTestTab(
|
|
HELPER_PAGE_URL,
|
|
"writer1",
|
|
knownTabs,
|
|
true
|
|
);
|
|
const writerTab2 = await openTestTab(
|
|
HELPER_PAGE_URL,
|
|
"writer2",
|
|
knownTabs,
|
|
true
|
|
);
|
|
|
|
// Apply the initial mutation using an explicit snapshot. The explicit
|
|
// snapshot here ensures that the parent process have received the changes.
|
|
await beginExplicitSnapshot(writerTab1);
|
|
await applyMutations(writerTab1, [["key", "something"]]);
|
|
await endExplicitSnapshot(writerTab1);
|
|
|
|
// Begin explicit snapshots in both tabs. Both tabs should see the initial
|
|
// state.
|
|
await beginExplicitSnapshot(writerTab1);
|
|
await beginExplicitSnapshot(writerTab2);
|
|
|
|
// Apply the first mutation in writerTab1 and end the explicit snapshot.
|
|
await applyMutations(writerTab1, [["key", "somethingBigger"]]);
|
|
await endExplicitSnapshot(writerTab1);
|
|
|
|
// Apply the second mutation in writerTab2 and end the explicit snapshot.
|
|
await applyMutations(writerTab2, [["key", null]]);
|
|
await endExplicitSnapshot(writerTab2);
|
|
|
|
// Verify the final state, it should match the state after the second
|
|
// mutation has been applied and "commited". An explicit snapshot is used.
|
|
await beginExplicitSnapshot(writerTab1);
|
|
await verifyState(writerTab1, {});
|
|
await endExplicitSnapshot(writerTab1);
|
|
|
|
// Clean up.
|
|
await cleanupTabs(knownTabs);
|
|
|
|
clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
|
|
});
|
|
|
|
/**
|
|
* Verify that snapshot usage is correctly updated after each operation.
|
|
*/
|
|
add_task(async function () {
|
|
if (!Services.domStorageManager.nextGenLocalStorageEnabled) {
|
|
ok(true, "Test ignored when the next gen local storage is not enabled.");
|
|
return;
|
|
}
|
|
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [
|
|
// Force multiple web and webIsolated content processes so that the
|
|
// multi-e10s logic works correctly.
|
|
["dom.ipc.processCount", 4],
|
|
["dom.ipc.processCount.webIsolated", 2],
|
|
// Disable snapshot peak usage pre-incrementation to make the testing
|
|
// easier.
|
|
["dom.storage.snapshot_peak_usage.initial_preincrement", 0],
|
|
["dom.storage.snapshot_peak_usage.reduced_initial_preincrement", 0],
|
|
["dom.storage.snapshot_peak_usage.gradual_preincrement", 0],
|
|
["dom.storage.snapshot_peak_usage.reuced_gradual_preincrement", 0],
|
|
// 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 clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
|
|
|
|
// Open tabs. Don't configure any of them yet.
|
|
const knownTabs = new KnownTabs();
|
|
const writerTab1 = await openTestTab(
|
|
HELPER_PAGE_URL,
|
|
"writer1",
|
|
knownTabs,
|
|
true
|
|
);
|
|
const writerTab2 = await openTestTab(
|
|
HELPER_PAGE_URL,
|
|
"writer2",
|
|
knownTabs,
|
|
true
|
|
);
|
|
|
|
// Apply the initial mutation using an explicit snapshot. The explicit
|
|
// snapshot here ensures that the parent process have received the changes.
|
|
await beginExplicitSnapshot(writerTab1);
|
|
await verifySnapshotUsage(writerTab1, 0);
|
|
await applyMutations(writerTab1, [["key", "something"]]);
|
|
await verifySnapshotUsage(writerTab1, 12);
|
|
await endExplicitSnapshot(writerTab1);
|
|
await verifyHasSnapshot(writerTab1, false);
|
|
|
|
// Begin an explicit snapshot in writerTab1 and apply the first mutatation
|
|
// in it.
|
|
await beginExplicitSnapshot(writerTab1);
|
|
await verifySnapshotUsage(writerTab1, 12);
|
|
await applyMutations(writerTab1, [["key", "somethingBigger"]]);
|
|
await verifySnapshotUsage(writerTab1, 18);
|
|
|
|
// Begin an explicit snapshot in writerTab2 and apply the second mutatation
|
|
// in it.
|
|
await beginExplicitSnapshot(writerTab2);
|
|
await verifySnapshotUsage(writerTab2, 18);
|
|
await applyMutations(writerTab2, [[null, null]]);
|
|
await verifySnapshotUsage(writerTab2, 6);
|
|
|
|
// End explicit snapshots in both tabs.
|
|
await endExplicitSnapshot(writerTab1);
|
|
await verifyHasSnapshot(writerTab1, false);
|
|
await endExplicitSnapshot(writerTab2);
|
|
await verifyHasSnapshot(writerTab2, false);
|
|
|
|
// Verify the final state, it should match the state after the second
|
|
// mutation has been applied and "commited". An explicit snapshot is used.
|
|
await beginExplicitSnapshot(writerTab1);
|
|
await verifySnapshotUsage(writerTab1, 0);
|
|
await verifyState(writerTab1, {});
|
|
await endExplicitSnapshot(writerTab1);
|
|
await verifyHasSnapshot(writerTab1, false);
|
|
|
|
// Clean up.
|
|
await cleanupTabs(knownTabs);
|
|
|
|
clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
|
|
});
|
|
|
|
/**
|
|
* Verify that datastore in the parent is correctly updated after a checkpoint.
|
|
*/
|
|
add_task(async function () {
|
|
if (!Services.domStorageManager.nextGenLocalStorageEnabled) {
|
|
ok(true, "Test ignored when the next gen local storage is not enabled.");
|
|
return;
|
|
}
|
|
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [
|
|
// Force multiple web and webIsolated content processes so that the
|
|
// multi-e10s logic works correctly.
|
|
["dom.ipc.processCount", 4],
|
|
["dom.ipc.processCount.webIsolated", 2],
|
|
// 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 clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
|
|
|
|
// Open tabs. Don't configure any of them yet.
|
|
const knownTabs = new KnownTabs();
|
|
const writerTab1 = await openTestTab(
|
|
HELPER_PAGE_URL,
|
|
"writer1",
|
|
knownTabs,
|
|
true
|
|
);
|
|
|
|
await verifyParentState({});
|
|
|
|
// Apply the initial mutation using an explicit snapshot. The explicit
|
|
// snapshot here ensures that the parent process have received the changes.
|
|
await beginExplicitSnapshot(writerTab1);
|
|
await verifyParentState({});
|
|
await applyMutations(writerTab1, [["key", "something"]]);
|
|
await verifyParentState({});
|
|
await endExplicitSnapshot(writerTab1);
|
|
|
|
await verifyParentState({ key: "something" });
|
|
|
|
// Begin an explicit snapshot in writerTab1, apply the first mutation in
|
|
// writerTab1 and checkpoint the explicit snapshot.
|
|
await beginExplicitSnapshot(writerTab1);
|
|
await verifyParentState({ key: "something" });
|
|
await applyMutations(writerTab1, [["key", "somethingBigger"]]);
|
|
await verifyParentState({ key: "something" });
|
|
await checkpointExplicitSnapshot(writerTab1);
|
|
|
|
await verifyParentState({ key: "somethingBigger" });
|
|
|
|
// Apply the second mutation in writerTab1 and checkpoint the explicit
|
|
// snapshot.
|
|
await applyMutations(writerTab1, [["key", null]]);
|
|
await verifyParentState({ key: "somethingBigger" });
|
|
await checkpointExplicitSnapshot(writerTab1);
|
|
|
|
await verifyParentState({});
|
|
|
|
// Apply the third mutation in writerTab1 and end the explicit snapshot.
|
|
await applyMutations(writerTab1, [["otherKey", "something"]]);
|
|
await verifyParentState({});
|
|
await endExplicitSnapshot(writerTab1);
|
|
|
|
await verifyParentState({ otherKey: "something" });
|
|
|
|
// Verify the final state, it should match the state after the third mutation
|
|
// has been applied and "commited". An explicit snapshot is used.
|
|
await beginExplicitSnapshot(writerTab1);
|
|
await verifyParentState({ otherKey: "something" });
|
|
await verifyState(writerTab1, { otherKey: "something" });
|
|
await endExplicitSnapshot(writerTab1);
|
|
|
|
await verifyParentState({ otherKey: "something" });
|
|
|
|
// Clean up.
|
|
await cleanupTabs(knownTabs);
|
|
|
|
clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
|
|
});
|