summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
blob: 383c5d217f314c7fea07c820d6bd579f7a0a77c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_PAINT_FRAGMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_PAINT_FRAGMENT_H_

#include <iterator>
#include <memory>

#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
#include "third_party/blink/renderer/core/scroll/scroll_types.h"
#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"

namespace blink {

class NGBlockBreakToken;
class NGInlineCursor;
struct LayoutSelectionStatus;
struct NGContainerInkOverflow;
enum class NGOutlineType;

// The NGPaintFragment contains a NGPhysicalFragment and geometry in the paint
// coordinate system.
//
// NGPhysicalFragment is limited to its parent coordinate system for caching and
// sharing subtree. This class makes it possible to compute visual rects in the
// parent transform node.
//
// NGPaintFragment is an ImageResourceObserver, which means that it gets
// notified when associated images are changed.
// This is used for 2 main use cases:
// - reply to 'background-image' as we need to invalidate the background in this
//   case.
//   (See https://drafts.csswg.org/css-backgrounds-3/#the-background-image)
// - image (<img>, svg <image>) or video (<video>) elements that are
//   placeholders for displaying them.
class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>,
                                    public DisplayItemClient {
 public:
  NGPaintFragment(scoped_refptr<const NGPhysicalFragment>,
                  PhysicalOffset offset,
                  NGPaintFragment*);
  ~NGPaintFragment() override;

  static scoped_refptr<NGPaintFragment> Create(
      scoped_refptr<const NGPhysicalFragment>,
      const NGBlockBreakToken* break_token,
      scoped_refptr<NGPaintFragment> previous_instance = nullptr);

  const NGPhysicalFragment& PhysicalFragment() const {
    CHECK(IsAlive());
    return *physical_fragment_;
  }

  static scoped_refptr<NGPaintFragment>* Find(scoped_refptr<NGPaintFragment>*,
                                              const NGBlockBreakToken*);

  template <typename Traverse>
  class List {
    STACK_ALLOCATED();

   public:
    explicit List(NGPaintFragment* first) : first_(first) {}

    class iterator final
        : public std::iterator<std::forward_iterator_tag, NGPaintFragment*> {
     public:
      explicit iterator(NGPaintFragment* first) : current_(first) {}

      NGPaintFragment* operator*() const { return current_; }
      NGPaintFragment* operator->() const { return current_; }
      iterator& operator++() {
        DCHECK(current_);
        current_ = Traverse::Next(current_);
        return *this;
      }
      bool operator==(const iterator& other) const {
        return current_ == other.current_;
      }
      bool operator!=(const iterator& other) const {
        return current_ != other.current_;
      }

     private:
      NGPaintFragment* current_;
    };

    CORE_EXPORT iterator begin() const { return iterator(first_); }
    CORE_EXPORT iterator end() const { return iterator(nullptr); }

    // Returns the first |NGPaintFragment| in |FragmentRange| as STL container.
    // It is error to call |front()| for empty range.
    NGPaintFragment& front() const;

    // Returns the last |NGPaintFragment| in |FragmentRange| as STL container.
    // It is error to call |back()| for empty range.
    // Note: The complexity of |back()| is O(n) where n is number of elements
    // in this |FragmentRange|.
    NGPaintFragment& back() const;

    // Returns number of fragments in this range. The complexity is O(n) where n
    // is number of elements.
    wtf_size_t size() const;
    CORE_EXPORT bool IsEmpty() const { return !first_; }

    void ToList(Vector<NGPaintFragment*, 16>*) const;

   private:
    NGPaintFragment* first_;
  };

  class TraverseNextSibling {
    STATIC_ONLY(TraverseNextSibling);

   public:
    static NGPaintFragment* Next(NGPaintFragment* current) {
      return current->NextSibling();
    }
  };
  using ChildList = List<TraverseNextSibling>;

  // The parent NGPaintFragment. This is nullptr for a root; i.e., when parent
  // is not for NGPaint. In the first phase, this means that this is a root of
  // an inline formatting context.
  NGPaintFragment* Parent() const { return parent_; }
  NGPaintFragment* FirstChild() const { return FirstAlive(first_child_.get()); }
  NGPaintFragment* NextSibling() const {
    return FirstAlive(next_sibling_.get());
  }
  NGPaintFragment* NextForSameLayoutObject() const {
    return next_for_same_layout_object_;
  }
  ChildList Children() const { return ChildList(FirstChild()); }
  bool IsEllipsis() const;

  // Note, as the name implies, |IsDescendantOfNotSelf| returns false for the
  // same object. This is different from |LayoutObject::IsDescendant| but is
  // same as |Node::IsDescendant|.
  bool IsDescendantOfNotSelf(const NGPaintFragment&) const;

  // Returns the root box containing this.
  const NGPaintFragment* Root() const;

  // Returns the first line box for a block-level container.
  NGPaintFragment* FirstLineBox() const;

  // Returns the container line box for inline fragments.
  const NGPaintFragment* ContainerLineBox() const;

  // Returns true if this fragment is line box and marked dirty.
  bool IsDirty() const { return is_dirty_inline_; }

  // Returns offset to its container box for inline and line box fragments.
  const PhysicalOffset& OffsetInContainerBlock() const {
    DCHECK(PhysicalFragment().IsInline() || PhysicalFragment().IsLineBox());
    return inline_offset_to_container_box_;
  }
  const PhysicalRect RectInContainerBlock() const {
    return PhysicalRect(OffsetInContainerBlock(), Size());
  }

  // InkOverflow of itself, not including contents, in the local coordinate.
  PhysicalRect SelfInkOverflow() const;

  // InkOverflow of its contents, not including itself, in the local coordinate.
  PhysicalRect ContentsInkOverflow() const;

  // InkOverflow of itself, including contents if they contribute to the ink
  // overflow of this object (e.g. when not clipped,) in the local coordinate.
  PhysicalRect InkOverflow() const;

  void RecalcInlineChildrenInkOverflow() const;

  // TODO(layout-dev): Implement when we have oveflow support.
  // TODO(eae): Switch to using NG geometry types.
  bool HasOverflowClip() const { return PhysicalFragment().HasOverflowClip(); }
  bool ShouldClipOverflow() const;
  bool HasSelfPaintingLayer() const;
  // This is equivalent to LayoutObject::VisualRect
  IntRect VisualRect() const override;
  IntRect PartialInvalidationVisualRect() const override;

  // Set ShouldDoFullPaintInvalidation flag in the corresponding LayoutObject.
  void SetShouldDoFullPaintInvalidation();

  // Set ShouldDoFullPaintInvalidation flag in the corresponding LayoutObject
  // recursively.
  void SetShouldDoFullPaintInvalidationRecursively();

  // Set ShouldDoFullPaintInvalidation flag to all objects in the first line of
  // this block-level fragment.
  void SetShouldDoFullPaintInvalidationForFirstLine() const;

  // DisplayItemClient methods.
  String DebugName() const override;

  // Commonly used functions for NGPhysicalFragment.
  Node* GetNode() const { return PhysicalFragment().GetNode(); }
  const LayoutObject* GetLayoutObject() const {
    return PhysicalFragment().GetLayoutObject();
  }
  LayoutObject* GetMutableLayoutObject() const {
    return PhysicalFragment().GetMutableLayoutObject();
  }
  const ComputedStyle& Style() const { return PhysicalFragment().Style(); }
  PhysicalOffset Offset() const {
    DCHECK(parent_);
    return offset_;
  }
  PhysicalSize Size() const { return PhysicalFragment().Size(); }
  PhysicalRect Rect() const { return {Offset(), Size()}; }

  // Converts the given point, relative to the fragment itself, into a position
  // in DOM tree.
  PositionWithAffinity PositionForPoint(const PhysicalOffset&) const;

  // A range of fragments for |FragmentsFor()|.
  class TraverseNextForSameLayoutObject {
    STATIC_ONLY(TraverseNextForSameLayoutObject);

   public:
    static NGPaintFragment* Next(NGPaintFragment* current) {
      return current->next_for_same_layout_object_;
    }
  };
  class CORE_EXPORT FragmentRange
      : public List<TraverseNextForSameLayoutObject> {
   public:
    explicit FragmentRange(
        NGPaintFragment* first,
        bool is_in_layout_ng_inline_formatting_context = true)
        : List(first),
          is_in_layout_ng_inline_formatting_context_(
              is_in_layout_ng_inline_formatting_context) {}

    bool IsInLayoutNGInlineFormattingContext() const {
      return is_in_layout_ng_inline_formatting_context_;
    }

   private:
    bool is_in_layout_ng_inline_formatting_context_;
  };

  // Returns a range of NGPaintFragment in an inline formatting context that are
  // for a LayoutObject.
  static FragmentRange InlineFragmentsFor(const LayoutObject*);

  const NGPaintFragment* LastForSameLayoutObject() const;
  NGPaintFragment* LastForSameLayoutObject();
  void LayoutObjectWillBeDestroyed();

  void ClearAssociationWithLayoutObject();

  // Computes LocalVisualRect for an inline LayoutObject. Returns nullopt if the
  // LayoutObject is not in LayoutNG inline formatting context.
  static base::Optional<PhysicalRect> LocalVisualRectFor(const LayoutObject&);

 private:
  bool IsAlive() const { return !is_layout_object_destroyed_; }

  // Returns the first "alive" fragment; i.e., fragment that doesn't have
  // destroyed LayoutObject.
  static NGPaintFragment* FirstAlive(NGPaintFragment* fragment) {
    while (UNLIKELY(fragment && !fragment->IsAlive()))
      fragment = fragment->next_sibling_.get();
    return fragment;
  }

  struct CreateContext {
    STACK_ALLOCATED();

   public:
    CreateContext(scoped_refptr<NGPaintFragment> previous_instance,
                  bool populate_children)
        : previous_instance(std::move(previous_instance)),
          populate_children(populate_children) {}
    CreateContext(CreateContext* parent_context, NGPaintFragment* parent)
        : parent(parent),
          last_fragment_map(parent_context->last_fragment_map),
          previous_instance(std::move(parent->first_child_)) {}

    void SkipDestroyedPreviousInstances();
    void DestroyPreviousInstances();

    NGPaintFragment* parent = nullptr;
    HashMap<const LayoutObject*, NGPaintFragment*>* last_fragment_map = nullptr;
    scoped_refptr<NGPaintFragment> previous_instance;
    bool populate_children = false;
    bool painting_layer_needs_repaint = false;
  };
  static scoped_refptr<NGPaintFragment> CreateOrReuse(
      scoped_refptr<const NGPhysicalFragment> fragment,
      PhysicalOffset offset,
      CreateContext* context);

  void PopulateDescendants(CreateContext* parent_context);
  void AssociateWithLayoutObject(
      LayoutObject*,
      HashMap<const LayoutObject*, NGPaintFragment*>* last_fragment_map);

  static void DestroyAll(scoped_refptr<NGPaintFragment> fragment);
  void RemoveChildren();

  // Helps for PositionForPoint() when |this| falls in different categories.
  PositionWithAffinity PositionForPointInText(const PhysicalOffset&) const;
  PositionWithAffinity PositionForPointInInlineFormattingContext(
      const PhysicalOffset&) const;
  PositionWithAffinity PositionForPointInInlineLevelBox(
      const PhysicalOffset&) const;

  // Dirty line boxes containing |layout_object|.
  static void MarkLineBoxesDirtyFor(const LayoutObject& layout_object);

  // Returns |LayoutBox| that holds ink overflow for this fragment.
  const LayoutBox* InkOverflowOwnerBox() const;

  // Re-compute ink overflow of children and return the union.
  PhysicalRect RecalcInkOverflow();
  PhysicalRect RecalcContentsInkOverflow() const;

  // This fragment will use the layout object's visual rect.
  const LayoutObject& VisualRectLayoutObject(bool& this_as_inline_box) const;

  //
  // Following fields are computed in the layout phase.
  //

  scoped_refptr<const NGPhysicalFragment> physical_fragment_;
  PhysicalOffset offset_;

  NGPaintFragment* parent_;
  scoped_refptr<NGPaintFragment> first_child_;
  scoped_refptr<NGPaintFragment> next_sibling_;

  // The next fragment for when this is fragmented.
  scoped_refptr<NGPaintFragment> next_fragmented_;

  NGPaintFragment* next_for_same_layout_object_ = nullptr;
  PhysicalOffset inline_offset_to_container_box_;

  // The ink overflow storage for when |InkOverflowOwnerBox()| is nullptr.
  std::unique_ptr<NGContainerInkOverflow> ink_overflow_;

  // Set when the corresponding LayoutObject is destroyed.
  // TODO(kojii): This should move to |NGPhysicalFragment|.
  unsigned is_layout_object_destroyed_ : 1;

  // For a line box, this indicates it is dirty. This helps to determine if the
  // fragment is re-usable when part of an inline formatting context is changed.
  // For an inline box, this flag helps to avoid traversing up to its line box
  // every time.
  unsigned is_dirty_inline_ : 1;
};

extern template class CORE_EXTERN_TEMPLATE_EXPORT
    NGPaintFragment::List<NGPaintFragment::TraverseNextForSameLayoutObject>;
extern template class CORE_EXTERN_TEMPLATE_EXPORT
    NGPaintFragment::List<NGPaintFragment::TraverseNextSibling>;

PhysicalRect ComputeLocalSelectionRectForText(
    const NGInlineCursor& cursor,
    const LayoutSelectionStatus& selection_status);

PhysicalRect ComputeLocalSelectionRectForReplaced(const NGInlineCursor& cursor);

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_PAINT_FRAGMENT_H_