Commit 4c95eae8 authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[NGFragmentItem] Add MoveToNextSkippingChildren

This patch adds |MoveToNextSkippingChildren| to
|NGInlineCursor|.

To support this, this patch also adds |ChildrenCount| to
|NGFragmentItem| so that we can compute the box tree
structure from a flat list of |FragmentItem|.

Bug: 982194
Change-Id: I0a5768d7cc8d522a52c5d7bb1556390583e6d172
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1746333Reviewed-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@{#686365}
parent 4dc52145
......@@ -73,6 +73,17 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient {
const PhysicalOffset& Offset() const { return offset_; }
void SetOffset(const PhysicalOffset& offset) { offset_ = offset; }
// Count of following items that are descendants of this item in the box tree,
// including this item. 1 means this is a box (box or line box) without
// children. 0 if this item type cannot have children.
wtf_size_t ChildrenCount() const {
if (Type() == kBox)
return box_.children_count;
if (Type() == kLine)
return line_.children_count;
return 0;
}
// DisplayItemClient overrides
String DebugName() const override;
IntRect VisualRect() const override;
......
......@@ -35,7 +35,7 @@ void NGFragmentItemsBuilder::AddLine(const NGPhysicalLineBoxFragment& line,
items_.Grow(line_start_index + 1);
offsets_.Grow(line_start_index + 1);
AddItems({children.begin(), children.size()});
AddItems(children.begin(), children.end());
// All children are added. Create an item for the start of the line.
wtf_size_t item_count = items_.size() - line_start_index;
......@@ -44,16 +44,18 @@ void NGFragmentItemsBuilder::AddLine(const NGPhysicalLineBoxFragment& line,
// traversals.
}
void NGFragmentItemsBuilder::AddItems(base::span<Child> children) {
void NGFragmentItemsBuilder::AddItems(Child* child_begin, Child* child_end) {
DCHECK_EQ(items_.size(), offsets_.size());
for (auto& child : children) {
for (Child* child_iter = child_begin; child_iter != child_end;) {
Child& child = *child_iter;
if (const NGPhysicalTextFragment* text = child.fragment.get()) {
DCHECK(text->TextShapeResult());
DCHECK_EQ(text->StartOffset(), text->TextShapeResult()->StartIndex());
DCHECK_EQ(text->EndOffset(), text->TextShapeResult()->EndIndex());
items_.push_back(std::make_unique<NGFragmentItem>(*text));
offsets_.push_back(child.offset);
++child_iter;
continue;
}
......@@ -62,9 +64,19 @@ void NGFragmentItemsBuilder::AddItems(base::span<Child> children) {
wtf_size_t box_start_index = items_.size();
items_.Grow(box_start_index + 1);
offsets_.push_back(child.offset);
// TODO(kojii): Add children and update children_count.
// All children are added. Create an item for the start of the box.
// Add all children, including their desendants, skipping this item.
CHECK_GE(child.children_count, 1u); // 0 will loop infinitely.
Child* end_child_iter = child_iter + child.children_count;
CHECK_LE(end_child_iter - child_begin, child_end - child_begin);
AddItems(child_iter + 1, end_child_iter);
child_iter = end_child_iter;
// All children are added. Compute how many items are actually added. The
// number of items added maybe different from |child.children_count|.
wtf_size_t item_count = items_.size() - box_start_index;
// Create an item for the start of the box.
const NGPhysicalBoxFragment& box =
To<NGPhysicalBoxFragment>(child.layout_result->PhysicalFragment());
items_[box_start_index] =
......@@ -72,7 +84,10 @@ void NGFragmentItemsBuilder::AddItems(base::span<Child> children) {
continue;
}
// OOF children should have been added to their parent box fragments.
// TODO(kojii): Consider handling them in NGFragmentItem too.
DCHECK(!child.out_of_flow_positioned_box);
++child_iter;
}
}
......
......@@ -32,7 +32,7 @@ class CORE_EXPORT NGFragmentItemsBuilder {
}
void SetTextContent(const NGInlineNode& node);
// Add a line at once.
// Add a line at once. The children in the given list maybe moved out.
using Child = NGLineBoxFragmentBuilder::Child;
using ChildList = NGLineBoxFragmentBuilder::ChildList;
void AddLine(const NGPhysicalLineBoxFragment& line, ChildList& children);
......@@ -45,7 +45,7 @@ class CORE_EXPORT NGFragmentItemsBuilder {
void* data);
private:
void AddItems(base::span<Child> children);
void AddItems(Child* child_begin, Child* child_end);
void ConvertToPhysical(WritingMode writing_mode,
TextDirection direction,
......
......@@ -572,18 +572,24 @@ void NGInlineLayoutStateStack::CreateBoxFragments(
unsigned start = box_data.fragment_start;
unsigned end = box_data.fragment_end;
DCHECK_GT(end, start);
NGLineBoxFragmentBuilder::Child& start_child = (*line_box)[start];
NGLineBoxFragmentBuilder::Child* child = &(*line_box)[start];
scoped_refptr<const NGLayoutResult> box_fragment =
box_data.CreateBoxFragment(line_box);
if (!start_child.HasFragment()) {
start_child.layout_result = std::move(box_fragment);
start_child.offset = box_data.offset;
if (!child->HasFragment()) {
child->layout_result = std::move(box_fragment);
child->offset = box_data.offset;
child->children_count = end - start;
} else {
// In most cases, |start_child| is moved to the children of the box, and
// is empty. It's not empty when it's out-of-flow. Insert in such case.
// TODO(kojii): With |NGFragmentItem|, all cases hit this code. Consider
// creating an empty item beforehand to avoid inserting.
line_box->InsertChild(start, std::move(box_fragment), box_data.offset,
LayoutUnit(), 0);
ChildInserted(start + 1);
child = &(*line_box)[start];
child->children_count = end - start + 1;
}
}
......
......@@ -65,15 +65,43 @@ bool NGInlineCursor::MoveToNext() {
return false;
}
bool NGInlineCursor::MoveToNextItem() {
bool NGInlineCursor::MoveToNextSkippingChildren() {
if (items_)
return MoveToNextItemSkippingChildren();
if (root_paint_fragment_)
return MoveToNextPaintFragmentSkippingChildren();
NOTREACHED();
return false;
}
bool NGInlineCursor::MoveToItem(unsigned item_index) {
DCHECK(items_);
if (next_item_index_ < items_->Items().size()) {
current_item_ = items_->Items()[next_item_index_++].get();
if (item_index < items_->Items().size()) {
current_item_index_ = item_index;
current_item_ = items_->Items()[item_index].get();
return true;
}
return false;
}
bool NGInlineCursor::MoveToNextItem() {
DCHECK(items_);
return MoveToItem(current_item_ ? current_item_index_ + 1
: current_item_index_);
}
bool NGInlineCursor::MoveToNextItemSkippingChildren() {
DCHECK(items_);
if (UNLIKELY(!current_item_))
return false;
// If the current item has |ChildrenCount|, add it to move to the next
// sibling, skipping all children and their descendants.
if (wtf_size_t children_count = current_item_->ChildrenCount()) {
return MoveToItem(current_item_index_ + children_count);
}
return MoveToNextItem();
}
bool NGInlineCursor::MoveToParentPaintFragment() {
DCHECK(root_paint_fragment_ && current_paint_fragment_);
const NGPaintFragment* parent = current_paint_fragment_->Parent();
......
......@@ -50,6 +50,10 @@ class CORE_EXPORT NGInlineCursor {
// Move the current position to the next fragment in pre-order DFS. Returns
// |true| if the move was successful.
bool MoveToNext();
// Same as |MoveToNext| except that this skips children even if they exist.
bool MoveToNextSkippingChildren();
// TODO(kojii): Add more variations as needed, NextSibling,
// NextSkippingChildren, Previous, etc.
......@@ -57,7 +61,9 @@ class CORE_EXPORT NGInlineCursor {
void SetRoot(const NGFragmentItems* items);
void SetRoot(const NGPaintFragment* root_paint_fragment);
bool MoveToItem(unsigned item_index);
bool MoveToNextItem();
bool MoveToNextItemSkippingChildren();
bool MoveToParentPaintFragment();
bool MoveToNextPaintFragment();
......@@ -70,7 +76,7 @@ class CORE_EXPORT NGInlineCursor {
const NGFragmentItem* current_item_ = nullptr;
const NGPaintFragment* current_paint_fragment_ = nullptr;
unsigned next_item_index_ = 0;
unsigned current_item_index_ = 0;
};
} // namespace blink
......
......@@ -77,4 +77,34 @@ TEST_P(NGInlineCursorTest, Next) {
"#span2", "text3", "text4", "text5"));
}
TEST_P(NGInlineCursorTest, NextSkippingChildren) {
SetBodyInnerHTML(R"HTML(
<style>
span { background: gray; }
</style>
<div id=root>
text1
<span id="span1">
text2
<span id="span2">
text3
</span>
text4
</span>
text5
</div>
)HTML");
LayoutBlockFlow* block_flow =
To<LayoutBlockFlow>(GetLayoutObjectByElementId("root"));
NGInlineCursor cursor(block_flow);
for (unsigned i = 0; i < 4; ++i)
cursor.MoveToNext();
EXPECT_EQ("text2", ToDebugString(cursor));
Vector<String> list;
while (cursor.MoveToNextSkippingChildren())
list.push_back(ToDebugString(cursor));
EXPECT_THAT(list, ElementsAre("#span2", "text4", "text5"));
}
} // namespace blink
......@@ -91,6 +91,10 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
// The index of |box_data_list_|, used in |PrepareForReorder()| and
// |UpdateAfterReorder()| to track children of boxes across BiDi reorder.
unsigned box_data_index = 0;
// For an inline box, shows the number of descendant |Child|ren, including
// empty ones. Includes itself, so 1 means no descendants. 0 if not an
// inline box. Available only after |CreateBoxFragments()|.
unsigned children_count = 0;
UBiDiLevel bidi_level = 0xff;
// The current text direction for OOF positioned items.
TextDirection container_direction = TextDirection::kLtr;
......
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