browser_storage_recovery.js (5430B)
1 "use strict"; 2 3 // This test registers a SW for a scope that will never control a document 4 // and therefore never trigger a "fetch" functional event that would 5 // automatically attempt to update the registration. The overlap of the 6 // PAGE_URI and SCOPE is incidental. checkForUpdate is the only thing that 7 // will trigger an update of the registration and so there is no need to 8 // worry about Schedule Job races to coalesce an update job. 9 10 const BASE_URI = "http://mochi.test:8888/browser/dom/serviceworkers/test/"; 11 const PAGE_URI = BASE_URI + "empty.html"; 12 const SCOPE = PAGE_URI + "?storage_recovery"; 13 const SW_SCRIPT = BASE_URI + "storage_recovery_worker.sjs"; 14 15 async function checkForUpdate(browser) { 16 return SpecialPowers.spawn(browser, [SCOPE], async function (uri) { 17 let reg = await content.navigator.serviceWorker.getRegistration(uri); 18 await reg.update(); 19 return !!reg.installing; 20 }); 21 } 22 23 // Delete all of our chrome-namespace Caches for this origin, leaving any 24 // content-owned caches in place. This is exclusively for simulating loss 25 // of the origin's storage without loss of the registration and without 26 // having to worry that future enhancements to QuotaClients/ServiceWorkerRegistrar 27 // will break this test. If you want to wipe storage for an origin, use 28 // QuotaManager APIs 29 async function wipeStorage(u) { 30 let uri = Services.io.newURI(u); 31 let principal = Services.scriptSecurityManager.createContentPrincipal( 32 uri, 33 {} 34 ); 35 let caches = new CacheStorage("chrome", principal); 36 let list = await caches.keys(); 37 return Promise.all(list.map(c => caches.delete(c))); 38 } 39 40 add_setup(async function () { 41 await SpecialPowers.pushPrefEnv({ 42 set: [ 43 ["dom.serviceWorkers.enabled", true], 44 ["dom.serviceWorkers.testing.enabled", true], 45 ["dom.serviceWorkers.idle_timeout", 0], 46 ], 47 }); 48 49 // Configure the server script to not redirect. 50 await fetch(SW_SCRIPT + "?clear-redirect"); 51 52 let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI); 53 let browser = gBrowser.getBrowserForTab(tab); 54 await BrowserTestUtils.browserLoaded(browser); 55 56 await SpecialPowers.spawn( 57 browser, 58 [{ script: SW_SCRIPT, scope: SCOPE }], 59 async function (opts) { 60 let reg = await content.navigator.serviceWorker.register(opts.script, { 61 scope: opts.scope, 62 }); 63 let worker = reg.installing || reg.waiting || reg.active; 64 await new Promise(resolve => { 65 if (worker.state === "activated") { 66 resolve(); 67 return; 68 } 69 worker.addEventListener("statechange", function onStateChange() { 70 if (worker.state === "activated") { 71 worker.removeEventListener("statechange", onStateChange); 72 resolve(); 73 } 74 }); 75 }); 76 } 77 ); 78 79 BrowserTestUtils.removeTab(tab); 80 }); 81 82 // Verify that our service worker doesn't update normally. 83 add_task(async function normal_update_check() { 84 let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI); 85 let browser = gBrowser.getBrowserForTab(tab); 86 await BrowserTestUtils.browserLoaded(browser); 87 88 let updated = await checkForUpdate(browser); 89 ok(!updated, "normal update check should not trigger an update"); 90 91 BrowserTestUtils.removeTab(tab); 92 }); 93 94 // Test what happens when we wipe the service worker scripts 95 // out from under the site before triggering the update. This 96 // should cause an update to occur. 97 add_task(async function wiped_update_check() { 98 // Wipe the backing cache storage, but leave the SW registered. 99 await wipeStorage(PAGE_URI); 100 101 let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI); 102 let browser = gBrowser.getBrowserForTab(tab); 103 await BrowserTestUtils.browserLoaded(browser); 104 105 let updated = await checkForUpdate(browser); 106 ok(updated, "wiping the service worker scripts should trigger an update"); 107 108 BrowserTestUtils.removeTab(tab); 109 }); 110 111 // Test what happens when we wipe the service worker scripts 112 // out from under the site before triggering the update. This 113 // should cause an update to occur. 114 add_task(async function wiped_and_failed_update_check() { 115 // Wipe the backing cache storage, but leave the SW registered. 116 await wipeStorage(PAGE_URI); 117 118 // Configure the service worker script to redirect. This will 119 // prevent the update from completing successfully. 120 await fetch(SW_SCRIPT + "?set-redirect"); 121 122 let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI); 123 let browser = gBrowser.getBrowserForTab(tab); 124 await BrowserTestUtils.browserLoaded(browser); 125 126 // Attempt to update the service worker. This should throw 127 // an error because the script is now redirecting. 128 let updateFailed = false; 129 try { 130 await checkForUpdate(browser); 131 } catch (e) { 132 updateFailed = true; 133 } 134 ok(updateFailed, "redirecting service worker script should fail to update"); 135 136 // Also, since the existing service worker's scripts are broken 137 // we should also remove the registration completely when the 138 // update fails. 139 let exists = await SpecialPowers.spawn( 140 browser, 141 [SCOPE], 142 async function (uri) { 143 let reg = await content.navigator.serviceWorker.getRegistration(uri); 144 return !!reg; 145 } 146 ); 147 ok( 148 !exists, 149 "registration should be removed after scripts are wiped and update fails" 150 ); 151 152 // Note, we don't have to clean up the service worker registration 153 // since its effectively been force-removed here. 154 155 BrowserTestUtils.removeTab(tab); 156 });