-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
We want to dispatch a close event when an entangled MessagePort is disconnected. Given a pair of entangled ports, port1 and port2, if port2 is closed at any point, a port1’s error handler is run. So we can change an error handler to dispatch a close event. The tests of close event are as follows: 1) port was explicitly closed. 2) owing document was destroyed. 3) owing document crashed. 4) port was garbage collected. Bug: 1495616 Change-Id: I99f9f5a0d7cc63f0916da316ec666ba793215019
- Loading branch information
1 parent
07f8002
commit d4b2548
Showing
7 changed files
with
240 additions
and
8 deletions.
There are no files selected for viewing
69 changes: 69 additions & 0 deletions
69
...sing-the-web/back-forward-cache/message-port-entangled-after-restore.https.tentative.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<!doctype html> | ||
<meta name="timeout" content="long"> | ||
<title>Confirm close event is not fired when the page enters BFCache and MessagePort still works after the page is restored</title> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<script src="/common/dispatcher/dispatcher.js"></script> | ||
<script src="/common/get-host-info.sub.js"></script> | ||
<script src="/common/utils.js"></script> | ||
<script src="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script> | ||
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script> | ||
<script src="resources/rc-helper.js"></script> | ||
|
||
<script> | ||
promise_test(async t => { | ||
// Register a service worker. | ||
const scope = | ||
'/html/browsers/browsing-the-web/remote-context-helper/resources' | ||
const workerUrl = | ||
`resources/service-worker.js?pipe=header(Service-Worker-Allowed,${ | ||
scope})`; | ||
const registration = | ||
await service_worker_unregister_and_register(t, workerUrl, scope); | ||
t.add_cleanup(_ => registration.unregister()); | ||
await wait_for_state(t, registration.installing, 'activated'); | ||
|
||
// Open a window with noopener so that BFCache will work. | ||
const rcHelper = new RemoteContextHelper(); | ||
const rc1 = await rcHelper.addWindow( | ||
/*extraConfig=*/ null, /*options=*/ {features: 'noopener'}); | ||
|
||
// Confirm the page is controlled. | ||
assert_true( | ||
await rc1.executeScript( | ||
() => (navigator.serviceWorker.controller !== null)), | ||
'The page should be controlled before navigation'); | ||
|
||
// Send MessagePort to the service worker. | ||
await rc1.executeScript(() => { | ||
const {port1, port2} = new MessageChannel(); | ||
port1.start(); | ||
const ctrl = navigator.serviceWorker.controller; | ||
ctrl.postMessage({type: 'storeMessagePort'}, [port2]); | ||
self.waitForMessage = (sentMessage) => { | ||
return new Promise(resolve => { | ||
port1.addEventListener('message', (event) => { | ||
resolve(event.data); | ||
}); | ||
port1.postMessage(sentMessage); | ||
}); | ||
}; | ||
}); | ||
|
||
// Verify that the page was BFCached. | ||
await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ true); | ||
|
||
// Confirm MessagePort can still work after the page is restored from | ||
// BFCache. | ||
assert_equals( | ||
await rc1.executeScript( | ||
async () => | ||
await self.waitForMessage('Confirm the ports can communicate')), | ||
'Receive message'); | ||
|
||
// Confirm the close event was not fired. | ||
assert_false(await rc1.executeScript( | ||
async () => | ||
await self.waitForMessage('Ask if the close event was fired'))); | ||
}, 'MessagePort still works after the page is restored from BFCache'); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 0 additions & 8 deletions
8
webmessaging/message-channels/close-event/close-event.tentative.any.js
This file was deleted.
Oops, something went wrong.
75 changes: 75 additions & 0 deletions
75
webmessaging/message-channels/close-event/document-destroyed.tentative.window.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// META: title=Close event test when the document is destroyed. | ||
// META: script=/common/dispatcher/dispatcher.js | ||
// META: script=/common/get-host-info.sub.js | ||
// META: script=/common/utils.js | ||
// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js | ||
// META: script=resources/helper.js | ||
|
||
async function addWindow() { | ||
const helper = new RemoteContextHelper(); | ||
return await helper.addWindow(); | ||
} | ||
|
||
/** | ||
* Create a new MessageChannel and transfers one of the ports to | ||
* the window which opened the window with a remote context provided | ||
* as an argument. | ||
* | ||
* @param {RemoteContextWrapper} remoteContextWrapper | ||
*/ | ||
async function createMessageChannelAndSendPort(remoteContextWrapper) { | ||
await remoteContextWrapper.executeScript(() => { | ||
const {port1, port2} = new MessageChannel(); | ||
port1.start(); | ||
window.opener.postMessage({}, '*', [port2]); | ||
}); | ||
} | ||
|
||
/** | ||
* Creates a new promise that resolves when the close event is fired. | ||
* | ||
* @param {MessagePort} port - MessagePort on which the close event will | ||
* be fired. | ||
* @returns {Promise} A promise you should await to ensure the close event | ||
* is dispatched. | ||
*/ | ||
function createCloseEventPromise(port) { | ||
return new Promise(resolve => port.onclose = resolve); | ||
} | ||
|
||
promise_test(async t => { | ||
const rc = await addWindow(); | ||
const waitForPort = expectMessagePortFromWindow(window); | ||
await createMessageChannelAndSendPort(rc); | ||
const closeEventPromise = createCloseEventPromise(await waitForPort); | ||
rc.navigateToNew(); | ||
await closeEventPromise; | ||
}, 'The context is navigated to a new document and a close event is fired.') | ||
|
||
promise_test(async t => { | ||
const rc = await addWindow(); | ||
const waitForPort = expectMessagePortFromWindow(window); | ||
await createMessageChannelAndSendPort(rc); | ||
const closeEventPromise = createCloseEventPromise(await waitForPort); | ||
rc.executeScript(() => window.close()); | ||
await closeEventPromise; | ||
}, 'The window is closed and a close event is fired.') | ||
|
||
promise_test(async t => { | ||
let iframe; | ||
const waitForLoad = new Promise(resolve => { | ||
iframe = document.createElement('iframe'); | ||
iframe.onload = resolve; | ||
document.documentElement.appendChild(iframe); | ||
}); | ||
await waitForLoad; | ||
|
||
const waitForPort = expectMessagePortFromWindow(iframe.contentWindow); | ||
const {port1, port2} = new MessageChannel(); | ||
port1.start(); | ||
iframe.contentWindow.postMessage('', '*', [port2]); | ||
await waitForPort; | ||
const closeEventPromise = createCloseEventPromise(port1); | ||
iframe.remove(); | ||
await closeEventPromise; | ||
}, 'The iframe is deleted and a close event is fired.') |
40 changes: 40 additions & 0 deletions
40
webmessaging/message-channels/close-event/explicitly-closed.tentative.window.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// META: title=Close event test when an entangled port is explicitly closed. | ||
// META: script=/common/dispatcher/dispatcher.js | ||
// META: script=/common/get-host-info.sub.js | ||
// META: script=/common/utils.js | ||
// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js | ||
// META: script=resources/helper.js | ||
|
||
async_test(t => { | ||
const channel = new MessageChannel(); | ||
channel.port1.start(); | ||
channel.port2.start(); | ||
channel.port2.onclose = t.step_func_done(); | ||
channel.port1.close(); | ||
}, 'Close event on port2 is fired when port1 is explicitly closed'); | ||
|
||
async_test(t => { | ||
const channel = new MessageChannel(); | ||
channel.port1.start(); | ||
channel.port2.start(); | ||
channel.port1.onclose = | ||
t.unreached_func('Should not fire a close event on port1'); | ||
channel.port1.close(); | ||
setTimeout(t.step_func_done(), 100); | ||
}, 'Close event on port1 is not fired when port1 is explicitly closed'); | ||
|
||
promise_test(async t => { | ||
const waitForPort = expectMessagePortFromWindow(window); | ||
const helper = new RemoteContextHelper(); | ||
const rc1 = await helper.addWindow(); | ||
|
||
await rc1.executeScript(() => { | ||
const {port1, port2} = new MessageChannel(); | ||
window.port = port1; | ||
window.opener.postMessage({}, '*', [port2]); | ||
}); | ||
const port = await waitForPort; | ||
const closeEventPromise = new Promise(resolve => port.onclose = resolve); | ||
rc1.executeScript(() => window.port.close()); | ||
await closeEventPromise; | ||
}, 'Close event on port1 is fired when port2, which is in a different window, is explicitly closed.') |
24 changes: 24 additions & 0 deletions
24
webmessaging/message-channels/close-event/garbage-collected.tentative.any.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// META: title=Close event test when an entangled port is GCed. | ||
// META: script=/common/gc.js | ||
// META: script=/wpt_internal/dom/abort/resources/run-async-gc.js | ||
|
||
/** | ||
* Create a new MessageChannel and return port1 and a weak reference to port2. | ||
* It is expected that port2 will be garbage collected and a close event | ||
* will be fired on port1. | ||
* | ||
* @returns {Array.<[MessagePort, WeakRef<MessagePort>]>} | ||
*/ | ||
function createMessageChannelAndWeakReferToPort() { | ||
const {port1, port2} = new MessageChannel(); | ||
port1.start(); | ||
return [port1, new WeakRef(port2)]; | ||
} | ||
|
||
promise_test(async t => { | ||
const [port1, weakport2] = createMessageChannelAndWeakReferToPort(); | ||
const closeEventPromise = new Promise(resolve => port1.onclose = resolve); | ||
runAsyncGC(); | ||
await closeEventPromise; | ||
assert_equals(weakport2.deref(), undefined, 'port2 should be GCed'); | ||
}, 'Entangled port is garbage collected, and the close event is fired.') |
18 changes: 18 additions & 0 deletions
18
webmessaging/message-channels/close-event/resources/helper.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* Create a new promise that resolves when the window receives | ||
* the MessagePort and starts it. | ||
* | ||
* @param {Window} window - The window to wait for the MessagePort. | ||
* @returns {Promise<MessagePort>} A promise you should await to ensure the | ||
* window | ||
* receives the MessagePort. | ||
*/ | ||
function expectMessagePortFromWindow(window) { | ||
return new Promise(resolve => { | ||
window.onmessage = e => { | ||
assert_true(e.ports[0] instanceof window.MessagePort); | ||
e.ports[0].start(); | ||
resolve(e.ports[0]); | ||
}; | ||
}); | ||
} |