1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 08:58:11 +00:00

LibWeb: Start making our layout system "transactional"

This patch adds a map of Layout::Node to FormattingState::NodeState.
Instead of updating layout nodes incrementally as layout progresses
through the formatting contexts, all updates are now written to the
corresponding NodeState instead.

At the end of layout, FormattingState::commit() is called, which
transfers all the values from the NodeState objects to the Node.

This will soon allow us to perform completely non-destructive layouts
which don't affect the tree.

Note that there are many imperfections here, and still many places
where we assign to the NodeState, but later read directly from the Node
instead. I'm just committing at this stage to make subsequent diffs
easier to understand.
This commit is contained in:
Andreas Kling 2022-02-20 15:51:24 +01:00
parent 561612f219
commit c9700e100e
27 changed files with 754 additions and 571 deletions

View file

@ -18,7 +18,7 @@
namespace Web::Layout {
BlockFormattingContext::BlockFormattingContext(FormattingState& state, BlockContainer& root, FormattingContext* parent)
BlockFormattingContext::BlockFormattingContext(FormattingState& state, BlockContainer const& root, FormattingContext* parent)
: FormattingContext(Type::Block, state, root, parent)
{
}
@ -38,7 +38,7 @@ bool BlockFormattingContext::is_initial() const
return is<InitialContainingBlock>(root());
}
void BlockFormattingContext::run(Box&, LayoutMode layout_mode)
void BlockFormattingContext::run(Box const&, LayoutMode layout_mode)
{
if (is_initial()) {
layout_initial_containing_block(layout_mode);
@ -61,7 +61,7 @@ void BlockFormattingContext::parent_context_did_dimension_child_root_box()
apply_transformations_to_children(root());
}
void BlockFormattingContext::apply_transformations_to_children(Box& box)
void BlockFormattingContext::apply_transformations_to_children(Box const& box)
{
box.for_each_child_of_type<Box>([&](auto& child_box) {
float transform_y_offset = 0.0f;
@ -90,12 +90,13 @@ void BlockFormattingContext::apply_transformations_to_children(Box& box)
}
}
auto untransformed_offset = child_box.effective_offset();
child_box.set_offset(untransformed_offset.x(), untransformed_offset.y() + transform_y_offset);
auto& child_box_state = m_state.ensure(child_box);
auto untransformed_offset = child_box_state.offset;
child_box_state.offset = Gfx::FloatPoint { untransformed_offset.x(), untransformed_offset.y() + transform_y_offset };
});
}
void BlockFormattingContext::compute_width(Box& box)
void BlockFormattingContext::compute_width(Box const& box)
{
if (box.is_absolutely_positioned()) {
compute_width_for_absolutely_positioned_element(box);
@ -105,7 +106,8 @@ void BlockFormattingContext::compute_width(Box& box)
if (is<ReplacedBox>(box)) {
// FIXME: This should not be done *by* ReplacedBox
auto& replaced = verify_cast<ReplacedBox>(box);
replaced.prepare_for_replaced_layout();
// FIXME: This const_cast is gross.
const_cast<ReplacedBox&>(replaced).prepare_for_replaced_layout();
compute_width_for_block_level_replaced_element_in_normal_flow(replaced);
return;
}
@ -115,8 +117,8 @@ void BlockFormattingContext::compute_width(Box& box)
return;
}
auto& computed_values = box.computed_values();
float width_of_containing_block = box.containing_block()->content_width();
auto const& computed_values = box.computed_values();
float width_of_containing_block = m_state.get(*box.containing_block()).content_width;
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto zero_value = CSS::Length::make_px(0);
@ -226,20 +228,22 @@ void BlockFormattingContext::compute_width(Box& box)
}
}
box.set_content_width(used_width.to_px(box));
box.box_model().margin.left = margin_left.to_px(box);
box.box_model().margin.right = margin_right.to_px(box);
box.box_model().border.left = computed_values.border_left().width;
box.box_model().border.right = computed_values.border_right().width;
box.box_model().padding.left = padding_left.to_px(box);
box.box_model().padding.right = padding_right.to_px(box);
auto& box_state = m_state.ensure(box);
box_state.content_width = used_width.to_px(box);
box_state.margin_left = margin_left.to_px(box);
box_state.margin_right = margin_right.to_px(box);
box_state.border_left = computed_values.border_left().width;
box_state.border_right = computed_values.border_right().width;
box_state.padding_left = padding_left.to_px(box);
box_state.padding_right = padding_right.to_px(box);
}
void BlockFormattingContext::compute_width_for_floating_box(Box& box)
void BlockFormattingContext::compute_width_for_floating_box(Box const& box)
{
// 10.3.5 Floating, non-replaced elements
auto& computed_values = box.computed_values();
float width_of_containing_block = box.containing_block()->content_width();
auto& containing_block = *box.containing_block();
float width_of_containing_block = m_state.get(containing_block).content_width;
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto zero_value = CSS::Length::make_px(0);
@ -272,26 +276,27 @@ void BlockFormattingContext::compute_width_for_floating_box(Box& box)
width = CSS::Length(min(max(result.preferred_minimum_width, available_width), result.preferred_width), CSS::Length::Type::Px);
}
float final_width = width.to_px(box);
box.set_content_width(final_width);
box.box_model().margin.left = margin_left.to_px(box);
box.box_model().margin.right = margin_right.to_px(box);
box.box_model().border.left = computed_values.border_left().width;
box.box_model().border.right = computed_values.border_right().width;
box.box_model().padding.left = padding_left.to_px(box);
box.box_model().padding.right = padding_right.to_px(box);
auto& box_state = m_state.ensure(box);
box_state.content_width = width.to_px(box);
box_state.margin_left = margin_left.to_px(box);
box_state.margin_right = margin_right.to_px(box);
box_state.border_left = computed_values.border_left().width;
box_state.border_right = computed_values.border_right().width;
box_state.padding_left = padding_left.to_px(box);
box_state.padding_right = padding_right.to_px(box);
}
void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox& box)
void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox const& box)
{
box.set_content_width(compute_width_for_replaced_element(box));
m_state.ensure(box).content_width = compute_width_for_replaced_element(m_state, box);
}
float BlockFormattingContext::compute_theoretical_height(Box const& box)
float BlockFormattingContext::compute_theoretical_height(FormattingState const& state, Box const& box)
{
auto& computed_values = box.computed_values();
auto& containing_block = *box.containing_block();
auto containing_block_height = CSS::Length::make_px(containing_block.content_height());
auto const& computed_values = box.computed_values();
auto const& containing_block = *box.containing_block();
auto const& containing_block_state = state.get(containing_block);
auto containing_block_height = CSS::Length::make_px(containing_block_state.content_height);
auto is_absolute = [](Optional<CSS::LengthPercentage> const& length_percentage) {
return length_percentage.has_value() && length_percentage->is_length() && length_percentage->length().is_absolute();
@ -300,12 +305,12 @@ float BlockFormattingContext::compute_theoretical_height(Box const& box)
// Then work out what the height is, based on box type and CSS properties.
float height = 0;
if (is<ReplacedBox>(box)) {
height = compute_height_for_replaced_element(verify_cast<ReplacedBox>(box));
height = compute_height_for_replaced_element(state, verify_cast<ReplacedBox>(box));
} else {
if (!box.computed_values().height().has_value()
|| (box.computed_values().height()->is_length() && box.computed_values().height()->length().is_auto())
|| (computed_values.height().has_value() && computed_values.height()->is_percentage() && !is_absolute(containing_block.computed_values().height()))) {
height = compute_auto_height_for_block_level_element(box, ConsiderFloats::No);
height = compute_auto_height_for_block_level_element(state, box, ConsiderFloats::No);
} else {
height = computed_values.height().has_value() ? computed_values.height()->resolved(box, containing_block_height).to_px(box) : 0;
}
@ -322,63 +327,29 @@ float BlockFormattingContext::compute_theoretical_height(Box const& box)
return height;
}
void BlockFormattingContext::compute_height(Box& box)
void BlockFormattingContext::compute_height(Box const& box, FormattingState& state)
{
auto& computed_values = box.computed_values();
auto& containing_block = *box.containing_block();
auto width_of_containing_block_as_length = CSS::Length::make_px(containing_block.content_width());
auto const& computed_values = box.computed_values();
auto const& containing_block = *box.containing_block();
auto width_of_containing_block_as_length = CSS::Length::make_px(state.get(containing_block).content_width);
// First, resolve the top/bottom parts of the surrounding box model.
auto& box_state = state.ensure(box);
// FIXME: While negative values are generally allowed for margins, for now just ignore those for height calculation
box.box_model().margin.top = max(computed_values.margin().top.resolved(box, width_of_containing_block_as_length).to_px(box), 0);
box.box_model().margin.bottom = max(computed_values.margin().bottom.resolved(box, width_of_containing_block_as_length).to_px(box), 0);
box_state.margin_top = max(computed_values.margin().top.resolved(box, width_of_containing_block_as_length).to_px(box), 0);
box_state.margin_bottom = max(computed_values.margin().bottom.resolved(box, width_of_containing_block_as_length).to_px(box), 0);
box.box_model().border.top = computed_values.border_top().width;
box.box_model().border.bottom = computed_values.border_bottom().width;
box.box_model().padding.top = computed_values.padding().top.resolved(box, width_of_containing_block_as_length).to_px(box);
box.box_model().padding.bottom = computed_values.padding().bottom.resolved(box, width_of_containing_block_as_length).to_px(box);
box_state.border_top = computed_values.border_top().width;
box_state.border_bottom = computed_values.border_bottom().width;
box_state.padding_top = computed_values.padding().top.resolved(box, width_of_containing_block_as_length).to_px(box);
box_state.padding_bottom = computed_values.padding().bottom.resolved(box, width_of_containing_block_as_length).to_px(box);
auto height = compute_theoretical_height(box);
box.set_content_height(height);
box_state.content_height = compute_theoretical_height(state, box);
}
void BlockFormattingContext::compute_position(Box& box)
{
// 9.4.3 Relative positioning
// Once a box has been laid out according to the normal flow or floated, it may be shifted relative to this position.
auto& box_model = box.box_model();
auto& computed_values = box.computed_values();
float width_of_containing_block = box.containing_block()->content_width();
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto specified_left = computed_values.offset().left.resolved(box, width_of_containing_block_as_length).resolved(box);
auto specified_right = computed_values.offset().right.resolved(box, width_of_containing_block_as_length).resolved(box);
if (specified_left.is_auto() && specified_right.is_auto()) {
// If both 'left' and 'right' are 'auto' (their initial values), the used values are '0' (i.e., the boxes stay in their original position).
box_model.offset.left = 0;
box_model.offset.right = 0;
} else if (specified_left.is_auto()) {
// If 'left' is 'auto', its used value is minus the value of 'right' (i.e., the boxes move to the left by the value of 'right').
box_model.offset.right = specified_right.to_px(box);
box_model.offset.left = 0 - box_model.offset.right;
} else if (specified_right.is_auto()) {
// If 'right' is specified as 'auto', its used value is minus the value of 'left'.
box_model.offset.left = specified_left.to_px(box);
box_model.offset.right = 0 - box_model.offset.left;
} else {
// If neither 'left' nor 'right' is 'auto', the position is over-constrained, and one of them has to be ignored.
// If the 'direction' property of the containing block is 'ltr', the value of 'left' wins and 'right' becomes -'left'.
// If 'direction' of the containing block is 'rtl', 'right' wins and 'left' is ignored.
// FIXME: Check direction (assuming 'ltr' for now).
box_model.offset.left = specified_left.to_px(box);
box_model.offset.right = 0 - box_model.offset.left;
}
}
void BlockFormattingContext::layout_inline_children(BlockContainer& block_container, LayoutMode layout_mode)
void BlockFormattingContext::layout_inline_children(BlockContainer const& block_container, LayoutMode layout_mode)
{
VERIFY(block_container.children_are_inline());
@ -386,7 +357,7 @@ void BlockFormattingContext::layout_inline_children(BlockContainer& block_contai
context.run(block_container, layout_mode);
}
void BlockFormattingContext::layout_block_level_children(BlockContainer& block_container, LayoutMode layout_mode)
void BlockFormattingContext::layout_block_level_children(BlockContainer const& block_container, LayoutMode layout_mode)
{
VERIFY(!block_container.children_are_inline());
@ -394,6 +365,8 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer& block_c
float content_width = 0;
block_container.for_each_child_of_type<Box>([&](Box& child_box) {
auto& box_state = m_state.ensure(child_box);
if (child_box.is_absolutely_positioned()) {
m_absolutely_positioned_boxes.append(child_box);
return IterationDecision::Continue;
@ -413,7 +386,7 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer& block_c
place_block_level_element_in_normal_flow_vertically(child_box, block_container);
if (child_box.has_definite_height()) {
compute_height(child_box);
compute_height(child_box, m_state);
}
OwnPtr<FormattingContext> independent_formatting_context;
@ -425,10 +398,9 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer& block_c
layout_block_level_children(verify_cast<BlockContainer>(child_box), layout_mode);
}
compute_height(child_box);
compute_height(child_box, m_state);
if (child_box.computed_values().position() == CSS::Position::Relative)
compute_position(child_box);
compute_position(child_box);
if (is<ReplacedBox>(child_box) || is<BlockContainer>(child_box))
place_block_level_element_in_normal_flow_horizontally(child_box, block_container);
@ -438,8 +410,8 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer& block_c
if (is<ListItemBox>(child_box))
verify_cast<ListItemBox>(child_box).layout_marker();
content_height = max(content_height, child_box.effective_offset().y() + child_box.content_height() + child_box.box_model().margin_box().bottom);
content_width = max(content_width, child_box.content_width());
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.content_width);
if (independent_formatting_context)
independent_formatting_context->parent_context_did_dimension_child_root_box();
@ -449,34 +421,36 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer& block_c
if (layout_mode != LayoutMode::Default) {
auto& width = block_container.computed_values().width();
if (!width.has_value() || (width->is_length() && width->length().is_auto()))
block_container.set_content_width(content_width);
if (!width.has_value() || (width->is_length() && width->length().is_auto())) {
auto& block_container_state = m_state.ensure(block_container);
block_container_state.content_width = content_width;
}
}
}
void BlockFormattingContext::compute_vertical_box_model_metrics(Box& child_box, BlockContainer const& containing_block)
void BlockFormattingContext::compute_vertical_box_model_metrics(Box const& box, BlockContainer const& containing_block)
{
auto& box_model = child_box.box_model();
auto const& computed_values = child_box.computed_values();
auto width_of_containing_block = CSS::Length::make_px(containing_block.content_width());
auto& box_state = m_state.ensure(box);
auto const& computed_values = box.computed_values();
auto width_of_containing_block = CSS::Length::make_px(m_state.get(containing_block).content_width);
box_model.margin.top = computed_values.margin().top.resolved(child_box, width_of_containing_block).resolved(containing_block).to_px(child_box);
box_model.margin.bottom = computed_values.margin().bottom.resolved(child_box, width_of_containing_block).resolved(containing_block).to_px(child_box);
box_model.border.top = computed_values.border_top().width;
box_model.border.bottom = computed_values.border_bottom().width;
box_model.padding.top = computed_values.padding().top.resolved(child_box, width_of_containing_block).resolved(containing_block).to_px(child_box);
box_model.padding.bottom = computed_values.padding().bottom.resolved(child_box, width_of_containing_block).resolved(containing_block).to_px(child_box);
box_state.margin_top = computed_values.margin().top.resolved(box, width_of_containing_block).resolved(containing_block).to_px(box);
box_state.margin_bottom = computed_values.margin().bottom.resolved(box, width_of_containing_block).resolved(containing_block).to_px(box);
box_state.border_top = computed_values.border_top().width;
box_state.border_bottom = computed_values.border_bottom().width;
box_state.padding_top = computed_values.padding().top.resolved(box, width_of_containing_block).resolved(containing_block).to_px(box);
box_state.padding_bottom = computed_values.padding().bottom.resolved(box, width_of_containing_block).resolved(containing_block).to_px(box);
}
void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically(Box& child_box, BlockContainer const& containing_block)
void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically(Box const& child_box, BlockContainer const& containing_block)
{
auto& box_model = child_box.box_model();
auto& box_state = m_state.ensure(child_box);
auto const& computed_values = child_box.computed_values();
compute_vertical_box_model_metrics(child_box, containing_block);
float y = box_model.margin_box().top
+ box_model.offset.top;
float y = box_state.margin_box_top()
+ box_state.offset_top;
// NOTE: Empty (0-height) preceding siblings have their margins collapsed with *their* preceding sibling, etc.
float collapsed_bottom_margin_of_preceding_siblings = 0;
@ -484,20 +458,22 @@ void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically
auto* relevant_sibling = child_box.previous_sibling_of_type<Layout::BlockContainer>();
while (relevant_sibling != nullptr) {
if (!relevant_sibling->is_absolutely_positioned() && !relevant_sibling->is_floating()) {
collapsed_bottom_margin_of_preceding_siblings = max(collapsed_bottom_margin_of_preceding_siblings, relevant_sibling->box_model().margin.bottom);
if (relevant_sibling->border_box_height() > 0)
auto const& relevant_sibling_state = m_state.get(*relevant_sibling);
collapsed_bottom_margin_of_preceding_siblings = max(collapsed_bottom_margin_of_preceding_siblings, relevant_sibling_state.margin_bottom);
if (relevant_sibling_state.border_box_height() > 0)
break;
}
relevant_sibling = relevant_sibling->previous_sibling();
}
if (relevant_sibling) {
y += relevant_sibling->effective_offset().y()
+ relevant_sibling->content_height()
+ relevant_sibling->box_model().border_box().bottom;
auto const& relevant_sibling_state = m_state.get(*relevant_sibling);
y += relevant_sibling_state.offset.y()
+ relevant_sibling_state.content_height
+ relevant_sibling_state.border_box_bottom();
// Collapse top margin with bottom margin of preceding siblings if needed
float my_margin_top = box_model.margin.top;
float my_margin_top = box_state.margin_top;
if (my_margin_top < 0 || collapsed_bottom_margin_of_preceding_siblings < 0) {
// Negative margins present.
@ -514,8 +490,9 @@ void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically
auto clear_floating_boxes = [&](FloatSideData& float_side) {
if (!float_side.boxes.is_empty()) {
float clearance_y = 0;
for (auto& floating_box : float_side.boxes) {
clearance_y = max(clearance_y, floating_box.effective_offset().y() + floating_box.border_box_height());
for (auto const& floating_box : float_side.boxes) {
auto const& floating_box_state = m_state.get(floating_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();
@ -529,21 +506,22 @@ void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically
if ((computed_values.clear() == CSS::Clear::Right || computed_values.clear() == CSS::Clear::Both) && !child_box.is_flex_item())
clear_floating_boxes(m_right_floats);
child_box.set_offset(child_box.effective_offset().x(), y);
box_state.offset = Gfx::FloatPoint { box_state.offset.x(), y };
}
void BlockFormattingContext::place_block_level_element_in_normal_flow_horizontally(Box& child_box, BlockContainer const& containing_block)
void BlockFormattingContext::place_block_level_element_in_normal_flow_horizontally(Box const& child_box, BlockContainer const& containing_block)
{
auto& box_model = child_box.box_model();
auto& box_state = m_state.ensure(child_box);
auto const& containing_block_state = m_state.get(containing_block);
float x = 0;
if (containing_block.computed_values().text_align() == CSS::TextAlign::LibwebCenter) {
x = (containing_block.content_width() / 2) - child_box.content_width() / 2;
x = (containing_block_state.content_width / 2) - box_state.content_width / 2;
} else {
x = box_model.margin_box().left + box_model.offset.left;
x = box_state.margin_box_left() + box_state.offset_left;
}
child_box.set_offset(x, child_box.effective_offset().y());
box_state.offset = Gfx::FloatPoint { x, box_state.offset.y() };
}
void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_mode)
@ -558,83 +536,92 @@ void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_m
// Compute scrollable overflow.
float bottom_edge = 0;
float right_edge = 0;
icb.for_each_in_subtree_of_type<Box>([&](Box& child) {
auto child_rect = child.absolute_border_box_rect();
icb.for_each_in_subtree_of_type<Box>([&](Box const& child) {
auto const& child_state = m_state.get(child);
auto child_rect = absolute_content_rect(child, m_state);
child_rect.inflate(child_state.border_box_top(), child_state.border_box_right(), child_state.border_box_bottom(), child_state.border_box_left());
bottom_edge = max(bottom_edge, child_rect.bottom());
right_edge = max(right_edge, child_rect.right());
return IterationDecision::Continue;
});
if (bottom_edge >= viewport_rect.height() || right_edge >= viewport_rect.width()) {
auto& overflow_data = icb.ensure_overflow_data();
// FIXME: Move overflow data to FormattingState!
auto& overflow_data = const_cast<InitialContainingBlock&>(icb).ensure_overflow_data();
overflow_data.scrollable_overflow_rect = viewport_rect.to_type<float>();
// NOTE: The edges are *within* the rectangle, so we add 1 to get the width and height.
overflow_data.scrollable_overflow_rect.set_size(right_edge + 1, bottom_edge + 1);
} else {
icb.clear_overflow_data();
// FIXME: Move overflow data to FormattingState!
const_cast<InitialContainingBlock&>(icb).clear_overflow_data();
}
}
void BlockFormattingContext::layout_floating_child(Box& box, BlockContainer const& containing_block)
void BlockFormattingContext::layout_floating_child(Box const& box, BlockContainer const& containing_block)
{
VERIFY(box.is_floating());
auto& box_state = m_state.ensure(box);
auto containing_block_content_width = m_state.get(containing_block).content_width;
compute_width(box);
(void)layout_inside(box, LayoutMode::Default);
compute_height(box);
compute_height(box, m_state);
// First we place the box normally (to get the right y coordinate.)
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 first_edge = [&](PixelBox const& thing) { return side == FloatSide::Left ? thing.left : thing.right; };
auto second_edge = [&](PixelBox const& thing) { return side == FloatSide::Right ? thing.left : thing.right; };
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 first_edge_of_margin_box = [&](FormattingState::NodeState const& thing) { return side == FloatSide::Left ? thing.margin_box_left() : thing.margin_box_right(); };
// Then we float it to the left or right.
float x = box.effective_offset().x();
float x = box_state.offset.x();
auto box_in_root_rect = box.margin_box_rect_in_ancestor_coordinate_space(root());
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 y = box.effective_offset().y();
float y = box_state.offset.y();
if (side_data.boxes.is_empty()) {
// This is the first floating box on this side. Go all the way to the edge.
if (side == FloatSide::Left)
x = box.box_model().margin_box().left;
x = box_state.margin_box_left();
else
x = containing_block.content_width() - box.box_model().margin_box().right - box.content_width();
x = containing_block_content_width - box_state.margin_box_right() - box_state.content_width;
side_data.y_offset = 0;
} else {
auto& previous_box = side_data.boxes.last();
auto previous_rect = previous_box.margin_box_rect_in_ancestor_coordinate_space(root());
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 margin_collapsed_with_previous = max(
second_edge(previous_box.box_model().margin),
first_edge(box.box_model().margin));
second_edge(previous_box_state),
first_edge(box_state));
float wanted_x = 0;
bool fits_on_line = false;
if (side == FloatSide::Left) {
auto previous_right_border_edge = previous_box.effective_offset().x()
+ previous_box.content_width()
+ previous_box.box_model().padding.right
+ previous_box.box_model().border.right
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
+ margin_collapsed_with_previous;
wanted_x = previous_right_border_edge + box.box_model().border.left + box.box_model().padding.left;
fits_on_line = (wanted_x + box.content_width() + box.box_model().padding.right + box.box_model().border.right + box.box_model().margin.right) <= containing_block.content_width();
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;
} else {
auto previous_left_border_edge = previous_box.effective_offset().x()
- previous_box.content_width()
- previous_box.box_model().padding.left
- previous_box.box_model().border.left
auto previous_left_border_edge = previous_box_state.offset.x()
- previous_box_state.content_width
- previous_box_state.padding_left
- previous_box_state.border_left
- margin_collapsed_with_previous;
wanted_x = previous_left_border_edge - box.box_model().border.right - box.box_model().padding.right - box.content_width();
fits_on_line = (wanted_x - box.box_model().padding.left - box.box_model().border.left - box.box_model().margin.left) >= 0;
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;
}
if (fits_on_line) {
@ -643,17 +630,19 @@ void BlockFormattingContext::layout_floating_child(Box& box, BlockContainer cons
x = wanted_x;
} else {
// This box does not touch another floating box, go all the way to the first edge.
x = first_edge(box.box_model().margin_box());
x = first_edge_of_margin_box(box_state);
// Also, forget all previous boxes floated to this side while since they're no longer relevant.
side_data.boxes.clear();
}
} else {
// We ran out of horizontal space on this "float line", and need to break.
x = first_edge(box.box_model().margin_box());
x = first_edge_of_margin_box(box_state);
float lowest_border_edge = 0;
for (auto const& box : side_data.boxes)
lowest_border_edge = max(lowest_border_edge, box.border_box_height());
for (auto const& box : side_data.boxes) {
auto const& box_state = m_state.get(box);
lowest_border_edge = max(lowest_border_edge, box_state.border_box_height());
}
side_data.y_offset += lowest_border_edge;
@ -664,7 +653,7 @@ void BlockFormattingContext::layout_floating_child(Box& box, BlockContainer cons
y += side_data.y_offset;
side_data.boxes.append(box);
box.set_offset(x, y);
box_state.offset = Gfx::FloatPoint { x, y };
};
// Next, float to the left and/or right