mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb: Fix race condition between read_all_bytes and stream population
There might be a race between read_all_bytes and stream population. If document load reads stream before it is populated, the stream will be empty and might lead to hang in SessionHistoryTraversalQueue which is expecting a promise to be resolved on document load. This race can occur when stream population and document source are set very close to each other. For example, when a newly generated blob is set as the source of an iframe. - navigation/multiple-navigable-cross-document-navigation.html has been modified to trigger this race.
This commit is contained in:
parent
50a79c6af8
commit
1f5ffe04c8
Notes:
github-actions[bot]
2025-11-26 11:28:23 +00:00
Author: https://github.com/AtkinsSJ
Commit: 1f5ffe04c8
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6929
3 changed files with 22 additions and 16 deletions
|
|
@ -145,24 +145,24 @@ WebIDL::ExceptionOr<Infrastructure::BodyWithType> extract_body(JS::Realm& realm,
|
|||
|
||||
// 12. If action is non-null, then run these steps in parallel:
|
||||
if (action) {
|
||||
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [&realm, stream, action = move(action)] {
|
||||
HTML::TemporaryExecutionContext execution_context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||
// AD-HOC: There is a race condition between document population(Ex: load_html_document->fully_read->read_all_bytes->readable_stream_default_reader_read)
|
||||
// and stream population. So currently we run stream population synchronously.
|
||||
HTML::TemporaryExecutionContext execution_context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||
|
||||
// 1. Run action.
|
||||
auto bytes = action();
|
||||
// 1. Run action.
|
||||
auto bytes = action();
|
||||
|
||||
// Whenever one or more bytes are available and stream is not errored, enqueue the result of creating a
|
||||
// Uint8Array from the available bytes into stream.
|
||||
if (!bytes.is_empty() && !stream->is_errored()) {
|
||||
auto array_buffer = JS::ArrayBuffer::create(stream->realm(), move(bytes));
|
||||
auto chunk = JS::Uint8Array::create(stream->realm(), array_buffer->byte_length(), *array_buffer);
|
||||
// Whenever one or more bytes are available and stream is not errored, enqueue the result of creating a
|
||||
// Uint8Array from the available bytes into stream.
|
||||
if (!bytes.is_empty() && !stream->is_errored()) {
|
||||
auto array_buffer = JS::ArrayBuffer::create(stream->realm(), move(bytes));
|
||||
auto chunk = JS::Uint8Array::create(stream->realm(), array_buffer->byte_length(), *array_buffer);
|
||||
|
||||
stream->enqueue(chunk).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
stream->enqueue(chunk).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
// When running action is done, close stream.
|
||||
stream->close();
|
||||
}));
|
||||
// When running action is done, close stream.
|
||||
stream->close();
|
||||
}
|
||||
|
||||
// 13. Let body be a body whose stream is stream, source is source, and length is length.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,4 @@
|
|||
PASS
|
||||
PASS
|
||||
PASS
|
||||
PASS
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
<script src="../include.js"></script>
|
||||
<iframe id="a"></iframe>
|
||||
<iframe id="b"></iframe>
|
||||
<iframe id="c"></iframe>
|
||||
<iframe id="d"></iframe>
|
||||
<script>
|
||||
asyncTest(done => {
|
||||
let doneA = false, doneB = false;
|
||||
function check() {if (doneA && doneB) done();}
|
||||
let doneA = false, doneB = false, doneC = false, doneD = false;
|
||||
function check() {if (doneA && doneB && doneC && doneD) done();}
|
||||
function makeContent(id, n) {
|
||||
let html = `<h3>${id} ${n}</h3>`;
|
||||
if (n % 3 === 0) html += `<iframe id="nest1+${id}" srcdoc="${id} ${n}"></iframe>`;
|
||||
|
|
@ -27,5 +29,7 @@
|
|||
}
|
||||
run(document.getElementById('a'), 'a', 0, 101, () => {doneA = true; check();});
|
||||
run(document.getElementById('b'), 'b', 0, 101, () => {doneB = true; check();});
|
||||
run(document.getElementById('c'), 'c', 0, 101, () => {doneC = true; check();});
|
||||
run(document.getElementById('d'), 'd', 0, 101, () => {doneD = true; check();});
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue