Commit 92a8ad33 authored by Alison Maher's avatar Alison Maher Committed by Chromium LUCI CQ

[LayoutNG] Store inner multicols with pending OOFs

To handle layout of OOF positioned elements inside a nested
fragmentation context, we will delay layout of the OOFs until they've
reached the outermost context, at which point they will get laid out
inside the inner multicol in which their containing block was found.

This change stores such inner multicols in the container builder
and physical fragment as a means to propagate the associated inner
multicols up to the outer context where their OOFs will be laid out.

This change also adds logic to halt the propagation of OOF positioned
fragmentainer descendants up the tree if we've reached a multicol.
In this case, we would know that we are in a nested context, and
any OOFs that have reached their containing block should remain
in the inner multicol's physical fragment rather than continuing up
the tree.

See design doc for more details:
https://docs.google.com/document/d/13Pn7SSJjzskg9kQ4jErzlJrj2j4zGBfNBFLEeDVENUU/edit?usp=sharing

Note: The OOF elements inside a nested context are not yet getting
laid out, which will cause related tests to crash. This will be
addressed in follow-up changes.

Bug: 1079031
Change-Id: I87399a67c6fc5021f3323afe8fae0b15e6e13eaa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2612392
Commit-Queue: Alison Maher <almaher@microsoft.com>
Reviewed-by: default avatarMorten Stenshorne <mstensho@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841140}
parent 741deac1
......@@ -244,6 +244,18 @@ class CORE_EXPORT NGBlockNode : public NGLayoutInputNode {
LayoutUnit percentage_resolution_inline_size) const;
};
// The NGBlockNode hash is based on its LayoutBox member.
struct NGBlockNodeHash : PtrHash<const LayoutBox*> {
STATIC_ONLY(NGBlockNodeHash);
static unsigned GetHash(const NGBlockNode& key) {
return PtrHash<const LayoutBox>::GetHash(key.GetLayoutBox());
}
static bool Equal(const NGBlockNode& a, const NGBlockNode& b) {
return PtrHash<const LayoutBox>::Equal(a.GetLayoutBox(), b.GetLayoutBox());
}
static const bool safe_to_compare_to_empty_or_deleted = true;
};
template <>
struct DowncastTraits<NGBlockNode> {
static bool AllowFrom(const NGLayoutInputNode& node) {
......@@ -253,4 +265,31 @@ struct DowncastTraits<NGBlockNode> {
} // namespace blink
namespace WTF {
template <>
struct DefaultHash<blink::NGBlockNode> {
STATIC_ONLY(DefaultHash);
typedef blink::NGBlockNodeHash Hash;
};
template <>
struct HashTraits<blink::NGBlockNode>
: public GenericHashTraits<blink::NGBlockNode> {
static const bool kEmptyValueIsZero = false;
static const bool kHasIsEmptyValueFunction = true;
static bool IsEmptyValue(const blink::NGBlockNode& value) {
return !value.GetLayoutBox();
}
static blink::NGBlockNode EmptyValue() { return nullptr; }
static void ConstructDeletedValue(blink::NGBlockNode& slot, bool) {
slot = nullptr;
}
static bool IsDeletedValue(const blink::NGBlockNode& value) {
return IsEmptyValue(value);
}
};
} // namespace WTF
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_NODE_H_
......@@ -299,6 +299,15 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
FinishFragmentation(Node(), ConstraintSpace(), BorderPadding().block_end,
FragmentainerSpaceAtBfcStart(ConstraintSpace()),
&container_builder_);
// OOF positioned elements inside a nested fragmentation context are laid
// out at the outermost context. If this multicol has OOF positioned
// elements pending layout, store its node for later use.
if (container_builder_.HasOutOfFlowFragmentainerDescendants()) {
// TODO(almaher): Run layout on the pending OOFs once we hit the
// outermost fragmentation context.
container_builder_.AddMulticolWithPendingOOFs(Node());
}
}
NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), &container_builder_).Run();
......
......@@ -61,7 +61,7 @@ void NGContainerFragmentBuilder::PropagateChildData(
new_inline_container);
}
PropagateOOFPositionedFragmentainerDescendants(child, child_offset);
PropagateOOFPositionedInfo(child, child_offset);
// We only need to report if inflow or floating elements depend on the
// percentage resolution block-size. OOF-positioned children resolve their
......@@ -240,6 +240,18 @@ void NGContainerFragmentBuilder::SwapOutOfFlowPositionedCandidates(
has_oof_candidate_that_needs_block_offset_adjustment_ = false;
}
void NGContainerFragmentBuilder::AddMulticolWithPendingOOFs(
const NGBlockNode& multicol) {
DCHECK(To<LayoutBlockFlow>(multicol.GetLayoutBox())->MultiColumnFlowThread());
multicols_with_pending_oofs_.insert(multicol);
}
void NGContainerFragmentBuilder::SwapMulticolsWithPendingOOFs(
HashSet<NGBlockNode>* multicols_with_pending_oofs) {
DCHECK(multicols_with_pending_oofs->IsEmpty());
std::swap(multicols_with_pending_oofs_, *multicols_with_pending_oofs);
}
void NGContainerFragmentBuilder::SwapOutOfFlowFragmentainerDescendants(
Vector<NGLogicalOutOfFlowPositionedNode>* descendants) {
DCHECK(descendants->IsEmpty());
......@@ -274,14 +286,28 @@ void NGContainerFragmentBuilder::
}
}
void NGContainerFragmentBuilder::PropagateOOFPositionedFragmentainerDescendants(
void NGContainerFragmentBuilder::PropagateOOFPositionedInfo(
const NGPhysicalContainerFragment& fragment,
LogicalOffset offset) {
const NGPhysicalBoxFragment* box_fragment =
DynamicTo<NGPhysicalBoxFragment>(&fragment);
// Fragmentainer descendants are only on box fragments.
if (!box_fragment ||
!box_fragment->HasOutOfFlowPositionedFragmentainerDescendants()) {
if (!box_fragment)
return;
if (box_fragment->HasMulticolsWithPendingOOFs()) {
const auto& multicols_with_pending_oofs =
box_fragment->MulticolsWithPendingOOFs();
for (const NGBlockNode& multicol : multicols_with_pending_oofs)
AddMulticolWithPendingOOFs(multicol);
}
// If we find a multicol with OOF positioned fragmentainer descendants,
// then that multicol is an inner multicol with pending OOFs. Those OOFs
// will be laid out inside the inner multicol when we reach the outermost
// fragmentation context, so we should not propagate those OOFs up the tree
// any further.
if (!box_fragment->HasOutOfFlowPositionedFragmentainerDescendants() ||
box_fragment->IsFragmentationContextRoot()) {
return;
}
......
......@@ -128,12 +128,22 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
void AddOutOfFlowDescendant(
const NGLogicalOutOfFlowPositionedNode& descendant);
// Out-of-flow positioned elements inside a nested fragmentation context
// are laid out once they've reached the outermost fragmentation context.
// However, once at the outer context, they will get laid out inside the
// inner multicol in which their containing block resides. Thus, we need to
// store such inner multicols for later use.
void AddMulticolWithPendingOOFs(const NGBlockNode& multicol);
void SwapOutOfFlowPositionedCandidates(
Vector<NGLogicalOutOfFlowPositionedNode>* candidates);
void SwapOutOfFlowFragmentainerDescendants(
Vector<NGLogicalOutOfFlowPositionedNode>* descendants);
void SwapMulticolsWithPendingOOFs(
HashSet<NGBlockNode>* multicols_with_pending_oofs);
bool HasOutOfFlowPositionedCandidates() const {
return !oof_positioned_candidates_.IsEmpty();
}
......@@ -142,6 +152,10 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
return !oof_positioned_fragmentainer_descendants_.IsEmpty();
}
bool HasMulticolsWithPendingOOFs() const {
return !multicols_with_pending_oofs_.IsEmpty();
}
// This method should only be used within the inline layout algorithm. It is
// used to convert all OOF-positioned candidates to descendants.
//
......@@ -155,9 +169,9 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
// descendants on the fragment are NGPhysicalOutOfFlowPositionedNodes, we
// first have to create NGLogicalOutOfFlowPositionedNodes copies before
// appending them to our list of descendants.
void PropagateOOFPositionedFragmentainerDescendants(
const NGPhysicalContainerFragment& fragment,
LogicalOffset offset);
// In addition, propagate any inner multicols with pending OOF descendants.
void PropagateOOFPositionedInfo(const NGPhysicalContainerFragment& fragment,
LogicalOffset offset);
void SetIsSelfCollapsing() { is_self_collapsing_ = true; }
......@@ -246,6 +260,8 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
oof_positioned_fragmentainer_descendants_;
Vector<NGLogicalOutOfFlowPositionedNode> oof_positioned_descendants_;
HashSet<NGBlockNode> multicols_with_pending_oofs_;
NGUnpositionedListMarker unpositioned_list_marker_;
ChildrenVector children_;
......
......@@ -641,7 +641,7 @@ void NGOutOfFlowLayoutPart::LayoutFragmentainerDescendants(
for (auto& descendant : *descendants) {
scoped_refptr<const NGLayoutResult> result =
LayoutFragmentainerDescendant(descendant);
container_builder_->PropagateOOFPositionedFragmentainerDescendants(
container_builder_->PropagateOOFPositionedInfo(
result->PhysicalFragment(), result->OutOfFlowPositionedOffset());
}
// Sweep any descendants that might have been bubbled up from the fragment
......
......@@ -1305,7 +1305,9 @@ TEST_F(NGOutOfFlowLayoutPartTest,
}
// Fragmented OOF element inside a nested multi-column.
TEST_F(NGOutOfFlowLayoutPartTest, AbsposNestedFragmentation) {
// TODO(almaher): Re-enable once layout is run on the pending OOFs of inner
// multicols inside a nested fragmentation context.
TEST_F(NGOutOfFlowLayoutPartTest, DISABLED_AbsposNestedFragmentation) {
SetBodyInnerHTML(
R"HTML(
<style>
......
......@@ -102,6 +102,7 @@ scoped_refptr<const NGPhysicalBoxFragment> NGPhysicalBoxFragment::Create(
bool has_rare_data =
builder->mathml_paint_info_ ||
!builder->oof_positioned_fragmentainer_descendants_.IsEmpty() ||
!builder->multicols_with_pending_oofs_.IsEmpty() ||
builder->table_grid_rect_ || builder->table_column_geometries_ ||
builder->table_collapsed_borders_ ||
builder->table_collapsed_borders_geometry_ ||
......@@ -326,6 +327,10 @@ NGPhysicalBoxFragment::RareData::RareData(NGBoxFragmentBuilder* builder,
: PhysicalSize()),
descendant.containing_block_fragment);
}
if (builder->HasMulticolsWithPendingOOFs()) {
multicols_with_pending_oofs =
std::move(builder->multicols_with_pending_oofs_);
}
if (builder->table_grid_rect_)
table_grid_rect = *builder->table_grid_rect_;
if (builder->table_column_geometries_)
......@@ -343,6 +348,7 @@ NGPhysicalBoxFragment::RareData::RareData(NGBoxFragmentBuilder* builder,
NGPhysicalBoxFragment::RareData::RareData(const RareData& other)
: oof_positioned_fragmentainer_descendants(
other.oof_positioned_fragmentainer_descendants),
multicols_with_pending_oofs(other.multicols_with_pending_oofs),
mathml_paint_info(other.mathml_paint_info
? new NGMathMLPaintInfo(*other.mathml_paint_info)
: nullptr),
......
......@@ -154,6 +154,20 @@ class CORE_EXPORT NGPhysicalBoxFragment final
return {descendants.data(), descendants.size()};
}
bool HasMulticolsWithPendingOOFs() const {
if (!has_rare_data_)
return false;
return !ComputeRareDataAddress()->multicols_with_pending_oofs.IsEmpty();
}
HashSet<NGBlockNode> MulticolsWithPendingOOFs() const {
if (!has_rare_data_)
return HashSet<NGBlockNode>();
return const_cast<HashSet<NGBlockNode>&>(
ComputeRareDataAddress()->multicols_with_pending_oofs);
}
NGPixelSnappedPhysicalBoxStrut PixelSnappedPadding() const {
if (!has_padding_)
return NGPixelSnappedPhysicalBoxStrut();
......@@ -305,6 +319,7 @@ class CORE_EXPORT NGPhysicalBoxFragment final
Vector<NGPhysicalOutOfFlowPositionedNode>
oof_positioned_fragmentainer_descendants;
HashSet<NGBlockNode> multicols_with_pending_oofs;
const std::unique_ptr<const NGMathMLPaintInfo> mathml_paint_info;
// TablesNG rare data.
......
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