LibWeb: Support inserting non-inline elements into inline elements

Our layout tree requires that all containers either have inline or
non-inline children. In order to support the layout of non-inline
elements inside inline elements, we need to do a bit of tree
restructuring. It effectively simulates temporarily closing all inline
nodes, appending the block element, and resumes appending to the last
open inline node.

The acid1.txt expectation needed to be updated to reflect the fact that
we now hoist its <p> elements out of the inline <form> they were in.
Visually, the before and after situations for acid1.html are identical.
This commit is contained in:
Jelle Raaijmakers 2025-01-15 16:37:30 +01:00 committed by Jelle Raaijmakers
parent 7eb4f3da37
commit 336684bc5c
Notes: github-actions[bot] 2025-01-23 08:34:24 +00:00
18 changed files with 520 additions and 145 deletions

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -463,7 +464,7 @@ int HTMLElement::offset_top() const
if (!paintable_box())
return 0;
CSSPixels top_border_edge_of_element = paintable_box()->absolute_border_box_rect().y();
CSSPixels top_border_edge_of_element = paintable_box()->absolute_united_border_box_rect().y();
// 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,
@ -487,7 +488,7 @@ int HTMLElement::offset_top() const
if (offset_parent->is_html_body_element() && !offset_parent->paintable_box()->is_positioned()) {
top_padding_edge_of_offset_parent = 0;
} else {
top_padding_edge_of_offset_parent = offset_parent->paintable_box()->absolute_padding_box_rect().y();
top_padding_edge_of_offset_parent = offset_parent->paintable_box()->absolute_united_padding_box_rect().y();
}
return (top_border_edge_of_element - top_padding_edge_of_offset_parent).to_int();
}
@ -505,7 +506,7 @@ int HTMLElement::offset_left() const
if (!paintable_box())
return 0;
CSSPixels left_border_edge_of_element = paintable_box()->absolute_border_box_rect().x();
CSSPixels left_border_edge_of_element = paintable_box()->absolute_united_border_box_rect().x();
// 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,
@ -529,7 +530,7 @@ int HTMLElement::offset_left() const
if (offset_parent->is_html_body_element() && !offset_parent->paintable_box()->is_positioned()) {
left_padding_edge_of_offset_parent = 0;
} else {
left_padding_edge_of_offset_parent = offset_parent->paintable_box()->absolute_padding_box_rect().x();
left_padding_edge_of_offset_parent = offset_parent->paintable_box()->absolute_united_padding_box_rect().x();
}
return (left_border_edge_of_element - left_padding_edge_of_offset_parent).to_int();
}
@ -540,13 +541,17 @@ int HTMLElement::offset_width() const
// NOTE: Ensure that layout is up-to-date before looking at metrics.
const_cast<DOM::Document&>(document()).update_layout();
// 1. If the element does not have any associated CSS layout box return zero and terminate this algorithm.
if (!paintable_box())
// 1. If the element does not have any associated box return zero and terminate this algorithm.
auto const* box = paintable_box();
if (!box)
return 0;
// 2. Return the width of the axis-aligned bounding box of the border boxes of all fragments generated by the elements principal box,
// ignoring any transforms that apply to the element and its ancestors.
return paintable_box()->border_box_width().to_int();
// 2. Return the unscaled width of the axis-aligned bounding box of the border boxes of all fragments generated by
// the elements principal box, ignoring any transforms that apply to the element and its ancestors.
//
// If the elements principal box is an inline-level box which was "split" by a block-level descendant, also
// include fragments generated by the block-level descendants, unless they are zero width or height.
return box->absolute_united_border_box_rect().width().to_int();
}
// https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetheight
@ -555,13 +560,17 @@ int HTMLElement::offset_height() const
// NOTE: Ensure that layout is up-to-date before looking at metrics.
const_cast<DOM::Document&>(document()).update_layout();
// 1. If the element does not have any associated CSS layout box return zero and terminate this algorithm.
if (!paintable_box())
// 1. If the element does not have any associated box return zero and terminate this algorithm.
auto const* box = paintable_box();
if (!box)
return 0;
// 2. Return the height of the axis-aligned bounding box of the border boxes of all fragments generated by the elements principal box,
// ignoring any transforms that apply to the element and its ancestors.
return paintable_box()->border_box_height().to_int();
// 2. Return the unscaled height of the axis-aligned bounding box of the border boxes of all fragments generated by
// the elements principal box, ignoring any transforms that apply to the element and its ancestors.
//
// If the elements principal box is an inline-level box which was "split" by a block-level descendant, also
// include fragments generated by the block-level descendants, unless they are zero width or height.
return box->absolute_united_border_box_rect().height().to_int();
}
// https://html.spec.whatwg.org/multipage/links.html#cannot-navigate