LibWeb: Make offsetTop and offsetLeft behave more like other browsers

We now follow the rules from the spec more closely, along with an
unspecified quirk for when the offsetParent is a non-positioned body
element. (Spec bug linked in a comment.)

This fixes a whole bunch of css-flexbox tests on WPT, which already had
correct layout, but the reported metrics from JS API were wrong.
This commit is contained in:
Andreas Kling 2024-07-25 17:01:16 +02:00 committed by Andreas Kling
commit d49ae5af32
Notes: github-actions[bot] 2024-07-25 16:52:52 +00:00
3 changed files with 48 additions and 16 deletions

View file

@ -22,10 +22,10 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BreakNode <br> BreakNode <br>
BreakNode <br> BreakNode <br>
BlockContainer <pre#out> at (8,109) content-size 784x51 children: inline BlockContainer <pre#out> at (8,109) content-size 784x51 children: inline
frag 0 from TextNode start: 0, length: 10, rect: [8,109 72.421875x17] baseline: 13.296875 frag 0 from TextNode start: 0, length: 10, rect: [8,109 72.203125x17] baseline: 13.296875
"well: 0, 0" "well: 8, 8"
frag 1 from TextNode start: 11, length: 13, rect: [8,126 96.765625x17] baseline: 13.296875 frag 1 from TextNode start: 11, length: 13, rect: [8,126 95.359375x17] baseline: 13.296875
"hello: 36, 25" "hello: 44, 33"
frag 2 from TextNode start: 25, length: 15, rect: [8,143 113.65625x17] baseline: 13.296875 frag 2 from TextNode start: 25, length: 15, rect: [8,143 113.65625x17] baseline: 13.296875
"friends: 45, 25" "friends: 45, 25"
TextNode <#text> TextNode <#text>

View file

@ -1,2 +1,2 @@
Top: 0 Top: 16
Left: 0 Left: 8

View file

@ -260,14 +260,20 @@ int HTMLElement::offset_top() const
if (!layout_node()) if (!layout_node())
return 0; return 0;
CSSPixels top_border_edge_of_element;
if (paintable()->is_paintable_box()) {
top_border_edge_of_element = paintable_box()->absolute_border_box_rect().y();
} else {
top_border_edge_of_element = paintable()->box_type_agnostic_position().y();
}
// 2. If the offsetParent of the element is null // 2. If the offsetParent of the element is null
// return the y-coordinate of the top border edge of the first CSS layout box associated with the element, // return the y-coordinate of the top border edge of the first CSS layout box associated with the element,
// relative to the initial containing block origin, // relative to the initial containing block origin,
// ignoring any transforms that apply to the element and its ancestors, and terminate this algorithm. // ignoring any transforms that apply to the element and its ancestors, and terminate this algorithm.
auto offset_parent = this->offset_parent(); auto offset_parent = this->offset_parent();
if (!offset_parent || !offset_parent->layout_node()) { if (!offset_parent || !offset_parent->layout_node()) {
auto position = paintable()->box_type_agnostic_position(); return top_border_edge_of_element.to_int();
return position.y().to_int();
} }
// 3. Return the result of subtracting the y-coordinate of the top padding edge // 3. Return the result of subtracting the y-coordinate of the top padding edge
@ -275,9 +281,19 @@ int HTMLElement::offset_top() const
// from the y-coordinate of the top border edge of the first box associated with the element, // from the y-coordinate of the top border edge of the first box associated with the element,
// relative to the initial containing block origin, // relative to the initial containing block origin,
// ignoring any transforms that apply to the element and its ancestors. // ignoring any transforms that apply to the element and its ancestors.
auto offset_parent_position = offset_parent->paintable()->box_type_agnostic_position();
auto position = paintable()->box_type_agnostic_position(); // NOTE: We give special treatment to the body element to match other browsers.
return position.y().to_int() - offset_parent_position.y().to_int(); // Spec bug: https://github.com/w3c/csswg-drafts/issues/10549
CSSPixels top_padding_edge_of_offset_parent;
if (offset_parent->is_html_body_element() && !offset_parent->paintable()->is_positioned()) {
top_padding_edge_of_offset_parent = 0;
} else if (offset_parent->paintable()->is_paintable_box()) {
top_padding_edge_of_offset_parent = offset_parent->paintable_box()->absolute_padding_box_rect().y();
} else {
top_padding_edge_of_offset_parent = offset_parent->paintable()->box_type_agnostic_position().y();
}
return (top_border_edge_of_element - top_padding_edge_of_offset_parent).to_int();
} }
// https://www.w3.org/TR/cssom-view-1/#dom-htmlelement-offsetleft // https://www.w3.org/TR/cssom-view-1/#dom-htmlelement-offsetleft
@ -293,14 +309,20 @@ int HTMLElement::offset_left() const
if (!layout_node()) if (!layout_node())
return 0; return 0;
CSSPixels left_border_edge_of_element;
if (paintable()->is_paintable_box()) {
left_border_edge_of_element = paintable_box()->absolute_border_box_rect().x();
} else {
left_border_edge_of_element = paintable()->box_type_agnostic_position().x();
}
// 2. If the offsetParent of the element is null // 2. If the offsetParent of the element is null
// return the x-coordinate of the left border edge of the first CSS layout box associated with the element, // return the x-coordinate of the left border edge of the first CSS layout box associated with the element,
// relative to the initial containing block origin, // relative to the initial containing block origin,
// ignoring any transforms that apply to the element and its ancestors, and terminate this algorithm. // ignoring any transforms that apply to the element and its ancestors, and terminate this algorithm.
auto offset_parent = this->offset_parent(); auto offset_parent = this->offset_parent();
if (!offset_parent || !offset_parent->layout_node()) { if (!offset_parent || !offset_parent->layout_node()) {
auto position = paintable()->box_type_agnostic_position(); return left_border_edge_of_element.to_int();
return position.x().to_int();
} }
// 3. Return the result of subtracting the x-coordinate of the left padding edge // 3. Return the result of subtracting the x-coordinate of the left padding edge
@ -308,9 +330,19 @@ int HTMLElement::offset_left() const
// from the x-coordinate of the left border edge of the first CSS layout box associated with the element, // from the x-coordinate of the left border edge of the first CSS layout box associated with the element,
// relative to the initial containing block origin, // relative to the initial containing block origin,
// ignoring any transforms that apply to the element and its ancestors. // ignoring any transforms that apply to the element and its ancestors.
auto offset_parent_position = offset_parent->paintable()->box_type_agnostic_position();
auto position = paintable()->box_type_agnostic_position(); // NOTE: We give special treatment to the body element to match other browsers.
return position.x().to_int() - offset_parent_position.x().to_int(); // Spec bug: https://github.com/w3c/csswg-drafts/issues/10549
CSSPixels left_padding_edge_of_offset_parent;
if (offset_parent->is_html_body_element() && !offset_parent->paintable()->is_positioned()) {
left_padding_edge_of_offset_parent = 0;
} else if (offset_parent->paintable()->is_paintable_box()) {
left_padding_edge_of_offset_parent = offset_parent->paintable_box()->absolute_padding_box_rect().x();
} else {
left_padding_edge_of_offset_parent = offset_parent->paintable()->box_type_agnostic_position().x();
}
return (left_border_edge_of_element - left_padding_edge_of_offset_parent).to_int();
} }
// https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetwidth // https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetwidth