summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc')
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc451
1 files changed, 338 insertions, 113 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index 83273c21fe2..442a448d9ad 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -92,7 +92,9 @@ NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart(
container_node.IsFixedContainer(),
container_node.Style(),
container_space,
- container_builder) {}
+ container_builder) {
+ can_traverse_fragments_ = container_node.CanTraversePhysicalFragments();
+}
NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart(
bool is_absolute_container,
@@ -103,6 +105,7 @@ NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart(
base::Optional<LogicalSize> initial_containing_block_fixed_size)
: container_builder_(container_builder),
writing_mode_(container_style.GetWritingMode()),
+ default_writing_direction_(container_style.GetWritingDirection()),
is_absolute_container_(is_absolute_container),
is_fixed_container_(is_fixed_container),
has_block_fragmentation_(container_space.HasBlockFragmentation()) {
@@ -111,32 +114,49 @@ NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart(
->HasPositionedObjects())
return;
- default_containing_block_.writing_direction =
- container_style.GetWritingDirection();
+ default_containing_block_info_for_absolute_.writing_direction =
+ default_writing_direction_;
+ default_containing_block_info_for_fixed_.writing_direction =
+ default_writing_direction_;
const NGBoxStrut border_scrollbar =
container_builder->Borders() + container_builder->Scrollbar();
allow_first_tier_oof_cache_ = border_scrollbar.IsEmpty();
- default_containing_block_.content_size_for_absolute =
+ default_containing_block_info_for_absolute_.rect.size =
ShrinkLogicalSize(container_builder_->Size(), border_scrollbar);
- default_containing_block_.content_size_for_fixed =
+ default_containing_block_info_for_fixed_.rect.size =
initial_containing_block_fixed_size
? *initial_containing_block_fixed_size
- : default_containing_block_.content_size_for_absolute;
-
- default_containing_block_.container_offset = LogicalOffset(
- border_scrollbar.inline_start, border_scrollbar.block_start);
+ : default_containing_block_info_for_absolute_.rect.size;
+ LogicalOffset container_offset = {border_scrollbar.inline_start,
+ border_scrollbar.block_start};
+ default_containing_block_info_for_absolute_.rect.offset = container_offset;
+ default_containing_block_info_for_fixed_.rect.offset = container_offset;
}
void NGOutOfFlowLayoutPart::Run(const LayoutBox* only_layout) {
if (container_builder_->IsBlockFragmentationContextRoot() &&
- !has_block_fragmentation_ &&
- container_builder_->HasOutOfFlowFragmentainerDescendants()) {
- Vector<NGLogicalOutOfFlowPositionedNode> fragmentainer_descendants;
- container_builder_->SwapOutOfFlowFragmentainerDescendants(
- &fragmentainer_descendants);
-
- if (!fragmentainer_descendants.IsEmpty())
- LayoutFragmentainerDescendants(&fragmentainer_descendants);
+ !has_block_fragmentation_) {
+ if (container_builder_->HasOutOfFlowFragmentainerDescendants()) {
+ Vector<NGLogicalOutOfFlowPositionedNode> fragmentainer_descendants;
+ container_builder_->SwapOutOfFlowFragmentainerDescendants(
+ &fragmentainer_descendants);
+ DCHECK(!fragmentainer_descendants.IsEmpty());
+ LayoutUnit column_inline_progression = ColumnInlineProgression(
+ container_builder_->ChildAvailableSize().inline_size,
+ container_builder_->Style());
+ LayoutFragmentainerDescendants(&fragmentainer_descendants,
+ column_inline_progression);
+ }
+
+ if (container_builder_->HasMulticolsWithPendingOOFs()) {
+ NGContainerFragmentBuilder::MulticolCollection
+ multicols_with_pending_oofs;
+ container_builder_->SwapMulticolsWithPendingOOFs(
+ &multicols_with_pending_oofs);
+ DCHECK(!multicols_with_pending_oofs.IsEmpty());
+ for (LayoutBox* multicol : multicols_with_pending_oofs)
+ LayoutOOFsInMulticol(NGBlockNode(multicol));
+ }
}
const LayoutObject* current_container = container_builder_->GetLayoutObject();
@@ -253,7 +273,7 @@ bool NGOutOfFlowLayoutPart::SweepLegacyCandidates(
// TODO(layout-dev): Remove this once LayoutFlexibleBox is removed.
LayoutBox* layout_box = To<LayoutBox>(legacy_object);
if (layout_box->Parent()->IsFlexibleBox()) {
- LayoutFlexibleBox* parent = ToLayoutFlexibleBox(layout_box->Parent());
+ auto* parent = To<LayoutFlexibleBox>(layout_box->Parent());
if (parent->SetStaticPositionForPositionedLayout(*layout_box)) {
NGLogicalOutOfFlowPositionedNode candidate((NGBlockNode(layout_box)),
NGLogicalStaticPosition());
@@ -285,14 +305,16 @@ bool NGOutOfFlowLayoutPart::SweepLegacyCandidates(
// When fragmenting, the ContainingBlockInfo is not stored ahead of time and
// must be generated on demand. The reason being that during fragmentation, we
// wait to place positioned nodes until they've reached the fragmentation
-// context root. In such cases, we cannot use |default_containing_block_| since
-// the fragmentation root is not the containing block of the positioned nodes.
-// Rather, we must generate their ContainingBlockInfo based on the provided
-// |containing_block_fragment|.
-const NGOutOfFlowLayoutPart::ContainingBlockInfo&
+// context root. In such cases, we cannot use default |ContainingBlockInfo|
+// since the fragmentation root is not the containing block of the positioned
+// nodes. Rather, we must generate their ContainingBlockInfo based on the
+// provided |containing_block_fragment|.
+const NGOutOfFlowLayoutPart::ContainingBlockInfo
NGOutOfFlowLayoutPart::GetContainingBlockInfo(
const NGLogicalOutOfFlowPositionedNode& candidate,
const NGPhysicalContainerFragment* containing_block_fragment) {
+ if (candidate.containing_block_rect)
+ return {default_writing_direction_, *candidate.containing_block_rect};
if (candidate.inline_container) {
const auto it = containing_blocks_map_.find(candidate.inline_container);
DCHECK(it != containing_blocks_map_.end());
@@ -325,14 +347,17 @@ NGOutOfFlowLayoutPart::GetContainingBlockInfo(
LogicalOffset(border.inline_start, border.block_start);
container_offset += candidate.containing_block_offset;
- ContainingBlockInfo containing_block_info{writing_direction, content_size,
- content_size, container_offset};
+ ContainingBlockInfo containing_block_info{
+ writing_direction, LogicalRect(container_offset, content_size)};
return containing_blocks_map_
.insert(containing_block, containing_block_info)
.stored_value->value;
}
- return default_containing_block_;
+
+ return candidate.node.Style().GetPosition() == EPosition::kAbsolute
+ ? default_containing_block_info_for_absolute_
+ : default_containing_block_info_for_fixed_;
}
void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks(
@@ -349,13 +374,8 @@ void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks(
}
// Fetch the inline start/end fragment geometry.
- if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
- container_builder_->ComputeInlineContainerGeometry(
- &inline_container_fragments);
- } else {
- container_builder_->ComputeInlineContainerGeometryFromFragmentTree(
- &inline_container_fragments);
- }
+ container_builder_->ComputeInlineContainerGeometry(
+ &inline_container_fragments);
LogicalSize container_builder_size = container_builder_->Size();
PhysicalSize container_builder_physical_size =
@@ -420,7 +440,7 @@ void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks(
DCHECK(inline_cb_style);
const auto container_writing_direction =
- default_containing_block_.writing_direction;
+ default_containing_block_info_for_absolute_.writing_direction;
const auto inline_writing_direction =
inline_cb_style->GetWritingDirection();
NGBoxStrut inline_cb_borders = ComputeBordersForInline(*inline_cb_style);
@@ -478,8 +498,8 @@ void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks(
containing_blocks_map_.insert(
block_info.key,
- ContainingBlockInfo{inline_writing_direction, inline_cb_size,
- inline_cb_size, container_offset});
+ ContainingBlockInfo{inline_writing_direction,
+ LogicalRect(container_offset, inline_cb_size)});
}
}
@@ -545,15 +565,13 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutCandidate(
node.GetLayoutBox()->ContainingBlock()) ||
node.GetLayoutBox()->ContainingBlock()->IsTable());
- const auto default_writing_direction =
- default_containing_block_.writing_direction;
- const ContainingBlockInfo& container_info = GetContainingBlockInfo(candidate);
+ const ContainingBlockInfo container_info = GetContainingBlockInfo(candidate);
const ComputedStyle& candidate_style = node.Style();
const auto candidate_writing_direction =
candidate_style.GetWritingDirection();
- LogicalSize container_content_size =
- container_info.ContentSize(candidate_style.GetPosition());
+ LogicalSize container_content_size = container_info.rect.size;
+
PhysicalSize container_physical_content_size =
ToPhysicalSize(container_content_size, writing_mode_);
@@ -580,12 +598,12 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutCandidate(
// container's border-box). ng_absolute_utils expects the static position to
// be relative to the container's padding-box.
NGLogicalStaticPosition static_position = candidate.static_position;
- static_position.offset -= container_info.container_offset;
+ static_position.offset -= container_info.rect.offset;
NGLogicalStaticPosition candidate_static_position =
static_position
.ConvertToPhysical(
- {default_writing_direction, container_physical_content_size})
+ {default_writing_direction_, container_physical_content_size})
.ConvertToLogical(
{candidate_writing_direction, container_physical_content_size});
@@ -602,7 +620,7 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutCandidate(
scoped_refptr<const NGLayoutResult> layout_result =
Layout(node, candidate_constraint_space, candidate_static_position,
container_physical_content_size, container_info,
- default_writing_direction, only_layout);
+ default_writing_direction_, only_layout);
if (!freeze_scrollbars.has_value()) {
// Since out-of-flow positioning sets up a constraint space with fixed
@@ -626,8 +644,144 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutCandidate(
} while (true);
}
+// TODO(almaher): Look into moving this to NGColumnLayoutAlgorithm instead.
+void NGOutOfFlowLayoutPart::LayoutOOFsInMulticol(const NGBlockNode& multicol) {
+ Vector<NGLogicalOutOfFlowPositionedNode> oof_nodes_to_layout;
+ Vector<MulticolChildInfo> multicol_children;
+ const NGBlockBreakToken* previous_column_break_token = nullptr;
+ LayoutUnit column_inline_progression = kIndefiniteSize;
+
+ NGConstraintSpace multicol_constraint_space =
+ CreateConstraintSpaceForMulticol(multicol);
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialFragmentGeometry(multicol_constraint_space, multicol);
+ NGBoxFragmentBuilder multicol_container_builder =
+ CreateContainerBuilderForMulticol(multicol, multicol_constraint_space,
+ fragment_geometry);
+
+ // Accumulate all of the pending OOF positioned nodes that are stored inside
+ // |multicol|.
+ for (auto& multicol_fragment : multicol.GetLayoutBox()->PhysicalFragments()) {
+ const NGPhysicalBoxFragment* multicol_box_fragment =
+ To<NGPhysicalBoxFragment>(&multicol_fragment);
+
+ const ComputedStyle& style = multicol_box_fragment->Style();
+ WritingDirectionMode writing_direction = style.GetWritingDirection();
+ const WritingModeConverter converter(writing_direction,
+ multicol_box_fragment->Size());
+ const NGBlockBreakToken* current_column_break_token =
+ previous_column_break_token;
+ wtf_size_t current_column_index = 0;
+
+ if (column_inline_progression == kIndefiniteSize) {
+ // TODO(almaher): This should eventually include scrollbar, as well.
+ NGBoxStrut border_padding =
+ multicol_box_fragment->Borders().ConvertToLogical(writing_direction) +
+ multicol_box_fragment->Padding().ConvertToLogical(writing_direction);
+ LayoutUnit available_inline_size =
+ multicol_box_fragment->Size()
+ .ConvertToLogical(writing_direction.GetWritingMode())
+ .inline_size -
+ border_padding.InlineSum();
+ column_inline_progression =
+ ColumnInlineProgression(available_inline_size, style);
+ }
+
+ // Collect the children of the multicol fragments.
+ for (auto& child :
+ multicol_box_fragment->GetMutableChildrenForOutOfFlow().Children()) {
+ const auto* fragment = To<NGPhysicalContainerFragment>(child.get());
+ LogicalOffset offset =
+ converter.ToLogical(child.Offset(), fragment->Size());
+ if (fragment->IsFragmentainerBox()) {
+ current_column_break_token =
+ To<NGBlockBreakToken>(fragment->BreakToken());
+ current_column_index = multicol_children.size();
+ }
+
+ multicol_container_builder.AddChild(*fragment, offset);
+ multicol_children.emplace_back(MulticolChildInfo(&child));
+ }
+
+ // If a column fragment is updated with OOF children, we may need to update
+ // the reference to its break token in its parent's break token. There
+ // should be at most one column break token per parent break token
+ // (representing the last column laid out in that fragment). Thus, search
+ // for |current_column_break_token| in |multicol_box_fragment|'s list of
+ // child break tokens and update the stored MulticolChildInfo if found.
+ const NGBlockBreakToken* break_token =
+ To<NGBlockBreakToken>(multicol_box_fragment->BreakToken());
+ if (break_token && break_token->ChildBreakTokens().size()) {
+ // If there is a column break token, it will be the last item in its
+ // parent's list of break tokens.
+ const auto children = break_token->ChildBreakTokens();
+ const NGBlockBreakToken* child_token =
+ To<NGBlockBreakToken>(children[children.size() - 1]);
+ if (child_token == current_column_break_token) {
+ MulticolChildInfo& child_info = multicol_children[current_column_index];
+ child_info.parent_break_token = break_token;
+ }
+ }
+
+ // Convert the OOF fragmentainer descendants to the logical coordinate space
+ // and store the resulting nodes inside |oof_nodes_to_layout|.
+ for (const auto& descendant :
+ multicol_box_fragment->OutOfFlowPositionedFragmentainerDescendants()) {
+ const NGPhysicalContainerFragment* containing_block_fragment =
+ descendant.containing_block_fragment.get();
+ LogicalOffset containing_block_offset =
+ converter.ToLogical(descendant.containing_block_offset,
+ containing_block_fragment->Size());
+
+ // The containing block offset should be the offset from the top of the
+ // inner multicol to the start of the containing block (as if all of the
+ // columns are placed one on top of the other). When propagating OOFs
+ // in a nested fragmentation context, we miss the block contribution
+ // from columns in previous outer fragmentainers. Add the block size
+ // for such columns here to account for this.
+ if (previous_column_break_token) {
+ containing_block_offset.block_offset +=
+ previous_column_break_token->ConsumedBlockSize();
+ }
+
+ // The static position should remain relative to its containing block
+ // fragment.
+ const WritingModeConverter containing_block_converter(
+ writing_direction, containing_block_fragment->Size());
+ NGLogicalStaticPosition static_position =
+ descendant.static_position.ConvertToLogical(
+ containing_block_converter);
+
+ NGLogicalOutOfFlowPositionedNode node = {
+ descendant.node,
+ static_position,
+ descendant.inline_container,
+ /* needs_block_offset_adjustment */ false,
+ containing_block_offset,
+ containing_block_fragment};
+ oof_nodes_to_layout.push_back(node);
+ }
+ previous_column_break_token = current_column_break_token;
+ }
+ DCHECK(!oof_nodes_to_layout.IsEmpty());
+
+ // Clear out any OOF fragmentainer descendants that had been re-propagated
+ // when setting up |multicol_container_builder|.
+ // TODO(almaher): Avoid adding the descendants again to begin with.
+ multicol_container_builder.ClearOutOfFlowFragmentainerDescendants();
+
+ // Layout the OOF positioned elements inside the inner multicol.
+ NGOutOfFlowLayoutPart(multicol, multicol_constraint_space,
+ &multicol_container_builder)
+ .LayoutFragmentainerDescendants(
+ &oof_nodes_to_layout, column_inline_progression, &multicol_children);
+}
+
void NGOutOfFlowLayoutPart::LayoutFragmentainerDescendants(
- Vector<NGLogicalOutOfFlowPositionedNode>* descendants) {
+ Vector<NGLogicalOutOfFlowPositionedNode>* descendants,
+ LayoutUnit column_inline_progression,
+ Vector<MulticolChildInfo>* multicol_children) {
+ nested_fragmentation_context_ = multicol_children;
original_column_block_size_ =
ShrinkLogicalSize(container_builder_->InitialBorderBoxSize(),
container_builder_->BorderScrollbarPadding())
@@ -635,27 +789,38 @@ void NGOutOfFlowLayoutPart::LayoutFragmentainerDescendants(
while (descendants->size() > 0) {
for (auto& descendant : *descendants) {
- LayoutFragmentainerDescendant(descendant);
+ scoped_refptr<const NGLayoutResult> result =
+ LayoutFragmentainerDescendant(descendant);
+ // TODO(almaher): Handle nested OOFs and inner multicols with pending OOFs
+ // in the case of nested fragmentation.
+ container_builder_->PropagateOOFPositionedInfo(
+ result->PhysicalFragment(), result->OutOfFlowPositionedOffset());
}
- // Sweep any descendants that might have been added.
- // This happens when an absolute container has a fixed child.
+ // Sweep any descendants that might have been bubbled up from the fragment
+ // to the |container_builder_|. This happens when we have nested absolute
+ // position elements.
descendants->Shrink(0);
container_builder_->SwapOutOfFlowFragmentainerDescendants(descendants);
}
// Add all of the descendant layout results as children to the fragment at
// the associated index.
- for (const auto& descendant_result : fragmentainer_descendant_results_) {
- // We don't allow keys of 0, so shift the index back by 1 when adding to the
- // fragmentainer.
- wtf_size_t index = descendant_result.key - 1;
- const Vector<scoped_refptr<const NGLayoutResult>>& results =
- descendant_result.value;
- AddOOFResultsToFragmentainer(results, index);
+ wtf_size_t index = 0;
+ while (!fragmentainer_descendant_results_.IsEmpty()) {
+ // We don't allow keys of 0, so shift the index by 1.
+ auto it = fragmentainer_descendant_results_.find(index + 1);
+ if (it != fragmentainer_descendant_results_.end()) {
+ Vector<scoped_refptr<const NGLayoutResult>>& results = it->value;
+ AddOOFResultsToFragmentainer(results, index, column_inline_progression,
+ multicol_children);
+ fragmentainer_descendant_results_.erase(it);
+ }
+ index++;
}
}
-void NGOutOfFlowLayoutPart::LayoutFragmentainerDescendant(
+scoped_refptr<const NGLayoutResult>
+NGOutOfFlowLayoutPart::LayoutFragmentainerDescendant(
const NGLogicalOutOfFlowPositionedNode& descendant) {
NGBlockNode node = descendant.node;
const NGPhysicalContainerFragment* containing_block_fragment =
@@ -665,7 +830,7 @@ void NGOutOfFlowLayoutPart::LayoutFragmentainerDescendant(
containing_block_fragment->GetLayoutObject() ==
node.GetLayoutBox()->ContainingBlock());
- const ContainingBlockInfo& container_info =
+ const ContainingBlockInfo container_info =
GetContainingBlockInfo(descendant, containing_block_fragment);
const auto default_writing_direction =
containing_block_fragment->Style().GetWritingDirection();
@@ -673,19 +838,18 @@ void NGOutOfFlowLayoutPart::LayoutFragmentainerDescendant(
const auto descendant_writing_direction =
descendant_style.GetWritingDirection();
- LogicalSize container_content_size =
- container_info.ContentSize(descendant_style.GetPosition());
+ LogicalSize container_content_size = container_info.rect.size;
PhysicalSize container_physical_content_size = ToPhysicalSize(
container_content_size, default_writing_direction.GetWritingMode());
// Adjust the |static_position| (which is currently relative to the default
// container's border-box). ng_absolute_utils expects the static position to
// be relative to the container's padding-box. Since
- // |container_info.container_offset| is relative to its fragmentainer in this
+ // |container_info.rect.offset| is relative to its fragmentainer in this
// case, we also need to adjust the offset to account for this.
NGLogicalStaticPosition static_position = descendant.static_position;
static_position.offset -=
- container_info.container_offset - descendant.containing_block_offset;
+ container_info.rect.offset - descendant.containing_block_offset;
NGLogicalStaticPosition descendant_static_position =
static_position
@@ -702,10 +866,10 @@ void NGOutOfFlowLayoutPart::LayoutFragmentainerDescendant(
builder.SetPercentageResolutionSize(container_content_size);
NGConstraintSpace descendant_constraint_space = builder.ToConstraintSpace();
- Layout(node, descendant_constraint_space, descendant_static_position,
- container_physical_content_size, container_info,
- default_writing_direction, /* only_layout */ nullptr,
- /* is_fragmentainer_descendant */ true);
+ return Layout(node, descendant_constraint_space, descendant_static_position,
+ container_physical_content_size, container_info,
+ default_writing_direction, /* only_layout */ nullptr,
+ /* is_fragmentainer_descendant */ true);
}
scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
@@ -792,15 +956,17 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
if (is_replaced) {
ComputeReplacedSize(node, candidate_constraint_space, min_max_sizes,
&replaced_size, &aspect_ratio);
- has_aspect_ratio_without_intrinsic_size =
- !replaced_size && aspect_ratio && !aspect_ratio->IsEmpty();
+ DCHECK(replaced_size.has_value() != aspect_ratio.has_value());
+ has_aspect_ratio_without_intrinsic_size = aspect_ratio.has_value();
// If we only have aspect ratio, and no replaced size, intrinsic size
// defaults to 300x150. min_max_sizes gets computed from the intrinsic size.
// We reset the min_max_sizes because spec says that OOF-positioned size
// should not be constrained by intrinsic size in this case.
// https://www.w3.org/TR/CSS22/visudet.html#inline-replaced-width
- if (has_aspect_ratio_without_intrinsic_size)
+ if (has_aspect_ratio_without_intrinsic_size) {
min_max_sizes = MinMaxSizes{LayoutUnit(), LayoutUnit::NearlyMax()};
+ DCHECK(!aspect_ratio->IsEmpty()) << *aspect_ratio;
+ }
} else if (!candidate_style.AspectRatio().IsAuto()) {
has_aspect_ratio_without_intrinsic_size = true;
aspect_ratio = node.GetAspectRatio();
@@ -839,15 +1005,14 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
node_dimensions.size.inline_size));
}
- // TODO(almaher): Handle fragmentation separately for the case where
- // |absolute_needs_child_block_size| is true.
if (absolute_needs_child_block_size) {
DCHECK(!has_computed_block_dimensions);
layout_result = GenerateFragment(
node, container_content_size_in_candidate_writing_mode, block_estimate,
node_dimensions, /* block_offset */ LayoutUnit(),
/* break_token */ nullptr,
- /* fragmentainer_constraint_space */ nullptr);
+ /* fragmentainer_constraint_space */ nullptr,
+ /* should_use_fixed_block_size */ false);
// TODO(layout-dev): Handle abortions caused by block fragmentation.
DCHECK(layout_result->Status() != NGLayoutResult::kOutOfFragmentainerSpace);
@@ -867,6 +1032,7 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
container_writing_direction, &node_dimensions);
has_computed_block_dimensions = true;
}
+ block_estimate = node_dimensions.size.block_size;
// Calculate the offsets.
NGBoxStrut inset =
@@ -875,7 +1041,7 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
// |inset| is relative to the container's padding-box. Convert this to being
// relative to the default container's border-box.
- LogicalOffset offset = container_info.container_offset;
+ LogicalOffset offset = container_info.rect.offset;
offset.inline_offset += inset.inline_start;
offset.block_offset += inset.block_start;
@@ -887,10 +1053,10 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
DCHECK_GT(num_children, 0u);
ComputeStartFragmentIndexAndRelativeOffset(
container_info, default_writing_direction.GetWritingMode(),
- &start_index, &offset);
+ *block_estimate, &start_index, &offset);
}
- if (!only_layout) {
+ if (!only_layout && !can_traverse_fragments_) {
// Special case: oof css container is a split inline.
// When css container spans multiple anonymous blocks, its dimensions can
// only be computed by a block that is an ancestor of all fragments
@@ -939,6 +1105,10 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
}
}
+ // Reset the |layout_result| computed earlier to allow fragmentation in the
+ // next layout pass, if needed.
+ if (is_fragmentainer_descendant)
+ layout_result = nullptr;
const NGBlockBreakToken* break_token = nullptr;
do {
if (break_token) {
@@ -955,18 +1125,21 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
offset.block_offset = LayoutUnit();
}
- // Skip this step if we produced a fragment when estimating the
- // block-size.
+ // Skip this step if we produced a fragment that can be reused when
+ // estimating the block-size.
if (!layout_result) {
- block_estimate = node_dimensions.size.block_size;
- const NGConstraintSpace* fragmentainer_constraint_space =
- is_fragmentainer_descendant
- ? &GetFragmentainerConstraintSpace(start_index)
- : nullptr;
+ const NGConstraintSpace* fragmentainer_constraint_space = nullptr;
+ bool should_use_fixed_block_size = !!block_estimate;
+ if (is_fragmentainer_descendant) {
+ fragmentainer_constraint_space =
+ &GetFragmentainerConstraintSpace(start_index);
+ should_use_fixed_block_size &= !absolute_needs_child_block_size;
+ }
+
layout_result = GenerateFragment(
node, container_content_size_in_candidate_writing_mode,
block_estimate, node_dimensions, offset.block_offset, break_token,
- fragmentainer_constraint_space);
+ fragmentainer_constraint_space, should_use_fixed_block_size);
}
// TODO(layout-dev): Handle abortions caused by block fragmentation.
@@ -1001,6 +1174,10 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
bool NGOutOfFlowLayoutPart::IsContainingBlockForCandidate(
const NGLogicalOutOfFlowPositionedNode& candidate) {
+ // Column boxes are not allowed to be containing blocks.
+ if (container_builder_->IsFragmentainerBoxType())
+ return false;
+
EPosition position = candidate.node.Style().GetPosition();
// Candidates whose containing block is inline are always positioned inside
@@ -1027,7 +1204,8 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::GenerateFragment(
const NGLogicalOutOfFlowDimensions& node_dimensions,
const LayoutUnit block_offset,
const NGBlockBreakToken* break_token,
- const NGConstraintSpace* fragmentainer_constraint_space) {
+ const NGConstraintSpace* fragmentainer_constraint_space,
+ bool should_use_fixed_block_size) {
const auto& style = node.Style();
LayoutUnit inline_size = node_dimensions.size.inline_size;
@@ -1045,7 +1223,7 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::GenerateFragment(
builder.SetPercentageResolutionSize(
container_content_size_in_candidate_writing_mode);
builder.SetIsFixedInlineSize(true);
- if (block_estimate)
+ if (should_use_fixed_block_size)
builder.SetIsFixedBlockSize(true);
if (fragmentainer_constraint_space) {
SetupSpaceBuilderForFragmentation(*fragmentainer_constraint_space, node,
@@ -1059,16 +1237,30 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::GenerateFragment(
void NGOutOfFlowLayoutPart::AddOOFResultsToFragmentainer(
const Vector<scoped_refptr<const NGLayoutResult>>& results,
- wtf_size_t index) {
+ wtf_size_t index,
+ LayoutUnit column_inline_progression,
+ Vector<MulticolChildInfo>* multicol_children) {
wtf_size_t num_children = container_builder_->Children().size();
bool is_new_fragment = index >= num_children;
+ // If an OOF positioned element is in a nested context, and it fragments
+ // beyond the last fragmentainer, we don't create a new column for it.
+ // Rather, we will add it to the last existing fragmentainter at the
+ // correct inline offset.
+ bool create_new_fragment = is_new_fragment && !nested_fragmentation_context_;
+ bool add_to_last_fragment = is_new_fragment && nested_fragmentation_context_;
+
+ wtf_size_t num_new_fragmentainers;
+ if (is_new_fragment)
+ num_new_fragmentainers = index - num_children + 1;
+
// If |index| is greater than the number of current children, we need to add
// empty column fragments at all of the indexes leading up to |index|.
- if (index > num_children) {
+ if (create_new_fragment) {
const Vector<scoped_refptr<const NGLayoutResult>> empty_results;
while (index > num_children) {
- AddOOFResultsToFragmentainer(empty_results, /*index */ num_children);
+ AddOOFResultsToFragmentainer(empty_results, /*index */ num_children,
+ column_inline_progression);
num_children++;
}
DCHECK_EQ(index, container_builder_->Children().size());
@@ -1107,16 +1299,24 @@ void NGOutOfFlowLayoutPart::AddOOFResultsToFragmentainer(
// |algorithm| corresponds to the "mutable copy" of our original
// fragmentainer. As long as this "copy" hasn't been laid out via
// NGSimplifiedOOFLayoutAlgorithm::Layout, we can append new items to it.
- NGSimplifiedOOFLayoutAlgorithm algorithm(params, fragment, is_new_fragment);
+ NGSimplifiedOOFLayoutAlgorithm algorithm(params, fragment,
+ create_new_fragment);
for (const auto& result : results) {
- // TODO(bebeaudr): Is the offset returned by OutOfFlowPositionedOffset the
- // one to use?
- algorithm.AppendOutOfFlowResult(result,
- result->OutOfFlowPositionedOffset());
+ // If we are adding the result to the last fragmentainer rather than
+ // creating a new fragmentainer to hold it, adjust the inline offset as if
+ // we had created a new fragmentainer.
+ if (add_to_last_fragment) {
+ LogicalOffset oof_offset = result->OutOfFlowPositionedOffset();
+ oof_offset.inline_offset +=
+ column_inline_progression * num_new_fragmentainers;
+ result->GetMutableForOutOfFlow().SetOutOfFlowPositionedOffset(
+ oof_offset, allow_first_tier_oof_cache_);
+ }
+ algorithm.AppendOutOfFlowResult(result);
}
- if (is_new_fragment) {
+ if (create_new_fragment) {
LogicalOffset offset;
if (index != num_children - 1 && !container_builder_->Children()[index + 1]
.fragment->IsFragmentainerBox()) {
@@ -1131,25 +1331,35 @@ void NGOutOfFlowLayoutPart::AddOOFResultsToFragmentainer(
// TODO(almaher): Include trailing spanner margin.
offset.block_offset += spanner_size.block_size;
} else {
- // Calculate the column inline progression in order to calculate the
- // inline offset of any newly added column fragments.
- if (column_inline_progression_ == kIndefiniteSize) {
- LayoutUnit available_size =
- container_builder_->ChildAvailableSize().inline_size;
- const ComputedStyle& style = container_builder_->Style();
- LayoutUnit column_inline_size =
- ResolveUsedColumnInlineSize(available_size, style);
- column_inline_progression_ =
- column_inline_size + ResolveUsedColumnGap(available_size, style);
- }
offset = fragmentainer.offset;
- offset.inline_offset += column_inline_progression_;
+ offset.inline_offset += column_inline_progression;
}
- container_builder_->AddChild(algorithm.Layout()->PhysicalFragment(),
- offset);
+ scoped_refptr<const NGLayoutResult> new_result = algorithm.Layout();
+ node.AddColumnResult(new_result);
+ container_builder_->AddChild(new_result->PhysicalFragment(), offset);
} else {
- container_builder_->ReplaceChild(
- index, algorithm.Layout()->PhysicalFragment(), fragmentainer.offset);
+ scoped_refptr<const NGLayoutResult> new_result = algorithm.Layout();
+ node.ReplaceColumnResult(new_result, fragment);
+ const NGPhysicalContainerFragment* new_fragment =
+ &new_result->PhysicalFragment();
+ container_builder_->ReplaceChild(index, *new_fragment,
+ fragmentainer.offset);
+
+ if (nested_fragmentation_context_) {
+ // We are in a nested fragmentation context. Replace the column entry
+ // and break token directly in the existing multicol fragment.
+ DCHECK(multicol_children);
+ MulticolChildInfo& column_info = (*multicol_children)[index];
+ if (auto* parent_break_token = column_info.parent_break_token) {
+ DCHECK_GT(parent_break_token->ChildBreakTokens().size(), 0u);
+ parent_break_token->GetMutableForOutOfFlow().ReplaceChildBreakToken(
+ new_fragment->BreakToken(),
+ parent_break_token->ChildBreakTokens().size() - 1);
+ }
+ column_info.mutable_link->fragment->Release();
+ new (&column_info.mutable_link->fragment)
+ scoped_refptr<const NGPhysicalFragment>(std::move(new_fragment));
+ }
}
}
@@ -1195,7 +1405,8 @@ const NGConstraintSpace& NGOutOfFlowLayoutPart::GetFragmentainerConstraintSpace(
// If we are a new fragment and are separated from other columns by a
// spanner, compute the correct column block size to use.
- if (is_new_fragment && index != num_children - 1 &&
+ if (is_new_fragment && !nested_fragmentation_context_ &&
+ index != num_children - 1 &&
original_column_block_size_ != kIndefiniteSize &&
!container_builder_->Children()[index + 1]
.fragment->IsFragmentainerBox()) {
@@ -1241,6 +1452,7 @@ void NGOutOfFlowLayoutPart::AddOOFResultToFragmentainerResults(
void NGOutOfFlowLayoutPart::ComputeStartFragmentIndexAndRelativeOffset(
const ContainingBlockInfo& container_info,
WritingMode default_writing_mode,
+ LayoutUnit block_estimate,
wtf_size_t* start_index,
LogicalOffset* offset) const {
wtf_size_t child_index = 0;
@@ -1257,9 +1469,20 @@ void NGOutOfFlowLayoutPart::ComputeStartFragmentIndexAndRelativeOffset(
fragmentainer_block_size = child.fragment->Size()
.ConvertToLogical(default_writing_mode)
.block_size;
+ fragmentainer_block_size =
+ ClampedToValidFragmentainerCapacity(fragmentainer_block_size);
current_max_block_size += fragmentainer_block_size;
- if (offset->block_offset < current_max_block_size) {
+ // Edge case: an abspos with an height of 0 positioned exactly at the
+ // |current_max_block_size| won't be fragmented, so no break token will be
+ // produced - as we'd expect. However, the break token is used to compute
+ // the |fragmentainer_consumed_block_size_| stored on the
+ // |container_builder_| when we have a nested abspos. Because we use that
+ // value to position the nested abspos, its start offset would be off by
+ // exactly one fragmentainer block size.
+ if (offset->block_offset < current_max_block_size ||
+ (offset->block_offset == current_max_block_size &&
+ block_estimate == 0)) {
*start_index = child_index;
offset->block_offset -= used_block_size;
return;
@@ -1274,13 +1497,15 @@ void NGOutOfFlowLayoutPart::ComputeStartFragmentIndexAndRelativeOffset(
// If we are a new fragment and are separated from other columns by a
// spanner, compute the correct fragmentainer_block_size.
- if (original_column_block_size_ != kIndefiniteSize &&
+ if (!nested_fragmentation_context_ &&
+ original_column_block_size_ != kIndefiniteSize &&
!container_builder_->Children()[child_index - 1]
.fragment->IsFragmentainerBox()) {
fragmentainer_block_size =
original_column_block_size_ -
container_builder_->BlockOffsetForAdditionalColumns();
- fragmentainer_block_size = fragmentainer_block_size.ClampNegativeToZero();
+ fragmentainer_block_size =
+ ClampedToValidFragmentainerCapacity(fragmentainer_block_size);
}
wtf_size_t additional_fragment_count =