summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/paint/find_paint_offset_and_visual_rect_needing_update.h
blob: b75179c759a0bd005575f561efcb21a124f37ee3 (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
// 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_FIND_PAINT_OFFSET_AND_VISUAL_RECT_NEEDING_UPDATE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_FIND_PAINT_OFFSET_AND_VISUAL_RECT_NEEDING_UPDATE_H_

#if DCHECK_IS_ON()

#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/paint/find_properties_needing_update.h"
#include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_property_tree_builder.h"

namespace blink {

// This file contains scope classes for catching cases where paint offset or
// visual rect needed an update but were not marked as such. If paint offset or
// any visual rect (including visual rect of the object itself, scroll controls,
// caret, selection, etc.) will change, the object must be marked as such by
// LayoutObject::setNeedsPaintOffsetAndVisualRectUpdate() (which is a private
// function called by several public paint-invalidation-flag setting functions).

class FindPaintOffsetNeedingUpdateScope {
 public:
  FindPaintOffsetNeedingUpdateScope(const LayoutObject& object,
                                    const FragmentData& fragment_data,
                                    const bool& is_actually_needed)
      : object_(object),
        fragment_data_(fragment_data),
        is_actually_needed_(is_actually_needed),
        old_paint_offset_(fragment_data.PaintOffset()) {
    auto* properties = fragment_data.PaintProperties();
    if (properties && properties->PaintOffsetTranslation()) {
      old_paint_offset_translation_ =
          properties->PaintOffsetTranslation()->Clone();
    }
  }

  ~FindPaintOffsetNeedingUpdateScope() {
    if (is_actually_needed_)
      return;
    LayoutPoint paint_offset = fragment_data_.PaintOffset();
    DCHECK_OBJECT_PROPERTY_EQ(object_, &old_paint_offset_, &paint_offset);
    auto* paint_properties = fragment_data_.PaintProperties();
    const auto* paint_offset_translation =
        paint_properties ? paint_properties->PaintOffsetTranslation() : nullptr;
    DCHECK_OBJECT_PROPERTY_EQ(object_, old_paint_offset_translation_.get(),
                              paint_offset_translation);
  }

 private:
  const LayoutObject& object_;
  const FragmentData& fragment_data_;
  const bool& is_actually_needed_;
  LayoutPoint old_paint_offset_;
  scoped_refptr<const TransformPaintPropertyNode> old_paint_offset_translation_;
};

class FindVisualRectNeedingUpdateScopeBase {
 protected:
  FindVisualRectNeedingUpdateScopeBase(const LayoutObject& object,
                                       const PaintInvalidatorContext& context,
                                       const LayoutRect& old_visual_rect,
                                       bool is_actually_needed)
      : object_(object),
        context_(context),
        old_visual_rect_(old_visual_rect),
        needed_visual_rect_update_(context.NeedsVisualRectUpdate(object)) {
    if (needed_visual_rect_update_) {
      DCHECK(is_actually_needed);
      return;
    }
    context.force_visual_rect_update_for_checking_ = true;
    DCHECK(context.NeedsVisualRectUpdate(object));
  }

  ~FindVisualRectNeedingUpdateScopeBase() {
    context_.force_visual_rect_update_for_checking_ = false;
    DCHECK_EQ(needed_visual_rect_update_,
              context_.NeedsVisualRectUpdate(object_));
  }

  static LayoutRect InflatedRect(const LayoutRect& r) {
    LayoutRect result = r;
    result.Inflate(1);
    return result;
  }

  void CheckVisualRect(const LayoutRect& new_visual_rect) {
    if (needed_visual_rect_update_)
      return;
    DCHECK((old_visual_rect_.IsEmpty() && new_visual_rect.IsEmpty()) ||
           object_.EnclosingLayer()->SubtreeIsInvisible() ||
           old_visual_rect_ == new_visual_rect ||
           // The following check is to tolerate the differences caused by
           // pixel snapping that may happen for one rect but not for another
           // while we need neither paint invalidation nor raster invalidation
           // for the change. This may miss some real subpixel changes of visual
           // rects. TODO(wangxianzhu): Look into whether we can tighten this
           // for SPv2.
           (InflatedRect(old_visual_rect_).Contains(new_visual_rect) &&
            InflatedRect(new_visual_rect).Contains(old_visual_rect_)))
        << "Visual rect changed without needing update"
        << " object=" << object_.DebugName()
        << " old=" << old_visual_rect_.ToString()
        << " new=" << new_visual_rect.ToString();
  }

  const LayoutObject& object_;
  const PaintInvalidatorContext& context_;
  LayoutRect old_visual_rect_;
  bool needed_visual_rect_update_;
};

// For updates of visual rects (e.g. of scroll controls, caret, selection,etc.)
// contained by an object.
class FindVisualRectNeedingUpdateScope : FindVisualRectNeedingUpdateScopeBase {
 public:
  FindVisualRectNeedingUpdateScope(const LayoutObject& object,
                                   const PaintInvalidatorContext& context,
                                   const LayoutRect& old_visual_rect,
                                   // Must be a reference to a rect that
                                   // outlives this scope.
                                   const LayoutRect& new_visual_rect)
      : FindVisualRectNeedingUpdateScopeBase(
            object,
            context,
            old_visual_rect,
            context.tree_builder_context_actually_needed_),
        new_visual_rect_ref_(new_visual_rect) {}

  ~FindVisualRectNeedingUpdateScope() { CheckVisualRect(new_visual_rect_ref_); }

 private:
  const LayoutRect& new_visual_rect_ref_;
};

// For updates of object visual rect and location.
class FindObjectVisualRectNeedingUpdateScope
    : FindVisualRectNeedingUpdateScopeBase {
 public:
  FindObjectVisualRectNeedingUpdateScope(const LayoutObject& object,
                                         const FragmentData& fragment_data,
                                         const PaintInvalidatorContext& context,
                                         bool is_actually_needed)
      : FindVisualRectNeedingUpdateScopeBase(object,
                                             context,
                                             fragment_data.VisualRect(),
                                             is_actually_needed),
        fragment_data_(fragment_data),
        old_location_(context.old_location) {}

  ~FindObjectVisualRectNeedingUpdateScope() {
    CheckVisualRect(fragment_data_.VisualRect());
    CheckLocation();
  }

  void CheckLocation() {
    if (needed_visual_rect_update_)
      return;
    LayoutPoint new_location = fragment_data_.LocationInBacking();
    // Location of LayoutText and non-root SVG is location of the visual rect
    // which have been checked above.
    DCHECK(object_.IsText() || object_.IsSVGChild() ||
           new_location == old_location_ ||
           object_.EnclosingLayer()->SubtreeIsInvisible() ||
           // See checkVisualRect for the issue of approximation.
           LayoutRect(-1, -1, 2, 2)
               .Contains(LayoutRect(LayoutPoint(new_location - old_location_),
                                    LayoutSize())))
        << "Location changed without needing update"
        << " object=" << object_.DebugName()
        << " old=" << old_location_.ToString()
        << " new=" << new_location.ToString();
  }

 private:
  const FragmentData& fragment_data_;
  LayoutPoint old_location_;
};

}  // namespace blink

#endif  // DCHECK_IS_ON()

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_FIND_PAINT_OFFSET_AND_VISUAL_RECT_NEEDING_UPDATE_H_