blob: d43c46e86b4de72bbb0d25d4fb212317f2d40d77 [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/layout_table.h"
#include "third_party/blink/renderer/core/layout/layout_table_section.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
namespace blink {
namespace {
class LayoutTableTest : public RenderingTest {
protected:
LayoutTable* GetTableByElementId(const char* id) {
return ToLayoutTable(GetLayoutObjectByElementId(id));
}
};
TEST_F(LayoutTableTest, OverflowViaOutline) {
SetBodyInnerHTML(R"HTML(
<style>
div { display: table; width: 100px; height: 200px; }
</style>
<div id=target>
<div id=child></div>
</div>
)HTML");
auto* target = GetTableByElementId("target");
EXPECT_EQ(LayoutRect(0, 0, 100, 200), target->SelfVisualOverflowRect());
ToElement(target->GetNode())
->setAttribute(html_names::kStyleAttr, "outline: 2px solid black");
auto* child = GetTableByElementId("child");
ToElement(child->GetNode())
->setAttribute(html_names::kStyleAttr, "outline: 2px solid black");
target->GetFrameView()->UpdateAllLifecyclePhases(
DocumentLifecycle::LifecycleUpdateReason::kTest);
EXPECT_EQ(LayoutRect(-2, -2, 104, 204), target->SelfVisualOverflowRect());
EXPECT_EQ(LayoutRect(-2, -2, 104, 204), child->SelfVisualOverflowRect());
}
TEST_F(LayoutTableTest, OverflowWithCollapsedBorders) {
SetBodyInnerHTML(R"HTML(
<style>
table { border-collapse: collapse }
td { border: 0px solid blue; padding: 0; width: 100px; height: 100px }
</style>
<table id='table'>
<tr>
<td style='border-top-width: 2px; border-left-width: 2px;
outline: 6px solid blue'></td>
<td style='border-top-width: 4px; border-right-width: 10px'></td>
</tr>
<tr style='outline: 8px solid green'>
<td style='border-left-width: 20px'></td>
<td style='border-right-width: 20px'></td>
</tr>
</table>
)HTML");
auto* table = GetTableByElementId("table");
// The table's border box rect covers all collapsed borders of the first
// row, and bottom collapsed borders of the last row.
LayoutRect expected_border_box_rect = table->PhysicalContentBoxRect();
expected_border_box_rect.ExpandEdges(LayoutUnit(2), LayoutUnit(5),
LayoutUnit(0), LayoutUnit(1));
EXPECT_EQ(expected_border_box_rect, table->BorderBoxRect());
// The table's self visual overflow rect covers all collapsed borders, but
// not visual overflows (outlines) from descendants.
LayoutRect expected_self_visual_overflow = table->PhysicalContentBoxRect();
expected_self_visual_overflow.ExpandEdges(LayoutUnit(2), LayoutUnit(10),
LayoutUnit(0), LayoutUnit(10));
EXPECT_EQ(expected_self_visual_overflow, table->SelfVisualOverflowRect());
// For this table, its layout overflow equals self visual overflow.
EXPECT_EQ(expected_self_visual_overflow, table->LayoutOverflowRect());
// The table's visual overflow covers self visual overflow and content visual
// overflows.
LayoutRect expected_visual_overflow = table->PhysicalContentBoxRect();
expected_visual_overflow.ExpandEdges(LayoutUnit(6), LayoutUnit(10),
LayoutUnit(8), LayoutUnit(10));
EXPECT_EQ(expected_visual_overflow, table->VisualOverflowRect());
}
TEST_F(LayoutTableTest, CollapsedBorders) {
SetBodyInnerHTML(
"<style>table { border-collapse: collapse }</style>"
"<table id='table1'"
" style='border-top: hidden; border-bottom: 8px solid;"
" border-left: hidden; border-right: 10px solid'>"
" <tr><td>A</td><td>B</td></tr>"
"</table>"
"<table id='table2' style='border: 10px solid'>"
" <tr>"
" <td style='border: hidden'>C</td>"
" <td style='border: hidden'>D</td>"
" </tr>"
"</table>"
"<table id='table3' style='border: 10px solid'>"
" <tr>"
" <td style='border-top: 15px solid;"
" border-left: 21px solid'>E</td>"
" <td style='border-right: 25px solid'>F</td>"
" </tr>"
// The second row won't affect start and end borders of the table.
" <tr>"
" <td style='border: 30px solid'>G</td>"
" <td style='border: 40px solid'>H</td>"
" </tr>"
"</table>");
auto* table1 = GetTableByElementId("table1");
EXPECT_EQ(0, table1->BorderBefore());
EXPECT_EQ(4, table1->BorderAfter());
EXPECT_EQ(0, table1->BorderStart());
EXPECT_EQ(5, table1->BorderEnd());
// All cells have hidden border.
auto* table2 = GetTableByElementId("table2");
EXPECT_EQ(0, table2->BorderBefore());
EXPECT_EQ(0, table2->BorderAfter());
EXPECT_EQ(0, table2->BorderStart());
EXPECT_EQ(0, table2->BorderEnd());
// Cells have wider borders.
auto* table3 = GetTableByElementId("table3");
// Cell E's border-top won.
EXPECT_EQ(7, table3->BorderBefore());
// Cell H's border-bottom won.
EXPECT_EQ(20, table3->BorderAfter());
// Cell E's border-left won.
EXPECT_EQ(10, table3->BorderStart());
// Cell F's border-bottom won.
EXPECT_EQ(13, table3->BorderEnd());
}
TEST_F(LayoutTableTest, CollapsedBordersWithCol) {
SetBodyInnerHTML(R"HTML(
<style>table { border-collapse: collapse }</style>
<table id='table1' style='border: hidden'>
<colgroup>
<col span='2000' style='border: 10px solid'>
<col span='2000' style='border: 20px solid'>
</colgroup>
<tr>
<td colspan='2000'>A</td>
<td colspan='2000'>B</td>
</tr>
</table>
<table id='table2' style='border: 10px solid'>
<colgroup>
<col span='2000' style='border: 10px solid'>
<col span='2000' style='border: 20px solid'>
</colgroup>
<tr>
<td colspan='2000' style='border: hidden'>C</td>
<td colspan='2000' style='border: hidden'>D</td>
</tr>
</table>
<table id='table3'>
<colgroup>
<col span='2000' style='border: 10px solid'>
<col span='2000' style='border: 20px solid'>
</colgroup>
<tr>
<td colspan='2000' style='border: 12px solid'>E</td>
<td colspan='2000' style='border: 16px solid'>F</td>
</tr>
</table>
)HTML");
// Table has hidden border.
auto* table1 = GetTableByElementId("table1");
EXPECT_EQ(0, table1->BorderBefore());
EXPECT_EQ(0, table1->BorderAfter());
EXPECT_EQ(0, table1->BorderStart());
EXPECT_EQ(0, table1->BorderEnd());
// All cells have hidden border.
auto* table2 = GetTableByElementId("table2");
EXPECT_EQ(0, table2->BorderBefore());
EXPECT_EQ(0, table2->BorderAfter());
EXPECT_EQ(0, table2->BorderStart());
EXPECT_EQ(0, table2->BorderEnd());
// Combined cell and col borders.
auto* table3 = GetTableByElementId("table3");
// The second col's border-top won.
EXPECT_EQ(10, table3->BorderBefore());
// The second col's border-bottom won.
EXPECT_EQ(10, table3->BorderAfter());
// Cell E's border-left won.
EXPECT_EQ(6, table3->BorderStart());
// The second col's border-right won.
EXPECT_EQ(10, table3->BorderEnd());
}
TEST_F(LayoutTableTest, WidthPercentagesExceedHundred) {
SetBodyInnerHTML(R"HTML(
<style>#outer { width: 2000000px; }
table { border-collapse: collapse; }</style>
<div id='outer'>
<table id='onlyTable'>
<tr>
<td width='100%'>
<div></div>
</td>
<td width='60%'>
<div width='10px;'></div>
</td>
</tr>
</table>
</div>
)HTML");
// Table width should be TableLayoutAlgorithm::kMaxTableWidth
auto* table = GetTableByElementId("onlyTable");
EXPECT_EQ(1000000, table->OffsetWidth());
}
TEST_F(LayoutTableTest, CloseToMaxWidth) {
SetBodyInnerHTML(R"HTML(
<style>#outer { width: 2000000px; }
table { border-collapse: collapse; }</style>
<div id='outer'>
<table id='onlyTable' width='999999px;'>
<tr>
<td>
<div></div>
</td>
</tr>
</table>
</div>
)HTML");
// Table width should be 999999
auto* table = GetTableByElementId("onlyTable");
EXPECT_EQ(999999, table->OffsetWidth());
}
TEST_F(LayoutTableTest, PaddingWithCollapsedBorder) {
SetBodyInnerHTML(R"HTML(
<table id='table' style='padding: 20px; border-collapse: collapse'>
<tr><td>TD</td</tr>
</table>
)HTML");
auto* table = GetTableByElementId("table");
EXPECT_EQ(0, table->PaddingLeft());
EXPECT_EQ(0, table->PaddingRight());
EXPECT_EQ(0, table->PaddingTop());
EXPECT_EQ(0, table->PaddingBottom());
EXPECT_EQ(0, table->PaddingStart());
EXPECT_EQ(0, table->PaddingEnd());
EXPECT_EQ(0, table->PaddingBefore());
EXPECT_EQ(0, table->PaddingAfter());
EXPECT_EQ(0, table->PaddingOver());
EXPECT_EQ(0, table->PaddingUnder());
}
TEST_F(LayoutTableTest, OutOfOrderHeadAndBody) {
SetBodyInnerHTML(R"HTML(
<table id='table' style='border-collapse: collapse'>
<tbody id='body'><tr><td>Body</td></tr></tbody>
<thead id='head'></thead>
<table>
)HTML");
auto* table = GetTableByElementId("table");
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("head")),
table->TopSection());
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("body")),
table->TopNonEmptySection());
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("body")),
table->BottomSection());
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("body")),
table->BottomNonEmptySection());
}
TEST_F(LayoutTableTest, OutOfOrderFootAndBody) {
SetBodyInnerHTML(R"HTML(
<table id='table'>
<tfoot id='foot'></tfoot>
<tbody id='body'><tr><td>Body</td></tr></tbody>
<table>
)HTML");
auto* table = GetTableByElementId("table");
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("body")),
table->TopSection());
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("body")),
table->TopNonEmptySection());
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("foot")),
table->BottomSection());
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("body")),
table->BottomNonEmptySection());
}
TEST_F(LayoutTableTest, OutOfOrderHeadFootAndBody) {
SetBodyInnerHTML(R"HTML(
<table id='table' style='border-collapse: collapse'>
<tfoot id='foot'><tr><td>foot</td></tr></tfoot>
<thead id='head'><tr><td>head</td></tr></thead>
<tbody id='body'><tr><td>Body</td></tr></tbody>
<table>
)HTML");
auto* table = GetTableByElementId("table");
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("head")),
table->TopSection());
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("head")),
table->TopNonEmptySection());
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("foot")),
table->BottomSection());
EXPECT_EQ(ToLayoutTableSection(GetLayoutObjectByElementId("foot")),
table->BottomNonEmptySection());
}
} // anonymous namespace
} // namespace blink