Commit Graph

1152 Commits

Author SHA1 Message Date
Andreas Kling
3b90eb1d49 LibWeb: Recompute child style when parent's display changes
When a parent element's display property changes (e.g., to flex or
grid), children may need to be blockified or un-blockified.
Previously, children only received a recompute_inherited_style() call
which doesn't run the blockification logic.

This patch adds a parent_display_changed flag to the recursive style
update that forces children to get a full style recompute when their
parent's display change triggers a layout tree rebuild.
2026-01-26 12:40:36 +01:00
Andreas Kling
5fc276872a LibWeb: Add style invalidation for :open pseudo-class
Add proper style invalidation when the `open` attribute changes on
HTMLDetailsElement and HTMLDialogElement. The :open pseudo-class can
affect sibling selectors (e.g., `dialog:open + sibling`), so we need
full subtree + sibling invalidation.
2026-01-26 12:40:36 +01:00
Andreas Kling
37bdcc3488 LibWeb: Support MIME type sniffing for streaming HTTP responses
Previously, when loading a document, we would try to sniff the MIME
type by reading from the response body's source. However, for streaming
HTTP responses, the body source is Empty (the data comes through the
stream instead), so we had no bytes to sniff.

This caused pages like hypr.land (which sends no Content-Type header)
to be misidentified as plain text instead of HTML, since the MIME
sniffing algorithm would receive zero bytes and fall back to the
default type.

The fix captures the first bytes of the response body during fetch,
storing them on the Body object. These bytes are the "resource header"
defined by the MIME Sniffing spec - up to 1445 bytes, which is enough
to identify any MIME type the spec can detect.

Since bytes may arrive asynchronously during streaming, we use a
callback mechanism: if bytes aren't ready yet when load_document()
needs them, it registers a callback that fires once enough bytes have
been captured (or the stream ends).

The flow is:
1. FetchedDataReceiver receives network bytes, buffers them
2. When Body is created, buffered bytes are flushed to Body's sniff
   buffer, and subsequent bytes are appended as they arrive
3. Before calling load_document(), Navigable waits for sniff bytes
4. load_document() passes the bytes to MimeSniff::Resource::sniff()
2026-01-24 15:21:26 +01:00
Aliaksandr Kalenik
3e54291813 LibWeb: Move VisualViewport transform to AccumulatedVisualContext tree
Move the visual viewport (pinch-to-zoom) transform from a reserved slot
in DisplayList to the AccumulatedVisualContext tree as a root transform
node. Fixed position elements now correctly inherit from this context.

This requires rebuilding the context tree and display list on each zoom
change, but this overhead will be eliminated by future partial context
tree rebuilds.
2026-01-23 18:56:24 +01:00
Colleirose
bf7fd80140 LibCrypto+AK: Merge LibCrypto/SecureRandom into AK/Random
AK/Random is already the same as SecureRandom. See PR for more details.

ProcessPrng is used on Windows for compatibility w/ sandboxing measures
See e.g. https://crbug.com/40277768
2026-01-23 15:53:27 +01:00
Aliaksandr Kalenik
44bfb32d47 LibWeb: Move AccumulatedVisualContext update in paint properties update
Integrate the AccumulatedVisualContext tree update into
update_paint_and_hit_testing_properties_if_needed() to consolidate
paint tree preparation into a single function.
2026-01-22 10:43:20 +01:00
Luke Wilde
babfd70ca7 LibGC: Enforce that a Cell type must declare the allocator to use
This ensures that we are explicitly declaring the allocator to use when
allocating a cell(-inheriting) type, instead of silently falling back
to size-based allocation.

Since this is done in allocate_cell, this will only be detected for
types that are actively being allocated. However, since that means
they're _not_ being allocated, that means it's safe to not declare
an allocator to use for those. For example, the base TypedArray<T>,
which is never directly allocated and only the defined specializations
are ever allocated.
2026-01-20 12:00:11 +01:00
Tim Ledbetter
0035690977 LibWeb: Return body from activeElement() when nothing is focused
This matches the behavior of other browsers.
2026-01-19 13:08:30 +01:00
Jelle Raaijmakers
f2f8f3ae57 LibWeb: Propagate <body>'s image-rendering to root element
Whenever we propagated a <body>'s background image to the root element,
we ignored any `image-rendering` property present.
2026-01-19 12:05:08 +01:00
Aliaksandr Kalenik
23a74ddc2a LibWeb: Use AccumulatedVisualContext in Element::get_client_rects()
The previous implementation had a bug: it composed all ancestor
transforms but applied them around only the innermost element's
transform origin. The correct behavior is to apply each transform
around its own origin.

