1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h"
6
7 #include "third_party/blink/renderer/core/layout/layout_object.h"
8 #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
9 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
10 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
11
12 namespace blink {
13
14 namespace {
15
16 // ------ Helpers for traversing inline fragments ------
17
IndexOf(const Vector<NGPaintFragment *,16> & fragments,const NGPaintFragment & fragment)18 unsigned IndexOf(const Vector<NGPaintFragment*, 16>& fragments,
19 const NGPaintFragment& fragment) {
20 auto* const* it = std::find_if(
21 fragments.begin(), fragments.end(),
22 [&fragment](const auto& child) { return &fragment == child; });
23 DCHECK(it != fragments.end());
24 return static_cast<unsigned>(std::distance(fragments.begin(), it));
25 }
26
27 } // namespace
28
NGPaintFragmentTraversal(const NGPaintFragment & root)29 NGPaintFragmentTraversal::NGPaintFragmentTraversal(const NGPaintFragment& root)
30 : current_(root.FirstChild()), root_(&root) {}
31
NGPaintFragmentTraversal(const NGPaintFragment & root,const NGPaintFragment & start)32 NGPaintFragmentTraversal::NGPaintFragmentTraversal(const NGPaintFragment& root,
33 const NGPaintFragment& start)
34 : root_(&root) {
35 MoveTo(start);
36 }
37
NGPaintFragmentTraversal(const NGPaintFragmentTraversal & other)38 NGPaintFragmentTraversal::NGPaintFragmentTraversal(
39 const NGPaintFragmentTraversal& other)
40 : current_(other.current_),
41 root_(other.root_),
42 current_index_(other.current_index_),
43 siblings_(other.siblings_) {}
44
NGPaintFragmentTraversal(NGPaintFragmentTraversal && other)45 NGPaintFragmentTraversal::NGPaintFragmentTraversal(
46 NGPaintFragmentTraversal&& other)
47 : current_(other.current_),
48 root_(other.root_),
49 current_index_(other.current_index_),
50 siblings_(std::move(other.siblings_)) {
51 other.current_ = nullptr;
52 }
53
54 NGPaintFragmentTraversal::NGPaintFragmentTraversal() = default;
55
operator =(const NGPaintFragmentTraversal & other)56 NGPaintFragmentTraversal& NGPaintFragmentTraversal::operator=(
57 const NGPaintFragmentTraversal& other) {
58 current_ = other.current_;
59 root_ = other.root_;
60 current_index_ = other.current_index_;
61 siblings_ = other.siblings_;
62 return *this;
63 }
64
EnsureIndex()65 void NGPaintFragmentTraversal::EnsureIndex() {
66 current_->Parent()->Children().ToList(&siblings_);
67 auto** const it =
68 std::find_if(siblings_.begin(), siblings_.end(),
69 [this](const auto& child) { return current_ == child; });
70 DCHECK(it != siblings_.end());
71 current_index_ = static_cast<unsigned>(std::distance(siblings_.begin(), it));
72 }
73
Reset()74 void NGPaintFragmentTraversal::Reset() {
75 current_ = nullptr;
76 current_index_ = 0;
77 siblings_.Shrink(0);
78 }
79
MoveTo(const NGPaintFragment & fragment)80 void NGPaintFragmentTraversal::MoveTo(const NGPaintFragment& fragment) {
81 DCHECK(fragment.IsDescendantOfNotSelf(*root_));
82 current_ = &fragment;
83 }
84
MoveToNext()85 void NGPaintFragmentTraversal::MoveToNext() {
86 if (IsAtEnd())
87 return;
88
89 if (const NGPaintFragment* first_child = current_->FirstChild())
90 return MoveToFirstChild();
91 MoveToNextSiblingOrAncestor();
92 }
93
MoveToNextSiblingOrAncestor()94 void NGPaintFragmentTraversal::MoveToNextSiblingOrAncestor() {
95 while (!IsAtEnd()) {
96 // Check if we have a next sibling.
97 if (const NGPaintFragment* next = current_->NextSibling()) {
98 current_ = next;
99 if (!siblings_.IsEmpty()) {
100 ++current_index_;
101 return;
102 }
103 EnsureIndex();
104 return;
105 }
106
107 MoveToParent();
108 }
109 }
110
MoveToParent()111 void NGPaintFragmentTraversal::MoveToParent() {
112 if (IsAtEnd())
113 return;
114
115 current_ = current_->Parent();
116 if (current_ == root_)
117 current_ = nullptr;
118 if (UNLIKELY(!siblings_.IsEmpty()))
119 siblings_.Shrink(0);
120 }
121
MoveToPrevious()122 void NGPaintFragmentTraversal::MoveToPrevious() {
123 if (IsAtEnd())
124 return;
125
126 if (siblings_.IsEmpty()) {
127 current_->Parent()->Children().ToList(&siblings_);
128 current_index_ = IndexOf(siblings_, *current_);
129 }
130
131 if (!current_index_) {
132 // There is no previous sibling of |current_|. We move to parent.
133 MoveToParent();
134 return;
135 }
136
137 current_ = siblings_[--current_index_];
138 while (current_->FirstChild())
139 MoveToLastChild();
140 }
141
142 NGPaintFragmentTraversal::AncestorRange
InclusiveAncestorsOf(const NGPaintFragment & start)143 NGPaintFragmentTraversal::InclusiveAncestorsOf(const NGPaintFragment& start) {
144 return AncestorRange(start);
145 }
146
147 NGPaintFragmentTraversal::InlineDescendantsRange
InlineDescendantsOf(const NGPaintFragment & container)148 NGPaintFragmentTraversal::InlineDescendantsOf(
149 const NGPaintFragment& container) {
150 return InlineDescendantsRange(container);
151 }
152
MoveToFirstChild()153 void NGPaintFragmentTraversal::MoveToFirstChild() {
154 DCHECK(current_->FirstChild());
155 current_ = current_->FirstChild();
156 current_index_ = 0;
157 if (UNLIKELY(!siblings_.IsEmpty()))
158 siblings_.Shrink(0);
159 }
160
MoveToLastChild()161 void NGPaintFragmentTraversal::MoveToLastChild() {
162 DCHECK(current_->FirstChild());
163 current_->Children().ToList(&siblings_);
164 DCHECK(!siblings_.IsEmpty());
165 current_index_ = siblings_.size() - 1;
166 current_ = siblings_[current_index_];
167 }
168
MoveToNextInlineLeaf()169 void NGPaintFragmentTraversal::MoveToNextInlineLeaf() {
170 while (!IsAtEnd() && !IsInlineLeaf())
171 MoveToNext();
172 do {
173 MoveToNext();
174 } while (!IsAtEnd() && !IsInlineLeaf());
175 }
176
MoveToPreviousInlineLeaf()177 void NGPaintFragmentTraversal::MoveToPreviousInlineLeaf() {
178 while (!IsAtEnd() && !IsInlineLeaf())
179 MoveToPrevious();
180 do {
181 MoveToPrevious();
182 } while (!IsAtEnd() && !IsInlineLeaf());
183 }
184
MoveToPreviousInlineLeafIgnoringLineBreak()185 void NGPaintFragmentTraversal::MoveToPreviousInlineLeafIgnoringLineBreak() {
186 do {
187 MoveToPreviousInlineLeaf();
188 } while (!IsAtEnd() && IsLineBreak());
189 }
190
MoveToNextInlineLeafIgnoringLineBreak()191 void NGPaintFragmentTraversal::MoveToNextInlineLeafIgnoringLineBreak() {
192 do {
193 MoveToNextInlineLeaf();
194 } while (!IsAtEnd() && IsLineBreak());
195 }
196
IsInlineLeaf() const197 bool NGPaintFragmentTraversal::IsInlineLeaf() const {
198 if (!current_->PhysicalFragment().IsInline())
199 return false;
200 return current_->PhysicalFragment().IsText() ||
201 current_->PhysicalFragment().IsAtomicInline();
202 }
203
IsLineBreak() const204 bool NGPaintFragmentTraversal::IsLineBreak() const {
205 DCHECK(current_->PhysicalFragment().IsInline());
206 auto* physical_text_fragment =
207 DynamicTo<NGPhysicalTextFragment>(current_->PhysicalFragment());
208 if (!physical_text_fragment)
209 return false;
210 return physical_text_fragment->IsLineBreak();
211 }
212
213 // ----
operator ->() const214 NGPaintFragment* NGPaintFragmentTraversal::AncestorRange::Iterator::operator->()
215 const {
216 DCHECK(current_);
217 return current_;
218 }
219
operator ++()220 void NGPaintFragmentTraversal::AncestorRange::Iterator::operator++() {
221 DCHECK(current_);
222 current_ = current_->Parent();
223 }
224
225 // ----
226
Iterator(const NGPaintFragment & container)227 NGPaintFragmentTraversal::InlineDescendantsRange::Iterator::Iterator(
228 const NGPaintFragment& container)
229 : container_(&container), current_(container.FirstChild()) {
230 if (!current_ || IsInlineFragment(*current_))
231 return;
232 operator++();
233 }
234
235 NGPaintFragment* NGPaintFragmentTraversal::InlineDescendantsRange::Iterator::
operator ->() const236 operator->() const {
237 DCHECK(current_);
238 return current_;
239 }
240
operator ++()241 void NGPaintFragmentTraversal::InlineDescendantsRange::Iterator::operator++() {
242 DCHECK(current_);
243 do {
244 current_ = Next(*current_);
245 } while (current_ && !IsInlineFragment(*current_));
246 }
247
248 // static
249 // Returns next fragment of |start| in DFS pre-order within |container_| and
250 // skipping descendants in block formatting context.
251 NGPaintFragment*
Next(const NGPaintFragment & start) const252 NGPaintFragmentTraversal::InlineDescendantsRange::Iterator::Next(
253 const NGPaintFragment& start) const {
254 if (ShouldTraverse(start) && start.FirstChild())
255 return start.FirstChild();
256 for (NGPaintFragment* runner = const_cast<NGPaintFragment*>(&start);
257 runner != container_; runner = runner->Parent()) {
258 if (NGPaintFragment* next_sibling = runner->NextSibling())
259 return next_sibling;
260 }
261 return nullptr;
262 }
263
264 // static
265 bool NGPaintFragmentTraversal::InlineDescendantsRange::Iterator::
IsInlineFragment(const NGPaintFragment & fragment)266 IsInlineFragment(const NGPaintFragment& fragment) {
267 return fragment.PhysicalFragment().IsInline() ||
268 fragment.PhysicalFragment().IsLineBox();
269 }
270
271 // static
ShouldTraverse(const NGPaintFragment & fragment)272 bool NGPaintFragmentTraversal::InlineDescendantsRange::Iterator::ShouldTraverse(
273 const NGPaintFragment& fragment) {
274 return fragment.PhysicalFragment().IsContainer() &&
275 !fragment.PhysicalFragment().IsFormattingContextRoot();
276 }
277
278 } // namespace blink
279