[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
+                                     ? &current_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,
             &params.node.Style(),
             &params.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>