mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
LibWeb: Handle height:auto separately for BFC root vs other block boxes
I was wrong in 56df05ae44
, there are
situations where floating children should not affect the auto height of
their parent.
It turns out we were using the "height:auto for BFC roots" algorithm for
all height:auto blocks. This patch fixes that by splitting it into two
separate functions, and implementing most of the two different variants.
Note that we don't support vertical margin collapsing here yet.
Thanks to Tim for noticing the error! :^)
This commit is contained in:
parent
4fa270d81f
commit
fa43a4118e
Notes:
sideshowbarker
2024-07-17 23:00:03 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/fa43a4118e
2 changed files with 62 additions and 10 deletions
|
@ -208,21 +208,68 @@ static Gfx::FloatSize solve_replaced_size_constraint(FormattingState const& stat
|
|||
|
||||
float FormattingContext::compute_auto_height_for_block_level_element(FormattingState const& state, Box const& box)
|
||||
{
|
||||
if (creates_block_formatting_context(box))
|
||||
return compute_auto_height_for_block_formatting_context_root(state, verify_cast<BlockContainer>(box));
|
||||
|
||||
auto const& box_state = state.get(box);
|
||||
|
||||
// https://www.w3.org/TR/CSS22/visudet.html#normal-block
|
||||
// 10.6.3 Block-level non-replaced elements in normal flow when 'overflow' computes to 'visible'
|
||||
|
||||
// The element's height is the distance from its top content edge to the first applicable of the following:
|
||||
|
||||
// 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
|
||||
if (box.children_are_inline() && !box_state.line_boxes.is_empty()) {
|
||||
// Find the top content edge (if negative).
|
||||
float top_content_edge = 0;
|
||||
for (auto const& fragment : box_state.line_boxes.first().fragments()) {
|
||||
float fragment_top_content_edge = fragment.offset().y();
|
||||
if (fragment_top_content_edge < top_content_edge)
|
||||
top_content_edge = fragment_top_content_edge;
|
||||
}
|
||||
return box_state.line_boxes.last().bottom() - top_content_edge;
|
||||
}
|
||||
|
||||
// 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin does not collapse with the element's bottom margin
|
||||
if (!box.children_are_inline()) {
|
||||
// FIXME: Don't walk all children from first to last here!
|
||||
Box const* last_in_flow_child = nullptr;
|
||||
box.for_each_child_of_type<Box>([&](Box const& child_box) {
|
||||
bool is_in_flow = !child_box.is_floating() && !child_box.is_absolutely_positioned();
|
||||
if (is_in_flow)
|
||||
last_in_flow_child = &child_box;
|
||||
});
|
||||
if (last_in_flow_child) {
|
||||
auto const& child_state = state.get(*last_in_flow_child);
|
||||
// FIXME: Handle margin collapsing.
|
||||
return child_state.offset.y() + child_state.content_height + child_state.margin_box_bottom();
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
|
||||
|
||||
// 4. zero, otherwise
|
||||
return 0;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/CSS22/visudet.html#root-height
|
||||
float FormattingContext::compute_auto_height_for_block_formatting_context_root(FormattingState const& state, BlockContainer const& root)
|
||||
{
|
||||
// 10.6.7 'Auto' heights for block formatting context roots
|
||||
Optional<float> top;
|
||||
Optional<float> bottom;
|
||||
|
||||
if (box.children_are_inline()) {
|
||||
if (root.children_are_inline()) {
|
||||
// If it only has inline-level children, the height is the distance between
|
||||
// the top content edge and the bottom of the bottommost line box.
|
||||
auto const& block_container = verify_cast<BlockContainer>(box);
|
||||
auto const& line_boxes = state.get(block_container).line_boxes;
|
||||
auto const& line_boxes = state.get(root).line_boxes;
|
||||
top = 0;
|
||||
if (!line_boxes.is_empty()) {
|
||||
// Find the top edge (if negative).
|
||||
for (auto const& fragment : line_boxes.first().fragments()) {
|
||||
float fragment_top = fragment.offset().y() - fragment.border_box_top();
|
||||
if (!top.has_value() || fragment_top < *top)
|
||||
top = fragment_top;
|
||||
float fragment_top_content_edge = fragment.offset().y();
|
||||
if (!top.has_value() || fragment_top_content_edge < *top)
|
||||
top = fragment_top_content_edge;
|
||||
}
|
||||
// Find the bottom edge.
|
||||
bottom = line_boxes.last().bottom();
|
||||
|
@ -231,10 +278,15 @@ float FormattingContext::compute_auto_height_for_block_level_element(FormattingS
|
|||
// If it has block-level children, the height is the distance between
|
||||
// the top margin-edge of the topmost block-level child box
|
||||
// and the bottom margin-edge of the bottommost block-level child box.
|
||||
box.for_each_child_of_type<Box>([&](Layout::Box& child_box) {
|
||||
root.for_each_child_of_type<Box>([&](Layout::Box& child_box) {
|
||||
// Absolutely positioned children are ignored,
|
||||
// and relatively positioned boxes are considered without their offset.
|
||||
// Note that the child box may be an anonymous block box.
|
||||
if (child_box.is_absolutely_positioned())
|
||||
return IterationDecision::Continue;
|
||||
if ((box.computed_values().overflow_y() == CSS::Overflow::Visible) && child_box.is_floating())
|
||||
|
||||
// FIXME: This doesn't look right.
|
||||
if ((root.computed_values().overflow_y() == CSS::Overflow::Visible) && child_box.is_floating())
|
||||
return IterationDecision::Continue;
|
||||
|
||||
auto const& child_box_state = state.get(child_box);
|
||||
|
@ -253,12 +305,11 @@ float FormattingContext::compute_auto_height_for_block_level_element(FormattingS
|
|||
// In addition, if the element has any floating descendants
|
||||
// whose bottom margin edge is below the element's bottom content edge,
|
||||
// then the height is increased to include those edges.
|
||||
box.for_each_child_of_type<Box>([&](Layout::Box& child_box) {
|
||||
root.for_each_child_of_type<Box>([&](Layout::Box& child_box) {
|
||||
if (!child_box.is_floating())
|
||||
return IterationDecision::Continue;
|
||||
|
||||
auto const& child_box_state = state.get(child_box);
|
||||
|
||||
float child_box_bottom = child_box_state.offset.y() + child_box_state.content_height + child_box_state.margin_box_bottom();
|
||||
|
||||
if (!bottom.has_value() || child_box_bottom > bottom.value())
|
||||
|
|
|
@ -58,6 +58,7 @@ protected:
|
|||
|
||||
static float tentative_width_for_replaced_element(FormattingState const&, ReplacedBox const&, CSS::Length const& width);
|
||||
static float tentative_height_for_replaced_element(FormattingState const&, ReplacedBox const&, CSS::Length const& height);
|
||||
static float compute_auto_height_for_block_formatting_context_root(FormattingState const&, BlockContainer const&);
|
||||
static float compute_auto_height_for_block_level_element(FormattingState const&, Box const&);
|
||||
|
||||
ShrinkToFitResult calculate_shrink_to_fit_widths(Box const&);
|
||||
|
|
Loading…
Reference in a new issue