Commit 7723556d authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[LayoutNG] Add |PostLayoutChildren()|

This patch adds |PostLayoutChildren()|, and use it for the
fix made at r661990 (crrev.com/c/1621796).

When subtree layout occurs, ancestors of the subtree root may
keep the reference to the old fragments. The new collection
|PostLayoutChildren()| returns the latest fragments.

Changing all uses of |Children()| to this does not work:
1. Some callers expect fragments being built during the
   layout, not the latest post-layout. Simply replacing
   breaks such callers.
2. This collection is not as fast as I wish it to be.
   Need to optimize/cache |IsRelayoutBoundary()| before
   using this in more performance critical work.
3. The best way to handle this situation isn't clear yet.
   Instead of this on-the-fly replacement, we may want to
   find all refences and patch them, as we tried in
   crrev.com/c/1622907. Maybe we need to support more
   generations. This is an architectual decision to be
   discussed more.

This addition makes it easier to apply to other places if
needed, and makes merging easier if we need to after the
branch point, but where to apply is in future investigation.

Bug: 965639
Change-Id: Idd4b539e88465cf0b810ae249fdcb87c0b19ae45
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1624010Reviewed-by: default avatarAleks Totic <atotic@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#662988}
parent d2ab9e87
......@@ -26,6 +26,14 @@ struct CORE_EXPORT NGLink {
const NGPhysicalFragment& operator*() const { return *fragment; }
const NGPhysicalFragment* operator->() const { return fragment; }
// Returns a |NGLink| with newer generation if exists, or |this|. See
// |NGPhysicalFragment::PostLayout()| for more details.
const NGLink PostLayout() const {
if (const NGPhysicalFragment* new_fragment = fragment->PostLayout())
return {new_fragment, offset};
return *this;
}
const NGPhysicalFragment* fragment;
PhysicalOffset offset;
};
......
......@@ -67,7 +67,7 @@ void NGPhysicalContainerFragment::AddOutlineRectsForNormalChildren(
const PhysicalOffset& additional_offset,
NGOutlineType outline_type,
const LayoutBoxModelObject* containing_block) const {
for (const auto& child : Children()) {
for (const auto& child : PostLayoutChildren()) {
// Outlines of out-of-flow positioned descendants are handled in
// NGPhysicalBoxFragment::AddSelfOutlineRects().
if (child->IsOutOfFlowPositioned())
......@@ -83,16 +83,6 @@ void NGPhysicalContainerFragment::AddOutlineRectsForNormalChildren(
if (child_layout_object->IsElementContinuation() ||
child_layout_block_flow->IsAnonymousBlockContinuation())
continue;
if (child_layout_block_flow->IsRelayoutBoundary()) {
const NGPhysicalBoxFragment* maybe_new_child_fragment =
child_layout_block_flow->CurrentFragment();
if (maybe_new_child_fragment) {
AddOutlineRectsForDescendant(
{maybe_new_child_fragment, child.Offset()}, outline_rects,
additional_offset, outline_type, containing_block);
continue;
}
}
}
}
AddOutlineRectsForDescendant(child, outline_rects, additional_offset,
......
......@@ -20,32 +20,100 @@ enum class NGOutlineType;
class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment {
public:
class ChildLinkList {
class ChildLinkListBase {
public:
ChildLinkList(wtf_size_t count, const NGLink* buffer)
ChildLinkListBase(wtf_size_t count, const NGLink* buffer)
: count_(count), buffer_(buffer) {}
wtf_size_t size() const { return count_; }
bool IsEmpty() const { return count_ == 0; }
protected:
wtf_size_t count_;
const NGLink* buffer_;
};
class ChildLinkList : public ChildLinkListBase {
public:
using ChildLinkListBase::ChildLinkListBase;
const NGLink& operator[](wtf_size_t idx) const { return buffer_[idx]; }
const NGLink& front() const { return buffer_[0]; }
const NGLink& back() const { return buffer_[count_ - 1]; }
const NGLink* begin() const { return buffer_; }
const NGLink* end() const { return begin() + count_; }
};
bool IsEmpty() const { return count_ == 0; }
private:
wtf_size_t count_;
const NGLink* buffer_;
// Same as |ChildLinkList|, except that each |NGLink| has the latest
// generation of post-layout. See |NGPhysicalFragment::UpdatedFragment()| for
// more details.
class PostLayoutChildLinkList : public ChildLinkListBase {
public:
using ChildLinkListBase::ChildLinkListBase;
class ConstIterator {
STACK_ALLOCATED();
public:
ConstIterator(const NGLink* current) : current_(current) {}
const NGLink& operator*() const { return *PostLayoutOrCurrent(); }
const NGLink* operator->() const { return PostLayoutOrCurrent(); }
ConstIterator& operator++() {
++current_;
return *this;
}
bool operator==(const ConstIterator& other) const {
return current_ == other.current_;
}
bool operator!=(const ConstIterator& other) const {
return current_ != other.current_;
}
private:
const NGLink* PostLayoutOrCurrent() const {
post_layout_.fragment = current_->fragment->PostLayout();
if (!post_layout_.fragment)
return current_;
post_layout_.offset = current_->offset;
return &post_layout_;
}
const NGLink* current_;
mutable NGLink post_layout_;
};
using const_iterator = ConstIterator;
const_iterator begin() const { return const_iterator(buffer_); }
const_iterator end() const { return const_iterator(buffer_ + count_); }
const NGLink operator[](wtf_size_t idx) const {
CHECK_LT(idx, count_);
return buffer_[idx].PostLayout();
}
const NGLink front() const { return (*this)[0]; }
const NGLink back() const { return (*this)[count_ - 1]; }
};
~NGPhysicalContainerFragment();
// Returns the children of |this|.
//
// Note, children in this collection maybe old generations. Items in this
// collection are safe, but their children (grandchildren of |this|) maybe
// from deleted nodes or LayoutObjects. Also see |PostLayoutChildren()|.
ChildLinkList Children() const {
return ChildLinkList(num_children_, buffer_);
}
// Similar to |Children()| but all children are the latest generation of
// post-layout, and therefore all descendants are safe.
PostLayoutChildLinkList PostLayoutChildren() const {
return PostLayoutChildLinkList(num_children_, buffer_);
}
bool HasFloatingDescendants() const { return has_floating_descendants_; }
bool HasOrthogonalFlowRoots() const { return has_orthogonal_flow_roots_; }
......
......@@ -347,6 +347,19 @@ bool NGPhysicalFragment::IsPlacedByLayoutNG() const {
return container->IsLayoutNGMixin() || container->IsLayoutNGFlexibleBox();
}
const NGPhysicalFragment* NGPhysicalFragment::PostLayout() const {
if (IsBox() && !IsInlineBox()) {
if (const auto* block = DynamicTo<LayoutBlockFlow>(GetLayoutObject())) {
if (block->IsRelayoutBoundary()) {
const NGPhysicalFragment* new_fragment = block->CurrentFragment();
if (new_fragment && new_fragment != this)
return new_fragment;
}
}
}
return nullptr;
}
#if DCHECK_IS_ON()
void NGPhysicalFragment::CheckCanUpdateInkOverflow() const {
if (!GetLayoutObject())
......
......@@ -206,6 +206,14 @@ class CORE_EXPORT NGPhysicalFragment
return !IsLineBox() ? &layout_object_ : nullptr;
}
// Returns the latest generation of the post-layout fragment. Returns
// |nullptr| if |this| is the one.
//
// When subtree relayout occurs at the relayout boundary, its containing block
// may keep the reference to old generations of this fragment. Callers can
// check if there were newer generations.
const NGPhysicalFragment* PostLayout() const;
// Scrollable overflow. including contents, in the local coordinate.
PhysicalRect ScrollableOverflow() const;
......
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