Commit 75568a32 authored by Kurt Catti-Schmidt's avatar Kurt Catti-Schmidt Committed by Commit Bot

[GridNG] Setting up measuring stage of NGGridLayoutAlgorithm

This change adds some important helper classes and plumbing for
the measuring stage of grid layout. It doesn't actually do any
measuring yet, which will come in my next change. This CL was getting
large enough on its own to put into review and the actual measuring
will add enough interesting test cases to warrant its own CL.

NGGridChildIterator is more or less a copy of NGFlexChildIterator
with some flex-specific items removed.

NGGridLayoutAlgorithmTest allows us to test the internal state of the
algorithm data without actually laying out or rendering the final
grid. This will be especially helpful for development early on, as we
have a few more stages to implement before we can reasonably test the
final grid layout via layout dump tests.

Bug: 1045599
Change-Id: Id60ad777beddb387b1669b4d4be8760777e29ec8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2229614
Commit-Queue: Kurt Catti-Schmidt <kschmi@microsoft.com>
Reviewed-by: default avatarChristian Biesinger <cbiesinger@chromium.org>
Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#776643}
parent 1d8ea385
...@@ -1229,6 +1229,8 @@ jumbo_source_set("unit_tests") { ...@@ -1229,6 +1229,8 @@ jumbo_source_set("unit_tests") {
"layout/ng/exclusions/ng_exclusion_space_test.cc", "layout/ng/exclusions/ng_exclusion_space_test.cc",
"layout/ng/geometry/ng_box_strut_test.cc", "layout/ng/geometry/ng_box_strut_test.cc",
"layout/ng/geometry/ng_static_position_test.cc", "layout/ng/geometry/ng_static_position_test.cc",
"layout/ng/grid/ng_grid_child_iterator_test.cc",
"layout/ng/grid/ng_grid_layout_algorithm_test.cc",
"layout/ng/grid/ng_grid_track_collection_test.cc", "layout/ng/grid/ng_grid_track_collection_test.cc",
"layout/ng/inline/layout_ng_text_test.cc", "layout/ng/inline/layout_ng_text_test.cc",
"layout/ng/inline/ng_bidi_paragraph_test.cc", "layout/ng/inline/ng_bidi_paragraph_test.cc",
......
...@@ -330,6 +330,8 @@ blink_core_sources("layout") { ...@@ -330,6 +330,8 @@ blink_core_sources("layout") {
"ng/geometry/ng_static_position.h", "ng/geometry/ng_static_position.h",
"ng/grid/layout_ng_grid.cc", "ng/grid/layout_ng_grid.cc",
"ng/grid/layout_ng_grid.h", "ng/grid/layout_ng_grid.h",
"ng/grid/ng_grid_child_iterator.cc",
"ng/grid/ng_grid_child_iterator.h",
"ng/grid/ng_grid_layout_algorithm.cc", "ng/grid/ng_grid_layout_algorithm.cc",
"ng/grid/ng_grid_layout_algorithm.h", "ng/grid/ng_grid_layout_algorithm.h",
"ng/grid/ng_grid_track_collection.cc", "ng/grid/ng_grid_track_collection.cc",
......
// Copyright 2020 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/grid/ng_grid_child_iterator.h"
namespace blink {
NGGridChildIterator::NGGridChildIterator(const NGBlockNode node) {
Setup(node);
}
void NGGridChildIterator::Setup(const NGBlockNode node) {
const int initial_order = ComputedStyleInitialValues::InitialOrder();
bool needs_sort = false;
// Collect all our children, and order them by either their order property.
for (NGLayoutInputNode child = node.FirstChild(); child;
child = child.NextSibling()) {
int order = child.Style().Order();
needs_sort |= order != initial_order;
children_.emplace_back(To<NGBlockNode>(child), order);
}
// We only need to sort this vector if we encountered a non-initial order
// property.
if (needs_sort) {
std::stable_sort(children_.begin(), children_.end(),
[](const ChildWithOrder& c1, const ChildWithOrder& c2) {
return c1.order < c2.order;
});
}
iterator_ = children_.begin();
}
} // namespace blink
// Copyright 2020 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_CHILD_ITERATOR_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_CHILD_ITERATOR_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
// A utility class which given the current grid node will iterate through its
// children.
//
// TODO(layout-dev): Once LayoutNG supports NG-fragmentation this will need
// to be updated to accept a break-token.
//
// This class does not handle modifications to its arguments after it has been
// constructed.
class CORE_EXPORT NGGridChildIterator {
STACK_ALLOCATED();
public:
explicit NGGridChildIterator(const NGBlockNode node);
// Returns the next block node which should be laid out.
NGBlockNode NextChild() {
if (iterator_ == children_.end())
return nullptr;
return (*iterator_++).child;
}
struct ChildWithOrder {
DISALLOW_NEW();
ChildWithOrder(NGBlockNode child, int order) : child(child), order(order) {}
NGBlockNode child;
int order;
};
protected:
virtual void Setup(const NGBlockNode node);
Vector<ChildWithOrder, 4> children_;
Vector<ChildWithOrder, 4>::const_iterator iterator_;
};
} // namespace blink
WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(
blink::NGGridChildIterator::ChildWithOrder)
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_CHILD_ITERATOR_H_
// Copyright 2020 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/grid/ng_grid_child_iterator.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
namespace blink {
namespace {
TEST_F(NGLayoutTest, TestNGGridChildIterator) {
SetBodyInnerHTML(R"HTML(
<div id="parent" style="display: grid">
<div id="child1">Child 1</div>
<div id="child2">Child 2</div>
<div id="child3">Child 3</div>
<div id="child4">Child 4</div>
</div>
)HTML");
NGBlockNode parent_block(ToLayoutBox(GetLayoutObjectByElementId("parent")));
int index = 0;
NGGridChildIterator iterator(parent_block);
for (NGBlockNode child = iterator.NextChild(); child;
child = iterator.NextChild()) {
StringBuilder cell_id;
cell_id.Append("child");
cell_id.Append(AtomicString::Number(++index));
NGBlockNode cell_block(ToLayoutBox(
GetLayoutObjectByElementId(cell_id.ToString().Ascii().c_str())));
EXPECT_EQ(child, cell_block);
}
EXPECT_EQ(index, 4);
}
TEST_F(NGLayoutTest, TestNGGridChildIteratorWithOrderReversed) {
SetBodyInnerHTML(R"HTML(
<div id="parent" style="display: grid">
<div id="child1" style="order: 4">Child 1</div>
<div id="child2" style="order: 3">Child 2</div>
<div id="child3" style="order: 2">Child 3</div>
<div id="child4" style="order: 1">Child 4</div>
</div>
)HTML");
NGBlockNode parent_block(ToLayoutBox(GetLayoutObjectByElementId("parent")));
int index = 4;
NGGridChildIterator iterator(parent_block);
for (NGBlockNode child = iterator.NextChild(); child;
child = iterator.NextChild()) {
StringBuilder cell_id;
cell_id.Append("child");
cell_id.Append(AtomicString::Number(index));
NGBlockNode cell_block(ToLayoutBox(
GetLayoutObjectByElementId(cell_id.ToString().Ascii().c_str())));
EXPECT_EQ(child, cell_block);
--index;
}
EXPECT_EQ(index, 0);
}
TEST_F(NGLayoutTest, TestNGGridChildIteratorWithOrderMixed) {
SetBodyInnerHTML(R"HTML(
<div id="parent" style="display: grid">
<div id="child1" style="order: 3">Child 1</div>
<div id="child2" style="order: 3">Child 2</div>
<div id="child3" style="order: -1">Child 3</div>
<div id="child4" style="order: 0">Child 4</div>
</div>
)HTML");
NGBlockNode parent_block(ToLayoutBox(GetLayoutObjectByElementId("parent")));
int expected_order[] = {3, 4, 1, 2};
int index = 0;
NGGridChildIterator iterator(parent_block);
for (NGBlockNode child = iterator.NextChild(); child;
child = iterator.NextChild()) {
StringBuilder cell_id;
cell_id.Append("child");
cell_id.Append(AtomicString::Number(expected_order[index]));
NGBlockNode cell_block(ToLayoutBox(
GetLayoutObjectByElementId(cell_id.ToString().Ascii().c_str())));
EXPECT_EQ(child, cell_block);
++index;
}
EXPECT_EQ(index, 4);
}
} // namespace
} // namespace blink
...@@ -4,18 +4,42 @@ ...@@ -4,18 +4,42 @@
#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
namespace blink { namespace blink {
NGGridLayoutAlgorithm::NGGridLayoutAlgorithm( NGGridLayoutAlgorithm::NGGridLayoutAlgorithm(
const NGLayoutAlgorithmParams& params) const NGLayoutAlgorithmParams& params)
: NGLayoutAlgorithm(params) { : NGLayoutAlgorithm(params),
state_(GridLayoutAlgorithmState::kMeasuringItems),
border_padding_(params.fragment_geometry.border +
params.fragment_geometry.padding),
border_scrollbar_padding_(border_padding_ +
params.fragment_geometry.scrollbar) {
DCHECK(params.space.IsNewFormattingContext()); DCHECK(params.space.IsNewFormattingContext());
DCHECK(!params.break_token); DCHECK(!params.break_token);
container_builder_.SetIsNewFormattingContext(true); container_builder_.SetIsNewFormattingContext(true);
container_builder_.SetInitialFragmentGeometry(params.fragment_geometry); container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
border_box_size_ = container_builder_.InitialBorderBoxSize();
content_box_size_ =
ShrinkAvailableSize(border_box_size_, border_scrollbar_padding_);
child_percentage_size_ = CalculateChildPercentageSize(
ConstraintSpace(), Node(), content_box_size_);
} }
scoped_refptr<const NGLayoutResult> NGGridLayoutAlgorithm::Layout() { scoped_refptr<const NGLayoutResult> NGGridLayoutAlgorithm::Layout() {
switch (state_) {
case GridLayoutAlgorithmState::kMeasuringItems:
ConstructAndAppendGridItems();
break;
default:
break;
}
return container_builder_.ToBoxFragment(); return container_builder_.ToBoxFragment();
} }
...@@ -24,4 +48,36 @@ MinMaxSizesResult NGGridLayoutAlgorithm::ComputeMinMaxSizes( ...@@ -24,4 +48,36 @@ MinMaxSizesResult NGGridLayoutAlgorithm::ComputeMinMaxSizes(
return {MinMaxSizes(), /* depends_on_percentage_block_size */ true}; return {MinMaxSizes(), /* depends_on_percentage_block_size */ true};
} }
void NGGridLayoutAlgorithm::ConstructAndAppendGridItems() {
NGGridChildIterator iterator(Node());
for (NGBlockNode child = iterator.NextChild(); child;
child = iterator.NextChild()) {
ConstructAndAppendGridItem(child);
}
}
void NGGridLayoutAlgorithm::ConstructAndAppendGridItem(
const NGBlockNode& node) {
GridItem item;
item.constraint_space = BuildSpaceForMeasure(node);
items_.emplace_back(item);
}
NGConstraintSpace NGGridLayoutAlgorithm::BuildSpaceForMeasure(
const NGBlockNode& grid_item) {
const ComputedStyle& child_style = grid_item.Style();
NGConstraintSpaceBuilder space_builder(ConstraintSpace(),
child_style.GetWritingMode(),
/* is_new_fc */ true);
space_builder.SetCacheSlot(NGCacheSlot::kMeasure);
space_builder.SetIsPaintedAtomically(true);
// TODO(kschmi) - do layout/measuring and handle non-fixed sizes here.
space_builder.SetAvailableSize(content_box_size_);
space_builder.SetPercentageResolutionSize(child_percentage_size_);
space_builder.SetTextDirection(child_style.Direction());
return space_builder.ToConstraintSpace();
}
} // namespace blink } // namespace blink
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink { namespace blink {
...@@ -21,6 +23,30 @@ class CORE_EXPORT NGGridLayoutAlgorithm ...@@ -21,6 +23,30 @@ class CORE_EXPORT NGGridLayoutAlgorithm
scoped_refptr<const NGLayoutResult> Layout() override; scoped_refptr<const NGLayoutResult> Layout() override;
MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesInput&) const override; MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesInput&) const override;
private:
friend class NGGridLayoutAlgorithmTest;
void ConstructAndAppendGridItems();
void ConstructAndAppendGridItem(const NGBlockNode& node);
NGConstraintSpace BuildSpaceForMeasure(const NGBlockNode& grid_item);
enum class GridLayoutAlgorithmState {
kMeasuringItems,
};
GridLayoutAlgorithmState state_;
struct GridItem {
NGConstraintSpace constraint_space;
};
Vector<GridItem> items_;
LogicalSize border_box_size_;
LogicalSize content_box_size_;
LogicalSize child_percentage_size_;
const NGBoxStrut border_padding_;
const NGBoxStrut border_scrollbar_padding_;
}; };
} // namespace blink } // namespace blink
......
// Copyright 2020 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/grid/ng_grid_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
class NGGridLayoutAlgorithmTest
: public NGBaseLayoutAlgorithmTest,
private ScopedLayoutNGGridForTest,
private ScopedLayoutNGBlockFragmentationForTest {
protected:
NGGridLayoutAlgorithmTest()
: ScopedLayoutNGGridForTest(true),
ScopedLayoutNGBlockFragmentationForTest(true) {}
void SetUp() override {
NGBaseLayoutAlgorithmTest::SetUp();
style_ = ComputedStyle::Create();
}
// Helper methods to access private data on NGGridLayoutAlgorithm. This class
// is a friend of NGGridLayoutAlgorithm but the individual tests are not.
size_t GridItemSize(NGGridLayoutAlgorithm& algorithm) {
return algorithm.items_.size();
}
Vector<NGConstraintSpace> GridItemConstraintSpaces(
NGGridLayoutAlgorithm& algorithm) {
Vector<NGConstraintSpace> constraint_spaces;
for (auto& item : algorithm.items_) {
constraint_spaces.push_back(NGConstraintSpace(item.constraint_space));
}
return constraint_spaces;
}
scoped_refptr<ComputedStyle> style_;
};
TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmMeasuring) {
if (!RuntimeEnabledFeatures::LayoutNGGridEnabled())
return;
SetBodyInnerHTML(R"HTML(
<style>
#grid1 {
display: grid;
grid-template-columns: 100px 100px;
grid-template-rows: 100px 100px;
}
#cell1 {
grid-column: 1;
grid-row: 1;
width: 50px;
}
#cell2 {
grid-column: 2;
grid-row: 1;
width: 50px;
}
#cell3 {
grid-column: 1;
grid-row: 2;
width: 50px;
}
#cell4 {
grid-column: 2;
grid-row: 2;
width: 50px;
}
</style>
<div id="grid1">
<div id="cell1">Cell 1</div>
<div id="cell2">Cell 2</div>
<div id="cell3">Cell 3</div>
<div id="cell4">Cell 4</div>
</div>
)HTML");
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("grid1")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
LogicalSize(LayoutUnit(100), LayoutUnit(100)), false, true);
NGFragmentGeometry fragment_geometry =
CalculateInitialFragmentGeometry(space, node);
NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
EXPECT_EQ(GridItemSize(algorithm), 0U);
algorithm.Layout();
EXPECT_EQ(GridItemSize(algorithm), 4U);
Vector<NGConstraintSpace> constraint_spaces =
GridItemConstraintSpaces(algorithm);
EXPECT_EQ(GridItemSize(algorithm), constraint_spaces.size());
for (auto& constraint_space : constraint_spaces) {
EXPECT_EQ(constraint_space.AvailableSize().inline_size.ToInt(), 100);
}
}
} // namespace blink
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