AccumulatedVisualContext already tracks all visual transformations
(transforms, scroll offsets, perspective) correctly for hit testing.
This change adds a new transform_rect_to_viewport() method that performs
the forward transformation (element coordinates to viewport
coordinates), which is the inverse direction of
transform_point_for_hit_test().

This fixes getBoundingClientRect() returning incorrect coordinates for
elements inside transformed ancestors with non-default
transform-origins.
2026-01-19 08:53:30 +01:00
Aliaksandr Kalenik
0ab35eee12 LibWeb: Fix spurious relayout triggers in font list comparison
Previously, compute_required_invalidation() compared font lists using
cached_computed_font_list(), which returns the lazily-cached value.
Since newly computed styles haven't had their font list computed yet,
this compared a non-null cached value (old style) against null (new
style), causing unnecessary relayout even when fonts hadn't changed.

Fix by using computed_font_list() to ensure both styles have their
font lists computed before comparison.
2026-01-19 04:01:37 +01:00
Adam Colvin
3a6d82245b LibWeb: Implement Screen.isExtended attribute
- Add WindowManagement to PolicyControlledFeature enum
- Add screen_count() virtual method to PageClient
- Store all screen rects in WebContent::PageClient, derive both
  screen_rect() and screen_count() from stored data
- Implement screen_count() overrides in SVGPageClient and PageHost
- Replace FIXME stub in Screen.cpp with spec-compliant implementation
2026-01-16 20:34:58 +01:00
Aliaksandr Kalenik
bb4e29be5c LibWeb: Remove ClipFrame
Remove the now-obsolete ClipFrame infrastructure:
- Delete ClipFrame.h and ClipFrame.cpp
- Remove assign_clip_frames() from ViewportPaintable
- Remove enclosing_clip_frame and own_clip_frame from PaintableBox
- Remove m_clip_state HashMap from ViewportPaintable

Clip handling is now fully managed through AccumulatedVisualContext
nodes with ClipData.
2026-01-15 19:50:53 +01:00
Aliaksandr Kalenik
a87b5c722d LibWeb: Add AccumulatedVisualContext debugging infrastructure 2026-01-15 19:50:53 +01:00
Aliaksandr Kalenik
04c3ad669b LibWeb: Rebuild AccumulatedVisualContext on transform changes
Ensure AccumulatedVisualContext stays synchronized when CSS transform
properties change.

AccumulatedVisualContext copies transform and perspective matrices from
the paintable tree at assignment time. When CSS properties that affect
these matrices change (transform, rotate, scale, translate, perspective,
transform-origin, perspective-origin), we must rebuild the
AccumulatedVisualContext tree to reflect the new values.

This adds a rebuild_accumulated_visual_contexts flag to style
invalidation that triggers a full rebuild during the next paint.

Note: The current invalidation strategy is inefficient - it rebuilds
the entire tree even for single-element transform changes. This could
be improved by patching the AccumulatedVisualContext node in-place with
updated matrices, but only when the transform doesn't transition
from/to none (which would change the tree structure). This optimization
is left for future work.
2026-01-15 19:50:53 +01:00
Aliaksandr Kalenik
10833640b1 LibWeb: Introduce AccumulatedVisualContext
Introduce AccumulatedVisualContext, a tree structure that tracks the
cumulative visual state (scroll offsets, clip regions, transforms,
perspective) for each paintable box.

Motivation:

Before this change, visual state was fragmented across multiple
mechanisms:
- ClipFrame: Tracked clip rectangles, each storing its own
  enclosing_scroll_frame_id to handle scroll offset adjustments
- scroll_frame_id: Passed separately to each display list command
- PushStackingContext: Stored transform matrices directly in the command
- Every display list command implemented translate_by() (45 methods
  total) to allow scroll offset adjustment during playback

This fragmentation led to:
- Complex, error-prone coordinate transformation logic scattered
  throughout the codebase
- Commands being mutated during playback to apply scroll offsets
- Duplicate logic between painting and hit testing for coordinate
  transformations

Solution:

