1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:27:45 +00:00

LibWeb: Support CSS floats in inline flow

CSS floats are now emitted by the InlineLevelIterator. When this
happens, IFC coordinates with the parent BFC to float the box to the
side, using the current LineBuilder state for vertical placement.

This makes the "instructions" text on Acid3 render as a single
contiguous flow of inline content.
This commit is contained in:
Andreas Kling 2022-03-22 19:18:05 +01:00
parent fa64a7f6cc
commit de6f7f0029
7 changed files with 57 additions and 9 deletions

View file

@ -12,6 +12,7 @@
#include <LibWeb/Layout/Box.h> #include <LibWeb/Layout/Box.h>
#include <LibWeb/Layout/InitialContainingBlock.h> #include <LibWeb/Layout/InitialContainingBlock.h>
#include <LibWeb/Layout/InlineFormattingContext.h> #include <LibWeb/Layout/InlineFormattingContext.h>
#include <LibWeb/Layout/LineBuilder.h>
#include <LibWeb/Layout/ListItemBox.h> #include <LibWeb/Layout/ListItemBox.h>
#include <LibWeb/Layout/ListItemMarkerBox.h> #include <LibWeb/Layout/ListItemMarkerBox.h>
#include <LibWeb/Layout/ReplacedBox.h> #include <LibWeb/Layout/ReplacedBox.h>
@ -551,7 +552,7 @@ void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_m
} }
} }
void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer const& containing_block, LayoutMode layout_mode) void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer const& containing_block, LayoutMode layout_mode, LineBuilder* line_builder)
{ {
VERIFY(box.is_floating()); VERIFY(box.is_floating());
@ -575,8 +576,16 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer
compute_height(box, m_state); compute_height(box, m_state);
// First we place the box normally (to get the right y coordinate.) // First we place the box normally (to get the right y coordinate.)
place_block_level_element_in_normal_flow_vertically(box, containing_block); // If we have a LineBuilder, we're in the middle of inline layout, otherwise this is block layout.
place_block_level_element_in_normal_flow_horizontally(box, containing_block); if (line_builder) {
float y_offset = box_state.margin_box_top() + box_state.offset_top;
line_builder->break_if_needed(layout_mode, box_state.border_box_width(), false);
box_state.offset.set_y(line_builder->current_y() + y_offset);
line_builder->adjust_last_line_after_inserting_floating_box({}, box.computed_values().float_(), box_state.border_box_width());
} else {
place_block_level_element_in_normal_flow_vertically(box, containing_block);
place_block_level_element_in_normal_flow_horizontally(box, containing_block);
}
auto float_box = [&](FloatSide side, FloatSideData& side_data) { 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 first_edge = [&](FormattingState::NodeState const& thing) { return side == FloatSide::Left ? thing.margin_left : thing.margin_right; };

View file

@ -13,6 +13,8 @@
namespace Web::Layout { namespace Web::Layout {
class LineBuilder;
// https://www.w3.org/TR/css-display/#block-formatting-context // https://www.w3.org/TR/css-display/#block-formatting-context
class BlockFormattingContext : public FormattingContext { class BlockFormattingContext : public FormattingContext {
public: public:
@ -42,6 +44,8 @@ public:
virtual float greatest_child_width(Box const&) override; virtual float greatest_child_width(Box const&) override;
void layout_floating_box(Box const& child, BlockContainer const& containing_block, LayoutMode, LineBuilder* = nullptr);
private: private:
virtual bool is_block_formatting_context() const final { return true; } virtual bool is_block_formatting_context() const final { return true; }
@ -58,8 +62,6 @@ private:
void place_block_level_element_in_normal_flow_horizontally(Box const& child_box, BlockContainer const&); 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 place_block_level_element_in_normal_flow_vertically(Box const& child_box, BlockContainer const&);
void layout_floating_box(Box const& child, BlockContainer const& containing_block, LayoutMode);
void layout_list_item_marker(ListItemBox const&); void layout_list_item_marker(ListItemBox const&);
enum class FloatSide { enum class FloatSide {

View file

@ -250,6 +250,11 @@ void InlineFormattingContext::generate_line_boxes(LayoutMode layout_mode)
parent().add_absolutely_positioned_box(static_cast<Layout::Box const&>(*item.node)); parent().add_absolutely_positioned_box(static_cast<Layout::Box const&>(*item.node));
break; break;
case InlineLevelIterator::Item::Type::FloatingElement:
if (is<Box>(*item.node))
parent().layout_floating_box(static_cast<Layout::Box const&>(*item.node), containing_block(), layout_mode, &line_builder);
break;
case InlineLevelIterator::Item::Type::Text: { case InlineLevelIterator::Item::Type::Text: {
auto& text_node = verify_cast<Layout::TextNode>(*item.node); auto& text_node = verify_cast<Layout::TextNode>(*item.node);
line_builder.break_if_needed(layout_mode, item.border_box_width(), item.should_force_break); line_builder.break_if_needed(layout_mode, item.border_box_width(), item.should_force_break);

View file

@ -69,8 +69,10 @@ void InlineLevelIterator::exit_node_with_box_model_metrics()
// This is similar to Layout::Node::next_in_pre_order() but will not descend into inline-block nodes. // This is similar to Layout::Node::next_in_pre_order() but will not descend into inline-block nodes.
Layout::Node const* InlineLevelIterator::next_inline_node_in_pre_order(Layout::Node const& current, Layout::Node const* stay_within) Layout::Node const* InlineLevelIterator::next_inline_node_in_pre_order(Layout::Node const& current, Layout::Node const* stay_within)
{ {
if (current.first_child() && current.first_child()->is_inline() && !current.is_inline_block()) if (current.first_child() && current.first_child()->is_inline() && !current.is_inline_block()) {
return current.first_child(); if (!current.is_box() || !static_cast<Box const&>(current).is_out_of_flow(m_inline_formatting_context))
return current.first_child();
}
Layout::Node const* node = &current; Layout::Node const* node = &current;
Layout::Node const* next = nullptr; Layout::Node const* next = nullptr;
@ -101,12 +103,12 @@ void InlineLevelIterator::compute_next()
return; return;
do { do {
m_next_node = next_inline_node_in_pre_order(*m_next_node, &m_container); m_next_node = next_inline_node_in_pre_order(*m_next_node, &m_container);
} while (m_next_node && !m_next_node->is_inline()); } while (m_next_node && (!m_next_node->is_inline() && !m_next_node->is_out_of_flow(m_inline_formatting_context)));
} }
void InlineLevelIterator::skip_to_next() void InlineLevelIterator::skip_to_next()
{ {
if (m_next_node && is<Layout::NodeWithStyleAndBoxModelMetrics>(*m_next_node) && !m_next_node->is_inline_block()) if (m_next_node && is<Layout::NodeWithStyleAndBoxModelMetrics>(*m_next_node) && !m_next_node->is_inline_block() && !m_next_node->is_out_of_flow(m_inline_formatting_context))
enter_node_with_box_model_metrics(static_cast<Layout::NodeWithStyleAndBoxModelMetrics const&>(*m_next_node)); enter_node_with_box_model_metrics(static_cast<Layout::NodeWithStyleAndBoxModelMetrics const&>(*m_next_node));
m_current_node = m_next_node; m_current_node = m_next_node;
@ -163,6 +165,15 @@ Optional<InlineLevelIterator::Item> InlineLevelIterator::next(float available_wi
}; };
} }
if (m_current_node->is_floating()) {
auto& node = *m_current_node;
skip_to_next();
return Item {
.type = Item::Type::FloatingElement,
.node = &node,
};
}
if (is<Layout::BreakNode>(*m_current_node)) { if (is<Layout::BreakNode>(*m_current_node)) {
skip_to_next(); skip_to_next();
return Item { return Item {

View file

@ -27,6 +27,7 @@ public:
Element, Element,
ForcedBreak, ForcedBreak,
AbsolutelyPositionedElement, AbsolutelyPositionedElement,
FloatingElement,
}; };
Type type {}; Type type {};
Layout::Node const* node { nullptr }; Layout::Node const* node { nullptr };

View file

@ -206,4 +206,20 @@ void LineBuilder::remove_last_line_if_empty()
m_last_line_needs_update = false; m_last_line_needs_update = false;
} }
} }
void LineBuilder::adjust_last_line_after_inserting_floating_box(Badge<BlockFormattingContext>, CSS::Float float_, float space_used_by_float)
{
// NOTE: LineBuilder generates lines from left-to-right, so if we've just added a left-side float,
// that means every fragment already on this line has to move towards the right.
if (float_ == CSS::Float::Left && !m_containing_block_state.line_boxes.is_empty()) {
for (auto& fragment : m_containing_block_state.line_boxes.last().fragments())
fragment.set_offset(fragment.offset().translated(space_used_by_float, 0));
m_containing_block_state.line_boxes.last().m_width += space_used_by_float;
}
m_available_width_for_current_line -= space_used_by_float;
if (m_available_width_for_current_line < 0)
m_available_width_for_current_line = 0;
}
} }

View file

@ -34,6 +34,10 @@ public:
void remove_last_line_if_empty(); void remove_last_line_if_empty();
float current_y() const { return m_current_y; }
void adjust_last_line_after_inserting_floating_box(Badge<BlockFormattingContext>, CSS::Float, float space_used_by_float);
private: private:
void begin_new_line(bool increment_y); void begin_new_line(bool increment_y);