MessagePort: implement disentanglement (#36654)

Implement
[disentangle](https://html.spec.whatwg.org/multipage/#disentangle)
Remove bespoke gc logic which now becomes unnecessary. 
Adds a wpt test that hits the "disentangle while in transfer" logic.
Updates streams code, fixing an error where disentanglement is
conditional on an error.

Test coverage: there are existing tests in
`/webmessaging/message-channels/close-event/explicitly-closed.tentative.window.js`
for the no transfer case, and the simple completed transfer case, and
this PR adds a test for the more complicated transfer in progress case.

Fix https://github.com/servo/servo/issues/36465

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
This commit is contained in:
Gregory Terzian 2025-04-30 12:49:38 +02:00 committed by GitHub
parent c46402e222
commit af5d665efa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 356 additions and 191 deletions

View file

@ -518802,7 +518802,7 @@
"close-event": {
"resources": {
"helper.js": [
"cb9ea9fe981e95374b836255c752a42de788fc7b",
"48744ac1c5b530ef8d46c3d9a0378c698353a5bc",
[]
]
}
@ -848537,7 +848537,7 @@
]
],
"explicitly-closed.tentative.window.js": [
"612003d58eaea908ad93294a7bbf777184356a28",
"12bfa0bd73e9278e39b825d4fa81437f943cbd02",
[
"webmessaging/message-channels/close-event/explicitly-closed.tentative.window.html",
{

View file

@ -95,9 +95,6 @@
[History interface: existence and properties of interface object]
expected: FAIL
[MessagePort interface: attribute onclose]
expected: FAIL
[WorkerGlobalScope interface: attribute onlanguagechange]
expected: FAIL

View file

@ -1517,9 +1517,6 @@
[SVGSVGElement interface: attribute onpagereveal]
expected: FAIL
[MessagePort interface: attribute onclose]
expected: FAIL
[NotRestoredReasonDetails interface: existence and properties of interface object]
expected: FAIL
@ -5363,9 +5360,6 @@
[Navigator interface: window.navigator must inherit property "pdfViewerEnabled" with the proper type]
expected: FAIL
[MessagePort interface: attribute onclose]
expected: FAIL
[SharedWorker interface: existence and properties of interface object]
expected: FAIL

View file

@ -1,7 +0,0 @@
[explicitly-closed.tentative.window.html]
expected: TIMEOUT
[Close event on port2 is fired when port1 is explicitly closed]
expected: TIMEOUT
[Close event on port2 is fired when port1, which is in a different window, is explicitly closed.]
expected: TIMEOUT

View file

@ -33,3 +33,13 @@ promise_test(async t => {
});
await closeEventPromise;
}, 'Close event on port2 is fired when port1, which is in a different window, is explicitly closed.')
promise_test(async t => {
const rc = await addWindow();
const waitForPort = expectMessagePortFromWindowWithoutStartingIt(window);
await createMessageChannelAndSendPortFollowedByClose(rc);
const port = await waitForPort;
const closeEventPromise = createCloseEventPromise(port);
port.start();
await closeEventPromise;
}, 'Close event on port2 is fired when port1, in a different window, is closed during the transfer of port2.')

View file

@ -21,6 +21,44 @@ function expectMessagePortFromWindow(window) {
});
}
/**
* Create a new promise that resolves when the window receives
* the MessagePort and does not start 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 expectMessagePortFromWindowWithoutStartingIt(window) {
return new Promise(resolve => {
window.onmessage = e => {
try {
assert_true(e.ports[0] instanceof window.MessagePort);
resolve(e.ports[0]);
} catch (e) {
reject(e);
}
};
});
}
/**
* 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, and immediately closes the entangled port.
*
* @param {RemoteContextWrapper} remoteContextWrapper
*/
async function createMessageChannelAndSendPortFollowedByClose(remoteContextWrapper) {
await remoteContextWrapper.executeScript(() => {
const {port1, port2} = new MessageChannel();
port1.start();
window.opener.postMessage({}, '*', [port2]);
port1.close();
});
}
/**
* Create a new MessageChannel and transfers one of the ports to
* the window which opened the window with a remote context provided