Commit 427a8f5d authored by Morten Stenshorne's avatar Morten Stenshorne Committed by Commit Bot

[LayoutNG] Use cached fragment if size won't change.

Available size may change from one layout pass to another, but that
doesn't have to mean that we need to re-lay out a child. If we can be
sure that its size will remain the same, and that if it will keep the
same size, that won't affect any descendants, we can skip layout and
return the cached fragment.

We cannot ignore initial containing block size changes if there are
orthogonal flow root children, so keep track of that.

Makes perf_tests/layout/many-block-children-fixed-inline-size.html
about 3 times faster.

Update expectations for one invalidation test. The test has some
fixed-width descendants, so when only the window width changes,
there's less work to do, than before.

Update one unit test, and expand it a bit, to provide a case where we
actually still do need layout.

Bug: 897450
Change-Id: I063f650110d5a488de430b938b3d46e713a1433e
Reviewed-on: https://chromium-review.googlesource.com/c/1297367
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Reviewed-by: default avatarChristian Biesinger <cbiesinger@chromium.org>
Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605168}
parent 7050dca6
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<link rel="help" href="https://www.w3.org/TR/CSS22/box.html#propdef-margin-bottom">
<style>
#container { overflow:hidden; background:blue; }
#container > div { margin-bottom:50%; height:50px; }
</style>
<p>There should be a blue square below.</p>
<div id="container" style="width:456px;" data-expected-width="100" data-expected-height="100">
<div></div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<script>
document.body.offsetTop;
document.getElementById("container").style.width = "100px";
checkLayout("#container");
</script>
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<link rel="help" href="https://www.w3.org/TR/CSS22/box.html#propdef-margin-left">
<style>
#container > div { margin-left:50%; height:100px; background:blue; }
</style>
<p>There should be a blue square below.</p>
<div id="container" style="width:456px;">
<div data-expected-width="100" data-expected-height="100"></div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<script>
document.body.offsetTop;
document.getElementById("container").style.width = "200px";
checkLayout("#container");
</script>
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<link rel="help" href="https://www.w3.org/TR/CSS22/box.html#propdef-margin-right">
<style>
#container > div { margin-right:50%; height:100px; background:blue; }
</style>
<p>There should be a blue square below.</p>
<div id="container" style="width:456px;">
<div data-expected-width="100" data-expected-height="100"></div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<script>
document.body.offsetTop;
document.getElementById("container").style.width = "200px";
checkLayout("#container");
</script>
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<link rel="help" href="https://www.w3.org/TR/CSS22/box.html#propdef-margin-top">
<style>
#container { overflow:hidden; background:blue; }
#container > div { margin-top:50%; height:50px; }
</style>
<p>There should be a blue square below.</p>
<div id="container" style="width:456px;" data-expected-width="100" data-expected-height="100">
<div></div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<script>
document.body.offsetTop;
document.getElementById("container").style.width = "100px";
checkLayout("#container");
</script>
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<link rel="help" href="https://www.w3.org/TR/CSS22/box.html#propdef-padding-bottom">
<style>
#container > div { padding-bottom:10%; width:100px; height:50px; background:blue; }
</style>
<p>There should be a blue square below.</p>
<div id="container" style="width:123px;">
<div data-expected-width="100" data-expected-height="100"></div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<script>
document.body.offsetTop;
document.getElementById("container").style.width = "500px";
checkLayout("#container");
</script>
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<link rel="help" href="https://www.w3.org/TR/CSS22/box.html#propdef-padding-left">
<style>
#container > div { padding-left:10%; width:50px; height:100px; background:blue; }
</style>
<p>There should be a blue square below.</p>
<div id="container" style="width:123px;">
<div data-expected-width="100" data-expected-height="100"></div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<script>
document.body.offsetTop;
document.getElementById("container").style.width = "500px";
checkLayout("#container");
</script>
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<link rel="help" href="https://www.w3.org/TR/CSS22/box.html#propdef-padding-right">
<style>
#container > div { padding-right:10%; width:50px; height:100px; background:blue; }
</style>
<p>There should be a blue square below.</p>
<div id="container" style="width:123px;">
<div data-expected-width="100" data-expected-height="100"></div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<script>
document.body.offsetTop;
document.getElementById("container").style.width = "500px";
checkLayout("#container");
</script>
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<link rel="help" href="https://www.w3.org/TR/CSS22/box.html#propdef-padding-top">
<style>
#container > div { padding-top:10%; width:100px; height:50px; background:blue; }
</style>
<p>There should be a blue square below.</p>
<div id="container" style="width:123px;">
<div data-expected-width="100" data-expected-height="100"></div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<script>
document.body.offsetTop;
document.getElementById("container").style.width = "500px";
checkLayout("#container");
</script>
......@@ -59,16 +59,6 @@
"object": "LayoutNGBlockFlow (positioned) DIV class='container'",
"rect": [0, 0, 400, 250],
"reason": "geometry"
},
{
"object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='parent'",
"rect": [0, 0, 6, 250],
"reason": "geometry"
},
{
"object": "LayoutNGBlockFlow (relative positioned) DIV class='child'",
"rect": [0, 125, 6, 30],
"reason": "geometry"
}
]
}
......@@ -150,16 +140,6 @@
"object": "Scrolling background of LayoutView #document",
"rect": [400, 0, 400, 600],
"reason": "incremental"
},
{
"object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='parent'",
"rect": [0, 0, 6, 600],
"reason": "geometry"
},
{
"object": "LayoutNGBlockFlow (relative positioned) DIV class='child'",
"rect": [0, 300, 6, 30],
"reason": "geometry"
}
]
}
......
......@@ -2539,7 +2539,7 @@ void LayoutBlockFlow::AddLayoutOverflowFromFloats() {
scoped_refptr<NGLayoutResult> LayoutBlockFlow::CachedLayoutResult(
const NGConstraintSpace&,
const NGBreakToken*) const {
const NGBreakToken*) {
return nullptr;
}
......
......@@ -458,7 +458,7 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock {
virtual NGPaintFragment* PaintFragment() const { return nullptr; }
virtual scoped_refptr<NGLayoutResult> CachedLayoutResult(
const NGConstraintSpace&,
const NGBreakToken*) const;
const NGBreakToken*);
virtual scoped_refptr<const NGLayoutResult> CachedLayoutResultForTesting();
virtual void SetCachedLayoutResult(const NGConstraintSpace&,
const NGBreakToken*,
......
......@@ -34,6 +34,9 @@ struct CORE_EXPORT NGPhysicalSize {
bool operator==(const NGPhysicalSize& other) const {
return std::tie(other.width, other.height) == std::tie(width, height);
}
bool operator!=(const NGPhysicalSize& other) const {
return !(*this == other);
}
bool IsEmpty() const {
return width == LayoutUnit() || height == LayoutUnit();
......
......@@ -219,19 +219,14 @@ LayoutUnit LayoutNGMixin<Base>::InlineBlockBaseline(
template <typename Base>
scoped_refptr<NGLayoutResult> LayoutNGMixin<Base>::CachedLayoutResult(
const NGConstraintSpace& constraint_space,
const NGBreakToken* break_token) const {
const NGConstraintSpace& new_space,
const NGBreakToken* break_token) {
if (!RuntimeEnabledFeatures::LayoutNGFragmentCachingEnabled())
return nullptr;
if (!cached_result_ || !Base::cached_constraint_space_ || break_token ||
Base::NeedsLayout())
return nullptr;
if (constraint_space != *Base::cached_constraint_space_)
return nullptr;
// The checks above should be enough to bail if layout is incomplete, but
// let's verify:
DCHECK(
IsBlockLayoutComplete(*Base::cached_constraint_space_, *cached_result_));
const NGConstraintSpace& old_space = *Base::cached_constraint_space_;
// If we used to contain abspos items, we can't reuse the fragment, because
// we can't be sure that the list of items hasn't changed (as we bubble them
// up during layout). In the case of newly-added abspos items to this
......@@ -240,6 +235,32 @@ scoped_refptr<NGLayoutResult> LayoutNGMixin<Base>::CachedLayoutResult(
// TODO(layout-ng): Come up with a better solution for this
if (cached_result_->OutOfFlowPositionedDescendants().size())
return nullptr;
if (!new_space.MaySkipLayout(old_space))
return nullptr;
// We won't attempt to guess how initial containing block changes might affect
// orthogonal flow root descendants. In that case, just bail if the size has
// changed.
if (cached_result_->HasOrthogonalFlowRoots() &&
new_space.InitialContainingBlockSize() !=
old_space.InitialContainingBlockSize())
return nullptr;
if (!new_space.AreSizesEqual(old_space)) {
// We need to descend all the way down into BODY if we're in quirks mode,
// since it magically follows the viewport size.
if (NGBlockNode(this).IsQuirkyAndFillsViewport())
return nullptr;
// If the available / percentage sizes have changed in a way that may affect
// layout, we cannot re-use the previous result.
if (SizeMayChange(Base::StyleRef(), new_space, old_space))
return nullptr;
}
// The checks above should be enough to bail if layout is incomplete, but
// let's verify:
DCHECK(IsBlockLayoutComplete(old_space, *cached_result_));
return base::AdoptRef(new NGLayoutResult(*cached_result_));
}
......
......@@ -58,9 +58,8 @@ class LayoutNGMixin : public Base {
// Returns the last layout result for this block flow with the given
// constraint space and break token, or null if it is not up-to-date or
// otherwise unavailable.
scoped_refptr<NGLayoutResult> CachedLayoutResult(
const NGConstraintSpace&,
const NGBreakToken*) const final;
scoped_refptr<NGLayoutResult> CachedLayoutResult(const NGConstraintSpace&,
const NGBreakToken*) final;
void SetCachedLayoutResult(const NGConstraintSpace&,
const NGBreakToken*,
......
......@@ -2278,11 +2278,7 @@ void NGBlockLayoutAlgorithm::AddPositionedFloats(const Vec& positioned_floats) {
// containing block. Percentage resolution size is minimal size
// that would fill the ICB.
LayoutUnit NGBlockLayoutAlgorithm::CalculateDefaultBlockSize() {
if (!Node().GetDocument().InQuirksMode())
return NGSizeIndefinite;
bool is_quirky_element = Node().IsDocumentElement() || Node().IsBody();
if (is_quirky_element && !Style().HasOutOfFlowPosition()) {
if (Node().IsQuirkyAndFillsViewport()) {
LayoutUnit block_size = ConstraintSpace().AvailableSize().block_size;
block_size -= ComputeMarginsForSelf(ConstraintSpace(), Style()).BlockSum();
return block_size.ClampNegativeToZero();
......
......@@ -95,12 +95,12 @@ TEST_F(NGBlockLayoutAlgorithmTest, Caching) {
ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
SetBodyInnerHTML(R"HTML(
<div id="box" style="width:30px; height:40px"></div>
<div id="box" style="width:30px; height:40%;"></div>
)HTML");
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
NGLogicalSize(LayoutUnit(100), NGSizeIndefinite));
NGLogicalSize(LayoutUnit(100), LayoutUnit(100)));
LayoutBlockFlow* block_flow =
ToLayoutBlockFlow(GetLayoutObjectByElementId("box"));
......@@ -117,14 +117,22 @@ TEST_F(NGBlockLayoutAlgorithmTest, Caching) {
// Test identical, but not pointer-equal, constraint space
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
NGLogicalSize(LayoutUnit(100), NGSizeIndefinite));
NGLogicalSize(LayoutUnit(100), LayoutUnit(100)));
result = block_flow->CachedLayoutResult(space, nullptr);
EXPECT_NE(result.get(), nullptr);
// Test different constraint space
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
NGLogicalSize(LayoutUnit(200), NGSizeIndefinite));
NGLogicalSize(LayoutUnit(200), LayoutUnit(100)));
result = block_flow->CachedLayoutResult(space, nullptr);
EXPECT_NE(result.get(), nullptr);
// Test a different constraint space that will actually result in a different
// size.
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
NGLogicalSize(LayoutUnit(200), LayoutUnit(200)));
result = block_flow->CachedLayoutResult(space, nullptr);
EXPECT_EQ(result.get(), nullptr);
......
......@@ -244,29 +244,10 @@ NGConstraintSpace NGConstraintSpace::CreateFromLayoutObject(
}
bool NGConstraintSpace::operator==(const NGConstraintSpace& other) const {
return available_size_ == other.available_size_ &&
percentage_resolution_size_ == other.percentage_resolution_size_ &&
replaced_percentage_resolution_size_ ==
other.replaced_percentage_resolution_size_ &&
return AreSizesEqual(other) &&
initial_containing_block_size_ ==
other.initial_containing_block_size_ &&
fragmentainer_block_size_ == other.fragmentainer_block_size_ &&
fragmentainer_space_at_bfc_start_ ==
other.fragmentainer_space_at_bfc_start_ &&
block_direction_fragmentation_type_ ==
other.block_direction_fragmentation_type_ &&
table_cell_child_layout_phase_ ==
other.table_cell_child_layout_phase_ &&
flags_ == other.flags_ &&
adjoining_floats_ == other.adjoining_floats_ &&
writing_mode_ == other.writing_mode_ &&
direction_ == other.direction_ &&
margin_strut_ == other.margin_strut_ &&
bfc_offset_ == other.bfc_offset_ &&
floats_bfc_block_offset_ == other.floats_bfc_block_offset_ &&
exclusion_space_ == other.exclusion_space_ &&
clearance_offset_ == other.clearance_offset_ &&
baseline_requests_ == other.baseline_requests_;
MaySkipLayout(other);
}
String NGConstraintSpace::ToString() const {
......
......@@ -284,6 +284,35 @@ class CORE_EXPORT NGConstraintSpace final {
return NGBaselineRequestList(baseline_requests_);
}
// Return true if the two constraint spaces are similar enough that it *may*
// be possible to skip re-layout. If true is returned, the caller is expected
// to verify that any constraint space size (available size, percentage size,
// and so on) changes won't require re-layout, before skipping.
bool MaySkipLayout(const NGConstraintSpace& other) const {
return fragmentainer_block_size_ == other.fragmentainer_block_size_ &&
fragmentainer_space_at_bfc_start_ ==
other.fragmentainer_space_at_bfc_start_ &&
block_direction_fragmentation_type_ ==
other.block_direction_fragmentation_type_ &&
table_cell_child_layout_phase_ ==
other.table_cell_child_layout_phase_ &&
flags_ == other.flags_ &&
adjoining_floats_ == other.adjoining_floats_ &&
writing_mode_ == other.writing_mode_ &&
direction_ == other.direction_ &&
margin_strut_ == other.margin_strut_ &&
bfc_offset_ == other.bfc_offset_ &&
floats_bfc_block_offset_ == other.floats_bfc_block_offset_ &&
exclusion_space_ == other.exclusion_space_ &&
clearance_offset_ == other.clearance_offset_ &&
baseline_requests_ == other.baseline_requests_;
}
bool AreSizesEqual(const NGConstraintSpace& other) const {
return available_size_ == other.available_size_ &&
percentage_resolution_size_ == other.percentage_resolution_size_ &&
replaced_percentage_resolution_size_ ==
other.replaced_percentage_resolution_size_;
}
bool operator==(const NGConstraintSpace&) const;
bool operator!=(const NGConstraintSpace& other) const {
return !(*this == other);
......
......@@ -9,6 +9,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/text/writing_mode.h"
namespace blink {
......@@ -56,6 +57,9 @@ NGContainerFragmentBuilder& NGContainerFragmentBuilder::AddChild(
}
}
if (child.HasOrthogonalFlowRoots())
has_orthogonal_flow_roots_ = true;
return AddChild(child.PhysicalFragment(), child_offset);
}
......@@ -82,6 +86,10 @@ NGContainerFragmentBuilder& NGContainerFragmentBuilder::AddChild(
}
}
if (!IsParallelWritingMode(child->Style().GetWritingMode(),
Style().GetWritingMode()))
has_orthogonal_flow_roots_ = true;
if (!has_last_resort_break_) {
if (const auto* token = child->BreakToken()) {
if (token->IsBlockType() &&
......
......@@ -229,6 +229,8 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
bool is_pushed_by_floats_ = false;
bool has_orthogonal_flow_roots_ = false;
friend class NGPhysicalContainerFragment;
};
......
......@@ -110,6 +110,14 @@ class CORE_EXPORT NGLayoutInputNode {
(box_->IsBody() || box_->IsTableCell());
}
// In quirks mode, in-flow positioned BODY and root elements must completely
// fill the viewport. Return true if this is such a node.
bool IsQuirkyAndFillsViewport() const {
if (!GetDocument().InQuirksMode())
return false;
return (IsDocumentElement() || IsBody()) && !Style().HasOutOfFlowPosition();
}
bool CreatesNewFormattingContext() const {
return IsBlock() && box_->AvoidsFloats();
}
......
......@@ -29,6 +29,7 @@ NGLayoutResult::NGLayoutResult(
has_forced_break_(builder->has_forced_break_),
is_pushed_by_floats_(builder->is_pushed_by_floats_),
adjoining_floats_(builder->adjoining_floats_),
has_orthogonal_flow_roots_(builder->has_orthogonal_flow_roots_),
status_(kSuccess) {
DCHECK(physical_fragment) << "Use the other constructor for aborting layout";
root_fragment_.fragment_ = std::move(physical_fragment);
......@@ -45,6 +46,7 @@ NGLayoutResult::NGLayoutResult(NGLayoutResultStatus status,
has_forced_break_(false),
is_pushed_by_floats_(false),
adjoining_floats_(kFloatTypeNone),
has_orthogonal_flow_roots_(builder->has_orthogonal_flow_roots_),
status_(status) {
DCHECK_NE(status, kSuccess)
<< "Use the other constructor for successful layout";
......@@ -64,6 +66,7 @@ NGLayoutResult::NGLayoutResult(
has_forced_break_(false),
is_pushed_by_floats_(builder->is_pushed_by_floats_),
adjoining_floats_(builder->adjoining_floats_),
has_orthogonal_flow_roots_(builder->has_orthogonal_flow_roots_),
status_(kSuccess) {
root_fragment_.fragment_ = std::move(physical_fragment);
oof_positioned_descendants_ = std::move(builder->oof_positioned_descendants_);
......@@ -87,6 +90,7 @@ NGLayoutResult::NGLayoutResult(const NGLayoutResult& other)
has_forced_break_(other.has_forced_break_),
is_pushed_by_floats_(other.is_pushed_by_floats_),
adjoining_floats_(other.adjoining_floats_),
has_orthogonal_flow_roots_(other.has_orthogonal_flow_roots_),
status_(other.status_) {}
// Define the destructor here, so that we can forward-declare more in the
......
......@@ -113,6 +113,8 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
// the block, and the block will fail to clear).
NGFloatTypes AdjoiningFloatTypes() const { return adjoining_floats_; }
bool HasOrthogonalFlowRoots() const { return has_orthogonal_flow_roots_; }
private:
friend class NGBoxFragmentBuilder;
friend class NGLineBoxFragmentBuilder;
......@@ -147,6 +149,8 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
unsigned is_pushed_by_floats_ : 1;
unsigned adjoining_floats_ : 2; // NGFloatTypes
unsigned has_orthogonal_flow_roots_ : 1;
unsigned status_ : 1;
};
......
......@@ -51,6 +51,49 @@ inline EBlockAlignment BlockAlignment(const ComputedStyle& style,
}
}
inline bool InlineLengthMayChange(Length length,
const NGConstraintSpace& new_space,
const NGConstraintSpace& old_space) {
// Percentage inline margins will affect the size if the size is unspecified
// (auto and similar). So we need to check both available size and the
// percentage resolution size in that case.
bool is_unspecified =
length.IsAuto() || length.IsFitContent() || length.IsFillAvailable();
if (is_unspecified) {
if (new_space.AvailableSize().inline_size !=
old_space.AvailableSize().inline_size)
return true;
}
if (is_unspecified || length.IsPercentOrCalc()) {
if (new_space.PercentageResolutionSize().inline_size !=
old_space.PercentageResolutionSize().inline_size)
return true;
}
return false;
}
inline bool BlockLengthMayChange(Length length,
const NGConstraintSpace& new_space,
const NGConstraintSpace& old_space) {
if (length.IsFillAvailable()) {
if (new_space.AvailableSize().block_size !=
old_space.AvailableSize().block_size)
return true;
} else if (length.IsAuto() || length.IsPercentOrCalc()) {
// Note that we check percentage resolution changes for 'auto' values here
// (in addition to percent values). The reason is that percentage resolution
// block sizes may be passed through auto-sized blocks, in some cases,
// e.g. for anonymous blocks, and also in quirks mode.
if (new_space.PercentageResolutionSize().block_size !=
old_space.PercentageResolutionSize().block_size)
return true;
if (new_space.ReplacedPercentageResolutionSize().block_size !=
old_space.ReplacedPercentageResolutionSize().block_size)
return true;
}
return false;
}
} // anonymous namespace
bool NeedMinMaxSizeForContentContribution(WritingMode mode,
......@@ -617,6 +660,58 @@ NGLogicalSize ComputeReplacedSize(
return replaced_size;
}
bool SizeMayChange(const ComputedStyle& style,
const NGConstraintSpace& new_space,
const NGConstraintSpace& old_space) {
DCHECK_EQ(new_space.IsFixedSizeInline(), old_space.IsFixedSizeInline());
DCHECK_EQ(new_space.IsFixedSizeBlock(), old_space.IsFixedSizeBlock());
// Go through all length properties, and, depending on length type
// (percentages, auto, etc.), check whether the constraint spaces differ in
// such a way that the resulting size *may* change. There are currently many
// possible false-positive situations here, as we don't rule out length
// changes that won't have any effect on the final size (e.g. if inline-size
// is 100px, max-inline-size is 50%, and percentage resolution inline size
// changes from 1000px to 500px). If the constraint space has "fixed" size in
// a dimension, we can skip checking properties in that dimension and just
// look for available size changes, since that's how a "fixed" constraint
// space works.
if (new_space.IsFixedSizeInline()) {
if (new_space.AvailableSize().inline_size !=
old_space.AvailableSize().inline_size)
return true;
} else {
if (InlineLengthMayChange(style.LogicalWidth(), new_space, old_space) ||
InlineLengthMayChange(style.LogicalMaxWidth(), new_space, old_space) ||
InlineLengthMayChange(style.LogicalMaxWidth(), new_space, old_space))
return true;
}
if (new_space.IsFixedSizeBlock()) {
if (new_space.AvailableSize().block_size !=
old_space.AvailableSize().block_size)
return true;
} else {
if (BlockLengthMayChange(style.LogicalHeight(), new_space, old_space) ||
BlockLengthMayChange(style.LogicalMinHeight(), new_space, old_space) ||
BlockLengthMayChange(style.LogicalMaxHeight(), new_space, old_space))
return true;
}
if (new_space.PercentageResolutionSize().inline_size !=
old_space.PercentageResolutionSize().inline_size) {
// Percentage-based padding is resolved against the inline content box size
// of the containing block.
if (style.PaddingTop().IsPercentOrCalc() ||
style.PaddingRight().IsPercentOrCalc() ||
style.PaddingBottom().IsPercentOrCalc() ||
style.PaddingLeft().IsPercentOrCalc())
return true;
}
return false;
}
int ResolveUsedColumnCount(int computed_count,
LayoutUnit computed_size,
LayoutUnit used_gap,
......
......@@ -158,6 +158,13 @@ ComputeReplacedSize(const NGLayoutInputNode&,
const NGConstraintSpace&,
const base::Optional<MinMaxSize>&);
// Return true if it's possible (but not necessarily guaranteed) that the new
// constraint space will give a different size compared to the old one, when
// computed style and child content remain unchanged.
bool SizeMayChange(const ComputedStyle&,
const NGConstraintSpace& new_space,
const NGConstraintSpace& old_space);
// Based on available inline size, CSS computed column-width, CSS computed
// column-count and CSS used column-gap, return CSS used column-count.
CORE_EXPORT int ResolveUsedColumnCount(int computed_count,
......
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