mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 04:09:13 +00:00
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:
parent
7eb4f3da37
commit
336684bc5c
Notes:
github-actions[bot]
2025-01-23 08:34:24 +00:00
Author: https://github.com/gmta
Commit: 336684bc5c
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3276
Reviewed-by: https://github.com/kalenikaliaksandr ✅
18 changed files with 520 additions and 145 deletions
|
@ -2,6 +2,7 @@
|
|||
* Copyright (c) 2022-2023, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022-2023, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -17,7 +18,6 @@
|
|||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Layout/BlockContainer.h>
|
||||
#include <LibWeb/Layout/InlineNode.h>
|
||||
#include <LibWeb/Layout/Viewport.h>
|
||||
#include <LibWeb/Painting/BackgroundPainting.h>
|
||||
#include <LibWeb/Painting/PaintableBox.h>
|
||||
#include <LibWeb/Painting/SVGPaintable.h>
|
||||
|
@ -214,6 +214,34 @@ CSSPixelRect PaintableBox::compute_absolute_paint_rect() const
|
|||
return rect;
|
||||
}
|
||||
|
||||
CSSPixelRect PaintableBox::absolute_padding_box_rect() const
|
||||
{
|
||||
auto absolute_rect = this->absolute_rect();
|
||||
CSSPixelRect rect;
|
||||
rect.set_x(absolute_rect.x() - box_model().padding.left);
|
||||
rect.set_width(content_width() + box_model().padding.left + box_model().padding.right);
|
||||
rect.set_y(absolute_rect.y() - box_model().padding.top);
|
||||
rect.set_height(content_height() + box_model().padding.top + box_model().padding.bottom);
|
||||
return rect;
|
||||
}
|
||||
|
||||
CSSPixelRect PaintableBox::absolute_border_box_rect() const
|
||||
{
|
||||
auto padded_rect = this->absolute_padding_box_rect();
|
||||
CSSPixelRect rect;
|
||||
auto use_collapsing_borders_model = override_borders_data().has_value();
|
||||
// Implement the collapsing border model https://www.w3.org/TR/CSS22/tables.html#collapsing-borders.
|
||||
auto border_top = use_collapsing_borders_model ? round(box_model().border.top / 2) : box_model().border.top;
|
||||
auto border_bottom = use_collapsing_borders_model ? round(box_model().border.bottom / 2) : box_model().border.bottom;
|
||||
auto border_left = use_collapsing_borders_model ? round(box_model().border.left / 2) : box_model().border.left;
|
||||
auto border_right = use_collapsing_borders_model ? round(box_model().border.right / 2) : box_model().border.right;
|
||||
rect.set_x(padded_rect.x() - border_left);
|
||||
rect.set_width(padded_rect.width() + border_left + border_right);
|
||||
rect.set_y(padded_rect.y() - border_top);
|
||||
rect.set_height(padded_rect.height() + border_top + border_bottom);
|
||||
return rect;
|
||||
}
|
||||
|
||||
CSSPixelRect PaintableBox::absolute_paint_rect() const
|
||||
{
|
||||
if (!m_absolute_paint_rect.has_value())
|
||||
|
@ -221,6 +249,51 @@ CSSPixelRect PaintableBox::absolute_paint_rect() const
|
|||
return *m_absolute_paint_rect;
|
||||
}
|
||||
|
||||
template<typename Callable>
|
||||
static CSSPixelRect united_rect_for_continuation_chain(PaintableBox const& start, Callable get_rect)
|
||||
{
|
||||
// Combine the absolute rects of all paintable boxes of all nodes in the continuation chain. Without this, we
|
||||
// calculate the wrong rect for inline nodes that were split because of block elements.
|
||||
Optional<CSSPixelRect> result;
|
||||
|
||||
// FIXME: instead of walking the continuation chain in the layout tree, also keep track of this chain in the
|
||||
// painting tree so we can skip visiting the layout nodes altogether.
|
||||
for (auto const* node = &start.layout_node_with_style_and_box_metrics(); node; node = node->continuation_of_node()) {
|
||||
for (auto const& paintable : node->paintables()) {
|
||||
if (!is<PaintableBox>(paintable))
|
||||
continue;
|
||||
auto const& paintable_box = static_cast<PaintableBox const&>(paintable);
|
||||
auto paintable_border_box_rect = get_rect(paintable_box);
|
||||
if (!result.has_value())
|
||||
result = paintable_border_box_rect;
|
||||
else if (!paintable_border_box_rect.is_empty())
|
||||
result->unite(paintable_border_box_rect);
|
||||
}
|
||||
}
|
||||
return result.value_or({});
|
||||
}
|
||||
|
||||
CSSPixelRect PaintableBox::absolute_united_border_box_rect() const
|
||||
{
|
||||
return united_rect_for_continuation_chain(*this, [](auto const& paintable_box) {
|
||||
return paintable_box.absolute_border_box_rect();
|
||||
});
|
||||
}
|
||||
|
||||
CSSPixelRect PaintableBox::absolute_united_content_rect() const
|
||||
{
|
||||
return united_rect_for_continuation_chain(*this, [](auto const& paintable_box) {
|
||||
return paintable_box.absolute_rect();
|
||||
});
|
||||
}
|
||||
|
||||
CSSPixelRect PaintableBox::absolute_united_padding_box_rect() const
|
||||
{
|
||||
return united_rect_for_continuation_chain(*this, [](auto const& paintable_box) {
|
||||
return paintable_box.absolute_padding_box_rect();
|
||||
});
|
||||
}
|
||||
|
||||
Optional<CSSPixelRect> PaintableBox::get_clip_rect() const
|
||||
{
|
||||
auto clip = computed_values().clip();
|
||||
|
@ -396,17 +469,18 @@ void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
|
|||
}
|
||||
|
||||
if (phase == PaintPhase::Overlay && layout_node().document().inspected_layout_node() == &layout_node_with_style_and_box_metrics()) {
|
||||
auto content_rect = absolute_rect();
|
||||
|
||||
auto margin_box = box_model().margin_box();
|
||||
CSSPixelRect margin_rect;
|
||||
margin_rect.set_x(absolute_x() - margin_box.left);
|
||||
margin_rect.set_width(content_width() + margin_box.left + margin_box.right);
|
||||
margin_rect.set_y(absolute_y() - margin_box.top);
|
||||
margin_rect.set_height(content_height() + margin_box.top + margin_box.bottom);
|
||||
|
||||
auto border_rect = absolute_border_box_rect();
|
||||
auto padding_rect = absolute_padding_box_rect();
|
||||
auto content_rect = absolute_united_content_rect();
|
||||
auto margin_rect = united_rect_for_continuation_chain(*this, [](PaintableBox const& box) {
|
||||
auto margin_box = box.box_model().margin_box();
|
||||
return CSSPixelRect {
|
||||
box.absolute_x() - margin_box.left,
|
||||
box.absolute_y() - margin_box.top,
|
||||
box.content_width() + margin_box.left + margin_box.right,
|
||||
box.content_height() + margin_box.top + margin_box.bottom,
|
||||
};
|
||||
});
|
||||
auto border_rect = absolute_united_border_box_rect();
|
||||
auto padding_rect = absolute_united_padding_box_rect();
|
||||
|
||||
auto paint_inspector_rect = [&](CSSPixelRect const& rect, Color color) {
|
||||
auto device_rect = context.enclosing_device_rect(rect).to_type<int>();
|
||||
|
@ -895,7 +969,7 @@ TraversalDecision PaintableBox::hit_test(CSSPixelPoint position, HitTestType typ
|
|||
if (!absolute_border_box_rect().contains(position_adjusted_by_scroll_offset.x(), position_adjusted_by_scroll_offset.y()))
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
if (!visible_for_hit_testing())
|
||||
if (!absolute_border_box_rect().contains(position_adjusted_by_scroll_offset))
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
return callback(HitTestResult { const_cast<PaintableBox&>(*this) });
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue