summaryrefslogtreecommitdiffstats
path: root/dom/serviceworkers/test/browser_storage_recovery.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/serviceworkers/test/browser_storage_recovery.js')
-rw-r--r--dom/serviceworkers/test/browser_storage_recovery.js156
1 files changed, 156 insertions, 0 deletions
diff --git a/dom/serviceworkers/test/browser_storage_recovery.js b/dom/serviceworkers/test/browser_storage_recovery.js
new file mode 100644
index 0000000000..8b4a1181f7
--- /dev/null
+++ b/dom/serviceworkers/test/browser_storage_recovery.js
@@ -0,0 +1,156 @@
+"use strict";
+
+// This test registers a SW for a scope that will never control a document
+// and therefore never trigger a "fetch" functional event that would
+// automatically attempt to update the registration. The overlap of the
+// PAGE_URI and SCOPE is incidental. checkForUpdate is the only thing that
+// will trigger an update of the registration and so there is no need to
+// worry about Schedule Job races to coalesce an update job.
+
+const BASE_URI = "http://mochi.test:8888/browser/dom/serviceworkers/test/";
+const PAGE_URI = BASE_URI + "empty.html";
+const SCOPE = PAGE_URI + "?storage_recovery";
+const SW_SCRIPT = BASE_URI + "storage_recovery_worker.sjs";
+
+async function checkForUpdate(browser) {
+ return SpecialPowers.spawn(browser, [SCOPE], async function (uri) {
+ let reg = await content.navigator.serviceWorker.getRegistration(uri);
+ await reg.update();
+ return !!reg.installing;
+ });
+}
+
+// Delete all of our chrome-namespace Caches for this origin, leaving any
+// content-owned caches in place. This is exclusively for simulating loss
+// of the origin's storage without loss of the registration and without
+// having to worry that future enhancements to QuotaClients/ServiceWorkerRegistrar
+// will break this test. If you want to wipe storage for an origin, use
+// QuotaManager APIs
+async function wipeStorage(u) {
+ let uri = Services.io.newURI(u);
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ let caches = new CacheStorage("chrome", principal);
+ let list = await caches.keys();
+ return Promise.all(list.map(c => caches.delete(c)));
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.serviceWorkers.idle_timeout", 0],
+ ],
+ });
+
+ // Configure the server script to not redirect.
+ await fetch(SW_SCRIPT + "?clear-redirect");
+
+ let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ await SpecialPowers.spawn(
+ browser,
+ [{ script: SW_SCRIPT, scope: SCOPE }],
+ async function (opts) {
+ let reg = await content.navigator.serviceWorker.register(opts.script, {
+ scope: opts.scope,
+ });
+ let worker = reg.installing || reg.waiting || reg.active;
+ await new Promise(resolve => {
+ if (worker.state === "activated") {
+ resolve();
+ return;
+ }
+ worker.addEventListener("statechange", function onStateChange() {
+ if (worker.state === "activated") {
+ worker.removeEventListener("statechange", onStateChange);
+ resolve();
+ }
+ });
+ });
+ }
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+// Verify that our service worker doesn't update normally.
+add_task(async function normal_update_check() {
+ let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let updated = await checkForUpdate(browser);
+ ok(!updated, "normal update check should not trigger an update");
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+// Test what happens when we wipe the service worker scripts
+// out from under the site before triggering the update. This
+// should cause an update to occur.
+add_task(async function wiped_update_check() {
+ // Wipe the backing cache storage, but leave the SW registered.
+ await wipeStorage(PAGE_URI);
+
+ let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let updated = await checkForUpdate(browser);
+ ok(updated, "wiping the service worker scripts should trigger an update");
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+// Test what happens when we wipe the service worker scripts
+// out from under the site before triggering the update. This
+// should cause an update to occur.
+add_task(async function wiped_and_failed_update_check() {
+ // Wipe the backing cache storage, but leave the SW registered.
+ await wipeStorage(PAGE_URI);
+
+ // Configure the service worker script to redirect. This will
+ // prevent the update from completing successfully.
+ await fetch(SW_SCRIPT + "?set-redirect");
+
+ let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Attempt to update the service worker. This should throw
+ // an error because the script is now redirecting.
+ let updateFailed = false;
+ try {
+ await checkForUpdate(browser);
+ } catch (e) {
+ updateFailed = true;
+ }
+ ok(updateFailed, "redirecting service worker script should fail to update");
+
+ // Also, since the existing service worker's scripts are broken
+ // we should also remove the registration completely when the
+ // update fails.
+ let exists = await SpecialPowers.spawn(
+ browser,
+ [SCOPE],
+ async function (uri) {
+ let reg = await content.navigator.serviceWorker.getRegistration(uri);
+ return !!reg;
+ }
+ );
+ ok(
+ !exists,
+ "registration should be removed after scripts are wiped and update fails"
+ );
+
+ // Note, we don't have to clean up the service worker registration
+ // since its effectively been force-removed here.
+
+ BrowserTestUtils.removeTab(tab);
+});