AccumulatedVisualContext builds a tree where each node represents a
single visual operation:
- ScrollData: A scroll frame with its ID
- ClipData: A clip rectangle with optional border radii
- TransformData: A 4x4 transform matrix with its origin
- PerspectiveData: A perspective projection matrix

Each PaintableBox stores a reference to its accumulated context node.
The tree structure naturally captures the parent-child relationships,
so traversing from any node to the root gives the complete chain of
visual transformations.

Benefits this enables (in subsequent commits):
- Display list commands become immutable - no more translate_by()
- Single RefPtr<AccumulatedVisualContext> replaces separate
  scroll_frame_id and ClipFrame on commands
- LCA-based tree traversal during playback for efficient save/restore
- transform_point_for_hit_test() provides coordinate transformation for
  hit testing using the same structure
2026-01-15 19:50:53 +01:00
Sam Atkins
692760b109 LibWeb/CSS: Update links to css-scoping and css-shadow-parts specs
These have been merged together into a new "CSS Shadow Module" spec. No
behaviour changes.

Corresponds to:
80d140567a
2026-01-13 16:18:11 +01:00
Psychpsyo
fe2bc2bfe7 LibWeb: Improve scrollingElement handling
This change is currently entirely undetectable because of what the
added FIXME talks about. Currently, the HTML element's overflow is
always set to visible in both axes, so it getting set to "clip" in
the imported test ends up not mattering at all.
2026-01-13 11:47:13 +00:00
Jelle Raaijmakers
018fed547c LibWeb: Move onpointer* IDL attributes to PointerEventHandlers.idl
A separate file is used instead of adding the partial interface mixin
to PointerEvent.idl to avoid a circular import dependency.
2026-01-13 10:09:22 +01:00
Jelle Raaijmakers
aed9f6a395 LibWeb: Move onanimation* IDL attributes to AnimationEvent.idl
Use the newly added `partial interface mixin` support to move the
CSS animation event handler attributes to their proper location as
specified in CSS Animations Level 1.
2026-01-13 10:09:22 +01:00
Jelle Raaijmakers
1bef8b259a LibWeb: Create selection range when enabling designMode
Previously, setting designMode to "on" would only modify the selection
range if one already existed. Since selections start empty, this meant
no range was created and no selectionchange event was fired.

Use Selection::collapse() instead, which creates a new range if needed
and properly triggers the selectionchange event.
2026-01-13 10:09:22 +01:00
Jelle Raaijmakers
64f1e6e11a LibWeb: Add onanimation* event handler IDL attributes
Add these animation event handlers to GlobalEventHandlers:
- onanimationcancel
- onanimationend
- onanimationiteration
- onanimationstart

