Commit 64f7f8c0 authored by Ian Kilpatrick's avatar Ian Kilpatrick Committed by Commit Bot

[LayoutNG] Check for solid edges for layout opportunities.

Previously we weren't checking for "solid" edges on layout opportunities
within the NGExclusionSpace.

This had the effect of us thinking a particular layout opportunity was
valid (when one edge wasn't against any floats). This caused us to place
the element in such a way that it was "avoiding" a float higher up in
the document (when it shouldn't have).

This introduces a "NGClosedArea" struct, which keeps track of the
"NGEdges" vectors so that before considering a layout opportunity as
valid, we check it has solid edges.

Bug: 959567
Change-Id: If6481a23279eee7a1b188ec4cf5129b503935cc7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1603303
Commit-Queue: Ian Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: default avatarMorten Stenshorne <mstensho@chromium.org>
Cr-Commit-Position: refs/heads/master@{#658748}
parent 65886285
...@@ -16,29 +16,33 @@ namespace { ...@@ -16,29 +16,33 @@ namespace {
// //
// We don't explicitly check the inline-size/block-size of the opportunity as // We don't explicitly check the inline-size/block-size of the opportunity as
// they are always produced in the order. // they are always produced in the order.
void InsertOpportunity(const NGLayoutOpportunity& opportunity, void InsertClosedArea(
Vector<NGLayoutOpportunity, 4>* opportunities) { const NGExclusionSpaceInternal::NGClosedArea area,
if (opportunities->IsEmpty()) { Vector<NGExclusionSpaceInternal::NGClosedArea, 4>* areas) {
opportunities->emplace_back(opportunity); if (areas->IsEmpty()) {
areas->emplace_back(area);
return; return;
} }
// We go backwards through the list as there is a higher probability that a // We go backwards through the list as there is a higher probability that a
// new opportunity will be at the end of the list. // new area will be at the end of the list.
for (wtf_size_t j = opportunities->size() - 1; j >= 0; --j) { for (wtf_size_t j = areas->size() - 1; j >= 0; --j) {
const NGLayoutOpportunity& other = opportunities->at(j); const NGExclusionSpaceInternal::NGClosedArea& other = areas->at(j);
if (other.rect.BlockStartOffset() <= opportunity.rect.BlockStartOffset()) { if (other.opportunity.rect.BlockStartOffset() <=
area.opportunity.rect.BlockStartOffset()) {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
// If we have the same block-start offset ensure that the size of the // If we have the same block-start offset ensure that the size of the
// opportunity doesn't violate the order. // opportunity doesn't violate the order.
if (other.rect.BlockStartOffset() == if (other.opportunity.rect.BlockStartOffset() ==
opportunity.rect.BlockStartOffset()) { area.opportunity.rect.BlockStartOffset()) {
DCHECK_LE(other.rect.BlockSize(), opportunity.rect.BlockSize()); DCHECK_LE(other.opportunity.rect.BlockSize(),
DCHECK_GE(other.rect.InlineSize(), opportunity.rect.InlineSize()); area.opportunity.rect.BlockSize());
DCHECK_GE(other.opportunity.rect.InlineSize(),
area.opportunity.rect.InlineSize());
} }
#endif #endif
opportunities->insert(j + 1, opportunity); areas->insert(j + 1, area);
return; return;
} }
} }
...@@ -397,7 +401,9 @@ void NGExclusionSpaceInternal::DerivedGeometry::Add( ...@@ -397,7 +401,9 @@ void NGExclusionSpaceInternal::DerivedGeometry::Add(
*shelf.shape_exclusions)) *shelf.shape_exclusions))
: nullptr); : nullptr);
InsertOpportunity(opportunity, &opportunities_); InsertClosedArea(NGClosedArea(opportunity, shelf.line_left_edges,
shelf.line_right_edges),
&areas_);
} }
} }
...@@ -579,12 +585,12 @@ void NGExclusionSpaceInternal::DerivedGeometry::IterateAllLayoutOpportunities( ...@@ -579,12 +585,12 @@ void NGExclusionSpaceInternal::DerivedGeometry::IterateAllLayoutOpportunities(
const LayoutUnit available_inline_size, const LayoutUnit available_inline_size,
const LambdaFunc& lambda) const { const LambdaFunc& lambda) const {
auto* shelves_it = shelves_.begin(); auto* shelves_it = shelves_.begin();
auto* opps_it = opportunities_.begin(); auto* areas_it = areas_.begin();
auto* const shelves_end = shelves_.end(); auto* const shelves_end = shelves_.end();
auto* const opps_end = opportunities_.end(); auto* const areas_end = areas_.end();
while (shelves_it != shelves_end || opps_it != opps_end) { while (shelves_it != shelves_end || areas_it != areas_end) {
// We should never exhaust the opportunities list before the shelves list, // We should never exhaust the opportunities list before the shelves list,
// as there is always an infinitely sized shelf at the very end. // as there is always an infinitely sized shelf at the very end.
DCHECK_NE(shelves_it, shelves_end); DCHECK_NE(shelves_it, shelves_end);
...@@ -595,22 +601,35 @@ void NGExclusionSpaceInternal::DerivedGeometry::IterateAllLayoutOpportunities( ...@@ -595,22 +601,35 @@ void NGExclusionSpaceInternal::DerivedGeometry::IterateAllLayoutOpportunities(
continue; continue;
} }
if (opps_it != opps_end) { if (areas_it != areas_end) {
const NGLayoutOpportunity& opportunity = *opps_it; const NGClosedArea& area = *areas_it;
if (!Intersects(opportunity, offset, available_inline_size)) { if (!Intersects(area.opportunity, offset, available_inline_size)) {
++opps_it; ++areas_it;
continue; continue;
} }
// We always prefer the closed-off opportunity, instead of the shelf LayoutUnit block_start_offset = std::max(
area.opportunity.rect.BlockStartOffset(), offset.block_offset);
// We always prefer the closed-off area opportunity, instead of the shelf
// opportunity if they exist at the some offset. // opportunity if they exist at the some offset.
if (opportunity.rect.BlockStartOffset() <= shelf.block_offset) { if (block_start_offset <=
if (lambda(CreateLayoutOpportunity(opportunity, offset, std::max(shelf.block_offset, offset.block_offset)) {
available_inline_size))) LayoutUnit block_end_offset = area.opportunity.rect.BlockEndOffset();
return;
bool has_solid_edges =
HasSolidEdges(area.line_left_edges, block_start_offset,
block_end_offset) &&
HasSolidEdges(area.line_right_edges, block_start_offset,
block_end_offset);
if (has_solid_edges) {
if (lambda(CreateLayoutOpportunity(area.opportunity, offset,
available_inline_size)))
return;
}
++opps_it; ++areas_it;
continue; continue;
} }
} }
......
...@@ -226,6 +226,49 @@ class CORE_EXPORT NGExclusionSpaceInternal { ...@@ -226,6 +226,49 @@ class CORE_EXPORT NGExclusionSpaceInternal {
bool has_shape_exclusions; bool has_shape_exclusions;
}; };
// The closed-off area is an internal data-structure representing an area
// above a float. It contains a layout opportunity, and two vectors of
// |NGShelfEdge|. E.g.
//
// 0 1 2 3 4 5 6 7 8
// 0 +---+. .+---+
// |xxx|. .|xxx|
// 10 |xxx|. .|xxx|
// +---+. .+---+
// 20 ........
// +---+
// 30 |xxx|
// |xxx|
// 40 +---+
//
// In the above example the closed-off area is represented with the dotted
// line.
//
// It has the internal values of:
// {
// opportunity: {
// start_offset: {20, LayoutUnit::Min()},
// end_offset: {65, 25},
// }
// line_left_edges: [{0, 15}],
// line_right_edges: [{0, 15}],
// }
//
// Once a closed-off area has been created, it can never be changed due to
// the property that floats always align their block-start edges.
struct NGClosedArea {
NGClosedArea(NGLayoutOpportunity opportunity,
const Vector<NGShelfEdge, 1>& line_left_edges,
const Vector<NGShelfEdge, 1>& line_right_edges)
: opportunity(opportunity),
line_left_edges(line_left_edges),
line_right_edges(line_right_edges) {}
const NGLayoutOpportunity opportunity;
const Vector<NGShelfEdge, 1> line_left_edges;
const Vector<NGShelfEdge, 1> line_right_edges;
};
private: private:
// In order to reduce the amount of Vector copies, instances of a // In order to reduce the amount of Vector copies, instances of a
// NGExclusionSpaceInternal can share the same exclusions_ Vector. See the // NGExclusionSpaceInternal can share the same exclusions_ Vector. See the
...@@ -296,40 +339,22 @@ class CORE_EXPORT NGExclusionSpaceInternal { ...@@ -296,40 +339,22 @@ class CORE_EXPORT NGExclusionSpaceInternal {
const LayoutUnit available_inline_size, const LayoutUnit available_inline_size,
const LambdaFunc&) const; const LambdaFunc&) const;
// See NGShelf for a broad description of what shelves are. We always begin // See |NGShelf| for a broad description of what shelves are. We always
// with one, which has the internal value of: // begin with one, which has the internal value of:
// { // {
// block_offset: LayoutUnit::Min(), // block_offset: LayoutUnit::Min(),
// line_left: LayoutUnit::Min(), // line_left: LayoutUnit::Min(),
// line_right: LayoutUnit::Max(), // line_right: LayoutUnit::Max(),
// } // }
// //
// The list of opportunities represent "closed-off" areas. E.g.
//
// 0 1 2 3 4 5 6 7 8
// 0 +---+. .+---+
// |xxx|. .|xxx|
// 10 |xxx|. .|xxx|
// +---+. .+---+
// 20 ........
// +---+
// 30 |xxx|
// |xxx|
// 40 +---+
//
// In the above example the opportunity is represented with the dotted line.
// It has the internal values of:
// {
// start_offset: {20, LayoutUnit::Min()},
// end_offset: {65, 25},
// }
// Once an opportunity has been created, it can never been changed due to
// the property that floats always align their block-start edges.
//
// We exploit this property by keeping this list of "closed-off" areas, and
// removing shelves to make insertion faster.
Vector<NGShelf, 4> shelves_; Vector<NGShelf, 4> shelves_;
Vector<NGLayoutOpportunity, 4> opportunities_;
// See |NGClosedArea| for a broad description of what closed-off areas are.
//
// Floats always align their block-start edges. We exploit this property by
// keeping a list of closed-off areas. Once a closed-off area has been
// created, it can never change.
Vector<NGClosedArea, 4> areas_;
bool track_shape_exclusions_; bool track_shape_exclusions_;
}; };
......
<!DOCTYPE html>
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#float-position">
<meta name="assert" content="This test checks placement of inflow content with floats present." />
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div style="width: 100px; height: 100px; background: red; line-height: 0; position: relative;">
<div style="float: left; width: 20px;"></div>
<div style="float: right; width: 30px; height: 50px; background: green;"></div>
<div style="float: right; clear: right; width: 100px; height: 50px; background: green;"></div>
<div style="display: inline-block; width: 50px; height: 50px; background: green;"></div>
<div style="position: absolute; width: 20px; height: 50px; top: 0; left: 50px; background: green;"></div>
</div>
<!DOCTYPE html>
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#float-position">
<meta name="assert" content="This test checks placement of inflow content with floats present." />
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div style="width: 100px; height: 100px; background: red; position: relative;">
<div style="float: left; width: 20px;"></div>
<div style="line-height: 0;">
<div style="display: inline-block; width: 100px; height: 20px; background: green;"></div>
</div>
<div style="float: right; width: 20px; height: 80px; background: green;"></div>
<div style="float: right; clear: right; width: 30px; clear: right;"></div>
<div style="display: inline-block; width: 60px; height: 60px; background: green;"></div>
<div style="position: absolute; width: 20px; height: 80px; background: green; top: 20px; right: 20px;"></div>
<div style="position: absolute; width: 60px; height: 20px; background: green; bottom: 0; left: 0;"></div>
</div>
<!DOCTYPE html>
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#float-position">
<meta name="assert" content="This test checks placement of inflow content with floats present." />
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div style="width: 100px; height: 100px; line-height: 0; background: red; position: relative;">
<div style="float: left; width: 20px; height: 20px;"></div>
<div style="height: 30px; background: green;"></div>
<div style="float: right; width: 40px; height: 20px; background: green;"></div>
<div style="float: right; clear: right; width: 50px; height: 50px; background: green;"></div>
<div style="float: left; width: 50px; height: 50px; background: green;"></div>
<span style="display: inline-block; width: 40px; height: 20px; background: green;"></span>
<div style="position: absolute; width: 20px; height: 20px; background: green; left: 40px; top: 30px;"></div>
</div>
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