Commit 5c50cb17 authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

Block fragmentation support in NGFragmentItems::DirtyLines*

This patch supports block fragmentation by:
1. |TryDirtyFirstLineFor| has micro-optimization by not using
   |NGInlineCursor| but calls |FirstInlineFragmentItemIndex|
   directly. This may crash when |LayoutObject| is not in the
   first fragment. This patch fixes this issue by using
   |NGInlineCursor| and its built-in block fragmentation
   support done in <crbug.com/1112657>.
2. Turn all functions to |static|, because which
   |NGFragmentItems| has the item to mark dirty isn't clear
   for callers.
3. The work 2 above eliminates two calls to|FragmentItems|,
   which calls |CurrentFragment|.

No perf impacts were observed.
layout: https://pinpoint-dot-chromeperf.appspot.com/job/16a9d285520000
paint: https://pinpoint-dot-chromeperf.appspot.com/job/17fba859520000

Bug: 1061423, 829028
Change-Id: I924ebe4eabcb9ec096830b8ecf53edbb63504f75
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2505262Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarMorten Stenshorne <mstensho@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822580}
parent 7f16e303
...@@ -1680,10 +1680,8 @@ void LayoutInline::DirtyLinesFromChangedChild( ...@@ -1680,10 +1680,8 @@ void LayoutInline::DirtyLinesFromChangedChild(
NOT_DESTROYED(); NOT_DESTROYED();
if (IsInLayoutNGInlineFormattingContext()) { if (IsInLayoutNGInlineFormattingContext()) {
if (UNLIKELY(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())) { if (UNLIKELY(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())) {
if (const LayoutBlockFlow* container = FragmentItemsContainer()) { if (const LayoutBlockFlow* container = FragmentItemsContainer())
if (const NGFragmentItems* items = container->FragmentItems()) NGFragmentItems::DirtyLinesFromChangedChild(*child, *container);
items->DirtyLinesFromChangedChild(*container, child);
}
return; return;
} }
SetAncestorLineBoxDirty(); SetAncestorLineBoxDirty();
......
...@@ -487,6 +487,13 @@ inline wtf_size_t LayoutInline::FirstInlineFragmentItemIndex() const { ...@@ -487,6 +487,13 @@ inline wtf_size_t LayoutInline::FirstInlineFragmentItemIndex() const {
return first_fragment_item_index_; return first_fragment_item_index_;
} }
template <>
struct DowncastTraits<LayoutInline> {
static bool AllowFrom(const LayoutObject& object) {
return object.IsLayoutInline();
}
};
DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutInline, IsLayoutInline()); DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutInline, IsLayoutInline());
} // namespace blink } // namespace blink
......
...@@ -251,75 +251,85 @@ const NGFragmentItem* NGFragmentItems::EndOfReusableItems( ...@@ -251,75 +251,85 @@ const NGFragmentItem* NGFragmentItems::EndOfReusableItems(
return nullptr; // all items are reusable. return nullptr; // all items are reusable.
} }
bool NGFragmentItems::TryDirtyFirstLineFor( // static
const LayoutObject& layout_object) const { bool NGFragmentItems::TryDirtyFirstLineFor(const LayoutObject& layout_object,
DCHECK(layout_object.IsInLayoutNGInlineFormattingContext()); const LayoutBlockFlow& container) {
DCHECK(!layout_object.IsFloatingOrOutOfFlowPositioned()); DCHECK(layout_object.IsDescendantOf(&container));
if (wtf_size_t index = layout_object.FirstInlineFragmentItemIndex()) { NGInlineCursor cursor(container);
const NGFragmentItem& item = Items()[index - 1]; cursor.MoveTo(layout_object);
DCHECK_EQ(&layout_object, item.GetLayoutObject()); if (!cursor)
item.SetDirty(); return false;
return true; DCHECK(cursor.Current().Item());
} DCHECK_EQ(&layout_object, cursor.Current().GetLayoutObject());
return false; cursor.Current()->SetDirty();
return true;
} }
bool NGFragmentItems::TryDirtyLastLineFor( // static
const LayoutBlockFlow& container, bool NGFragmentItems::TryDirtyLastLineFor(const LayoutObject& layout_object,
const LayoutObject& layout_object) const { const LayoutBlockFlow& container) {
DCHECK(layout_object.IsDescendantOf(&container));
NGInlineCursor cursor(container); NGInlineCursor cursor(container);
cursor.MoveTo(layout_object); cursor.MoveTo(layout_object);
if (!cursor) if (!cursor)
return false; return false;
cursor.MoveToLastForSameLayoutObject(); cursor.MoveToLastForSameLayoutObject();
DCHECK(cursor.Current().Item()); DCHECK(cursor.Current().Item());
const NGFragmentItem& item = *cursor.Current().Item(); DCHECK_EQ(&layout_object, cursor.Current().GetLayoutObject());
DCHECK_EQ(&layout_object, item.GetLayoutObject()); cursor.Current()->SetDirty();
item.SetDirty();
return true; return true;
} }
// static
void NGFragmentItems::DirtyLinesFromChangedChild( void NGFragmentItems::DirtyLinesFromChangedChild(
const LayoutBlockFlow& container, const LayoutObject& child,
const LayoutObject* child) const { const LayoutBlockFlow& container) {
if (UNLIKELY(!child)) { if (child.IsInLayoutNGInlineFormattingContext() &&
front().SetDirty(); !child.IsFloatingOrOutOfFlowPositioned()) {
return; if (TryDirtyFirstLineFor(child, container))
return;
} }
if (child->IsInLayoutNGInlineFormattingContext() &&
!child->IsFloatingOrOutOfFlowPositioned() && TryDirtyFirstLineFor(*child))
return;
// If |child| is new, or did not generate fragments, mark the fragments for // If |child| is new, or did not generate fragments, mark the fragments for
// previous |LayoutObject| instead. // previous |LayoutObject| instead.
while (true) { for (const LayoutObject* current = &child;;) {
if (const LayoutObject* previous = child->PreviousSibling()) { if (const LayoutObject* previous = current->PreviousSibling()) {
while (const LayoutInline* layout_inline = while (const auto* layout_inline = DynamicTo<LayoutInline>(previous)) {
ToLayoutInlineOrNull(previous)) {
if (const LayoutObject* last_child = layout_inline->LastChild()) if (const LayoutObject* last_child = layout_inline->LastChild())
previous = last_child; previous = last_child;
else else
break; break;
} }
child = previous; current = previous;
if (UNLIKELY(child->IsFloatingOrOutOfFlowPositioned())) if (UNLIKELY(current->IsFloatingOrOutOfFlowPositioned()))
continue; continue;
if (child->IsInLayoutNGInlineFormattingContext() && if (current->IsInLayoutNGInlineFormattingContext()) {
TryDirtyLastLineFor(container, *child)) if (TryDirtyLastLineFor(*current, container))
return; return;
}
continue; continue;
} }
child = child->Parent(); current = current->Parent();
if (!child || child->IsLayoutBlockFlow()) { if (!current || current->IsLayoutBlockFlow()) {
front().SetDirty(); DirtyFirstItem(container);
return; return;
} }
DCHECK(child->IsLayoutInline()); DCHECK(current->IsLayoutInline());
if (child->IsInLayoutNGInlineFormattingContext() && if (current->IsInLayoutNGInlineFormattingContext()) {
TryDirtyFirstLineFor(*child)) if (TryDirtyFirstLineFor(*current, container))
return;
}
}
}
// static
void NGFragmentItems::DirtyFirstItem(const LayoutBlockFlow& container) {
for (const NGPhysicalBoxFragment& fragment : container.PhysicalFragments()) {
if (const NGFragmentItems* items = fragment.Items()) {
items->front().SetDirty();
return; return;
}
} }
} }
...@@ -335,7 +345,7 @@ void NGFragmentItems::DirtyLinesFromNeedsLayout( ...@@ -335,7 +345,7 @@ void NGFragmentItems::DirtyLinesFromNeedsLayout(
for (LayoutObject* child = container->FirstChild(); child; for (LayoutObject* child = container->FirstChild(); child;
child = child->NextSibling()) { child = child->NextSibling()) {
if (child->NeedsLayout()) { if (child->NeedsLayout()) {
DirtyLinesFromChangedChild(*container, child); DirtyLinesFromChangedChild(*child, *container);
return; return;
} }
} }
......
...@@ -78,8 +78,8 @@ class CORE_EXPORT NGFragmentItems { ...@@ -78,8 +78,8 @@ class CORE_EXPORT NGFragmentItems {
const NGPhysicalBoxFragment& container) const; const NGPhysicalBoxFragment& container) const;
// Mark items dirty when |child| is removed from the tree. // Mark items dirty when |child| is removed from the tree.
void DirtyLinesFromChangedChild(const LayoutBlockFlow& container, static void DirtyLinesFromChangedChild(const LayoutObject& child,
const LayoutObject* child) const; const LayoutBlockFlow& container);
// Mark items dirty from |LayoutObject::NeedsLayout| flags. // Mark items dirty from |LayoutObject::NeedsLayout| flags.
void DirtyLinesFromNeedsLayout(const LayoutBlockFlow* block_flow) const; void DirtyLinesFromNeedsLayout(const LayoutBlockFlow* block_flow) const;
...@@ -98,9 +98,11 @@ class CORE_EXPORT NGFragmentItems { ...@@ -98,9 +98,11 @@ class CORE_EXPORT NGFragmentItems {
const NGFragmentItem* ItemsData() const { return items_; } const NGFragmentItem* ItemsData() const { return items_; }
static bool CanReuseAll(NGInlineCursor* cursor); static bool CanReuseAll(NGInlineCursor* cursor);
bool TryDirtyFirstLineFor(const LayoutObject& layout_object) const; static bool TryDirtyFirstLineFor(const LayoutObject& layout_object,
bool TryDirtyLastLineFor(const LayoutBlockFlow& container, const LayoutBlockFlow& container);
const LayoutObject& layout_object) const; static bool TryDirtyLastLineFor(const LayoutObject& layout_object,
const LayoutBlockFlow& container);
static void DirtyFirstItem(const LayoutBlockFlow& container);
String text_content_; String text_content_;
String first_line_text_content_; String first_line_text_content_;
......
...@@ -283,12 +283,9 @@ void LayoutNGBlockFlowMixin<Base>::DirtyLinesFromChangedChild( ...@@ -283,12 +283,9 @@ void LayoutNGBlockFlowMixin<Base>::DirtyLinesFromChangedChild(
// We need to dirty line box fragments only if the child is once laid out in // We need to dirty line box fragments only if the child is once laid out in
// LayoutNG inline formatting context. New objects are handled in // LayoutNG inline formatting context. New objects are handled in
// NGInlineNode::MarkLineBoxesDirty(). // NGInlineNode::MarkLineBoxesDirty().
if (child->IsInLayoutNGInlineFormattingContext()) { if (child->IsInLayoutNGInlineFormattingContext() &&
if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
if (const NGFragmentItems* items = Base::FragmentItems()) NGFragmentItems::DirtyLinesFromChangedChild(*child, *this);
items->DirtyLinesFromChangedChild(*this, child);
}
}
} }
template <typename Base> template <typename Base>
......
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