LibWeb: Parse srcdoc documents synchronously during activation
Bypass the async body-reading pipeline for about:srcdoc iframes whose body bytes are already in memory. Set up a deferred parser at document load time and run the post-activation update synchronously, so the body element exists before parent script can observe the new document via contentDocument. This matches Chrome and Firefox behavior for srcdoc iframes and fixes the flaky test `set-innerHTML-inside-iframe-srcdoc-document.html` that relied on body being non-null. Co-authored-by: Tim Ledbetter <tim.ledbetter@ladybird.org>
This commit is contained in:
parent
732063aca9
commit
a5e1c33743
3 changed files with 32 additions and 0 deletions
|
|
@ -813,6 +813,7 @@ public:
|
|||
bool ready_to_run_scripts() const { return m_ready_to_run_scripts; }
|
||||
void set_ready_to_run_scripts();
|
||||
void set_deferred_parser_start(GC::Ref<GC::Function<void()>>);
|
||||
bool has_deferred_parser_start() const { return m_deferred_parser_start; }
|
||||
|
||||
void set_latest_entry(RefPtr<HTML::SessionHistoryEntry>);
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,28 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_html_document(HTML::Navi
|
|||
HTML::HTMLParser::the_end(document);
|
||||
}
|
||||
|
||||
// AD-HOC: For about:srcdoc, the body bytes are always immediately available in the response source (the srcdoc
|
||||
// string was inlined when the navigation params were created). Bypass the async body-reading pipeline
|
||||
// and set up a deferred parser directly. Combined with running the post-activation update synchronously
|
||||
// when a deferred parser is set, this guarantees the body element exists before the document becomes
|
||||
// observable to the parent — matching Chrome and Firefox behavior for srcdoc iframes.
|
||||
//
|
||||
// FIXME: This only fixes the transient `contentDocument.body === null` race for srcdoc. The same race exists in
|
||||
// Ladybird for any other same-origin async iframe load (notably blob: URLs and same-origin HTTP), where
|
||||
// Chrome and Firefox also keep `contentDocument` pointed at the initial about:blank until the new document
|
||||
// reaches readyState="interactive". A spec-aligned fix would split "what the parent sees via
|
||||
// contentDocument" from the navigable's active document, swapping the parent-visible pointer only at
|
||||
// parser readiness.
|
||||
else if (auto const* data = navigation_params.response->body()->source().get_pointer<ByteBuffer>();
|
||||
data && document->url() == URL::about_srcdoc()) {
|
||||
auto mime_type = Fetch::Infrastructure::extract_mime_type(navigation_params.response->header_list());
|
||||
auto url = navigation_params.response->url().value();
|
||||
auto parser = HTML::HTMLParser::create_with_uncertain_encoding(document, *data, mime_type);
|
||||
document->set_deferred_parser_start(GC::create_function(document->heap(), [parser, url] {
|
||||
parser->run(url);
|
||||
}));
|
||||
}
|
||||
|
||||
// 3. Otherwise, create an HTML parser and associate it with the document.
|
||||
// Each task that the networking task source places on the task queue while fetching runs must then fill the
|
||||
// parser's input byte stream with the fetched bytes and cause the HTML parser to perform the appropriate
|
||||
|
|
|
|||
|
|
@ -942,6 +942,15 @@ void ApplyHistoryStepState::process_continuations()
|
|||
if (target_entry->document_state()->document_id() == displayed_document_id) {
|
||||
update_document();
|
||||
}
|
||||
// AD-HOC: When the document already has its parser pre-loaded with in-memory data (currently set up
|
||||
// only for about:srcdoc), perform updateDocument synchronously instead of queueing it.
|
||||
// updateDocument calls Document::set_ready_to_run_scripts(), which kicks off the deferred
|
||||
// parser. Running it in the same task as activation guarantees the body element exists before
|
||||
// script in the parent navigable can observe the new document — matching Chrome and Firefox
|
||||
// behavior for srcdoc iframes.
|
||||
else if (resolved_document->has_deferred_parser_start()) {
|
||||
update_document();
|
||||
}
|
||||
// 5. Otherwise, queue a global task on the navigation and traversal task source given targetEntry's document's relevant global object to perform updateDocument
|
||||
else {
|
||||
queue_global_task(Task::Source::NavigationAndTraversal, relevant_global_object(*resolved_document), GC::create_function(heap(), move(update_document)));
|
||||
|
|
|
|||
Loading…
Reference in a new issue