blob: 65bdebf1006bf7d9493f5c9123543a058ab92b96 [file] [log] [blame]
// Copyright 2018 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/grid.h"
#include "third_party/blink/renderer/core/layout/layout_grid.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
namespace blink {
namespace {
class GridTest : public RenderingTest {
protected:
LayoutGrid* GetGridByElementId(const char* id) {
return ToLayoutGrid(GetLayoutObjectByElementId(id));
}
};
TEST_F(GridTest, EmptyGrid) {
SetBodyInnerHTML(R"HTML(
<style>
.grid { display: grid; }
</style>
<div id=target class=grid>
</div>
)HTML");
auto* layout_grid = GetGridByElementId("target");
auto* grid = layout_grid->InternalGrid();
ASSERT_NE(grid, nullptr);
EXPECT_EQ(0u, grid->NumTracks(kForRows));
EXPECT_EQ(0u, grid->NumTracks(kForColumns));
EXPECT_FALSE(grid->HasGridItems());
EXPECT_EQ(0, grid->SmallestTrackStart(kForRows));
EXPECT_EQ(0, grid->SmallestTrackStart(kForColumns));
EXPECT_EQ(0u, grid->AutoRepeatTracks(kForRows));
EXPECT_EQ(0u, grid->AutoRepeatTracks(kForColumns));
EXPECT_FALSE(grid->HasAutoRepeatEmptyTracks(kForRows));
EXPECT_FALSE(grid->HasAutoRepeatEmptyTracks(kForColumns));
}
TEST_F(GridTest, SingleChild) {
SetBodyInnerHTML(R"HTML(
<style>
.grid { display: grid; }
</style>
<div id=target class=grid>
<div id=child></div>
</div>
)HTML");
auto* layout_grid = GetGridByElementId("target");
auto* grid = layout_grid->InternalGrid();
ASSERT_NE(grid, nullptr);
auto* child = ToLayoutBox(GetLayoutObjectByElementId("child"));
ASSERT_NE(child, nullptr);
EXPECT_EQ(1u, grid->NumTracks(kForRows));
EXPECT_EQ(1u, grid->NumTracks(kForColumns));
EXPECT_TRUE(grid->HasGridItems());
EXPECT_EQ(0, grid->SmallestTrackStart(kForRows));
EXPECT_EQ(0, grid->SmallestTrackStart(kForColumns));
auto area = grid->GridItemArea(*child);
EXPECT_EQ(0u, area.columns.StartLine());
EXPECT_EQ(1u, area.columns.EndLine());
EXPECT_EQ(0u, area.rows.StartLine());
EXPECT_EQ(1u, area.rows.EndLine());
}
TEST_F(GridTest, OverlappingChildren) {
SetBodyInnerHTML(R"HTML(
<style>
.grid { display: grid; grid-template: repeat(3, 20px) / repeat(3, 20px); }
#child1 { grid-row: 1 / 3; grid-column: 1 / 3 }
#child2 { grid-row: 1 / 3; grid-column: 2 / 4 }
#child3 { grid-row: 2 / 4; grid-column: 1 / 3 }
#child4 { grid-row: 2 / 4; grid-column: 2 / 4 }
</style>
<div id=target class=grid>
<div id=child1></div>
<div id=child2></div>
<div id=child3></div>
<div id=child4></div>
</div>
)HTML");
auto* layout_grid = GetGridByElementId("target");
auto* grid = layout_grid->InternalGrid();
ASSERT_NE(grid, nullptr);
size_t num_rows = grid->NumTracks(kForRows);
size_t num_cols = grid->NumTracks(kForColumns);
EXPECT_EQ(3u, num_rows);
EXPECT_EQ(3u, num_cols);
EXPECT_TRUE(grid->HasGridItems());
size_t index = 0;
Vector<size_t> expected_items_per_cell = {1, 2, 1, 2, 4, 2, 1, 2, 1};
for (size_t row = 0; row < num_rows; ++row) {
for (size_t col = 0; col < num_cols; ++col)
EXPECT_EQ(expected_items_per_cell[index++], grid->Cell(row, col).size());
}
}
TEST_F(GridTest, PartiallyOverlappingChildren) {
SetBodyInnerHTML(R"HTML(
<style>
.grid { display: grid; grid-template: repeat(1, 20px) / repeat(3, 20px); }
#child1 { grid-row: 1; grid-column: 1; }
#child2 { grid-row: 1; grid-column: 3; }
#child3 { grid-row: 1; grid-column: 1 / 3 }
</style>
<div id=target class=grid>
<div id=child1></div>
<div id=child2></div>
<div id=child3></div>
</div>
)HTML");
auto* layout_grid = GetGridByElementId("target");
auto* grid = layout_grid->InternalGrid();
ASSERT_NE(grid, nullptr);
size_t num_rows = grid->NumTracks(kForRows);
size_t num_cols = grid->NumTracks(kForColumns);
EXPECT_EQ(1u, num_rows);
EXPECT_EQ(3u, num_cols);
EXPECT_TRUE(grid->HasGridItems());
size_t index = 0;
Vector<size_t> expected_items_per_cell = {2, 1, 1};
for (size_t col = 0; col < num_cols; ++col)
EXPECT_EQ(expected_items_per_cell[index++], grid->Cell(0, col).size());
}
TEST_F(GridTest, IntrinsicGrid) {
SetBodyInnerHTML(R"HTML(
<style>
.grid { display: grid; grid-template-rows: repeat(2, 10px); }
#child1 { grid-row: -1 / -5; }
#child2 { grid-row: 3 / span 4; }
</style>
<div id=target class=grid>
<div id=child1></div>
<div id=child2></div>
</div>
)HTML");
auto* layout_grid = GetGridByElementId("target");
auto* grid = layout_grid->InternalGrid();
ASSERT_NE(grid, nullptr);
auto* child1 = ToLayoutBox(GetLayoutObjectByElementId("child1"));
ASSERT_NE(child1, nullptr);
auto* child2 = ToLayoutBox(GetLayoutObjectByElementId("child2"));
ASSERT_NE(child2, nullptr);
EXPECT_EQ(8u, grid->NumTracks(kForRows));
EXPECT_EQ(1u, grid->NumTracks(kForColumns));
EXPECT_TRUE(grid->HasGridItems());
EXPECT_EQ(-2, grid->SmallestTrackStart(kForRows));
EXPECT_EQ(0, grid->SmallestTrackStart(kForColumns));
auto area = grid->GridItemArea(*child1);
EXPECT_EQ(0u, area.columns.StartLine());
EXPECT_EQ(1u, area.columns.EndLine());
EXPECT_EQ(0u, area.rows.StartLine());
EXPECT_EQ(4u, area.rows.EndLine());
area = grid->GridItemArea(*child2);
EXPECT_EQ(0u, area.columns.StartLine());
EXPECT_EQ(1u, area.columns.EndLine());
EXPECT_EQ(4u, area.rows.StartLine());
EXPECT_EQ(8u, area.rows.EndLine());
}
TEST_F(GridTest, AutoFit) {
SetBodyInnerHTML(R"HTML(
<style>
.grid { display: grid; width: 100px; grid-template-columns: repeat(auto-fit, 10px); }
#child { grid-column: 2 / 6; }
#child2 { grid-column: 9; }
</style>
<div id=target class=grid>
<div id=child></div>
<div id=child2></div>
</div>
)HTML");
auto* layout_grid = GetGridByElementId("target");
auto* grid = layout_grid->InternalGrid();
ASSERT_NE(grid, nullptr);
EXPECT_EQ(1u, grid->NumTracks(kForRows));
EXPECT_EQ(10u, grid->NumTracks(kForColumns));
EXPECT_TRUE(grid->HasGridItems());
EXPECT_EQ(0u, grid->AutoRepeatTracks(kForRows));
EXPECT_EQ(10u, grid->AutoRepeatTracks(kForColumns));
EXPECT_FALSE(grid->HasAutoRepeatEmptyTracks(kForRows));
EXPECT_TRUE(grid->HasAutoRepeatEmptyTracks(kForColumns));
auto* empty_tracks = grid->AutoRepeatEmptyTracks(kForColumns);
ASSERT_NE(empty_tracks, nullptr);
ASSERT_EQ(empty_tracks->size(), 5u);
Vector<size_t> expected_empty_tracks = {0, 5, 6, 7, 9};
size_t index = 0;
for (auto track : *empty_tracks) {
EXPECT_EQ(expected_empty_tracks[index++], track);
EXPECT_TRUE(grid->IsEmptyAutoRepeatTrack(kForColumns, track));
}
}
TEST_F(GridTest, AutoFill) {
SetBodyInnerHTML(R"HTML(
<style>
.grid { display: grid; width: 100px; grid-template-columns: repeat(auto-fill, 10px); }
#child { grid-column: 2 / 6; }
#child2 { grid-column: 9; }
</style>
<div id=target class=grid>
<div id=child></div>
<div id=child2></div>
</div>
)HTML");
auto* layout_grid = GetGridByElementId("target");
auto* grid = layout_grid->InternalGrid();
ASSERT_NE(grid, nullptr);
EXPECT_EQ(1u, grid->NumTracks(kForRows));
EXPECT_EQ(10u, grid->NumTracks(kForColumns));
EXPECT_TRUE(grid->HasGridItems());
EXPECT_EQ(0u, grid->AutoRepeatTracks(kForRows));
EXPECT_EQ(10u, grid->AutoRepeatTracks(kForColumns));
EXPECT_FALSE(grid->HasAutoRepeatEmptyTracks(kForRows));
EXPECT_FALSE(grid->HasAutoRepeatEmptyTracks(kForColumns));
}
TEST_F(GridTest, AutoPositionedItems) {
SetBodyInnerHTML(R"HTML(
<style>
.grid { display: grid; grid-template-rows: repeat(3, 10px); grid-auto-flow: column }
.vertical { writing-mode: vertical-rl}
</style>
<div id=target class=grid>
<div></div>
<div class=vertical></div>
<div></div>
<div></div>
</div>
)HTML");
auto* layout_grid = GetGridByElementId("target");
auto* grid = layout_grid->InternalGrid();
ASSERT_NE(grid, nullptr);
EXPECT_EQ(3u, grid->NumTracks(kForRows));
EXPECT_EQ(2u, grid->NumTracks(kForColumns));
EXPECT_TRUE(grid->HasGridItems());
EXPECT_FALSE(grid->NeedsItemsPlacement());
}
TEST_F(GridTest, ExplicitlyPositionedChild) {
SetBodyInnerHTML(R"HTML(
<style>
.grid { display: grid; }
#child { grid-row: 1; grid-column: 2 / span 3; }
</style>
<div id=target class=grid>
<div id=child></div>
</div>
)HTML");
auto* layout_grid = GetGridByElementId("target");
auto* grid = layout_grid->InternalGrid();
ASSERT_NE(grid, nullptr);
auto* child = ToLayoutBox(GetLayoutObjectByElementId("child"));
ASSERT_NE(child, nullptr);
EXPECT_EQ(1u, grid->NumTracks(kForRows));
EXPECT_EQ(4u, grid->NumTracks(kForColumns));
EXPECT_TRUE(grid->HasGridItems());
EXPECT_EQ(0, grid->SmallestTrackStart(kForRows));
EXPECT_EQ(0, grid->SmallestTrackStart(kForColumns));
auto area = grid->GridItemArea(*child);
EXPECT_EQ(1u, area.columns.StartLine());
EXPECT_EQ(4u, area.columns.EndLine());
EXPECT_EQ(0u, area.rows.StartLine());
EXPECT_EQ(1u, area.rows.EndLine());
for (auto row : area.rows) {
for (auto column : area.columns) {
auto& cell = grid->Cell(row, column);
EXPECT_EQ(1u, cell.size());
}
}
}
TEST_F(GridTest, CellInsert) {
auto track = base::WrapUnique(new ListGrid::GridTrack(0, kForColumns));
auto* cell = new ListGrid::GridCell(0, 0);
auto result = track->Insert(cell);
EXPECT_TRUE(result.is_new_entry);
EXPECT_EQ(cell, result.node);
auto* cell2 = new ListGrid::GridCell(1, 0);
result = track->Insert(cell2);
EXPECT_TRUE(result.is_new_entry);
EXPECT_EQ(cell2, result.node);
result = track->InsertAfter(cell2, cell);
EXPECT_FALSE(result.is_new_entry);
EXPECT_EQ(cell2, result.node);
auto* cell3 = new ListGrid::GridCell(2, 0);
result = track->InsertAfter(cell3, cell2);
EXPECT_TRUE(result.is_new_entry);
EXPECT_EQ(cell3, result.node);
}
} // anonymous namespace
} // namespace blink