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