blob: e06f0cf2a94612405093078ff086298e38c7be16 [file] [edit]
<!DOCTYPE html>
<html>
<body>
<p>Tests that pipeTo does not crash when the document frame is detached during error propagation.<br>
WebKit should not crash, and you should see PASS below.</p>
<div id="result"></div>
<script>
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}
// The crash: StreamPipeToState::globalObject() calls
// jsDynamicCast<JSDOMGlobalObject*>(context->globalObject()), but
// ScriptExecutionContext::globalObject() can return null when the document
// has no frame (e.g. after its iframe is removed). jsDynamicCast does not
// null-check its argument, so passing null crashes with a read from 0x0.
//
// To reproduce, pipeTo must be called in the iframe's JS context so that
// StreamPipeToState is associated with the iframe's ScriptExecutionContext.
// Erroring the source stream queues a microtask. If the iframe is removed
// before that microtask runs, context->globalObject() returns null when the
// error-propagation callback fires.
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
// Run pipeTo inside the iframe's context (CallWith=CurrentGlobalObject means
// the StreamPipeToState gets the iframe's ScriptExecutionContext).
iframe.contentWindow.eval(`
var readable = new ReadableStream({ start(c) { window._controller = c; } });
var writable = new WritableStream();
readable.pipeTo(writable).catch(() => {});
window._controller.error("test error");
`);
// Detach the iframe's document from its frame before the error-propagation
// microtask runs. After this, ScriptExecutionContext::globalObject() returns
// null for the iframe's context (document->frame() is null).
iframe.remove();
// Let microtasks and the event loop run. Without the fix, the error-propagation
// microtask crashes in StreamPipeToState::globalObject().
setTimeout(() => {
document.getElementById('result').textContent = 'PASS';
if (window.testRunner)
testRunner.notifyDone();
}, 0);
</script>
</body>
</html>