// Copyright 2014 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 "core/layout/LayoutMultiColumnFlowThread.h"
#include "core/layout/LayoutMultiColumnSet.h"
#include "core/layout/LayoutMultiColumnSpannerPlaceholder.h"
#include "core/layout/LayoutTestHelper.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
class MultiColumnRenderingTest : public RenderingTest {
public:
LayoutMultiColumnFlowThread* FindFlowThread(const char* id) const;
// Generate a signature string based on what kind of column boxes the flow
// thread has established. 'c' is used for regular column content sets, while
// 's' is used for spanners. '?' is used when there's an unknown box type
// (which should be considered a failure).
String ColumnSetSignature(LayoutMultiColumnFlowThread*);
String ColumnSetSignature(const char* multicol_id);
void SetMulticolHTML(const String&);
};
LayoutMultiColumnFlowThread* MultiColumnRenderingTest::FindFlowThread(
const char* id) const {
if (LayoutBlockFlow* multicol_container =
ToLayoutBlockFlow(GetLayoutObjectByElementId(id)))
return multicol_container->MultiColumnFlowThread();
return nullptr;
}
String MultiColumnRenderingTest::ColumnSetSignature(
LayoutMultiColumnFlowThread* flow_thread) {
String signature = "";
for (LayoutBox* column_box = flow_thread->FirstMultiColumnBox(); column_box;
column_box = column_box->NextSiblingMultiColumnBox()) {
if (column_box->IsLayoutMultiColumnSpannerPlaceholder())
signature.append('s');
else if (column_box->IsLayoutMultiColumnSet())
signature.append('c');
else
signature.append('?');
}
return signature;
}
String MultiColumnRenderingTest::ColumnSetSignature(const char* multicol_id) {
return ColumnSetSignature(FindFlowThread(multicol_id));
}
void MultiColumnRenderingTest::SetMulticolHTML(const String& html) {
const char* style =
"";
SetBodyInnerHTML(style + html);
}
TEST_F(MultiColumnRenderingTest, OneBlockWithInDepthTreeStructureCheck) {
// Examine the layout tree established by a simple multicol container with a
// block with some text inside.
SetMulticolHTML("
");
LayoutBlockFlow* multicol_container =
ToLayoutBlockFlow(GetLayoutObjectByElementId("mc"));
ASSERT_TRUE(multicol_container);
LayoutMultiColumnFlowThread* flow_thread =
multicol_container->MultiColumnFlowThread();
ASSERT_TRUE(flow_thread);
EXPECT_EQ(ColumnSetSignature(flow_thread), "c");
EXPECT_EQ(flow_thread->Parent(), multicol_container);
EXPECT_FALSE(flow_thread->PreviousSibling());
LayoutMultiColumnSet* column_set = flow_thread->FirstMultiColumnSet();
ASSERT_TRUE(column_set);
EXPECT_EQ(column_set->PreviousSibling(), flow_thread);
EXPECT_FALSE(column_set->NextSibling());
LayoutBlockFlow* block = ToLayoutBlockFlow(flow_thread->FirstChild());
ASSERT_TRUE(block);
EXPECT_FALSE(block->NextSibling());
ASSERT_TRUE(block->FirstChild());
EXPECT_TRUE(block->FirstChild()->IsText());
EXPECT_FALSE(block->FirstChild()->NextSibling());
}
TEST_F(MultiColumnRenderingTest, Empty) {
// If there's no column content, there should be no column set.
SetMulticolHTML("");
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnRenderingTest, OneBlock) {
// There is some content, so we should create a column set.
SetMulticolHTML("");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
ASSERT_EQ(ColumnSetSignature(flow_thread), "c");
LayoutMultiColumnSet* column_set = flow_thread->FirstMultiColumnSet();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("block")),
column_set);
}
TEST_F(MultiColumnRenderingTest, TwoBlocks) {
// No matter how much content, we should only create one column set (unless
// there are spanners).
SetMulticolHTML(
"");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
ASSERT_EQ(ColumnSetSignature(flow_thread), "c");
LayoutMultiColumnSet* column_set = flow_thread->FirstMultiColumnSet();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("block1")),
column_set);
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("block2")),
column_set);
}
TEST_F(MultiColumnRenderingTest, Spanner) {
// With one spanner and no column content, we should create a spanner set.
SetMulticolHTML("");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
ASSERT_EQ(ColumnSetSignature(flow_thread), "s");
LayoutBox* column_box = flow_thread->FirstMultiColumnBox();
EXPECT_EQ(flow_thread->FirstMultiColumnSet(), nullptr);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner")),
column_box);
EXPECT_EQ(GetLayoutObjectByElementId("spanner")->SpannerPlaceholder(),
column_box);
}
TEST_F(MultiColumnRenderingTest, ContentThenSpanner) {
// With some column content followed by a spanner, we need a column set
// followed by a spanner set.
SetMulticolHTML(
"");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
ASSERT_EQ(ColumnSetSignature(flow_thread), "cs");
LayoutBox* column_box = flow_thread->FirstMultiColumnBox();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("columnContent")),
column_box);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner")),
column_box);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("columnContent")),
nullptr);
}
TEST_F(MultiColumnRenderingTest, SpannerThenContent) {
// With a spanner followed by some column content, we need a spanner set
// followed by a column set.
SetMulticolHTML(
"");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
ASSERT_EQ(ColumnSetSignature(flow_thread), "sc");
LayoutBox* column_box = flow_thread->FirstMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner")),
column_box);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("columnContent")),
column_box);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("columnContent")),
nullptr);
}
TEST_F(MultiColumnRenderingTest, ContentThenSpannerThenContent) {
// With column content followed by a spanner followed by some column content,
// we need a column
// set followed by a spanner set followed by a column set.
SetMulticolHTML(
"");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
ASSERT_EQ(ColumnSetSignature(flow_thread), "csc");
LayoutBox* column_box = flow_thread->FirstMultiColumnSet();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("columnContentBefore")),
column_box);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("columnContentBefore")),
nullptr);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner")),
column_box);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("columnContentAfter")),
column_box);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("columnContentAfter")),
nullptr);
}
TEST_F(MultiColumnRenderingTest, TwoSpanners) {
// With two spanners and no column content, we need two spanner sets.
SetMulticolHTML(
"");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
ASSERT_EQ(ColumnSetSignature(flow_thread), "ss");
LayoutBox* column_box = flow_thread->FirstMultiColumnBox();
EXPECT_EQ(flow_thread->FirstMultiColumnSet(), nullptr);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner1")),
column_box);
EXPECT_EQ(GetLayoutObjectByElementId("spanner1")->SpannerPlaceholder(),
column_box);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner2")),
column_box);
EXPECT_EQ(GetLayoutObjectByElementId("spanner2")->SpannerPlaceholder(),
column_box);
}
TEST_F(MultiColumnRenderingTest, SpannerThenContentThenSpanner) {
// With two spanners and some column content in-between, we need a spanner
// set, a column set and another spanner set.
SetMulticolHTML(
"");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
ASSERT_EQ(ColumnSetSignature(flow_thread), "scs");
LayoutMultiColumnSet* column_set = flow_thread->FirstMultiColumnSet();
EXPECT_EQ(column_set->NextSiblingMultiColumnSet(), nullptr);
LayoutBox* column_box = flow_thread->FirstMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner1")),
column_box);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(column_box, column_set);
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("columnContent")),
column_set);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("columnContent")),
nullptr);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner2")),
column_box);
}
TEST_F(MultiColumnRenderingTest, SpannerWithSpanner) {
// column-span:all on something inside column-span:all has no effect.
SetMulticolHTML(
"");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
ASSERT_EQ(ColumnSetSignature(flow_thread), "s");
LayoutBox* column_box = flow_thread->FirstMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner")),
column_box);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("invalidSpanner")),
column_box);
EXPECT_EQ(ToLayoutMultiColumnSpannerPlaceholder(column_box)
->LayoutObjectInFlowThread(),
GetLayoutObjectByElementId("spanner"));
EXPECT_EQ(GetLayoutObjectByElementId("spanner")->SpannerPlaceholder(),
column_box);
EXPECT_EQ(GetLayoutObjectByElementId("invalidSpanner")->SpannerPlaceholder(),
nullptr);
}
TEST_F(MultiColumnRenderingTest, SubtreeWithSpanner) {
SetMulticolHTML(
"");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
EXPECT_EQ(ColumnSetSignature(flow_thread), "csc");
LayoutBox* column_box = flow_thread->FirstMultiColumnBox();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("outer")),
column_box);
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("block1")),
column_box);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner")),
column_box);
EXPECT_EQ(GetLayoutObjectByElementId("spanner")->SpannerPlaceholder(),
column_box);
EXPECT_EQ(ToLayoutMultiColumnSpannerPlaceholder(column_box)
->LayoutObjectInFlowThread(),
GetLayoutObjectByElementId("spanner"));
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("outer")),
nullptr);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("block1")),
nullptr);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("block2")),
nullptr);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("block2")),
column_box);
}
TEST_F(MultiColumnRenderingTest, SubtreeWithSpannerAfterSpanner) {
SetMulticolHTML(
"");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
EXPECT_EQ(ColumnSetSignature(flow_thread), "scsc");
LayoutBox* column_box = flow_thread->FirstMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner1")),
column_box);
EXPECT_EQ(ToLayoutMultiColumnSpannerPlaceholder(column_box)
->LayoutObjectInFlowThread(),
GetLayoutObjectByElementId("spanner1"));
EXPECT_EQ(GetLayoutObjectByElementId("spanner1")->SpannerPlaceholder(),
column_box);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("outer")),
column_box);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner2")),
column_box);
EXPECT_EQ(ToLayoutMultiColumnSpannerPlaceholder(column_box)
->LayoutObjectInFlowThread(),
GetLayoutObjectByElementId("spanner2"));
EXPECT_EQ(GetLayoutObjectByElementId("spanner2")->SpannerPlaceholder(),
column_box);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("outer")),
nullptr);
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("after")),
nullptr);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("after")),
column_box);
}
TEST_F(MultiColumnRenderingTest, SubtreeWithSpannerBeforeSpanner) {
SetMulticolHTML(
"");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
EXPECT_EQ(ColumnSetSignature(flow_thread), "cscs");
LayoutBox* column_box = flow_thread->FirstMultiColumnSet();
EXPECT_EQ(flow_thread->MapDescendantToColumnSet(
GetLayoutObjectByElementId("outer")),
column_box);
column_box = column_box->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner1")),
column_box);
EXPECT_EQ(GetLayoutObjectByElementId("spanner1")->SpannerPlaceholder(),
column_box);
EXPECT_EQ(ToLayoutMultiColumnSpannerPlaceholder(column_box)
->LayoutObjectInFlowThread(),
GetLayoutObjectByElementId("spanner1"));
column_box =
column_box->NextSiblingMultiColumnBox()->NextSiblingMultiColumnBox();
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("spanner2")),
column_box);
EXPECT_EQ(GetLayoutObjectByElementId("spanner2")->SpannerPlaceholder(),
column_box);
EXPECT_EQ(ToLayoutMultiColumnSpannerPlaceholder(column_box)
->LayoutObjectInFlowThread(),
GetLayoutObjectByElementId("spanner2"));
EXPECT_EQ(flow_thread->ContainingColumnSpannerPlaceholder(
GetLayoutObjectByElementId("outer")),
nullptr);
}
TEST_F(MultiColumnRenderingTest, columnSetAtBlockOffset) {
SetMulticolHTML(
"text
text
text
text
text
spanner
text
text
text
text
text
");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
EXPECT_EQ(ColumnSetSignature(flow_thread), "cscsc");
LayoutMultiColumnSet* first_row = flow_thread->FirstMultiColumnSet();
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(-10000), LayoutBox::kAssociateWithFormerPage),
first_row); // negative overflow
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(-10000), LayoutBox::kAssociateWithLatterPage),
first_row); // negative overflow
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(), LayoutBox::kAssociateWithFormerPage),
first_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(), LayoutBox::kAssociateWithLatterPage),
first_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(499), LayoutBox::kAssociateWithFormerPage),
first_row); // bottom of last line in first row.
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(499), LayoutBox::kAssociateWithLatterPage),
first_row); // bottom of last line in first row.
LayoutMultiColumnSet* second_row = first_row->NextSiblingMultiColumnSet();
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(500), LayoutBox::kAssociateWithFormerPage),
first_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(500), LayoutBox::kAssociateWithLatterPage),
second_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(699), LayoutBox::kAssociateWithFormerPage),
second_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(699), LayoutBox::kAssociateWithLatterPage),
second_row);
LayoutMultiColumnSet* third_row = second_row->NextSiblingMultiColumnSet();
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(700), LayoutBox::kAssociateWithFormerPage),
second_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(700), LayoutBox::kAssociateWithLatterPage),
third_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(799), LayoutBox::kAssociateWithLatterPage),
third_row); // bottom of last row
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(10000), LayoutBox::kAssociateWithFormerPage),
third_row); // overflow
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(10000), LayoutBox::kAssociateWithLatterPage),
third_row); // overflow
}
TEST_F(MultiColumnRenderingTest, columnSetAtBlockOffsetVerticalRl) {
SetMulticolHTML(
"text
text
text
text
text<"
"div id='spanner1'>spanner
text
texttext
text
text");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
EXPECT_EQ(ColumnSetSignature(flow_thread), "cscsc");
LayoutMultiColumnSet* first_row = flow_thread->FirstMultiColumnSet();
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(-10000), LayoutBox::kAssociateWithFormerPage),
first_row); // negative overflow
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(-10000), LayoutBox::kAssociateWithLatterPage),
first_row); // negative overflow
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(), LayoutBox::kAssociateWithFormerPage),
first_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(), LayoutBox::kAssociateWithLatterPage),
first_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(499), LayoutBox::kAssociateWithFormerPage),
first_row); // bottom of last line in first row.
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(499), LayoutBox::kAssociateWithLatterPage),
first_row); // bottom of last line in first row.
LayoutMultiColumnSet* second_row = first_row->NextSiblingMultiColumnSet();
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(500), LayoutBox::kAssociateWithFormerPage),
first_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(500), LayoutBox::kAssociateWithLatterPage),
second_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(699), LayoutBox::kAssociateWithFormerPage),
second_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(699), LayoutBox::kAssociateWithLatterPage),
second_row);
LayoutMultiColumnSet* third_row = second_row->NextSiblingMultiColumnSet();
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(700), LayoutBox::kAssociateWithFormerPage),
second_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(700), LayoutBox::kAssociateWithLatterPage),
third_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(799), LayoutBox::kAssociateWithLatterPage),
third_row); // bottom of last row
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(10000), LayoutBox::kAssociateWithFormerPage),
third_row); // overflow
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(10000), LayoutBox::kAssociateWithLatterPage),
third_row); // overflow
}
TEST_F(MultiColumnRenderingTest, columnSetAtBlockOffsetVerticalLr) {
SetMulticolHTML(
"text
text
text
text
text<"
"div id='spanner1'>spanner
text
texttext
text
text");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
EXPECT_EQ(ColumnSetSignature(flow_thread), "cscsc");
LayoutMultiColumnSet* first_row = flow_thread->FirstMultiColumnSet();
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(-10000), LayoutBox::kAssociateWithFormerPage),
first_row); // negative overflow
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(-10000), LayoutBox::kAssociateWithLatterPage),
first_row); // negative overflow
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(), LayoutBox::kAssociateWithFormerPage),
first_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(), LayoutBox::kAssociateWithLatterPage),
first_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(499), LayoutBox::kAssociateWithFormerPage),
first_row); // bottom of last line in first row.
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(499), LayoutBox::kAssociateWithLatterPage),
first_row); // bottom of last line in first row.
LayoutMultiColumnSet* second_row = first_row->NextSiblingMultiColumnSet();
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(500), LayoutBox::kAssociateWithFormerPage),
first_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(500), LayoutBox::kAssociateWithLatterPage),
second_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(699), LayoutBox::kAssociateWithFormerPage),
second_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(699), LayoutBox::kAssociateWithLatterPage),
second_row);
LayoutMultiColumnSet* third_row = second_row->NextSiblingMultiColumnSet();
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(700), LayoutBox::kAssociateWithFormerPage),
second_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(700), LayoutBox::kAssociateWithLatterPage),
third_row);
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(799), LayoutBox::kAssociateWithLatterPage),
third_row); // bottom of last row
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(10000), LayoutBox::kAssociateWithFormerPage),
third_row); // overflow
EXPECT_EQ(flow_thread->ColumnSetAtBlockOffset(
LayoutUnit(10000), LayoutBox::kAssociateWithLatterPage),
third_row); // overflow
}
class MultiColumnTreeModifyingTest : public MultiColumnRenderingTest {
public:
void SetMulticolHTML(const char*);
void ReparentLayoutObject(const char* new_parent_id,
const char* child_id,
const char* insert_before_id = nullptr);
void DestroyLayoutObject(LayoutObject* child);
void DestroyLayoutObject(const char* child_id);
};
void MultiColumnTreeModifyingTest::SetMulticolHTML(const char* html) {
MultiColumnRenderingTest::SetMulticolHTML(html);
// Allow modifications to the layout tree structure, because that's what we
// want to test.
GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
}
void MultiColumnTreeModifyingTest::ReparentLayoutObject(
const char* new_parent_id,
const char* child_id,
const char* insert_before_id) {
LayoutObject* new_parent = GetLayoutObjectByElementId(new_parent_id);
LayoutObject* child = GetLayoutObjectByElementId(child_id);
LayoutObject* insert_before =
insert_before_id ? GetLayoutObjectByElementId(insert_before_id) : nullptr;
child->Remove();
new_parent->AddChild(child, insert_before);
}
void MultiColumnTreeModifyingTest::DestroyLayoutObject(LayoutObject* child) {
// Remove and destroy in separate steps, so that we get to test removal of
// subtrees.
child->Remove();
child->GetNode()->DetachLayoutTree();
}
void MultiColumnTreeModifyingTest::DestroyLayoutObject(const char* child_id) {
DestroyLayoutObject(GetLayoutObjectByElementId(child_id));
}
TEST_F(MultiColumnTreeModifyingTest, InsertFirstContentAndRemove) {
SetMulticolHTML("");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
LayoutBlockFlow* block =
ToLayoutBlockFlow(GetLayoutObjectByElementId("block"));
LayoutBlockFlow* multicol_container =
ToLayoutBlockFlow(GetLayoutObjectByElementId("mc"));
block->Remove();
multicol_container->AddChild(block);
EXPECT_EQ(block->Parent(), flow_thread);
// A set should have appeared, now that the multicol container has content.
EXPECT_EQ(ColumnSetSignature(flow_thread), "c");
DestroyLayoutObject(block);
// The set should be gone again now, since there's nothing inside the multicol
// container anymore.
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnTreeModifyingTest, InsertContentBeforeContentAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
ReparentLayoutObject("mc", "block", "insertBefore");
// There was already some content prior to our insertion, so no new set should
// be inserted.
EXPECT_EQ(ColumnSetSignature("mc"), "c");
DestroyLayoutObject("block");
// There's still some content after the removal, so the set should remain.
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest, InsertContentAfterContentAndRemove) {
SetMulticolHTML("");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
ReparentLayoutObject("mc", "block");
// There was already some content prior to our insertion, so no new set should
// be inserted.
EXPECT_EQ(ColumnSetSignature("mc"), "c");
DestroyLayoutObject("block");
// There's still some content after the removal, so the set should remain.
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest, InsertSpannerAndRemove) {
SetMulticolHTML("");
LayoutMultiColumnFlowThread* flow_thread = FindFlowThread("mc");
LayoutBlockFlow* spanner =
ToLayoutBlockFlow(GetLayoutObjectByElementId("spanner"));
LayoutBlockFlow* multicol_container =
ToLayoutBlockFlow(GetLayoutObjectByElementId("mc"));
spanner->Remove();
multicol_container->AddChild(spanner);
EXPECT_EQ(spanner->Parent(), flow_thread);
// We should now have a spanner placeholder, since we just moved a spanner
// into the multicol container.
EXPECT_EQ(ColumnSetSignature(flow_thread), "s");
DestroyLayoutObject(spanner);
EXPECT_EQ(ColumnSetSignature(flow_thread), "");
}
TEST_F(MultiColumnTreeModifyingTest, InsertTwoSpannersAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "block");
EXPECT_EQ(ColumnSetSignature("mc"), "css");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnTreeModifyingTest, InsertSpannerAfterContentAndRemove) {
SetMulticolHTML("");
ReparentLayoutObject("mc", "spanner");
// We should now have a spanner placeholder, since we just moved a spanner
// into the multicol container.
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest, InsertSpannerBeforeContentAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "spanner", "columnContent");
// We should now have a spanner placeholder, since we just moved a spanner
// into the multicol container.
EXPECT_EQ(ColumnSetSignature("mc"), "sc");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest, InsertSpannerBetweenContentAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "spanner", "insertBefore");
// Since the spanner was inserted in the middle of column content, what used
// to be one column set had to be split in two, in order to get a spot to
// insert the spanner placeholder.
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("spanner");
// The spanner placeholder should be gone again now, and the two sets be
// merged into one.
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertSubtreeWithContentAndSpannerAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "block");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnTreeModifyingTest, InsertInsideSpannerAndRemove) {
SetMulticolHTML(
"text
");
ReparentLayoutObject("spanner", "block");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertSpannerInContentBeforeSpannerAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
ReparentLayoutObject("mc", "spanner", "insertBefore");
EXPECT_EQ(ColumnSetSignature("mc"), "cscs");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertSpannerInContentAfterSpannerAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "sc");
ReparentLayoutObject("mc", "spanner", "insertBefore");
EXPECT_EQ(ColumnSetSignature("mc"), "scsc");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "sc");
}
TEST_F(MultiColumnTreeModifyingTest, InsertSpannerAfterSpannerAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "ss");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
}
TEST_F(MultiColumnTreeModifyingTest, InsertSpannerBeforeSpannerAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "spanner", "insertBefore");
EXPECT_EQ(ColumnSetSignature("mc"), "ss");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
}
TEST_F(MultiColumnTreeModifyingTest, InsertContentBeforeSpannerAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "block", "insertBefore");
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertContentAfterContentBeforeSpannerAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
ReparentLayoutObject("mc", "block", "insertBefore");
// There was already some content before the spanner prior to our insertion,
// so no new set should be inserted.
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertContentAfterContentAndSpannerAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
ReparentLayoutObject("mc", "block");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertContentBeforeSpannerAndContentAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "sc");
ReparentLayoutObject("mc", "block", "insertBefore");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "sc");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertSpannerIntoContentBeforeSpannerAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cssc");
ReparentLayoutObject("mc", "spanner", "insertBefore");
EXPECT_EQ(ColumnSetSignature("mc"), "cscssc");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "cssc");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertSpannerIntoContentAfterSpannerAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cssc");
ReparentLayoutObject("mc", "spanner", "insertBefore");
EXPECT_EQ(ColumnSetSignature("mc"), "csscsc");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "cssc");
}
TEST_F(MultiColumnTreeModifyingTest, InsertInvalidSpannerAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
ReparentLayoutObject("spanner", "invalidSpanner");
// It's not allowed to nest spanners.
EXPECT_EQ(ColumnSetSignature("mc"), "s");
DestroyLayoutObject("invalidSpanner");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
}
TEST_F(MultiColumnTreeModifyingTest, InsertSpannerWithInvalidSpannerAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "spanner");
// It's not allowed to nest spanners.
EXPECT_EQ(ColumnSetSignature("mc"), "s");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertInvalidSpannerInSpannerBetweenContentAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
ReparentLayoutObject("spanner", "invalidSpanner");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("invalidSpanner");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
}
TEST_F(MultiColumnTreeModifyingTest, InsertContentAndSpannerAndRemove) {
SetMulticolHTML(
"text
");
ReparentLayoutObject("mc", "block");
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertContentAndSpannerAndContentAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "block");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnTreeModifyingTest, InsertSubtreeWithSpannerAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "block");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertSubtreeWithSpannerAfterContentAndRemove) {
SetMulticolHTML(
"column "
"content
");
ReparentLayoutObject("mc", "block");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertSubtreeWithSpannerBeforeContentAndRemove) {
SetMulticolHTML(
"");
ReparentLayoutObject("mc", "block", "insertBefore");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertSubtreeWithSpannerInsideContentAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
ReparentLayoutObject("newParent", "block", "insertBefore");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertSubtreeWithSpannerAfterSpannerAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
ReparentLayoutObject("mc", "block");
EXPECT_EQ(ColumnSetSignature("mc"), "scsc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
}
TEST_F(MultiColumnTreeModifyingTest,
InsertSubtreeWithSpannerBeforeSpannerAndRemove) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
ReparentLayoutObject("mc", "block", "insertBefore");
EXPECT_EQ(ColumnSetSignature("mc"), "cscs");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "s");
}
TEST_F(MultiColumnTreeModifyingTest, RemoveSpannerAndContent) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnTreeModifyingTest, RemoveSpannerAndSomeContentBefore) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest, RemoveSpannerAndAllContentBefore) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cs");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnTreeModifyingTest,
RemoveSpannerAndAllContentBeforeWithContentAfter) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest, RemoveSpannerAndSomeContentAfter) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest, RemoveSpannerAndAllContentAfter) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnTreeModifyingTest,
RemoveSpannerAndAllContentAfterWithContentBefore) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest, RemoveTwoSpannersBeforeContent) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cssc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest, RemoveSpannerAndContentAndSpanner) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cscsc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "");
}
TEST_F(MultiColumnTreeModifyingTest,
RemoveSpannerAndContentAndSpannerBeforeContent) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cscsc");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest,
RemoveSpannerAndContentAndSpannerAfterContent) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "cscs");
DestroyLayoutObject("block");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
TEST_F(MultiColumnTreeModifyingTest,
RemoveInvalidSpannerInSpannerBetweenContent) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
}
TEST_F(MultiColumnTreeModifyingTest,
RemoveSpannerWithInvalidSpannerBetweenContent) {
SetMulticolHTML(
"");
EXPECT_EQ(ColumnSetSignature("mc"), "csc");
DestroyLayoutObject("spanner");
EXPECT_EQ(ColumnSetSignature("mc"), "c");
}
} // anonymous namespace
} // namespace blink