1 // Copyright 2018 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 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_
6 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_
7 
8 #include "third_party/blink/public/common/input/web_input_event.h"
9 #include "third_party/blink/public/web/web_swap_result.h"
10 #include "third_party/blink/renderer/core/core_export.h"
11 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
12 #include "third_party/blink/renderer/core/paint/paint_timing_visualizer.h"
13 #include "third_party/blink/renderer/core/scroll/scroll_types.h"
14 #include "third_party/blink/renderer/platform/heap/member.h"
15 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
16 
17 namespace blink {
18 
19 class Image;
20 class ImagePaintTimingDetector;
21 class ImageResourceContent;
22 class LargestContentfulPaintCalculator;
23 class LayoutObject;
24 class LocalFrameView;
25 class PropertyTreeState;
26 class StyleFetchedImage;
27 class TextPaintTimingDetector;
28 struct WebFloatRect;
29 
30 // |PaintTimingCallbackManager| is an interface between
31 // |ImagePaintTimingDetector|/|TextPaintTimingDetector| and |ChromeClient|.
32 // As |ChromeClient| is shared among the paint-timing-detecters, it
33 // makes it hard to test each detector without being affected other detectors.
34 // The interface, however, allows unit tests to mock |ChromeClient| for each
35 // detector. With the mock, |ImagePaintTimingDetector|'s callback does not need
36 // to store in the same queue as |TextPaintTimingDetector|'s. The separate
37 // queue makes it possible to pop an |ImagePaintTimingDetector|'s callback
38 // without having to popping the |TextPaintTimingDetector|'s.
39 class PaintTimingCallbackManager : public GarbageCollectedMixin {
40  public:
41   using LocalThreadCallback = base::OnceCallback<void(base::TimeTicks)>;
42   using CallbackQueue = std::queue<LocalThreadCallback>;
43 
44   virtual void RegisterCallback(
45       PaintTimingCallbackManager::LocalThreadCallback) = 0;
46 };
47 
48 // This class is responsible for managing the swap-time callback for Largest
49 // Image Paint and Largest Text Paint. In frames where both text and image are
50 // painted, Largest Image Paint and Largest Text Paint need to assign the same
51 // paint-time for their records. In this case, |PaintTimeCallbackManager|
52 // requests a swap-time callback and share the swap-time with LIP and LTP.
53 // Otherwise LIP and LTP would have to request their own swap-time callbacks.
54 // An extra benefit of this design is that |LargestContentfulPaintCalculator|
55 // can thus hook to the end of the LIP and LTP's record assignments.
56 //
57 // |GarbageCollected| inheritance is required by the swap-time callback
58 // registration.
59 class PaintTimingCallbackManagerImpl final
60     : public GarbageCollected<PaintTimingCallbackManagerImpl>,
61       public PaintTimingCallbackManager {
62   USING_GARBAGE_COLLECTED_MIXIN(PaintTimingCallbackManagerImpl);
63 
64  public:
PaintTimingCallbackManagerImpl(LocalFrameView * frame_view)65   PaintTimingCallbackManagerImpl(LocalFrameView* frame_view)
66       : frame_view_(frame_view),
67         frame_callbacks_(
68             std::make_unique<std::queue<
69                 PaintTimingCallbackManager::LocalThreadCallback>>()) {}
~PaintTimingCallbackManagerImpl()70   ~PaintTimingCallbackManagerImpl() { frame_callbacks_.reset(); }
71 
72   // Instead of registering the callback right away, this impl of the interface
73   // combine the callback into |frame_callbacks_| before registering a separate
74   // swap-time callback for the combined callbacks. When the swap-time callback
75   // is invoked, the swap-time is then assigned to each callback of
76   // |frame_callbacks_|.
RegisterCallback(PaintTimingCallbackManager::LocalThreadCallback callback)77   void RegisterCallback(
78       PaintTimingCallbackManager::LocalThreadCallback callback) override {
79     frame_callbacks_->push(std::move(callback));
80   }
81 
82   void RegisterPaintTimeCallbackForCombinedCallbacks();
83 
CountCallbacks()84   inline size_t CountCallbacks() { return frame_callbacks_->size(); }
85 
86   void ReportPaintTime(
87       std::unique_ptr<std::queue<
88           PaintTimingCallbackManager::LocalThreadCallback>> frame_callbacks,
89       WebSwapResult,
90       base::TimeTicks paint_time);
91 
92   void Trace(Visitor* visitor) override;
93 
94  private:
95   Member<LocalFrameView> frame_view_;
96   // |frame_callbacks_| stores the callbacks of |TextPaintTimingDetector| and
97   // |ImagePaintTimingDetector| in an (animated) frame. It is passed as an
98   // argument of a swap-time callback which once is invoked, invokes every
99   // callback in |frame_callbacks_|. This hierarchical callback design is to
100   // reduce the need of calling ChromeClient to register swap-time callbacks for
101   // both detectos.
102   // Although |frame_callbacks_| intends to store callbacks
103   // of a frame, it occasionally has to do that for more than one frame, when it
104   // fails to register a swap-time callback.
105   std::unique_ptr<PaintTimingCallbackManager::CallbackQueue> frame_callbacks_;
106 };
107 
108 // PaintTimingDetector contains some of paint metric detectors,
109 // providing common infrastructure for these detectors.
110 //
111 // Users has to enable 'loading' trace category to enable the metrics.
112 //
113 // See also:
114 // https://docs.google.com/document/d/1DRVd4a2VU8-yyWftgOparZF-sf16daf0vfbsHuz2rws/edit
115 class CORE_EXPORT PaintTimingDetector
116     : public GarbageCollected<PaintTimingDetector> {
117   friend class ImagePaintTimingDetectorTest;
118   friend class TextPaintTimingDetectorTest;
119 
120  public:
121   PaintTimingDetector(LocalFrameView*);
122 
123   static void NotifyBackgroundImagePaint(
124       const Node*,
125       const Image*,
126       const StyleFetchedImage*,
127       const PropertyTreeState& current_paint_chunk_properties,
128       const IntRect& image_border);
129   static void NotifyImagePaint(
130       const LayoutObject&,
131       const IntSize& intrinsic_size,
132       const ImageResourceContent* cached_image,
133       const PropertyTreeState& current_paint_chunk_properties);
134   inline static void NotifyTextPaint(const IntRect& text_visual_rect);
135 
136   void NotifyImageFinished(const LayoutObject&, const ImageResourceContent*);
137   void LayoutObjectWillBeDestroyed(const LayoutObject&);
138   void NotifyImageRemoved(const LayoutObject&, const ImageResourceContent*);
139   void NotifyPaintFinished();
140   void NotifyInputEvent(WebInputEvent::Type);
141   bool NeedToNotifyInputOrScroll() const;
142   void NotifyScroll(mojom::blink::ScrollType);
143   // The returned value indicates whether the candidates have changed.
144   bool NotifyIfChangedLargestImagePaint(base::TimeTicks, uint64_t size);
145   bool NotifyIfChangedLargestTextPaint(base::TimeTicks, uint64_t size);
146 
147   void DidChangePerformanceTiming();
148 
IsTracing()149   inline static bool IsTracing() {
150     bool tracing_enabled;
151     TRACE_EVENT_CATEGORY_GROUP_ENABLED("loading", &tracing_enabled);
152     return tracing_enabled;
153   }
154 
155   void ConvertViewportToWindow(WebFloatRect* float_rect) const;
156   FloatRect CalculateVisualRect(const IntRect& visual_rect,
157                                 const PropertyTreeState&) const;
158 
GetTextPaintTimingDetector()159   TextPaintTimingDetector* GetTextPaintTimingDetector() const {
160     DCHECK(text_paint_timing_detector_);
161     return text_paint_timing_detector_;
162   }
GetImagePaintTimingDetector()163   ImagePaintTimingDetector* GetImagePaintTimingDetector() const {
164     return image_paint_timing_detector_;
165   }
166 
167   LargestContentfulPaintCalculator* GetLargestContentfulPaintCalculator();
168 
LargestImagePaint()169   base::TimeTicks LargestImagePaint() const {
170     return largest_image_paint_time_;
171   }
LargestImagePaintSize()172   uint64_t LargestImagePaintSize() const { return largest_image_paint_size_; }
LargestTextPaint()173   base::TimeTicks LargestTextPaint() const { return largest_text_paint_time_; }
LargestTextPaintSize()174   uint64_t LargestTextPaintSize() const { return largest_text_paint_size_; }
FirstInputOrScrollNotifiedTimestamp()175   base::TimeTicks FirstInputOrScrollNotifiedTimestamp() const {
176     return first_input_or_scroll_notified_timestamp_;
177   }
178 
179   void UpdateLargestContentfulPaintCandidate();
180 
Visualizer()181   base::Optional<PaintTimingVisualizer>& Visualizer() { return visualizer_; }
182   void Trace(Visitor* visitor);
183 
184  private:
185   // Method called to stop recording the Largest Contentful Paint.
186   void OnInputOrScroll();
187   bool HasLargestImagePaintChanged(base::TimeTicks, uint64_t size) const;
188   bool HasLargestTextPaintChanged(base::TimeTicks, uint64_t size) const;
189   Member<LocalFrameView> frame_view_;
190   // This member lives forever because it is also used for Text Element Timing.
191   Member<TextPaintTimingDetector> text_paint_timing_detector_;
192   // This member lives until the end of the paint phase after the largest
193   // image paint is found.
194   Member<ImagePaintTimingDetector> image_paint_timing_detector_;
195 
196   // This member lives for as long as the largest contentful paint is being
197   // computed. However, it is initialized lazily, so it may be nullptr because
198   // it has not yet been initialized or because we have stopped computing LCP.
199   Member<LargestContentfulPaintCalculator> largest_contentful_paint_calculator_;
200   // Time at which the first input or scroll is notified to PaintTimingDetector,
201   // hence causing LCP to stop being recorded. This is the same time at which
202   // |largest_contentful_paint_calculator_| is set to nullptr.
203   base::TimeTicks first_input_or_scroll_notified_timestamp_;
204 
205   Member<PaintTimingCallbackManagerImpl> callback_manager_;
206 
207   base::Optional<PaintTimingVisualizer> visualizer_;
208 
209   // Largest image information.
210   base::TimeTicks largest_image_paint_time_;
211   uint64_t largest_image_paint_size_ = 0;
212   // Largest text information.
213   base::TimeTicks largest_text_paint_time_;
214   uint64_t largest_text_paint_size_ = 0;
215   bool is_recording_largest_contentful_paint_ = true;
216 };
217 
218 // Largest Text Paint and Text Element Timing aggregate text nodes by these
219 // text nodes' ancestors. In order to tell whether a text node is contained by
220 // another node efficiently, The aggregation relies on the paint order of the
221 // rendering tree (https://www.w3.org/TR/CSS21/zindex.html). Because of the
222 // paint order, we can assume that if a text node T is visited during the visit
223 // of another node B, then B contains T. This class acts as the hook to certain
224 // container nodes (block object or inline object) to tell whether a text node
225 // is their descendant. The hook should be placed right before visiting the
226 // subtree of an container node, so that the constructor and the destructor can
227 // tell the start and end of the visit.
228 // TODO(crbug.com/960946): we should document the text aggregation.
229 class ScopedPaintTimingDetectorBlockPaintHook {
230   STACK_ALLOCATED();
231 
232  public:
233   // This constructor does nothing by itself. It will only set relevant
234   // variables when EmplaceIfNeeded() is called successfully. The lifetime of
235   // the object helps keeping the lifetime of |reset_top_| and |data_| to the
236   // appropriate scope.
ScopedPaintTimingDetectorBlockPaintHook()237   ScopedPaintTimingDetectorBlockPaintHook() {}
238 
239   void EmplaceIfNeeded(const LayoutBoxModelObject&, const PropertyTreeState&);
240   ~ScopedPaintTimingDetectorBlockPaintHook();
241 
242  private:
243   friend class PaintTimingDetector;
AggregateTextPaint(const IntRect & visual_rect)244   inline static void AggregateTextPaint(const IntRect& visual_rect) {
245     // Ideally we'd assert that |top_| exists, but there may be text nodes that
246     // do not have an ancestor non-anonymous block layout objects in the layout
247     // tree. An example of this is a multicol div, since the
248     // LayoutMultiColumnFlowThread is in a different layer from the DIV. In
249     // these cases, |top_| will be null. This is a known bug, see the related
250     // crbug.com/933479.
251     if (top_ && top_->data_)
252       top_->data_->aggregated_visual_rect_.Unite(visual_rect);
253   }
254 
255   base::Optional<base::AutoReset<ScopedPaintTimingDetectorBlockPaintHook*>>
256       reset_top_;
257   struct Data {
258     STACK_ALLOCATED();
259 
260    public:
261     Data(const LayoutBoxModelObject& aggregator,
262          const PropertyTreeState&,
263          TextPaintTimingDetector*);
264 
265     const LayoutBoxModelObject& aggregator_;
266     const PropertyTreeState& property_tree_state_;
267     TextPaintTimingDetector* detector_;
268     IntRect aggregated_visual_rect_;
269   };
270   base::Optional<Data> data_;
271   static ScopedPaintTimingDetectorBlockPaintHook* top_;
272 
273   DISALLOW_COPY_AND_ASSIGN(ScopedPaintTimingDetectorBlockPaintHook);
274 };
275 
276 // Creates a scope to ignore paint timing, e.g. when we are painting contents
277 // under opacity:0.
278 class IgnorePaintTimingScope {
279   STACK_ALLOCATED();
280 
281  public:
IgnorePaintTimingScope()282   IgnorePaintTimingScope() : auto_reset_(&should_ignore_, true) {}
283   ~IgnorePaintTimingScope() = default;
284 
ShouldIgnore()285   static bool ShouldIgnore() { return should_ignore_; }
286 
287  private:
288   base::AutoReset<bool> auto_reset_;
289   static bool should_ignore_;
290 };
291 
292 // static
NotifyTextPaint(const IntRect & text_visual_rect)293 inline void PaintTimingDetector::NotifyTextPaint(
294     const IntRect& text_visual_rect) {
295   if (IgnorePaintTimingScope::ShouldIgnore())
296     return;
297   ScopedPaintTimingDetectorBlockPaintHook::AggregateTextPaint(text_visual_rect);
298 }
299 
300 }  // namespace blink
301 
302 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_
303