mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 22:17:42 +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:
parent
561612f219
commit
c9700e100e
27 changed files with 754 additions and 571 deletions
|
@ -17,11 +17,11 @@
|
|||
|
||||
namespace Web::Layout {
|
||||
|
||||
static float get_pixel_size(Box const& box, Optional<CSS::LengthPercentage> const& length_percentage)
|
||||
static float get_pixel_size(FormattingState const& state, Box const& box, Optional<CSS::LengthPercentage> const& length_percentage)
|
||||
{
|
||||
if (!length_percentage.has_value())
|
||||
return 0;
|
||||
auto inner_main_size = CSS::Length::make_px(box.containing_block()->content_width());
|
||||
auto inner_main_size = CSS::Length::make_px(state.get(*box.containing_block()).content_width);
|
||||
return length_percentage->resolved(box, inner_main_size).to_px(box);
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,9 @@ static bool is_undefined_or_auto(Optional<CSS::LengthPercentage> const& length_p
|
|||
return length_percentage->is_length() && length_percentage->length().is_auto();
|
||||
}
|
||||
|
||||
FlexFormattingContext::FlexFormattingContext(FormattingState& state, Box& flex_container, FormattingContext* parent)
|
||||
FlexFormattingContext::FlexFormattingContext(FormattingState& state, Box const& flex_container, FormattingContext* parent)
|
||||
: FormattingContext(Type::Flex, state, flex_container, parent)
|
||||
, m_flex_container_state(m_state.ensure(flex_container))
|
||||
, m_flex_direction(flex_container.computed_values().flex_direction())
|
||||
{
|
||||
}
|
||||
|
@ -42,7 +43,7 @@ FlexFormattingContext::~FlexFormattingContext()
|
|||
{
|
||||
}
|
||||
|
||||
void FlexFormattingContext::run(Box& run_box, LayoutMode)
|
||||
void FlexFormattingContext::run(Box const& run_box, LayoutMode)
|
||||
{
|
||||
VERIFY(&run_box == &flex_container());
|
||||
|
||||
|
@ -116,7 +117,7 @@ void FlexFormattingContext::run(Box& run_box, LayoutMode)
|
|||
|
||||
void FlexFormattingContext::populate_specified_margins(FlexItem& item, CSS::FlexDirection flex_direction) const
|
||||
{
|
||||
auto width_of_containing_block = item.box.containing_block()->content_width();
|
||||
auto width_of_containing_block = m_state.get(*item.box.containing_block()).content_width;
|
||||
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
|
||||
// FIXME: This should also take reverse-ness into account
|
||||
if (flex_direction == CSS::FlexDirection::Row || flex_direction == CSS::FlexDirection::RowReverse) {
|
||||
|
@ -135,6 +136,7 @@ void FlexFormattingContext::populate_specified_margins(FlexItem& item, CSS::Flex
|
|||
// https://www.w3.org/TR/css-flexbox-1/#flex-items
|
||||
void FlexFormattingContext::generate_anonymous_flex_items()
|
||||
{
|
||||
auto& containing_block_state = m_state.get(*flex_container().containing_block());
|
||||
// More like, sift through the already generated items.
|
||||
// After this step no items are to be added or removed from flex_items!
|
||||
// It holds every item we need to consider and there should be nothing in the following
|
||||
|
@ -142,29 +144,29 @@ void FlexFormattingContext::generate_anonymous_flex_items()
|
|||
// This is particularly important since we take references to the items stored in flex_items
|
||||
// later, whose addresses won't be stable if we added or removed any items.
|
||||
if (!flex_container().has_definite_width()) {
|
||||
flex_container().set_content_width(flex_container().containing_block()->content_width());
|
||||
m_flex_container_state.content_width = containing_block_state.content_width;
|
||||
} else {
|
||||
auto container_width = flex_container().containing_block()->content_width();
|
||||
auto container_width = containing_block_state.content_width;
|
||||
|
||||
auto& maybe_width = flex_container().computed_values().width();
|
||||
if (maybe_width.has_value()) {
|
||||
auto width = maybe_width->resolved(flex_container(), CSS::Length::make_px(container_width)).to_px(flex_container());
|
||||
flex_container().set_content_width(width);
|
||||
m_flex_container_state.content_width = width;
|
||||
} else {
|
||||
flex_container().set_content_width(0);
|
||||
m_flex_container_state.content_width = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!flex_container().has_definite_height()) {
|
||||
flex_container().set_content_height(flex_container().containing_block()->content_height());
|
||||
m_flex_container_state.content_height = containing_block_state.content_height;
|
||||
} else {
|
||||
auto container_height = flex_container().containing_block()->content_height();
|
||||
auto container_height = containing_block_state.content_height;
|
||||
auto& maybe_height = flex_container().computed_values().height();
|
||||
if (maybe_height.has_value()) {
|
||||
auto height = maybe_height->resolved(flex_container(), CSS::Length::make_px(container_height)).to_px(flex_container());
|
||||
flex_container().set_content_height(height);
|
||||
m_flex_container_state.content_height = height;
|
||||
} else {
|
||||
flex_container().set_content_height(0);
|
||||
m_flex_container_state.content_height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,12 +205,14 @@ bool FlexFormattingContext::has_definite_main_size(Box const& box) const
|
|||
|
||||
float FlexFormattingContext::specified_main_size(Box const& box) const
|
||||
{
|
||||
return is_row_layout() ? box.content_width() : box.content_height();
|
||||
auto const& box_state = m_state.get(box);
|
||||
return is_row_layout() ? box_state.content_width : box_state.content_height;
|
||||
}
|
||||
|
||||
float FlexFormattingContext::specified_cross_size(Box const& box) const
|
||||
{
|
||||
return is_row_layout() ? box.content_height() : box.content_width();
|
||||
auto const& box_state = m_state.get(box);
|
||||
return is_row_layout() ? box_state.content_height : box_state.content_width;
|
||||
}
|
||||
|
||||
bool FlexFormattingContext::has_main_min_size(Box const& box) const
|
||||
|
@ -263,15 +267,15 @@ float FlexFormattingContext::specified_main_size_of_child_box(Box const& child_b
|
|||
float FlexFormattingContext::specified_main_min_size(Box const& box) const
|
||||
{
|
||||
return is_row_layout()
|
||||
? get_pixel_size(box, box.computed_values().min_width())
|
||||
: get_pixel_size(box, box.computed_values().min_height());
|
||||
? get_pixel_size(m_state, box, box.computed_values().min_width())
|
||||
: get_pixel_size(m_state, box, box.computed_values().min_height());
|
||||
}
|
||||
|
||||
float FlexFormattingContext::specified_cross_min_size(Box const& box) const
|
||||
{
|
||||
return is_row_layout()
|
||||
? get_pixel_size(box, box.computed_values().min_height())
|
||||
: get_pixel_size(box, box.computed_values().min_width());
|
||||
? get_pixel_size(m_state, box, box.computed_values().min_height())
|
||||
: get_pixel_size(m_state, box, box.computed_values().min_width());
|
||||
}
|
||||
|
||||
bool FlexFormattingContext::has_main_max_size(Box const& box) const
|
||||
|
@ -291,20 +295,21 @@ bool FlexFormattingContext::has_cross_max_size(Box const& box) const
|
|||
float FlexFormattingContext::specified_main_max_size(Box const& box) const
|
||||
{
|
||||
return is_row_layout()
|
||||
? get_pixel_size(box, box.computed_values().max_width())
|
||||
: get_pixel_size(box, box.computed_values().max_height());
|
||||
? get_pixel_size(m_state, box, box.computed_values().max_width())
|
||||
: get_pixel_size(m_state, box, box.computed_values().max_height());
|
||||
}
|
||||
|
||||
float FlexFormattingContext::specified_cross_max_size(Box const& box) const
|
||||
{
|
||||
return is_row_layout()
|
||||
? get_pixel_size(box, box.computed_values().max_height())
|
||||
: get_pixel_size(box, box.computed_values().max_width());
|
||||
? get_pixel_size(m_state, box, box.computed_values().max_height())
|
||||
: get_pixel_size(m_state, box, box.computed_values().max_width());
|
||||
}
|
||||
|
||||
float FlexFormattingContext::calculated_main_size(Box const& box) const
|
||||
{
|
||||
return is_row_layout() ? box.content_width() : box.content_height();
|
||||
auto const& box_state = m_state.get(box);
|
||||
return is_row_layout() ? box_state.content_width : box_state.content_height;
|
||||
}
|
||||
|
||||
bool FlexFormattingContext::is_cross_auto(Box const& box) const
|
||||
|
@ -327,60 +332,58 @@ bool FlexFormattingContext::is_main_axis_margin_second_auto(Box const& box) cons
|
|||
return box.computed_values().margin().bottom.is_length() && box.computed_values().margin().bottom.length().is_auto();
|
||||
}
|
||||
|
||||
void FlexFormattingContext::set_main_size(Box& box, float size)
|
||||
void FlexFormattingContext::set_main_size(Box const& box, float size)
|
||||
{
|
||||
if (is_row_layout())
|
||||
box.set_content_width(size);
|
||||
m_state.ensure(box).content_width = size;
|
||||
else
|
||||
box.set_content_height(size);
|
||||
m_state.ensure(box).content_height = size;
|
||||
}
|
||||
|
||||
void FlexFormattingContext::set_cross_size(Box& box, float size)
|
||||
void FlexFormattingContext::set_cross_size(Box const& box, float size)
|
||||
{
|
||||
if (is_row_layout())
|
||||
box.set_content_height(size);
|
||||
m_state.ensure(box).content_height = size;
|
||||
else
|
||||
box.set_content_width(size);
|
||||
m_state.ensure(box).content_width = size;
|
||||
}
|
||||
|
||||
void FlexFormattingContext::set_offset(Box& box, float main_offset, float cross_offset)
|
||||
void FlexFormattingContext::set_offset(Box const& box, float main_offset, float cross_offset)
|
||||
{
|
||||
if (is_row_layout())
|
||||
box.set_offset(main_offset, cross_offset);
|
||||
m_state.ensure(box).offset = Gfx::FloatPoint { main_offset, cross_offset };
|
||||
else
|
||||
box.set_offset(cross_offset, main_offset);
|
||||
m_state.ensure(box).offset = Gfx::FloatPoint { cross_offset, main_offset };
|
||||
}
|
||||
|
||||
void FlexFormattingContext::set_main_axis_first_margin(Box& box, float margin)
|
||||
void FlexFormattingContext::set_main_axis_first_margin(Box const& box, float margin)
|
||||
{
|
||||
if (is_row_layout())
|
||||
box.box_model().margin.left = margin;
|
||||
m_state.ensure(box).margin_left = margin;
|
||||
else
|
||||
box.box_model().margin.top = margin;
|
||||
m_state.ensure(box).margin_top = margin;
|
||||
}
|
||||
|
||||
void FlexFormattingContext::set_main_axis_second_margin(Box& box, float margin)
|
||||
void FlexFormattingContext::set_main_axis_second_margin(Box const& box, float margin)
|
||||
{
|
||||
if (is_row_layout())
|
||||
box.box_model().margin.right = margin;
|
||||
m_state.ensure(box).margin_right = margin;
|
||||
else
|
||||
box.box_model().margin.bottom = margin;
|
||||
m_state.ensure(box).margin_bottom = margin;
|
||||
}
|
||||
|
||||
float FlexFormattingContext::sum_of_margin_padding_border_in_main_axis(Box const& box) const
|
||||
{
|
||||
auto& margin = box.box_model().margin;
|
||||
auto& padding = box.box_model().padding;
|
||||
auto& border = box.box_model().border;
|
||||
auto const& box_state = m_state.get(box);
|
||||
|
||||
if (is_row_layout()) {
|
||||
return margin.left + margin.right
|
||||
+ padding.left + padding.right
|
||||
+ border.left + border.right;
|
||||
return box_state.margin_left + box_state.margin_right
|
||||
+ box_state.padding_left + box_state.padding_right
|
||||
+ box_state.border_left + box_state.border_right;
|
||||
} else {
|
||||
return margin.top + margin.bottom
|
||||
+ padding.top + padding.bottom
|
||||
+ border.top + border.bottom;
|
||||
return box_state.margin_top + box_state.margin_bottom
|
||||
+ box_state.padding_top + box_state.padding_bottom
|
||||
+ box_state.border_top + box_state.border_bottom;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,14 +391,15 @@ float FlexFormattingContext::sum_of_margin_padding_border_in_main_axis(Box const
|
|||
FlexFormattingContext::AvailableSpace FlexFormattingContext::determine_available_main_and_cross_space(bool& main_size_is_infinite, bool& main_is_constrained, bool& cross_is_constrained, float& main_min_size, float& main_max_size, float& cross_min_size, float& cross_max_size) const
|
||||
{
|
||||
auto containing_block_effective_main_size = [&](Box const& box) {
|
||||
auto& containing_block = *box.containing_block();
|
||||
if (is_row_layout()) {
|
||||
if (box.containing_block()->has_definite_width())
|
||||
return box.containing_block()->content_width();
|
||||
if (containing_block.has_definite_width())
|
||||
return m_state.get(containing_block).content_width;
|
||||
main_size_is_infinite = true;
|
||||
return NumericLimits<float>::max();
|
||||
} else {
|
||||
if (box.containing_block()->has_definite_height())
|
||||
return box.containing_block()->content_height();
|
||||
if (containing_block.has_definite_height())
|
||||
return m_state.get(containing_block).content_height;
|
||||
main_size_is_infinite = true;
|
||||
return NumericLimits<float>::max();
|
||||
}
|
||||
|
@ -456,7 +460,7 @@ FlexFormattingContext::AvailableSpace FlexFormattingContext::determine_available
|
|||
return AvailableSpace { .main = main_available_space, .cross = cross_available_space };
|
||||
}
|
||||
|
||||
float FlexFormattingContext::layout_for_maximum_main_size(Box& box)
|
||||
float FlexFormattingContext::layout_for_maximum_main_size(Box const& box)
|
||||
{
|
||||
bool main_constrained = false;
|
||||
if (is_row_layout()) {
|
||||
|
@ -477,17 +481,17 @@ float FlexFormattingContext::layout_for_maximum_main_size(Box& box)
|
|||
|
||||
if (is_row_layout()) {
|
||||
ifc.run(box, LayoutMode::OnlyRequiredLineBreaks);
|
||||
return box.content_width();
|
||||
return m_state.get(box).content_width;
|
||||
} else {
|
||||
ifc.run(box, LayoutMode::AllPossibleLineBreaks);
|
||||
return box.content_height();
|
||||
return m_state.get(box).content_height;
|
||||
}
|
||||
}
|
||||
if (is_row_layout()) {
|
||||
(void)layout_inside(box, LayoutMode::OnlyRequiredLineBreaks);
|
||||
return box.content_width();
|
||||
return m_state.get(box).content_width;
|
||||
} else {
|
||||
return BlockFormattingContext::compute_theoretical_height(box);
|
||||
return BlockFormattingContext::compute_theoretical_height(m_state, box);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -501,7 +505,7 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
|
|||
|
||||
// A. If the item has a definite used flex basis, that’s the flex base size.
|
||||
if (used_flex_basis.is_definite()) {
|
||||
auto specified_base_size = get_pixel_size(child_box, used_flex_basis.length_percentage.value());
|
||||
auto specified_base_size = get_pixel_size(m_state, child_box, used_flex_basis.length_percentage.value());
|
||||
if (specified_base_size == 0)
|
||||
return calculated_main_size(flex_item.box);
|
||||
return specified_base_size;
|
||||
|
@ -806,7 +810,7 @@ void FlexFormattingContext::resolve_flexible_lengths(float const main_available_
|
|||
}
|
||||
|
||||
// https://www.w3.org/TR/css-flexbox-1/#algo-cross-item
|
||||
float FlexFormattingContext::determine_hypothetical_cross_size_of_item(Box& box)
|
||||
float FlexFormattingContext::determine_hypothetical_cross_size_of_item(Box const& box)
|
||||
{
|
||||
bool cross_constrained = false;
|
||||
if (is_row_layout()) {
|
||||
|
@ -826,14 +830,15 @@ float FlexFormattingContext::determine_hypothetical_cross_size_of_item(Box& box)
|
|||
InlineFormattingContext ifc(m_state, block_container, bfc);
|
||||
ifc.run(box, LayoutMode::OnlyRequiredLineBreaks);
|
||||
|
||||
return is_row_layout() ? box.content_height() : box.content_width();
|
||||
auto const& box_state = m_state.get(box);
|
||||
return is_row_layout() ? box_state.content_height : box_state.content_width;
|
||||
}
|
||||
if (is_row_layout())
|
||||
return BlockFormattingContext::compute_theoretical_height(box);
|
||||
return BlockFormattingContext::compute_theoretical_height(m_state, box);
|
||||
|
||||
BlockFormattingContext context(m_state, verify_cast<BlockContainer>(box), this);
|
||||
context.compute_width(box);
|
||||
return box.content_width();
|
||||
return m_state.get(box).content_width;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-flexbox-1/#algo-cross-line
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue