Selecting text without custom ::selection styling changed the
foreground color of the selected content. This was especially visible
for links, where the text changed color but the underline did not.
The default selection style supplied both a selection background and a
foreground color from the palette or HighlightText system color. That
made ordinary selections behave as if the page had explicitly styled
::selection color.
Only provide a default selection background, so selected content keeps
its own foreground color unless CSS overrides it. Remove the now-unused
SelectionText palette role.
This allows us to remove the `frame_rect` accessor.
This also fixes a bug where we computed the scaling mode based on the
clipping rect rather than the draw rect for `ImagePaintable`
box_baseline() applied CSS2's bottom margin edge rule for non-visible
overflow to every caller, so flex items with hidden overflow were
baseline-aligned by their margin edge instead of their text. CSS Align
scopes that rule to a box's last baseline set, while flex baseline
alignment and table cells use the first set, which always derives from
content. Parameterize box_baseline() on the requested baseline set and
propagate it through the recursive child lookup.
Re-import these tests now that the importer supports multiple
references, so their mismatch *-notref.xht files are present instead
of pointing at unimported paths.
The <resolution> of the chosen image-set() option overrides the image's
natural resolution, so the image-set()'s natural dimensions are the
selected image's pixel dimensions divided by that resolution.
Effect culling can stop a visual context switch after applying
some ancestor contexts. The player left those contexts on the painter
stack even though the target command was skipped.
Restore the painter stack back to the common ancestor when effect
culling aborts the switch.
compute_transform() resolved transform-origin to a 2D point, so 3D
rotations pivoted around the element's own plane. Build the conjugation
T(0, 0, z) * M * T(0, 0, -z) into the matrix instead; this composes
with the 2D origin at paint time into the full 3D conjugation.
SVG image content drawn outside the image's viewport escaped the image
box, because `PaintNestedDisplayList` playback translated to the
destination rect without clipping to it. We now clip it during playback.
Problem: On https://turbovision.in6-addr.net a tiny-background-size +
background-repeat tile over a large painted area crashed WebContent.
Cause: BackgroundPainting was recording one paint command per tile for
any repeating background that’s not a decoded image. A small tile over a
large area could produce literally millions of commands — resulting in a
display list exceeding MAX_MESSAGE_PAYLOAD_SIZE, and tripping an assert.
Fix: When a repeating AbstractImageStyleValue would emit more tiles than
a fixed threshold, record a single tile into a nested display list and,
reusing some existing SVG pattern machinery, fill the coverage rect with
a PatternPaintStyle. We render the tile once, and repeat it with a
tiling shader — so the display list holds a single command regardless of
tile count. Normal backgrounds keep their exact per-tile painting.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/9876
When replacing the data in `CharacterData`, we were only invalidating
text-dependent cache and layout of the layout node(s) corresponding to
that `CharacterData`. This is not sufficient: With `::first-letter`
styling, `TextSliceNode`s are being created for the first letter and the
remainder of the text. So, contrary to regular `TextNode`s, these store
offsets into the text data determined while building the layout tree.
Subsequently these become stale when only invalidating layout, but not
the layout tree. Using such stale offsets with updated text data leads
to crashes in `TextNode::compute_text_for_rendering()` if the text got
shorter and incorrect behavior if it got longer.
To fix this, invalidate the layout tree of the parent of the affected
`TextSliceNode`s, causing a rebuild that creates new slice nodes with
the updated data.
This fixes a crash when typing in the search box on
https://search.brave.com/ where a first-letter style is used to
capitalize the first letter of the search suggestions descriptions. This
crash was a regression from b67d73a661.
Build an accumulated visual context tree when recording SVG mask and
clip subtrees. Mask content can contain filters, opacity, and blending,
but the nested display list previously had an empty visual context tree.
Filtered mask contents therefore painted as if those effects did not
exist.
Resolve filters for the nested paintables and attach their accumulated
visual context before recording the mask display list. Add direct and
SVG use based coverage for alpha-inverting filters inside masks.
Skip the document-complete clone when a resolved SVG use element already
has a current shadow tree. Href processing creates that root for refs.
Replacing it during load can leave stale mask contents.
Still clone at document-complete for forward refs and changed refs.
Add coverage for SVG use in masks, pre-load subtree mutations, and
unchanged instance roots.
Paint the background phase for replaced boxes reached through SVG
foreignObject traversal before foreground content. This lets img
background images render correctly when the containing SVG establishes a
stacking context.
Add a ref test for a paint-contained SVG with a foreignObject-hosted img
that uses a transparent SVG source and a CSS background image.
Do not force nested SVG roots back to position: static when they
are in HTML content inside a foreignObject. These SVG elements take
part in CSS box layout, so absolute positioning must be preserved.
Add a ref test that covers a nested masked foreignObject shape like
the Discord server icons, where ignoring the absolute offset clips the
icon four pixels too far inward.
Store CSS mask image layers in computed values so painting can honor the
coordinated mask-position, mask-size, mask-repeat, mask-origin, and
mask-clip longhands. Paint the CSS mask display list through the same
layer resolution path used by backgrounds, but keep the nested mask
commands local to the mask rect so existing mask-image placement remains
unchanged.
Add ref coverage for a no-repeat mask image sized and positioned at the
bottom of an element, which previously painted as a full-element mask.
When resolving the used positions of color stops, we were
unconditionally overwriting the first position with 0% and the last
position with 100%. We now only set these default values for color
stops without an explicit position.
Wait for two animation frames after the iframe document's fonts are
ready before allowing the reftest harness to snapshot the page. The
parent document can otherwise unblock while the iframe content has not
painted yet, making the test compare rendered content against an empty
reference iframe.
This makes font-face-descriptor-relative-length-iframe.html stop flaking
on my machine, and hopefully CI.
Use the page palette for default selection backgrounds instead of a
hardcoded blue color. Tint opaque selection backgrounds before painting.
Selected images stay visible through the overlay.
Use SelectionText for default selected text and HighlightText. Authored
::selection colors still take precedence. On macOS, use a light
translucent color matching other engines.
Update selection and system-color baselines for the themed colors. Add
macOS-specific screenshot baselines for tests whose expectations depend
on system colors.
A flex item with a preferred aspect ratio but no natural width or
height and no definite cross size is stretched to fill its container's
main size as a fallback. This was applied unconditionally, but when
the container itself is being measured for an intrinsic size, the
container's main size is indefinite and the size evaluated to zero.
This clobbered the max-content size the flex algorithm had already
computed as the intrinsic contribution.
Previously, when no previous sibling has a line box fragment, the
static position for inline-level elements defaulted to (0, 0), ignoring
float intrusion into the line box. We now use
`leftmost_inline_offset_at()` so the hypothetical box is placed on the
float-shortened line.
Previously, the `linked_gradient()` lookup only searched the document
for gradient IDs, so gradients inheriting stops via href inside a
shadow DOM would fail to find the correct target and be rendered black.
We now check any containing shadow root first, before checking the
document for linked gradient IDs.
When an embedded document was render-blocked, the parent display list
recording skipped drawing that iframe's compositor surface. Once the
child document later unblocked, only the child surface was repainted, so
the parent could keep replaying a display list that had no surface draw
command until another invalidation, such as resize, forced a recording.
Invalidate the containing iframe when a document's render-blocking set
becomes empty so the parent display list records the child surface. Add
a deterministic reftest that gates a render-blocking stylesheet on a
test-server signal, forcing the parent to paint the blocked iframe
before unblocking it.
Store the static-position insertion point produced by an inline
formatting context and let block layout consume it for originally-inline
absolute boxes that follow an anonymous inline wrapper. This removes the
block formatting context's fragment-walking reconstruction of the same
position.
Keep the calculation in IFC aware of forced breaks, aligned inline
content, trailing inline margins, wrapped lines, RTL inline content, and
vertical writing modes.
Keep regression coverage for the static-position cases covered while
building this model.
When an inline flex fragment raises the line box baseline, include the
parent strut descent in the line box bottom and advance the next line
from that enlarged line box. This keeps following wrapped or broken
lines from overlapping the extra descent.
Add ref and text coverage for inline-flex line height and line
advancement.
Skip the early effect culling optimization when the draw command has
coordinate-changing visual contexts before the target. Without this, a
filter or opacity layer can be rejected using an untransformed bounding
rect even though a later transform moves the pixels into view.
Add a reference test where an opacity layer starts below the viewport,
then a rotation brings it back on screen.
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
Problem: An absolutely-positioned element with “clip: rect(...)” stays
visible when “display: table” is also applied to it.
Cause: “display: table” elements generate an anonymous table-wrapper
box — and the “position” property is used on that wrapper rather than on
the table box itself. PaintableBox::get_clip_rect() only applies “clip”
to absolutely-positioned boxes — so it checks the table box, finds it’s
not absolutely positioned, and never applies the clip.
Fix: Transfer the “clip” property from the table box to the table-
wrapper box. That wrapper is the absolutely-positioned box — so
get_clip_rect() applies the clip correctly.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/9349
This adds a new class `ElementReferencePseudoElement` which forwards
any accesses to the referenced element for "element-reference" pseudo
elements.
This allows us to, for instance:
- Access the underlying `ComputedProperties` in getComputedStyle, which
includes inline style.
- Store and access `CustomPropertyData`
- Access the layout node for "resolved value" computation
- Apply animations from `update_animated_style_if_needed`
We register these with the originating element when calling
`set_associated_shadow_host_pseudo_element`, this requires us to append
the element to the tree beforehand since we need to be able to get the
shadow host.
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`.
When loading an image, there are two success paths: Either the image is
loaded through fetch, or it is part of the list of available images
already and doesn't need to be downloaded anymore.
Only the fetch code path was invalidating style and layout, meaning that
an image loaded from the list of available images was not visible until
something else caused an invalidation.
To fix this, perform the same invalidation in both code paths.
This fixes that most of the images on https://bleepingcomputer.com/ were
not loading until hovered. They are using
[`bLazy.JS`](https://github.com/dinbror/blazy), which loads the files on
a disconnected `img` element and then swaps out `src` or `srcset` on the
actual `img` element once the load has completed.
The `requestAnimationFrame`-dance in the testcase is there to ensure
that the test fails reliably without this change applied.
Treat non-positioned flex items like inline-blocks for painting, just
as we already do for grid items. This ensures later flex items cover
earlier items overflowing content instead of allowing earlier text to
paint over later backgrounds.
Canvas frame publishing was tied to CanvasPaintable::paint(), so cached
paint commands could replay DrawExternalContent without updating the
ExternalContentSource first. That made dynamic canvas content depend on
whether the display list was re-recorded for the frame.
Move canvas presentation to the rendering update step after animation
frame callbacks and layout, immediately before navigables paint. The
canvas paintable now only records the external-content draw command,
which keeps display-list replay from skipping the resource update.
Add a ref test that draws red, lets the display list cache, then draws
green on a later animation frame. The final rendering must come from the
updated external content source while reusing the cached draw command.
An auto margin on a grid item absorbs positive free space prior to
alignment via the box alignment properties, disabling the effects
of any self-alignment in that axis. We were applying both
auto-margin absorption and alignment additively, which doubled the
offset and pushed the item outside its grid area when, for example,
`margin-top: auto` was combined with `align-items: center`.
Avoid recursing into atomic inline-level descendants during the ancestor
float sweep.
CSS 2.2 paints inline-block and inline-table boxes in the inline-level
painting step, as if they created their own stacking contexts. Paint
their internal floats during
BackgroundAndBordersForInlineLevelAndReplaced instead.
paint_descendants already skipped positioned descendants at stack level
zero so they're only painted by paint_internal's pass, but the SVG-root
and grid-item branches returned before reaching the skip, making them
be painted twice.
Move the skip above them to make it apply to all deferred positioned
descendants uniformly.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/9277