summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc')
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc119
1 files changed, 85 insertions, 34 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
index e61e4d42998..f1034c33a1c 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -8,7 +8,6 @@
#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h"
-#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
@@ -138,10 +137,16 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
intrinsic_block_size_ = border_scrollbar_padding_.block_start;
- if (!LayoutChildren()) {
+ NGBreakStatus break_status = LayoutChildren();
+ if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
// We need to discard this layout and do it again. We found an earlier break
// point that's more appealing than the one we ran out of space at.
return RelayoutAndBreakEarlier();
+ } else if (break_status == NGBreakStatus::kBrokeBefore) {
+ // If we want to break before, make sure that we're actually at the start.
+ DCHECK(!BreakToken());
+
+ return container_builder_.Abort(NGLayoutResult::kOutOfFragmentainerSpace);
}
// Figure out how much space we've already been able to process in previous
@@ -166,10 +171,9 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
if (is_constrained_by_outer_fragmentation_context_) {
// In addition to establishing one, we're nested inside another
// fragmentation context.
- FinishFragmentation(ConstraintSpace(), block_size, intrinsic_block_size_,
- previously_consumed_block_size,
- FragmentainerSpaceAtBfcStart(ConstraintSpace()),
- &container_builder_);
+ FinishFragmentation(
+ ConstraintSpace(), BreakToken(), block_size, intrinsic_block_size_,
+ FragmentainerSpaceAtBfcStart(ConstraintSpace()), &container_builder_);
} else {
container_builder_.SetBlockSize(block_size);
container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_);
@@ -184,19 +188,17 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
return container_builder_.ToBoxFragment();
}
-base::Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize(
- const MinMaxSizeInput& input) const {
+base::Optional<MinMaxSizes> NGColumnLayoutAlgorithm::ComputeMinMaxSizes(
+ const MinMaxSizesInput& input) const {
// First calculate the min/max sizes of columns.
NGConstraintSpace space = CreateConstraintSpaceForMinMax();
NGFragmentGeometry fragment_geometry =
CalculateInitialMinMaxFragmentGeometry(space, Node());
NGBlockLayoutAlgorithm algorithm({Node(), fragment_geometry, space});
- MinMaxSizeInput child_input(input);
- child_input.size_type = NGMinMaxSizeType::kContentBoxSize;
- base::Optional<MinMaxSize> min_max_sizes =
- algorithm.ComputeMinMaxSize(child_input);
+ base::Optional<MinMaxSizes> min_max_sizes =
+ algorithm.ComputeMinMaxSizes(input);
DCHECK(min_max_sizes.has_value());
- MinMaxSize sizes = *min_max_sizes;
+ MinMaxSizes sizes = *min_max_sizes;
// If column-width is non-auto, pick the larger of that and intrinsic column
// width.
@@ -217,14 +219,11 @@ base::Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize(
// TODO(mstensho): Need to include spanners.
- if (input.size_type == NGMinMaxSizeType::kBorderBoxSize) {
- sizes += border_scrollbar_padding_.InlineSum();
- }
-
+ sizes += border_scrollbar_padding_.InlineSum();
return sizes;
}
-bool NGColumnLayoutAlgorithm::LayoutChildren() {
+NGBreakStatus NGColumnLayoutAlgorithm::LayoutChildren() {
NGMarginStrut margin_strut;
// First extract incoming child break tokens.
@@ -281,7 +280,7 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() {
PushSpannerBreakTokens(std::move(spanner_break_token),
std::move(next_column_token),
&container_builder_);
- return true;
+ return NGBreakStatus::kContinue;
}
} else {
// Breaking before the first element in the fragmentainer isn't allowed,
@@ -291,7 +290,7 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() {
}
if (BreakToken() && BreakToken()->HasSeenAllChildren() && !next_column_token)
- return true;
+ return NGBreakStatus::kContinue;
// Entering the child main loop. Here we'll alternate between laying out
// column content and column spanners, until we're either done, or until
@@ -301,6 +300,26 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() {
do {
scoped_refptr<const NGLayoutResult> result =
LayoutRow(next_column_token.get(), &margin_strut);
+
+ if (!result) {
+ // Not enough outer fragmentainer space to produce any columns at all.
+ container_builder_.SetDidBreak();
+ if (intrinsic_block_size_) {
+ // We have preceding initial border/padding, or a column spanner
+ // (possibly preceded by other spanners or even column content). So we
+ // need to break inside the multicol container. Stop walking the
+ // children, but "continue" layout, so that we produce a fragment. Note
+ // that we normally don't want to break right after initial
+ // border/padding, but will do so as a last resort. It's up to our
+ // containing block to decide what's best.
+ FinishAfterBreakBeforeRow(std::move(next_column_token));
+ return NGBreakStatus::kContinue;
+ }
+ // Otherwise we have nothing here, and need to break before the multicol
+ // container. No fragment will be produced.
+ return NGBreakStatus::kBrokeBefore;
+ }
+
next_column_token =
To<NGBlockBreakToken>(result->PhysicalFragment().BreakToken());
@@ -320,7 +339,7 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() {
container_builder_.AddBreakBeforeChild(
spanner_node, kBreakAppealPerfect, /* is_forced_break */ false);
FinishAfterBreakBeforeSpanner(std::move(next_column_token));
- return true;
+ return NGBreakStatus::kContinue;
}
}
@@ -328,18 +347,18 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() {
NGBreakStatus break_status = LayoutSpanner(
spanner_node, nullptr, &margin_strut, &spanner_break_token);
if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
- return false;
+ return break_status;
} else if (break_status == NGBreakStatus::kBrokeBefore) {
DCHECK(ConstraintSpace().HasBlockFragmentation());
FinishAfterBreakBeforeSpanner(std::move(next_column_token));
- return true;
+ return NGBreakStatus::kContinue;
} else if (spanner_break_token) {
DCHECK_EQ(break_status, NGBreakStatus::kContinue);
// We broke inside the spanner. This may happen if we're nested inside
// another fragmentation context.
PushSpannerBreakTokens(std::move(spanner_break_token),
std::move(next_column_token), &container_builder_);
- return true;
+ return NGBreakStatus::kContinue;
}
} while (next_column_token);
@@ -362,7 +381,7 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() {
intrinsic_block_size_ += margin_strut.Sum();
}
- return true;
+ return NGBreakStatus::kContinue;
}
scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow(
@@ -401,13 +420,23 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow(
LayoutUnit column_block_offset = intrinsic_block_size_ + margin_strut->Sum();
bool needs_more_fragments_in_outer = false;
+ bool zero_outer_space_left = false;
if (is_constrained_by_outer_fragmentation_context_) {
LayoutUnit available_outer_space =
FragmentainerSpaceAtBfcStart(ConstraintSpace()) - column_block_offset;
- // TODO(mstensho): This should never be negative, or even zero. Turn into a
- // DCHECK when the underlying problem is fixed.
- available_outer_space = available_outer_space.ClampNegativeToZero();
+ if (available_outer_space <= LayoutUnit()) {
+ if (available_outer_space < LayoutUnit()) {
+ // We're past the end of the outer fragmentainer (typically due to a
+ // margin). Nothing will fit here, not even zero-size content.
+ return nullptr;
+ }
+
+ // We are out of space, but we're exactly at the end of the outer
+ // fragmentainer. If none of our contents take up space, we're going to
+ // fit, otherwise not. Lay out and find out.
+ zero_outer_space_left = true;
+ }
// Check if we can fit everything (that's remaining), block-wise, within the
// current outer fragmentainer. If we can't, we need to adjust the block
@@ -498,6 +527,11 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow(
if (ConstraintSpace().HasBlockFragmentation() && column_break_token &&
actual_column_count >= used_column_count_ &&
needs_more_fragments_in_outer) {
+ // We cannot keep any of this if we have zero space left. Then we need
+ // to resume in the next outer fragmentainer.
+ if (zero_outer_space_left)
+ return nullptr;
+
container_builder_.SetDidBreak();
container_builder_.SetBreakAppeal(kBreakAppealPerfect);
break;
@@ -626,7 +660,8 @@ NGBreakStatus NGColumnLayoutAlgorithm::LayoutSpanner(
margin_strut->Append(margins.block_start, /* is_quirky */ false);
LayoutUnit block_offset = intrinsic_block_size_ + margin_strut->Sum();
- auto spanner_space = CreateConstraintSpaceForSpanner(block_offset);
+ auto spanner_space =
+ CreateConstraintSpaceForSpanner(spanner_node, block_offset);
const NGEarlyBreak* early_break_in_child = nullptr;
if (early_break_ && early_break_->Type() == NGEarlyBreak::kBlock &&
@@ -764,6 +799,10 @@ LayoutUnit NGColumnLayoutAlgorithm::CalculateBalancedColumnBlockSize(
NGBlockLayoutAlgorithm balancing_algorithm(
{Node(), fragment_geometry, space, break_token.get()});
scoped_refptr<const NGLayoutResult> result = balancing_algorithm.Layout();
+
+ // This algorithm should never abort.
+ DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess);
+
const NGPhysicalBoxFragment& fragment =
To<NGPhysicalBoxFragment>(result->PhysicalFragment());
LayoutUnit column_block_size = CalculateColumnContentBlockSize(
@@ -841,7 +880,7 @@ LayoutUnit NGColumnLayoutAlgorithm::ConstrainColumnBlockSize(
const ComputedStyle& style = Style();
LayoutUnit max = ResolveMaxBlockLength(
- ConstraintSpace(), style, border_padding_, style.LogicalMaxHeight(), size,
+ ConstraintSpace(), style, border_padding_, style.LogicalMaxHeight(),
LengthResolvePhase::kLayout);
LayoutUnit extent = ResolveMainBlockLength(
ConstraintSpace(), style, border_padding_, style.LogicalHeight(), size,
@@ -856,6 +895,20 @@ LayoutUnit NGColumnLayoutAlgorithm::ConstrainColumnBlockSize(
return size - extra;
}
+void NGColumnLayoutAlgorithm::FinishAfterBreakBeforeRow(
+ scoped_refptr<const NGBlockBreakToken> next_column_token) {
+ // We broke before a row for columns. We're done here. Take up the remaining
+ // space in the outer fragmentation context.
+ intrinsic_block_size_ = FragmentainerSpaceAtBfcStart(ConstraintSpace());
+
+ // If we were about to resume column layout after a spanner, add a break token
+ // for this, so that we resume there in the next outer fragmentainer. If
+ // there's no such break token, it means that we're at the start of the
+ // multicol container.
+ if (next_column_token)
+ container_builder_.AddBreakToken(std::move(next_column_token));
+}
+
void NGColumnLayoutAlgorithm::FinishAfterBreakBeforeSpanner(
scoped_refptr<const NGBlockBreakToken> next_column_token) {
// We broke before the spanner. We're done here. Take up the remaining space
@@ -898,9 +951,6 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForColumns(
space_builder.SetAvailableSize(column_size);
space_builder.SetPercentageResolutionSize(column_size);
- if (NGBaseline::ShouldPropagateBaselines(Node()))
- space_builder.AddBaselineRequests(ConstraintSpace().BaselineRequests());
-
// To ensure progression, we need something larger than 0 here. The spec
// actually says that fragmentainers have to accept at least 1px of content.
// See https://www.w3.org/TR/css-break-3/#breaking-rules
@@ -939,6 +989,7 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForBalancing(
}
NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForSpanner(
+ const NGBlockNode& spanner,
LayoutUnit block_offset) const {
NGConstraintSpaceBuilder space_builder(
ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true);
@@ -946,7 +997,7 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForSpanner(
space_builder.SetPercentageResolutionSize(content_box_size_);
if (ConstraintSpace().HasBlockFragmentation()) {
- SetupFragmentation(ConstraintSpace(), block_offset, &space_builder,
+ SetupFragmentation(ConstraintSpace(), spanner, block_offset, &space_builder,
/* is_new_fc */ true);
}