Index: third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc |
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc |
index a4df599b4260b20826bd670faf5e8cba83ee4b94..c19fe3063d32b699c5df5d3e446a2b9ff3e3a65b 100644 |
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc |
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc |
@@ -6,8 +6,8 @@ |
#include "core/layout/ng/ng_absolute_utils.h" |
#include "core/layout/ng/ng_block_break_token.h" |
+#include "core/layout/ng/ng_block_child_iterator.h" |
#include "core/layout/ng/ng_box_fragment.h" |
-#include "core/layout/ng/ng_column_mapper.h" |
#include "core/layout/ng/ng_constraint_space.h" |
#include "core/layout/ng/ng_constraint_space_builder.h" |
#include "core/layout/ng/ng_fragment.h" |
@@ -294,12 +294,19 @@ bool IsNewFormattingContextForInFlowBlockLevelChild( |
return false; |
} |
+// Whether we've run out of space in this flow. If so, there will be no work |
+// left to do for this block in this fragmentainer. |
+bool IsOutOfSpace(const NGConstraintSpace& space, LayoutUnit content_size) { |
+ return space.HasBlockFragmentation() && |
+ content_size >= space.FragmentainerSpaceAvailable(); |
+} |
+ |
} // namespace |
NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm( |
NGBlockNode* node, |
NGConstraintSpace* constraint_space, |
- NGBreakToken* break_token) |
+ NGBlockBreakToken* break_token) |
: node_(node), |
constraint_space_(constraint_space), |
break_token_(break_token), |
@@ -367,8 +374,8 @@ RefPtr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { |
if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) |
sizes = ComputeMinAndMaxContentSizes(); |
- border_and_padding_ = |
- ComputeBorders(Style()) + ComputePadding(ConstraintSpace(), Style()); |
+ border_and_padding_ = ComputeBorders(ConstraintSpace(), Style()) + |
+ ComputePadding(ConstraintSpace(), Style()); |
LayoutUnit inline_size = |
ComputeInlineSizeForFragment(ConstraintSpace(), Style(), sizes); |
@@ -386,34 +393,24 @@ RefPtr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { |
adjusted_block_size -= border_and_padding_.BlockSum(); |
space_builder_ = new NGConstraintSpaceBuilder(constraint_space_); |
- if (Style().specifiesColumns()) { |
- space_builder_->SetFragmentationType(kFragmentColumn); |
- adjusted_inline_size = |
- ResolveUsedColumnInlineSize(adjusted_inline_size, Style()); |
- LayoutUnit inline_progression = |
- adjusted_inline_size + ResolveUsedColumnGap(Style()); |
- fragmentainer_mapper_ = |
- new NGColumnMapper(inline_progression, adjusted_block_size); |
- } |
- space_builder_->SetAvailableSize( |
- NGLogicalSize(adjusted_inline_size, adjusted_block_size)); |
- space_builder_->SetPercentageResolutionSize( |
- NGLogicalSize(adjusted_inline_size, adjusted_block_size)); |
+ space_builder_ |
+ ->SetAvailableSize( |
+ NGLogicalSize(adjusted_inline_size, adjusted_block_size)) |
+ .SetPercentageResolutionSize( |
+ NGLogicalSize(adjusted_inline_size, adjusted_block_size)); |
builder_->SetDirection(constraint_space_->Direction()); |
builder_->SetWritingMode(constraint_space_->WritingMode()); |
builder_->SetInlineSize(inline_size).SetBlockSize(block_size); |
- // TODO(glebl): fix multicol after the new margin collapsing/floats algorithm |
- // based on BFCOffset is checked in. |
- if (NGBlockBreakToken* token = CurrentBlockBreakToken()) { |
- // Resume after a previous break. |
- content_size_ = token->BreakOffset(); |
- current_child_ = token->InputNode(); |
- } else { |
- content_size_ = border_and_padding_.block_start; |
- current_child_ = node_->FirstChild(); |
- } |
+ NGBlockChildIterator child_iterator(node_->FirstChild(), break_token_); |
+ NGBlockChildIterator::Entry entry = child_iterator.NextChild(); |
+ current_child_ = entry.node; |
+ NGBreakToken* child_break_token = entry.token; |
+ |
+ // If we are resuming from a break token our start border and padding is |
+ // within a previous fragment. |
+ content_size_ = break_token_ ? LayoutUnit() : border_and_padding_.block_start; |
curr_margin_strut_ = ConstraintSpace().MarginStrut(); |
curr_bfc_offset_ = ConstraintSpace().BfcOffset(); |
@@ -445,13 +442,13 @@ RefPtr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { |
if (position == EPosition::kAbsolute || position == EPosition::kFixed) { |
builder_->AddOutOfFlowChildCandidate(current_block_child, |
GetChildSpaceOffset()); |
- current_child_ = current_block_child->NextSibling(); |
+ NGBlockChildIterator::Entry entry = child_iterator.NextChild(); |
+ current_child_ = entry.node; |
+ child_break_token = entry.token; |
continue; |
} |
} |
- DCHECK(!ConstraintSpace().HasBlockFragmentation() || |
- SpaceAvailableForCurrentChild() > LayoutUnit()); |
space_for_current_child_ = CreateConstraintSpaceForCurrentChild(); |
if (current_child_->Type() == NGLayoutInputNode::kLegacyInline) { |
@@ -460,12 +457,15 @@ RefPtr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { |
} |
RefPtr<NGLayoutResult> layout_result = |
- current_child_->Layout(space_for_current_child_); |
+ current_child_->Layout(space_for_current_child_, child_break_token); |
FinishCurrentChildLayout(layout_result); |
- if (!ProceedToNextUnfinishedSibling( |
- layout_result->PhysicalFragment().get())) |
+ entry = child_iterator.NextChild(); |
+ current_child_ = entry.node; |
+ child_break_token = entry.token; |
+ |
+ if (IsOutOfSpace(ConstraintSpace(), content_size_)) |
break; |
} |
@@ -486,7 +486,7 @@ RefPtr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { |
builder_->SetBlockSize(block_size); |
// Layout our absolute and fixed positioned children. |
- NGOutOfFlowLayoutPart(Style(), builder_.get()).Run(); |
+ NGOutOfFlowLayoutPart(ConstraintSpace(), Style(), builder_.get()).Run(); |
// Non-empty blocks always know their position in space: |
if (block_size) { |
@@ -566,6 +566,9 @@ void NGBlockLayoutAlgorithm::FinishCurrentChildLayout( |
// content_size_ or known fragment's BFC offset. |
WTF::Optional<NGLogicalOffset> bfc_offset; |
if (CurrentChildConstraintSpace().IsNewFormattingContext()) { |
+ // TODO(ikilpatrick): We may need to place ourself within the BFC |
+ // before a new formatting context child is laid out. (Not after layout as |
+ // is done here). |
curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
bfc_offset = curr_bfc_offset_; |
} else if (fragment.BfcOffset()) { |
@@ -589,11 +592,6 @@ void NGBlockLayoutAlgorithm::FinishCurrentChildLayout( |
} |
NGLogicalOffset logical_offset = CalculateLogicalOffset(bfc_offset); |
- if (fragmentainer_mapper_) |
- fragmentainer_mapper_->ToVisualOffset(logical_offset); |
- else |
- logical_offset.block_offset -= PreviousBreakOffset(); |
- |
// Update margin strut. |
curr_margin_strut_ = fragment.EndMarginStrut(); |
curr_margin_strut_.Append(curr_child_margins_.block_end); |
@@ -615,149 +613,44 @@ void NGBlockLayoutAlgorithm::FinishCurrentChildLayout( |
builder_->AddChild(layout_result, logical_offset); |
} |
-bool NGBlockLayoutAlgorithm::ProceedToNextUnfinishedSibling( |
- NGPhysicalFragment* child_fragment) { |
- DCHECK(current_child_); |
- NGBlockNode* finished_child = toNGBlockNode(current_child_); |
- current_child_ = current_child_->NextSibling(); |
- if (!ConstraintSpace().HasBlockFragmentation() && !fragmentainer_mapper_) |
- return true; |
- // If we're resuming layout after a fragmentainer break, we need to skip |
- // siblings that we're done with. We may have been able to fully lay out some |
- // node(s) preceding a node that we had to break inside (and therefore were |
- // not able to fully lay out). This happens when we have parallel flows [1], |
- // which are caused by floats, overflow, etc. |
- // |
- // [1] https://drafts.csswg.org/css-break/#parallel-flows |
- if (CurrentBlockBreakToken()) { |
- // TODO(layout-ng): Figure out if we need a better way to determine if the |
- // node is finished. Maybe something to encode in a break token? |
- // TODO(kojii): Handle inline children. |
- while (current_child_ && |
- current_child_->Type() == NGLayoutInputNode::kLegacyBlock && |
- toNGBlockNode(current_child_)->IsLayoutFinished()) { |
- current_child_ = current_child_->NextSibling(); |
- } |
- } |
- LayoutUnit break_offset = NextBreakOffset(); |
- bool is_out_of_space = content_size_ - PreviousBreakOffset() >= break_offset; |
- if (!HasPendingBreakToken()) { |
- bool child_broke = child_fragment->BreakToken(); |
- // This block needs to break if the child broke, or if we're out of space |
- // and there's more content waiting to be laid out. Otherwise, just bail |
- // now. |
- if (!child_broke && (!is_out_of_space || !current_child_)) |
- return true; |
- // Prepare a break token for this block, so that we know where to resume |
- // when the time comes for that. We may not be able to abort layout of this |
- // block right away, due to the posibility of parallel flows. We can only |
- // abort when we're out of space, or when there are no siblings left to |
- // process. |
- NGBlockBreakToken* token; |
- if (child_broke) { |
- // The child we just laid out was the first one to break. So that is |
- // where we need to resume. |
- token = new NGBlockBreakToken(finished_child, break_offset); |
- } else { |
- // Resume layout at the next sibling that needs layout. |
- DCHECK(current_child_); |
- token = |
- new NGBlockBreakToken(toNGBlockNode(current_child_), break_offset); |
- } |
- SetPendingBreakToken(token); |
- } |
- |
- if (!fragmentainer_mapper_) { |
- if (!is_out_of_space) |
- return true; |
- // We have run out of space in this flow, so there's no work left to do for |
- // this block in this fragmentainer. We should finalize the fragment and get |
- // back to the remaining content when laying out the next fragmentainer(s). |
- return false; |
- } |
- |
- if (is_out_of_space || !current_child_) { |
- NGBlockBreakToken* token = fragmentainer_mapper_->Advance(); |
- DCHECK(token || !is_out_of_space); |
- if (token) { |
- break_token_ = token; |
- content_size_ = token->BreakOffset(); |
- current_child_ = token->InputNode(); |
- } |
- } |
- return true; |
-} |
- |
-void NGBlockLayoutAlgorithm::SetPendingBreakToken(NGBlockBreakToken* token) { |
- if (fragmentainer_mapper_) |
- fragmentainer_mapper_->SetBreakToken(token); |
- else |
- builder_->SetBreakToken(token); |
-} |
- |
-bool NGBlockLayoutAlgorithm::HasPendingBreakToken() const { |
- if (fragmentainer_mapper_) |
- return fragmentainer_mapper_->HasBreakToken(); |
- return builder_->HasBreakToken(); |
-} |
- |
void NGBlockLayoutAlgorithm::FinalizeForFragmentation() { |
- LayoutUnit block_size = |
- ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_); |
- LayoutUnit previous_break_offset = PreviousBreakOffset(); |
- block_size -= previous_break_offset; |
- block_size = std::max(LayoutUnit(), block_size); |
- LayoutUnit space_left = ConstraintSpace().FragmentainerSpaceAvailable(); |
+ LayoutUnit used_block_size = |
+ break_token_ ? break_token_->UsedBlockSize() : LayoutUnit(); |
+ LayoutUnit block_size = ComputeBlockSizeForFragment( |
+ ConstraintSpace(), Style(), used_block_size + content_size_); |
+ |
+ block_size -= used_block_size; |
+ DCHECK_GE(block_size, LayoutUnit()) |
+ << "Adding and subtracting the used_block_size shouldn't leave the " |
+ "block_size for this fragment smaller than zero."; |
+ |
+ DCHECK(builder_->BfcOffset()) << "We must have our BfcOffset by this point " |
+ "to determine the space left in the flow."; |
+ LayoutUnit space_left = ConstraintSpace().FragmentainerSpaceAvailable() - |
+ builder_->BfcOffset().value().block_offset; |
DCHECK_GE(space_left, LayoutUnit()); |
- if (builder_->HasBreakToken()) { |
- // A break token is ready, which means that we're going to break |
- // before or inside a block-level child. |
+ |
+ if (builder_->DidBreak()) { |
+ // One of our children broke. Even if we fit within the remaining space we |
+ // need to prepare a break token. |
+ builder_->SetUsedBlockSize(std::min(space_left, block_size) + |
+ used_block_size); |
builder_->SetBlockSize(std::min(space_left, block_size)); |
builder_->SetBlockOverflow(space_left); |
return; |
} |
+ |
if (block_size > space_left) { |
// Need a break inside this block. |
- builder_->SetBreakToken(new NGBlockBreakToken(nullptr, NextBreakOffset())); |
+ builder_->SetUsedBlockSize(space_left + used_block_size); |
builder_->SetBlockSize(space_left); |
builder_->SetBlockOverflow(space_left); |
return; |
} |
+ |
// The end of the block fits in the current fragmentainer. |
builder_->SetBlockSize(block_size); |
- builder_->SetBlockOverflow(content_size_ - previous_break_offset); |
-} |
- |
-NGBlockBreakToken* NGBlockLayoutAlgorithm::CurrentBlockBreakToken() const { |
- NGBreakToken* token = break_token_; |
- if (!token || token->Type() != NGBreakToken::kBlockBreakToken) |
- return nullptr; |
- return toNGBlockBreakToken(token); |
-} |
- |
-LayoutUnit NGBlockLayoutAlgorithm::PreviousBreakOffset() const { |
- const NGBlockBreakToken* token = CurrentBlockBreakToken(); |
- return token ? token->BreakOffset() : LayoutUnit(); |
-} |
- |
-LayoutUnit NGBlockLayoutAlgorithm::NextBreakOffset() const { |
- if (fragmentainer_mapper_) |
- return fragmentainer_mapper_->NextBreakOffset(); |
- DCHECK(ConstraintSpace().HasBlockFragmentation()); |
- return PreviousBreakOffset() + |
- ConstraintSpace().FragmentainerSpaceAvailable(); |
-} |
- |
-LayoutUnit NGBlockLayoutAlgorithm::SpaceAvailableForCurrentChild() const { |
- LayoutUnit space_left; |
- if (fragmentainer_mapper_) |
- space_left = fragmentainer_mapper_->BlockSize(); |
- else if (ConstraintSpace().HasBlockFragmentation()) |
- space_left = ConstraintSpace().FragmentainerSpaceAvailable(); |
- else |
- return NGSizeIndefinite; |
- space_left -= BorderEdgeForCurrentChild() - PreviousBreakOffset(); |
- return space_left; |
+ builder_->SetBlockOverflow(content_size_); |
} |
NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins( |
@@ -809,8 +702,6 @@ NGBlockLayoutAlgorithm::CreateConstraintSpaceForCurrentChild() { |
.SetIsShrinkToFit( |
ShouldShrinkToFit(ConstraintSpace(), CurrentChildStyle())) |
.SetTextDirection(current_child_style.direction()); |
- LayoutUnit space_available = SpaceAvailableForCurrentChild(); |
- space_builder_->SetFragmentainerSpaceAvailable(space_available); |
// Clearance : |
// - *Always* collapse margins and update *container*'s BFC offset. |
@@ -851,6 +742,19 @@ NGBlockLayoutAlgorithm::CreateConstraintSpaceForCurrentChild() { |
space_builder_->SetBfcOffset(curr_bfc_offset_); |
+ LayoutUnit space_available; |
+ if (constraint_space_->HasBlockFragmentation()) { |
+ space_available = ConstraintSpace().FragmentainerSpaceAvailable(); |
+ // If a block establishes a new formatting context we must know our |
+ // position in the formatting context, and are able to adjust the |
+ // fragmentation line. |
+ if (is_new_bfc) { |
+ DCHECK(builder_->BfcOffset()); |
+ space_available -= curr_bfc_offset_.block_offset; |
+ } |
+ } |
+ space_builder_->SetFragmentainerSpaceAvailable(space_available); |
+ |
return space_builder_->ToConstraintSpace( |
FromPlatformWritingMode(current_child_style.getWritingMode())); |
} |