Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

"use strict";
const { ExtensionStorageIDB } = ChromeUtils.importESModule(
"resource://gre/modules/ExtensionStorageIDB.sys.mjs"
);
async function test_multiple_pages() {
let extension = ExtensionTestUtils.loadExtension({
async background() {
function awaitMessage(expectedMsg, api = "test") {
return new Promise(resolve => {
browser[api].onMessage.addListener(function listener(msg) {
if (msg === expectedMsg) {
browser[api].onMessage.removeListener(listener);
resolve();
}
});
});
}
let tabReady = awaitMessage("tab-ready", "runtime");
try {
let storage = browser.storage.local;
browser.test.sendMessage(
"load-page",
browser.runtime.getURL("tab.html")
);
await awaitMessage("page-loaded");
await tabReady;
let result = await storage.get("key");
browser.test.assertEq(undefined, result.key, "Key should be undefined");
await browser.runtime.sendMessage("tab-set-key");
result = await storage.get("key");
browser.test.assertEq(
JSON.stringify({ foo: { bar: "baz" } }),
JSON.stringify(result.key),
"Key should be set to the value from the tab"
);
browser.test.sendMessage("remove-page");
await awaitMessage("page-removed");
result = await storage.get("key");
browser.test.assertEq(
JSON.stringify({ foo: { bar: "baz" } }),
JSON.stringify(result.key),
"Key should still be set to the value from the tab"
);
browser.test.notifyPass("storage-multiple");
} catch (e) {
browser.test.fail(`Error: ${e} :: ${e.stack}`);
browser.test.notifyFail("storage-multiple");
}
},
files: {
"tab.html": `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="tab.js"></script>
</head>
</html>`,
"tab.js"() {
browser.test.log("tab");
browser.runtime.onMessage.addListener(msg => {
if (msg == "tab-set-key") {
return browser.storage.local.set({ key: { foo: { bar: "baz" } } });
}
});
browser.runtime.sendMessage("tab-ready");
},
},
manifest: {
permissions: ["storage"],
},
});
let contentPage;
extension.onMessage("load-page", async url => {
contentPage = await ExtensionTestUtils.loadContentPage(url, { extension });
extension.sendMessage("page-loaded");
});
extension.onMessage("remove-page", async () => {
await contentPage.close();
extension.sendMessage("page-removed");
});
await extension.startup();
await extension.awaitFinish("storage-multiple");
await extension.unload();
}
add_task(async function test_storage_local_file_backend_from_tab() {
return runWithPrefs(
[[ExtensionStorageIDB.BACKEND_ENABLED_PREF, false]],
test_multiple_pages
);
});
add_task(async function test_storage_local_idb_backend_from_tab() {
return runWithPrefs(
[[ExtensionStorageIDB.BACKEND_ENABLED_PREF, true]],
test_multiple_pages
);
});
async function test_storage_local_call_from_destroying_context() {
let extension = ExtensionTestUtils.loadExtension({
async background() {
let numberOfChanges = 0;
browser.storage.onChanged.addListener((changes, areaName) => {
if (areaName !== "local") {
browser.test.fail(
`Received unexpected storage changes for "${areaName}"`
);
}
numberOfChanges++;
});
browser.test.onMessage.addListener(async ({ msg, values }) => {
switch (msg) {
case "storage-set": {
await browser.storage.local.set(values);
browser.test.sendMessage("storage-set:done");
break;
}
case "storage-get": {
const res = await browser.storage.local.get();
browser.test.sendMessage("storage-get:done", res);
break;
}
case "storage-changes": {
browser.test.sendMessage("storage-changes-count", numberOfChanges);
break;
}
default:
browser.test.fail(`Received unexpected message: ${msg}`);
}
});
browser.test.sendMessage(
"ext-page-url",
browser.runtime.getURL("tab.html")
);
},
files: {
"tab.html": `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="tab.js"></script>
</head>
</html>`,
"tab.js"() {
browser.test.log("Extension tab - calling storage.local API method");
// Call the storage.local API from a tab that is going to be quickly closed.
browser.storage.local.set({
"test-key-from-destroying-context": "testvalue2",
});
// Navigate away from the extension page, so that the storage.local API call will be unable
// to send the call to the caller context (because it has been destroyed in the meantime).
window.location = "about:blank";
},
},
manifest: {
permissions: ["storage"],
},
});
await extension.startup();
const url = await extension.awaitMessage("ext-page-url");
let contentPage = await ExtensionTestUtils.loadContentPage(url, {
extension,
});
let expectedBackgroundPageData = {
"test-key-from-background-page": "test-value",
};
let expectedTabData = { "test-key-from-destroying-context": "testvalue2" };
info(
"Call storage.local.set from the background page and wait it to be completed"
);
extension.sendMessage({
msg: "storage-set",
values: expectedBackgroundPageData,
});
await extension.awaitMessage("storage-set:done");
info(
"Call storage.local.get from the background page and wait it to be completed"
);
extension.sendMessage({ msg: "storage-get" });
let res = await extension.awaitMessage("storage-get:done");
Assert.deepEqual(
res,
{
...expectedBackgroundPageData,
...expectedTabData,
},
"Got the expected data set in the storage.local backend"
);
extension.sendMessage({ msg: "storage-changes" });
equal(
await extension.awaitMessage("storage-changes-count"),
2,
"Got the expected number of storage.onChanged event received"
);
contentPage.close();
await extension.unload();
}
add_task(
async function test_storage_local_file_backend_destroyed_context_promise() {
return runWithPrefs(
[[ExtensionStorageIDB.BACKEND_ENABLED_PREF, false]],
test_storage_local_call_from_destroying_context
);
}
);
add_task(
async function test_storage_local_idb_backend_destroyed_context_promise() {
return runWithPrefs(
[[ExtensionStorageIDB.BACKEND_ENABLED_PREF, true]],
test_storage_local_call_from_destroying_context
);
}
);