LibWeb: Handle null active document in destroy_the_child_navigable

When an ancestor document is unloaded, its child documents are unloaded
(and destroyed) first, which leaves their navigable's active document
null. If the ancestor's pagehide handler then removes a subtree
containing one of those iframe containers, destroy_the_child_navigable
crashed dereferencing the null active document.

Treat the "destroy a document and its descendants" step as a no-op when
there is no document left to destroy, and still run the remaining
post-destruction cleanup.

This fixes a crash when closing a GMail tab.
This commit is contained in:
Andreas Kling
2026-04-16 01:35:25 +02:00
committed by Andreas Kling
parent 4575d23b1e
commit 354a20217c
Notes: github-actions[bot] 2026-04-16 11:17:52 +00:00
2 changed files with 60 additions and 3 deletions

View File

@@ -330,8 +330,7 @@ void NavigableContainer::destroy_the_child_navigable()
// 4. Inform the navigation API about child navigable destruction given navigable.
navigable->inform_the_navigation_api_about_child_navigable_destruction();
// 5. Destroy a document and its descendants given navigable's active document.
navigable->active_document()->destroy_a_document_and_its_descendants(GC::create_function(heap(), [this, navigable] {
auto after_document_destruction = GC::create_function(heap(), [this, navigable] {
// 3. Set container's content navigable to null.
m_content_navigable = nullptr;
document().schedule_html_parser_end_check();
@@ -357,7 +356,19 @@ void NavigableContainer::destroy_the_child_navigable()
signal->resolve({});
}));
}));
}));
});
// 5. Destroy a document and its descendants given navigable's active document.
// AD-HOC: The spec assumes the active document is non-null here, but during an ancestor
// unload the child documents are unloaded (and destroyed) before the ancestor's
// pagehide fires. If that pagehide handler then removes a subtree containing this
// container, we reach step 5 with navigable's active document already null. We
// treat the destroy step as a no-op in that case and proceed with the remaining
// post-destruction cleanup.
if (auto active_document = navigable->active_document())
active_document->destroy_a_document_and_its_descendants(after_document_destruction);
else
after_document_destruction->function()();
}
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#potentially-delays-the-load-event