Skip to content

Commit

Permalink
[WPT] BFCache: service worker clients
Browse files Browse the repository at this point in the history
This CL adds tests for BFCache +
Clients.claim(), Clients.matchAll() and unregister().

Bug: 1107415, 1204228, w3c/ServiceWorker#1594
Change-Id: I73233cf917e31dd91b974823d5490d0190f0eade
  • Loading branch information
hiroshige-g authored and chromium-wpt-export-bot committed Oct 14, 2021
1 parent 0a8c303 commit 40c13a9
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,16 @@ function runEventTest(params, description) {
params.expectedEvents);
}, description);
}

// Call clients.claim() on the service worker
async function claim(t, worker) {
const channel = new MessageChannel();
const saw_message = new Promise(function(resolve) {
channel.port1.onmessage = t.step_func(function(e) {
assert_equals(e.data, 'PASS', 'Worker call to claim() should fulfill.');
resolve();
});
});
worker.postMessage({port: channel.port2}, [channel.port2]);
await saw_message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
self.addEventListener('message', function(event) {
self.clients.claim()
.then(function(result) {
if (result !== undefined) {
event.data.port.postMessage(
'FAIL: claim() should be resolved with undefined');
return;
}
event.data.port.postMessage('PASS');
})
.catch(function(error) {
event.data.port.postMessage('FAIL: exception: ' + error.name);
});
});

self.addEventListener('fetch', e => {
if (e.request.url.match(/\/is-controlled/)) {
e.respondWith(new Response('controlled'));
}
else if (e.request.url.match(/\/get-clients-matchall/)) {
const options = { includeUncontrolled: true, type: 'all' };
e.respondWith(
self.clients.matchAll(options)
.then(clients => {
const client_urls = [];
clients.forEach(client => client_urls.push(client.url));
return new Response(JSON.stringify(client_urls));
})
);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="resources/helper.sub.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
<script>

promise_test(async t => {
const pageA = new RemoteContext(token());
const pageB = new RemoteContext(token());

const urlA = location.origin + executorPath + pageA.context_id + '&events=pagehide,pageshow,load';
const urlB = originCrossSite + executorPath + pageB.context_id;

window.open(urlA, '_blank', 'noopener');

await pageA.execute_script(waitForPageShow);

await pageA.execute_script(
(url) => {
navigator.serviceWorker.oncontrollerchange =
() => recordEvent('controllerchange');
});

// Register a service worker after `pageA` is loaded to make `pageA`
// uncontrolled at this time.
const workerUrl = 'resources/service-worker.js?pipe=header(Service-Worker-Allowed,../)';
const registration = await service_worker_unregister_and_register(t, workerUrl, './');
t.add_cleanup(_ => registration.unregister());
await wait_for_state(t, registration.installing, 'activated');

await pageA.execute_script(
(url) => {
prepareNavigation(() => { location.href = url; });
},
[urlB]);

await pageB.execute_script(waitForPageShow);

// Call clients.claim() when `pageA` is in BFCache.
await claim(t, registration.active);

// `pageA` doesn't appear in matchAll() because it is in BFCache.
const clients1 = await (await fetch('/get-clients-matchall')).json();
assert_true(clients1.indexOf(urlA) < 0, '1: matchAll() before back navigation');

await pageB.execute_script(
() => {
prepareNavigation(() => { history.back(); });
}
);
await pageA.execute_script(waitForPageShow);
await assert_bfcached(pageA);

// After back navigation, `pageA` appears in matchAll(), but not controlled.
const clients2 = await (await fetch('/get-clients-matchall')).json();
const controlled2 = await pageA.execute_script(
() => fetch('/is-controlled?claim-2').then(r => r.text()));
assert_true(clients2.indexOf(urlA) >= 0, '2: matchAll() just after back navigation');
assert_not_equals(controlled2, 'controlled',
'2: pageA should not be controlled just after back navigation');
assert_array_equals(
await pageA.execute_script(() => getRecordedEvents()),
[
'window.load',
'window.pageshow',
'window.pagehide.persisted',
'window.pageshow.persisted',
],
'2: controllerchange should not be fired just after back navigation');

// Call clients.claim() again when `pageA` is not in BFCache.
await claim(t, registration.active);

// After claim(), `pageA` is controlled.
const clients3 = await (await fetch('/get-clients-matchall')).json();
const controlled3 = await pageA.execute_script(
() => fetch('/is-controlled?claim-3').then(r => r.text()));
assert_true(clients3.indexOf(urlA) >= 0, '3: matchAll() after back navigation and claim()');
assert_equals(controlled3, 'controlled',
'3: pageA should be controlled after back navigation and claim()');
assert_array_equals(
await pageA.execute_script(() => getRecordedEvents()),
[
'window.load',
'window.pageshow',
'window.pagehide.persisted',
'window.pageshow.persisted',
'controllerchange',
],
'3: controllerchange should be fired');

}, 'Serviceworker Clients.claim()');
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="resources/helper.sub.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
<script>

promise_test(async t => {
const workerUrl = 'resources/service-worker.js?pipe=header(Service-Worker-Allowed,../)';
const registration = await service_worker_unregister_and_register(t, workerUrl, './');
t.add_cleanup(_ => registration.unregister());
await wait_for_state(t, registration.installing, 'activated');
claim(t, registration.active);

const pageA = new RemoteContext(token());
const pageB = new RemoteContext(token());

const urlA = location.origin + executorPath + pageA.context_id;
const urlB = originCrossSite + executorPath + pageB.context_id;

window.open(urlA, '_blank', 'noopener');

await pageA.execute_script(waitForPageShow);

const clients1 = await (await fetch('/get-clients-matchall')).json();
const controlled1 = await pageA.execute_script(
() => fetch('/is-controlled?matchall-1').then(r => r.text()));

await pageA.execute_script(
(url) => prepareNavigation(() => {
location.href = url;
}),
[urlB]);

await pageB.execute_script(waitForPageShow);

const clients2 = await (await fetch('/get-clients-matchall')).json();

await pageB.execute_script(
() => {
prepareNavigation(() => { history.back(); });
}
);

await pageA.execute_script(waitForPageShow);
await assert_bfcached(pageA);

const clients3 = await (await fetch('/get-clients-matchall')).json();
const controlled3 = await pageA.execute_script(
() => fetch('/is-controlled?matchall-3').then(r => r.text()));

assert_true(clients1.indexOf(urlA) >= 0, '1: matchAll() before navigation');
assert_true(clients2.indexOf(urlA) < 0, '2: matchAll() before back navigation');
assert_true(clients3.indexOf(urlA) >= 0, '3: matchAll() after back navigation');

assert_equals(controlled1, 'controlled',
'pageA should be controlled before BFCached');
assert_equals(controlled3, 'controlled',
'pageA should be controlled after restored');
}, 'Serviceworker Clients.matchAll()');
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="resources/helper.sub.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
<script>

promise_test(async t => {
const workerUrl = 'resources/service-worker.js?pipe=header(Service-Worker-Allowed,../)';
const registration = await service_worker_unregister_and_register(t, workerUrl, './');
t.add_cleanup(_ => registration.unregister());
await wait_for_state(t, registration.installing, 'activated');
await claim(t, registration.active);

const pageA = new RemoteContext(token());
const pageB = new RemoteContext(token());

const urlA = location.origin + executorPath + pageA.context_id;
const urlB = originCrossSite + executorPath + pageB.context_id;

window.open(urlA, '_blank', 'noopener');

await pageA.execute_script(waitForPageShow);
await pageA.execute_script(
(url) => prepareNavigation(() => {
location.href = url;
}),
[urlB]);

await pageB.execute_script(waitForPageShow);

await registration.unregister();

await pageB.execute_script(
() => {
prepareNavigation(() => { history.back(); });
}
);

await pageA.execute_script(waitForPageShow);
await assert_bfcached(pageA);

assert_equals(
await pageA.execute_script(
() => fetch('/is-controlled').then(r => r.text())),
'controlled',
'pageA should be still controlled');

}, 'Unregister service worker while a controlled page is in BFCache should not crash');
</script>

0 comments on commit 40c13a9

Please sign in to comment.