summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/paint/object_paint_properties.h
blob: 736b174648d733392e22ecab1e0b21d925495ab0 (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
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
// Copyright 2015 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_OBJECT_PAINT_PROPERTIES_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_OBJECT_PAINT_PROPERTIES_H_

#include <memory>
#include <utility>

#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"

namespace blink {

// This class stores the paint property nodes created by a LayoutObject. The
// object owns each of the property nodes directly and RefPtrs are only used to
// harden against use-after-free bugs. These paint properties are built/updated
// by PaintPropertyTreeBuilder during the PrePaint lifecycle step.
//
// [update & clear implementation note] This class has Update[property](...) and
// Clear[property]() helper functions for efficiently creating and updating
// properties. The update functions returns a 3-state result to indicate whether
// the value or the existence of the node has changed. They use a create-or-
// update pattern of re-using existing properties for efficiency:
// 1. It avoids extra allocations.
// 2. It preserves existing child->parent pointers.
// The clear functions return true if an existing node is removed. Property
// nodes store parent pointers but not child pointers and these return values
// are important for catching property tree structure changes which require
// updating descendant's parent pointers.
class CORE_EXPORT ObjectPaintProperties {
  USING_FAST_MALLOC(ObjectPaintProperties);

 public:
  static std::unique_ptr<ObjectPaintProperties> Create() {
    return base::WrapUnique(new ObjectPaintProperties());
  }

  // The hierarchy of the transform subtree created by a LayoutObject is as
  // follows:
  // [ paintOffsetTranslation ]           Normally paint offset is accumulated
  // |                                    without creating a node until we see,
  // |                                    for example, transform or
  // |                                    position:fixed.
  // +---[ transform ]                    The space created by CSS transform.
  //     |                                This is the local border box space.
  //     +---[ perspective ]              The space created by CSS perspective.
  //         +---[ svgLocalToBorderBoxTransform ] Additional transform for
  //                                      children of the outermost root SVG.
  //                    OR                (SVG does not support scrolling.)
  //         +---[ scrollTranslation ]    The space created by overflow clip.
  const TransformPaintPropertyNode* PaintOffsetTranslation() const {
    return paint_offset_translation_.get();
  }
  const TransformPaintPropertyNode* Transform() const {
    return transform_.get();
  }
  const TransformPaintPropertyNode* Perspective() const {
    return perspective_.get();
  }
  const TransformPaintPropertyNode* SvgLocalToBorderBoxTransform() const {
    return svg_local_to_border_box_transform_.get();
  }
  const ScrollPaintPropertyNode* Scroll() const { return scroll_.get(); }
  const TransformPaintPropertyNode* ScrollTranslation() const {
    return scroll_translation_.get();
  }

  // The hierarchy of the effect subtree created by a LayoutObject is as
  // follows:
  // [ effect ]
  // |   Isolated group to apply various CSS effects, including opacity,
  // |   mix-blend-mode, and for isolation if a mask needs to be applied or
  // |   backdrop-dependent children are present.
  // +-[ filter ]
  // |     Isolated group for CSS filter.
  // +-[ vertical/horizontal scrollbar effect ]
  // |     Overlay Scrollbars on Aura and Android need effect node for fade
  // |     animation.
  // +-[ mask ]
  // |     Isolated group for painting the CSS mask. This node will have
  // |     SkBlendMode::kDstIn and shall paint last, i.e. after masked contents.
  // +-[ clip path ]
  //       Isolated group for painting the CSS clip-path. This node will have
  //       SkBlendMode::kDstIn and shall paint last, i.e. after clipped
  //       contents.
  const EffectPaintPropertyNode* Effect() const { return effect_.get(); }
  const EffectPaintPropertyNode* Filter() const { return filter_.get(); }
  const EffectPaintPropertyNode* VerticalScrollbarEffect() const {
    return vertical_scrollbar_effect_.get();
  }
  const EffectPaintPropertyNode* HorizontalScrollbarEffect() const {
    return horizontal_scrollbar_effect_.get();
  }
  const EffectPaintPropertyNode* Mask() const { return mask_.get(); }
  const EffectPaintPropertyNode* ClipPath() const { return clip_path_.get(); }

  // The hierarchy of the clip subtree created by a LayoutObject is as follows:
  // [ fragment clip ]
  // |    Clips to a fragment's bounds.
  // |    This is only present for content under a fragmentation container.
  // +-[ clip path clip ]
  //   |  Clip created by path-based CSS clip-path. Only exists if the
  //  /   clip-path is "simple" that can be applied geometrically. This and
  // /    the clip path effect node are mutually exclusive.
  // | NOTE: for composited SPv1 clip path clips, we move clip path clip
  // |       below mask.
  // +-[ mask clip ]
  //   |   Clip created by CSS mask or CSS clip-path. It serves two purposes:
  //   |   1. Cull painting of the masked subtree. Because anything outside of
  //   |      the mask is never visible, it is pointless to paint them.
  //   |   2. Raster clip of the masked subtree. Because the mask implemented
  //   |      as SkBlendMode::kDstIn, pixels outside of mask's bound will be
  //   |      intact when they shall be masked out. This clip ensures no pixels
  //   |      leak out.
  //   +-[ css clip ]
  //     |   Clip created by CSS clip. CSS clip applies to all descendants, this
  //     |   node only applies to containing block descendants. For descendants
  //     |   not contained by this object, use [ css clip fixed position ].
  //     +-[ overflow controls clip ]
  //     |   Clip created by overflow clip to clip overflow controls
  //     |   (scrollbars, resizer, scroll corner) that would overflow the box.
  //     +-[ inner border radius clip]
  //       |   Clip created by a rounded border with overflow clip. This clip is
  //       |   not inset by scrollbars.
  //       +-[ overflow clip ]
  //             Clip created by overflow clip and is inset by the scrollbar.
  //   [ css clip fixed position ]
  //       Clip created by CSS clip. Only exists if the current clip includes
  //       some clip that doesn't apply to our fixed position descendants.
  const ClipPaintPropertyNode* FragmentClip() const {
    return fragment_clip_.get();
  }
  const ClipPaintPropertyNode* ClipPathClip() const {
    return clip_path_clip_.get();
  }
  const ClipPaintPropertyNode* MaskClip() const { return mask_clip_.get(); }
  const ClipPaintPropertyNode* CssClip() const { return css_clip_.get(); }
  const ClipPaintPropertyNode* CssClipFixedPosition() const {
    return css_clip_fixed_position_.get();
  }
  const ClipPaintPropertyNode* OverflowControlsClip() const {
    return overflow_controls_clip_.get();
  }
  const ClipPaintPropertyNode* InnerBorderRadiusClip() const {
    return inner_border_radius_clip_.get();
  }
  const ClipPaintPropertyNode* OverflowClip() const {
    return overflow_clip_.get();
  }

  // The following clear* functions return true if the property tree structure
  // changes (an existing node was deleted), and false otherwise. See the
  // class-level comment ("update & clear implementation note") for details
  // about why this is needed for efficient updates.
  bool ClearPaintOffsetTranslation() {
    return Clear(paint_offset_translation_);
  }
  bool ClearTransform() { return Clear(transform_); }
  bool ClearEffect() { return Clear(effect_); }
  bool ClearFilter() { return Clear(filter_); }
  bool ClearVerticalScrollbarEffect() {
    return Clear(vertical_scrollbar_effect_);
  }
  bool ClearHorizontalScrollbarEffect() {
    return Clear(horizontal_scrollbar_effect_);
  }
  bool ClearMask() { return Clear(mask_); }
  bool ClearClipPath() { return Clear(clip_path_); }
  bool ClearFragmentClip() { return Clear(fragment_clip_); }
  bool ClearClipPathClip() { return Clear(clip_path_clip_); }
  bool ClearMaskClip() { return Clear(mask_clip_); }
  bool ClearCssClip() { return Clear(css_clip_); }
  bool ClearCssClipFixedPosition() { return Clear(css_clip_fixed_position_); }
  bool ClearOverflowControlsClip() { return Clear(overflow_controls_clip_); }
  bool ClearInnerBorderRadiusClip() { return Clear(inner_border_radius_clip_); }
  bool ClearOverflowClip() { return Clear(overflow_clip_); }
  bool ClearPerspective() { return Clear(perspective_); }
  bool ClearSvgLocalToBorderBoxTransform() {
    return Clear(svg_local_to_border_box_transform_);
  }
  bool ClearScroll() { return Clear(scroll_); }
  bool ClearScrollTranslation() { return Clear(scroll_translation_); }

  class UpdateResult {
   public:
    bool Unchanged() const { return result_ == kUnchanged; }
    bool NewNodeCreated() const { return result_ == kNewNodeCreated; }

   private:
    friend class ObjectPaintProperties;
    enum Result { kUnchanged, kValueChanged, kNewNodeCreated };
    UpdateResult(Result r) : result_(r) {}
    Result result_;
  };

  UpdateResult UpdatePaintOffsetTranslation(
      const TransformPaintPropertyNode& parent,
      TransformPaintPropertyNode::State&& state) {
    return Update(paint_offset_translation_, parent, std::move(state));
  }
  UpdateResult UpdateTransform(const TransformPaintPropertyNode& parent,
                               TransformPaintPropertyNode::State&& state) {
    return Update(transform_, parent, std::move(state));
  }
  UpdateResult UpdatePerspective(const TransformPaintPropertyNode& parent,
                                 TransformPaintPropertyNode::State&& state) {
    return Update(perspective_, parent, std::move(state));
  }
  UpdateResult UpdateSvgLocalToBorderBoxTransform(
      const TransformPaintPropertyNode& parent,
      TransformPaintPropertyNode::State&& state) {
    DCHECK(!ScrollTranslation()) << "SVG elements cannot scroll so there "
                                    "should never be both a scroll translation "
                                    "and an SVG local to border box transform.";
    return Update(svg_local_to_border_box_transform_, parent, std::move(state));
  }
  UpdateResult UpdateScroll(const ScrollPaintPropertyNode& parent,
                            ScrollPaintPropertyNode::State&& state) {
    return Update(scroll_, parent, std::move(state));
  }
  UpdateResult UpdateScrollTranslation(
      const TransformPaintPropertyNode& parent,
      TransformPaintPropertyNode::State&& state) {
    DCHECK(!SvgLocalToBorderBoxTransform())
        << "SVG elements cannot scroll so there should never be both a scroll "
           "translation and an SVG local to border box transform.";
    return Update(scroll_translation_, parent, std::move(state));
  }
  UpdateResult UpdateEffect(const EffectPaintPropertyNode& parent,
                            EffectPaintPropertyNode::State&& state) {
    return Update(effect_, parent, std::move(state));
  }
  UpdateResult UpdateFilter(const EffectPaintPropertyNode& parent,
                            EffectPaintPropertyNode::State&& state) {
    return Update(filter_, parent, std::move(state));
  }
  UpdateResult UpdateVerticalScrollbarEffect(
      const EffectPaintPropertyNode& parent,
      EffectPaintPropertyNode::State&& state) {
    return Update(vertical_scrollbar_effect_, parent, std::move(state));
  }
  UpdateResult UpdateHorizontalScrollbarEffect(
      const EffectPaintPropertyNode& parent,
      EffectPaintPropertyNode::State&& state) {
    return Update(horizontal_scrollbar_effect_, parent, std::move(state));
  }
  UpdateResult UpdateMask(const EffectPaintPropertyNode& parent,
                          EffectPaintPropertyNode::State&& state) {
    return Update(mask_, parent, std::move(state));
  }
  UpdateResult UpdateClipPath(const EffectPaintPropertyNode& parent,
                              EffectPaintPropertyNode::State&& state) {
    return Update(clip_path_, parent, std::move(state));
  }
  UpdateResult UpdateFragmentClip(const ClipPaintPropertyNode& parent,
                                  ClipPaintPropertyNode::State&& state) {
    return Update(fragment_clip_, parent, std::move(state));
  }
  UpdateResult UpdateClipPathClip(const ClipPaintPropertyNode& parent,
                                  ClipPaintPropertyNode::State&& state) {
    return Update(clip_path_clip_, parent, std::move(state));
  }
  UpdateResult UpdateMaskClip(const ClipPaintPropertyNode& parent,
                              ClipPaintPropertyNode::State&& state) {
    return Update(mask_clip_, parent, std::move(state));
  }
  UpdateResult UpdateCssClip(const ClipPaintPropertyNode& parent,
                             ClipPaintPropertyNode::State&& state) {
    return Update(css_clip_, parent, std::move(state));
  }
  UpdateResult UpdateCssClipFixedPosition(
      const ClipPaintPropertyNode& parent,
      ClipPaintPropertyNode::State&& state) {
    return Update(css_clip_fixed_position_, parent, std::move(state));
  }
  UpdateResult UpdateOverflowControlsClip(
      const ClipPaintPropertyNode& parent,
      ClipPaintPropertyNode::State&& state) {
    return Update(overflow_controls_clip_, parent, std::move(state));
  }
  UpdateResult UpdateInnerBorderRadiusClip(
      const ClipPaintPropertyNode& parent,
      ClipPaintPropertyNode::State&& state) {
    return Update(inner_border_radius_clip_, parent, std::move(state));
  }
  UpdateResult UpdateOverflowClip(const ClipPaintPropertyNode& parent,
                                  ClipPaintPropertyNode::State&& state) {
    return Update(overflow_clip_, parent, std::move(state));
  }

#if DCHECK_IS_ON()
  // Used by FindPropertiesNeedingUpdate.h for recording the current properties.
  std::unique_ptr<ObjectPaintProperties> Clone() const {
    std::unique_ptr<ObjectPaintProperties> cloned = Create();
    if (paint_offset_translation_)
      cloned->paint_offset_translation_ = paint_offset_translation_->Clone();
    if (transform_)
      cloned->transform_ = transform_->Clone();
    if (effect_)
      cloned->effect_ = effect_->Clone();
    if (filter_)
      cloned->filter_ = filter_->Clone();
    if (vertical_scrollbar_effect_)
      cloned->vertical_scrollbar_effect_ = vertical_scrollbar_effect_->Clone();
    if (horizontal_scrollbar_effect_) {
      cloned->horizontal_scrollbar_effect_ =
          horizontal_scrollbar_effect_->Clone();
    }
    if (mask_)
      cloned->mask_ = mask_->Clone();
    if (clip_path_)
      cloned->clip_path_ = clip_path_->Clone();
    if (fragment_clip_)
      cloned->fragment_clip_ = fragment_clip_->Clone();
    if (clip_path_clip_)
      cloned->clip_path_clip_ = clip_path_clip_->Clone();
    if (mask_clip_)
      cloned->mask_clip_ = mask_clip_->Clone();
    if (css_clip_)
      cloned->css_clip_ = css_clip_->Clone();
    if (css_clip_fixed_position_)
      cloned->css_clip_fixed_position_ = css_clip_fixed_position_->Clone();
    if (overflow_controls_clip_)
      cloned->overflow_controls_clip_ = overflow_controls_clip_->Clone();
    if (inner_border_radius_clip_)
      cloned->inner_border_radius_clip_ = inner_border_radius_clip_->Clone();
    if (overflow_clip_)
      cloned->overflow_clip_ = overflow_clip_->Clone();
    if (perspective_)
      cloned->perspective_ = perspective_->Clone();
    if (svg_local_to_border_box_transform_) {
      cloned->svg_local_to_border_box_transform_ =
          svg_local_to_border_box_transform_->Clone();
    }
    if (scroll_)
      cloned->scroll_ = scroll_->Clone();
    if (scroll_translation_)
      cloned->scroll_translation_ = scroll_translation_->Clone();
    return cloned;
  }
#endif

 private:
  ObjectPaintProperties() = default;

  // Return true if the property tree structure changes (an existing node was
  // deleted), and false otherwise. See the class-level comment ("update & clear
  // implementation note") for details about why this is needed for efficiency.
  template <typename PaintPropertyNode>
  bool Clear(scoped_refptr<PaintPropertyNode>& field) {
    if (field) {
      field = nullptr;
      return true;
    }
    return false;
  }

  // Return true if the property tree structure changes (a new node was
  // created), and false otherwise. See the class-level comment ("update & clear
  // implementation note") for details about why this is needed for efficiency.
  template <typename PaintPropertyNode>
  UpdateResult Update(scoped_refptr<PaintPropertyNode>& field,
                      const PaintPropertyNode& parent,
                      typename PaintPropertyNode::State&& state) {
    if (field) {
      return field->Update(parent, std::move(state))
                 ? UpdateResult::kValueChanged
                 : UpdateResult::kUnchanged;
    }
    field = PaintPropertyNode::Create(parent, std::move(state));
    return UpdateResult::kNewNodeCreated;
  }

  // ATTENTION! Make sure to keep FindPropertiesNeedingUpdate.h in sync when
  // new properites are added!
  scoped_refptr<TransformPaintPropertyNode> paint_offset_translation_;
  scoped_refptr<TransformPaintPropertyNode> transform_;
  scoped_refptr<EffectPaintPropertyNode> effect_;
  scoped_refptr<EffectPaintPropertyNode> filter_;
  scoped_refptr<EffectPaintPropertyNode> vertical_scrollbar_effect_;
  scoped_refptr<EffectPaintPropertyNode> horizontal_scrollbar_effect_;
  scoped_refptr<EffectPaintPropertyNode> mask_;
  scoped_refptr<EffectPaintPropertyNode> clip_path_;
  scoped_refptr<ClipPaintPropertyNode> fragment_clip_;
  scoped_refptr<ClipPaintPropertyNode> clip_path_clip_;
  scoped_refptr<ClipPaintPropertyNode> mask_clip_;
  scoped_refptr<ClipPaintPropertyNode> css_clip_;
  scoped_refptr<ClipPaintPropertyNode> css_clip_fixed_position_;
  scoped_refptr<ClipPaintPropertyNode> overflow_controls_clip_;
  scoped_refptr<ClipPaintPropertyNode> inner_border_radius_clip_;
  scoped_refptr<ClipPaintPropertyNode> overflow_clip_;
  scoped_refptr<TransformPaintPropertyNode> perspective_;
  // TODO(pdr): Only LayoutSVGRoot needs this and it should be moved there.
  scoped_refptr<TransformPaintPropertyNode> svg_local_to_border_box_transform_;
  scoped_refptr<ScrollPaintPropertyNode> scroll_;
  scoped_refptr<TransformPaintPropertyNode> scroll_translation_;

  DISALLOW_COPY_AND_ASSIGN(ObjectPaintProperties);
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_OBJECT_PAINT_PROPERTIES_H_