| 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 ef259608b6a7629e153cdb1c81ab18ab62122dbc..b2852ff6cf6946a7714c68e2bf7acf95ccd42b5e 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
|
| @@ -4,8 +4,9 @@
|
|
|
| #include "core/layout/ng/ng_block_layout_algorithm.h"
|
|
|
| +#include "core/layout/ng/ng_block_break_token.h"
|
| #include "core/layout/ng/ng_box_fragment.h"
|
| -#include "core/layout/ng/ng_break_token.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"
|
| @@ -274,20 +275,30 @@ NGLayoutStatus NGBlockLayoutAlgorithm::Layout(
|
| 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));
|
|
|
| - content_size_ = border_and_padding_.block_start;
|
| -
|
| builder_ = new NGFragmentBuilder(NGPhysicalFragment::kFragmentBox);
|
| builder_->SetDirection(constraint_space_->Direction());
|
| builder_->SetWritingMode(constraint_space_->WritingMode());
|
| builder_->SetInlineSize(inline_size).SetBlockSize(block_size);
|
|
|
| - current_child_ = first_child_;
|
| + 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_ = first_child_;
|
| + }
|
| +
|
| layout_state_ = kStatePrepareForChildLayout;
|
| return kNotFinished;
|
| }
|
| @@ -300,6 +311,8 @@ NGLayoutStatus NGBlockLayoutAlgorithm::Layout(
|
| current_child_ = current_child_->NextSibling();
|
| return kNotFinished;
|
| }
|
| + DCHECK(!ConstraintSpace().HasBlockFragmentation() ||
|
| + SpaceAvailableForCurrentChild() > LayoutUnit());
|
| space_for_current_child_ = CreateConstraintSpaceForCurrentChild();
|
| *algorithm_out = NGLayoutInputNode::AlgorithmForInputNode(
|
| current_child_, space_for_current_child_);
|
| @@ -336,8 +349,11 @@ NGLayoutStatus NGBlockLayoutAlgorithm::Layout(
|
| FinishCurrentChildLayout(new NGBoxFragment(
|
| ConstraintSpace().WritingMode(), ConstraintSpace().Direction(),
|
| toNGPhysicalBoxFragment(child_fragment)));
|
| - current_child_ = current_child_->NextSibling();
|
| - layout_state_ = kStatePrepareForChildLayout;
|
| +
|
| + if (ProceedToNextUnfinishedSibling(child_fragment))
|
| + layout_state_ = kStatePrepareForChildLayout;
|
| + else
|
| + layout_state_ = kStateFinalize;
|
| return kNotFinished;
|
| }
|
| case kStateOutOfFlowLayout:
|
| @@ -347,6 +363,10 @@ NGLayoutStatus NGBlockLayoutAlgorithm::Layout(
|
| case kStateFinalize: {
|
| builder_->SetInlineOverflow(max_inline_size_)
|
| .SetBlockOverflow(content_size_);
|
| +
|
| + if (ConstraintSpace().HasBlockFragmentation())
|
| + FinalizeForFragmentation();
|
| +
|
| *fragment_out = builder_->ToBoxFragment();
|
| layout_state_ = kStateInit;
|
| return kNewFragment;
|
| @@ -370,6 +390,10 @@ void NGBlockLayoutAlgorithm::FinishCurrentChildLayout(NGFragment* fragment) {
|
| &child_margins);
|
| fragment_offset = PositionFragment(*fragment, child_margins);
|
| }
|
| + if (fragmentainer_mapper_)
|
| + fragmentainer_mapper_->ToVisualOffset(fragment_offset);
|
| + else
|
| + fragment_offset.block_offset -= PreviousBreakOffset();
|
| builder_->AddChild(fragment, fragment_offset);
|
| }
|
|
|
| @@ -401,6 +425,146 @@ bool NGBlockLayoutAlgorithm::LayoutOutOfFlowChild() {
|
| return false;
|
| }
|
|
|
| +bool NGBlockLayoutAlgorithm::ProceedToNextUnfinishedSibling(
|
| + NGPhysicalFragment* child_fragment) {
|
| + DCHECK(current_child_);
|
| + NGBlockNode* finished_child = 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?
|
| + while (current_child_ && 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(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();
|
| + 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.
|
| + 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_->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;
|
| +}
|
| +
|
| NGBoxStrut NGBlockLayoutAlgorithm::CollapseMargins(
|
| const NGBoxStrut& margins,
|
| const NGBoxFragment& fragment) {
|
| @@ -544,6 +708,8 @@ NGBlockLayoutAlgorithm::CreateConstraintSpaceForCurrentChild() const {
|
| .SetWritingMode(
|
| FromPlatformWritingMode(CurrentChildStyle().getWritingMode()))
|
| .SetTextDirection(CurrentChildStyle().direction());
|
| + LayoutUnit space_available = SpaceAvailableForCurrentChild();
|
| + space_builder_->SetFragmentainerSpaceAvailable(space_available);
|
| NGConstraintSpace* child_space = space_builder_->ToConstraintSpace();
|
|
|
| // TODO(layout-ng): Set offset through the space builder.
|
| @@ -563,6 +729,7 @@ DEFINE_TRACE(NGBlockLayoutAlgorithm) {
|
| visitor->trace(current_minmax_child_);
|
| visitor->trace(out_of_flow_layout_);
|
| visitor->trace(out_of_flow_candidates_);
|
| + visitor->trace(fragmentainer_mapper_);
|
| }
|
|
|
| } // namespace blink
|
|
|