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