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(
NOT_DESTROYED();
if (IsInLayoutNGInlineFormattingContext()) {
if (UNLIKELY(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())) {
if (const LayoutBlockFlow* container = FragmentItemsContainer()) {
if (const NGFragmentItems* items = container->FragmentItems())
items->DirtyLinesFromChangedChild(*container, child);
}
if (const LayoutBlockFlow* container = FragmentItemsContainer())
NGFragmentItems::DirtyLinesFromChangedChild(*child, *container);
return;
}
SetAncestorLineBoxDirty();
......
......@@ -487,6 +487,13 @@ inline wtf_size_t LayoutInline::FirstInlineFragmentItemIndex() const {
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());
} // namespace blink
......
......@@ -251,75 +251,85 @@ const NGFragmentItem* NGFragmentItems::EndOfReusableItems(
return nullptr; // all items are reusable.
}
bool NGFragmentItems::TryDirtyFirstLineFor(
const LayoutObject& layout_object) const {
DCHECK(layout_object.IsInLayoutNGInlineFormattingContext());
DCHECK(!layout_object.IsFloatingOrOutOfFlowPositioned());
if (wtf_size_t index = layout_object.FirstInlineFragmentItemIndex()) {
const NGFragmentItem& item = Items()[index - 1];
DCHECK_EQ(&layout_object, item.GetLayoutObject());
item.SetDirty();
return true;
}
return false;
// static
bool NGFragmentItems::TryDirtyFirstLineFor(const LayoutObject& layout_object,
const LayoutBlockFlow& container) {
DCHECK(layout_object.IsDescendantOf(&container));
NGInlineCursor cursor(container);
cursor.MoveTo(layout_object);
if (!cursor)
return false;
DCHECK(cursor.Current().Item());
DCHECK_EQ(&layout_object, cursor.Current().GetLayoutObject());
cursor.Current()->SetDirty();
return true;
}
bool NGFragmentItems::TryDirtyLastLineFor(
const LayoutBlockFlow& container,
const LayoutObject& layout_object) const {
// static
bool NGFragmentItems::TryDirtyLastLineFor(const LayoutObject& layout_object,
const LayoutBlockFlow& container) {
DCHECK(layout_object.IsDescendantOf(&container));
NGInlineCursor cursor(container);
cursor.MoveTo(layout_object);
if (!cursor)
return false;
cursor.MoveToLastForSameLayoutObject();
DCHECK(cursor.Current().Item());
const NGFragmentItem& item = *cursor.Current().Item();
DCHECK_EQ(&layout_object, item.GetLayoutObject());
item.SetDirty();
DCHECK_EQ(&layout_object, cursor.Current().GetLayoutObject());
cursor.Current()->SetDirty();
return true;
}
// static
void NGFragmentItems::DirtyLinesFromChangedChild(
const LayoutBlockFlow& container,
const LayoutObject* child) const {
if (UNLIKELY(!child)) {
front().SetDirty();
return;
const LayoutObject& child,
const LayoutBlockFlow& container) {
if (child.IsInLayoutNGInlineFormattingContext() &&
!child.IsFloatingOrOutOfFlowPositioned()) {
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
// previous |LayoutObject| instead.
while (true) {
if (const LayoutObject* previous = child->PreviousSibling()) {
while (const LayoutInline* layout_inline =
ToLayoutInlineOrNull(previous)) {
for (const LayoutObject* current = &child;;) {
if (const LayoutObject* previous = current->PreviousSibling()) {
while (const auto* layout_inline = DynamicTo<LayoutInline>(previous)) {
if (const LayoutObject* last_child = layout_inline->LastChild())
previous = last_child;
else
break;
}
child = previous;
if (UNLIKELY(child->IsFloatingOrOutOfFlowPositioned()))
current = previous;
if (UNLIKELY(current->IsFloatingOrOutOfFlowPositioned()))
continue;
if (child->IsInLayoutNGInlineFormattingContext() &&
TryDirtyLastLineFor(container, *child))
return;
if (current->IsInLayoutNGInlineFormattingContext()) {
if (TryDirtyLastLineFor(*current, container))
return;
}
continue;
}
child = child->Parent();
if (!child || child->IsLayoutBlockFlow()) {
front().SetDirty();
current = current->Parent();
if (!current || current->IsLayoutBlockFlow()) {
DirtyFirstItem(container);
return;
}
DCHECK(child->IsLayoutInline());
if (child->IsInLayoutNGInlineFormattingContext() &&
TryDirtyFirstLineFor(*child))
DCHECK(current->IsLayoutInline());
if (current->IsInLayoutNGInlineFormattingContext()) {
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;
}
}
}
......@@ -335,7 +345,7 @@ void NGFragmentItems::DirtyLinesFromNeedsLayout(
for (LayoutObject* child = container->FirstChild(); child;
child = child->NextSibling()) {
if (child->NeedsLayout()) {
DirtyLinesFromChangedChild(*container, child);
DirtyLinesFromChangedChild(*child, *container);
return;
}
}
......
......@@ -78,8 +78,8 @@ class CORE_EXPORT NGFragmentItems {
const NGPhysicalBoxFragment& container) const;
// Mark items dirty when |child| is removed from the tree.
void DirtyLinesFromChangedChild(const LayoutBlockFlow& container,
const LayoutObject* child) const;
static void DirtyLinesFromChangedChild(const LayoutObject& child,
const LayoutBlockFlow& container);
// Mark items dirty from |LayoutObject::NeedsLayout| flags.
void DirtyLinesFromNeedsLayout(const LayoutBlockFlow* block_flow) const;
......@@ -98,9 +98,11 @@ class CORE_EXPORT NGFragmentItems {
const NGFragmentItem* ItemsData() const { return items_; }
static bool CanReuseAll(NGInlineCursor* cursor);
bool TryDirtyFirstLineFor(const LayoutObject& layout_object) const;
bool TryDirtyLastLineFor(const LayoutBlockFlow& container,
const LayoutObject& layout_object) const;
static bool TryDirtyFirstLineFor(const LayoutObject& layout_object,
const LayoutBlockFlow& container);
static bool TryDirtyLastLineFor(const LayoutObject& layout_object,
const LayoutBlockFlow& container);
static void DirtyFirstItem(const LayoutBlockFlow& container);
String text_content_;
String first_line_text_content_;
......
......@@ -283,12 +283,9 @@ void LayoutNGBlockFlowMixin<Base>::DirtyLinesFromChangedChild(
// 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
// NGInlineNode::MarkLineBoxesDirty().
if (child->IsInLayoutNGInlineFormattingContext()) {
if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
if (const NGFragmentItems* items = Base::FragmentItems())
items->DirtyLinesFromChangedChild(*this, child);
}
}
if (child->IsInLayoutNGInlineFormattingContext() &&
RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
NGFragmentItems::DirtyLinesFromChangedChild(*child, *this);
}
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