diff --git a/clear-site-data/storage.https.html b/clear-site-data/storage.https.html index 35c9fd1a03bc2c..8d04f206532496 100644 --- a/clear-site-data/storage.https.html +++ b/clear-site-data/storage.https.html @@ -13,6 +13,8 @@ return datatype.name == "storage"; })[0]; + var serviceWorkerTestPageIFrame; + // The tests are set up asynchronously. setup({"explicit_done": true}); @@ -22,7 +24,30 @@ test(function() {}, "Populate backends."); TestUtils.populateStorage() + .then(() => { + return navigator.serviceWorker.getRegistration("support/").then(function(reg) { + return TestUtils.waitForServiceWorkerActive(reg); + }); + }) + .then(() => { + const serviceWorkerResponseBody = TestUtils.getServiceWorkerControlledResponseBody(); + + // Create iFrame in the service worker's scope. This page will make a request + // for another page that is only served by the service worker + serviceWorkerTestPageIFrame = document.createElement("iframe"); + serviceWorkerTestPageIFrame.src = "support/page_using_service_worker.html"; + document.body.appendChild(serviceWorkerTestPageIFrame); + + promise_test(function() { + return serviceWorkerResponseBody.then(function(body) { + assert_equals(body, "FROM_SERVICE_WORKER", "Response should be from service worker"); + }); + }, "Baseline: Service worker responds to request"); + + return serviceWorkerResponseBody; + }) .then(function() { + const waitForControllerChange = TestUtils.waitForControllerChangeEvent(); // Navigate to a resource with a Clear-Site-Data header in // an iframe, then verify that all backends of the "storage" // datatype have been deleted. @@ -45,6 +70,33 @@ }, test_name); }); + promise_test(function() { + const serviceWorkerResponseBody = TestUtils.getServiceWorkerControlledResponseBody(); + + // Tell the iframe to make a request for the URL that the service worker + // responds to + serviceWorkerTestPageIFrame.contentWindow.postMessage(null, "*"); + + return serviceWorkerResponseBody.then(function(body) { + assert_equals(body, "FROM_NETWORK", "Response should not be 200"); + }); + }, "Service worker no longer responds to requests"); + + promise_test(function() { + const halfSecondTimeout = new Promise(function(resolve, reject) { + step_timeout(() => { + reject("controllerchange event was not fired"); + }, 500); + }); + + return Promise.race([ + waitForControllerChange, + halfSecondTimeout + ]).then(function(hasController) { + assert_false(hasController, "Client should not have a controller"); + }); + }, "controllerchange event fires and client no longer has controller"); + done(); }); }); diff --git a/clear-site-data/support/controlled-endpoint.py b/clear-site-data/support/controlled-endpoint.py new file mode 100644 index 00000000000000..5604be31672847 --- /dev/null +++ b/clear-site-data/support/controlled-endpoint.py @@ -0,0 +1,3 @@ +def main(request, response): + return ([("Content-Type", "text/html")], + "FROM_NETWORK") \ No newline at end of file diff --git a/clear-site-data/support/page_using_service_worker.html b/clear-site-data/support/page_using_service_worker.html new file mode 100644 index 00000000000000..a20b05de351cfd --- /dev/null +++ b/clear-site-data/support/page_using_service_worker.html @@ -0,0 +1,38 @@ + + + + Clear-Site-Data + Service Workers Test Page + + + + + \ No newline at end of file diff --git a/clear-site-data/support/service_worker.js b/clear-site-data/support/service_worker.js index 8504a030ddfc4d..a4e5709ee17cb5 100644 --- a/clear-site-data/support/service_worker.js +++ b/clear-site-data/support/service_worker.js @@ -1 +1,6 @@ -/* This file is intentionally left blank. */ +self.addEventListener('fetch', (e) => { + const url = new URL(e.request.url); + if (url.pathname.match('controlled-endpoint.py')) { + e.respondWith(new Response('FROM_SERVICE_WORKER')); + } +}); \ No newline at end of file diff --git a/clear-site-data/support/test_utils.sub.js b/clear-site-data/support/test_utils.sub.js index 235273bd5ba4cb..25ce6a0b37c41c 100644 --- a/clear-site-data/support/test_utils.sub.js +++ b/clear-site-data/support/test_utils.sub.js @@ -121,7 +121,7 @@ var TestUtils = (function() { "add": function() { return navigator.serviceWorker.register( "support/service_worker.js", - { scope: "support/scope-that-does-not-contain-this-test/"}); + { scope: "support/"}); }, "isEmpty": function() { return new Promise(function(resolve, reject) { @@ -245,6 +245,60 @@ var TestUtils = (function() { */ TestUtils.populateStorage = populate.bind(this, TestUtils.STORAGE); + /** + * Wait for a postMessage from an iFrame, specifically a message that communicates + * the result of a request that is served by the service worker + * @private + */ + TestUtils.getServiceWorkerControlledResponseBody = function() { + return new Promise(function(resolve) { + const cb = (m) => { + if (m.data && m.data.subject === "service-worker-page-request") { + window.removeEventListener("message", cb); + resolve(m.data.body); + } + }; + window.addEventListener("message", cb); + }); + } + + /** + * Wait for a postMessage from an iFrame, specifically a message that communicates + * that a controllerchange event has happened. + * @private + */ + TestUtils.waitForControllerChangeEvent = function() { + return new Promise(function(resolve) { + const cb = (m) => { + if (m.data && m.data.subject === "service-worker-page-controllerchange") { + window.removeEventListener("message", cb); + resolve(m.data.hasController); + } + }; + window.addEventListener("message", cb); + }); + } + + /** + * Returns a promise that resolves when the registration has an active worker + * @param reg The service worker registration object + * @return a promise that resolves when the worker becomes active + * @private + */ + TestUtils.waitForServiceWorkerActive = function(reg) { + return new Promise((resolve) => { + (function pollForControllingWorker() { + if (reg.active && reg.active.state === 'activated') { + resolve(); + } else { + step_timeout(() => { + pollForControllingWorker(); + }, 100); + } + })(); + }); + } + /** * Get the support server URL that returns a Clear-Site-Data header * to clear |datatypes|.