Commit 34c1c0a5 authored by Morten Stenshorne's avatar Morten Stenshorne Committed by Commit Bot

[LayoutNG] Support for forced fragmentainer breaks.

This is about implementing the "forced break" values for the CSS
properties break-after and break-before. The legacy
page-break-before:always and page-break-after:always declarations also
work, if in paged media. For multicol, on the other hand, it's more about
supporting break-before:column and break-after:column.

Forced fragmentainer breaks may only occur at class A break points
[1]. If there's a forced break-before value on a first in-flow child,
or a break-after value on a last in-flow child, the values are to be
propagated through the containing block chain, until we find a
suitable place to break. [2] If we don't find a suitable break point,
no break will be inserted. Initial and final break values inside a
block are propagated upwards via NGLayoutResult.

A few more layout tests pass. A few regress, too, among other things
because we don't handle borders at column boundaries too well yet, and
that we don't disable fragmentation inside scrollable containers.

Some of the code in this CL is taken from LayoutBox in the legacy
engine.

[1] https://www.w3.org/TR/css-break-3/#possible-breaks
[2] https://www.w3.org/TR/css-break-3/#break-between

Cq-Include-Trybots: master.tryserver.chromium.linux:linux_layout_tests_layout_ng
Change-Id: Id67f2ed2d008a6d98a7c7a43d98c345964035e7b
Reviewed-on: https://chromium-review.googlesource.com/835110
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#525325}
parent b0dc3da9
...@@ -2570,8 +2570,6 @@ crbug.com/591099 external/wpt/css/css-multicol/multicol-basic-004.html [ Failure ...@@ -2570,8 +2570,6 @@ crbug.com/591099 external/wpt/css/css-multicol/multicol-basic-004.html [ Failure
crbug.com/591099 external/wpt/css/css-multicol/multicol-basic-007.xht [ Failure ] crbug.com/591099 external/wpt/css/css-multicol/multicol-basic-007.xht [ Failure ]
crbug.com/591099 external/wpt/css/css-multicol/multicol-basic-008.xht [ Failure ] crbug.com/591099 external/wpt/css/css-multicol/multicol-basic-008.xht [ Failure ]
crbug.com/591099 external/wpt/css/css-multicol/multicol-br-inside-avoidcolumn-001.xht [ Failure ] crbug.com/591099 external/wpt/css/css-multicol/multicol-br-inside-avoidcolumn-001.xht [ Failure ]
crbug.com/591099 external/wpt/css/css-multicol/multicol-break-000.xht [ Failure ]
crbug.com/591099 external/wpt/css/css-multicol/multicol-break-001.xht [ Failure ]
crbug.com/591099 external/wpt/css/css-multicol/multicol-clip-001.xht [ Failure ] crbug.com/591099 external/wpt/css/css-multicol/multicol-clip-001.xht [ Failure ]
crbug.com/591099 external/wpt/css/css-multicol/multicol-clip-002.xht [ Failure ] crbug.com/591099 external/wpt/css/css-multicol/multicol-clip-002.xht [ Failure ]
crbug.com/591099 external/wpt/css/css-multicol/multicol-collapsing-001.xht [ Failure ] crbug.com/591099 external/wpt/css/css-multicol/multicol-collapsing-001.xht [ Failure ]
...@@ -5631,6 +5629,8 @@ crbug.com/591099 fast/multicol/border-radius-clipped-layer.html [ Failure ] ...@@ -5631,6 +5629,8 @@ crbug.com/591099 fast/multicol/border-radius-clipped-layer.html [ Failure ]
crbug.com/591099 fast/multicol/break-after-always-bottom-margin.html [ Failure ] crbug.com/591099 fast/multicol/break-after-always-bottom-margin.html [ Failure ]
crbug.com/591099 fast/multicol/break-after-empty-set-crash.html [ Crash ] crbug.com/591099 fast/multicol/break-after-empty-set-crash.html [ Crash ]
crbug.com/591099 fast/multicol/break-before-first-line-in-first-child.html [ Failure ] crbug.com/591099 fast/multicol/break-before-first-line-in-first-child.html [ Failure ]
crbug.com/591099 fast/multicol/break-in-scrollable.html [ Failure ]
crbug.com/591099 fast/multicol/break-properties.html [ Failure ]
crbug.com/591099 fast/multicol/caret-range-anonymous-block-rtl.html [ Failure ] crbug.com/591099 fast/multicol/caret-range-anonymous-block-rtl.html [ Failure ]
crbug.com/591099 fast/multicol/caret-range-anonymous-block.html [ Failure ] crbug.com/591099 fast/multicol/caret-range-anonymous-block.html [ Failure ]
crbug.com/591099 fast/multicol/caret-range-outside-columns-rtl.html [ Failure ] crbug.com/591099 fast/multicol/caret-range-outside-columns-rtl.html [ Failure ]
...@@ -5643,7 +5643,6 @@ crbug.com/591099 fast/multicol/client-rects-crossing-boundaries.html [ Failure ] ...@@ -5643,7 +5643,6 @@ crbug.com/591099 fast/multicol/client-rects-crossing-boundaries.html [ Failure ]
crbug.com/591099 fast/multicol/client-rects-rtl.html [ Failure ] crbug.com/591099 fast/multicol/client-rects-rtl.html [ Failure ]
crbug.com/591099 fast/multicol/client-rects.html [ Failure ] crbug.com/591099 fast/multicol/client-rects.html [ Failure ]
crbug.com/591099 fast/multicol/clone-block-children-inline-mismatch-crash.html [ Crash ] crbug.com/591099 fast/multicol/clone-block-children-inline-mismatch-crash.html [ Crash ]
crbug.com/591099 fast/multicol/column-break-with-balancing.html [ Failure ]
crbug.com/591099 fast/multicol/column-count-with-rules.html [ Failure ] crbug.com/591099 fast/multicol/column-count-with-rules.html [ Failure ]
crbug.com/591099 fast/multicol/column-rules.html [ Failure ] crbug.com/591099 fast/multicol/column-rules.html [ Failure ]
crbug.com/591099 fast/multicol/columns-shorthand-parsing.html [ Failure ] crbug.com/591099 fast/multicol/columns-shorthand-parsing.html [ Failure ]
...@@ -5842,7 +5841,6 @@ crbug.com/714962 fast/multicol/newmulticol/regular-block-becomes-multicol.html [ ...@@ -5842,7 +5841,6 @@ crbug.com/714962 fast/multicol/newmulticol/regular-block-becomes-multicol.html [
crbug.com/591099 fast/multicol/newmulticol/spanner-inside-child-crash.html [ Crash ] crbug.com/591099 fast/multicol/newmulticol/spanner-inside-child-crash.html [ Crash ]
crbug.com/591099 fast/multicol/newmulticol/table-cell.html [ Failure ] crbug.com/591099 fast/multicol/newmulticol/table-cell.html [ Failure ]
crbug.com/591099 fast/multicol/newmulticol/unresolvable-percent-max-height-2.html [ Failure ] crbug.com/591099 fast/multicol/newmulticol/unresolvable-percent-max-height-2.html [ Failure ]
crbug.com/591099 fast/multicol/one-column-with-break.html [ Failure ]
crbug.com/591099 fast/multicol/orphaned-line-at-exact-top-of-column.html [ Failure ] crbug.com/591099 fast/multicol/orphaned-line-at-exact-top-of-column.html [ Failure ]
crbug.com/591099 fast/multicol/orphans-relayout.html [ Failure ] crbug.com/591099 fast/multicol/orphans-relayout.html [ Failure ]
crbug.com/591099 fast/multicol/out-of-flow/abspos-auto-left-right.html [ Pass ] crbug.com/591099 fast/multicol/out-of-flow/abspos-auto-left-right.html [ Pass ]
...@@ -6838,10 +6836,10 @@ crbug.com/591099 fragmentation/abspos-after-forced-break.html [ Failure ] ...@@ -6838,10 +6836,10 @@ crbug.com/591099 fragmentation/abspos-after-forced-break.html [ Failure ]
crbug.com/591099 fragmentation/auto-scrollbar-shrink-to-fit.html [ Failure ] crbug.com/591099 fragmentation/auto-scrollbar-shrink-to-fit.html [ Failure ]
crbug.com/591099 fragmentation/avoid-break-inside-first-child-nested.html [ Failure ] crbug.com/591099 fragmentation/avoid-break-inside-first-child-nested.html [ Failure ]
crbug.com/591099 fragmentation/avoid-break-inside-first-child.html [ Failure ] crbug.com/591099 fragmentation/avoid-break-inside-first-child.html [ Failure ]
crbug.com/591099 fragmentation/become-fragmented-same-widths.html [ Failure ]
crbug.com/591099 fragmentation/block-after-float-first-child.html [ Failure ] crbug.com/591099 fragmentation/block-after-float-first-child.html [ Failure ]
crbug.com/591099 fragmentation/block-with-float-and-1-orphaned-line.html [ Failure ] crbug.com/591099 fragmentation/block-with-float-and-1-orphaned-line.html [ Failure ]
crbug.com/591099 fragmentation/border-spacing-break-before-unbreakable-row.html [ Failure ] crbug.com/591099 fragmentation/border-spacing-break-before-unbreakable-row.html [ Failure ]
crbug.com/591099 fragmentation/break-before-first-child.html [ Failure ]
crbug.com/591099 fragmentation/break-in-first-table-row-only.html [ Failure ] crbug.com/591099 fragmentation/break-in-first-table-row-only.html [ Failure ]
crbug.com/591099 fragmentation/break-in-second-table-section.html [ Failure ] crbug.com/591099 fragmentation/break-in-second-table-section.html [ Failure ]
crbug.com/591099 fragmentation/break-in-tbody-after-caption.html [ Failure ] crbug.com/591099 fragmentation/break-in-tbody-after-caption.html [ Failure ]
......
...@@ -141,7 +141,8 @@ scoped_refptr<NGLayoutResult> NGLineBoxFragmentBuilder::ToLineBoxFragment() { ...@@ -141,7 +141,8 @@ scoped_refptr<NGLayoutResult> NGLineBoxFragmentBuilder::ToLineBoxFragment() {
std::move(fragment), oof_positioned_descendants_, positioned_floats_, std::move(fragment), oof_positioned_descendants_, positioned_floats_,
unpositioned_floats_, std::move(exclusion_space_), bfc_offset_, unpositioned_floats_, std::move(exclusion_space_), bfc_offset_,
end_margin_strut_, end_margin_strut_,
/* intrinsic_block_size */ LayoutUnit(), NGLayoutResult::kSuccess)); /* intrinsic_block_size */ LayoutUnit(), EBreakBetween::kAuto,
EBreakBetween::kAuto, NGLayoutResult::kSuccess));
} }
} // namespace blink } // namespace blink
...@@ -369,6 +369,13 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { ...@@ -369,6 +369,13 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() {
HandleFloat(previous_inflow_position, ToNGBlockNode(child), HandleFloat(previous_inflow_position, ToNGBlockNode(child),
ToNGBlockBreakToken(child_break_token)); ToNGBlockBreakToken(child_break_token));
} else { } else {
// We need to propagate the initial break-before value up our container
// chain, until we reach a container that's not a first child. If we get
// all the way to the root of the fragmentation context without finding
// any such container, we have no valid class A break point, and if a
// forced break was requested, none will be inserted.
container_builder_.SetInitialBreakBefore(child.Style().BreakBefore());
bool success = bool success =
child.CreatesNewFormattingContext() child.CreatesNewFormattingContext()
? HandleNewFormattingContext(child, child_break_token, ? HandleNewFormattingContext(child, child_break_token,
...@@ -663,8 +670,11 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext( ...@@ -663,8 +670,11 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
container_builder_.Size().inline_size, ConstraintSpace().Direction()); container_builder_.Size().inline_size, ConstraintSpace().Direction());
if (ConstraintSpace().HasBlockFragmentation() && if (ConstraintSpace().HasBlockFragmentation() &&
BreakBeforeChild(child, physical_fragment, logical_offset.block_offset)) BreakBeforeChild(child, *layout_result, logical_offset.block_offset))
return true; return true;
EBreakBetween break_after = JoinFragmentainerBreakValues(
layout_result->FinalBreakAfter(), child.Style().BreakAfter());
container_builder_.SetPreviousBreakAfter(break_after);
intrinsic_block_size_ = intrinsic_block_size_ =
std::max(intrinsic_block_size_, std::max(intrinsic_block_size_,
...@@ -990,8 +1000,11 @@ bool NGBlockLayoutAlgorithm::HandleInflow( ...@@ -990,8 +1000,11 @@ bool NGBlockLayoutAlgorithm::HandleInflow(
CalculateLogicalOffset(fragment, child_data.margins, child_bfc_offset); CalculateLogicalOffset(fragment, child_data.margins, child_bfc_offset);
if (ConstraintSpace().HasBlockFragmentation() && if (ConstraintSpace().HasBlockFragmentation() &&
BreakBeforeChild(child, physical_fragment, logical_offset.block_offset)) BreakBeforeChild(child, *layout_result, logical_offset.block_offset))
return true; return true;
EBreakBetween break_after = JoinFragmentainerBreakValues(
layout_result->FinalBreakAfter(), child.Style().BreakAfter());
container_builder_.SetPreviousBreakAfter(break_after);
// Only modify intrinsic_block_size_ if the fragment is non-empty block. // Only modify intrinsic_block_size_ if the fragment is non-empty block.
// //
...@@ -1230,10 +1243,10 @@ void NGBlockLayoutAlgorithm::FinalizeForFragmentation() { ...@@ -1230,10 +1243,10 @@ void NGBlockLayoutAlgorithm::FinalizeForFragmentation() {
bool NGBlockLayoutAlgorithm::BreakBeforeChild( bool NGBlockLayoutAlgorithm::BreakBeforeChild(
NGLayoutInputNode child, NGLayoutInputNode child,
const NGPhysicalFragment& physical_fragment, const NGLayoutResult& layout_result,
LayoutUnit block_offset) { LayoutUnit block_offset) {
DCHECK(ConstraintSpace().HasBlockFragmentation()); DCHECK(ConstraintSpace().HasBlockFragmentation());
if (!ShouldBreakBeforeChild(child, physical_fragment, block_offset)) if (!ShouldBreakBeforeChild(child, layout_result, block_offset))
return false; return false;
// The remaining part of the fragmentainer (the unusable space for child // The remaining part of the fragmentainer (the unusable space for child
...@@ -1250,11 +1263,14 @@ bool NGBlockLayoutAlgorithm::BreakBeforeChild( ...@@ -1250,11 +1263,14 @@ bool NGBlockLayoutAlgorithm::BreakBeforeChild(
bool NGBlockLayoutAlgorithm::ShouldBreakBeforeChild( bool NGBlockLayoutAlgorithm::ShouldBreakBeforeChild(
NGLayoutInputNode child, NGLayoutInputNode child,
const NGPhysicalFragment& physical_fragment, const NGLayoutResult& layout_result,
LayoutUnit block_offset) const { LayoutUnit block_offset) const {
if (!container_builder_.BfcOffset().has_value()) if (!container_builder_.BfcOffset().has_value())
return false; return false;
const NGPhysicalFragment& physical_fragment =
*layout_result.PhysicalFragment();
// If we haven't used any space at all in the fragmentainer yet, we cannot // If we haven't used any space at all in the fragmentainer yet, we cannot
// break, or there'd be no progress. We'd end up creating an infinite number // break, or there'd be no progress. We'd end up creating an infinite number
// of fragmentainers without putting any content into them. // of fragmentainers without putting any content into them.
...@@ -1274,6 +1290,17 @@ bool NGBlockLayoutAlgorithm::ShouldBreakBeforeChild( ...@@ -1274,6 +1290,17 @@ bool NGBlockLayoutAlgorithm::ShouldBreakBeforeChild(
if (space_left <= LayoutUnit()) if (space_left <= LayoutUnit())
return true; return true;
EBreakBetween break_before = JoinFragmentainerBreakValues(
child.Style().BreakBefore(), layout_result.InitialBreakBefore());
EBreakBetween break_between =
container_builder_.JoinedBreakBetweenValue(break_before);
if (IsForcedBreakValue(ConstraintSpace(), break_between)) {
// There should be a forced break before this child, and if we're not at the
// first in-flow child, just go ahead and break.
if (has_processed_first_child_)
return true;
}
const auto* token = physical_fragment.BreakToken(); const auto* token = physical_fragment.BreakToken();
if (!token || token->IsFinished()) if (!token || token->IsFinished())
return false; return false;
......
...@@ -156,13 +156,13 @@ class CORE_EXPORT NGBlockLayoutAlgorithm ...@@ -156,13 +156,13 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
// Insert a fragmentainer break before the child if necessary. // Insert a fragmentainer break before the child if necessary.
// Return true if a break was inserted, false otherwise. // Return true if a break was inserted, false otherwise.
bool BreakBeforeChild(NGLayoutInputNode child, bool BreakBeforeChild(NGLayoutInputNode child,
const NGPhysicalFragment&, const NGLayoutResult&,
LayoutUnit block_offset); LayoutUnit block_offset);
// Given a child fragment and the corresponding node's style, return true if // Given a child fragment and the corresponding node's style, return true if
// we need to insert a fragmentainer break in front of it. // we need to insert a fragmentainer break in front of it.
bool ShouldBreakBeforeChild(NGLayoutInputNode child, bool ShouldBreakBeforeChild(NGLayoutInputNode child,
const NGPhysicalFragment& physical_fragment, const NGLayoutResult&,
LayoutUnit block_offset) const; LayoutUnit block_offset) const;
// Final adjustments before fragment creation. We need to prevent the // Final adjustments before fragment creation. We need to prevent the
......
...@@ -1454,6 +1454,59 @@ TEST_F(NGColumnLayoutAlgorithmTest, BreakInsideWithBorder) { ...@@ -1454,6 +1454,59 @@ TEST_F(NGColumnLayoutAlgorithmTest, BreakInsideWithBorder) {
EXPECT_EQ(expectation, dump); EXPECT_EQ(expectation, dump);
} }
TEST_F(NGColumnLayoutAlgorithmTest, ForcedBreaks) {
// This tests that forced breaks are honored, but only at valid class A break
// points (i.e. *between* in-flow block siblings).
SetBodyInnerHTML(R"HTML(
<style>
#parent {
columns: 3;
column-fill: auto;
column-gap: 10px;
width: 320px;
height: 100px;
}
</style>
<div id="container">
<div id="parent">
<div style="float:left; width:1px; height:1px;"></div>
<div style="break-before:column; break-after:column;">
<div style="float:left; width:1px; height:1px;"></div>
<div style="break-after:column; width:50px; height:10px;"></div>
<div style="break-before:column; width:60px; height:10px;"></div>
<div>
<div>
<div style="break-after:column; width:70px; height:10px;"></div>
</div>
</div>
<div style="width:80px; height:10px;"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x100
offset:0,0 size:320x100
offset:0,0 size:100x100
offset:0,0 size:1x1
offset:0,0 size:100x100
offset:1,0 size:1x1
offset:0,0 size:50x10
offset:110,0 size:100x100
offset:0,0 size:100x100
offset:0,0 size:60x10
offset:0,10 size:100x10
offset:0,0 size:100x10
offset:0,0 size:70x10
offset:220,0 size:100x10
offset:0,0 size:100x10
offset:0,0 size:80x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGColumnLayoutAlgorithmTest, MinMax) { TEST_F(NGColumnLayoutAlgorithmTest, MinMax) {
// The multicol container here contains two inline-blocks with a line break // The multicol container here contains two inline-blocks with a line break
// opportunity between them. We'll test what min/max values we get for the // opportunity between them. We'll test what min/max values we get for the
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "core/layout/ng/ng_break_token.h" #include "core/layout/ng/ng_break_token.h"
#include "core/layout/ng/ng_exclusion_space.h" #include "core/layout/ng/ng_exclusion_space.h"
#include "core/layout/ng/ng_fragment.h" #include "core/layout/ng/ng_fragment.h"
#include "core/layout/ng/ng_fragmentation_utils.h"
#include "core/layout/ng/ng_layout_result.h" #include "core/layout/ng/ng_layout_result.h"
#include "core/layout/ng/ng_physical_box_fragment.h" #include "core/layout/ng/ng_physical_box_fragment.h"
#include "core/layout/ng/ng_positioned_float.h" #include "core/layout/ng/ng_positioned_float.h"
...@@ -201,6 +202,11 @@ void NGFragmentBuilder::AddBaseline(NGBaselineRequest request, ...@@ -201,6 +202,11 @@ void NGFragmentBuilder::AddBaseline(NGBaselineRequest request,
baselines_.push_back(NGBaseline{request, offset}); baselines_.push_back(NGBaseline{request, offset});
} }
EBreakBetween NGFragmentBuilder::JoinedBreakBetweenValue(
EBreakBetween break_before) const {
return JoinFragmentainerBreakValues(previous_break_after_, break_before);
}
scoped_refptr<NGLayoutResult> NGFragmentBuilder::ToBoxFragment() { scoped_refptr<NGLayoutResult> NGFragmentBuilder::ToBoxFragment() {
DCHECK_EQ(offsets_.size(), children_.size()); DCHECK_EQ(offsets_.size(), children_.size());
...@@ -240,17 +246,18 @@ scoped_refptr<NGLayoutResult> NGFragmentBuilder::ToBoxFragment() { ...@@ -240,17 +246,18 @@ scoped_refptr<NGLayoutResult> NGFragmentBuilder::ToBoxFragment() {
return base::AdoptRef(new NGLayoutResult( return base::AdoptRef(new NGLayoutResult(
std::move(fragment), oof_positioned_descendants_, positioned_floats, std::move(fragment), oof_positioned_descendants_, positioned_floats,
unpositioned_floats_, std::move(exclusion_space_), bfc_offset_, unpositioned_floats_, std::move(exclusion_space_), bfc_offset_,
end_margin_strut_, intrinsic_block_size_, NGLayoutResult::kSuccess)); end_margin_strut_, intrinsic_block_size_, initial_break_before_,
previous_break_after_, NGLayoutResult::kSuccess));
} }
scoped_refptr<NGLayoutResult> NGFragmentBuilder::Abort( scoped_refptr<NGLayoutResult> NGFragmentBuilder::Abort(
NGLayoutResult::NGLayoutResultStatus status) { NGLayoutResult::NGLayoutResultStatus status) {
Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants; Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants;
Vector<NGPositionedFloat> positioned_floats; Vector<NGPositionedFloat> positioned_floats;
return base::AdoptRef( return base::AdoptRef(new NGLayoutResult(
new NGLayoutResult(nullptr, oof_positioned_descendants, positioned_floats, nullptr, oof_positioned_descendants, positioned_floats,
unpositioned_floats_, nullptr, bfc_offset_, unpositioned_floats_, nullptr, bfc_offset_, end_margin_strut_,
end_margin_strut_, LayoutUnit(), status)); LayoutUnit(), EBreakBetween::kAuto, EBreakBetween::kAuto, status));
} }
// Finds FragmentPairs that define inline containing blocks. // Finds FragmentPairs that define inline containing blocks.
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "core/layout/ng/ng_container_fragment_builder.h" #include "core/layout/ng/ng_container_fragment_builder.h"
#include "core/layout/ng/ng_layout_result.h" #include "core/layout/ng/ng_layout_result.h"
#include "core/layout/ng/ng_out_of_flow_positioned_descendant.h" #include "core/layout/ng/ng_out_of_flow_positioned_descendant.h"
#include "core/style/ComputedStyleConstants.h"
#include "platform/heap/Handle.h" #include "platform/heap/Handle.h"
#include "platform/wtf/Allocator.h" #include "platform/wtf/Allocator.h"
namespace blink { namespace blink {
...@@ -75,6 +76,19 @@ class CORE_EXPORT NGFragmentBuilder final : public NGContainerFragmentBuilder { ...@@ -75,6 +76,19 @@ class CORE_EXPORT NGFragmentBuilder final : public NGContainerFragmentBuilder {
return *this; return *this;
} }
void SetInitialBreakBefore(EBreakBetween break_before) {
initial_break_before_ = break_before;
}
void SetPreviousBreakAfter(EBreakBetween break_after) {
previous_break_after_ = break_after;
}
// Join/"collapse" the previous (stored) break-after value with the next
// break-before value, to determine how to deal with breaking between two
// in-flow siblings.
EBreakBetween JoinedBreakBetweenValue(EBreakBetween break_before) const;
// Offsets are not supposed to be set during fragment construction, so we // Offsets are not supposed to be set during fragment construction, so we
// do not provide a setter here. // do not provide a setter here.
...@@ -144,6 +158,13 @@ class CORE_EXPORT NGFragmentBuilder final : public NGContainerFragmentBuilder { ...@@ -144,6 +158,13 @@ class CORE_EXPORT NGFragmentBuilder final : public NGContainerFragmentBuilder {
bool did_break_; bool did_break_;
LayoutUnit used_block_size_; LayoutUnit used_block_size_;
// The break-before value on the initial child we cannot honor. There's no
// valid class A break point before a first child, only *between* siblings.
EBreakBetween initial_break_before_ = EBreakBetween::kAuto;
// The break-after value of the previous in-flow sibling.
EBreakBetween previous_break_after_ = EBreakBetween::kAuto;
Vector<scoped_refptr<NGBreakToken>> child_break_tokens_; Vector<scoped_refptr<NGBreakToken>> child_break_tokens_;
scoped_refptr<NGBreakToken> last_inline_break_token_; scoped_refptr<NGBreakToken> last_inline_break_token_;
......
...@@ -43,4 +43,64 @@ bool IsLastFragment(const NGPhysicalFragment& fragment) { ...@@ -43,4 +43,64 @@ bool IsLastFragment(const NGPhysicalFragment& fragment) {
return !break_token || break_token->IsFinished(); return !break_token || break_token->IsFinished();
} }
// At a class A break point [1], the break value with the highest precedence
// wins. If the two values have the same precedence (e.g. "left" and "right"),
// the value specified on a latter object wins.
//
// [1] https://drafts.csswg.org/css-break/#possible-breaks
inline int FragmentainerBreakPrecedence(EBreakBetween break_value) {
// "auto" has the lowest priority.
// "avoid*" values win over "auto".
// "avoid-page" wins over "avoid-column".
// "avoid" wins over "avoid-page".
// Forced break values win over "avoid".
// Any forced page break value wins over "column" forced break.
// More specific break values (left, right, recto, verso) wins over generic
// "page" values.
switch (break_value) {
default:
NOTREACHED();
// fall-through
case EBreakBetween::kAuto:
return 0;
case EBreakBetween::kAvoidColumn:
return 1;
case EBreakBetween::kAvoidPage:
return 2;
case EBreakBetween::kAvoid:
return 3;
case EBreakBetween::kColumn:
return 4;
case EBreakBetween::kPage:
return 5;
case EBreakBetween::kLeft:
case EBreakBetween::kRight:
case EBreakBetween::kRecto:
case EBreakBetween::kVerso:
return 6;
}
}
EBreakBetween JoinFragmentainerBreakValues(EBreakBetween first_value,
EBreakBetween second_value) {
if (FragmentainerBreakPrecedence(second_value) >=
FragmentainerBreakPrecedence(first_value))
return second_value;
return first_value;
}
bool IsForcedBreakValue(const NGConstraintSpace& constraint_space,
EBreakBetween break_value) {
if (break_value == EBreakBetween::kColumn)
return constraint_space.BlockFragmentationType() == kFragmentColumn;
if (break_value == EBreakBetween::kLeft ||
break_value == EBreakBetween::kPage ||
break_value == EBreakBetween::kRecto ||
break_value == EBreakBetween::kRight ||
break_value == EBreakBetween::kVerso)
return constraint_space.BlockFragmentationType() == kFragmentPage;
return false;
}
} // namespace blink } // namespace blink
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef NGFragmentationUtils_h #ifndef NGFragmentationUtils_h
#define NGFragmentationUtils_h #define NGFragmentationUtils_h
#include "core/style/ComputedStyleConstants.h"
#include "platform/LayoutUnit.h" #include "platform/LayoutUnit.h"
namespace blink { namespace blink {
...@@ -24,6 +25,23 @@ bool IsFirstFragment(const NGConstraintSpace&, const NGPhysicalFragment&); ...@@ -24,6 +25,23 @@ bool IsFirstFragment(const NGConstraintSpace&, const NGPhysicalFragment&);
// Return true if the specified fragment is the final fragment of some node. // Return true if the specified fragment is the final fragment of some node.
bool IsLastFragment(const NGPhysicalFragment&); bool IsLastFragment(const NGPhysicalFragment&);
// 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
// than |second_value|. This method is used at class A break points [1], to join
// the values of the previous break-after and the next break-before, to figure
// out whether we may, must, or should not break at that point. It is also used
// when propagating break-before values from first children and break-after
// values on last children to their container.
//
// [1] https://drafts.csswg.org/css-break/#possible-breaks
EBreakBetween JoinFragmentainerBreakValues(EBreakBetween first_value,
EBreakBetween second_value);
// Return true if the specified break value has a forced break effect in the
// current fragmentation context.
bool IsForcedBreakValue(const NGConstraintSpace&, EBreakBetween);
} // namespace blink } // namespace blink
#endif // NGFragmentationUtils_h #endif // NGFragmentationUtils_h
...@@ -20,12 +20,16 @@ NGLayoutResult::NGLayoutResult( ...@@ -20,12 +20,16 @@ NGLayoutResult::NGLayoutResult(
const WTF::Optional<NGBfcOffset> bfc_offset, const WTF::Optional<NGBfcOffset> bfc_offset,
const NGMarginStrut end_margin_strut, const NGMarginStrut end_margin_strut,
const LayoutUnit intrinsic_block_size, const LayoutUnit intrinsic_block_size,
EBreakBetween initial_break_before,
EBreakBetween final_break_after,
NGLayoutResultStatus status) NGLayoutResultStatus status)
: physical_fragment_(std::move(physical_fragment)), : physical_fragment_(std::move(physical_fragment)),
exclusion_space_(std::move(exclusion_space)), exclusion_space_(std::move(exclusion_space)),
bfc_offset_(bfc_offset), bfc_offset_(bfc_offset),
end_margin_strut_(end_margin_strut), end_margin_strut_(end_margin_strut),
intrinsic_block_size_(intrinsic_block_size), intrinsic_block_size_(intrinsic_block_size),
initial_break_before_(initial_break_before),
final_break_after_(final_break_after),
status_(status) { status_(status) {
oof_positioned_descendants_.swap(oof_positioned_descendants); oof_positioned_descendants_.swap(oof_positioned_descendants);
positioned_floats_.swap(positioned_floats); positioned_floats_.swap(positioned_floats);
...@@ -47,7 +51,8 @@ scoped_refptr<NGLayoutResult> NGLayoutResult::CloneWithoutOffset() const { ...@@ -47,7 +51,8 @@ scoped_refptr<NGLayoutResult> NGLayoutResult::CloneWithoutOffset() const {
return base::AdoptRef(new NGLayoutResult( return base::AdoptRef(new NGLayoutResult(
physical_fragment_->CloneWithoutOffset(), oof_positioned_descendants, physical_fragment_->CloneWithoutOffset(), oof_positioned_descendants,
positioned_floats, unpositioned_floats, std::move(exclusion_space), positioned_floats, unpositioned_floats, std::move(exclusion_space),
bfc_offset_, end_margin_strut_, intrinsic_block_size_, Status())); bfc_offset_, end_margin_strut_, intrinsic_block_size_,
initial_break_before_, final_break_after_, Status()));
} }
} // namespace blink } // namespace blink
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "core/layout/ng/geometry/ng_margin_strut.h" #include "core/layout/ng/geometry/ng_margin_strut.h"
#include "core/layout/ng/ng_out_of_flow_positioned_descendant.h" #include "core/layout/ng/ng_out_of_flow_positioned_descendant.h"
#include "core/layout/ng/ng_physical_fragment.h" #include "core/layout/ng/ng_physical_fragment.h"
#include "core/style/ComputedStyleConstants.h"
#include "platform/wtf/Vector.h" #include "platform/wtf/Vector.h"
namespace blink { namespace blink {
...@@ -86,6 +87,14 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { ...@@ -86,6 +87,14 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
return intrinsic_block_size_; return intrinsic_block_size_;
} }
// The break-before value on the first child needs to be propagated to the
// container, in search of a valid class A break point.
EBreakBetween InitialBreakBefore() const { return initial_break_before_; }
// The break-after value on the last child needs to be propagated to the
// container, in search of a valid class A break point.
EBreakBetween FinalBreakAfter() const { return final_break_after_; }
scoped_refptr<NGLayoutResult> CloneWithoutOffset() const; scoped_refptr<NGLayoutResult> CloneWithoutOffset() const;
private: private:
...@@ -102,6 +111,8 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { ...@@ -102,6 +111,8 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
const WTF::Optional<NGBfcOffset> bfc_offset, const WTF::Optional<NGBfcOffset> bfc_offset,
const NGMarginStrut end_margin_strut, const NGMarginStrut end_margin_strut,
const LayoutUnit intrinsic_block_size, const LayoutUnit intrinsic_block_size,
EBreakBetween initial_break_before,
EBreakBetween final_break_after,
NGLayoutResultStatus status); NGLayoutResultStatus status);
scoped_refptr<NGPhysicalFragment> physical_fragment_; scoped_refptr<NGPhysicalFragment> physical_fragment_;
...@@ -115,6 +126,9 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { ...@@ -115,6 +126,9 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
const NGMarginStrut end_margin_strut_; const NGMarginStrut end_margin_strut_;
const LayoutUnit intrinsic_block_size_; const LayoutUnit intrinsic_block_size_;
EBreakBetween initial_break_before_;
EBreakBetween final_break_after_;
unsigned status_ : 1; unsigned status_ : 1;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment