script/layout: Ensure a StackingContextTree before IntersectionObserver geometry queries (#38473)

IntersectionObserver needs to be able to query node geometry without
forcing a layout. A previous layout could have run without needing a
`StackingContextTree`. In that case the layout-less query should finish
building the `StackingContextTree` before doing the query.  Add a new
type of layout API which requests that layout finishes building the
StackingContextTree.

This change also slightly simplifies and corrects the naming of
`Element` APIs around client box queries.

Testing: This should fix intermittent failures in WPT tests.
Fixes: #38380.
Fixes: #38390.
Closes: #38400.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-08-06 15:46:43 +02:00 committed by GitHub
parent 757dbc0eda
commit 44a11a7c6c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 78 additions and 59 deletions

View file

@ -1005,7 +1005,7 @@ impl Element {
block: ScrollLogicalPosition,
inline: ScrollLogicalPosition,
) -> ScrollPosition {
let target_bounding_box = self.upcast::<Node>().bounding_content_box_or_zero();
let target_bounding_box = self.upcast::<Node>().content_box().unwrap_or_default();
let device_pixel_ratio = self
.upcast::<Node>()
@ -1071,7 +1071,7 @@ impl Element {
} else {
// Handle element-specific scrolling
// Scrolling box bounds and current scroll position
let scrolling_box = scrolling_node.bounding_content_box_or_zero();
let scrolling_box = scrolling_node.content_box().unwrap_or_default();
let scrolling_left = scrolling_box.origin.x.to_nearest_pixel(device_pixel_ratio) as f64;
let scrolling_top = scrolling_box.origin.y.to_nearest_pixel(device_pixel_ratio) as f64;
let scrolling_width = scrolling_box
@ -1125,7 +1125,7 @@ impl Element {
if matches!(position, Position::Relative | Position::Absolute) {
// If this element establishes a positioning context,
// Get its bounding box to calculate the offset
let positioning_box = node.bounding_content_box_or_zero();
let positioning_box = node.content_box().unwrap_or_default();
let positioning_left = positioning_box
.origin
.x
@ -3458,7 +3458,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
// https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect
fn GetBoundingClientRect(&self, can_gc: CanGc) -> DomRoot<DOMRect> {
let win = self.owner_window();
let rect = self.upcast::<Node>().bounding_content_box_or_zero();
let rect = self.upcast::<Node>().content_box().unwrap_or_default();
DOMRect::new(
win.upcast(),
rect.origin.x.to_f64_px(),