blob: 7418bb509f689721e70d78cd25483591e9894b79 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
namespace blink {
namespace {
class NGOutOfFlowLayoutPartTest
: public NGBaseLayoutAlgorithmTest,
private ScopedLayoutNGBlockFragmentationForTest {
protected:
NGOutOfFlowLayoutPartTest() : ScopedLayoutNGBlockFragmentationForTest(true) {}
scoped_refptr<const NGPhysicalBoxFragment> RunBlockLayoutAlgorithm(
Element* element) {
NGBlockNode container(element->GetLayoutBox());
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
{WritingMode::kHorizontalTb, TextDirection::kLtr},
LogicalSize(LayoutUnit(1000), kIndefiniteSize));
return NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(container, space);
}
String DumpFragmentTree(Element* element) {
auto fragment = RunBlockLayoutAlgorithm(element);
return DumpFragmentTree(fragment.get());
}
String DumpFragmentTree(const blink::NGPhysicalBoxFragment* fragment) {
NGPhysicalFragment::DumpFlags flags =
NGPhysicalFragment::DumpHeaderText | NGPhysicalFragment::DumpSubtree |
NGPhysicalFragment::DumpIndentation | NGPhysicalFragment::DumpOffset |
NGPhysicalFragment::DumpSize;
return fragment->DumpFragmentTree(flags);
}
};
// Fixed blocks inside absolute blocks trigger otherwise unused while loop
// inside NGOutOfFlowLayoutPart::Run.
// This test exercises this loop by placing two fixed elements inside abs.
TEST_F(NGOutOfFlowLayoutPartTest, FixedInsideAbs) {
SetBodyInnerHTML(
R"HTML(
<style>
body{ padding:0px; margin:0px}
#rel { position:relative }
#abs {
position: absolute;
top:49px;
left:0px;
}
#pad {
width:100px;
height:50px;
}
#fixed1 {
position:fixed;
width:50px;
}
#fixed2 {
position:fixed;
top:9px;
left:7px;
}
</style>
<div id='rel'>
<div id='abs'>
<div id='pad'></div>
<div id='fixed1'>
<p>fixed static</p>
</div>
<div id='fixed2'>
<p>fixed plain</p>
</div>
</div>
</div>
)HTML");
// Test whether the oof fragments have been collected at NG->Legacy boundary.
Element* rel = GetDocument().getElementById("rel");
auto* block_flow = To<LayoutBlockFlow>(rel->GetLayoutObject());
scoped_refptr<const NGLayoutResult> result =
block_flow->GetCachedLayoutResult();
EXPECT_TRUE(result);
EXPECT_EQ(result->PhysicalFragment().OutOfFlowPositionedDescendants().size(),
2u);
// Test the final result.
Element* fixed_1 = GetDocument().getElementById("fixed1");
Element* fixed_2 = GetDocument().getElementById("fixed2");
// fixed1 top is static: #abs.top + #pad.height
EXPECT_EQ(fixed_1->OffsetTop(), LayoutUnit(99));
// fixed2 top is positioned: #fixed2.top
EXPECT_EQ(fixed_2->OffsetTop(), LayoutUnit(9));
}
// Tests non-fragmented positioned nodes inside a multi-column.
TEST_F(NGOutOfFlowLayoutPartTest, PositionedInMulticol) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count: 2; height: 40px; column-fill: auto; column-gap: 16px;
}
.rel {
position: relative;
}
.abs {
position: absolute;
}
</style>
<div id="container">
<div id="multicol">
<div style="width:100px; height:50px;"></div>
<div class="rel" style="width:30px;">
<div class="abs" style="width:5px; top:10px; height:5px;">
</div>
<div class="rel" style="width:35px; padding-top:8px;">
<div class="abs" style="width:10px; top:20px; height:10px;">
</div>
</div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:100x40
offset:508,0 size:492x40
offset:0,0 size:100x10
offset:0,10 size:30x8
offset:0,0 size:35x8
offset:0,30 size:10x10
offset:0,20 size:5x5
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that positioned nodes fragment correctly.
TEST_F(NGOutOfFlowLayoutPartTest, SimplePositionedFragmentation) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; top:0px; width:5px; height:50px;
border:solid 2px; margin-top:5px; padding:5px;
}
</style>
<div id="container">
<div id="multicol">
<div style="width:100px; height:50px;"></div>
<div class="rel">
<div class="abs"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:100x40
offset:508,0 size:492x40
offset:0,0 size:100x10
offset:0,10 size:30x0
offset:0,15 size:19x25
offset:1016,0 size:492x40
offset:0,0 size:19x39
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests fragmentation when a positioned node's child overflows.
TEST_F(NGOutOfFlowLayoutPartTest, PositionedFragmentationWithOverflow) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; top:10px; width:5px; height:10px;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div class="abs">
<div style="width:100px; height:50px;"></div>
</div>
</div>
<div style="width:20px; height:100px;"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:30x0
offset:0,0 size:20x40
offset:0,10 size:5x10
offset:0,0 size:100x30
offset:508,0 size:492x40
offset:0,0 size:20x40
offset:0,0 size:5x0
offset:0,0 size:100x20
offset:1016,0 size:492x40
offset:0,0 size:20x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that new column fragments are added correctly if a positioned node
// fragments beyond the last fragmentainer in a context.
TEST_F(NGOutOfFlowLayoutPartTest, PositionedFragmentationWithNewColumns) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; width:5px; height:120px;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div class="abs"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:30x0
offset:0,0 size:5x40
offset:508,0 size:492x40
offset:0,0 size:5x40
offset:1016,0 size:492x40
offset:0,0 size:5x40
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that empty column fragments are added if an OOF element begins layout
// in a fragmentainer that is more than one index beyond the last existing
// column fragmentainer.
TEST_F(NGOutOfFlowLayoutPartTest, PositionedFragmentationWithNewEmptyColumns) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; top:80px; width:5px; height:120px;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div class="abs"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:30x0
offset:508,0 size:492x40
offset:1016,0 size:492x40
offset:0,0 size:5x40
offset:1524,0 size:492x40
offset:0,0 size:5x40
offset:2032,0 size:492x40
offset:0,0 size:5x40
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Break-inside does not apply to absolute positioned elements.
TEST_F(NGOutOfFlowLayoutPartTest, BreakInsideAvoid) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position:relative;
}
.abs {
position:absolute; break-inside:avoid;
}
</style>
<div id="container">
<div id="multicol">
<div style="width:20px; height:10px;"></div>
<div class="rel" style="width:30px;">
<div class="abs" style="width:40px; height:40px;"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:20x10
offset:0,10 size:30x0
offset:0,10 size:40x30
offset:508,0 size:492x40
offset:0,0 size:40x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Break-before does not apply to absolute positioned elements.
TEST_F(NGOutOfFlowLayoutPartTest, BreakBeforeColumn) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative;
}
.abs {
position:absolute; break-before:column;
}
</style>
<div id="container">
<div id="multicol">
<div style="width:10px; height:30px;"></div>
<div class="rel" style="width:30px;">
<div class="abs" style="width:40px; height:30px;"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:10x30
offset:0,30 size:30x0
offset:0,30 size:40x10
offset:508,0 size:492x40
offset:0,0 size:40x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Break-after does not apply to absolute positioned elements.
TEST_F(NGOutOfFlowLayoutPartTest, BreakAfterColumn) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative;
}
.abs {
position:absolute; break-after:column;
}
</style>
<div id="container">
<div id="multicol">
<div style="width:10px; height:20px;"></div>
<div class="rel" style="width:30px; height:10px;">
<div class="abs" style="width:40px; height:10px;"></div>
</div>
<div style="width:20px; height:10px;"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:10x20
offset:0,20 size:30x10
offset:0,30 size:20x10
offset:0,20 size:40x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Break-inside should still apply to children of absolute positioned elements.
TEST_F(NGOutOfFlowLayoutPartTest, ChildBreakInsideAvoid) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:100px;
}
.rel {
position: relative;
}
.abs {
position:absolute;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel" style="width:30px;">
<div class="abs" style="width:40px; height:150px;">
<div style="width:15px; height:50px;"></div>
<div style="break-inside:avoid; width:20px; height:100px;"></div>
</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:1000x100
offset:0,0 size:492x100
offset:0,0 size:30x0
offset:0,0 size:40x100
offset:0,0 size:15x50
offset:508,0 size:492x100
offset:0,0 size:40x50
offset:0,0 size:20x100
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Break-before should still apply to children of absolute positioned elements.
TEST_F(NGOutOfFlowLayoutPartTest, ChildBreakBeforeAvoid) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:100px;
}
.rel {
position: relative;
}
.abs {
position:absolute;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel" style="width:30px;">
<div class="abs" style="width:40px; height:150px;">
<div style="width:15px; height:50px;"></div>
<div style="width:20px; height:50px;"></div>
<div style="break-before:avoid; width:10px; height:20px;"></div>
</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:1000x100
offset:0,0 size:492x100
offset:0,0 size:30x0
offset:0,0 size:40x100
offset:0,0 size:15x50
offset:508,0 size:492x100
offset:0,0 size:40x50
offset:0,0 size:20x50
offset:0,50 size:10x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Break-after should still apply to children of absolute positioned elements.
TEST_F(NGOutOfFlowLayoutPartTest, ChildBreakAfterAvoid) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:100px;
}
.rel {
position: relative;
}
.abs {
position:absolute;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel" style="width:30px;">
<div class="abs" style="width:40px; height:150px;">
<div style="width:15px; height:50px;"></div>
<div style="break-after:avoid; width:20px; height:50px;"></div>
<div style="width:10px; height:20px;"></div>
</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:1000x100
offset:0,0 size:492x100
offset:0,0 size:30x0
offset:0,0 size:40x100
offset:0,0 size:15x50
offset:508,0 size:492x100
offset:0,0 size:40x50
offset:0,0 size:20x50
offset:0,50 size:10x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that a positioned element with a negative top property moves the OOF
// node to the previous fragmentainer and spans 3 columns.
TEST_F(NGOutOfFlowLayoutPartTest,
PositionedFragmentationWithNegativeTopPropertyAndNewEmptyColumn) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; top:-40px; width:5px; height:80px;
}
</style>
<div id="container">
<div id="multicol">
<div style="height: 60px; width: 32px;"></div>
<div class="rel">
<div class="abs"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:32x40
offset:0,20 size:5x20
offset:508,0 size:492x40
offset:0,0 size:32x20
offset:0,0 size:5x40
offset:0,20 size:30x0
offset:1016,0 size:492x40
offset:0,0 size:5x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
TEST_F(NGOutOfFlowLayoutPartTest, PositionedFragmentationWithBottomProperty) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative;
}
.abs {
position:absolute; bottom:10px; width:5px; height:40px;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel" style="height: 60px; width: 32px;">
<div class="abs"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:32x40
offset:0,10 size:5x30
offset:508,0 size:492x40
offset:0,0 size:32x20
offset:0,0 size:5x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that a positioned element without a top or bottom property stays in
// flow - even though it's treated as an OOF element.
TEST_F(NGOutOfFlowLayoutPartTest,
PositionedFragmentationInFlowWithAddedColumns) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position:relative; width:30px;
}
.abs {
position:absolute; width:5px; height:80px;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div style="height: 60px; width: 32px;"></div>
<div class="abs"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:30x40
offset:0,0 size:32x40
offset:508,0 size:492x40
offset:0,0 size:30x20
offset:0,0 size:32x20
offset:0,20 size:5x20
offset:1016,0 size:492x40
offset:0,0 size:5x40
offset:1524,0 size:492x40
offset:0,0 size:5x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that the fragments of a positioned element are added to the right
// fragmentainer despite the presence of column spanners.
TEST_F(NGOutOfFlowLayoutPartTest, PositionedFragmentationAndColumnSpanners) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position:relative; width:30px;
}
.abs {
position:absolute; width:5px; height:20px;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div style="column-span:all;"></div>
<div style="height: 60px; width: 32px;"></div>
<div style="column-span:all;"></div>
<div class="abs"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x0
offset:0,0 size:30x0
offset:0,0 size:1000x0
offset:0,0 size:492x30
offset:0,0 size:30x30
offset:0,0 size:32x30
offset:508,0 size:492x30
offset:0,0 size:30x30
offset:0,0 size:32x30
offset:0,30 size:1000x0
offset:0,30 size:492x10
offset:0,0 size:30x0
offset:0,0 size:5x10
offset:508,30 size:492x10
offset:0,0 size:5x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that column spanners are skipped over when laying out fragmented abspos
// elements.
TEST_F(NGOutOfFlowLayoutPartTest, PositionedFragmentationWithNestedSpanner) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; width:5px; height:50px;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div style="column-span:all;"></div>
<div class="abs"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x0
offset:0,0 size:30x0
offset:0,0 size:1000x0
offset:0,0 size:492x40
offset:0,0 size:30x0
offset:0,0 size:5x40
offset:508,0 size:492x40
offset:0,0 size:5x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that column spanners are skipped over when laying out fragmented abspos
// elements.
TEST_F(NGOutOfFlowLayoutPartTest, PositionedFragmentationWithNestedSpanners) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; width:5px; height:50px;
}
.content { height:20px; }
</style>
<div id="container">
<div id="multicol">
<div style="column-span:all;"></div>
<div class="rel">
<div class="content"></div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
<div class="abs"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x0
offset:0,0 size:1000x0
offset:0,0 size:492x10
offset:0,0 size:30x10
offset:0,0 size:30x10
offset:508,0 size:492x10
offset:0,0 size:30x10
offset:0,0 size:30x10
offset:0,10 size:1000x0
offset:0,10 size:1000x0
offset:0,10 size:1000x0
offset:0,10 size:492x30
offset:0,0 size:30x0
offset:0,0 size:5x30
offset:508,10 size:492x30
offset:0,0 size:5x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that abspos elements bubble up to their containing block when nested
// inside of a spanner.
TEST_F(NGOutOfFlowLayoutPartTest, AbsposInSpanner) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative;
}
.abs {
position:absolute; width:5px; height:50px; top:5px;
}
</style>
<div id="container">
<div class="rel" style="width:50px;">
<div id="multicol">
<div class="rel" style="width:30px;">
<div style="width:10px; height:30px;"></div>
<div>
<div style="column-span:all;">
<div class="abs"></div>
</div>
</div>
</div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:50x40
offset:0,0 size:50x40
offset:0,0 size:17x15
offset:0,0 size:30x15
offset:0,0 size:10x15
offset:33,0 size:17x15
offset:0,0 size:30x15
offset:0,0 size:10x15
offset:0,15 size:30x0
offset:0,15 size:50x0
offset:0,15 size:17x25
offset:0,0 size:30x0
offset:0,0 size:30x0
offset:0,5 size:5x50
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that abspos elements bubble up to their containing block when nested
// inside of a spanner and get the correct static position.
TEST_F(NGOutOfFlowLayoutPartTest, AbsposInSpannerStaticPos) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative;
}
.abs {
position:absolute; width:5px; height:50px;
}
</style>
<div id="container">
<div class="rel" style="width:50px;">
<div id="multicol">
<div class="rel" style="width:30px;">
<div style="width:10px; height:30px;"></div>
<div style="column-span:all; margin-top:5px;">
<div style="width:20px; height:5px;"></div>
<div class="abs"></div>
</div>
</div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:50x40
offset:0,0 size:50x40
offset:0,0 size:17x15
offset:0,0 size:30x15
offset:0,0 size:10x15
offset:33,0 size:17x15
offset:0,0 size:30x15
offset:0,0 size:10x15
offset:0,20 size:50x5
offset:0,0 size:20x5
offset:0,25 size:17x15
offset:0,0 size:30x0
offset:0,25 size:5x50
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests fragmented abspos elements with a spanner nested inside.
TEST_F(NGOutOfFlowLayoutPartTest, SpannerInAbspos) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; width:5px; height:50px;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div class="abs">
<div style="column-span:all;"></div>
</div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:30x0
offset:0,0 size:5x40
offset:0,0 size:5x0
offset:508,0 size:492x40
offset:0,0 size:5x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that new column fragments are added correctly if a positioned node
// fragments beyond the last fragmentainer in a context in the presence of a
// spanner.
TEST_F(NGOutOfFlowLayoutPartTest,
PositionedFragmentationWithNewColumnsAndSpanners) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; width:5px; height:120px; top:0px;
}
.content { height:20px; }
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div class="content"></div>
<div class="abs"></div>
</div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x10
offset:0,0 size:30x10
offset:0,0 size:30x10
offset:0,0 size:5x10
offset:508,0 size:492x10
offset:0,0 size:30x10
offset:0,0 size:30x10
offset:0,0 size:5x10
offset:0,10 size:1000x0
offset:0,10 size:1000x0
offset:0,10 size:1000x0
offset:0,10 size:492x30
offset:0,0 size:5x30
offset:508,10 size:492x30
offset:0,0 size:5x30
offset:1016,10 size:492x30
offset:0,0 size:5x30
offset:1524,10 size:492x30
offset:0,0 size:5x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that new column fragments are added correctly if a positioned node
// fragments beyond the last fragmentainer in a context directly after a
// spanner.
TEST_F(NGOutOfFlowLayoutPartTest,
PositionedFragmentationWithNewColumnsAfterSpanner) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; width:5px; height:50px; top:25px;
}
.content { height:20px; }
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div class="content"></div>
<div class="abs"></div>
</div>
<div style="column-span:all;"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x10
offset:0,0 size:30x10
offset:0,0 size:30x10
offset:508,0 size:492x10
offset:0,0 size:30x10
offset:0,0 size:30x10
offset:0,10 size:1000x0
offset:0,10 size:492x30
offset:0,5 size:5x25
offset:508,10 size:492x30
offset:0,0 size:5x25
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that new column fragments are added correctly if a positioned node
// fragments beyond the last fragmentainer in a context in the presence of a
// spanner.
TEST_F(NGOutOfFlowLayoutPartTest,
AbsposFragWithSpannerAndNewColumnsAutoHeight) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; width:5px; height:4px;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div class="abs"></div>
</div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x0
offset:0,0 size:1000x0
offset:0,0 size:492x0
offset:0,0 size:30x0
offset:0,0 size:5x1
offset:0,0 size:1000x0
offset:0,0 size:1000x0
offset:0,0 size:1000x0
offset:0,0 size:492x0
offset:0,0 size:5x1
offset:508,0 size:492x0
offset:0,0 size:5x1
offset:1016,0 size:492x0
offset:0,0 size:5x1
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that empty column fragments are added if an OOF element begins layout
// in a fragmentainer that is more than one index beyond the last existing
// column fragmentainer in the presence of a spanner.
TEST_F(NGOutOfFlowLayoutPartTest, AbsposFragWithSpannerAndNewEmptyColumns) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; top:80px; width:5px; height:120px;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div class="abs"></div>
</div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x0
offset:0,0 size:30x0
offset:0,0 size:1000x0
offset:0,0 size:1000x0
offset:0,0 size:1000x0
offset:0,0 size:492x40
offset:508,0 size:492x40
offset:0,39 size:5x1
offset:1016,0 size:492x40
offset:0,0 size:5x40
offset:1524,0 size:492x40
offset:0,0 size:5x40
offset:2032,0 size:492x40
offset:0,0 size:5x39
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Fragmented OOF element with block-size percentage resolution.
TEST_F(NGOutOfFlowLayoutPartTest, AbsposFragmentationPctResolution) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position: relative; width:30px;
}
.abs {
position:absolute; top:30px; width:5px; height:100%;
}
.spanner {
column-span:all; height:25%;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div class="abs"></div>
<div style="width: 10px; height:30px;"></div>
</div>
<div class="spanner"></div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x15
offset:0,0 size:30x15
offset:0,0 size:10x15
offset:508,0 size:492x15
offset:0,0 size:30x15
offset:0,0 size:10x15
offset:0,15 size:1000x10
offset:0,25 size:492x15
offset:0,0 size:5x15
offset:508,25 size:492x15
offset:0,0 size:5x15
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Fragmented OOF element with block-size percentage resolution and overflow.
TEST_F(NGOutOfFlowLayoutPartTest,
AbsposFragmentationPctResolutionWithOverflow) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
columns:5; column-fill:auto; column-gap:0px; height:100px;
}
.rel {
position: relative; width:55px;
}
.abs {
position:absolute; top:0px; width:5px; height:100%;
}
</style>
<div id="container">
<div id="multicol">
<div style="height:30px;"></div>
<div class="rel">
<div class="abs"></div>
<div style="width:44px; height:200px;">
<div style="width:33px; height:400px;"></div>
</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:1000x100
offset:0,0 size:200x100
offset:0,0 size:200x30
offset:0,30 size:55x70
offset:0,0 size:44x70
offset:0,0 size:33x70
offset:0,30 size:5x70
offset:200,0 size:200x100
offset:0,0 size:55x100
offset:0,0 size:44x100
offset:0,0 size:33x100
offset:0,0 size:5x100
offset:400,0 size:200x100
offset:0,0 size:55x30
offset:0,0 size:44x30
offset:0,0 size:33x100
offset:0,0 size:5x30
offset:600,0 size:200x100
offset:0,0 size:55x0
offset:0,0 size:44x0
offset:0,0 size:33x100
offset:800,0 size:200x100
offset:0,0 size:55x0
offset:0,0 size:44x0
offset:0,0 size:33x30
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Fragmented OOF element inside a nested multi-column.
TEST_F(NGOutOfFlowLayoutPartTest, SimpleAbsposNestedFragmentation) {
SetBodyInnerHTML(
R"HTML(
<style>
.multicol {
columns:2; column-fill:auto; column-gap:0px;
}
.rel {
position: relative; width:55px; height:80px;
}
.abs {
position:absolute; top:0px; width:5px; height:80px;
}
</style>
<div id="container">
<div class="multicol" id="outer" style="height:100px;">
<div style="height:40px; width:40px;"></div>
<div class="multicol" id="inner">
<div class="rel">
<div class="abs"></div>
</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:1000x100
offset:0,0 size:500x100
offset:0,0 size:40x40
offset:0,40 size:500x60
offset:0,0 size:250x60
offset:0,0 size:55x60
offset:0,0 size:5x60
offset:250,0 size:250x60
offset:0,0 size:55x20
offset:0,0 size:5x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Fragmented OOF element inside a nested multi-column with new columns.
TEST_F(NGOutOfFlowLayoutPartTest, AbsposNestedFragmentationNewColumns) {
SetBodyInnerHTML(
R"HTML(
<style>
.multicol {
columns:2; column-fill:auto; column-gap:0px;
}
#inner {
column-gap:16px; height:40px; padding:10px;
}
.rel {
position: relative; width:55px; height:20px;
}
.abs {
position:absolute; top:0px; width:5px; height:40px;
}
</style>
<div id="container">
<div class="multicol" id="outer" style="height:100px;">
<div style="height:40px; width:40px;"></div>
<div class="multicol" id="inner">
<div class="rel">
<div class="abs"></div>
</div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></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:1000x100
offset:0,0 size:500x100
offset:0,0 size:40x40
offset:0,40 size:500x60
offset:10,10 size:232x10
offset:0,0 size:55x10
offset:0,0 size:5x10
offset:258,10 size:232x10
offset:0,0 size:55x10
offset:0,0 size:5x10
offset:248,0 size:5x10
offset:496,0 size:5x10
offset:10,20 size:480x0
offset:10,20 size:480x0
offset:10,20 size:480x0
)DUMP";
EXPECT_EQ(expectation, dump);
}
// TODO(almaher): Figure out why this is hitting a DCHECK in
// AssertClearedPaintInvalidationFlags.
//
// Fragmented OOF element inside a nested multi-column starting at a
// fragmentainer index beyond the last existing fragmentainer.
TEST_F(NGOutOfFlowLayoutPartTest,
DISABLED_AbsposNestedFragmentationNewEmptyColumns) {
SetBodyInnerHTML(
R"HTML(
<style>
.multicol {
columns:2; column-fill:auto; column-gap:0px;
}
.rel {
position: relative; width:55px; height:80px;
}
.abs {
position:absolute; top:120px; width:5px; height:120px;
}
</style>
<div id="container">
<div class="multicol" id="outer" style="height:100px;">
<div style="height:40px; width:40px;"></div>
<div class="multicol" id="inner" style="column-gap:16px;">
<div class="rel">
<div class="abs"></div>
</div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
<div style="column-span:all;"></div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
// TODO(almaher): The OOFs are added out of order due to how ordering is
// currently handled in NGSimplifiedOOFLayoutAlgorithm.
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x100
offset:0,0 size:1000x100
offset:0,0 size:500x100
offset:0,0 size:40x40
offset:0,40 size:500x40
offset:0,0 size:242x40
offset:0,0 size:55x40
offset:258,0 size:242x40
offset:0,0 size:55x40
offset:774,0 size:5x40
offset:1032,0 size:5x40
offset:516,0 size:5x40
offset:0,40 size:500x0
offset:0,40 size:500x0
offset:0,40 size:500x0
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Fragmented OOF with `height: auto` and positioned with the bottom property.
TEST_F(NGOutOfFlowLayoutPartTest,
PositionedFragmentationWithBottomPropertyAndHeightAuto) {
SetBodyInnerHTML(
R"HTML(
<style>
#multicol {
column-count:2; column-fill:auto; column-gap:16px; height:40px;
}
.rel {
position:relative; height:60px; width:32px;
}
.abs {
position:absolute; bottom:0; width:5px; height:auto;
}
</style>
<div id="container">
<div id="multicol">
<div class="rel">
<div class="abs">
<div style="width: 2px; height: 10px"></div>
<div style="width: 3px; height: 20px"></div>
<div style="width: 4px; height: 10px"></div>
</div>
</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("container"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x40
offset:0,0 size:1000x40
offset:0,0 size:492x40
offset:0,0 size:32x40
offset:0,20 size:5x20
offset:0,0 size:2x10
offset:0,10 size:3x10
offset:508,0 size:492x40
offset:0,0 size:32x20
offset:0,0 size:5x20
offset:0,0 size:3x10
offset:0,10 size:4x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
} // namespace
} // namespace blink