mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
LibWeb: Don't crash accessing stale nested navigable paintable
The scroll state collection loop in record_display_list_and_scroll_state() called paintable() on hosted documents, which asserts layout is up to date. This crashes when a nested document has stale layout but a cached display list, e.g. a render-blocked iframe whose DOM was modified by document.open(). Since scroll offsets are independent of layout freshness, use unsafe_paintable() to skip the assertion.
This commit is contained in:
committed by
Andreas Kling
parent
e564eff402
commit
dd6d17d60d
Notes:
github-actions[bot]
2026-03-24 11:48:05 +00:00
Author: https://github.com/gmta Commit: https://github.com/LadybirdBrowser/ladybird/commit/dd6d17d60dc Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8594
@@ -2839,19 +2839,29 @@ void Navigable::record_display_list_and_scroll_state(PaintConfig paint_config)
|
||||
Painting::ScrollStateSnapshotByDisplayList scroll_state_snapshot_by_display_list;
|
||||
document_paintable.refresh_scroll_state();
|
||||
scroll_state_snapshot_by_display_list.set(*display_list, document_paintable.scroll_state_snapshot());
|
||||
|
||||
// Collect scroll state snapshots for each nested navigable
|
||||
document_paintable.for_each_in_inclusive_subtree_of_type<Painting::NavigableContainerViewportPaintable>([&scroll_state_snapshot_by_display_list](auto& navigable_container_paintable) {
|
||||
auto const* hosted_document = navigable_container_paintable.navigable_container().content_document_without_origin_check();
|
||||
if (!hosted_document || !hosted_document->paintable())
|
||||
auto* hosted_document = navigable_container_paintable.navigable_container().content_document_without_origin_check();
|
||||
if (!hosted_document)
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
// We can use unsafe_paintable() here since the scroll state collection only reads scroll offsets, which are
|
||||
// valid even when layout is stale (e.g., a render-blocked iframe whose DOM was modified but whose scroll
|
||||
// positions haven't changed).
|
||||
auto* hosted_paintable = const_cast<Painting::ViewportPaintable*>(hosted_document->unsafe_paintable());
|
||||
if (!hosted_paintable)
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
// We are only interested in collecting scroll state snapshots for visible nested navigables, which is
|
||||
// detectable by checking if they have a cached display list that should've been populated by
|
||||
// record_display_list() on top-level document.
|
||||
auto navigable_display_list = hosted_document->cached_display_list();
|
||||
if (!navigable_display_list)
|
||||
return TraversalDecision::Continue;
|
||||
const_cast<DOM::Document&>(*hosted_document).paintable()->refresh_scroll_state();
|
||||
scroll_state_snapshot_by_display_list.set(*navigable_display_list, hosted_document->paintable()->scroll_state_snapshot());
|
||||
|
||||
hosted_paintable->refresh_scroll_state();
|
||||
scroll_state_snapshot_by_display_list.set(*navigable_display_list, hosted_paintable->scroll_state_snapshot());
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user