Commit Graph

13 Commits

Author SHA1 Message Date
Andreas Kling
bf15a35ae2 LibWeb: Fix precision loss in CSSPixelFraction::to_float/to_double
CSSPixelFraction::to_float() and to_double() were converting to
CSSPixels (6-bit fixed-point) before converting to float/double,
which truncated the result to 1/64 precision. For example, 500/2500
(= 0.2) became 12/64 (= 0.1875).

Fix by dividing the numerator and denominator as floats/doubles
directly, which is what every other browser engine does when
converting layout units to floating-point ratios.

This was an oversight in the original CSSPixelFraction implementation
(commit 8cd1f65507). The class was designed for lossless comparison
and multiply-then-divide operations (which stay in CSSPixels land),
and the to_float/to_double methods were convenience additions that
took the easy path through CSSPixels conversion without considering
the precision loss.
2026-03-22 14:09:22 -05:00
Andreas Kling
9abb7e4517 LibWeb: Apply scroll margin in IntersectionObserver compute_intersection
Per the spec, the observer's [[scrollMargin]] should be applied to
each scroll container's scrollport when walking the containing block
chain. This expands the effective clip rect, allowing targets to be
detected as intersecting before they actually enter the visible area
of the scroll container.

Add a scroll_margin_values() accessor to IntersectionObserver so the
raw LengthPercentage values can be used during intersection computation.
The scroll margin is applied by inflating the scroll container's
padding box rect before clipping the intersection rect against it.
2026-03-22 14:09:22 -05:00
Andreas Kling
c871c56178 Tests: Add IntersectionObserver test for scroll margin
Add a test that creates two observers watching the same target inside
an overflow:hidden container: one without scroll margin and one with
scrollMargin "50px". The target is partially visible (10px out of 50px).

Without scroll margin, the ratio is ~0.19 (10px visible / 50px target).
With scroll margin, the expanded scrollport should make the full target
visible (ratio=1), but currently both report the same ratio because
scroll margin is not yet applied in compute_intersection.
2026-03-22 14:09:22 -05:00
Andreas Kling
229eba9a06 LibWeb: Clip against scroll containers in IntersectionObserver
Implement the containing block chain traversal in compute_intersection
(steps 2-3 of the spec algorithm). Walk from the target's paintable box
up through the containing block chain, clipping the intersection rect
against each ancestor that has a content clip (overflow != visible).

This fixes the case where a target is inside an overflow:hidden or
overflow:clip container and pushed below the visible area. Previously,
the intersection ratio was incorrectly non-zero because we only
intersected with the root bounds (viewport), not intermediate
scroll containers.

Also update the scroll container clipping tests to verify the
intersection ratio (which is what compute_intersection affects)
rather than isIntersecting (which per spec is based on targetRect
vs rootBounds, not the clipped intersection rect).
2026-03-22 14:09:22 -05:00
Andreas Kling
d1b485e6d6 Tests: Add IntersectionObserver tests for scroll container clipping
Add tests for the implicit root case where targets are inside scroll
containers with various overflow types:

- overflow:hidden container with target pushed below visible area:
  should NOT be intersecting (currently wrong, shows intersecting).
- overflow:clip container with target pushed below visible area:
  should NOT be intersecting (currently wrong, shows intersecting).
- overflow:hidden container with target at top (visible):
  should be intersecting (correct).

The two failing tests currently reflect wrong behavior because
compute_intersection does not clip against intermediate scroll
containers when walking the containing block chain.
2026-03-22 14:09:22 -05:00
Andreas Kling
bbadb38881 LibWeb: Fix IntersectionObserver skip condition for explicit roots
The boolean logic in steps 2-3 of "run the update intersection
observations steps" was incorrect. The code used || between the two
negated skip conditions instead of &&, and the first sub-expression
had its negation inverted.

This caused intersection geometry to be computed (and callbacks fired)
for targets that are not descendants of an explicit element root,
as long as they spatially overlapped the root's bounding rect.

The spec says:
- Step 2: Skip if root is not implicit AND target not in same document.
- Step 3: Skip if root is Element AND target not descendant of root.

The enter condition is NOT(skip2) AND NOT(skip3), but we had
NOT(skip2) OR NOT(skip3) with a wrong negation in the first part.
2026-03-22 14:09:22 -05:00
Andreas Kling
5e5dbae218 Tests: Add IntersectionObserver tests for explicit element root
Add tests verifying behavior when an IntersectionObserver has an
explicit element root:

- A target that is a descendant of the root should be reported as
  intersecting when it overlaps the root.
- A target that is NOT a descendant of the root but spatially overlaps
  it should NOT be reported as intersecting per spec step 3 of "run
  the update intersection observations steps". The expected output
  currently reflects the wrong (buggy) behavior.
2026-03-22 14:09:22 -05:00
Aliaksandr Kalenik
3e1d718b7f LibWeb: Ignore boxes without layout node in intersection observer steps
Check if box has associated layout node is not mentioned in the spec,
but it is required to match behavior of other browsers that do not
invoke intersection observer steps for boxes without layout node.
2025-04-14 17:01:53 +02:00
Timothy Flynn
bf668696de LibWeb+WebContent: Do not include DOM HTML in text test expectations
For example, in the following abbreviated test HTML:

    <span>some text</span>
    <script>println("whf")</script>

We would have to craft the expectation file to include the "some text"
segment, usually with some leading whitespace. This is a bit annoying,
and makes it difficult to manually craft expectation files.

So instead of comparing the expectation against the entire DOM inner
text, we now send the inner text of just the <pre> element containing
the test output when we invoke `internals.signalTextTestIsDone`.
2024-10-03 07:07:28 -04:00
Aliaksandr Kalenik
e232a84f0e LibWeb: Do not include box's own scroll offset in get_client_rects()
Fixes https://github.com/SerenityOS/serenity/issues/23631
2024-03-22 12:13:59 +01:00
Andreas Kling
8addfc14af LibWeb: Implement IntersectionObserver "intersection roots" per spec
In particular, get the implicit root correctly for intersection
observers that don't have an explicit root specified.

This makes it possible to load the Terminal app on https://puter.com/
2024-02-24 19:56:08 +01:00
Andreas Kling
fe04d83ef5 LibWeb: Process all pending lazy loading intersection observations
This fixes an issue where images outside the viewport could prevent
loading of images inside the viewport, depending on DOM order.
2023-12-24 13:23:40 +01:00
Andreas Kling
ef809eea1e LibWeb: Don't assume IO.unobserve() called on observed element
It's perfectly possible for JavaScript to call unobserve() on an element
that hasn't been observed. Let's stop asserting if that happens. :^)

Fixes #22020
2023-11-23 09:37:12 +01:00