Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test has a WPT meta file that expects 1 subtest issues.
- This WPT test may be referenced by the following Test IDs:
- /speculation-rules/prerender/protocol-handler-unregister.https.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="../resources/utils.js"></script>
<script src="resources/utils.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
<script>
function getNextWindowMessageFromFrame(frame) {
return new Promise(resolve => {
window.addEventListener('message', event => {
if (event.source === frame.contentWindow) {
resolve(event.data);
}
});
});
}
function getNextMessageFromServiceWorker(serviceWorker) {
return new Promise(resolve => {
serviceWorker.addEventListener('message', event => {
resolve(event.data);
}, {once: true});
});
}
promise_setup(async () => {
assertSpeculationRulesIsSupported()
await test_driver.set_rph_registration_mode('autoAccept');
await test_driver.bless('handler registration');
});
// The overall idea for this test is:
// 1. Register a protocol handler for a custom URI scheme.
// 2. Create a prerendered page that unregisters the protocol handler.
// 3. Navigate an iframe to the custom URI scheme. It should navigate
// successfully.
// 4. Activate the prerendered page. This should run the deferred unregistration
// work.
// 5. Navigate the iframe to the custom URI scheme again. It should fail to
// navigate.
// To detect the navigation failure, we use a service worker to perform the
// navigation via client.navigate and report the result back to
// the test. This is because the service worker's client.navigate method
// actually reports if the navigation failed, unlike other mechanisms which
// tell us nothing in this case.
promise_test(async t => {
const customUrlScheme = 'web+wptrphtest';
function getProtocolHandlerUrlTemplate(id) {
return new URL(
`resources/protocol-handler.html?id=${id}&s=%s`, location.href).href;
}
const urlTemplate = getProtocolHandlerUrlTemplate('unregister');
t.add_cleanup(() => {
navigator.unregisterProtocolHandler(
customUrlScheme, urlTemplate);
});
navigator.registerProtocolHandler(customUrlScheme, urlTemplate);
const {exec, activate} = await create_prerendered_page(t);
const result = await exec(
(customUrlScheme, urlTemplate) => {
try {
navigator.unregisterProtocolHandler(
customUrlScheme, urlTemplate);
} catch (registerProtocolHandlerException) {
return 'registerProtocolHandler failed with \'' +
registerProtocolHandlerException.name + '\'';
}
return 'success';
}, [customUrlScheme, urlTemplate]);
assert_equals(result, 'success', 'unregisterProtocolHandler should succeed.');
const frame1 = await with_iframe('about:blank');
const frame1MessagePromise = getNextWindowMessageFromFrame(frame1);
frame1.src = `${customUrlScheme}:1`;
assert_equals((await frame1MessagePromise).id, 'unregister',
'Until activation, the initial handler should be registered.');
frame1.remove();
// Activate the prerendered page.
await activate();
// At this point the deferred unregistration work has run during activation
// and the protocol handler is no longer registered.
// We use Service Worker client.navigate to detect the failed navigation since
// it is well supported and reliably reports error for unknown URL schemes.
const serviceWorkerScope =
'resources/protocol-handler.html?service_worker_client';
const frame2Url = serviceWorkerScope + '&id=communication';
const frame3Url = serviceWorkerScope + '&id=nav_target';
const serviceWorkerUrl = 'resources/protocol-handler-service-worker.js';
const serviceWorkerRegistration =
await service_worker_unregister_and_register(
t, serviceWorkerUrl, serviceWorkerScope);
t.add_cleanup(async () => {
await serviceWorkerRegistration.unregister();
});
await wait_for_state(t, serviceWorkerRegistration.installing, 'activated');
// We use frame2 to communicate with the service worker.
const frame2 = await with_iframe(frame2Url);
// Frame3 is used by the service worker to attempt navigation.
const frame3 = await with_iframe(frame3Url);
const serviceWorkerMessagePromise = getNextMessageFromServiceWorker(
frame2.contentWindow.navigator.serviceWorker);
// Post message via frame2 to the service worker to tell it to navigate frame3
// to the custom URI.
frame2.contentWindow.navigator.serviceWorker.controller.postMessage(
{clientUrlMatch: new URL(frame3Url, location.href).href,
navigationUrl: `${customUrlScheme}:3`});
// The service worker will post message back the result of the navigation.
const navigationResult = await serviceWorkerMessagePromise;
frame2.remove();
frame3.remove();
assert_false(navigationResult.success, 'Navigation to unregistered URI should fail');
assert_equals(navigationResult.message, 'navigate failure: TypeError',
'unregisterProtocolHandler should have completed.');
}, 'prerendering page unregisterProtocolHandler call defers registration until activation.');
</script>