They were missing, causing `window.onanimationend = ...` style
assignments to silently fail.
2026-01-13 10:09:22 +01:00
Tim Ledbetter
a34c51a35e LibWeb: Only use base element target if no target attribute is present
This aligns step 2 of `get_an_elements_target()` with the specification.
2026-01-13 10:05:40 +01:00
Tim Ledbetter
94fa08dcfe LibWeb/SVG: Implement activation behavior for SVGAElement
This makes navigation work when clicking on SVG `<a>` elements.
2026-01-13 10:05:40 +01:00
Tim Ledbetter
ba7b0c60f0 LibWeb: Move hyperlink navigation methods to DOM::Element
This allows us to use these methods from `SVGAElement` without
inheriting  `HTMLHyperlinkElementUtils`, which we can't do for
`SVGAElement` due to a naming conflict with the `href()` method in
`SVGURIReferenceMixin`.
2026-01-13 10:05:40 +01:00
Jonathan Gamble
8f1cb4cbb0 LibWeb: Implement resizing for eligible elements and update scrollbars
Add ElementResizeAction to Page (maybe there's a better place). It's
just a mousemove delegate that updates styles on the target element.

Add ChromeMetrics for zoom-invariant chrome like scrollbar thumb
thickness, resize gripper size, paddings, etc. It's not user-stylable
but separates basic concerns in a way that a visually gifted
designer unlike myself can adjust to taste.

These values are pre-divided by zoom factor so that PaintableBox can
continue using device_pixels_per_css_pixel calls as normal.

The adjusted metrics are computed on demand from Page multiple times
per paint cycle, which is not ideal but avoids lifetime management and
atomics. Maybe someone with more surety about the painting flow control
can improve this, but it won't be a huge win. If profiling shows
this slowing paints, then Ladybird is in good shape.

Update PaintableBox to draw the resize gripper and deconflict
the scrollbars. Set apropriate cursors for scrollbars and gripper in
mousemove. We override EventHandler's cursor handling because nothing
should ever come between a man and his resize gripper.

Chrome metrics use the CSSPixels class. This is good because it's
broadly compatible but bad because they're actually different units
when zoom is not 1.0. If that's a problem, we could make a new type
or just use double.
2026-01-12 11:00:14 +00:00
Shannon Booth
40b490a6a2 LibWeb/SVG: Implement SVGFEDropShadowElement IDL interface 2026-01-11 13:14:40 +01:00
Tim Ledbetter
79a427e1ef LibWeb: Implement HTMLImageElement x() and y() getters
These attributes get the image's top left border edge  relative to the
root element's origin.

These methods ignore any transforms.
2026-01-11 00:33:08 +01:00
sideshowbarker
1b41659efd LibXML+LibWeb: Use existing HTML entities table for XML parsing too
For XHTML documents, resolve named character entities (e.g., &nbsp;)
using the HTML entity table via a getEntity SAX callback. This avoids
parsing a large embedded DTD on every document and matches the approach
used by Blink and WebKit.

This also removes the now-unused DTD infrastructure:

- Remove resolve_external_resource callback from Parser::Options
- Remove resolve_xml_resource() function and its ~60KB embedded DTD
- Remove all call sites passing the unused callback
2026-01-09 19:13:41 +00:00
Jonathan Gamble
196be843a5 LibWeb: Add helpers for focus management of text editors 2026-01-09 18:09:09 +01:00
Jonathan Gamble
116ccdd878 LibWeb: Add find_in_shadow_including_ancestry method to Node
The search walks self and ancestors until the predicate returns true.
2026-01-09 18:09:09 +01:00
Jonathan Gamble
0120e3513f LibWeb: Dont scroll elements into view if focus is lost 2026-01-09 18:09:09 +01:00
Sam Atkins
bcc8e6ca60 LibWeb/DOM: Clarify attribute-list comparison
Corresponds to:
7b84f10e47
2026-01-09 14:35:21 +00:00
Sam Atkins
960558f30a LibWeb: Register and unregister @property rules when added or removed
Previously, we registered `@property` rules during parsing, and treated
them the same as `CSS.registerProperty()` calls. This is not correct
for a couple of reasons: One, the spec wants us to distinguish between
those two sources of registered custom properties, with
`CSS.registerProperty()` calls taking precedence. Two, we never removed
the registered property when its `@property` was removed from the
document.

This commit deals with this by iterating active CSSPropertyRules to find
which ones currently apply, and storing those in a cache. This cache is
invalidated whenever the Document's style is invalidated, which happens
whenever a CSSRule is added or removed from the Document.

The attached test demonstrates this now working as it should.
2026-01-09 10:54:37 +00:00
Sam Atkins
02149a8032 LibWeb: Store registered custom properties as CustomPropertyRegistration
This brings us closer to the spec.
2026-01-09 10:54:37 +00:00
Shannon Booth
d901e937b6 LibWeb/CSS: Do not give internal stylesheets a location URL
This fixes Acid3 numbered test 72.
2026-01-09 10:54:11 +00:00
Sam Atkins
d84a0d411c LibWeb/HTML: Return Promises from Element scroll methods
This re-applies 3a7fcde341 which was
reverted in cacadc8806. The issues it had
were bugs in the previous commit and have been resolved.
2026-01-08 14:50:09 +00:00
Sam Atkins
0a57e1e8ac LibWeb: Clarify some scrolling-related code
A lot of our scrolling code is quite old, and doesn't match the spec,
but does use some similar names. This is quite confusing. In particular
`perform_scroll_of_viewport()` is not the same as the spec algorithm.
That algorithm is actually almost implemented in
`scroll_viewport_by_delta()`.

To clarify things, this commit makes a few changes:
- Rename perform_scroll_of_viewport() to
  perform_scroll_of_viewport_scrolling_box(). This is a better match
  for how we use this method, even if it's not actually a match for the
  algorithm. (:yakbait:)
- Move `scroll_viewport_by_delta()`'s code into a new
  `perform_a_scroll_of_the_viewport()` method, and make it take a
  position like it should. `scroll_viewport_by_delta()` now calls it
  with a calculated position.

I've avoided reusing the original `perform_scroll_of_viewport()` name to
avoid accidents.
2026-01-08 14:50:09 +00:00
Andreas Kling
2ac363dcba LibGC: Only call finalize() on types that override finalize()
This dramatically cuts down on time spent in the GC's finalizer pass,
since most types don't override finalize().
2026-01-07 20:51:17 +01:00
sideshowbarker
fac81e84ba LibXML: Replace the existing XML parser with libxml2 parsing
This change replaces our LibXML parser with a new implementation that
wraps libxml2's SAX2 API.

The new Parser class uses libxml2's SAX2 callbacks to drive the existing
XML::Listener interface. That preserves backward compatibility with all
existing consumers (XMLDocumentBuilder, DOMParser, etc.).
2026-01-07 14:38:52 +01:00
Andreas Kling
a9cc425cde LibJS+LibWeb: Add missing GC marking visits
This adds visit_edges(Cell::Visitor&) methods to various helper structs
that contain GC pointers, and makes sure they are called from owning
GC-heap-allocated objects as needed.

These were found by our Clang plugin after expanding its capabilities.
The added rules will be enforced by CI going forward.
2026-01-07 12:48:58 +01:00
Aliaksandr Kalenik
5336c53171 LibWeb: Stop intrinsic size cache invalidation at abspos boundaries
When content changes inside a layout node, we now reset intrinsic size
caches only up to the nearest absolutely positioned ancestor, rather
than all the way to the document root.

This optimization is safe because absolutely positioned elements don't
contribute to their ancestors' intrinsic sizes - they are skipped in
min/max content width calculations.

The needs_layout_update flag still propagates to all ancestors so the
document knows layout is needed. Only the cache reset is bounded.
2026-01-05 23:00:06 +01:00
Aliaksandr Kalenik
3353ec9663 LibWeb: Add result caching for :has() pseudo-class matching
The `:has()` pseudo-class requires traversing descendants (or siblings)
to find matches.

With this change we cache results keyed by `(Selector*, Element*)`
pairs. The cache is stored in `StyleComputer` and cleared at the start
of each style computation pass in `Document::update_style()`.

When `:has()` uses a descendant combinator and we find a match, we also
cache that all ancestors between the matching descendant and the
anchor match. For example with `div:has(.target)`:

```html
<div id="A">  <!-- checking :has(.target) here -->
  <div id="B">
    <div id="C">
      <span class="target"/>
    </div>
  </div>
</div>
```

When we find `.target` while checking `div#A`, we also cache that
`div#B` and `div#C` match `:has(.target)` since they also contain
`.target`. Later when styling these elements, we get cache hits and skip
traversal.
2026-01-04 19:36:40 +01:00
Gingeh
789ab3cc57 LibWeb: Declare namespace prefixes in parse_xml_fragment 2026-01-02 11:09:19 +01:00
Aliaksandr Kalenik
ebcf85f6e8 LibWeb: Lowercase attribute only once in NamedNodeMap::get_attribute()
...instead of doing it on every iteration.

This function is hot in profiles on github.com landing page (invoked
through `for_each_matching_attribute()`), so this helps a bit.
2025-12-28 14:11:36 +00:00
Andreas Kling
0949d4d193 LibWeb: Make PseudoElementTreeNode protect its tree pointers from GC 2025-12-27 16:40:34 +01:00
Andreas Kling
418a243c04 LibWeb: Don't run tasks in documents that haven't been BC associated
Documents that have never been associated with a browsing context will
never become "fully active" so we shouldn't schedule tasks in them since
they'll never run.
2025-12-27 16:40:34 +01:00
Andreas Kling
f12f72f139 LibWeb: Don't associate mutation observer microtask with a document
The microtask is conceptually global and fires pending observers at the
agent level. As such, it doesn't make sense for it to be associated with
any specific document.
2025-12-27 16:40:34 +01:00
Tim Ledbetter
cacadc8806 Revert "LibWeb/HTML: Return Promises from Element scroll methods"
This reverts commit 3a7fcde341.
2025-12-26 19:33:51 +01:00
Aliaksandr Kalenik
00562d53e5 LibWeb: Save weak pointer to document in DocumentLoadEventDelayer
This prevents GC leaks caused by a DocumentLoadEventDelayer that is
never destroyed (for example, when its Document becomes inactive) from
holding a strong reference to the Document and keeping it alive.
2025-12-24 10:19:28 +01:00