Skip to content

Commit 1f5ffe0

Browse files
prajjwal000kalenikaliaksandr
authored andcommitted
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.
1 parent 50a79c6 commit 1f5ffe0

File tree

3 files changed

+22
-16
lines changed

3 files changed

+22
-16
lines changed

Libraries/LibWeb/Fetch/BodyInit.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,24 +145,24 @@ WebIDL::ExceptionOr<Infrastructure::BodyWithType> extract_body(JS::Realm& realm,
145145

146146
// 12. If action is non-null, then run these steps in parallel:
147147
if (action) {
148-
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [&realm, stream, action = move(action)] {
149-
HTML::TemporaryExecutionContext execution_context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
148+
// AD-HOC: There is a race condition between document population(Ex: load_html_document->fully_read->read_all_bytes->readable_stream_default_reader_read)
149+
// and stream population. So currently we run stream population synchronously.
150+
HTML::TemporaryExecutionContext execution_context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
150151

151-
// 1. Run action.
152-
auto bytes = action();
152+
// 1. Run action.
153+
auto bytes = action();
153154

154-
// Whenever one or more bytes are available and stream is not errored, enqueue the result of creating a
155-
// Uint8Array from the available bytes into stream.
156-
if (!bytes.is_empty() && !stream->is_errored()) {
157-
auto array_buffer = JS::ArrayBuffer::create(stream->realm(), move(bytes));
158-
auto chunk = JS::Uint8Array::create(stream->realm(), array_buffer->byte_length(), *array_buffer);
155+
// Whenever one or more bytes are available and stream is not errored, enqueue the result of creating a
156+
// Uint8Array from the available bytes into stream.
157+
if (!bytes.is_empty() && !stream->is_errored()) {
158+
auto array_buffer = JS::ArrayBuffer::create(stream->realm(), move(bytes));
159+
auto chunk = JS::Uint8Array::create(stream->realm(), array_buffer->byte_length(), *array_buffer);
159160

160-
stream->enqueue(chunk).release_value_but_fixme_should_propagate_errors();
161-
}
161+
stream->enqueue(chunk).release_value_but_fixme_should_propagate_errors();
162+
}
162163

163-
// When running action is done, close stream.
164-
stream->close();
165-
}));
164+
// When running action is done, close stream.
165+
stream->close();
166166
}
167167

168168
// 13. Let body be a body whose stream is stream, source is source, and length is length.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
PASS
22
PASS
3+
PASS
4+
PASS

Tests/LibWeb/Text/input/navigation/multiple-navigable-cross-document-navigation.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
<script src="../include.js"></script>
33
<iframe id="a"></iframe>
44
<iframe id="b"></iframe>
5+
<iframe id="c"></iframe>
6+
<iframe id="d"></iframe>
57
<script>
68
asyncTest(done => {
7-
let doneA = false, doneB = false;
8-
function check() {if (doneA && doneB) done();}
9+
let doneA = false, doneB = false, doneC = false, doneD = false;
10+
function check() {if (doneA && doneB && doneC && doneD) done();}
911
function makeContent(id, n) {
1012
let html = `<h3>${id} ${n}</h3>`;
1113
if (n % 3 === 0) html += `<iframe id="nest1+${id}" srcdoc="${id} ${n}"></iframe>`;
@@ -27,5 +29,7 @@
2729
}
2830
run(document.getElementById('a'), 'a', 0, 101, () => {doneA = true; check();});
2931
run(document.getElementById('b'), 'b', 0, 101, () => {doneB = true; check();});
32+
run(document.getElementById('c'), 'c', 0, 101, () => {doneC = true; check();});
33+
run(document.getElementById('d'), 'd', 0, 101, () => {doneD = true; check();});
3034
});
3135
</script>

0 commit comments

Comments
 (0)