The `is_ignorable_whitespace()` check in table fixup traverses anonymous
block wrappers to see if they contain only whitespace. It rejected
out-of-flow and text descendants but silently skipped in-flow non-text
elements like `<span>`, misclassifying wrappers with real content as
ignorable and absorbing them into the table structure.
Previously, a table with `width: 100%` and `margin: auto` whose content
was narrower than the viewport would be centered based on content
width rather than filling the containing block. Resizing the viewport
wider than the content would shift the table progressively further to
the right.
Co-authored-by: Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
SVG elements (except the outermost <svg>) use SVG's coordinate system,
not the CSS box model, so CSS positioning doesn't apply to them.
This adds SVGElement::adjust_computed_style() to force position:static
on all SVG elements except the outermost <svg> element (which has no
owner_svg_element()). SVGSymbolElement's existing override now calls
Base::adjust_computed_style() to inherit this behavior.
With this in place, the FIXME in layout_absolutely_positioned_element()
for SVG boxes becomes unreachable and is replaced with
VERIFY_NOT_REACHED().
Extract the box size computation into a new compute_box_size() that
returns a plain struct with two doubles, and use it in is_active()
instead of calculate_box_size() which allocates a GC-managed
ResizeObserverSize object.
Since is_active() only needs to compare sizes and immediately discards
the result, there's no reason to involve the GC. The GC-allocating
calculate_box_size() now delegates to compute_box_size() internally.
This was 2.6% of CPU time while playing a YouTube video.
Cache the result of the visibility+opacity check as a bit field
(m_visible) computed in resolve_paint_properties(), which already runs
before each paint when paint properties need updating.
This makes Paintable::is_visible() a simple inline bit field read
instead of chasing through layout_node->computed_values() every time.
This was 3.3% of CPU time while playing a YouTube video.
Since we now maintain cached state (m_in_editable_subtree) in the base
Node::moved_from(), subclasses must always call up to ensure the flag
is recomputed. Mark it MUST_UPCALL like inserted() and removed_from().
Add a cached m_in_editable_subtree flag to Node, updated on tree
mutations and contenteditable/designMode changes.
This replaces the recursive parent walk in is_editable() and
is_editing_host() with an O(1) flag check. The flag is recomputed
in inserted(), moved_from(), and cleared in removed_from(). Subtree
walks recompute the flag when contenteditable attributes change or
design mode is toggled.
This was 4% of CPU time while playing a YouTube video.
This should help avoid the footgun of forgetting to check for null on
m_fetch_controller. We had missed this check when firing off an error
due to an unsupported format in the PlaybackManager, so we could call
stop_fetch() on a null pointer if the download had completed already.
If we've already fired off an error, calling stop_fetch() should
make no difference, other than stopping the Requests::Request.
Eventually, we'll probably want abort() and terminate() to
eventually stop the Requests::Request in an unobservable way.
There are actually a couple of bugs here:
1. As of commit ebda8fcf11, editing hosts
are now excluded from Node::is_editable. Since this special hit test
handling is specifically for contenteditable nodes, we would not
enter this branch for these nodes.
2. We were not checking if the contenteditable node actually contained
the hit testing position. So if a page had multiple empty editable
nodes, we would just return whichever was hit test first.
These bugs were exposed by 7c9b3c08fa.
This commit resulted in the text cursor hit test node being set as the
document focus node. If we returned the wrong result, we would not set
the correct node.
This was seen on discord, where clicking the message box would result in
the search box being focused.
The grammar groups this component together meaning that all
sub-components must occur together i.e.
`ordinal slashed-zero small-caps` is valid but
`ordinal small-caps slashed-zero` is not.
We also reuse the logic for parsing from the longhand
`font-variant-numeric` property for simplicity.
The grammar groups this component together meaning that all
sub-components must occur together i.e.
`common-ligatures no-contextual small-caps` is valid but
`common-ligatures small-caps no-contextual` is not.
We also reuse the logic for parsing from the longhand
`font-variant-ligatures` property for simplicity.
The grammar groups this component together meaning that all
sub-components must occur together i.e. `jis78 full-width small-caps` is
valid but `jis78 small-caps full-width` is not.
We also reuse the logic for parsing from the longhand
`font-variant-east-asian` property for simplicity.
Many CSS grammars call for us to parse `Foo || Bar` but we don't have a
good way to represent this unless we have a custom style value. Usually
we store the values in a `StyleValueList` but since that can only store
non-null elements we have to manually recheck which elements we have at
which index whenever we use it.
This style value holds a Vector of `RefPtr<StyleValue const>` so that we
can represent null values and know what values are at what index without
manually rechecking.
These were moved in 0f04c6d but the namespace remained the same. We also
now forward declare these in `AK/Forward.h` rather than
`LibWeb/Forward.h` (or not at all in the case of `ValueComparingRefPtr`)
These tasks' captures aren't clearly safe as written, since raw
references don't make it apparent that we're capturing a GC-aware
reference. Conservative scanning made this safe, but let's make it a
bit clearer.
No callers of draw_painting_surface remain after the previous commits
migrated canvas, video, and SVG to use ExternalContentSource or
ImmutableBitmap snapshots.
The last consumer of draw_painting_surface now snapshots the surface
into an ImmutableBitmap and uses draw_scaled_immutable_bitmap instead,
removing the final dependency on DrawPaintingSurface.
present() now snapshots the PaintingSurface into an ImmutableBitmap
and publishes it to the ExternalContentSource, so the rendering thread
never touches the live GPU surface — eliminating the data race
described in the ExternalContentSource commit (problem 1).
Canvas elements are registered with Page and presented once per frame
from the event loop, rather than on every individual draw call in
CRC2D::did_draw(). A dirty flag on HTMLCanvasElement ensures the
snapshot is only taken when content has actually changed, and makes
the present() call in CanvasPaintable::paint() a no-op when the
surface has already been snapshotted for the current frame.
Publish new video frames to an ExternalContentSource, and switch
VideoPaintable from draw_scaled_immutable_bitmap to
draw_external_content.
Because DrawExternalContent reads the latest bitmap at replay time,
frame-only updates (no timeline or control change) now call
set_needs_display(InvalidateDisplayList::No) — skipping display list
rebuilds entirely. This addresses problem 2 from the previous commit.
Two related problems exist in the current display list architecture:
1. DrawPaintingSurface thread safety: CanvasPaintable::paint() records
the *same* PaintingSurface that the canvas rendering context draws
to. The rendering thread later reads from it, but the main thread
may be concurrently drawing — a data race.
2. Video frames force display list rebuilds: each new video frame
triggers set_needs_display() → full display list rebuild.
Both stem from display list commands holding direct references to
content (surface/bitmap) rather than going through an indirection
layer.
ExternalContentSource is a thread-safe, atomically-refcounted
container that holds an ImmutableBitmap snapshot. The accompanying
DrawExternalContent display list command reads from it during replay,
so producers can swap in new content without rebuilding the list.
Subsequent commits migrate canvas, video, and SVG painting to
ExternalContentSource and then remove DrawPaintingSurface.
By doing so, we attenuate the perspective transform on higher resolution
devices such as Retina displays (2x).
This fixes the perspective transform on sites such as
https://poke-holo.simey.me/.
The constraint equations for absolutely positioned replaced elements
only subtracted content width/height from the containing block size,
omitting padding and border.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/7820
When an img element is changed from animated image to static image, the
animation briefly continues into the new image, even if the new image
has only a single frame (static image).
Could also impact when going from animated image to another animated
image, but the new image has less frames versus the previously animated
image.
In some cases a newly loaded static image would continue to be animated,
so that is also stopped.
fixes: #7879fixes: #7945
The HTML event loop spec explicitly provides guidance for reentrancy in
the "perform a microtask checkpoint" algorithm, so we cannot VERIFY
for empty execution context before this early exit (as much as we'd
like to).
https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
See the microtask-checkpoint-reentrancy-via-responsexml-script test for
an example of how this can happen from user scripts.
The previous fix for scroll containers in grid unconditionally set the
min-content contribution to 0 in both dimensions. This caused grids with
height:min-content to collapse rows containing scroll container items to
0 height. Restrict the check to the column dimension only, since scroll
containers can overflow and scroll horizontally but must still
contribute their content height for correct row sizing.
This allows the Request to be cleaned up when it becomes inactive,
which in turn allows the GC to clean up the FetchController which is
indirectly captured by a root in the Request callbacks.
The stream's data request callback can't hold a strong reference to
FetchData, as that will create a reference loop:
FetchData -> IncrementallyPopulatedStream -> (lambda) -> FetchData
To prevent a use-after-free on the FetchData& capture, we clear the
data request callback in ~FetchData().