Follow the spec reuse of the initial same-origin about:blank window for
a browsing context's first real navigation. This fixes a crash in
promise-job-entry-different-function-realm.html by preserving the
correct iframe realm.
However, reusing the initial about:blank Window means
create-and-initialize can associate that Window with the pending
Document before session history activation has made the Document active.
Treating a browsing context's active document as its active Window's
associated Document therefore exposes the pending Document too early.
To fix this, add an explicit active document slot to BrowsingContext and
update it when a Document is made active.
A similar fix was attempted in 7fc7263a4d,
but that version still queued navigation tasks through the reused active
Window, tagging them with the pending Document before it was active.
This caused parser-created iframe loads to hang in encoding WPTs. This
commit instead queues those navigation-internal tasks against the
navigable's currently active Document.
The newly added iframe-initial-load-chunked-body.html mimics the same
type of failure seen by the WPT regressions mentioned above.
The spec doesn't say what time to use here but in other places where we
schedule animation events it says to use "the result of applying the
procedure to convert timeline time to origin-relative time to the
current time of the timeline with which animation is associated", so we
do that here as well.
This time is stable per animation frame per timeline so we now correctly
fall back to composite ordering of animation/transition events where we
previously wouldn't (since the scheduled time was so precise that it was
always unique), which causes the imported test to no longer be flakey.
Problem: An iframe whose content changes while it (or an ancestor)
has visibility:hidden isn’t painted once it becomes visible again.
The stale previous frame stays on screen until an unrelated
repaint (e.g., window resize) happens to occur.
Cause: set_needs_repaint() returns early for any document inside an
iframe with a visibility:hidden ancestor — discarding the request
entirely. The navigable’s needs_repaint flag is never set. Nothing
sets it again when the iframe becomes visible — so the rendering
loop keeps skipping it — and its display list stays stale.
Fix: Stop discarding the request in set_needs_repaint(). Instead,
skip painting hidden navigables in the rendering loop — while
leaving needs_repaint set. Once an ancestor iframe becomes
visible, the still-set flag makes the rendering loop paint the
navigable on the next frame.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/9305
Track explicit inherit of non-inherited properties only on the direct
parent shadow root. A deeper descendant with margin-left: inherit still
inherits from its own parent, so a host margin change does not require
marking every ancestor as possibly affected.
Extend the shadow-root inherited style test to cover both the direct
child case that must still update and the deeper descendant case that
must not trigger broad inherited-style recomputation.
Make style updates reach a fixed point when slot invalidation dirties
assigned nodes after their traversal point. During inherited-style
cascades, only the topmost changed element scans for descendant slots.
Animation inherited-style updates now include the target slot and walk
shadow-including descendants, so host animations propagate inherited
values through shadow trees and assigned slottables. Animated inherited
longhands also carry the same inherited-style invalidation signal as
regular style changes.
Mark custom elements dirty when their :defined state flips, so upgraded
elements do not keep stale :not(:defined) computed style. Add coverage
for slotted menu invalidation, descendant-slot scan counts, target slot
animations, host shadow-tree animations, and explicit inherit from an
animated non-inherited longhand.
Track when style recomputation may require inherited-style work in a
shadow tree, and use that signal when crossing from a shadow host into
its shadow root. Shadow descendants can explicitly inherit normally
non-inherited host properties, so any host style change may need
inherited-style recomputation there.
Use the CSS property definition to tell whether changed longhands need
to propagate to shadow descendants. Do the same after recomputing
inheritance-dependent values, such as host font-size: 2em after a parent
font-size change.
The shadow DOM tests cover inline and class-driven inherited host style
changes, explicit inherit for non-inherited host properties, relative
units on hosts, and nested slotted inheritance updates.
Install the inherited about base URL before setting the document URL so
fallback-base URL consumers see the correct source during URL-dependent
style and content blocker work.
This is used whereever we use `unsafe_layout_node`. There's no
difference in behavior for `SyntheticPseudoElement` but once we
implement element-reference pseudo-elements it will call
`unsafe_layout_node` on the referenced element rather than `layout_node`
Most of this functionality was already implicitly disallowed for
element-reference pseudo-elements by the fact that we weren't creating
entries in `m_pseudo_element_data` for them, but we need to explicitly
limit it in preparation of creating those entries.
Due to the above this is mostly non-functional apart from a regression
where we no longer support custom properties on element-reference
pseudo-elements. Previously when setting custom properties for an
element-reference pseudo-element we would call `ensure_pseudo_element()`
which created a synthetic pseudo-element entry distinct from the
referenced element which only stored custom property data - this was
clearly wrong and will be implemented properly in a future commit.
When adopting an element, we now set the node document of each of its
attributes to the new document. Without this, `Attr` objects retained
document pointers back to a temporary document created during fragment
parsing, these documents were then kept alive after the parsed nodes
were moved into the real document.
Apply the focusing steps' get-the-focusable-area mapping before
rejecting a non-focusable target. This preserves documentElement.focus()
by mapping the non-focusable document element to the Document viewport.
Also map rendered navigable containers with content navigables to their
active document, while leaving hidden containers unfocused. Preserve
Window focus events for child document viewports reached through iframe
focus, while still suppressing the top-level viewport surrogate events.
Treat rendered object elements as focusable through their default
non-null tabindex, even when they show fallback or image content instead
of a child navigable.
Keep the spec focus-chain common-tail handling intact for viewport
focus. The Document object is only our surrogate for the viewport, so
designate viewport focus from the new focus target without dispatching
Window focus/focusin events for that top-level surrogate.
Pass that viewport surrogate as the fallback target for fragment
scrolling and NavigateEvent focus reset, so unfocusable body or fragment
targets still clear stale element focus.
Cover documentElement.focus() in both the activeElement and focus-chain
tests, including a tabindex document element that remains focused as an
element. Cover object focus with and without a child navigable, hidden
object focus attempts, iframe focus events, hidden iframe focus, and
blurring a focused iframe after it becomes hidden. Also cover viewport
fallback for intercepted navigation focus reset and fragment scrolling
to an unfocusable target.
Similar to GC::Root<T>, make GC::RootVector<T> constructible without
explicitly passing a Heap.
This is implemented by having RootVectorBase use GC::Heap::the() for
heap-free construction.
Partial SVG relayout builds a throwaway layout state rooted at the
nearest dirty SVG viewport. SVG graphics layout can still walk past
intermediate SVG viewports when looking for ancestor transforms, so
pre-populate every SVG graphics ancestor instead of stopping at the
first ancestor SVG viewport.
Fixes an issue where the typing indicator would crash Discord when
someone starts typing a message. :^)
e.g., `@container (width >= 300px) {}` and similar.
During style computation, flag any elements whose style depends on a
size container. Then re-evaluate their style after the initial layout
has been computed and size containers have a size. This may take
multiple passes, as these may have further descendants that depend on
their size, etc. We limit this to 8 passes currently.
SizeFeature itself is very similar to MediaFeature, but queries the
container element instead. There are only 6 size features specified, so
they're hard-coded instead of generated from JSON.
Also add a counter test for the narrower restyle path.
Avoid recomputing boundary event offsets after dispatch has started.
Boundary event listeners can invalidate layout while set_hovered_node()
is still synthesizing the rest of the event batch, and transformed
targets need the document paintable's visual context tree for offset
calculation.
Compute every target-specific offset before firing the first boundary
event, while the paint tree from the platform hit-test is still intact.
Keep a defensive fallback for a missing viewport paintable when applying
transforms.
Add a UIEvents text test that opens a popover from pointerout and then
continues into a transformed target.
We now apply first letter styles by splitting text with a first-letter
style applied into 2 `TextSliceNode` objects. The
`DOM::Text` layout node always points at the non first-letter slice
and the first-letter slice is reachable via
`TextSliceNode::first_letter_slice()`.
First letter splitting works by `TreeBuilder` walking a block
container's inline descendants to find the first typographic letter
unit per the pattern given in css-pseudo level 4, which is then
wrapped in an anonymous inline box styled with the `::first-letter`
computed properties.
Consumers that map between DOM offsets and layout geometry
are updated to visit all slices of a `DOM::Text` through
`TextOffsetMapping`.
Recompute offsetX and offsetY for each synthesized mouse and pointer
boundary event target instead of reusing the offset for the original hit
target. Boundary event listeners on ancestors now observe coordinates
relative to their own targets.
Add a UIEvents text test that covers bubbling over/out events, enter
and leave events across multiple nested targets, partial exits into an
ancestor, and full exits to the test output element.
Set platform pointer events as primary mouse pointer events. This lets
pages recognize real mouse input from pointermove before they install
mouse tracking behavior.
Also thread platform mouse coordinates through hover target changes.
This makes mouseover, mouseout, mouseenter, mouseleave, and matching
pointer boundary events trusted and gives them the coordinate data from
the originating platform mouse event.
Cover both paths with UIEvents text tests.
Descendants of a display:none element are not rendered and their
computed style is only observable through on-demand reads. Skip the
recursive style descent at display:none ancestors during the
top-down traversal in `update_style_recursively()`.
Keep the temporary document used by HTML fragment parsing from
running post-connection work while the parser is staging nodes there.
This lets scripts from Range.createContextualFragment() remain
unstarted until the returned fragment is inserted into the real
document, and removes the script-specific preparation guard.
Strengthen parser coverage so contextual fragment scripts must wait
until the fragment is applied before running.
document.close() can defer script-created parser cleanup while a
parser-blocking script is pending. If document.open() installs a new
parser before the old parser resumes, the deferred action must clean up
the parser that scheduled it instead of the document's current parser.
Capture that parser before installing the deferred action. This keeps
the parked cleanup from affecting a parser installed by a later
document.open() call.
Finish the Rust implementation of the spec tree-construction algorithms
needed by the LibWeb test suite. Add the remaining table modes, foster
parenting, scope helpers, adoption agency handling, ruby/list/form and
select cases, frameset state, foreign-content edge cases, and parser
host callbacks.
Preserve behavior that depends on the C++ DOM integration, including
parser-created custom element reactions, fragment quirks mode, arbitrary
fragment namespaces, template fragment mode, fragment form ownership,
MathML annotation-xml boundaries, contextual fragment scripts, parser
script source positions, document.close() parser state, void-element
insertion, and duplicate attribute tracking.
Add focused tests for the parser edge cases that are easy to regress at
the boundary between the Rust tree builder and the C++ DOM host.
Animation updates need a rendering opportunity so current time can
advance and effects can update computed properties. They should not mark
the document as needing paint before any animated value changed.
Request a frame for pending animation style updates. The existing
AnimationUpdateContext invalidation will decide whether layout or paint
work is needed. This avoids presenting unchanged frames for infinite
animations on pages like x.com.
Prune decoded image resources retained by active documents once they
grow beyond a recent working set. Route-heavy applications can load
many unique images through one Document, and the shared resource and
available-image caches would otherwise keep decoded image data alive
after the DOM stopped using those images.
Track cache touches and evict least recently used decoded images from
both caches. This keeps active documents from accumulating unbounded
decoded image resources while preserving a small hot cache for repeat
loads.
Display lists used to own the resource storage needed to replay their
command bytes. That kept the compositor tied to in-process object
ownership: sending a display list update also meant sharing the same
resource container with the recording side.
Move resource storage out of DisplayList and make display list updates
carry a transaction of resources to add and remove. Navigable now tracks
the resources referenced by the current display list, sends only the
delta to the compositor, and trims its recording-side storage to the
active set. The compositor applies those transactions to its own storage
before replacing the cached display list.
This still carries in-process resource objects, but it puts the
ownership boundary in the right place. Command bytes and resource
lifetime are now synchronized explicitly, which is the shape needed
before the compositor can receive serializable resource updates across a
process boundary.
Store ResizeObservation targets weakly, matching Blink and WebKit. A
ResizeObserver can be kept alive by the document while it has observed
targets, but the observation itself should not keep a removed target and
its subtree alive forever.
Prune dead observations before gathering active resize observations.
Snapshot the document observer list before pruning so unregistering an
observer cannot mutate the intrusive list being iterated. Keep gathered
active targets rooted while broadcasting callbacks, since an earlier
callback can remove a later target and trigger GC before delivery.
Expose an internals helper to force environment-bound test objects to be
treated as garbage. Add text coverage for both pruning a dead observer
during gather and GC during an earlier resize observer callback.
Remove every element from Document's potentially named element cache
when its id-bearing element is removed from the document. Dynamic id
changes add all elements to this cache so Window named properties can
observe them, but removal only pruned object and image elements. This
left ordinary removed elements, such as divs, strongly reachable from
the active document and kept their subtrees alive.
When an ancestor's custom property changes, intermediate elements that
don't themselves consume `var()`/`inherit()` were skipped during style
recomputation, leaving their `m_custom_property_data` pointing at the
ancestor's stale `CustomPropertyData`. Deeper descendants that did
consume `var()` then resolved against the out-of-date chain.
This change adds `Element::refresh_inherited_custom_property_data()` to
re-link an element's inherited chain to its parent's current data
without re-cascading. This is called during `update_style_recursively()`
for elements that don't need a full recompute.
Use compositor hit-test commands in the display list to rebuild async
wheel targets, and serialize the async scroll metadata needed to
reconstruct AsyncScrollingState from the same display-list snapshot.
Driving the async scroll tree off the display list rather than a
separately collected tree has a few benefits:
- No additional full paintable tree traversal is required, since the
information needed by the compositor is gathered while recording
the display list.
- The display list is already serializable, so the async scroll tree
no longer needs its own serialization path.
- It is more debuggable, as the existing display list dump now also
covers the data used to reconstruct the async scroll tree.
- In the future we will want to include other areas that can
interfere with hit-testing; recording them during display list
construction makes it straightforward to preserve a hit-testing
order that matches painting order.
Whenever an ancestor's container-name or container-type changes, it
affects the matching of any `@container` queries, and so can affect the
style of any descendant.
When nothing invalidates the display list between frames, push only an
updated scroll state snapshot to the rendering thread instead of
handing it the display list again.
This is preparation for moving rendering to a separate process, where
sending the display list across the process boundary on every frame
would be expensive.
Use GC::Timer instead of Core::Timer for the cursor blink timer so
that capturing [this] in the callback properly protects the Document
from garbage collection.
Previously we were inconsistent by generating code for enum definitions
but not generating code for dictionaries. With future changes to the
IDL generator to expose helpers to convert to and from IDL values
this produced circular depdendencies. To solve this problem, also
generate the dictionary definitions in bindings headers.
These steps need to be run whenever update_current_time is called and in
a future commit that will be more than just the one place currently.
This also removes the early return in `set_current_time` if the new
`current time` is the same as the old one, since we want to update
animations regardless (e.g. to run pending tasks)
Replace Vector<Variant<...>> display-list storage with a contiguous byte
buffer of command headers, trivially-destructible payloads, and inline
data spans. Variable-size data such as glyphs, paths, dash arrays,
gradient stops, and nested command bytes is appended next to the command
that references it.
The flat representation is prep work for sending display lists over IPC
to a dedicated rasterization process, which the previous Variant-based
structure could not support directly. It also avoids walking command
destructors when a display list is discarded and reduces per-command
allocation and indirection, improving memory use and data locality.
Commands used to keep rendering resources directly in the variant:
image frames, external content and video sources, filters, SVG paint
styles, and nested display lists. That makes the command stream own its
dependencies and prevents it from becoming a POD-like byte buffer.
Add DisplayListResourceStorage and replace those command fields with
stable resource IDs. The storage deduplicates resources by their
existing IDs and can copy only the resources referenced by a captured
command sequence, giving the future IPC boundary a clear list of
resources that must be communicated to the rasterization process.
Follow the spec reuse of the initial same-origin about:blank window for
a browsing context's first real navigation. This fixes a crash in
promise-job-entry-different-function-realm.html by preserving the
correct iframe realm.
Making this change also requires another AD-HOC workaround to defer
updating the reused window's associated document until activation,
since doing so during document creation makes the browsing context's
active document change too early.
Currently, this is the only pseudo class which is URL-sensitive. When
we implement proper tracking of visited URLs this will need to be
replaced with something more comprehensive.
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.
When the update intersection observations steps run (HTML rendering
update step 19), the algorithm calls Element::get_bounding_client_rect()
on each observed target and on element-typed roots. That path always
calls update_layout_if_needed_for_node() before reading the paintable
rect.
By the time step 19 runs, layout is already up to date: step 16 has
just laid out the document, and the preamble of step 19 itself flushes
any post-step-16 invalidation with a single update_layout call. So each
per-target update_layout call inside getBoundingClientRect is a
guaranteed no-op early-exit.