summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2022-03-18 14:44:36 +0100
committerAndreas Kling <kling@serenityos.org>2022-03-18 15:18:48 +0100
commit39b7fbfeb9e469b4089f5424dc61aa074023bb5a (patch)
tree4170b708d3cefbef741e07bdc221e462a03dec1f
parent28642de6ed75133712d9936180874a4bb2ee989e (diff)
downloadserenity-39b7fbfeb9e469b4089f5424dc61aa074023bb5a.zip
LibWeb: Rewrite CSS float implementation to use offset-from-edge
The previous implementation used relative X offsets for both left and right-side floats. This made right-side floats super awkward, since we could only determine their X position once the width of the BFC root was known, and for BFC roots with automatic width, this was not even working at all most of the time. This patch changes the way we deal with floats so that BFC keeps track of the offset-from-edge for each float. The offset is the distance from the BFC root edge (left or right, depending on float direction) to the "innermost" margin edge of the floating box. Floating box are now laid out in two passes: while going through the normal flow layout, we put floats in their *static* position (i.e the position they would have occupied if they weren't floating) and then update the Y position value to the final one. The second pass occurs later on, when the BFC root has had its width assigned by the parent context. Once we know the root width, we can set the X position value of floating boxes. (Because the X position of right-side floats is relative to the right edge of the BFC root.)
-rw-r--r--Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp230
-rw-r--r--Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h43
-rw-r--r--Userland/Libraries/LibWeb/Layout/FormattingContext.cpp8
-rw-r--r--Userland/Libraries/LibWeb/Layout/FormattingContext.h4
-rw-r--r--Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp24
-rw-r--r--Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h3
-rw-r--r--Userland/Libraries/LibWeb/Layout/LineBuilder.cpp16
7 files changed, 245 insertions, 83 deletions
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
index 466d5b000d..6a0fb41d1d 100644
--- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
@@ -55,9 +55,26 @@ void BlockFormattingContext::parent_context_did_dimension_child_root_box()
{
m_was_notified_after_parent_dimensioned_my_root_box = true;
+ // Now that we know the width of our root box, we can position floats horizontally.
+ auto root_width = m_state.get(root()).content_width;
+
+ // Left-side floats: offset_from_edge is from left edge (0) to left content edge of floating_box.
+ for (auto& floating_box : m_left_floats.all_boxes) {
+ auto& box_state = m_state.get_mutable(floating_box->box);
+ box_state.offset.set_x(floating_box->offset_from_edge);
+ }
+
+ // Right-side floats: offset_from_edge is from right edge (root_width) to the left content edge of floating_box.
+ for (auto& floating_box : m_right_floats.all_boxes) {
+ auto& box_state = m_state.get_mutable(floating_box->box);
+ box_state.offset.set_x(root_width - floating_box->offset_from_edge);
+ }
+
+ // We can also layout absolutely positioned boxes within this BFC.
for (auto& box : m_absolutely_positioned_boxes)
layout_absolutely_positioned_element(box);
+ // FIXME: Transforms should be a painting concept, not a layout concept.
apply_transformations_to_children(root());
}
@@ -96,7 +113,7 @@ void BlockFormattingContext::apply_transformations_to_children(Box const& box)
});
}
-void BlockFormattingContext::compute_width(Box const& box)
+void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mode)
{
if (box.is_absolutely_positioned()) {
compute_width_for_absolutely_positioned_element(box);
@@ -113,7 +130,7 @@ void BlockFormattingContext::compute_width(Box const& box)
}
if (box.is_floating()) {
- compute_width_for_floating_box(box);
+ compute_width_for_floating_box(box, layout_mode);
return;
}
@@ -238,12 +255,26 @@ void BlockFormattingContext::compute_width(Box const& box)
box_state.padding_right = padding_right.to_px(box);
}
-void BlockFormattingContext::compute_width_for_floating_box(Box const& box)
+void BlockFormattingContext::compute_width_for_floating_box(Box const& box, LayoutMode layout_mode)
{
// 10.3.5 Floating, non-replaced elements
auto& computed_values = box.computed_values();
auto& containing_block = *box.containing_block();
- float width_of_containing_block = m_state.get(containing_block).content_width;
+
+ float width_of_containing_block = 0;
+
+ switch (layout_mode) {
+ case LayoutMode::Default:
+ width_of_containing_block = m_state.get(containing_block).content_width;
+ break;
+ case LayoutMode::AllPossibleLineBreaks:
+ width_of_containing_block = 0;
+ break;
+ case LayoutMode::OnlyRequiredLineBreaks:
+ width_of_containing_block = INFINITY;
+ break;
+ }
+
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto zero_value = CSS::Length::make_px(0);
@@ -362,7 +393,6 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer const& b
VERIFY(!block_container.children_are_inline());
float content_height = 0;
- float content_width = 0;
block_container.for_each_child_of_type<Box>([&](Box& child_box) {
auto& box_state = m_state.get_mutable(child_box);
@@ -377,11 +407,12 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer const& b
return IterationDecision::Continue;
if (child_box.is_floating()) {
- layout_floating_child(child_box, block_container);
+ layout_floating_box(child_box, block_container, layout_mode);
+ content_height = max(content_height, box_state.offset.y() + box_state.content_height + box_state.margin_box_bottom());
return IterationDecision::Continue;
}
- compute_width(child_box);
+ compute_width(child_box, layout_mode);
if (is<ReplacedBox>(child_box) || is<BlockContainer>(child_box))
place_block_level_element_in_normal_flow_vertically(child_box, block_container);
@@ -410,7 +441,6 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer const& b
}
content_height = max(content_height, box_state.offset.y() + box_state.content_height + box_state.margin_box_bottom());
- content_width = max(content_width, box_state.border_box_width());
if (independent_formatting_context)
independent_formatting_context->parent_context_did_dimension_child_root_box();
@@ -422,7 +452,7 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer const& b
auto& width = block_container.computed_values().width();
if (!width.has_value() || (width->is_length() && width->length().is_auto())) {
auto& block_container_state = m_state.get_mutable(block_container);
- block_container_state.content_width = content_width;
+ block_container_state.content_width = greatest_child_width(block_container);
}
}
}
@@ -487,15 +517,14 @@ void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically
}
auto clear_floating_boxes = [&](FloatSideData& float_side) {
- if (!float_side.boxes.is_empty()) {
+ if (!float_side.current_boxes.is_empty()) {
float clearance_y = 0;
- for (auto const& floating_box : float_side.boxes) {
- auto const& floating_box_state = m_state.get(floating_box);
+ for (auto const& floating_box : float_side.current_boxes) {
+ auto const& floating_box_state = m_state.get(floating_box.box);
clearance_y = max(clearance_y, floating_box_state.offset.y() + floating_box_state.border_box_height());
}
y = max(y, clearance_y);
- float_side.boxes.clear();
- float_side.y_offset = 0;
+ float_side.clear();
}
};
@@ -514,10 +543,18 @@ void BlockFormattingContext::place_block_level_element_in_normal_flow_horizontal
auto const& containing_block_state = m_state.get(containing_block);
float x = 0;
+ float available_width_within_containing_block = containing_block_state.content_width;
+
+ if ((!m_left_floats.current_boxes.is_empty() || !m_right_floats.current_boxes.is_empty())
+ && creates_block_formatting_context(child_box)) {
+ available_width_within_containing_block -= m_left_floats.current_width + m_right_floats.current_width;
+ x += m_left_floats.current_width;
+ }
+
if (containing_block.computed_values().text_align() == CSS::TextAlign::LibwebCenter) {
- x = (containing_block_state.content_width / 2) - box_state.content_width / 2;
+ x += (available_width_within_containing_block / 2) - box_state.content_width / 2;
} else {
- x = box_state.margin_box_left() + box_state.offset_left;
+ x += box_state.margin_box_left() + box_state.offset_left;
}
box_state.offset = Gfx::FloatPoint { x, box_state.offset.y() };
@@ -554,15 +591,27 @@ void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_m
}
}
-void BlockFormattingContext::layout_floating_child(Box const& box, BlockContainer const& containing_block)
+void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer const& containing_block, LayoutMode layout_mode)
{
VERIFY(box.is_floating());
auto& box_state = m_state.get_mutable(box);
- auto containing_block_content_width = m_state.get(containing_block).content_width;
+ float width_of_containing_block = 0;
+
+ switch (layout_mode) {
+ case LayoutMode::Default:
+ width_of_containing_block = m_state.get(containing_block).content_width;
+ break;
+ case LayoutMode::AllPossibleLineBreaks:
+ width_of_containing_block = 0;
+ break;
+ case LayoutMode::OnlyRequiredLineBreaks:
+ width_of_containing_block = INFINITY;
+ break;
+ }
- compute_width(box);
- (void)layout_inside(box, LayoutMode::Default);
+ compute_width(box, layout_mode);
+ (void)layout_inside(box, layout_mode);
compute_height(box, m_state);
// First we place the box normally (to get the right y coordinate.)
@@ -572,85 +621,98 @@ void BlockFormattingContext::layout_floating_child(Box const& box, BlockContaine
auto float_box = [&](FloatSide side, FloatSideData& side_data) {
auto first_edge = [&](FormattingState::NodeState const& thing) { return side == FloatSide::Left ? thing.margin_left : thing.margin_right; };
auto second_edge = [&](FormattingState::NodeState const& thing) { return side == FloatSide::Right ? thing.margin_left : thing.margin_right; };
- auto edge_of_containing_block = [&] {
- if (side == FloatSide::Left)
- return box_state.margin_box_left();
- return containing_block_content_width - box_state.margin_box_right() - box_state.content_width;
- };
// Then we float it to the left or right.
+ float offset_from_edge = 0;
+ auto float_to_edge = [&] {
+ if (side == FloatSide::Left)
+ offset_from_edge = box_state.margin_box_left();
+ else
+ offset_from_edge = box_state.content_width + box_state.margin_box_right();
+ };
+
auto box_in_root_rect = margin_box_rect_in_ancestor_coordinate_space(box, root(), m_state);
float y_in_root = box_in_root_rect.y();
-
- float x = 0;
float y = box_state.offset.y();
- if (side_data.boxes.is_empty()) {
+ if (side_data.current_boxes.is_empty()) {
// This is the first floating box on this side. Go all the way to the edge.
- x = edge_of_containing_block();
+ float_to_edge();
side_data.y_offset = 0;
} else {
- auto& previous_box = side_data.boxes.last();
- auto const& previous_box_state = m_state.get(previous_box);
- auto previous_rect = margin_box_rect_in_ancestor_coordinate_space(previous_box, root(), m_state);
+ auto& previous_box = side_data.current_boxes.last();
+ auto const& previous_box_state = m_state.get(previous_box.box);
auto margin_collapsed_with_previous = max(
second_edge(previous_box_state),
first_edge(box_state));
- float wanted_x = 0;
+ float wanted_offset_from_edge = 0;
bool fits_on_line = false;
if (side == FloatSide::Left) {
- auto previous_right_border_edge = previous_box_state.offset.x()
- + previous_box_state.content_width
- + previous_box_state.padding_right
- + previous_box_state.border_right
+ auto previous_right_edge = side_data.current_width
+ - previous_box_state.margin_right
+ margin_collapsed_with_previous;
- wanted_x = previous_right_border_edge + box_state.border_left + box_state.padding_left;
- fits_on_line = (wanted_x + box_state.content_width + box_state.padding_right + box_state.border_right + box_state.margin_right) <= containing_block_content_width;
+ wanted_offset_from_edge = previous_right_edge + box_state.border_left + box_state.padding_left;
+ fits_on_line = (wanted_offset_from_edge + box_state.content_width + box_state.padding_right + box_state.border_right + box_state.margin_right) <= width_of_containing_block;
} else {
- auto previous_left_border_edge = previous_box_state.offset.x()
- - previous_box_state.padding_left
- - previous_box_state.border_left
- - margin_collapsed_with_previous;
+ auto previous_left_edge = side_data.current_width
+ - previous_box_state.margin_left
+ + margin_collapsed_with_previous;
- wanted_x = previous_left_border_edge - box_state.border_right - box_state.padding_right - box_state.content_width;
- fits_on_line = (wanted_x - box_state.padding_left - box_state.border_left - box_state.margin_left) >= 0;
+ wanted_offset_from_edge = previous_left_edge + box_state.border_right + box_state.padding_right + box_state.content_width;
+ fits_on_line = (wanted_offset_from_edge - box_state.padding_left - box_state.border_left - box_state.margin_left) >= 0;
}
if (fits_on_line) {
+ auto const previous_rect = margin_box_rect_in_ancestor_coordinate_space(previous_box.box, root(), m_state);
if (previous_rect.contains_vertically(y_in_root + side_data.y_offset)) {
// This box touches another already floating box. Stack after others.
- x = wanted_x;
+ offset_from_edge = wanted_offset_from_edge;
} else {
// This box does not touch another floating box, go all the way to the edge.
- x = edge_of_containing_block();
+ float_to_edge();
// Also, forget all previous boxes floated to this side while since they're no longer relevant.
- side_data.boxes.clear();
+ side_data.clear();
}
} else {
// We ran out of horizontal space on this "float line", and need to break.
- x = edge_of_containing_block();
+ float_to_edge();
float lowest_border_edge = 0;
- for (auto const& box : side_data.boxes) {
- auto const& box_state = m_state.get(box);
+ for (auto const& box : side_data.current_boxes) {
+ auto const& box_state = m_state.get(box.box);
lowest_border_edge = max(lowest_border_edge, box_state.border_box_height());
}
side_data.y_offset += lowest_border_edge;
// Also, forget all previous boxes floated to this side while since they're no longer relevant.
- side_data.boxes.clear();
+ side_data.clear();
}
}
y += side_data.y_offset;
- side_data.boxes.append(box);
+ side_data.all_boxes.append(adopt_own(*new FloatingBox {
+ .box = box,
+ .offset_from_edge = offset_from_edge,
+ .top_margin_edge = y - box_state.margin_box_top(),
+ .bottom_margin_edge = y + box_state.content_height + box_state.margin_box_bottom(),
+ }));
+ side_data.current_boxes.append(*side_data.all_boxes.last());
+
+ if (side == FloatSide::Left) {
+ side_data.current_width = offset_from_edge + box_state.content_width + box_state.margin_box_right();
+ } else {
+ side_data.current_width = offset_from_edge + box_state.margin_box_left();
+ }
+ side_data.max_width = max(side_data.current_width, side_data.max_width);
- box_state.offset = Gfx::FloatPoint { x, y };
+ // NOTE: We don't set the X position here, that happens later, once we know the root block width.
+ // See parent_context_did_dimension_child_root_box() for that logic.
+ box_state.offset.set_y(y);
};
// Next, float to the left and/or right
@@ -692,29 +754,67 @@ void BlockFormattingContext::layout_list_item_marker(ListItemBox const& list_ite
list_item_state.content_height = marker_state.content_height;
}
-BlockFormattingContext::AvailableSpaceForLineInfo BlockFormattingContext::available_space_for_line(float y) const
+BlockFormattingContext::SpaceUsedByFloats BlockFormattingContext::space_used_by_floats(float y) const
{
- AvailableSpaceForLineInfo info;
+ SpaceUsedByFloats space_used_by_floats;
- for (auto const& floating_box : m_left_floats.boxes.in_reverse()) {
- auto rect = margin_box_rect_in_ancestor_coordinate_space(floating_box, root(), m_state);
+ for (auto const& floating_box : m_left_floats.current_boxes.in_reverse()) {
+ auto const& floating_box_state = m_state.get(floating_box.box);
+ // NOTE: The floating box is *not* in the final horizontal position yet, but the size and vertical position is valid.
+ auto rect = margin_box_rect_in_ancestor_coordinate_space(floating_box.box, root(), m_state);
if (rect.contains_vertically(y)) {
- info.left = rect.right() + 1;
+ space_used_by_floats.left = floating_box.offset_from_edge
+ + floating_box_state.content_width
+ + floating_box_state.margin_box_right();
break;
}
}
- info.right = m_state.get(root()).content_width;
-
- for (auto const& floating_box : m_right_floats.boxes.in_reverse()) {
- auto rect = margin_box_rect_in_ancestor_coordinate_space(floating_box, root(), m_state);
+ for (auto const& floating_box : m_right_floats.current_boxes.in_reverse()) {
+ auto const& floating_box_state = m_state.get(floating_box.box);
+ // NOTE: The floating box is *not* in the final horizontal position yet, but the size and vertical position is valid.
+ auto rect = margin_box_rect_in_ancestor_coordinate_space(floating_box.box, root(), m_state);
if (rect.contains_vertically(y)) {
- info.right = rect.left();
+ space_used_by_floats.right = floating_box.offset_from_edge
+ + floating_box_state.margin_box_left();
break;
}
}
- return info;
+ return space_used_by_floats;
+}
+
+float BlockFormattingContext::greatest_child_width(Box const& box)
+{
+ // Similar to FormattingContext::greatest_child_width()
+ // but this one takes floats into account!
+ float max_width = m_left_floats.max_width + m_right_floats.max_width;
+ if (box.children_are_inline()) {
+ for (auto const& line_box : m_state.get(verify_cast<BlockContainer>(box)).line_boxes) {
+ auto width_here = line_box.width();
+ float extra_width_from_left_floats = 0;
+ for (auto& left_float : m_left_floats.all_boxes) {
+ if (line_box.baseline() >= left_float->top_margin_edge || line_box.baseline() <= left_float->bottom_margin_edge) {
+ auto const& left_float_state = m_state.get(left_float->box);
+ extra_width_from_left_floats = max(extra_width_from_left_floats, left_float->offset_from_edge + left_float_state.content_width + left_float_state.margin_box_right());
+ }
+ }
+ float extra_width_from_right_floats = 0;
+ for (auto& right_float : m_right_floats.all_boxes) {
+ if (line_box.baseline() >= right_float->top_margin_edge || line_box.baseline() <= right_float->bottom_margin_edge) {
+ auto const& right_float_state = m_state.get(right_float->box);
+ extra_width_from_right_floats = max(extra_width_from_right_floats, right_float->offset_from_edge + right_float_state.margin_box_left());
+ }
+ }
+ width_here += extra_width_from_left_floats + extra_width_from_right_floats;
+ max_width = max(max_width, width_here);
+ }
+ } else {
+ box.for_each_child_of_type<Box>([&](auto& child) {
+ max_width = max(max_width, m_state.get(child).border_box_width());
+ });
+ }
+ return max_width;
}
}
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
index 933806c7c3..8924bc433a 100644
--- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
@@ -27,7 +27,7 @@ public:
auto const& right_side_floats() const { return m_right_floats; }
static float compute_theoretical_height(FormattingState const&, Box const&);
- void compute_width(Box const&);
+ void compute_width(Box const&, LayoutMode = LayoutMode::Default);
// https://www.w3.org/TR/css-display/#block-formatting-context-root
BlockContainer const& root() const { return static_cast<BlockContainer const&>(context_box()); }
@@ -38,12 +38,14 @@ public:
void add_absolutely_positioned_box(Box const& box) { m_absolutely_positioned_boxes.append(box); }
- AvailableSpaceForLineInfo available_space_for_line(float y) const;
+ SpaceUsedByFloats space_used_by_floats(float y) const;
+
+ virtual float greatest_child_width(Box const&) override;
private:
virtual bool is_block_formatting_context() const final { return true; }
- void compute_width_for_floating_box(Box const&);
+ void compute_width_for_floating_box(Box const&, LayoutMode);
void compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox const&);
@@ -56,7 +58,7 @@ private:
void place_block_level_element_in_normal_flow_horizontally(Box const& child_box, BlockContainer const&);
void place_block_level_element_in_normal_flow_vertically(Box const& child_box, BlockContainer const&);
- void layout_floating_child(Box const& child, BlockContainer const& containing_block);
+ void layout_floating_box(Box const& child, BlockContainer const& containing_block, LayoutMode);
void apply_transformations_to_children(Box const&);
@@ -67,9 +69,40 @@ private:
Right,
};
+ struct FloatingBox {
+ Box const& box;
+ // Offset from left/right edge to the left content edge of `box`.
+ float offset_from_edge { 0 };
+
+ // Top margin edge of `box`.
+ float top_margin_edge { 0 };
+
+ // Bottom margin edge of `box`.
+ float bottom_margin_edge { 0 };
+ };
+
struct FloatSideData {
- Vector<Box const&> boxes;
+ // Floating boxes currently accumulating on this side.
+ Vector<FloatingBox&> current_boxes;
+
+ // Combined width of boxes currently accumulating on this side.
+ // This is the innermost margin of the innermost floating box.
+ float current_width { 0 };
+
+ // Highest value of `m_current_width` we've seen.
+ float max_width { 0 };
+
+ // All floating boxes encountered thus far within this BFC.
+ Vector<NonnullOwnPtr<FloatingBox>> all_boxes;
+
+ // Current Y offset from BFC root top.
float y_offset { 0 };
+
+ void clear()
+ {
+ current_boxes.clear();
+ current_width = 0;
+ }
};
FloatSideData m_left_floats;
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
index 705d9844bb..4613e0b744 100644
--- a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
@@ -131,16 +131,16 @@ OwnPtr<FormattingContext> FormattingContext::layout_inside(Box const& child_box,
return independent_formatting_context;
}
-static float greatest_child_width(FormattingState const& state, Box const& box)
+float FormattingContext::greatest_child_width(Box const& box)
{
float max_width = 0;
if (box.children_are_inline()) {
- for (auto& child : state.get(verify_cast<BlockContainer>(box)).line_boxes) {
- max_width = max(max_width, child.width());
+ for (auto& line_box : m_state.get(verify_cast<BlockContainer>(box)).line_boxes) {
+ max_width = max(max_width, line_box.width());
}
} else {
box.for_each_child_of_type<Box>([&](auto& child) {
- max_width = max(max_width, state.get(child).border_box_width());
+ max_width = max(max_width, m_state.get(child).border_box_width());
});
}
return max_width;
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
index 75869089ec..160a1408aa 100644
--- a/Userland/Libraries/LibWeb/Layout/FormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
@@ -56,6 +56,8 @@ public:
float calculate_fit_content_height(Layout::Box const&, Optional<float> available_height) const;
float calculate_fit_content_width(Layout::Box const&, Optional<float> available_width) const;
+ virtual float greatest_child_width(Box const&);
+
protected:
FormattingContext(Type, FormattingState&, Box const&, FormattingContext* parent = nullptr);
@@ -65,7 +67,7 @@ protected:
OwnPtr<FormattingContext> layout_inside(Box const&, LayoutMode);
void compute_position(Box const&);
- struct AvailableSpaceForLineInfo {
+ struct SpaceUsedByFloats {
float left { 0 };
float right { 0 };
};
diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
index 36fd83ae13..1b12faf26a 100644
--- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
@@ -36,14 +36,30 @@ BlockFormattingContext const& InlineFormattingContext::parent() const
return static_cast<BlockFormattingContext const&>(*FormattingContext::parent());
}
-FormattingContext::AvailableSpaceForLineInfo InlineFormattingContext::available_space_for_line(float y) const
+float InlineFormattingContext::leftmost_x_offset_at(float y) const
{
// NOTE: Floats are relative to the BFC root box, not necessarily the containing block of this IFC.
auto box_in_root_rect = margin_box_rect_in_ancestor_coordinate_space(containing_block(), parent().root(), m_state);
float y_in_root = box_in_root_rect.y() + y;
- auto space = parent().available_space_for_line(y_in_root);
- space.right = min(space.right, m_state.get(containing_block()).content_width);
- return space;
+ auto space = parent().space_used_by_floats(y_in_root);
+ float containing_block_x = m_state.get(containing_block()).offset.x();
+ return max(space.left, containing_block_x) - containing_block_x;
+}
+
+float InlineFormattingContext::available_space_for_line(float y) const
+{
+ // NOTE: Floats are relative to the BFC root box, not necessarily the containing block of this IFC.
+ auto box_in_root_rect = margin_box_rect_in_ancestor_coordinate_space(containing_block(), parent().root(), m_state);
+ float y_in_root = box_in_root_rect.y() + y;
+ auto space = parent().space_used_by_floats(y_in_root);
+
+ auto const& containing_block_state = m_state.get(containing_block());
+ auto const& root_block_state = m_state.get(parent().root());
+
+ space.left = max(space.left, containing_block_state.offset.x());
+ space.right = min(root_block_state.content_width - space.right, containing_block_state.offset.x() + containing_block_state.content_width);
+
+ return space.right - space.left;
}
void InlineFormattingContext::run(Box const&, LayoutMode layout_mode)
diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
index d0a5e09d82..0d480da6d5 100644
--- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
@@ -27,7 +27,8 @@ public:
void dimension_box_on_line(Box const&, LayoutMode);
- AvailableSpaceForLineInfo available_space_for_line(float y) const;
+ float leftmost_x_offset_at(float y) const;
+ float available_space_for_line(float y) const;
private:
void generate_line_boxes(LayoutMode);
diff --git a/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp b/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp
index bfc3aad290..bf1668235a 100644
--- a/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp
+++ b/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp
@@ -35,8 +35,18 @@ void LineBuilder::begin_new_line(bool increment_y)
{
if (increment_y)
m_current_y += max(m_max_height_on_current_line, m_context.containing_block().line_height());
- auto space = m_context.available_space_for_line(m_current_y);
- m_available_width_for_current_line = space.right - space.left;
+
+ switch (m_layout_mode) {
+ case LayoutMode::Default:
+ m_available_width_for_current_line = m_context.available_space_for_line(m_current_y);
+ break;
+ case LayoutMode::AllPossibleLineBreaks:
+ m_available_width_for_current_line = 0;
+ break;
+ case LayoutMode::OnlyRequiredLineBreaks:
+ m_available_width_for_current_line = INFINITY;
+ break;
+ }
m_max_height_on_current_line = 0;
m_last_line_needs_update = true;
@@ -108,7 +118,7 @@ void LineBuilder::update_last_line()
auto& line_box = line_boxes.last();
auto text_align = m_context.containing_block().computed_values().text_align();
- float x_offset = m_context.available_space_for_line(m_current_y).left;
+ float x_offset = m_context.leftmost_x_offset_at(m_current_y);
float bottom = m_current_y + m_context.containing_block().line_height();
float excess_horizontal_space = m_containing_block_state.content_width - line_box.width();