[FlexNG] Handle break rules for multi-line columns
Add support for break-before/after/inside rules for multi-line column
flex containers. Because we fragment the columns independently, we
needed a way to keep track of the break-after and early-break for each
column (tracked by NGFlexColumnBreakInfo).
We also may now have more than one early break in a given fragment, so
a way to pass in additional early breaks was added to the layout
algorithm when we abort due to an early break.
Because columns can be considered a "row" of columns, the break-before
for all first items in a column were accumulated to the container
(same with break-after and the last items in each column).
Bug: 660611
Change-Id: I198fe0543f2c89bb5d188a4e2996bec89b4546cf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3531137
Reviewed-by: Morten Stenshorne <mstensho@chromium.org>
Commit-Queue: Alison Maher <almaher@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#982441}
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
index e621bc79..59677476 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
@@ -28,7 +28,6 @@
}
HeapVector<NGFlexLine> flex_lines;
- // |row_break_between| is only used in the case of row flex containers.
Vector<EBreakBetween> row_break_between;
LayoutUnit intrinsic_block_size;
// |broke_before_row| is only used in the case of row flex containers. If this
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
index 6b5a281..3adfce4 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -815,7 +815,7 @@
// break at that location.
DCHECK(result->GetEarlyBreak());
return RelayoutAndBreakEarlier<NGFlexLayoutAlgorithm>(
- *result->GetEarlyBreak());
+ *result->GetEarlyBreak(), &column_early_breaks_);
case NGLayoutResult::kNeedsRelayoutWithNoChildScrollbarChanges:
return RelayoutIgnoringChildScrollbarChanges();
case NGLayoutResult::kDisableFragmentation:
@@ -946,9 +946,11 @@
}
// For rows, the break-before of the first row and the break-after of the
- // last row are propagated to the container.
- if (is_horizontal_flow_ &&
- ConstraintSpace().ShouldPropagateChildBreakValues()) {
+ // last row are propagated to the container. For columns, treat the set
+ // of columns as a single row and propagate the combined break-before rules
+ // for the first items in each column and break-after rules for last items in
+ // each column.
+ if (ConstraintSpace().ShouldPropagateChildBreakValues()) {
DCHECK(!row_break_between_outputs.IsEmpty());
container_builder_.SetInitialBreakBefore(row_break_between_outputs.front());
container_builder_.SetPreviousBreakAfter(row_break_between_outputs.back());
@@ -1145,14 +1147,21 @@
}
bool should_propagate_row_break_values =
- is_horizontal_flow_ &&
ConstraintSpace().ShouldPropagateChildBreakValues();
if (should_propagate_row_break_values) {
DCHECK(row_break_between_outputs);
// The last row break between will store the final break-after to be
// propagated to the container.
- *row_break_between_outputs = Vector<EBreakBetween>(
- flex_line_outputs->size() + 1, EBreakBetween::kAuto);
+ if (is_horizontal_flow_) {
+ *row_break_between_outputs = Vector<EBreakBetween>(
+ flex_line_outputs->size() + 1, EBreakBetween::kAuto);
+ } else {
+ // For flex columns, we only need to store two values - one for
+ // the break-before value of all combined columns, and the second for
+ // for the break-after values for all combined columns.
+ *row_break_between_outputs =
+ Vector<EBreakBetween>(2, EBreakBetween::kAuto);
+ }
}
absl::optional<LayoutUnit> fallback_baseline;
@@ -1185,12 +1194,6 @@
flex_item.has_descendant_that_depends_on_percentage_block_size =
layout_result->HasDescendantThatDependsOnPercentageBlockSize();
- // The break-before and break-after values of flex items in a flex row are
- // propagated to the row itself. Accumulate the BreakBetween values for
- // each row ahead of time so that they can be stored on the break token
- // for future use.
- //
- // https://drafts.csswg.org/css-flexbox-1/#pagination
if (should_propagate_row_break_values) {
const auto& item_style = flex_item.Style();
auto item_break_before = JoinFragmentainerBreakValues(
@@ -1198,13 +1201,34 @@
auto item_break_after = JoinFragmentainerBreakValues(
item_style.BreakAfter(), layout_result->FinalBreakAfter());
- (*row_break_between_outputs)[flex_line_idx] =
- JoinFragmentainerBreakValues(
- (*row_break_between_outputs)[flex_line_idx], item_break_before);
- (*row_break_between_outputs)[flex_line_idx + 1] =
- JoinFragmentainerBreakValues(
- (*row_break_between_outputs)[flex_line_idx + 1],
- item_break_after);
+ // The break-before and break-after values of flex items in a flex row
+ // are propagated to the row itself. Accumulate the BreakBetween values
+ // for each row ahead of time so that they can be stored on the break
+ // token for future use.
+ //
+ // https://drafts.csswg.org/css-flexbox-1/#pagination
+ if (is_horizontal_flow_) {
+ (*row_break_between_outputs)[flex_line_idx] =
+ JoinFragmentainerBreakValues(
+ (*row_break_between_outputs)[flex_line_idx],
+ item_break_before);
+ (*row_break_between_outputs)[flex_line_idx + 1] =
+ JoinFragmentainerBreakValues(
+ (*row_break_between_outputs)[flex_line_idx + 1],
+ item_break_after);
+ } else {
+ // Treat all columns as a "row" of columns, and accumulate the initial
+ // and final break values for all columns, which will be propagated to
+ // the container.
+ if (flex_item_idx == 0) {
+ (*row_break_between_outputs)[0] = JoinFragmentainerBreakValues(
+ (*row_break_between_outputs)[0], item_break_before);
+ }
+ if (flex_item_idx == line_output.line_items.size() - 1) {
+ (*row_break_between_outputs)[1] = JoinFragmentainerBreakValues(
+ (*row_break_between_outputs)[1], item_break_after);
+ }
+ }
}
const auto& physical_fragment =
@@ -1267,6 +1291,13 @@
is_horizontal_flow_);
Vector<bool> has_inflow_child_break_inside_line(flex_line_outputs->size(),
false);
+ bool needs_earlier_break_in_column = false;
+
+ HeapVector<NGFlexColumnBreakInfo> column_break_info;
+ if (!is_horizontal_flow_) {
+ column_break_info =
+ HeapVector<NGFlexColumnBreakInfo>(flex_line_outputs->size());
+ }
for (auto entry = item_iterator.NextItem(*broke_before_row);
NGFlexItem* flex_item = entry.flex_item;
@@ -1285,6 +1316,16 @@
has_inflow_child_break_inside_line[flex_line_idx - 1])
break;
} else {
+ // If we are relaying out as a result of an early break, and we have early
+ // breaks for more than one column, they will be stored in
+ // |additional_early_breaks_|. Keep |early_break_| consistent with that of
+ // the current column.
+ if (additional_early_breaks_ &&
+ flex_line_idx < additional_early_breaks_->size())
+ early_break_ = (*additional_early_breaks_)[flex_line_idx];
+ else if (early_break_ && flex_line_idx != 0)
+ early_break_ = nullptr;
+
if (has_inflow_child_break_inside_line[flex_line_idx]) {
if (!last_item_in_line)
item_iterator.NextLine();
@@ -1348,10 +1389,17 @@
*broke_before_row = true;
ConsumeRemainingFragmentainerSpace(previously_consumed_block_size,
&line_output);
+ // For column flex containers, continue to the next column. For rows,
+ // continue until we've processed all items in the current row.
has_inflow_child_break_inside_line[flex_line_idx] = true;
- if (!last_item_in_line && !is_horizontal_flow_)
- item_iterator.NextLine();
- return NGLayoutResult::kSuccess;
+ if (!is_horizontal_flow_) {
+ if (!last_item_in_line)
+ item_iterator.NextLine();
+ } else if (last_item_in_line) {
+ return NGLayoutResult::kSuccess;
+ }
+ last_line_idx_to_process_first_child_ = flex_line_idx;
+ continue;
} else {
early_break_in_child =
EnterEarlyBreakInChild(flex_item->ng_input_node, *early_break_);
@@ -1375,6 +1423,7 @@
child_space, item_break_token, early_break_in_child);
NGBreakStatus break_status = NGBreakStatus::kContinue;
+ NGFlexColumnBreakInfo* current_column_break_info = nullptr;
if (!early_break_ && ConstraintSpace().HasBlockFragmentation()) {
bool has_container_separation = false;
if (is_horizontal_flow_) {
@@ -1403,13 +1452,42 @@
}
} else {
has_container_separation =
- last_line_idx_to_process_first_child_ == flex_line_idx ||
+ (last_line_idx_to_process_first_child_ != kNotFound &&
+ last_line_idx_to_process_first_child_ >= flex_line_idx) ||
(!item_break_token && offset.block_offset > LayoutUnit());
+
+ // We may switch back and forth between columns, so we need to make sure
+ // to use the break-after for the current column.
+ if (flex_line_outputs->size() > 1) {
+ current_column_break_info = &column_break_info[flex_line_idx];
+ container_builder_.SetPreviousBreakAfter(
+ current_column_break_info->break_after);
+ }
}
break_status = BreakBeforeChildIfNeeded(
ConstraintSpace(), flex_item->ng_input_node, *layout_result,
ConstraintSpace().FragmentainerOffsetAtBfc() + offset.block_offset,
- has_container_separation, &container_builder_, is_horizontal_flow_);
+ has_container_separation, &container_builder_, is_horizontal_flow_,
+ current_column_break_info);
+ }
+
+ if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
+ if (current_column_break_info) {
+ DCHECK(!is_horizontal_flow_);
+ DCHECK(current_column_break_info->early_break);
+ if (!needs_earlier_break_in_column) {
+ needs_earlier_break_in_column = true;
+ container_builder_.SetEarlyBreak(
+ current_column_break_info->early_break);
+ }
+ // Keep track of the early breaks for each column.
+ AddColumnEarlyBreak(current_column_break_info->early_break,
+ flex_line_idx);
+ if (!last_item_in_line)
+ item_iterator.NextLine();
+ continue;
+ }
+ return NGLayoutResult::kNeedsEarlierBreak;
}
if (break_status == NGBreakStatus::kBrokeBefore) {
@@ -1427,9 +1505,6 @@
last_line_idx_to_process_first_child_ = flex_line_idx;
continue;
}
- if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
- return NGLayoutResult::kNeedsEarlierBreak;
- }
const auto& physical_fragment =
To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment());
@@ -1461,13 +1536,25 @@
}
}
+ if (!is_horizontal_flow_) {
+ line_output.line_intrinsic_block_size =
+ line_output.line_intrinsic_block_size.ClampIndefiniteToZero();
+ line_output.line_intrinsic_block_size +=
+ offset.block_offset + fragment.BlockSize();
+ }
+
// TODO(almaher): What to do in the case where the line extends past
// the last item? Should that be included when fragmenting?
intrinsic_block_size_ +=
(offset.block_offset + fragment.BlockSize() - intrinsic_block_size_)
.ClampNegativeToZero();
- container_builder_.AddResult(*layout_result, offset);
+ container_builder_.AddResult(*layout_result, offset,
+ /* relative_offset */ absl::nullopt,
+ /* inline_container */ nullptr,
+ current_column_break_info
+ ? ¤t_column_break_info->break_after
+ : nullptr);
// Only propagate baselines from children on the first flex-line.
if (&line_output == flex_line_outputs->begin()) {
@@ -1499,6 +1586,9 @@
last_line_idx_to_process_first_child_ = flex_line_idx;
}
+ if (needs_earlier_break_in_column)
+ return NGLayoutResult::kNeedsEarlierBreak;
+
if (!container_builder_.HasInflowChildBreakInside() &&
!item_iterator.NextItem(*broke_before_row).flex_item) {
container_builder_.SetHasSeenAllChildren();
@@ -1988,8 +2078,15 @@
// This will be further adjusted by the total consumed block size once we
// handle the break before in the next fragmentainer. This ensures that the
// expansion is properly handled in the column balancing pass.
+ LayoutUnit intrinsic_block_size = intrinsic_block_size_;
+ if (flex_line->line_intrinsic_block_size != kIndefiniteSize) {
+ DCHECK(!is_horizontal_flow_);
+ intrinsic_block_size = (flex_line->line_intrinsic_block_size -
+ previously_consumed_block_size)
+ .ClampNegativeToZero();
+ }
flex_line->item_offset_adjustment -=
- intrinsic_block_size_ + previously_consumed_block_size;
+ intrinsic_block_size + previously_consumed_block_size;
}
if (!ConstraintSpace().HasKnownFragmentainerBlockSize())
@@ -2096,6 +2193,14 @@
return true;
}
+void NGFlexLayoutAlgorithm::AddColumnEarlyBreak(NGEarlyBreak* breakpoint,
+ wtf_size_t index) {
+ DCHECK(!is_horizontal_flow_);
+ while (column_early_breaks_.size() <= index)
+ column_early_breaks_.push_back(nullptr);
+ column_early_breaks_[index] = breakpoint;
+}
+
#if DCHECK_IS_ON()
void NGFlexLayoutAlgorithm::CheckFlexLines(
const HeapVector<NGFlexLine>& flex_line_outputs) const {
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
index d421a30..205c294 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
@@ -150,6 +150,9 @@
LayoutUnit row_block_size,
wtf_size_t row_index);
+ // Add an early break for the column at the provided |index|.
+ void AddColumnEarlyBreak(NGEarlyBreak* breakpoint, wtf_size_t index);
+
#if DCHECK_IS_ON()
void CheckFlexLines(const HeapVector<NGFlexLine>& flex_line_outputs) const;
#endif
@@ -184,6 +187,11 @@
// fragmenting, |total_intrinsic_block_size| and |intrinsic_block_size_| will
// be equivalent.
LayoutUnit total_intrinsic_block_size_;
+
+ // Only one early break is supported per container. However, we may need to
+ // return to an early break within multiple flex columns. This stores the
+ // early breaks per column to be used when aborting layout.
+ HeapVector<Member<NGEarlyBreak>> column_early_breaks_;
};
} // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
index b4840861..5fc0923 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
@@ -43,6 +43,8 @@
LayoutUnit line_cross_size;
LayoutUnit cross_axis_offset;
LayoutUnit item_offset_adjustment;
+ // This is only used for columns during fragmentation.
+ LayoutUnit line_intrinsic_block_size = kIndefiniteSize;
bool has_seen_all_children = false;
HeapVector<NGFlexItem> line_items;
};
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index c83e3e0..88f1fe0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -85,7 +85,8 @@
const NGLayoutResult& child_layout_result,
const LogicalOffset offset,
absl::optional<LogicalOffset> relative_offset,
- const NGInlineContainer<LogicalOffset>* inline_container) {
+ const NGInlineContainer<LogicalOffset>* inline_container,
+ EBreakBetween* flex_column_break_after) {
const auto& fragment = child_layout_result.PhysicalFragment();
// We'll normally propagate info from child_layout_result here, but if that's
@@ -129,7 +130,7 @@
PropagateBreakInfo(*result_for_propagation, offset);
if (UNLIKELY(ConstraintSpace() &&
ConstraintSpace()->ShouldPropagateChildBreakValues()))
- PropagateChildBreakValues(*result_for_propagation);
+ PropagateChildBreakValues(*result_for_propagation, flex_column_break_after);
}
void NGBoxFragmentBuilder::AddChild(
@@ -444,7 +445,8 @@
}
void NGBoxFragmentBuilder::PropagateChildBreakValues(
- const NGLayoutResult& child_layout_result) {
+ const NGLayoutResult& child_layout_result,
+ EBreakBetween* flex_column_break_after) {
if (child_layout_result.Status() != NGLayoutResult::kSuccess)
return;
@@ -474,6 +476,8 @@
EBreakBetween break_after = JoinFragmentainerBreakValues(
child_layout_result.FinalBreakAfter(), child_style.BreakAfter());
SetPreviousBreakAfter(break_after);
+ if (flex_column_break_after)
+ *flex_column_break_after = break_after;
}
const NGLayoutResult* NGBoxFragmentBuilder::ToBoxFragment(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index 0c93eb4..1a3fd79 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -197,12 +197,15 @@
// computed ahead of time. If so, a |relative_offset| will be passed
// in. Otherwise, the relative offset will be calculated as normal.
// |inline_container| is passed when adding an OOF that is contained by a
- // non-atomic inline.
+ // non-atomic inline. If |flex_column_break_after| is supplied, we are running
+ // layout for a column flex container, in which case, we need to update the
+ // break-after value for the column itself.
void AddResult(
const NGLayoutResult&,
const LogicalOffset,
absl::optional<LogicalOffset> relative_offset = absl::nullopt,
- const NGInlineContainer<LogicalOffset>* inline_container = nullptr);
+ const NGInlineContainer<LogicalOffset>* inline_container = nullptr,
+ EBreakBetween* flex_column_break_after = nullptr);
// Add a child fragment and propagate info from it. Called by AddResult().
// Other callers should call AddResult() instead of this when possible, since
@@ -669,7 +672,10 @@
}
// Propagate the break-before/break-after of the child (if applicable).
- void PropagateChildBreakValues(const NGLayoutResult& child_layout_result);
+ // Update the break-after value for |flex_column_break_after|, if supplied.
+ void PropagateChildBreakValues(
+ const NGLayoutResult& child_layout_result,
+ EBreakBetween* flex_column_break_after = nullptr);
private:
// Propagate fragmentation details. This includes checking whether we have
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
index 13149c3f..3222e7a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
@@ -593,13 +593,15 @@
return NGBreakStatus::kContinue;
}
-NGBreakStatus BreakBeforeChildIfNeeded(const NGConstraintSpace& space,
- NGLayoutInputNode child,
- const NGLayoutResult& layout_result,
- LayoutUnit fragmentainer_block_offset,
- bool has_container_separation,
- NGBoxFragmentBuilder* builder,
- bool is_row_item) {
+NGBreakStatus BreakBeforeChildIfNeeded(
+ const NGConstraintSpace& space,
+ NGLayoutInputNode child,
+ const NGLayoutResult& layout_result,
+ LayoutUnit fragmentainer_block_offset,
+ bool has_container_separation,
+ NGBoxFragmentBuilder* builder,
+ bool is_row_item,
+ NGFlexColumnBreakInfo* flex_column_break_info) {
DCHECK(space.HasBlockFragmentation());
// Break-before and break-after are handled at the row level.
@@ -608,8 +610,11 @@
CalculateBreakBetweenValue(child, layout_result, *builder);
if (IsForcedBreakValue(space, break_between)) {
BreakBeforeChild(space, child, &layout_result, fragmentainer_block_offset,
- kBreakAppealPerfect, /* is_forced_break */ true,
- builder);
+ kBreakAppealPerfect, /* is_forced_break */ true, builder,
+ /* block_size_override */ absl::nullopt,
+ flex_column_break_info
+ ? &flex_column_break_info->break_after
+ : nullptr);
return NGBreakStatus::kBrokeBefore;
}
}
@@ -622,14 +627,16 @@
// the appeal of breaking there, even if we didn't.
if (MovePastBreakpoint(space, child, layout_result,
fragmentainer_block_offset, appeal_before, builder,
- is_row_item))
+ is_row_item, flex_column_break_info))
return NGBreakStatus::kContinue;
// Breaking inside the child isn't appealing, and we're out of space. Figure
// out where to insert a soft break. It will either be before this child, or
// before an earlier sibling, if there's a more appealing breakpoint there.
if (!AttemptSoftBreak(space, child, &layout_result,
- fragmentainer_block_offset, appeal_before, builder))
+ fragmentainer_block_offset, appeal_before, builder,
+ /* block_size_override */ absl::nullopt,
+ flex_column_break_info))
return NGBreakStatus::kNeedsEarlierBreak;
return NGBreakStatus::kBrokeBefore;
@@ -642,7 +649,8 @@
absl::optional<NGBreakAppeal> appeal,
bool is_forced_break,
NGBoxFragmentBuilder* builder,
- absl::optional<LayoutUnit> block_size_override) {
+ absl::optional<LayoutUnit> block_size_override,
+ EBreakBetween* flex_column_break_after) {
#if DCHECK_IS_ON()
DCHECK(layout_result || block_size_override);
if (layout_result && layout_result->Status() == NGLayoutResult::kSuccess) {
@@ -668,7 +676,7 @@
if (layout_result && space.ShouldPropagateChildBreakValues() &&
!is_forced_break)
- builder->PropagateChildBreakValues(*layout_result);
+ builder->PropagateChildBreakValues(*layout_result, flex_column_break_after);
// We'll drop the fragment (if any) on the floor and retry at the start of the
// next fragmentainer.
@@ -723,7 +731,8 @@
LayoutUnit fragmentainer_block_offset,
NGBreakAppeal appeal_before,
NGBoxFragmentBuilder* builder,
- bool is_row_item) {
+ bool is_row_item,
+ NGFlexColumnBreakInfo* flex_column_break_info) {
if (layout_result.Status() != NGLayoutResult::kSuccess) {
// Layout aborted - no fragment was produced. There's nothing to move
// past. We need to break before.
@@ -819,10 +828,17 @@
// breaking before is impossible, break inside regardless of appeal.
if (refuse_break_before)
return true;
- if (appeal_inside >= appeal_before &&
- (!builder || !builder->HasEarlyBreak() ||
- appeal_inside >= builder->EarlyBreak().BreakAppeal()))
- return true;
+ if (appeal_inside >= appeal_before) {
+ if (flex_column_break_info &&
+ (!flex_column_break_info->early_break ||
+ appeal_inside >=
+ flex_column_break_info->early_break->BreakAppeal())) {
+ return true;
+ } else if (!builder || !builder->HasEarlyBreak() ||
+ appeal_inside >= builder->EarlyBreak().BreakAppeal()) {
+ return true;
+ }
+ }
} else {
bool move_past = refuse_break_before;
if (!move_past) {
@@ -849,7 +865,8 @@
// orphans and widows, if at all possible. We also only do this for
// non-row items since items in a row will be parallel to one another.)
UpdateEarlyBreakAtBlockChild(space, To<NGBlockNode>(child),
- layout_result, appeal_before, builder);
+ layout_result, appeal_before, builder,
+ flex_column_break_info);
}
return true;
}
@@ -859,11 +876,13 @@
return false;
}
-void UpdateEarlyBreakAtBlockChild(const NGConstraintSpace& space,
- NGBlockNode child,
- const NGLayoutResult& layout_result,
- NGBreakAppeal appeal_before,
- NGBoxFragmentBuilder* builder) {
+void UpdateEarlyBreakAtBlockChild(
+ const NGConstraintSpace& space,
+ NGBlockNode child,
+ const NGLayoutResult& layout_result,
+ NGBreakAppeal appeal_before,
+ NGBoxFragmentBuilder* builder,
+ NGFlexColumnBreakInfo* flex_column_break_info) {
// If the child already broke, it's a little too late to look for breakpoints.
DCHECK(!layout_result.PhysicalFragment().BreakToken());
@@ -872,8 +891,19 @@
if (const NGEarlyBreak* breakpoint = layout_result.GetEarlyBreak()) {
appeal_inside = CalculateBreakAppealInside(space, layout_result,
breakpoint->BreakAppeal());
- if (!builder->HasEarlyBreak() ||
- builder->EarlyBreak().BreakAppeal() <= breakpoint->BreakAppeal()) {
+ if (flex_column_break_info) {
+ if (!flex_column_break_info->early_break ||
+ flex_column_break_info->early_break->BreakAppeal() <=
+ breakpoint->BreakAppeal()) {
+ // Found a good breakpoint inside the child. Add the child to the early
+ // break chain for the current column.
+ auto* parent_break = MakeGarbageCollected<NGEarlyBreak>(
+ child, appeal_inside, breakpoint);
+ flex_column_break_info->early_break = parent_break;
+ }
+ } else if (!builder->HasEarlyBreak() ||
+ builder->EarlyBreak().BreakAppeal() <=
+ breakpoint->BreakAppeal()) {
// Found a good breakpoint inside the child. Add the child to the early
// break container chain, and store it.
auto* parent_break =
@@ -889,6 +919,15 @@
if (appeal_before <= appeal_inside)
return;
+ if (flex_column_break_info) {
+ if (flex_column_break_info->early_break &&
+ flex_column_break_info->early_break->BreakAppeal() > appeal_before)
+ return;
+ flex_column_break_info->early_break =
+ MakeGarbageCollected<NGEarlyBreak>(child, appeal_before);
+ return;
+ }
+
if (builder->HasEarlyBreak() &&
builder->EarlyBreak().BreakAppeal() > appeal_before)
return;
@@ -903,12 +942,21 @@
LayoutUnit fragmentainer_block_offset,
NGBreakAppeal appeal_before,
NGBoxFragmentBuilder* builder,
- absl::optional<LayoutUnit> block_size_override) {
+ absl::optional<LayoutUnit> block_size_override,
+ NGFlexColumnBreakInfo* flex_column_break_info) {
DCHECK(layout_result || block_size_override);
// If there's a breakpoint with higher appeal among earlier siblings, we need
// to abort and re-layout to that breakpoint.
- if (builder->HasEarlyBreak() &&
- builder->EarlyBreak().BreakAppeal() > appeal_before) {
+ bool found_earlier_break = false;
+ if (flex_column_break_info) {
+ found_earlier_break =
+ flex_column_break_info->early_break &&
+ flex_column_break_info->early_break->BreakAppeal() > appeal_before;
+ } else {
+ found_earlier_break = builder->HasEarlyBreak() &&
+ builder->EarlyBreak().BreakAppeal() > appeal_before;
+ }
+ if (found_earlier_break) {
// Found a better place to break. Before aborting, calculate and report
// space shortage from where we'd actually break.
PropagateSpaceShortage(space, layout_result, fragmentainer_block_offset,
@@ -919,9 +967,10 @@
// Break before the child. Note that there may be a better break further up
// with higher appeal (but it's too early to tell), in which case this
// breakpoint will be replaced.
- BreakBeforeChild(space, child, layout_result, fragmentainer_block_offset,
- appeal_before, /* is_forced_break */ false, builder,
- block_size_override);
+ BreakBeforeChild(
+ space, child, layout_result, fragmentainer_block_offset, appeal_before,
+ /* is_forced_break */ false, builder, block_size_override,
+ flex_column_break_info ? &flex_column_break_info->break_after : nullptr);
return true;
}
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
index b49e4e9..aae9d6b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
@@ -22,6 +22,19 @@
class NGEarlyBreak;
class NGLayoutResult;
+// Each column in a flex container is fragmented independently, so we need to
+// track early break and break-after info for each column separately.
+struct NGFlexColumnBreakInfo {
+ DISALLOW_NEW();
+
+ NGFlexColumnBreakInfo() = default;
+
+ void Trace(Visitor* visitor) const { visitor->Trace(early_break); }
+
+ Member<NGEarlyBreak> early_break = nullptr;
+ EBreakBetween break_after = EBreakBetween::kAuto;
+};
+
// Join two adjacent break values specified on break-before and/or break-
// after. avoid* values win over auto values, and forced break values win over
// avoid* values. |first_value| is specified on an element earlier in the flow
@@ -268,17 +281,24 @@
// class C breakpoints. Those occur if there's a positive gap between the
// block-start content edge of the container and the block-start margin edge of
// the first in-flow child. https://www.w3.org/TR/css-break-3/#possible-breaks
-NGBreakStatus BreakBeforeChildIfNeeded(const NGConstraintSpace&,
- NGLayoutInputNode child,
- const NGLayoutResult&,
- LayoutUnit fragmentainer_block_offset,
- bool has_container_separation,
- NGBoxFragmentBuilder*,
- bool is_row_item = false);
+// If |flex_column_break_info| is supplied, we are running layout for a column
+// flex container, in which case, we may be tracking certain break behavior at
+// the column level.
+NGBreakStatus BreakBeforeChildIfNeeded(
+ const NGConstraintSpace&,
+ NGLayoutInputNode child,
+ const NGLayoutResult&,
+ LayoutUnit fragmentainer_block_offset,
+ bool has_container_separation,
+ NGBoxFragmentBuilder*,
+ bool is_row_item = false,
+ NGFlexColumnBreakInfo* flex_column_break_info = nullptr);
// Insert a break before the child, and propagate space shortage if needed.
// |block_size_override| should only be supplied when you wish to propagate a
-// different block-size than that of the provided layout result.
+// different block-size than that of the provided layout result. If
+// |flex_column_break_after| is supplied, then we need to update the
+// break-after value for the column, as well.
void BreakBeforeChild(
const NGConstraintSpace&,
NGLayoutInputNode child,
@@ -287,7 +307,8 @@
absl::optional<NGBreakAppeal> appeal,
bool is_forced_break,
NGBoxFragmentBuilder*,
- absl::optional<LayoutUnit> block_size_override = absl::nullopt);
+ absl::optional<LayoutUnit> block_size_override = absl::nullopt,
+ EBreakBetween* flex_column_break_after = nullptr);
// Propagate the block-size of unbreakable content. This is used to inflate the
// initial minimal column block-size when balancing columns, before we calculate
@@ -321,29 +342,36 @@
// Move past the breakpoint before the child, if possible, and return true. Also
// update the appeal of breaking before or inside the child (if we're not going
// to break before it). If false is returned, it means that we need to break
-// before the child (or even earlier).
-bool MovePastBreakpoint(const NGConstraintSpace& space,
- NGLayoutInputNode child,
- const NGLayoutResult& layout_result,
- LayoutUnit fragmentainer_block_offset,
- NGBreakAppeal appeal_before,
- NGBoxFragmentBuilder* builder,
- bool is_row_item = false);
+// before the child (or even earlier). See BreakBeforeChildIfNeeded() for
+// details on |flex_column_break_info|.
+bool MovePastBreakpoint(
+ const NGConstraintSpace& space,
+ NGLayoutInputNode child,
+ const NGLayoutResult& layout_result,
+ LayoutUnit fragmentainer_block_offset,
+ NGBreakAppeal appeal_before,
+ NGBoxFragmentBuilder* builder,
+ bool is_row_item = false,
+ NGFlexColumnBreakInfo* flex_column_break_info = nullptr);
// If the appeal of breaking before or inside the child is the same or higher
// than any previous breakpoint we've found, set a new breakpoint in the
-// builder, and update appeal accordingly.
-void UpdateEarlyBreakAtBlockChild(const NGConstraintSpace&,
- NGBlockNode child,
- const NGLayoutResult&,
- NGBreakAppeal appeal_before,
- NGBoxFragmentBuilder*);
+// builder, and update appeal accordingly. See BreakBeforeChildIfNeeded() for
+// details on |flex_column_break_info|.
+void UpdateEarlyBreakAtBlockChild(
+ const NGConstraintSpace&,
+ NGBlockNode child,
+ const NGLayoutResult&,
+ NGBreakAppeal appeal_before,
+ NGBoxFragmentBuilder*,
+ NGFlexColumnBreakInfo* flex_column_break_info = nullptr);
// Attempt to insert a soft break before the child, and return true if we did.
// If false is returned, it means that the desired breakpoint is earlier in the
// container, and that we need to abort and re-layout to that breakpoint.
// |block_size_override| should only be supplied when you wish to propagate a
-// different block-size than that of the provided layout result.
+// different block-size than that of the provided layout result. See
+// BreakBeforeChildIfNeeded() for details on |flex_column_break_info|.
bool AttemptSoftBreak(
const NGConstraintSpace&,
NGLayoutInputNode child,
@@ -351,7 +379,8 @@
LayoutUnit fragmentainer_block_offset,
NGBreakAppeal appeal_before,
NGBoxFragmentBuilder*,
- absl::optional<LayoutUnit> block_size_override = absl::nullopt);
+ absl::optional<LayoutUnit> block_size_override = absl::nullopt,
+ NGFlexColumnBreakInfo* flex_column_break_info = nullptr);
// If we have an previously found break point, and we're entering an ancestor of
// the node we're going to break before, return the early break inside. This can
@@ -431,4 +460,6 @@
} // namespace blink
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::NGFlexColumnBreakInfo)
+
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENTATION_UTILS_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h
index 4adffc7..7af8c85 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h
@@ -40,16 +40,19 @@
STACK_ALLOCATED();
public:
- NGLayoutAlgorithmParams(NGBlockNode node,
- const NGFragmentGeometry& fragment_geometry,
- const NGConstraintSpace& space,
- const NGBlockBreakToken* break_token = nullptr,
- const NGEarlyBreak* early_break = nullptr)
+ NGLayoutAlgorithmParams(
+ NGBlockNode node,
+ const NGFragmentGeometry& fragment_geometry,
+ const NGConstraintSpace& space,
+ const NGBlockBreakToken* break_token = nullptr,
+ const NGEarlyBreak* early_break = nullptr,
+ const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks = nullptr)
: node(node),
fragment_geometry(fragment_geometry),
space(space),
break_token(break_token),
- early_break(early_break) {}
+ early_break(early_break),
+ additional_early_breaks(additional_early_breaks) {}
NGBlockNode node;
const NGFragmentGeometry& fragment_geometry;
@@ -57,6 +60,7 @@
const NGBlockBreakToken* break_token;
const NGEarlyBreak* early_break;
const NGLayoutResult* previous_result = nullptr;
+ const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks;
};
// Base class for all LayoutNG algorithms.
@@ -88,7 +92,8 @@
params.node,
¶ms.node.Style(),
¶ms.space,
- {params.space.GetWritingMode(), params.space.Direction()}) {
+ {params.space.GetWritingMode(), params.space.Direction()}),
+ additional_early_breaks_(params.additional_early_breaks) {
container_builder_.SetIsNewFormattingContext(
params.space.IsNewFormattingContext());
container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
@@ -138,13 +143,16 @@
// such as orphans, widows, break-before:avoid or break-after:avoid.
template <typename Algorithm>
const NGLayoutResult* RelayoutAndBreakEarlier(
- const NGEarlyBreak& breakpoint) {
+ const NGEarlyBreak& breakpoint,
+ const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks =
+ nullptr) {
// Not allowed to recurse!
DCHECK(!early_break_);
+ DCHECK(!additional_early_breaks_ || additional_early_breaks_->IsEmpty());
NGLayoutAlgorithmParams params(
Node(), container_builder_.InitialFragmentGeometry(), ConstraintSpace(),
- BreakToken(), &breakpoint);
+ BreakToken(), &breakpoint, additional_early_breaks);
Algorithm algorithm_with_break(params);
auto& new_builder = algorithm_with_break.container_builder_;
new_builder.SetBoxType(container_builder_.BoxType());
@@ -191,6 +199,11 @@
const NGBreakTokenType* break_token_;
NGBoxFragmentBuilderType container_builder_;
+
+ // There are cases where we may need more than one early break per fragment.
+ // For example, there may be an early break within multiple flex columns. This
+ // can be used to pass additional early breaks to the next layout pass.
+ const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks_;
};
} // namespace blink
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index fb287d7..1d7c847 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1633,6 +1633,18 @@
virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/flex-container-fragmentation-007.tentative.html [ Pass ]
virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-015.html [ Pass ]
virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-016.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html [ Pass ]
virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-007.html [ Pass ]
virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-008.html [ Pass ]
virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-010.html [ Pass ]
@@ -4406,6 +4418,18 @@
crbug.com/660611 external/wpt/css/css-break/flexbox/flex-container-fragmentation-007.tentative.html [ Failure ]
crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-015.html [ Failure ]
crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-016.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html [ Failure ]
crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-007.html [ Failure ]
crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-008.html [ Failure ]
crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-010.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html
new file mode 100644
index 0000000..070b0d6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation with break-inside: avoid.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 5;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 10px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 500px;
+ }
+ #flex > div {
+ background: green;
+ width: 10px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div id="flex">
+ <div style="height: 250px; break-inside: avoid;"></div>
+ <div style="height: 200px; break-inside: avoid;"></div>
+ <div style="height: 120px; break-inside: avoid;"></div>
+ <div style="height: 180px; break-inside: avoid;"></div>
+ <div style="height: 100px; break-inside: avoid;"></div>
+ </div>
+ <div class="abs" style="top: 20px; left: 30px; height: 80px;"></div>
+ <div class="abs" style="top: 50px; left: 40px; height: 50px;"></div>
+ <div class="abs" style="top: 80px; left: 70px; height: 20px;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html
new file mode 100644
index 0000000..a491dfa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation with break-before: avoid.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 5;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 5px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 500px;
+ }
+ #flex > div {
+ background: green;
+ width: 5px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="top: 50px; height: 50px;"></div>
+ <div class="abs" style="top: 50px; left: 25px; height: 50px;"></div>
+ <div id="flex">
+ <div style="height: 50px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 350px; break-before: avoid;"></div>
+ <div style="height: 100px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 250px; break-before: avoid;"></div>
+ <div style="height: 350px; break-before: avoid;"></div>
+ <div style="height: 150px; break-before: avoid;"></div>
+ <div style="height: 500px;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html
new file mode 100644
index 0000000..414326a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation with break-before: avoid.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 5;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 5px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 500px;
+ }
+ #flex > div {
+ background: green;
+ width: 5px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="top: 50px; height: 50px;"></div>
+ <div class="abs" style="top: 50px; left: 25px; height: 50px;"></div>
+ <div id="flex">
+ <div style="height: 50px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 350px; break-before: avoid;"></div>
+ <div style="height: 100px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 250px; break-before: avoid;"></div>
+ <div style="height: 400px;"></div>
+ <div style="height: 100px; break-before: avoid;"></div>
+ <div style="height: 500px;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html
new file mode 100644
index 0000000..18030dd0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation with break-after: avoid.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 5;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 5px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 500px;
+ }
+ #flex > div {
+ background: green;
+ width: 5px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="top: 50px; height: 50px;"></div>
+ <div class="abs" style="top: 50px; left: 25px; height: 50px;"></div>
+ <div id="flex">
+ <div style="height: 50px;"></div>
+ <div style="height: 50px; break-after: avoid;"></div>
+ <div style="height: 350px;"></div>
+ <div style="height: 100px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 50px; break-after: avoid;"></div>
+ <div style="height: 250px;"></div>
+ <div style="height: 400px; break-after: avoid;"></div>
+ <div style="height: 100px; break-after: avoid;"></div>
+ <div style="height: 500px;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html
new file mode 100644
index 0000000..833da1a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation with break-before: column.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 5;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 10px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 500px;
+ }
+ #flex > div {
+ background: green;
+ width: 10px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="top: 50px; height: 50px;"></div>
+ <div class="abs" style="top: 50px; left: 30px; height: 50px;"></div>
+ <div id="flex">
+ <div style="height: 50px;"></div>
+ <div style="height: 50px; break-before: column;"></div>
+ <div style="height: 350px;"></div>
+ <div style="height: 100px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 50px; break-before: column;"></div>
+ <div style="height: 250px;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html
new file mode 100644
index 0000000..60f543b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation with break-after: column.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 5;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 10px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 500px;
+ }
+ #flex > div {
+ background: green;
+ width: 10px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="top: 50px; height: 50px;"></div>
+ <div class="abs" style="top: 50px; left: 30px; height: 50px;"></div>
+ <div id="flex">
+ <div style="height: 50px; break-after: column;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 350px;"></div>
+ <div style="height: 100px;"></div>
+ <div style="height: 50px; break-after: column;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 250px;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html
new file mode 100644
index 0000000..626d267
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation: break-before values on the first item
+ are propagated to the flex container.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 5;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 20px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 400px;
+ }
+ #flex > div {
+ background: green;
+ width: 10px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="top: 50px; height: 50px;"></div>
+ <div style="width: 20px; height: 50px; background: green;"></div>
+ <div style="width: 20px; height: 50px; background: green;"></div>
+ <div id="flex">
+ <div style="height: 50px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 250px;"></div>
+ <div style="height: 100px; break-before: avoid;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 150px;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html
new file mode 100644
index 0000000..4cd53ad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation: break-after values on the first item
+ are propagated to the flex container.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 2;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 50px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 50px;
+ }
+ #flex > div {
+ background: green;
+ width: 25x;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="top: 50px; height: 50px;"></div>
+ <div style="width: 50px; height: 50px; background: green;"></div>
+ <div id="flex">
+ <div style="height: 25px;"></div>
+ <div style="height: 25px; break-after: avoid;"></div>
+ <div style="height: 50px;"></div>
+ </div>
+ <div style="width: 50px; height: 50px; background: green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html
new file mode 100644
index 0000000..93dd9b4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation: break-before values on the first item
+ are propagated to the flex container.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 5;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 20px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 350px;
+ }
+ #flex > div {
+ background: green;
+ width: 10px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="top: 50px; height: 50px;"></div>
+ <div style="width: 20px; height: 50px; background: green;"></div>
+ <div id="flex">
+ <div style="height: 50px; break-before: avoid;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 250px;"></div>
+ <div style="height: 100px; break-before: column;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 150px;"></div>
+ </div>
+ <div style="width: 20px; height: 50px; background: green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html
new file mode 100644
index 0000000..204cc15
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation: break-after values on the first item
+ are propagated to the flex container.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 2;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 50px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 50px;
+ }
+ #flex > div {
+ background: green;
+ width: 25x;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="top: 50px; height: 50px;"></div>
+ <div id="flex">
+ <div style="height: 25px;"></div>
+ <div style="height: 25px; break-after: column;"></div>
+ <div style="height: 50px; break-after: avoid;"></div>
+ </div>
+ <div style="width: 50px; height: 50px; background: green;"></div>
+ <div style="width: 50px; height: 50px; background: green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-027.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-027.html
new file mode 100644
index 0000000..95b7390
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-027.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation: early break inside columns.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 2;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 50px;
+ background: green;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 200px;
+ }
+ #flex > div {
+ background: green;
+ width: 25px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="top: 50px; height: 50px;"></div>
+ <div class="abs" style="left: 100px; height: 50px; background: white;"></div>
+ <div id="flex">
+ <div style="height: 50px;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 100px; break-before: avoid;"></div>
+ <div style="height: 50px;"></div>
+ <div style="height: 50px; break-after: avoid;"></div>
+ <div style="height: 100px;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-028.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-028.html
new file mode 100644
index 0000000..d96cd56
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-028.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation: early break inside columns.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 2;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ .abs {
+ position: absolute;
+ width: 50px;
+ background: white;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 200px;
+ }
+ #flex > div {
+ background: green;
+ width: 25px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div class="abs" style="left: 100px; height: 50px;"></div>
+ <div id="flex">
+ <div>
+ <div style="height: 50px; width: 25px;"></div>
+ <div style="height: 50px; width: 25px;"></div>
+ </div>
+ <div style="height: 100px; break-before: avoid;"></div>
+ <div>
+ <div style="height: 50px; width: 25px;"></div>
+ <div style="height: 50px; width: 25px; break-after: avoid;"></div>
+ </div>
+ <div style="height: 100px;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-029.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-029.html
new file mode 100644
index 0000000..ec47c9f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-029.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation: we shouldn't insert a forced break if
+ there's no preceding content at the start of a fragmentainer.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 2;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div style="display: flex; flex-direction: column; flex-wrap: wrap; width: 50px; height: 200px;">
+ <div style="height: 100px; width: 25px; break-before: column; background: green;"></div>
+ <div style="height: 100px; width: 25px; break-before: column; background: green;"></div>
+ <div style="height: 50px; width: 25px; break-before: column; background: green;"></div>
+ <div style="height: 150px; width: 25px; background: green;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-030.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-030.html
new file mode 100644
index 0000000..4439845
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-030.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation: the flex container consumes the
+ remaining fragmentainer space if an item breaks before.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 2;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div style="display: flex; flex-direction: column; flex-wrap: wrap; width: 50px; height: 200px; background: green;">
+ <div style="height: 10px; width: 25px;"></div>
+ <div style="height: 100px; width: 25px; break-before: column;"></div>
+ <div style="height: 50px; width: 25px;"></div>
+ <div style="height: 100px; width: 25px; break-before: column;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html
new file mode 100644
index 0000000..fc88e70
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation with break-inside: avoid.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 5;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 500px;
+ position: relative;
+ }
+ #flex > div {
+ background: green;
+ width: 10px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div id="flex">
+ <div>
+ <div style="contain: size; width: 10px; height: 90px;"></div>
+ <div style="contain: size; width: 10px; height: 80px;"></div>
+ </div>
+ <div style="height: 30px; break-inside: avoid;"></div>
+ <div style="height: 170px;"></div>
+ <div style="height: 100px;"></div>
+ <div>
+ <div style="contain: size; width: 10px; height: 90px;"></div>
+ <div style="contain: size; width: 10px; height: 80px;"></div>
+ </div>
+ <div style="height: 30px; break-inside: avoid;"></div>
+ <div style="height: 170px;"></div>
+ <div style="height: 100px;"></div>
+ <div style="position: absolute; height: 20px; width: 20px; top: 180px;"></div>
+ </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html
new file mode 100644
index 0000000..978dbd1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>
+ Multi-line column flex fragmentation with forced break and negative margin.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .multicol {
+ column-count: 5;
+ column-fill: auto;
+ column-gap: 0px;
+ height: 100px;
+ width: 100px;
+ position: relative;
+ background: red;
+ z-index: -1;
+ }
+ #flex {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ height: 500px;
+ position: relative;
+ }
+ #flex > div {
+ background: green;
+ width: 10px;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+ <div id="flex">
+ <div style="height: 150px;"></div>
+ <div style="height: 350px; break-before: column; margin-top: -50px;"></div>
+ <div style="height: 150px;"></div>
+ <div style="height: 300px; break-before: column;"></div>
+ </div>
+ <div style="position: absolute; top: 50px; left: 20px; width: 20px; height: 50px; background: green;"></div>
+ <div style="position: absolute; top: -50px; left: 40px; width: 10px; height: 50px; background: white;"></div>
+</div>