Commit ea4db76f authored by Morten Stenshorne's avatar Morten Stenshorne Committed by Commit Bot

[LayoutNG] Initial support for column-span:all.

This adds support for the fundamentals required by column-span:all. We
still don't attempt to position the spanners correctly. We need to
margin-collapse between spanners and calculate the inline position
first. We also still don't support breaking between spanners, or between
column rows and spanners. This may be necessary if we're nested inside
another fragmentation context.

Spanners are detected during regular column layout, and will bubble up
to the column layout algorithm, where they will be laid out as children,
before regular column content resumes. The column content will see the
spanner as a forced column break.

Some tests now start to fail. Two dynamic tests fail because
NGPaintFragment doesn't really understand block fragmentation. One
starts to crash, because a spanner causes a break inside the containing
block of an out-of-flow positioned descendant, which we have never
supported anyway. See crbug.com/996655

Bug: 829028
Change-Id: I60229da59f745dd469bbf104f6f46e5f93e1a1de
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1771909
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Cr-Commit-Position: refs/heads/master@{#691548}
parent 52f80bb3
......@@ -200,7 +200,8 @@ base::Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize(
for (NGLayoutInputNode child = Node().FirstChild(); child;
child = child.NextSibling()) {
if (child.IsOutOfFlowPositioned() || child.IsColumnSpanAll())
if (child.IsOutOfFlowPositioned() ||
(child.IsColumnSpanAll() && ConstraintSpace().IsInColumnBfc()))
continue;
const ComputedStyle& child_style = child.Style();
......@@ -516,7 +517,12 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
const NGBreakToken* child_break_token = entry.token;
if (child.IsOutOfFlowPositioned()) {
DCHECK(!child_break_token);
// We don't support fragmentation inside out-of-flow positioned boxes yet,
// but breaking before is fine. This may happen when a column spanner is
// directly followed by an OOF.
DCHECK(!child_break_token ||
(child_break_token->IsBlockType() &&
To<NGBlockBreakToken>(child_break_token)->IsBreakBefore()));
HandleOutOfFlowPositioned(previous_inflow_position,
To<NGBlockNode>(child));
} else if (child.IsFloating()) {
......@@ -525,6 +531,21 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
} else if (child.IsListMarker() && !child.ListMarkerOccupiesWholeLine()) {
container_builder_.SetUnpositionedListMarker(
NGUnpositionedListMarker(To<NGBlockNode>(child)));
} else if (child.IsColumnSpanAll() && ConstraintSpace().IsInColumnBfc()) {
// The child is a column spanner. We now need to finish this
// fragmentainer, then abort and let the column layout algorithm handle
// the spanner as a child.
container_builder_.SetColumnSpanner(To<NGBlockNode>(child));
// We also need to find out where to resume column layout after the
// spanner. If it has a next sibling, that's where we'll resume. If not,
// we'll need to keep looking for subsequent content on the way up the
// tree.
if (NGLayoutInputNode next = child.NextSibling()) {
container_builder_.AddBreakBeforeChild(next,
/* is_forced_break */ true);
container_builder_.SetDidBreak();
}
break;
} else {
// We need to propagate the initial break-before value up our container
// chain, until we reach a container that's not a first child. If we get
......@@ -543,8 +564,15 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
abort = !HandleInflow(
child, child_break_token, &previous_inflow_position,
inline_child_layout_context, &previous_inline_break_token);
if (container_builder_.FoundColumnSpanner())
break;
}
// Spanners should be detected above. They can only occur in the same
// block formatting context as the initial one established by the multicol
// container.
DCHECK(!container_builder_.FoundColumnSpanner());
if (abort) {
// We need to abort the layout, as our BFC block offset was resolved.
return container_builder_.Abort(
......@@ -1611,6 +1639,23 @@ bool NGBlockLayoutAlgorithm::FinishInflow(
child.IsInline() ? To<NGInlineBreakToken>(physical_fragment.BreakToken())
: nullptr;
// If a spanner was found inside the child, we need to finish up and propagate
// the spanner to the column layout algorithm, so that it can take care of it.
if (UNLIKELY(ConstraintSpace().IsInColumnBfc())) {
if (NGBlockNode spanner_node = layout_result->ColumnSpanner()) {
container_builder_.SetColumnSpanner(spanner_node);
if (!container_builder_.DidBreak()) {
// If we still haven't found a descendant at which to resume column
// layout after the spanner, look for one now.
if (NGLayoutInputNode next = child.NextSibling()) {
container_builder_.AddBreakBeforeChild(next,
/* is_forced_break */ true);
container_builder_.SetDidBreak();
}
}
}
}
return true;
}
......@@ -2288,7 +2333,8 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild(
// fragmentation line.
if (is_new_fc)
new_bfc_block_offset = child_data.bfc_offset_estimate.block_offset;
SetupFragmentation(ConstraintSpace(), new_bfc_block_offset, &builder);
SetupFragmentation(ConstraintSpace(), new_bfc_block_offset, &builder,
is_new_fc);
}
return builder.ToConstraintSpace();
......
......@@ -131,6 +131,11 @@ void UpdateLegacyMultiColumnFlowThread(
// Stitch the columns together.
for (const auto& child : fragment.Children()) {
// Skip column spanners, as they are not part of the flow thread (and
// besides, otherwise we'd hit a DCHECK below, because the inline-size of a
// spanner is typically different from that of the columns).
if (child->GetLayoutObject() && child->GetLayoutObject()->IsColumnSpanAll())
continue;
NGFragment child_fragment(writing_mode, *child);
flow_end += child_fragment.BlockSize();
// Non-uniform fragmentainer widths not supported by legacy layout.
......@@ -849,8 +854,13 @@ void NGBlockNode::PlaceChildrenInFlowThread(
const NGPhysicalBoxFragment& physical_fragment) {
LayoutUnit flowthread_offset;
for (const auto& child : physical_fragment.Children()) {
if (child->GetLayoutObject() != box_) {
DCHECK(child->GetLayoutObject()->IsColumnSpanAll());
// TODO(mstensho): Write back the spanner offset to the associated
// LayoutMultiColumnSpannerPlaceholder (if we bother)
continue;
}
// Each anonymous child of a multicol container constitutes one column.
DCHECK(child->GetLayoutObject() == box_);
// TODO(mstensho): writing modes
PhysicalOffset offset(LayoutUnit(), flowthread_offset);
......
......@@ -144,6 +144,7 @@ void NGBoxFragmentBuilder::AddResult(const NGLayoutResult& child_layout_result,
void NGBoxFragmentBuilder::AddBreakToken(
scoped_refptr<const NGBreakToken> token) {
DCHECK(token.get());
child_break_tokens_.push_back(std::move(token));
}
......
......@@ -154,6 +154,9 @@ class CORE_EXPORT NGBoxFragmentBuilder final
// left to discover.
void SetHasSeenAllChildren() { has_seen_all_children_ = true; }
void SetColumnSpanner(NGBlockNode spanner) { column_spanner_ = spanner; }
bool FoundColumnSpanner() const { return !!column_spanner_; }
// Offsets are not supposed to be set during fragment construction, so we
// do not provide a setter here.
......@@ -243,6 +246,8 @@ class CORE_EXPORT NGBoxFragmentBuilder final
NGFragmentItemsBuilder* items_builder_ = nullptr;
NGBlockNode column_spanner_ = nullptr;
NGPhysicalFragment::NGBoxType box_type_;
bool is_fieldset_container_ = false;
bool is_initial_block_size_indefinite_ = false;
......
......@@ -52,6 +52,30 @@ LayoutUnit CalculateColumnContentBlockSize(
return total_size;
}
inline bool IsColumnSpanner(NGBlockNode multicol_container,
const NGBlockBreakToken& token) {
// A column spanner may also establish a multicol container on its own, so
// before returning true here, make sure that the spanner isn't the multicol
// container itself.
NGLayoutInputNode broken_node = token.InputNode();
return broken_node.IsColumnSpanAll() && broken_node != multicol_container;
}
// Add the spanner's break token, AND another break token for the column content
// that comes after. In the next fragment we need to resume layout of the
// spanner, and then proceed to the column content - if there's room for both.
// Note that it's possible for the spanner to break again in the next fragment.
void PushSpannerBreakTokens(
scoped_refptr<const NGBlockBreakToken> spanner_break_token,
scoped_refptr<const NGBlockBreakToken> next_column_token,
NGBoxFragmentBuilder* builder) {
builder->AddBreakToken(std::move(spanner_break_token));
if (next_column_token)
builder->AddBreakToken(std::move(next_column_token));
else
builder->SetHasSeenAllChildren();
}
} // namespace
NGColumnLayoutAlgorithm::NGColumnLayoutAlgorithm(
......@@ -89,38 +113,7 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
if (!IsResumingLayout(BreakToken()))
intrinsic_block_size_ = border_scrollbar_padding_.block_start;
scoped_refptr<const NGBreakToken> child_break_token;
if (const auto* token = BreakToken()) {
// We're resuming layout of this multicol container after an outer
// fragmentation break. Resume at the break token of the last column that we
// were able to lay out. Note that in some cases, there may be no child
// break tokens. That happens if we weren't able to lay out anything at all
// in the previous outer fragmentainer, e.g. due to a forced break before
// this multicol container, or e.g. if there was leading unbreakable content
// that couldn't fit in the space we were offered back then. In other words,
// in that case, we're about to create the first fragment for this multicol
// container.
const auto child_tokens = token->ChildBreakTokens();
if (child_tokens.size()) {
DCHECK_EQ(child_tokens.size(), 1u);
child_break_token = child_tokens[0];
}
}
if (!BreakToken() || !BreakToken()->HasSeenAllChildren()) {
child_break_token =
LayoutRow(To<NGBlockBreakToken>(child_break_token.get()));
}
if (!child_break_token) {
// We've gone through all the content. This doesn't necessarily mean that
// we're done fragmenting, since the multicol container may be taller than
// what the content requires, which means that we might create more
// (childless) fragments, if we're nested inside another fragmentation
// context. In that case we must make sure to skip the contents when
// resuming.
container_builder_.SetHasSeenAllChildren();
}
LayoutChildren();
// Figure out how much space we've already been able to process in previous
// fragments, if this multicol container participates in an outer
......@@ -142,9 +135,6 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
}
if (ConstraintSpace().HasBlockFragmentation()) {
if (child_break_token)
container_builder_.AddBreakToken(std::move(child_break_token));
// In addition to establishing one, we're nested inside another
// fragmentation context.
FinishFragmentation(&container_builder_, block_size, intrinsic_block_size_,
......@@ -195,6 +185,8 @@ base::Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize(
LayoutUnit column_gap = ResolveUsedColumnGap(LayoutUnit(), Style());
sizes += column_gap * (column_count - 1);
// TODO(mstensho): Need to include spanners.
if (input.size_type == NGMinMaxSizeType::kBorderBoxSize) {
sizes += border_scrollbar_padding_.InlineSum();
}
......@@ -202,7 +194,111 @@ base::Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize(
return sizes;
}
scoped_refptr<const NGBreakToken> NGColumnLayoutAlgorithm::LayoutRow(
void NGColumnLayoutAlgorithm::LayoutChildren() {
// First extract incoming child break tokens.
scoped_refptr<const NGBlockBreakToken> spanner_break_token;
scoped_refptr<const NGBlockBreakToken> next_column_token;
if (const auto* token = BreakToken()) {
// We're resuming layout of this multicol container after an outer
// fragmentation break. Resume at the break token of the last column that we
// were able to lay out, or before or inside the spanner that caused an
// outer fragmentainer break. Note that in some cases, there may be no child
// break tokens. That happens if we weren't able to lay out anything at all
// in the previous outer fragmentainer, e.g. due to a forced break before
// this multicol container, or e.g. if there was leading unbreakable content
// that couldn't fit in the space we were offered back then. In other words,
// in that case, we're about to create the first fragment for this multicol
// container.
const auto child_tokens = token->ChildBreakTokens();
if (wtf_size_t break_token_count = child_tokens.size()) {
wtf_size_t break_token_idx = 0;
scoped_refptr<const NGBlockBreakToken> child_token =
To<NGBlockBreakToken>(child_tokens[break_token_idx++]);
if (child_token && IsColumnSpanner(Node(), *child_token)) {
// We're resuming at a column spanner. Get the next break token after
// the spanner, if any. That would be the column content to resume at,
// once we're done with the spanner.
spanner_break_token = child_token;
if (break_token_idx < break_token_count) {
next_column_token =
To<NGBlockBreakToken>(child_tokens[break_token_idx++]);
}
} else {
next_column_token = child_token;
}
// There shouldn't be any additional break tokens.
DCHECK_EQ(break_token_idx, break_token_count);
}
if (token->HasSeenAllChildren())
container_builder_.SetHasSeenAllChildren();
}
if (spanner_break_token) {
// The multicol container previously broke at a spanner (this may happen if
// we're nested inside another fragmentation context), so that's where we'll
// resume now.
spanner_break_token =
LayoutSpanner(To<NGBlockNode>(spanner_break_token->InputNode()),
spanner_break_token.get());
if (spanner_break_token) {
// We broke at the spanner again!
PushSpannerBreakTokens(std::move(spanner_break_token),
std::move(next_column_token), &container_builder_);
return;
}
}
if (BreakToken() && BreakToken()->HasSeenAllChildren() && !next_column_token)
return;
// Entering the child main loop. Here we'll alternate between laying out
// column content and column spanners, until we're either done, or until
// something breaks. Spanners are discovered as part of laying out a row, so
// we'll always start with attempting to lay out a row, even if the first
// child is a spanner.
do {
scoped_refptr<const NGLayoutResult> result =
LayoutRow(next_column_token.get());
next_column_token =
To<NGBlockBreakToken>(result->PhysicalFragment().BreakToken());
// If we didn't find a spanner, it either means that we're through
// everything, or that column layout needs to continue from the next outer
// fragmentainer.
NGBlockNode spanner_node = result->ColumnSpanner();
if (!spanner_node)
break;
// We found a spanner. Lay it out, and then resume column layout.
spanner_break_token = LayoutSpanner(spanner_node, nullptr);
if (spanner_break_token) {
// We broke before or 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;
}
} while (next_column_token);
if (next_column_token) {
// We broke inside column content. Add a break token for where to resume
// column layout at in the next fragment.
container_builder_.AddBreakToken(std::move(next_column_token));
} else {
// We've gone through all the content. This doesn't necessarily mean that
// we're done fragmenting, since the multicol container may be taller than
// what the content requires, which means that we might create more
// (childless) fragments, if we're nested inside another fragmentation
// context. In that case we must make sure to skip the contents when
// resuming.
container_builder_.SetHasSeenAllChildren();
}
}
scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow(
const NGBlockBreakToken* next_column_token) {
LayoutUnit column_block_offset = intrinsic_block_size_;
LogicalSize column_size(column_inline_size_, content_box_size_.block_size);
......@@ -212,11 +308,15 @@ scoped_refptr<const NGBreakToken> NGColumnLayoutAlgorithm::LayoutRow(
// fragmentation context.
if (ConstraintSpace().HasBlockFragmentation() &&
column_size.block_size != kIndefiniteSize) {
if (const auto* token = BreakToken()) {
if (const auto* token = BreakToken())
column_size.block_size -= token->ConsumedBlockSize();
// Subtract the space already taken in the current fragment (spanners and
// earlier column rows).
column_size.block_size -= CurrentContentBlockOffset();
column_size.block_size = column_size.block_size.ClampNegativeToZero();
}
}
// We balance if block-size is unconstrained, or when we're explicitly told
// to. Note that the block-size may be constrained by outer fragmentation
......@@ -261,7 +361,7 @@ scoped_refptr<const NGBreakToken> NGColumnLayoutAlgorithm::LayoutRow(
// retry otherwise.
NGContainerFragmentBuilder::ChildrenVector new_columns;
scoped_refptr<const NGBreakToken> last_break_token;
scoped_refptr<const NGLayoutResult> result;
do {
scoped_refptr<const NGBlockBreakToken> column_break_token =
......@@ -292,7 +392,7 @@ scoped_refptr<const NGBreakToken> NGColumnLayoutAlgorithm::LayoutRow(
NGBlockLayoutAlgorithm child_algorithm(
{Node(), fragment_geometry, child_space, column_break_token.get()});
child_algorithm.SetBoxType(NGPhysicalFragment::kColumnBox);
scoped_refptr<const NGLayoutResult> result = child_algorithm.Layout();
result = child_algorithm.Layout();
const auto& column = result->PhysicalFragment();
// Add the new column fragment to the list, but don't commit anything to
......@@ -311,8 +411,10 @@ scoped_refptr<const NGBreakToken> NGColumnLayoutAlgorithm::LayoutRow(
column_inline_offset += column_inline_progression_;
last_break_token = column.BreakToken();
column_break_token = To<NGBlockBreakToken>(last_break_token.get());
if (result->ColumnSpanner())
break;
column_break_token = To<NGBlockBreakToken>(column.BreakToken());
// If we're participating in an outer fragmentation context, we'll only
// allow as many columns as the used value of column-count, so that we
......@@ -337,6 +439,17 @@ scoped_refptr<const NGBreakToken> NGColumnLayoutAlgorithm::LayoutRow(
if (container_builder_.DidBreak())
break;
if (!balance_columns && result->ColumnSpanner()) {
// We always have to balance columns preceding a spanner, so if we didn't
// do that initially, switch over to column balancing mode now, and lay
// out again.
balance_columns = true;
new_columns.clear();
column_size.block_size =
CalculateBalancedColumnBlockSize(column_size, next_column_token);
continue;
}
// If we overflowed (actual column count larger than what we have room for),
// and we're supposed to calculate the column lengths automatically (column
// balancing), see if we're able to stretch them.
......@@ -381,7 +494,7 @@ scoped_refptr<const NGBreakToken> NGColumnLayoutAlgorithm::LayoutRow(
// propagating them.
if (column.Children().size() == 0 &&
!column.HasOutOfFlowPositionedDescendants())
return last_break_token;
return result;
}
intrinsic_block_size_ += column_size.block_size;
......@@ -392,7 +505,32 @@ scoped_refptr<const NGBreakToken> NGColumnLayoutAlgorithm::LayoutRow(
column.offset);
}
return last_break_token;
return result;
}
scoped_refptr<const NGBlockBreakToken> NGColumnLayoutAlgorithm::LayoutSpanner(
NGBlockNode spanner_node,
const NGBlockBreakToken* break_token) {
// TODO(mstensho): Margins (including margin collapsing), inline alignment,
// and outer fragmentainer breaks between spanners and rows (the spanner may
// be unbreakable inside, and we may be in a nested fragmentation context and
// out of space).
LayoutUnit block_offset = intrinsic_block_size_;
auto spanner_space = CreateConstraintSpaceForSpanner(block_offset);
// TODO(mstensho): Passing a break-before token shouldn't be a problem, but it
// would cause problems for the NGPaintFragment code. Just pass nullptr. Won't
// make any difference anyway.
if (break_token && break_token->IsBreakBefore())
break_token = nullptr;
auto result = spanner_node.Layout(spanner_space, break_token);
LogicalOffset offset(border_scrollbar_padding_.inline_start,
intrinsic_block_size_);
container_builder_.AddResult(*result, offset);
NGFragment fragment(Style().GetWritingMode(), result->PhysicalFragment());
intrinsic_block_size_ = offset.block_offset + fragment.BlockSize();
return To<NGBlockBreakToken>(result->PhysicalFragment().BreakToken());
}
LayoutUnit NGColumnLayoutAlgorithm::CalculateBalancedColumnBlockSize(
......@@ -473,10 +611,15 @@ LayoutUnit NGColumnLayoutAlgorithm::CalculateBalancedColumnBlockSize(
scoped_refptr<const NGLayoutResult> result = balancing_algorithm.Layout();
const NGPhysicalBoxFragment& fragment =
To<NGPhysicalBoxFragment>(result->PhysicalFragment());
break_token = To<NGBlockBreakToken>(fragment.BreakToken());
LayoutUnit column_block_size = CalculateColumnContentBlockSize(
fragment, IsHorizontalWritingMode(space.GetWritingMode()));
content_runs.emplace_back(column_block_size);
// Stop when we reach a spanner. That's where this row of columns will end.
if (result->ColumnSpanner())
break;
break_token = To<NGBlockBreakToken>(fragment.BreakToken());
} while (break_token);
// Then distribute as many implicit breaks into the content runs as we need.
......@@ -545,6 +688,14 @@ LayoutUnit NGColumnLayoutAlgorithm::ConstrainColumnBlockSize(
return size - extra;
}
LayoutUnit NGColumnLayoutAlgorithm::CurrentContentBlockOffset() const {
// If we're past the block-start border and padding, this is simple:
if (IsResumingLayout(BreakToken()))
return intrinsic_block_size_;
// Otherwise subtract those.
return intrinsic_block_size_ - border_scrollbar_padding_.block_start;
}
NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForColumns(
const LogicalSize& column_size,
bool is_first_fragmentainer,
......@@ -567,6 +718,7 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForColumns(
space_builder.SetFragmentainerBlockSize(column_block_size);
space_builder.SetFragmentainerSpaceAtBfcStart(column_block_size);
space_builder.SetIsAnonymous(true);
space_builder.SetIsInColumnBfc();
if (balance_columns)
space_builder.SetIsInsideBalancedColumns();
if (!is_first_fragmentainer) {
......@@ -593,17 +745,34 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForBalancing(
space_builder.SetAvailableSize({column_size.inline_size, kIndefiniteSize});
space_builder.SetPercentageResolutionSize(column_size);
space_builder.SetIsAnonymous(true);
space_builder.SetIsInColumnBfc();
space_builder.SetIsIntermediateLayout(true);
space_builder.SetIsInsideBalancedColumns();
return space_builder.ToConstraintSpace();
}
NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForSpanner(
LayoutUnit block_offset) const {
NGConstraintSpaceBuilder space_builder(
ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true);
space_builder.SetAvailableSize(content_box_size_);
space_builder.SetPercentageResolutionSize(content_box_size_);
if (ConstraintSpace().HasBlockFragmentation()) {
SetupFragmentation(ConstraintSpace(), block_offset, &space_builder,
/* is_new_fc */ true);
}
return space_builder.ToConstraintSpace();
}
NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForMinMax()
const {
NGConstraintSpaceBuilder space_builder(
ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true);
space_builder.SetIsAnonymous(true);
space_builder.SetIsInColumnBfc();
space_builder.SetIsIntermediateLayout(true);
return space_builder.ToConstraintSpace();
......
......@@ -28,9 +28,21 @@ class CORE_EXPORT NGColumnLayoutAlgorithm
const MinMaxSizeInput&) const override;
private:
scoped_refptr<const NGBreakToken> LayoutRow(
// Lay out as many children as we can.
void LayoutChildren();
// Lay out one row of columns. The layout result returned is for the last
// column that was laid out. The rows themselves don't create fragments.
scoped_refptr<const NGLayoutResult> LayoutRow(
const NGBlockBreakToken* next_column_token);
// Lay out a column spanner. Will return a break token if we break before or
// inside the spanner. If no break token is returned, it means that we can
// proceed to the next row of columns.
scoped_refptr<const NGBlockBreakToken> LayoutSpanner(
NGBlockNode spanner_node,
const NGBlockBreakToken* break_token);
LayoutUnit CalculateBalancedColumnBlockSize(
const LogicalSize& column_size,
const NGBlockBreakToken* child_break_token);
......@@ -41,6 +53,7 @@ class CORE_EXPORT NGColumnLayoutAlgorithm
LayoutUnit current_column_size) const;
LayoutUnit ConstrainColumnBlockSize(LayoutUnit size) const;
LayoutUnit CurrentContentBlockOffset() const;
NGConstraintSpace CreateConstraintSpaceForColumns(
const LogicalSize& column_size,
......@@ -48,6 +61,8 @@ class CORE_EXPORT NGColumnLayoutAlgorithm
bool balance_columns) const;
NGConstraintSpace CreateConstraintSpaceForBalancing(
const LogicalSize& column_size) const;
NGConstraintSpace CreateConstraintSpaceForSpanner(
LayoutUnit block_offset) const;
NGConstraintSpace CreateConstraintSpaceForMinMax() const;
const NGBoxStrut border_padding_;
......
......@@ -3359,5 +3359,694 @@ TEST_F(NGColumnLayoutAlgorithmTest, AbsposFitsInOneColumn) {
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, Spanner) {
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
width: 320px;
border: 1px solid;
}
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div id="parent">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div style="column-span:all; height:44px;"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x126
offset:0,0 size:322x126
offset:1,1 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:111,1 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:221,1 size:100x20
offset:0,0 size:100x20
offset:1,41 size:320x44
offset:1,85 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:111,85 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:221,85 size:100x20
offset:0,0 size:100x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, SpannerWithContent) {
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
width: 320px;
border: 1px solid;
}
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div id="parent">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div style="column-span:all; padding:1px;">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x144
offset:0,0 size:322x144
offset:1,1 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:111,1 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:221,1 size:100x20
offset:0,0 size:100x20
offset:1,41 size:320x62
offset:1,1 size:318x20
offset:1,21 size:318x20
offset:1,41 size:318x20
offset:1,103 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:111,103 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:221,103 size:100x20
offset:0,0 size:100x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, TwoSpannersPercentWidth) {
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
width: 320px;
border: 1px solid;
}
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div id="parent">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div style="column-span:all; width:50%; height:44px;"></div>
<div style="column-span:all; width:50%; height:1px;"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x127
offset:0,0 size:322x127
offset:1,1 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:111,1 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:221,1 size:100x20
offset:0,0 size:100x20
offset:1,41 size:160x44
offset:1,85 size:160x1
offset:1,86 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:111,86 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:221,86 size:100x20
offset:0,0 size:100x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, SpannerNoBalancing) {
// Even if column-fill is auto and block-size is restricted, we have to
// balance column contents in front of a spanner (but not after).
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
column-fill: auto;
height: 200px;
width: 320px;
border: 1px solid;
}
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div id="parent">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div style="column-span:all; height:44px;"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x202
offset:0,0 size:322x202
offset:1,1 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:111,1 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:221,1 size:100x20
offset:0,0 size:100x20
offset:1,41 size:320x44
offset:1,85 size:100x100
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:0,40 size:100x20
offset:0,60 size:100x20
offset:0,80 size:100x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, SpannerAtStart) {
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
width: 320px;
border: 1px solid;
}
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div id="parent">
<div style="column-span:all; height:44px;"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x86
offset:0,0 size:322x86
offset:1,1 size:320x44
offset:1,45 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:111,45 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:221,45 size:100x20
offset:0,0 size:100x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, SpannerAtEnd) {
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
width: 320px;
border: 1px solid;
}
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div id="parent">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div style="column-span:all; height:44px;"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x86
offset:0,0 size:322x86
offset:1,1 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:111,1 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:221,1 size:100x20
offset:0,0 size:100x20
offset:1,41 size:320x44
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, SpannerAlone) {
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
width: 320px;
border: 1px solid;
}
</style>
<div id="container">
<div id="parent">
<div style="column-span:all; height:44px;"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x46
offset:0,0 size:322x46
offset:1,1 size:320x44
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, SpannerInBlock) {
// Spanners don't have to be direct children of the multicol container, but
// have to be defined in the same block formatting context as the one
// established by the multicol container.
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
width: 320px;
border: 1px solid;
}
</style>
<div id="container">
<div id="parent">
<div style="width:11px;">
<div style="column-span:all; height:44px;"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x46
offset:0,0 size:322x46
offset:1,1 size:100x0
offset:0,0 size:11x0
offset:1,1 size:320x44
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, SpannerWithSiblingsInBlock) {
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
width: 320px;
border: 1px solid;
}
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div id="parent">
<div style="width:11px;">
<div style="column-span:all; height:44px;"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x86
offset:0,0 size:322x86
offset:1,1 size:100x0
offset:0,0 size:11x0
offset:1,1 size:320x44
offset:1,45 size:100x40
offset:0,0 size:11x40
offset:0,0 size:11x20
offset:0,20 size:11x20
offset:111,45 size:100x40
offset:0,0 size:11x40
offset:0,0 size:11x20
offset:0,20 size:11x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, SpannerInBlockWithSiblings) {
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
width: 320px;
border: 1px solid;
}
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div id="parent">
<div style="width:11px;">
<div style="column-span:all; height:44px;"></div>
</div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x86
offset:0,0 size:322x86
offset:1,1 size:100x0
offset:0,0 size:11x0
offset:1,1 size:320x44
offset:1,45 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
offset:111,45 size:100x40
offset:0,0 size:100x20
offset:0,20 size:100x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, InvalidSpanners) {
// Spanners cannot exist inside new formatting context roots. They will just
// be treated as normal column content then.
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-gap: 10px;
width: 320px;
border: 1px solid;
}
</style>
<div id="container">
<div id="parent">
<div style="float:left; width:10px;">
<div style="column-span:all; height:30px;"></div>
</div>
<div style="display:flow-root;">
<div style="column-span:all; height:30px;"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x12
offset:0,0 size:322x12
offset:1,1 size:100x10
offset:0,0 size:10x10
offset:0,0 size:10x10
offset:10,0 size:90x10
offset:0,0 size:90x10
offset:111,1 size:100x10
offset:0,0 size:10x10
offset:0,0 size:10x10
offset:10,0 size:90x10
offset:0,0 size:90x10
offset:221,1 size:100x10
offset:0,0 size:10x10
offset:0,0 size:10x10
offset:10,0 size:90x10
offset:0,0 size:90x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, BreakInsideSpanner) {
SetBodyInnerHTML(R"HTML(
<style>
.outer { columns:3; height:50px; column-fill:auto; width:320px; }
.inner { columns:2; height:100px; column-fill:auto; padding:1px; }
.outer, .inner { column-gap:10px; }
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div class="outer">
<div class="content"></div>
<div class="inner">
<div class="content"></div>
<div class="content"></div>
<div style="column-span:all; height:35px;"></div>
<div class="content" style="width:7px;"></div>
<div class="content" style="width:8px;"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x50
offset:0,0 size:320x50
offset:0,0 size:100x50
offset:0,0 size:100x20
offset:0,20 size:100x30
offset:1,1 size:44x20
offset:0,0 size:44x20
offset:55,1 size:44x20
offset:0,0 size:44x20
offset:1,21 size:98x9
offset:110,0 size:100x50
offset:0,0 size:100x50
offset:1,0 size:98x26
offset:1,26 size:44x24
offset:0,0 size:7x20
offset:55,26 size:44x20
offset:0,0 size:8x20
offset:220,0 size:100x22
offset:0,0 size:100x22
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, BreakInsideSpannerTwice) {
SetBodyInnerHTML(R"HTML(
<style>
.outer { columns:3; height:50px; column-fill:auto; width:320px; }
.inner { columns:2; height:150px; column-fill:auto; padding:1px; }
.outer, .inner { column-gap:10px; }
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div class="outer">
<div class="content"></div>
<div class="inner">
<div class="content"></div>
<div class="content"></div>
<div style="column-span:all; height:85px;"></div>
<div class="content" style="width:7px;"></div>
<div class="content" style="width:8px;"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x50
offset:0,0 size:320x50
offset:0,0 size:100x50
offset:0,0 size:100x20
offset:0,20 size:100x30
offset:1,1 size:44x20
offset:0,0 size:44x20
offset:55,1 size:44x20
offset:0,0 size:44x20
offset:1,21 size:98x9
offset:110,0 size:100x50
offset:0,0 size:100x50
offset:1,0 size:98x50
offset:220,0 size:100x50
offset:0,0 size:100x50
offset:1,0 size:98x26
offset:1,26 size:44x24
offset:0,0 size:7x20
offset:55,26 size:44x20
offset:0,0 size:8x20
offset:330,0 size:100x22
offset:0,0 size:100x22
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, BreakInsideSpannerWithContent) {
SetBodyInnerHTML(R"HTML(
<style>
.outer { columns:3; height:50px; column-fill:auto; width:320px; }
.inner { columns:2; height:98px; column-fill:auto; padding:1px; }
.outer, .inner { column-gap:10px; }
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div class="outer">
<div class="inner">
<div class="content"></div>
<div class="content"></div>
<div style="column-span:all;">
<div style="width:3px;" class="content"></div>
<div style="width:4px;" class="content"></div>
</div>
<div class="content" style="width:7px;"></div>
<div class="content" style="width:8px;"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x50
offset:0,0 size:320x50
offset:0,0 size:100x50
offset:0,0 size:100x50
offset:1,1 size:44x20
offset:0,0 size:44x20
offset:55,1 size:44x20
offset:0,0 size:44x20
offset:1,21 size:98x29
offset:0,0 size:3x20
offset:110,0 size:100x50
offset:0,0 size:100x50
offset:1,0 size:98x20
offset:0,0 size:4x20
offset:1,20 size:44x28
offset:0,0 size:7x20
offset:55,20 size:44x20
offset:0,0 size:8x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, SpannerAsMulticol) {
SetBodyInnerHTML(R"HTML(
<style>
.outer { columns:3; height:50px; column-fill:auto; width:320px; }
.middle { columns:3; height:140px; column-fill:auto; }
.inner { column-span:all; columns:2; height:80px; column-fill:auto; }
.outer, .middle, .inner { column-gap:10px; }
.content { break-inside:avoid; height:20px; }
</style>
<div id="container">
<div class="outer">
<div class="middle">
<div class="inner">
<div class="content" style="width:131px;"></div>
<div class="content" style="width:132px;"></div>
<div class="content" style="width:133px;"></div>
<div class="content" style="width:134px;"></div>
<div class="content" style="width:135px;"></div>
<div class="content" style="width:136px;"></div>
</div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x50
offset:0,0 size:320x50
offset:0,0 size:100x50
offset:0,0 size:100x50
offset:0,0 size:100x50
offset:0,0 size:45x50
offset:0,0 size:131x20
offset:0,20 size:132x20
offset:55,0 size:45x50
offset:0,0 size:133x20
offset:0,20 size:134x20
offset:110,0 size:100x50
offset:0,0 size:100x50
offset:0,0 size:100x30
offset:0,0 size:45x30
offset:0,0 size:135x20
offset:55,0 size:45x20
offset:0,0 size:136x20
offset:220,0 size:100x40
offset:0,0 size:100x40
)DUMP";
EXPECT_EQ(expectation, dump);
}
} // anonymous namespace
} // namespace blink
......@@ -342,6 +342,12 @@ class CORE_EXPORT NGConstraintSpace final {
return HasRareData() && rare_data_->is_inside_balanced_columns;
}
// Return true if we're participating in the same block formatting context as
// the one established by the nearest ancestor multicol container.
bool IsInColumnBfc() const {
return HasRareData() && rare_data_->is_in_column_bfc;
}
// Returns if this node is a table cell child, and which table layout mode
// is occurring.
NGTableCellChildLayoutMode TableCellChildLayoutMode() const {
......@@ -553,7 +559,8 @@ class CORE_EXPORT NGConstraintSpace final {
: bfc_offset(bfc_offset),
block_direction_fragmentation_type(
static_cast<unsigned>(kFragmentNone)),
is_inside_balanced_columns(false) {}
is_inside_balanced_columns(false),
is_in_column_bfc(false) {}
RareData(const RareData&) = default;
~RareData() = default;
......@@ -577,6 +584,7 @@ class CORE_EXPORT NGConstraintSpace final {
unsigned block_direction_fragmentation_type : 2;
unsigned is_inside_balanced_columns : 1;
unsigned is_in_column_bfc : 1;
bool MaySkipLayout(const RareData& other) const {
return margin_strut == other.margin_strut &&
......@@ -588,7 +596,8 @@ class CORE_EXPORT NGConstraintSpace final {
other.fragmentainer_space_at_bfc_start &&
block_direction_fragmentation_type ==
other.block_direction_fragmentation_type &&
is_inside_balanced_columns == other.is_inside_balanced_columns;
is_inside_balanced_columns == other.is_inside_balanced_columns &&
is_in_column_bfc == other.is_in_column_bfc;
}
// Must be kept in sync with members checked within |MaySkipLayout|.
......@@ -599,7 +608,7 @@ class CORE_EXPORT NGConstraintSpace final {
fragmentainer_block_size == kIndefiniteSize &&
fragmentainer_space_at_bfc_start == kIndefiniteSize &&
block_direction_fragmentation_type == kFragmentNone &&
!is_inside_balanced_columns;
!is_inside_balanced_columns && !is_in_column_bfc;
}
};
......
......@@ -167,6 +167,8 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
space_.EnsureRareData()->is_inside_balanced_columns = true;
}
void SetIsInColumnBfc() { space_.EnsureRareData()->is_in_column_bfc = true; }
void SetIsTableCell(bool b) { space_.bitfields_.is_table_cell = b; }
void SetIsRestrictedBlockSizeTableCell(bool b) {
......
......@@ -79,7 +79,8 @@ NGConstraintSpace CreateConstraintSpaceForFloat(
DCHECK(parent_space.HasBlockFragmentation());
DCHECK_EQ(style.GetWritingMode(), parent_space.GetWritingMode());
SetupFragmentation(parent_space, *origin_block_offset, &builder);
SetupFragmentation(parent_space, *origin_block_offset, &builder,
/* is_new_fc */ true);
} else {
builder.SetFragmentationType(NGFragmentationType::kFragmentNone);
}
......
......@@ -75,7 +75,8 @@ bool IsForcedBreakValue(const NGConstraintSpace& constraint_space,
void SetupFragmentation(const NGConstraintSpace& parent_space,
LayoutUnit new_bfc_block_offset,
NGConstraintSpaceBuilder* builder) {
NGConstraintSpaceBuilder* builder,
bool is_new_fc) {
DCHECK(parent_space.HasBlockFragmentation());
LayoutUnit space_available =
......@@ -84,6 +85,9 @@ void SetupFragmentation(const NGConstraintSpace& parent_space,
builder->SetFragmentainerBlockSize(parent_space.FragmentainerBlockSize());
builder->SetFragmentainerSpaceAtBfcStart(space_available);
builder->SetFragmentationType(parent_space.BlockFragmentationType());
if (parent_space.IsInColumnBfc() && !is_new_fc)
builder->SetIsInColumnBfc();
}
void FinishFragmentation(NGBoxFragmentBuilder* builder,
......
......@@ -45,7 +45,8 @@ inline bool IsResumingLayout(const NGBlockBreakToken* token) {
// fragmentainer block-start.
void SetupFragmentation(const NGConstraintSpace& parent_space,
LayoutUnit new_bfc_block_offset,
NGConstraintSpaceBuilder*);
NGConstraintSpaceBuilder*,
bool is_new_fc);
// Write fragmentation information to the fragment builder after layout.
void FinishFragmentation(NGBoxFragmentBuilder*,
......
......@@ -49,6 +49,8 @@ NGLayoutResult::NGLayoutResult(
EnsureRareData()->custom_layout_data =
std::move(builder->custom_layout_data_);
}
if (builder->column_spanner_)
EnsureRareData()->column_spanner = builder->column_spanner_;
bitfields_.initial_break_before =
static_cast<unsigned>(builder->initial_break_before_);
bitfields_.final_break_after =
......
......@@ -12,6 +12,7 @@
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h"
#include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_link.h"
......@@ -68,6 +69,11 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
: NGUnpositionedListMarker();
}
// Get the column spanner (if any) that interrupted column layout.
NGBlockNode ColumnSpanner() const {
return HasRareData() ? rare_data_->column_spanner : NGBlockNode(nullptr);
}
const NGExclusionSpace& ExclusionSpace() const {
if (bitfields_.has_rare_data_exclusion_space) {
DCHECK(HasRareData());
......@@ -284,6 +290,7 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
LogicalOffset oof_positioned_offset;
NGMarginStrut end_margin_strut;
NGUnpositionedListMarker unpositioned_list_marker;
NGBlockNode column_spanner = nullptr;
LayoutUnit minimal_space_shortage = LayoutUnit::Max();
NGExclusionSpace exclusion_space;
scoped_refptr<SerializedScriptValue> custom_layout_data;
......
......@@ -1348,6 +1348,7 @@ crbug.com/967329 virtual/layout_ng_experimental/external/wpt/css/css-multicol/co
crbug.com/967329 virtual/layout_ng_experimental/external/wpt/css/css-multicol/columnfill-auto-max-height-001.html [ Failure ]
crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/float-and-block.html [ Failure ]
crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/float-with-line-after-spanner.html [ Failure ]
crbug.com/996655 virtual/layout_ng_experimental/external/wpt/css/css-multicol/going-out-of-flow-after-spanner.html [ Crash Pass ]
crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/inline-block-and-column-span-all.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-basic-001.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-basic-002.html [ Failure ]
......@@ -1481,6 +1482,8 @@ crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/mu
crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-children-height-004b.html [ Failure ]
crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-children-height-005.html [ Failure ]
crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-children-height-006.html [ Failure ]
crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-004.html [ Failure ]
crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-007.html [ Failure ]
crbug.com/924142 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-010.html [ Crash Pass ]
crbug.com/874051 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-fieldset-001.html [ Failure ]
crbug.com/874051 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-fieldset-002.html [ Failure ]
......@@ -1552,7 +1555,6 @@ crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/block-beco
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/change-second-row-height.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/change-spanner-display.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/change-spanner-parent-display.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/float-becomes-spanner.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/insert-block-among-text-in-anonymous-wrapper.html [ Crash Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/insert-block-before-spanner-before-content.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/insert-block-before-spanner.html [ Failure ]
......@@ -1571,14 +1573,11 @@ crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/insert-spa
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/insert-spanner-pseudo-before-after-in-content.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/insert-spanner-pseudo-before-following-content.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/insert-spanner-pseudo-before.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/invalid-spanner-container-becomes-valid.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/relpos-becomes-static-has-abspos.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-abspos-next-to-spanner.html [ Crash Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-and-insert-block-after-spanner.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-and-insert-block-before-spanner.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-and-insert-block-between-spanners.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-block-after-spanner.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-block-before-spanner.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-block-between-spanners.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-column-content-next-to-abspos-between-spanners.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-spanner-after-content.html [ Failure ]
......@@ -1658,7 +1657,6 @@ crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-with-forced
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-with-padding.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-with-single-empty-block.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-with-single-tall-line.html [ Failure ]
crbug.com/994172 virtual/layout_ng_experimental/fast/multicol/nested-with-spanner-inside-margins-crash.html [ Crash ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/avoid-column-break-inside.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/balance2.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/balance3.html [ Failure ]
......@@ -1707,15 +1705,11 @@ crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/balance-after
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/balance-after-spanner-some-extra-height.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/balance-before-and-after-spanner.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/balance-before-spanner-extra-height.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/becomes-spanner-with-new-width.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/block-with-top-border-and-margin-around-spanner-exact-fit.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/block-with-top-border-and-margin-around-spanner-extra-space.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/break-in-columns-before-spanner.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/change-multicol-writing-mode.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/change-spanner-margins.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/empty-block-between-spanners.html [ Crash ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/empty-block-between-spanners-with-margins.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/empty-block-with-bottom-margin-between-spanners.html [ Crash ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/empty-spanner-between-spanners-with-margins.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/fill-after-spanner-exact-fit.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/fill-after-spanner-extra-height.html [ Failure ]
......@@ -1735,7 +1729,6 @@ crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/offset-proper
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/outer-column-break-after-inner-spanner-2.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/outer-column-break-after-inner-spanner-and-float.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/outer-column-break-after-inner-spanner.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/outline.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/overflow-on-multicol.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/overflow-on-viewport.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/percent-margins.html [ Failure ]
......@@ -1750,8 +1743,6 @@ crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/relpos-in-blo
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/relpos-spanner-with-abspos-child.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/remaining-space-in-last-column.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/simple-margins.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/sole-spanner.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/sole-spanner-inside-div.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/span-between-text.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-first.html [ Failure ]
crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-img.html [ Failure ]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment