Commit dea199ac authored by Morten Stenshorne's avatar Morten Stenshorne Committed by Commit Bot

[LayoutNG] Improve handling of adjoining floats.

A float always needs to be positioned by its block parent, so passing
them around to children, parents or siblings really shouldn't be
necessary (and it *was* somewhat confusing, since nobody but the direct
float parent is allowed to touch them anyway, apart from placing them
into temporary exclusion spaces). The main reason for passing them
around like that, was for other blocks to determine, based on the list
of floats being empty or not, the need for relayout once the BFC offset
was resolved.

Instead, confine the list of unpositioned floats to the block parent of
those floats, and introduce the concept of adjoining float types (none,
left, right, both). Adjoining floats occur when the BFC offset is
unknown, meaning that their position may be affected by the current
layout algorithm.

Adjoining float types will now be the thing that's both input to and
output from the layout algorithms. Having something other than "none"
means that a block's BFC offset is unknown, but that doesn't
automatically mean that we have to abort and re-layout if the BFC offset
gets resolved. If the "floats BFC offset" is known, for instance, those
adjoining floats may be positioned right away. Still we need to know
about them (positioned or not), to get clearance correct.

We're going to need to treat adjoining floats specially when applying
clearance. Will deal with that in a later CL. For now, we just keep
track of the adjoining float types, so that the clearance machinery can
tell that there are floats there that may not yet be positioned. That
used to be taken care of the list of unpositioned floats, but, as
previously stated, adjoining floats are special, and we need to know
about them, whether they are positioned or not. This is a preparatory CL
for that.

Each time we add an unpositioned float, we need to update the types of
adjoining floats, so that these can be returned from the algorithm if
necessary. Whether they end up being positioned right away or not isn't
relevant. Adjoining is adjoining.

Note that we don't have to #include the header file for unpositioned
floats as much as before now, but I'll clean that up in a follow-up CL,
because it turned out that there were quite a few translation units that
got stuff for free via that header file, instead of explicitly including
what they need.

Had to rewrite how we deal with floats in HandleNewFormattingContext()
and LayoutNewFormattingContext(), since those depended on a list of all
preceding unpositioned floats to place them into a temporary exclusion
space, to figure out whether to let the child's margin be adjoining with
the current margin strut or not. Instead of using a temporary exclusion
space, we now position floats via the regular mechanisms, and initially
assume that the child's margin is going to be adjoining. This means that
we have to abort and roll back if there are preceding unpositioned
floats. This is no different from how we lay out regular blocks, though.
What *is* different is that if it turns out that the child's margin has
to be separated from the strut, we'll have to abort and roll back
once *again* (but only once).

The algorithms now need to keep track of whether they need to abort if
the BFC offset *changes*, rather than if it is *resolved*. We only allow
the offset to resolve and optionally change *once* afterwards, though.

Cq-Include-Trybots: master.tryserver.chromium.linux:linux_layout_tests_layout_ng
Change-Id: Ie527d659213049f180ebedc764e1d7f4926a5876
Reviewed-on: https://chromium-review.googlesource.com/1030191
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#555314}
parent e28bf02a
<!DOCTYPE html>
<title>A new formatting context that fits beside an adjoining float, and thus pulls down the float with its top margin</title>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#bfc-next-to-float" title="9.5 Floats">
<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#flow-control" title="9.5.2 Controlling flow next to floats: the 'clear' property">
<meta name="assert" content="The float is adjoining with the box that establishes a new formatting context when it fits beside it, and will therefore be affected by its margin">
<link rel="match" href="../../reference/ref-filled-green-200px-square.html">
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div style="overflow:hidden; width:200px; height:200px; background:red;">
<div style="margin-top:190px;">
<div>
<div style="float:left; width:100px; height:200px; background:green;"></div>
</div>
<div style="margin-top:-190px; overflow:hidden; width:100px; height:200px; background:green;"></div>
</div>
</div>
<!DOCTYPE html>
<title>A new formatting context that doesn't fit beside a float make the float non-adjoining</title>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#bfc-next-to-float" title="9.5 Floats">
<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#flow-control" title="9.5.2 Controlling flow next to floats: the 'clear' property">
<meta name="assert" content="Although the 'clear' property isn't specified in this test, a new formatting context that doesn't fit below a float that would otherwise be adjoining will need to separate its margin from the float, so that it doesn't affect the float. This is very similar to clearance.">
<link rel="match" href="../../reference/ref-filled-green-200px-square.html">
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div style="overflow:hidden; width:200px; height:200px; background:red;">
<div style="margin-top:-50px;">
<div>
<div style="float:left; width:200px; height:150px; background:green;"></div>
</div>
<div style="margin-top:12345px; overflow:hidden; width:200px; height:100px; background:green;"></div>
</div>
</div>
......@@ -419,6 +419,8 @@ blink_core_sources("layout") {
"ng/ng_layout_input_node.h",
"ng/ng_layout_result.cc",
"ng/ng_layout_result.h",
"ng/ng_layout_utils.cc",
"ng/ng_layout_utils.h",
"ng/ng_length_utils.cc",
"ng/ng_length_utils.h",
"ng/ng_out_of_flow_layout_part.cc",
......
......@@ -84,7 +84,6 @@ NGInlineLayoutAlgorithm::NGInlineLayoutAlgorithm(
is_horizontal_writing_mode_(
blink::IsHorizontalWritingMode(space.GetWritingMode())) {
quirks_mode_ = inline_node.InLineHeightQuirksMode();
unpositioned_floats_ = ConstraintSpace().UnpositionedFloats();
if (!is_horizontal_writing_mode_)
baseline_type_ = FontBaseline::kIdeographicBaseline;
......@@ -552,10 +551,20 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
bool is_empty_inline = Node().IsEmptyInline();
if (!is_empty_inline) {
DCHECK(ConstraintSpace().UnpositionedFloats().IsEmpty());
if (is_empty_inline) {
// We're just going to collapse through this one, so whatever went in on one
// side will go out on the other side. The position of the adjoining floats
// will be affected by any subsequent block, until the BFC offset is
// resolved.
container_builder_.AddAdjoiningFloatTypes(
ConstraintSpace().AdjoiningFloatTypes());
} else {
DCHECK(ConstraintSpace().MarginStrut().IsEmpty());
container_builder_.SetBfcOffset(ConstraintSpace().BfcOffset());
// The BFC offset was determined before entering this algorithm. This means
// that there should be no adjoining floats.
DCHECK(!ConstraintSpace().AdjoiningFloatTypes());
}
// In order to get the correct list of layout opportunities, we need to
......@@ -569,7 +578,6 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
DCHECK_EQ(handled_item_index, Node().Items().size());
container_builder_.SwapPositionedFloats(&positioned_floats_);
container_builder_.SwapUnpositionedFloats(&unpositioned_floats_);
container_builder_.SetEndMarginStrut(ConstraintSpace().MarginStrut());
container_builder_.SetExclusionSpace(std::move(initial_exclusion_space));
......@@ -606,10 +614,10 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
std::make_unique<NGExclusionSpace>(*initial_exclusion_space);
NGLineInfo line_info;
NGLineBreaker line_breaker(Node(), NGLineBreakerMode::kContent,
constraint_space_, &positioned_floats,
&unpositioned_floats_, exclusion_space.get(),
handled_item_index, break_token);
NGLineBreaker line_breaker(
Node(), NGLineBreakerMode::kContent, constraint_space_,
&positioned_floats, &unpositioned_floats_, &container_builder_,
exclusion_space.get(), handled_item_index, break_token);
// TODO(ikilpatrick): Does this always succeed when we aren't an empty
// inline?
......@@ -689,10 +697,12 @@ unsigned NGInlineLayoutAlgorithm::PositionLeadingItems(
NGBoxStrut margins =
ComputeMarginsForContainer(ConstraintSpace(), node.Style());
unpositioned_floats_.push_back(NGUnpositionedFloat::Create(
auto unpositioned_float = NGUnpositionedFloat::Create(
ConstraintSpace().AvailableSize(),
ConstraintSpace().PercentageResolutionSize(), bfc_line_offset,
bfc_line_offset, margins, node, /* break_token */ nullptr));
bfc_line_offset, margins, node, /* break_token */ nullptr);
AddUnpositionedFloat(&unpositioned_floats_, &container_builder_,
std::move(unpositioned_float));
} else if (is_empty_inline &&
item.Type() == NGInlineItem::kOutOfFlowPositioned) {
NGBlockNode node(ToLayoutBox(item.GetLayoutObject()));
......
......@@ -463,8 +463,9 @@ static LayoutUnit ComputeContentSize(NGInlineNode node,
unpositioned_floats.clear();
NGLineBreaker line_breaker(node, mode, *space, &positioned_floats,
&unpositioned_floats, &empty_exclusion_space, 0u,
break_token.get());
&unpositioned_floats,
nullptr /* container_builder */,
&empty_exclusion_space, 0u, break_token.get());
if (!line_breaker.NextLine(opportunity, &line_info))
break;
......
......@@ -163,12 +163,12 @@ scoped_refptr<NGLayoutResult> NGLineBoxFragmentBuilder::ToLineBoxFragment() {
return base::AdoptRef(new NGLayoutResult(
std::move(fragment), oof_positioned_descendants_, positioned_floats_,
unpositioned_floats_, unpositioned_list_marker_,
std::move(exclusion_space_), bfc_offset_, end_margin_strut_,
unpositioned_list_marker_, std::move(exclusion_space_), bfc_offset_,
end_margin_strut_,
/* intrinsic_block_size */ LayoutUnit(),
/* minimal_space_shortage */ LayoutUnit::Max(), EBreakBetween::kAuto,
EBreakBetween::kAuto, /* has_forced_break */ false, is_pushed_by_floats_,
NGLayoutResult::kSuccess));
adjoining_floats_, NGLayoutResult::kSuccess));
}
} // namespace blink
......@@ -41,6 +41,7 @@ NGLineBreaker::NGLineBreaker(
const NGConstraintSpace& space,
Vector<NGPositionedFloat>* positioned_floats,
Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats,
NGContainerFragmentBuilder* container_builder,
NGExclusionSpace* exclusion_space,
unsigned handled_float_index,
const NGInlineBreakToken* break_token)
......@@ -49,6 +50,7 @@ NGLineBreaker::NGLineBreaker(
constraint_space_(space),
positioned_floats_(positioned_floats),
unpositioned_floats_(unpositioned_floats),
container_builder_(container_builder),
exclusion_space_(exclusion_space),
break_iterator_(node.Text()),
shaper_(node.Text().Characters16(), node.Text().length()),
......@@ -652,7 +654,8 @@ void NGLineBreaker::HandleFloat(const NGInlineItem& item,
// Check if we already have a pending float. That's because a float cannot be
// higher than any block or floated box generated before.
if (!unpositioned_floats_->IsEmpty() || float_after_line) {
unpositioned_floats_->push_back(std::move(unpositioned_float));
AddUnpositionedFloat(unpositioned_floats_, container_builder_,
std::move(unpositioned_float));
} else {
LayoutUnit origin_block_offset = bfc_block_offset_;
......
......@@ -17,6 +17,7 @@
namespace blink {
class Hyphenation;
class NGContainerFragmentBuilder;
class NGInlineBreakToken;
class NGInlineItem;
class NGInlineLayoutStateStack;
......@@ -38,6 +39,7 @@ class CORE_EXPORT NGLineBreaker {
const NGConstraintSpace&,
Vector<NGPositionedFloat>*,
Vector<scoped_refptr<NGUnpositionedFloat>>*,
NGContainerFragmentBuilder* container_builder,
NGExclusionSpace*,
unsigned handled_float_index,
const NGInlineBreakToken* = nullptr);
......@@ -162,6 +164,7 @@ class CORE_EXPORT NGLineBreaker {
const NGConstraintSpace& constraint_space_;
Vector<NGPositionedFloat>* positioned_floats_;
Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats_;
NGContainerFragmentBuilder* container_builder_; /* May be nullptr */
NGExclusionSpace* exclusion_space_;
scoped_refptr<const ComputedStyle> current_style_;
......
......@@ -53,6 +53,7 @@ class NGLineBreakerTest : public NGBaseLayoutAlgorithmTest {
while (!break_token || !break_token->IsFinished()) {
NGLineBreaker line_breaker(node, NGLineBreakerMode::kContent, *space,
&positioned_floats, &unpositioned_floats,
/* container_builder */ nullptr,
&exclusion_space, 0u, break_token.get());
if (!line_breaker.NextLine(opportunity, &line_info))
break;
......
......@@ -12,6 +12,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/page/scrolling/root_scroller_util.h"
#include "third_party/blink/renderer/core/paint/ng/ng_block_flow_painter.h"
......@@ -147,9 +148,9 @@ scoped_refptr<NGLayoutResult> LayoutNGMixin<Base>::CachedLayoutResult(
return nullptr;
if (constraint_space != *cached_constraint_space_)
return nullptr;
if (cached_constraint_space_->UnpositionedFloats().size() ||
cached_result_->UnpositionedFloats().size())
return nullptr;
// The checks above should be enough to bail if layout is incomplete, but
// let's verify:
DCHECK(IsBlockLayoutComplete(*cached_constraint_space_, *cached_result_));
return cached_result_->CloneWithoutOffset();
}
......
......@@ -178,6 +178,10 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
// Updates the fragment's BFC offset if it's not already set.
bool MaybeUpdateFragmentBfcOffset(LayoutUnit bfc_block_offset);
// Return true if the BFC offset has changed and this means that we need to
// abort layout.
bool NeedsAbortOnBfcOffsetChange() const;
// Positions pending floats starting from {@origin_block_offset}.
void PositionPendingFloats(LayoutUnit origin_block_offset);
......@@ -228,7 +232,14 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
// Set if we're resuming layout of a node that has already produced fragments.
bool is_resuming_;
bool abort_when_bfc_resolved_;
// Set when we're to abort if the BFC offset gets resolved or updated.
// Sometimes we walk past elements (i.e. floats) that depend on the BFC offset
// being known (in order to position and lay themselves out properly). When
// this happens, and we finally manage to resolve (or update) the BFC offset
// at some subsequent element, we need to check if this flag is set, and abort
// layout if it is.
bool abort_when_bfc_offset_updated_ = false;
bool has_processed_first_child_ = false;
std::unique_ptr<NGExclusionSpace> exclusion_space_;
......
......@@ -26,6 +26,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
......@@ -191,8 +192,7 @@ scoped_refptr<NGLayoutResult> NGBlockNode::Layout(
block_flow->ClearPaintFragment();
}
if (layout_result->Status() == NGLayoutResult::kSuccess &&
layout_result->UnpositionedFloats().IsEmpty()) {
if (IsBlockLayoutComplete(constraint_space, *layout_result)) {
DCHECK(layout_result->PhysicalFragment());
if (block_flow && first_child && first_child.IsInline()) {
......
......@@ -47,11 +47,11 @@ NGConstraintSpace::NGConstraintSpace(
bool is_new_fc,
bool is_anonymous,
bool use_first_line_style,
NGFloatTypes adjoining_floats,
const NGMarginStrut& margin_strut,
const NGBfcOffset& bfc_offset,
const base::Optional<NGBfcOffset>& floats_bfc_offset,
const NGExclusionSpace& exclusion_space,
Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats,
LayoutUnit clearance_offset,
Vector<NGBaselineRequest>& baseline_requests)
: available_size_(available_size),
......@@ -74,6 +74,7 @@ NGConstraintSpace::NGConstraintSpace(
is_new_fc_(is_new_fc),
is_anonymous_(is_anonymous),
use_first_line_style_(use_first_line_style),
adjoining_floats_(adjoining_floats),
writing_mode_(static_cast<unsigned>(writing_mode)),
is_orthogonal_writing_mode_root_(is_orthogonal_writing_mode_root),
direction_(static_cast<unsigned>(direction)),
......@@ -82,7 +83,6 @@ NGConstraintSpace::NGConstraintSpace(
floats_bfc_offset_(floats_bfc_offset),
exclusion_space_(std::make_unique<NGExclusionSpace>(exclusion_space)),
clearance_offset_(clearance_offset) {
unpositioned_floats_.swap(unpositioned_floats);
baseline_requests_.swap(baseline_requests);
}
......@@ -213,12 +213,6 @@ NGFragmentationType NGConstraintSpace::BlockFragmentationType() const {
}
bool NGConstraintSpace::operator==(const NGConstraintSpace& other) const {
// TODO(cbiesinger): For simplicity and performance, for now, we only
// consider two constraint spaces equal if neither one has unpositioned
// floats. We should consider changing this in the future.
if (unpositioned_floats_.size() || other.unpositioned_floats_.size())
return false;
if (exclusion_space_ && other.exclusion_space_ &&
*exclusion_space_ != *other.exclusion_space_)
return false;
......@@ -245,6 +239,7 @@ bool NGConstraintSpace::operator==(const NGConstraintSpace& other) const {
separate_leading_fragmentainer_margins_ ==
other.separate_leading_fragmentainer_margins_ &&
is_anonymous_ == other.is_anonymous_ &&
adjoining_floats_ == other.adjoining_floats_ &&
writing_mode_ == other.writing_mode_ &&
direction_ == other.direction_ &&
margin_strut_ == other.margin_strut_ &&
......
......@@ -174,20 +174,30 @@ class CORE_EXPORT NGConstraintSpace final
// If present, and the current layout hasn't resolved its BFC offset yet (see
// BfcOffset), the layout should position all of its unpositioned floats at
// this offset.
// this offset. This value is the BFC offset that we calculated in the
// previous pass, a pass which aborted once the BFC offset got resolved,
// because we had walked past content (i.e. floats) that depended on it being
// resolved.
//
// This value should be propogated to child layouts if the current layout
// hasn't resolved its BFC offset yet.
//
// This value is calculated *after* an initial pass of the tree, this value
// should only be present during the second pass.
// This value is calculated *after* an initial pass of the tree, and should
// only be present during subsequent passes.
base::Optional<NGBfcOffset> FloatsBfcOffset() const {
return floats_bfc_offset_;
}
const Vector<scoped_refptr<NGUnpositionedFloat>>& UnpositionedFloats() const {
return unpositioned_floats_;
}
// Return the types (none, left, right, both) of preceding adjoining
// floats. These are floats that are added while the in-flow BFC offset is
// still unknown. The floats may or may not be unpositioned (pending). That
// depends on which layout pass we're in. They are typically positioned if
// FloatsBfcOffset() is known. Adjoining floats should be treated differently
// when calculating clearance on a block with adjoining block-start margin.
// (in such cases we will know up front that the block will need clearance,
// since, if it doesn't, the float will be pulled along with the block, and
// the block will fail to clear).
NGFloatTypes AdjoiningFloatTypes() const { return adjoining_floats_; }
bool HasClearanceOffset() const {
return clearance_offset_ != LayoutUnit::Min();
......@@ -206,33 +216,32 @@ class CORE_EXPORT NGConstraintSpace final
private:
friend class NGConstraintSpaceBuilder;
// Default constructor.
NGConstraintSpace(
WritingMode,
bool is_orthogonal_writing_mode_root,
TextDirection,
NGLogicalSize available_size,
NGLogicalSize percentage_resolution_size,
LayoutUnit parent_percentage_resolution_inline_size,
NGPhysicalSize initial_containing_block_size,
LayoutUnit fragmentainer_block_size,
LayoutUnit fragmentainer_space_at_bfc_start,
bool is_fixed_size_inline,
bool is_fixed_size_block,
bool is_shrink_to_fit,
bool is_inline_direction_triggers_scrollbar,
bool is_block_direction_triggers_scrollbar,
NGFragmentationType block_direction_fragmentation_type,
bool separate_leading_fragmentainer_margins_,
bool is_new_fc,
bool is_anonymous,
bool use_first_line_style,
const NGMarginStrut& margin_strut,
const NGBfcOffset& bfc_offset,
const base::Optional<NGBfcOffset>& floats_bfc_offset,
const NGExclusionSpace& exclusion_space,
Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats,
LayoutUnit clearance_offset,
Vector<NGBaselineRequest>& baseline_requests);
NGConstraintSpace(WritingMode,
bool is_orthogonal_writing_mode_root,
TextDirection,
NGLogicalSize available_size,
NGLogicalSize percentage_resolution_size,
LayoutUnit parent_percentage_resolution_inline_size,
NGPhysicalSize initial_containing_block_size,
LayoutUnit fragmentainer_block_size,
LayoutUnit fragmentainer_space_at_bfc_start,
bool is_fixed_size_inline,
bool is_fixed_size_block,
bool is_shrink_to_fit,
bool is_inline_direction_triggers_scrollbar,
bool is_block_direction_triggers_scrollbar,
NGFragmentationType block_direction_fragmentation_type,
bool separate_leading_fragmentainer_margins_,
bool is_new_fc,
bool is_anonymous,
bool use_first_line_style,
NGFloatTypes adjoining_floats,
const NGMarginStrut& margin_strut,
const NGBfcOffset& bfc_offset,
const base::Optional<NGBfcOffset>& floats_bfc_offset,
const NGExclusionSpace& exclusion_space,
LayoutUnit clearance_offset,
Vector<NGBaselineRequest>& baseline_requests);
NGLogicalSize available_size_;
NGLogicalSize percentage_resolution_size_;
......@@ -259,6 +268,7 @@ class CORE_EXPORT NGConstraintSpace final
unsigned is_anonymous_ : 1;
unsigned use_first_line_style_ : 1;
unsigned adjoining_floats_ : 2; // NGFloatTypes
unsigned writing_mode_ : 3;
unsigned is_orthogonal_writing_mode_root_ : 1;
......@@ -270,7 +280,6 @@ class CORE_EXPORT NGConstraintSpace final
const std::unique_ptr<const NGExclusionSpace> exclusion_space_;
LayoutUnit clearance_offset_;
Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_;
Vector<NGBaselineRequest> baseline_requests_;
};
......
......@@ -31,6 +31,7 @@ NGConstraintSpaceBuilder::NGConstraintSpaceBuilder(WritingMode writing_mode,
is_new_fc_(false),
is_anonymous_(false),
use_first_line_style_(false),
adjoining_floats_(kFloatTypeNone),
text_direction_(static_cast<unsigned>(TextDirection::kLtr)),
exclusion_space_(nullptr) {}
......@@ -140,12 +141,6 @@ NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetUseFirstLineStyle(
return *this;
}
NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetUnpositionedFloats(
Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats) {
unpositioned_floats_ = unpositioned_floats;
return *this;
}
void NGConstraintSpaceBuilder::AddBaselineRequests(
const Vector<NGBaselineRequest>& requests) {
DCHECK(baseline_requests_.IsEmpty());
......@@ -202,9 +197,7 @@ scoped_refptr<NGConstraintSpace> NGConstraintSpaceBuilder::ToConstraintSpace(
DEFINE_STATIC_LOCAL(NGExclusionSpace, empty_exclusion_space, ());
// Reset things that do not pass the Formatting Context boundary.
if (is_new_fc_)
DCHECK(unpositioned_floats_.IsEmpty());
DCHECK(!is_new_fc_ || !adjoining_floats_);
const NGExclusionSpace& exclusion_space = (is_new_fc_ || !exclusion_space_)
? empty_exclusion_space
......@@ -234,8 +227,8 @@ scoped_refptr<NGConstraintSpace> NGConstraintSpaceBuilder::ToConstraintSpace(
is_block_direction_triggers_scrollbar_,
static_cast<NGFragmentationType>(fragmentation_type_),
separate_leading_fragmentainer_margins_, is_new_fc_, is_anonymous_,
use_first_line_style_, margin_strut, bfc_offset, floats_bfc_offset,
exclusion_space, unpositioned_floats_, clearance_offset,
use_first_line_style_, adjoining_floats_, margin_strut, bfc_offset,
floats_bfc_offset, exclusion_space, clearance_offset,
baseline_requests_));
}
return base::AdoptRef(new NGConstraintSpace(
......@@ -249,8 +242,8 @@ scoped_refptr<NGConstraintSpace> NGConstraintSpaceBuilder::ToConstraintSpace(
is_inline_direction_triggers_scrollbar_,
static_cast<NGFragmentationType>(fragmentation_type_),
separate_leading_fragmentainer_margins_, is_new_fc_, is_anonymous_,
use_first_line_style_, margin_strut, bfc_offset, floats_bfc_offset,
exclusion_space, unpositioned_floats_, clearance_offset,
use_first_line_style_, adjoining_floats_, margin_strut, bfc_offset,
floats_bfc_offset, exclusion_space, clearance_offset,
baseline_requests_));
}
......
......@@ -63,8 +63,10 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
NGConstraintSpaceBuilder& SetIsAnonymous(bool is_anonymous);
NGConstraintSpaceBuilder& SetUseFirstLineStyle(bool use_first_line_style);
NGConstraintSpaceBuilder& SetUnpositionedFloats(
Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats);
NGConstraintSpaceBuilder& SetAdjoiningFloatTypes(NGFloatTypes floats) {
adjoining_floats_ = floats;
return *this;
}
NGConstraintSpaceBuilder& SetMarginStrut(const NGMarginStrut& margin_strut);
......@@ -111,6 +113,7 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
unsigned is_new_fc_ : 1;
unsigned is_anonymous_ : 1;
unsigned use_first_line_style_ : 1;
unsigned adjoining_floats_ : 2; // NGFloatTypes
unsigned text_direction_ : 1;
NGMarginStrut margin_strut_;
......@@ -118,7 +121,6 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
base::Optional<NGBfcOffset> floats_bfc_offset_;
const NGExclusionSpace* exclusion_space_;
LayoutUnit clearance_offset_;
Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_;
Vector<NGBaselineRequest> baseline_requests_;
};
......
......@@ -45,12 +45,6 @@ NGContainerFragmentBuilder& NGContainerFragmentBuilder::SetExclusionSpace(
return *this;
}
NGContainerFragmentBuilder& NGContainerFragmentBuilder::SwapUnpositionedFloats(
Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats) {
unpositioned_floats_.swap(*unpositioned_floats);
return *this;
}
NGContainerFragmentBuilder&
NGContainerFragmentBuilder::SetUnpositionedListMarker(
const NGUnpositionedListMarker& marker) {
......
......@@ -12,6 +12,7 @@
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h"
#include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h"
#include "third_party/blink/renderer/platform/text/text_direction.h"
#include "third_party/blink/renderer/platform/text/writing_mode.h"
......@@ -23,7 +24,6 @@ class ComputedStyle;
class NGExclusionSpace;
class NGLayoutResult;
class NGPhysicalFragment;
struct NGUnpositionedFloat;
class CORE_EXPORT NGContainerFragmentBuilder : public NGBaseFragmentBuilder {
STACK_ALLOCATED();
......@@ -41,15 +41,16 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGBaseFragmentBuilder {
// it is not set, this fragment may be placed anywhere within the BFC.
const base::Optional<NGBfcOffset>& BfcOffset() const { return bfc_offset_; }
NGContainerFragmentBuilder& SetBfcOffset(const NGBfcOffset&);
NGContainerFragmentBuilder& ResetBfcOffset() {
bfc_offset_.reset();
return *this;
}
NGContainerFragmentBuilder& SetEndMarginStrut(const NGMarginStrut&);
NGContainerFragmentBuilder& SetExclusionSpace(
std::unique_ptr<const NGExclusionSpace> exclusion_space);
NGContainerFragmentBuilder& SwapUnpositionedFloats(
Vector<scoped_refptr<NGUnpositionedFloat>>*);
const NGUnpositionedListMarker& UnpositionedListMarker() const {
return unpositioned_list_marker_;
}
......@@ -121,6 +122,16 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGBaseFragmentBuilder {
}
bool IsPushedByFloats() const { return is_pushed_by_floats_; }
NGContainerFragmentBuilder& ResetAdjoiningFloatTypes() {
adjoining_floats_ = kFloatTypeNone;
return *this;
}
NGContainerFragmentBuilder& AddAdjoiningFloatTypes(NGFloatTypes floats) {
adjoining_floats_ |= floats;
return *this;
}
NGFloatTypes AdjoiningFloatTypes() const { return adjoining_floats_; }
#ifndef NDEBUG
String ToString() const;
#endif
......@@ -174,10 +185,6 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGBaseFragmentBuilder {
NGMarginStrut end_margin_strut_;
std::unique_ptr<const NGExclusionSpace> exclusion_space_;
// Floats that need to be positioned by the next in-flow fragment that can
// determine its block position in space.
Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_;
Vector<NGOutOfFlowPositionedCandidate> oof_positioned_candidates_;
Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants_;
......@@ -186,6 +193,8 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGBaseFragmentBuilder {
Vector<scoped_refptr<NGPhysicalFragment>> children_;
Vector<NGLogicalOffset> offsets_;
NGFloatTypes adjoining_floats_ = kFloatTypeNone;
bool has_last_resort_break_ = false;
bool is_pushed_by_floats_ = false;
......
......@@ -7,10 +7,12 @@
#include "third_party/blink/renderer/core/layout/min_max_size.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h"
#include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
namespace blink {
......@@ -266,4 +268,31 @@ const Vector<NGPositionedFloat> PositionFloats(
return positioned_floats;
}
void AddUnpositionedFloat(
Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats,
NGContainerFragmentBuilder* fragment_builder,
scoped_refptr<NGUnpositionedFloat> unpositioned_float) {
if (fragment_builder && !fragment_builder->BfcOffset()) {
fragment_builder->AddAdjoiningFloatTypes(
unpositioned_float->IsLeft() ? kFloatTypeLeft : kFloatTypeRight);
}
unpositioned_floats->push_back(std::move(unpositioned_float));
}
NGFloatTypes ToFloatTypes(EClear clear) {
switch (clear) {
default:
NOTREACHED();
FALLTHROUGH;
case EClear::kNone:
return kFloatTypeNone;
case EClear::kLeft:
return kFloatTypeLeft;
case EClear::kRight:
return kFloatTypeRight;
case EClear::kBoth:
return kFloatTypeBoth;
};
}
} // namespace blink
......@@ -7,16 +7,26 @@
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/layout_unit.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
class NGConstraintSpace;
class NGContainerFragmentBuilder;
class NGExclusionSpace;
struct NGPositionedFloat;
struct NGUnpositionedFloat;
enum NGFloatTypeValue {
kFloatTypeNone = 0b00,
kFloatTypeLeft = 0b01,
kFloatTypeRight = 0b10,
kFloatTypeBoth = 0b11
};
typedef int NGFloatTypes;
// Returns the inline size (relative to {@code parent_space}) of the
// unpositioned float. If the float is in a different writing mode, this will
// perform a layout.
......@@ -42,6 +52,15 @@ CORE_EXPORT const Vector<NGPositionedFloat> PositionFloats(
const NGConstraintSpace& space,
NGExclusionSpace* exclusion_space);
// Add a pending float to the list. It will be committed (positioned) once we
// have resolved the BFC offset.
void AddUnpositionedFloat(
Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats,
NGContainerFragmentBuilder* fragment_builder,
scoped_refptr<NGUnpositionedFloat> unpositioned_float);
NGFloatTypes ToFloatTypes(EClear clear);
} // namespace blink
#endif // NGFloatsUtils_h
......@@ -295,11 +295,10 @@ scoped_refptr<NGLayoutResult> NGFragmentBuilder::ToBoxFragment() {
return base::AdoptRef(new NGLayoutResult(
std::move(fragment), oof_positioned_descendants_, positioned_floats,
unpositioned_floats_, unpositioned_list_marker_,
std::move(exclusion_space_), bfc_offset_, end_margin_strut_,
intrinsic_block_size_, minimal_space_shortage_, initial_break_before_,
previous_break_after_, has_forced_break_, is_pushed_by_floats_,
NGLayoutResult::kSuccess));
unpositioned_list_marker_, std::move(exclusion_space_), bfc_offset_,
end_margin_strut_, intrinsic_block_size_, minimal_space_shortage_,
initial_break_before_, previous_break_after_, has_forced_break_,
is_pushed_by_floats_, adjoining_floats_, NGLayoutResult::kSuccess));
}
scoped_refptr<NGLayoutResult> NGFragmentBuilder::Abort(
......@@ -308,9 +307,9 @@ scoped_refptr<NGLayoutResult> NGFragmentBuilder::Abort(
Vector<NGPositionedFloat> positioned_floats;
return base::AdoptRef(new NGLayoutResult(
nullptr, oof_positioned_descendants, positioned_floats,
unpositioned_floats_, NGUnpositionedListMarker(), nullptr, bfc_offset_,
end_margin_strut_, LayoutUnit(), LayoutUnit(), EBreakBetween::kAuto,
EBreakBetween::kAuto, false, false, status));
NGUnpositionedListMarker(), nullptr, bfc_offset_, end_margin_strut_,
LayoutUnit(), LayoutUnit(), EBreakBetween::kAuto, EBreakBetween::kAuto,
false, false, kFloatTypeNone, status));
}
// Finds FragmentPairs that define inline containing blocks.
......
......@@ -17,7 +17,6 @@ NGLayoutResult::NGLayoutResult(
scoped_refptr<NGPhysicalFragment> physical_fragment,
Vector<NGOutOfFlowPositionedDescendant>& oof_positioned_descendants,
Vector<NGPositionedFloat>& positioned_floats,
Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats,
const NGUnpositionedListMarker& unpositioned_list_marker,
std::unique_ptr<const NGExclusionSpace> exclusion_space,
const base::Optional<NGBfcOffset> bfc_offset,
......@@ -28,6 +27,7 @@ NGLayoutResult::NGLayoutResult(
EBreakBetween final_break_after,
bool has_forced_break,
bool is_pushed_by_floats,
NGFloatTypes adjoining_floats,
NGLayoutResultStatus status)
: physical_fragment_(std::move(physical_fragment)),
unpositioned_list_marker_(unpositioned_list_marker),
......@@ -40,10 +40,10 @@ NGLayoutResult::NGLayoutResult(
final_break_after_(final_break_after),
has_forced_break_(has_forced_break),
is_pushed_by_floats_(is_pushed_by_floats),
adjoining_floats_(adjoining_floats),
status_(status) {
oof_positioned_descendants_.swap(oof_positioned_descendants);
positioned_floats_.swap(positioned_floats);
unpositioned_floats_.swap(unpositioned_floats);
}
// Keep the implementation of the destructor here, to avoid dependencies on
......@@ -54,8 +54,6 @@ scoped_refptr<NGLayoutResult> NGLayoutResult::CloneWithoutOffset() const {
Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants(
oof_positioned_descendants_);
Vector<NGPositionedFloat> positioned_floats(positioned_floats_);
Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats(
unpositioned_floats_);
std::unique_ptr<const NGExclusionSpace> exclusion_space;
// TODO(layoutng) Replace this with DCHECK(exclusion_space_) when
// callers guarantee exclusion_space_ != null.
......@@ -64,10 +62,10 @@ scoped_refptr<NGLayoutResult> NGLayoutResult::CloneWithoutOffset() const {
}
return base::AdoptRef(new NGLayoutResult(
physical_fragment_->CloneWithoutOffset(), oof_positioned_descendants,
positioned_floats, unpositioned_floats, unpositioned_list_marker_,
std::move(exclusion_space), bfc_offset_, end_margin_strut_,
intrinsic_block_size_, minimal_space_shortage_, initial_break_before_,
final_break_after_, has_forced_break_, is_pushed_by_floats_, Status()));
positioned_floats, unpositioned_list_marker_, std::move(exclusion_space),
bfc_offset_, end_margin_strut_, intrinsic_block_size_,
minimal_space_shortage_, initial_break_before_, final_break_after_,
has_forced_break_, is_pushed_by_floats_, adjoining_floats_, Status()));
}
} // namespace blink
......@@ -10,6 +10,7 @@
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h"
#include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
......@@ -19,7 +20,6 @@ namespace blink {
class NGExclusionSpace;
struct NGPositionedFloat;
struct NGUnpositionedFloat;
// The NGLayoutResult stores the resulting data from layout. This includes
// geometry information in form of a NGPhysicalFragment, which is kept around
......@@ -59,18 +59,6 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
return positioned_floats_;
}
// List of floats that need to be positioned by the next in-flow child that
// can determine its position in space.
// Use case example where it may be needed:
// <div><float></div>
// <div style="margin-top: 10px; height: 20px"></div>
// The float cannot be positioned right away inside of the 1st div because
// the vertical position is not known at that moment. It will be known only
// after the 2nd div collapses its margin with its parent.
const Vector<scoped_refptr<NGUnpositionedFloat>>& UnpositionedFloats() const {
return unpositioned_floats_;
}
const NGUnpositionedListMarker& UnpositionedListMarker() const {
return unpositioned_list_marker_;
}
......@@ -109,35 +97,43 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
// of floats.
bool IsPushedByFloats() const { return is_pushed_by_floats_; }
// Return the types (none, left, right, both) of preceding adjoining
// floats. These are floats that are added while the in-flow BFC offset is
// still unknown. The floats may or may not be unpositioned (pending). That
// depends on which layout pass we're in. Adjoining floats should be treated
// differently when calculating clearance on a block with adjoining
// block-start margin (in such cases we will know up front that the block will
// need clearance, since, if it doesn't, the float will be pulled along with
// the block, and the block will fail to clear).
NGFloatTypes AdjoiningFloatTypes() const { return adjoining_floats_; }
scoped_refptr<NGLayoutResult> CloneWithoutOffset() const;
private:
friend class NGFragmentBuilder;
friend class NGLineBoxFragmentBuilder;
NGLayoutResult(
scoped_refptr<NGPhysicalFragment> physical_fragment,
Vector<NGOutOfFlowPositionedDescendant>&
out_of_flow_positioned_descendants,
Vector<NGPositionedFloat>& positioned_floats,
Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats,
const NGUnpositionedListMarker& unpositioned_list_marker,
std::unique_ptr<const NGExclusionSpace> exclusion_space,
const base::Optional<NGBfcOffset> bfc_offset,
const NGMarginStrut end_margin_strut,
const LayoutUnit intrinsic_block_size,
LayoutUnit minimal_space_shortage,
EBreakBetween initial_break_before,
EBreakBetween final_break_after,
bool has_forced_break,
bool is_pushed_by_floats,
NGLayoutResultStatus status);
NGLayoutResult(scoped_refptr<NGPhysicalFragment> physical_fragment,
Vector<NGOutOfFlowPositionedDescendant>&
out_of_flow_positioned_descendants,
Vector<NGPositionedFloat>& positioned_floats,
const NGUnpositionedListMarker& unpositioned_list_marker,
std::unique_ptr<const NGExclusionSpace> exclusion_space,
const base::Optional<NGBfcOffset> bfc_offset,
const NGMarginStrut end_margin_strut,
const LayoutUnit intrinsic_block_size,
LayoutUnit minimal_space_shortage,
EBreakBetween initial_break_before,
EBreakBetween final_break_after,
bool has_forced_break,
bool is_pushed_by_floats,
NGFloatTypes adjoining_floats,
NGLayoutResultStatus status);
scoped_refptr<NGPhysicalFragment> physical_fragment_;
Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants_;
Vector<NGPositionedFloat> positioned_floats_;
Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_;
NGUnpositionedListMarker unpositioned_list_marker_;
......@@ -153,6 +149,7 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
unsigned has_forced_break_ : 1;
unsigned is_pushed_by_floats_ : 1;
unsigned adjoining_floats_ : 2; // NGFloatTypes
unsigned status_ : 1;
};
......
// 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/ng/ng_layout_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
namespace blink {
bool IsBlockLayoutComplete(const NGConstraintSpace& space,
const NGLayoutResult& result) {
if (result.Status() != NGLayoutResult::kSuccess)
return false;
// Check that we're done positioning pending floats.
return !result.AdjoiningFloatTypes() || result.BfcOffset() ||
space.FloatsBfcOffset();
}
} // namespace blink
// 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_UTILS_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_UTILS_H_
namespace blink {
class NGConstraintSpace;
class NGLayoutResult;
// Return true if layout is considered complete. In some cases we require more
// than one layout pass.
bool IsBlockLayoutComplete(const NGConstraintSpace&, const NGLayoutResult&);
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_UTILS_H_
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