LibWeb: Restrict throwaway layout states to their subtree
Throwaway LayoutState instances used for intrinsic sizing should not access nodes outside the laid-out subtree. Make this explicit by setting a subtree root on each throwaway state, pre-populating only the immediate containing block, and using try_get() for any ancestor lookups — treating unavailable ancestors as indefinite rather than silently populating them with incorrectly-resolved values.
This commit is contained in:
parent
703bef336a
commit
edf1ca8f19
9 changed files with 145 additions and 68 deletions
|
|
@ -1358,8 +1358,7 @@ void Document::mark_svg_root_as_needing_relayout(Layout::SVGSVGBox& svg_root)
|
|||
|
||||
static void relayout_svg_root(Layout::SVGSVGBox& svg_root)
|
||||
{
|
||||
Layout::LayoutState layout_state;
|
||||
layout_state.set_subtree_root(svg_root);
|
||||
Layout::LayoutState layout_state(svg_root);
|
||||
|
||||
// Pre-populate the svg_root itself.
|
||||
if (auto const* paintable = svg_root.paintable_box())
|
||||
|
|
|
|||
|
|
@ -609,7 +609,9 @@ void BlockFormattingContext::layout_inline_children(BlockContainer const& block_
|
|||
auto used_width_px = context.automatic_content_width();
|
||||
// https://www.w3.org/TR/css-sizing-3/#sizing-values
|
||||
// Percentages are resolved against the width/height, as appropriate, of the box’s containing block.
|
||||
auto containing_block_width = m_state.get(*block_container.containing_block()).content_width();
|
||||
CSSPixels containing_block_width = 0;
|
||||
if (auto const* containing_block_used_values = m_state.try_get(*block_container.containing_block()))
|
||||
containing_block_width = containing_block_used_values->content_width();
|
||||
auto available_width = AvailableSize::make_definite(containing_block_width);
|
||||
if (!should_treat_max_width_as_none(block_container, available_space.width)) {
|
||||
auto max_width_px = calculate_inner_width(block_container, available_width, block_container.computed_values().max_width());
|
||||
|
|
@ -714,13 +716,20 @@ CSSPixels BlockFormattingContext::compute_auto_height_for_block_level_element(Bo
|
|||
|
||||
static CSSPixels containing_block_height_to_resolve_percentage_in_quirks_mode(Box const& box, LayoutState const& state)
|
||||
{
|
||||
auto content_height_of = [&](NodeWithStyleAndBoxModelMetrics const& node) -> CSSPixels {
|
||||
auto const* node_used_values = state.try_get(node);
|
||||
if (!node_used_values)
|
||||
return 0;
|
||||
return node_used_values->content_height();
|
||||
};
|
||||
|
||||
// https://quirks.spec.whatwg.org/#the-percentage-height-calculation-quirk
|
||||
auto containing_block = box.containing_block();
|
||||
while (containing_block) {
|
||||
// 1. Let element be the nearest ancestor containing block of element, if there is one.
|
||||
// Otherwise, return the initial containing block.
|
||||
if (containing_block->is_viewport()) {
|
||||
return state.get(*containing_block).content_height();
|
||||
return content_height_of(*containing_block);
|
||||
}
|
||||
|
||||
// 2. If element has a computed value of the display property that is table-cell, then return a
|
||||
|
|
@ -732,13 +741,13 @@ static CSSPixels containing_block_height_to_resolve_percentage_in_quirks_mode(Bo
|
|||
|
||||
// 3. If element has a computed value of the height property that is not auto, then return element.
|
||||
if (!containing_block->computed_values().height().is_auto()) {
|
||||
return state.get(*containing_block).content_height();
|
||||
return content_height_of(*containing_block);
|
||||
}
|
||||
|
||||
// 4. If element has a computed value of the position property that is absolute, or if element is a
|
||||
// not a block container or a table wrapper box, then return element.
|
||||
if (containing_block->is_absolutely_positioned() || !is<BlockContainer>(*containing_block) || is<TableWrapper>(*containing_block)) {
|
||||
return state.get(*containing_block).content_height();
|
||||
return content_height_of(*containing_block);
|
||||
}
|
||||
|
||||
// 5. Jump to the first step.
|
||||
|
|
@ -879,7 +888,9 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
|
|||
// For boxes with auto height but non-auto min-height, we need to determine if the content height is less than
|
||||
// min-height. If so, we run layout with min-height as the available height.
|
||||
if (should_treat_height_as_auto(box, available_space) && !box.computed_values().min_height().is_auto()) {
|
||||
LayoutState throwaway_state;
|
||||
LayoutState throwaway_state(box);
|
||||
throwaway_state.populate_node_from(m_state, *box.containing_block());
|
||||
|
||||
auto measuring_context = create_independent_formatting_context_if_needed(throwaway_state, LayoutMode::IntrinsicSizing, box);
|
||||
measuring_context->run(inner_available_space);
|
||||
auto content_height = measuring_context->automatic_content_height();
|
||||
|
|
|
|||
|
|
@ -312,9 +312,12 @@ CSSPixelSize FormattingContext::solve_replaced_size_constraint(CSSPixels input_w
|
|||
// https://www.w3.org/TR/CSS22/visudet.html#min-max-widths
|
||||
|
||||
auto const& containing_block = *box.non_anonymous_containing_block();
|
||||
auto const& containing_block_state = m_state.get(containing_block);
|
||||
auto width_of_containing_block = containing_block_state.content_width();
|
||||
auto height_of_containing_block = containing_block_state.content_height();
|
||||
CSSPixels width_of_containing_block = 0;
|
||||
CSSPixels height_of_containing_block = 0;
|
||||
if (auto const* containing_block_used_values = m_state.try_get(containing_block)) {
|
||||
width_of_containing_block = containing_block_used_values->content_width();
|
||||
height_of_containing_block = containing_block_used_values->content_height();
|
||||
}
|
||||
|
||||
auto min_width = box.computed_values().min_width().is_auto() ? 0 : box.computed_values().min_width().to_px(box, width_of_containing_block);
|
||||
auto specified_max_width = should_treat_max_width_as_none(box, available_space.width) ? input_width : box.computed_values().max_width().to_px(box, width_of_containing_block);
|
||||
|
|
@ -458,7 +461,8 @@ CSSPixels FormattingContext::compute_table_box_width_inside_table_wrapper(Box co
|
|||
});
|
||||
VERIFY(table_box.has_value());
|
||||
|
||||
LayoutState throwaway_state;
|
||||
LayoutState throwaway_state(box);
|
||||
throwaway_state.populate_node_from(m_state, *box.containing_block());
|
||||
|
||||
auto& table_box_state = throwaway_state.get_mutable(*table_box);
|
||||
auto const& table_box_computed_values = table_box->computed_values();
|
||||
|
|
@ -467,13 +471,6 @@ CSSPixels FormattingContext::compute_table_box_width_inside_table_wrapper(Box co
|
|||
table_box_state.padding_left = table_box_computed_values.padding().left().to_px_or_zero(*table_box, width_of_containing_block);
|
||||
table_box_state.padding_right = table_box_computed_values.padding().right().to_px_or_zero(*table_box, width_of_containing_block);
|
||||
|
||||
// Propagate the containing block width so percentage table widths can resolve instead of being treated as "auto".
|
||||
if (auto wrapper_containing_block = box.containing_block()) {
|
||||
auto const& containing_block_state = m_state.get(*wrapper_containing_block);
|
||||
if (containing_block_state.has_definite_width())
|
||||
throwaway_state.get_mutable(*wrapper_containing_block).set_content_width(containing_block_state.content_width());
|
||||
}
|
||||
|
||||
auto context = make<TableFormattingContext>(throwaway_state, LayoutMode::IntrinsicSizing, *table_box, this);
|
||||
context->run_until_width_calculation(m_state.get(*table_box).available_inner_space_or_constraints_from(available_space));
|
||||
|
||||
|
|
@ -499,7 +496,8 @@ CSSPixels FormattingContext::compute_table_box_height_inside_table_wrapper(Box c
|
|||
// table-wrapper can't have borders or paddings but it might have margin taken from table-root.
|
||||
auto available_height = height_of_containing_block - margin_top - margin_bottom;
|
||||
|
||||
LayoutState throwaway_state;
|
||||
LayoutState throwaway_state(box);
|
||||
throwaway_state.populate_node_from(m_state, *box.containing_block());
|
||||
|
||||
auto context = create_independent_formatting_context_if_needed(throwaway_state, LayoutMode::IntrinsicSizing, box);
|
||||
VERIFY(context);
|
||||
|
|
@ -665,7 +663,9 @@ CSSPixels FormattingContext::compute_height_for_replaced_element(Box const& box,
|
|||
// 10.6.6 Floating replaced elements
|
||||
// 10.6.10 'inline-block' replaced elements in normal flow
|
||||
|
||||
auto height_of_containing_block = m_state.get(*box.non_anonymous_containing_block()).content_height();
|
||||
CSSPixels height_of_containing_block = 0;
|
||||
if (auto const* containing_block_used_values = m_state.try_get(*box.non_anonymous_containing_block()))
|
||||
height_of_containing_block = containing_block_used_values->content_height();
|
||||
auto computed_width = should_treat_width_as_auto(box, available_space) ? CSS::Size::make_auto() : box.computed_values().width();
|
||||
auto computed_height = should_treat_height_as_auto(box, available_space) ? CSS::Size::make_auto() : box.computed_values().height();
|
||||
|
||||
|
|
@ -1687,7 +1687,8 @@ CSSPixels FormattingContext::calculate_min_content_width(Layout::Box const& box)
|
|||
if (cache.has_value())
|
||||
return cache.value();
|
||||
|
||||
LayoutState throwaway_state;
|
||||
LayoutState throwaway_state(box);
|
||||
throwaway_state.populate_node_from(m_state, *box.containing_block());
|
||||
|
||||
auto& box_state = throwaway_state.get_mutable(box);
|
||||
box_state.width_constraint = SizeConstraint::MinContent;
|
||||
|
|
@ -1721,7 +1722,8 @@ CSSPixels FormattingContext::calculate_max_content_width(Layout::Box const& box)
|
|||
if (cache.has_value())
|
||||
return cache.value();
|
||||
|
||||
LayoutState throwaway_state;
|
||||
LayoutState throwaway_state(box);
|
||||
throwaway_state.populate_node_from(m_state, *box.containing_block());
|
||||
|
||||
auto const& actual_box_state = m_state.get(box);
|
||||
|
||||
|
|
@ -1770,7 +1772,8 @@ CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box
|
|||
if (cache.has_value())
|
||||
return cache.value();
|
||||
|
||||
LayoutState throwaway_state;
|
||||
LayoutState throwaway_state(box);
|
||||
throwaway_state.populate_node_from(m_state, *box.containing_block());
|
||||
|
||||
auto& box_state = throwaway_state.get_mutable(box);
|
||||
box_state.height_constraint = SizeConstraint::MinContent;
|
||||
|
|
@ -1802,7 +1805,8 @@ CSSPixels FormattingContext::calculate_max_content_height(Layout::Box const& box
|
|||
if (cache_slot.has_value())
|
||||
return cache_slot.value();
|
||||
|
||||
LayoutState throwaway_state;
|
||||
LayoutState throwaway_state(box);
|
||||
throwaway_state.populate_node_from(m_state, *box.containing_block());
|
||||
|
||||
auto& box_state = throwaway_state.get_mutable(box);
|
||||
box_state.height_constraint = SizeConstraint::MaxContent;
|
||||
|
|
@ -1892,8 +1896,10 @@ CSSPixels FormattingContext::calculate_inner_height(Box const& box, AvailableSpa
|
|||
containing_block = containing_block->containing_block();
|
||||
}
|
||||
|
||||
if (containing_block && m_state.get(*containing_block).has_definite_height())
|
||||
height_of_containing_block = m_state.get(*containing_block).content_height();
|
||||
if (auto const* containing_block_used_values = containing_block ? m_state.try_get(*containing_block) : nullptr) {
|
||||
if (containing_block_used_values->has_definite_height())
|
||||
height_of_containing_block = containing_block_used_values->content_height();
|
||||
}
|
||||
}
|
||||
auto& computed_values = box.computed_values();
|
||||
|
||||
|
|
@ -2034,7 +2040,12 @@ bool FormattingContext::should_treat_height_as_auto(Box const& box, AvailableSpa
|
|||
auto containing_block = box.containing_block();
|
||||
while (containing_block && containing_block->is_anonymous())
|
||||
containing_block = containing_block->containing_block();
|
||||
if (containing_block && !m_state.get(*containing_block).has_definite_height())
|
||||
if (!containing_block)
|
||||
return true;
|
||||
auto const* containing_block_used_values = m_state.try_get(*containing_block);
|
||||
if (!containing_block_used_values)
|
||||
return true;
|
||||
if (!containing_block_used_values->has_definite_height())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2261,7 +2272,10 @@ bool FormattingContext::should_treat_max_width_as_none(Box const& box, Available
|
|||
return true;
|
||||
return false;
|
||||
}
|
||||
if (!m_state.get(*box.non_anonymous_containing_block()).has_definite_width())
|
||||
auto const* containing_block_used_values = m_state.try_get(*box.non_anonymous_containing_block());
|
||||
if (!containing_block_used_values)
|
||||
return true;
|
||||
if (!containing_block_used_values->has_definite_width())
|
||||
return true;
|
||||
}
|
||||
if (max_width.is_fit_content() && available_width.is_intrinsic_sizing_constraint())
|
||||
|
|
@ -2285,7 +2299,10 @@ bool FormattingContext::should_treat_max_height_as_none(Box const& box, Availabl
|
|||
if (max_height.contains_percentage()) {
|
||||
if (available_height.is_min_content())
|
||||
return false;
|
||||
if (!m_state.get(*box.non_anonymous_containing_block()).has_definite_height())
|
||||
auto const* containing_block_used_values = m_state.try_get(*box.non_anonymous_containing_block());
|
||||
if (!containing_block_used_values)
|
||||
return true;
|
||||
if (!containing_block_used_values->has_definite_height())
|
||||
return true;
|
||||
}
|
||||
if (max_height.is_fit_content() && available_height.is_intrinsic_sizing_constraint())
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@
|
|||
|
||||
namespace Web::Layout {
|
||||
|
||||
LayoutState::LayoutState(NodeWithStyle const& subtree_root)
|
||||
: m_subtree_root(&subtree_root)
|
||||
{
|
||||
}
|
||||
|
||||
LayoutState::~LayoutState()
|
||||
{
|
||||
}
|
||||
|
|
@ -52,6 +57,18 @@ LayoutState::UsedValues& LayoutState::populate_from_paintable(NodeWithStyle cons
|
|||
return used_values;
|
||||
}
|
||||
|
||||
LayoutState::UsedValues& LayoutState::populate_node_from(LayoutState const& source, NodeWithStyle const& node)
|
||||
{
|
||||
VERIFY(m_subtree_root);
|
||||
auto index = node.layout_index();
|
||||
VERIFY(!m_used_values_store.get(index));
|
||||
|
||||
auto& values = m_used_values_store.allocate(index);
|
||||
values = source.get(node);
|
||||
values.m_containing_block_used_values = nullptr;
|
||||
return values;
|
||||
}
|
||||
|
||||
LayoutState::UsedValues& LayoutState::ensure_used_values_for(NodeWithStyle const& node)
|
||||
{
|
||||
auto index = node.layout_index();
|
||||
|
|
@ -59,10 +76,16 @@ LayoutState::UsedValues& LayoutState::ensure_used_values_for(NodeWithStyle const
|
|||
if (auto* used_values = m_used_values_store.get(index))
|
||||
return *used_values;
|
||||
|
||||
// During subtree layout, all nodes outside the subtree must be pre-populated before running the formatting context
|
||||
// During subtree layout, only the subtree root and nodes inside the subtree are allowed.
|
||||
VERIFY(!m_subtree_root || m_subtree_root == &node || m_subtree_root->is_inclusive_ancestor_of(node));
|
||||
|
||||
auto const* containing_block_used_values = (node.is_viewport() || m_subtree_root == &node) ? nullptr : &get(*node.containing_block());
|
||||
UsedValues const* containing_block_used_values = nullptr;
|
||||
if (m_subtree_root == &node) {
|
||||
// For the subtree root, ancestor values are not available in the throwaway state.
|
||||
containing_block_used_values = try_get(*node.containing_block());
|
||||
} else if (!node.is_viewport()) {
|
||||
containing_block_used_values = &get(*node.containing_block());
|
||||
}
|
||||
|
||||
auto& used_values = m_used_values_store.allocate(index);
|
||||
used_values.set_node(node, containing_block_used_values);
|
||||
|
|
|
|||
|
|
@ -278,19 +278,20 @@ struct LayoutState {
|
|||
Optional<StaticPositionRect> m_static_position_rect;
|
||||
};
|
||||
|
||||
LayoutState() = default;
|
||||
explicit LayoutState(NodeWithStyle const& subtree_root);
|
||||
~LayoutState();
|
||||
|
||||
// Commits the used values produced by layout and builds a paintable tree.
|
||||
void commit(Box& root);
|
||||
|
||||
void set_subtree_root(NodeWithStyle const& node) { m_subtree_root = &node; }
|
||||
|
||||
void ensure_capacity(u32 node_count);
|
||||
|
||||
UsedValues& get_mutable(NodeWithStyle const&);
|
||||
UsedValues const& get(NodeWithStyle const&) const;
|
||||
|
||||
UsedValues& populate_from_paintable(NodeWithStyle const&, Painting::PaintableBox const&);
|
||||
UsedValues& populate_node_from(LayoutState const& source, NodeWithStyle const& node);
|
||||
|
||||
UsedValues const* try_get(NodeWithStyle const&) const;
|
||||
UsedValues* try_get_mutable(NodeWithStyle const&);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,22 @@ TableFormattingContext::TableFormattingContext(LayoutState& state, LayoutMode la
|
|||
|
||||
TableFormattingContext::~TableFormattingContext() = default;
|
||||
|
||||
CSSPixels TableFormattingContext::table_wrapper_containing_block_width() const
|
||||
{
|
||||
auto const* containing_block_used_values = m_state.try_get(*table_wrapper().containing_block());
|
||||
if (!containing_block_used_values)
|
||||
return 0;
|
||||
return containing_block_used_values->content_width();
|
||||
}
|
||||
|
||||
CSSPixels TableFormattingContext::table_wrapper_containing_block_height() const
|
||||
{
|
||||
auto const* containing_block_used_values = m_state.try_get(*table_wrapper().containing_block());
|
||||
if (!containing_block_used_values)
|
||||
return 0;
|
||||
return containing_block_used_values->content_height();
|
||||
}
|
||||
|
||||
static inline bool is_table_column_group(Box const& box)
|
||||
{
|
||||
return box.display().is_table_column_group();
|
||||
|
|
@ -119,16 +135,17 @@ void TableFormattingContext::compute_constrainedness()
|
|||
void TableFormattingContext::compute_cell_measures()
|
||||
{
|
||||
// Implements https://www.w3.org/TR/css-tables-3/#computing-cell-measures.
|
||||
auto const& containing_block = m_state.get(*table_wrapper().containing_block());
|
||||
auto containing_block_width = table_wrapper_containing_block_width();
|
||||
auto containing_block_height = table_wrapper_containing_block_height();
|
||||
|
||||
compute_constrainedness();
|
||||
|
||||
for (auto& cell : m_cells) {
|
||||
auto const& computed_values = cell.box->computed_values();
|
||||
CSSPixels padding_top = computed_values.padding().top().to_px_or_zero(cell.box, containing_block.content_height());
|
||||
CSSPixels padding_bottom = computed_values.padding().bottom().to_px_or_zero(cell.box, containing_block.content_height());
|
||||
CSSPixels padding_left = computed_values.padding().left().to_px_or_zero(cell.box, containing_block.content_width());
|
||||
CSSPixels padding_right = computed_values.padding().right().to_px_or_zero(cell.box, containing_block.content_width());
|
||||
CSSPixels padding_top = computed_values.padding().top().to_px_or_zero(cell.box, containing_block_height);
|
||||
CSSPixels padding_bottom = computed_values.padding().bottom().to_px_or_zero(cell.box, containing_block_height);
|
||||
CSSPixels padding_left = computed_values.padding().left().to_px_or_zero(cell.box, containing_block_width);
|
||||
CSSPixels padding_right = computed_values.padding().right().to_px_or_zero(cell.box, containing_block_width);
|
||||
|
||||
auto const& cell_state = m_state.get(cell.box);
|
||||
auto use_collapsing_borders_model = cell_state.override_borders_data().has_value();
|
||||
|
|
@ -144,11 +161,11 @@ void TableFormattingContext::compute_cell_measures()
|
|||
auto max_content_height = calculate_max_content_height(cell.box, min_content_width);
|
||||
|
||||
// The outer min-content height of a table-cell is max(min-height, min-content height) adjusted by the cell intrinsic offsets.
|
||||
auto min_height = computed_values.min_height().to_px(cell.box, containing_block.content_height());
|
||||
auto min_height = computed_values.min_height().to_px(cell.box, containing_block_height);
|
||||
auto cell_intrinsic_height_offsets = padding_top + padding_bottom + border_top + border_bottom;
|
||||
cell.outer_min_height = max(min_height, min_content_height) + cell_intrinsic_height_offsets;
|
||||
// The outer min-content width of a table-cell is max(min-width, min-content width) adjusted by the cell intrinsic offsets.
|
||||
auto min_width = computed_values.min_width().to_px(cell.box, containing_block.content_width());
|
||||
auto min_width = computed_values.min_width().to_px(cell.box, containing_block_width);
|
||||
auto cell_intrinsic_width_offsets = padding_left + padding_right + border_left + border_right;
|
||||
// For fixed mode, according to https://www.w3.org/TR/css-tables-3/#computing-column-measures:
|
||||
// The min-content and max-content width of cells is considered zero unless they are directly specified as a length-percentage,
|
||||
|
|
@ -161,8 +178,8 @@ void TableFormattingContext::compute_cell_measures()
|
|||
// The tables specification isn't explicit on how to use the height and max-height CSS properties in the outer max-content formulas.
|
||||
// However, during this early phase we don't have enough information to resolve percentage sizes yet and the formulas for outer sizes
|
||||
// in the specification give enough clues to pick defaults in a way that makes sense.
|
||||
auto height = computed_values.height().is_length() ? computed_values.height().to_px(cell.box, containing_block.content_height()) : 0;
|
||||
auto max_height = computed_values.max_height().is_length() ? computed_values.max_height().to_px(cell.box, containing_block.content_height()) : CSSPixels::max();
|
||||
auto height = computed_values.height().is_length() ? computed_values.height().to_px(cell.box, containing_block_height) : 0;
|
||||
auto max_height = computed_values.max_height().is_length() ? computed_values.max_height().to_px(cell.box, containing_block_height) : CSSPixels::max();
|
||||
if (m_rows[cell.row_index].is_constrained) {
|
||||
// The outer max-content height of a table-cell in a constrained row is
|
||||
// max(min-height, height, min-content height, min(max-height, height)) adjusted by the cell intrinsic offsets.
|
||||
|
|
@ -175,8 +192,8 @@ void TableFormattingContext::compute_cell_measures()
|
|||
}
|
||||
|
||||
// See the explanation for height and max_height above.
|
||||
auto width = computed_values.width().is_length() ? computed_values.width().to_px(cell.box, containing_block.content_width()) : 0;
|
||||
auto max_width = computed_values.max_width().is_length() ? computed_values.max_width().to_px(cell.box, containing_block.content_width()) : CSSPixels::max();
|
||||
auto width = computed_values.width().is_length() ? computed_values.width().to_px(cell.box, containing_block_width) : 0;
|
||||
auto max_width = computed_values.max_width().is_length() ? computed_values.max_width().to_px(cell.box, containing_block_width) : CSSPixels::max();
|
||||
if (use_fixed_mode_layout() && !width_is_specified_length_or_percentage) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -195,15 +212,16 @@ void TableFormattingContext::compute_cell_measures()
|
|||
|
||||
void TableFormattingContext::compute_outer_content_sizes()
|
||||
{
|
||||
auto const& containing_block = m_state.get(*table_wrapper().containing_block());
|
||||
auto containing_block_width = table_wrapper_containing_block_width();
|
||||
auto containing_block_height = table_wrapper_containing_block_height();
|
||||
|
||||
size_t column_index = 0;
|
||||
TableGrid::for_each_child_box_matching(table_box(), is_table_column_group, [&](auto& column_group_box) {
|
||||
TableGrid::for_each_child_box_matching(column_group_box, is_table_column, [&](auto& column_box) {
|
||||
auto const& computed_values = column_box.computed_values();
|
||||
auto min_width = computed_values.min_width().to_px(column_box, containing_block.content_width());
|
||||
auto max_width = computed_values.max_width().is_length() ? computed_values.max_width().to_px(column_box, containing_block.content_width()) : CSSPixels::max();
|
||||
auto width = computed_values.width().to_px(column_box, containing_block.content_width());
|
||||
auto min_width = computed_values.min_width().to_px(column_box, containing_block_width);
|
||||
auto max_width = computed_values.max_width().is_length() ? computed_values.max_width().to_px(column_box, containing_block_width) : CSSPixels::max();
|
||||
auto width = computed_values.width().to_px(column_box, containing_block_width);
|
||||
// The outer min-content width of a table-column or table-column-group is max(min-width, width).
|
||||
m_columns[column_index].min_size = max(min_width, width);
|
||||
// The outer max-content width of a table-column or table-column-group is max(min-width, min(max-width, width)).
|
||||
|
|
@ -216,9 +234,9 @@ void TableFormattingContext::compute_outer_content_sizes()
|
|||
|
||||
for (auto& row : m_rows) {
|
||||
auto const& computed_values = row.box->computed_values();
|
||||
auto min_height = computed_values.min_height().to_px(row.box, containing_block.content_height());
|
||||
auto max_height = computed_values.max_height().is_length() ? computed_values.max_height().to_px(row.box, containing_block.content_height()) : CSSPixels::max();
|
||||
auto height = computed_values.height().to_px(row.box, containing_block.content_height());
|
||||
auto min_height = computed_values.min_height().to_px(row.box, containing_block_height);
|
||||
auto max_height = computed_values.max_height().is_length() ? computed_values.max_height().to_px(row.box, containing_block_height) : CSSPixels::max();
|
||||
auto height = computed_values.height().to_px(row.box, containing_block_height);
|
||||
// The outer min-content height of a table-row or table-row-group is max(min-height, height).
|
||||
row.min_size = max(min_height, height);
|
||||
// The outer max-content height of a table-row or table-row-group is max(min-height, min(max-height, height)).
|
||||
|
|
@ -229,12 +247,12 @@ void TableFormattingContext::compute_outer_content_sizes()
|
|||
template<>
|
||||
void TableFormattingContext::initialize_table_measures<TableFormattingContext::Row>()
|
||||
{
|
||||
auto const& containing_block = m_state.get(*table_wrapper().containing_block());
|
||||
auto containing_block_height = table_wrapper_containing_block_height();
|
||||
|
||||
for (auto& cell : m_cells) {
|
||||
auto const& computed_values = cell.box->computed_values();
|
||||
if (cell.row_span == 1) {
|
||||
auto specified_height = computed_values.height().to_px(cell.box, containing_block.content_height());
|
||||
auto specified_height = computed_values.height().to_px(cell.box, containing_block_height);
|
||||
// https://www.w3.org/TR/css-tables-3/#row-layout makes specified cell height part of the initialization formula for row table measures:
|
||||
// This is done by running the same algorithm as the column measurement, with the span=1 value being initialized (for min-content) with
|
||||
// the largest of the resulting height of the previous row layout, the height specified on the corresponding table-row (if any), and
|
||||
|
|
@ -464,9 +482,15 @@ CSSPixels TableFormattingContext::compute_capmin()
|
|||
return capmin;
|
||||
}
|
||||
|
||||
static bool width_is_auto_relative_to_state(CSS::Size const& width, LayoutState::UsedValues const& state)
|
||||
static bool width_is_auto_or_indefinite_percentage(CSS::Size const& width, LayoutState::UsedValues const* containing_block_state)
|
||||
{
|
||||
return width.is_auto() || (width.contains_percentage() && !state.has_definite_width());
|
||||
if (width.is_auto())
|
||||
return true;
|
||||
if (width.contains_percentage()) {
|
||||
if (!containing_block_state || !containing_block_state->has_definite_width())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TableFormattingContext::compute_table_width()
|
||||
|
|
@ -481,8 +505,7 @@ void TableFormattingContext::compute_table_width()
|
|||
|
||||
// Percentages on 'width' and 'height' on the table are relative to the table wrapper box's containing block,
|
||||
// not the table wrapper box itself.
|
||||
auto const& containing_block_state = m_state.get(*table_wrapper().containing_block());
|
||||
CSSPixels width_of_table_wrapper_containing_block = containing_block_state.content_width();
|
||||
CSSPixels width_of_table_wrapper_containing_block = table_wrapper_containing_block_width();
|
||||
|
||||
// Compute undistributable space due to border spacing: https://www.w3.org/TR/css-tables-3/#computing-undistributable-space.
|
||||
auto undistributable_space = (m_columns.size() + 1) * border_spacing_horizontal();
|
||||
|
|
@ -514,7 +537,7 @@ void TableFormattingContext::compute_table_width()
|
|||
used_width = grid_min;
|
||||
} else if (m_available_space->width.is_max_content()) {
|
||||
used_width = grid_max;
|
||||
} else if (width_is_auto_relative_to_state(computed_values.width(), containing_block_state)) {
|
||||
} else if (width_is_auto_or_indefinite_percentage(computed_values.width(), m_state.try_get(*table_wrapper().containing_block()))) {
|
||||
// If the table-root has 'width: auto', the used width is the greater of
|
||||
// min(GRIDMAX, the table’s containing block width), the used min-width of the table.
|
||||
if (width_of_table_containing_block.is_definite())
|
||||
|
|
@ -948,7 +971,7 @@ void TableFormattingContext::compute_table_height()
|
|||
// If the table has a height property with a value other than auto, it is treated as a minimum height for the
|
||||
// table grid, and will eventually be distributed to the height of the rows if their collective minimum height
|
||||
// ends up smaller than this number.
|
||||
CSSPixels height_of_table_containing_block = m_state.get(*table_wrapper().containing_block()).content_height();
|
||||
CSSPixels height_of_table_containing_block = table_wrapper_containing_block_height();
|
||||
auto specified_table_height = table_box().computed_values().height().to_px(table_box(), height_of_table_containing_block);
|
||||
if (table_box().computed_values().box_sizing() == CSS::BoxSizing::BorderBox) {
|
||||
auto const& table_state = m_state.get(table_box());
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@ private:
|
|||
|
||||
bool use_fixed_mode_layout() const;
|
||||
|
||||
CSSPixels table_wrapper_containing_block_width() const;
|
||||
CSSPixels table_wrapper_containing_block_height() const;
|
||||
|
||||
CSSPixels m_table_height { 0 };
|
||||
CSSPixels m_automatic_content_height { 0 };
|
||||
|
||||
|
|
|
|||
|
|
@ -4,20 +4,20 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children
|
|||
Box <div.grid-container> at [8,8] [0+0+0 784 0+0+0] [0+0+0 18 0+0+0] [GFC] children: not-inline
|
||||
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
|
||||
TextNode <#text> (not painted)
|
||||
BlockContainer <div.grid-item> at [8,8] [0+0+0 100 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline
|
||||
BlockContainer <div.grid-item> at [8,8] [0+0+0 98 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline
|
||||
frag 0 from TextNode start: 0, length: 11, rect: [8,8 93.765625x18] baseline: 13.796875
|
||||
"min-content"
|
||||
TextNode <#text> (not painted)
|
||||
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
|
||||
TextNode <#text> (not painted)
|
||||
BlockContainer <div.grid-item> at [208,8] [0+0+0 98.640625 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline
|
||||
frag 0 from TextNode start: 0, length: 11, rect: [208,8 98.640625x18] baseline: 13.796875
|
||||
BlockContainer <div.grid-item> at [204,8] [0+0+0 98.640625 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline
|
||||
frag 0 from TextNode start: 0, length: 11, rect: [204,8 98.640625x18] baseline: 13.796875
|
||||
"max-content"
|
||||
TextNode <#text> (not painted)
|
||||
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
|
||||
TextNode <#text> (not painted)
|
||||
BlockContainer <div.grid-item> at [306.640625,8] [0+0+0 485.359375 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline
|
||||
frag 0 from TextNode start: 0, length: 3, rect: [306.640625,8 21.609375x18] baseline: 13.796875
|
||||
BlockContainer <div.grid-item> at [302.640625,8] [0+0+0 489.359375 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline
|
||||
frag 0 from TextNode start: 0, length: 3, rect: [302.640625,8 21.609375x18] baseline: 13.796875
|
||||
"1fr"
|
||||
TextNode <#text> (not painted)
|
||||
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
|
||||
|
|
@ -27,11 +27,11 @@ ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
|||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x34]
|
||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x18]
|
||||
PaintableBox (Box<DIV>.grid-container) [8,8 784x18]
|
||||
PaintableWithLines (BlockContainer<DIV>.grid-item) [8,8 100x18]
|
||||
PaintableWithLines (BlockContainer<DIV>.grid-item) [8,8 98x18]
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer<DIV>.grid-item) [208,8 98.640625x18]
|
||||
PaintableWithLines (BlockContainer<DIV>.grid-item) [204,8 98.640625x18]
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer<DIV>.grid-item) [306.640625,8 485.359375x18]
|
||||
PaintableWithLines (BlockContainer<DIV>.grid-item) [302.640625,8 489.359375x18]
|
||||
TextPaintable (TextNode<#text>)
|
||||
|
||||
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children
|
|||
TextNode <#text> (not painted)
|
||||
TextNode <#text> (not painted)
|
||||
TableWrapper <(anonymous)> at [125.59375,8] floating [0+0+0 478.234375 0+0+0] [0+0+0 24 0+0+0] [BFC] children: not-inline
|
||||
Box <table.middle> at [125.59375,8] table-box [0+0+0 478.234375 0+0+190.328125] [0+0+0 24 0+0+0] [TFC] children: not-inline
|
||||
Box <table.middle> at [125.59375,8] table-box [0+0+0 478.234375 0+0+186.515625] [0+0+0 24 0+0+0] [TFC] children: not-inline
|
||||
BlockContainer <(anonymous)> (not painted) children: inline
|
||||
TextNode <#text> (not painted)
|
||||
Box <tbody> at [127.59375,10] table-row-group [0+0+0 474.234375 0+0+0] [0+0+0 20 0+0+0] children: not-inline
|
||||
|
|
|
|||
Loading…
Reference in a new issue