summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
Diffstat (limited to 'Userland')
-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();