Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MessagePort is unexpectedly GC'ed after activity absence
https://bugs.webkit.org/show_bug.cgi?id=193184 rdar://54095480 Reviewed by Ryosuke Niwa and Geoffrey Garen. Per the HTML specification [1], we should keep the MessagePort alive as long as it is entangled. This makes sense since the port could receive messages as long as it is entangled. However, we were missing this check inside MessagePort::virtualHasPendingActivity(). We do still allow collection when entangled if there is no message event handler, since it wouldn't be observable then. Even if a new message would arrive, there would be no-one on JS side to notify. [1] https://html.spec.whatwg.org/multipage/web-messaging.html#ports-and-garbage-collection * LayoutTests/http/tests/messaging/messageport-gc-after-xhr-expected.txt: Added. * LayoutTests/http/tests/messaging/messageport-gc-after-xhr.html: Added. * LayoutTests/http/tests/messaging/resources/messageport-pong-after-xhr.html: Added. Add test coverage. The test is based on the one provided by Yoshiaki Jitsukawa from Sony. * Source/WebCore/dom/MessagePort.cpp: (WebCore::MessagePort::virtualHasPendingActivity const): Canonical link: https://commits.webkit.org/256342@main
- Loading branch information
Showing
14 changed files
with
224 additions
and
4 deletions.
There are no files selected for viewing
11 changes: 11 additions & 0 deletions
11
LayoutTests/fast/events/message-port-gc-after-closing-expected.txt
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,11 @@ | ||
Checks that MessagePort objects get GC'd after closing them | ||
|
||
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". | ||
|
||
|
||
Closing ports... | ||
PASS At least one port got GC'd after closing | ||
PASS successfullyParsed is true | ||
|
||
TEST COMPLETE | ||
|
57 changes: 57 additions & 0 deletions
57
LayoutTests/fast/events/message-port-gc-after-closing.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,57 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<body> | ||
<script src="../../resources/js-test.js"></script> | ||
<script> | ||
description("Checks that MessagePort objects get GC'd after closing them"); | ||
jsTestIsAsync = true; | ||
|
||
const messagePortsToCreate = 50; | ||
let portIdentifiers = []; | ||
let ports = []; | ||
let worker = new Worker("resources/message-port-gc-worker.js"); | ||
for (let i = 0; i < messagePortsToCreate; ++i) { | ||
let { port1, port2 } = new MessageChannel(); | ||
worker.postMessage("port", [port2]); | ||
port1.onmessage = (event) => { | ||
testFailed("Received unexpected message on MessagePort"); | ||
}; | ||
portIdentifiers.push(internals.messagePortIdentifier(port1)); | ||
ports.push(port1); | ||
} | ||
|
||
function checkSomePortsGotGCd() | ||
{ | ||
gc(); | ||
for (let portIdentifier of portIdentifiers) { | ||
if (!internals.isMessagePortAlive(portIdentifier)) { | ||
testPassed("At least one port got GC'd after closing"); | ||
finishJSTest(); | ||
clearInterval(intervalHandle); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
gc(); | ||
setTimeout(() => { | ||
gc(); | ||
for (let portIdentifier of portIdentifiers) { | ||
if (!internals.isMessagePortAlive(portIdentifier)) { | ||
testFailed("MessagePort was GC'd too eagerly"); | ||
finishJSTest(); | ||
return; | ||
} | ||
} | ||
debug("Closing ports..."); | ||
for (let port of ports) | ||
port.close(); | ||
|
||
ports = []; | ||
|
||
gc(); | ||
intervalHandle = setInterval(checkSomePortsGotGCd, 100); | ||
}, 0); | ||
</script> | ||
</body> | ||
</html> |
11 changes: 11 additions & 0 deletions
11
LayoutTests/fast/events/message-port-gc-after-removing-event-listener-expected.txt
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,11 @@ | ||
Checks that MessagePort objects get GC'd once they no longer have an event listener | ||
|
||
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". | ||
|
||
|
||
Removing event listeners on ports... | ||
PASS At least one port got GC'd after removing their event listener | ||
PASS successfullyParsed is true | ||
|
||
TEST COMPLETE | ||
|
57 changes: 57 additions & 0 deletions
57
LayoutTests/fast/events/message-port-gc-after-removing-event-listener.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,57 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<body> | ||
<script src="../../resources/js-test.js"></script> | ||
<script> | ||
description("Checks that MessagePort objects get GC'd once they no longer have an event listener"); | ||
jsTestIsAsync = true; | ||
|
||
const messagePortsToCreate = 50; | ||
let portIdentifiers = []; | ||
let ports = []; | ||
let worker = new Worker("resources/message-port-gc-worker.js"); | ||
for (let i = 0; i < messagePortsToCreate; ++i) { | ||
let { port1, port2 } = new MessageChannel(); | ||
worker.postMessage("port", [port2]); | ||
port1.onmessage = (event) => { | ||
testFailed("Received unexpected message on MessagePort"); | ||
}; | ||
portIdentifiers.push(internals.messagePortIdentifier(port1)); | ||
ports.push(port1); | ||
} | ||
|
||
function checkSomePortsGotGCd() | ||
{ | ||
gc(); | ||
for (let portIdentifier of portIdentifiers) { | ||
if (!internals.isMessagePortAlive(portIdentifier)) { | ||
testPassed("At least one port got GC'd after removing their event listener"); | ||
finishJSTest(); | ||
clearInterval(intervalHandle); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
gc(); | ||
setTimeout(() => { | ||
gc(); | ||
for (let portIdentifier of portIdentifiers) { | ||
if (!internals.isMessagePortAlive(portIdentifier)) { | ||
testFailed("MessagePort was GC'd too eagerly"); | ||
finishJSTest(); | ||
return; | ||
} | ||
} | ||
debug("Removing event listeners on ports..."); | ||
for (let port of ports) | ||
port.onmessage = null; | ||
|
||
ports = []; | ||
|
||
gc(); | ||
intervalHandle = setInterval(checkSomePortsGotGCd, 100); | ||
}, 0); | ||
</script> | ||
</body> | ||
</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,5 @@ | ||
let ports = []; | ||
|
||
onmessage = (event) => { | ||
ports.push(event.ports[0]); | ||
} |
11 changes: 11 additions & 0 deletions
11
LayoutTests/http/tests/messaging/messageport-gc-after-xhr-expected.txt
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,11 @@ | ||
Tests that MessagePort doesn't get GC'd while still entangled. | ||
|
||
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". | ||
|
||
|
||
Posting message to the iframe. | ||
PASS Received the message from the iframe. The MessagePort was not GC'd | ||
PASS successfullyParsed is true | ||
|
||
TEST COMPLETE | ||
|
27 changes: 27 additions & 0 deletions
27
LayoutTests/http/tests/messaging/messageport-gc-after-xhr.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,27 @@ | ||
<html> | ||
<body> | ||
<script src="/js-test-resources/js-test.js"></script> | ||
<iframe id="testFrame" src="resources/messageport-pong-after-xhr.html" style="display:none"></iframe> | ||
<script> | ||
description("Tests that MessagePort doesn't get GC'd while still entangled."); | ||
jsTestIsAsync = true; | ||
|
||
window.onload = () => { | ||
let { port1, port2 } = new MessageChannel(); | ||
debug("Posting message to the iframe."); | ||
document.getElementById('testFrame').contentWindow.postMessage("ping", "*", [port2]); | ||
port1.onmessage = (e) => { | ||
testPassed("Received the message from the iframe. The MessagePort was not GC'd"); | ||
finishJSTest(); | ||
}; | ||
|
||
setInterval(gc, 100); | ||
|
||
setTimeout(() => { | ||
testFailed("Did not receive the message from the iframe. The MessagePort was probably GC'd."); | ||
finishJSTest(); | ||
}, 5000); | ||
}; | ||
</script> | ||
</body> | ||
</html> |
11 changes: 11 additions & 0 deletions
11
LayoutTests/http/tests/messaging/resources/messageport-pong-after-xhr.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,11 @@ | ||
<script> | ||
onmessage = (e) => { | ||
let port = e.ports[0]; | ||
let xhr = new XMLHttpRequest(); | ||
xhr.open("GET", "/incremental/resources/delayed-css.py?delay=300"); | ||
xhr.onload = () => { | ||
port.postMessage("pong"); | ||
}; | ||
xhr.send(); | ||
} | ||
</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
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
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
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
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
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