Nested navigables were represented through compositor surface ids owned
by the parent context. That forced CompositorState and ContextState to
maintain bidirectional attach/detach bookkeeping, publish child
snapshots into a surface map, and keep presentation mode variants just
to distinguish UI presentation from parent composition.
Record the child compositor context id directly in the display list and
let the compositor resolve it against the painting parent at playback
time. Child contexts now keep their parent context id and latest
rendered surface, while parents no longer track child maps or compositor
surface ids. UI presentation is represented separately from parent
composition, so closing a page only stops client presentation and nested
contexts keep using set_parent_context.
Display lists kept canvas and nested navigable content alive through
ExternalContentSource objects. That made the resource graph depend on
process-local object identity instead of a stable surface handle, which
blocks compositor process isolation and made teardown-sensitive embedded
content harder to reason about.
Allocate CompositorSurfaceId values for canvases and child navigables,
publish their backing stores to the owning compositor, and paint them
with DrawCompositorSurface. Child navigables now publish to parent
compositors by CompositorContextId instead of raw object pointers, so
the in-process path uses the same stable addressing model required by a
remote compositor.
Clear and skip stale child surfaces during teardown, preserve Skia
canvas state while drawing compositor surfaces, and add display-list
coverage for canvas and iframe compositor surfaces. The nested navigable
async-scrolling baseline now expects DrawCompositorSurface.
The Paintable tree and its supplemental painting data structures were
GC allocated because that was the easiest way to manage it and avoid
leaks introduced by ref cycles. This included the Paintable subclasses
themselves plus StackingContext, ChromeWidget, Scrollbar, ResizeHandle,
and scroll-frame state.
We are now trying to reduce GC allocation churn on layout and painting
updates, so keeping this short-lived rendering tree outside the JS heap
is a better fit. Move Paintable to RefCountedTreeNode, make painting
helpers ref-counted or weakly reference Paintables, and update the
layout and event-handler call sites to use RefPtr/WeakPtr ownership.
Previously, iframes were rasterized synchronously as nested display
lists inside their parent's display list: the parent's paint walk called
record_display_list() on each hosted iframe document and emitted a
PaintNestedDisplayList command that the player would recurse into. Only
the top-level traversable's RenderingThread was ever active, even though
every Navigable already owned one.
The motivation for splitting this apart:
- Work in the outer document no longer has to be re-recorded when only
an iframe changes. The parent's cached display list now references the
iframe's rasterized output live via an ExternalContentSource, so an
iframe invalidation just needs the parent's display list replayed, not
re-recorded.
- Each iframe now has a self-contained rasterization pipeline, which is
prep work for moving iframes into separate sandboxed processes.
Parent document paints could still record and paint a nested iframe's
contents even while the child document was render-blocked. That let
unstyled iframe content leak into the parent display list before the
child's blocking stylesheet had loaded, which matched the FOUC seen in
Speedometer's TodoMVC suites.
Skip painting hosted documents from NavigableContainerViewportPaintable
while they are render-blocked. Add a reftest that keeps an iframe child
render-blocked with a delayed stylesheet and forces a parent repaint;
without the fix the child's inline FAIL text becomes visible.
The new reftest passes with this change, and the existing render-
blocking requestAnimationFrame tests still pass.
When painting a navigable container (iframe/object), the hosted
document's layout may have been invalidated during the parent
document's layout pass via did_set_content_size -> set_viewport_size
-> set_needs_layout_update.
If the hosted document is not in the event loop's docs list (e.g.,
it was created during a rAF callback after the list was built), its
layout would never be re-updated, causing a VERIFY failure in
Node::paintable() which asserts layout_is_up_to_date().
Fix this by calling update_layout() on the hosted document in
NavigableContainerViewportPaintable::paint() before accessing its
paintable. This is a no-op when layout is already current.
Fixes#8297.
Move the inline dom_node() method to Viewport.cpp so the header no
longer needs the full Document definition. Add explicit includes to
files that relied on the transitive dependency.
PaintContext dates back to a time when display lists didn't exist and it
truly represented "paint context". Renaming it to better align with its
current role.
6507d23 introduced a bug when snapshot for iframe is saved in
`PaintNestedDisplayList` and, since display lists are immutable, it's
not possible to update before the next repaint.
This change fixes the issue by moving `ScrollStateSnapshot` for
nested display lists from `PaintNestedDisplayList` to
`HashMap<NonnullRefPtr<DisplayList>, ScrollStateSnapshot>` that is
placed into pending rendering task, making it possible to update
snapshots for all display lists before the next repaint.
This change doesn't have a test because it's really hard to make a ref
test that will specifically check scenario when scroll offset of an
iframe is advanced after display list is cached. We already have
`Tests/LibWeb/Ref/input/scroll-iframe.html` but unfortunately it did
not catch this bug.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/5486
Making navigables responsible for backing store allocation will allow us
to have separate backing stores for iframes and run paint updates for
them independently, which is a step toward isolating them into separate
processes.
Another nice side effect is that now Skia backend context is ready by
the time backing stores are allocated, so we will be able to get rid of
BackingStore class in the upcoming changes and allocate PaintingSurface
directly.
With this change we save a copy of of scroll state at the time of
recording a display list, instead of actual ScrollState pointer that
could be modifed by the main thread while display list is beings
rasterized on the rendering thread, which leads to a frame painted with
inconsistent scroll state.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/4288
This reduces the number of `.cpp` files that need to be recompiled when
one of the below header files changes as follows:
Painting/Command.h: 1030 -> 61
Painting/DisplayList.h: 1030 -> 60
Painting/DisplayListRecorder.h: 557 -> 59