| // 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_PAINT_TIMING_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_H_ |
| |
| #include <memory> |
| |
| #include "third_party/blink/public/web/web_swap_result.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h" |
| #include "third_party/blink/renderer/core/paint/paint_event.h" |
| #include "third_party/blink/renderer/platform/heap/handle.h" |
| #include "third_party/blink/renderer/platform/supplementable.h" |
| #include "third_party/blink/renderer/platform/wtf/functional.h" |
| |
| namespace base { |
| class TickClock; |
| } |
| |
| namespace blink { |
| |
| class LocalFrame; |
| |
| // PaintTiming is responsible for tracking paint-related timings for a given |
| // document. |
| class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>, |
| public Supplement<Document> { |
| friend class FirstMeaningfulPaintDetector; |
| using ReportTimeCallback = |
| WTF::CrossThreadOnceFunction<void(WebSwapResult, base::TimeTicks)>; |
| |
| public: |
| static const char kSupplementName[]; |
| |
| explicit PaintTiming(Document&); |
| PaintTiming(const PaintTiming&) = delete; |
| PaintTiming& operator=(const PaintTiming&) = delete; |
| virtual ~PaintTiming() = default; |
| |
| static PaintTiming& From(Document&); |
| |
| // Mark*() methods record the time for the given paint event and queue a swap |
| // promise to record the |first_*_swap_| timestamp. These methods do nothing |
| // (early return) if a time has already been recorded for the given paint |
| // event. |
| void MarkFirstPaint(); |
| |
| // MarkFirstImagePaint, and MarkFirstContentfulPaint |
| // will also record first paint if first paint hasn't been recorded yet. |
| void MarkFirstContentfulPaint(); |
| |
| // MarkFirstImagePaint will also record first contentful paint if first |
| // contentful paint hasn't been recorded yet. |
| void MarkFirstImagePaint(); |
| |
| // MarkFirstEligibleToPaint records the first time that the frame is not |
| // throttled and so is eligible to paint. A null value indicates throttling. |
| void MarkFirstEligibleToPaint(); |
| |
| // MarkIneligibleToPaint resets the paint eligibility timestamp to null. |
| // A null value indicates throttling. This call is ignored if a first |
| // contentful paint has already been recorded. |
| void MarkIneligibleToPaint(); |
| |
| void SetFirstMeaningfulPaintCandidate(base::TimeTicks timestamp); |
| void SetFirstMeaningfulPaint( |
| base::TimeTicks swap_stamp, |
| FirstMeaningfulPaintDetector::HadUserInput had_input); |
| void NotifyPaint(bool is_first_paint, bool text_painted, bool image_painted); |
| |
| // Notifies the PaintTiming that this Document received the onPortalActivate |
| // event. |
| void OnPortalActivate(); |
| void SetPortalActivatedPaint(base::TimeTicks stamp); |
| |
| // The getters below return monotonically-increasing seconds, or zero if the |
| // given paint event has not yet occurred. See the comments for |
| // monotonicallyIncreasingTime in wtf/Time.h for additional details. |
| |
| // FirstPaint returns the first time that anything was painted for the |
| // current document. |
| base::TimeTicks FirstPaint() const { return first_paint_swap_; } |
| |
| // Times when the first paint happens after the page is restored from the |
| // back-forward cache. If the element value is zero time tick, the first paint |
| // event did not happen for that navigation. |
| WTF::Vector<base::TimeTicks> FirstPaintsAfterBackForwardCacheRestore() const { |
| return first_paints_after_back_forward_cache_restore_swap_; |
| } |
| |
| // FirstContentfulPaint returns the first time that 'contentful' content was |
| // painted. For instance, the first time that text or image content was |
| // painted. |
| base::TimeTicks FirstContentfulPaint() const { |
| return first_contentful_paint_swap_; |
| } |
| |
| // FirstImagePaint returns the first time that image content was painted. |
| base::TimeTicks FirstImagePaint() const { return first_image_paint_swap_; } |
| |
| // FirstEligibleToPaint returns the first time that the frame is not |
| // throttled and is eligible to paint. A null value indicates throttling. |
| base::TimeTicks FirstEligibleToPaint() const { |
| return first_eligible_to_paint_; |
| } |
| |
| // FirstMeaningfulPaint returns the first time that page's primary content |
| // was painted. |
| base::TimeTicks FirstMeaningfulPaint() const { |
| return first_meaningful_paint_swap_; |
| } |
| |
| // The time that the first paint happened after a portal activation. |
| base::TimeTicks LastPortalActivatedPaint() const { |
| return last_portal_activated_swap_; |
| } |
| |
| // FirstMeaningfulPaintCandidate indicates the first time we considered a |
| // paint to qualify as the potentially first meaningful paint. Unlike |
| // firstMeaningfulPaint, this signal is available in real time, but it may be |
| // an optimistic (i.e., too early) estimate. |
| base::TimeTicks FirstMeaningfulPaintCandidate() const { |
| return first_meaningful_paint_candidate_; |
| } |
| |
| FirstMeaningfulPaintDetector& GetFirstMeaningfulPaintDetector() { |
| return *fmp_detector_; |
| } |
| |
| void RegisterNotifySwapTime(ReportTimeCallback); |
| void ReportSwapTime(PaintEvent, WebSwapResult, base::TimeTicks timestamp); |
| void ReportFirstPaintAfterBackForwardCacheRestoreSwapTime( |
| size_t index, |
| WebSwapResult, |
| base::TimeTicks timestamp); |
| |
| void ReportSwapResultHistogram(WebSwapResult); |
| |
| // The caller owns the |clock| which must outlive the PaintTiming. |
| void SetTickClockForTesting(const base::TickClock* clock); |
| |
| void OnRestoredFromBackForwardCache(); |
| |
| void Trace(Visitor*) const override; |
| |
| private: |
| LocalFrame* GetFrame() const; |
| void NotifyPaintTimingChanged(); |
| |
| // Set*() set the timing for the given paint event to the given timestamp if |
| // the value is currently zero, and queue a swap promise to record the |
| // |first_*_swap_| timestamp. These methods can be invoked from other Mark*() |
| // or Set*() methods to make sure that first paint is marked as part of |
| // marking first contentful paint, or that first contentful paint is marked as |
| // part of marking first text/image paint, for example. |
| void SetFirstPaint(base::TimeTicks stamp); |
| |
| // setFirstContentfulPaint will also set first paint time if first paint |
| // time has not yet been recorded. |
| void SetFirstContentfulPaint(base::TimeTicks stamp); |
| |
| // Set*Swap() are called when the SwapPromise is fulfilled and the swap |
| // timestamp is available. These methods will record trace events, update Web |
| // Perf API (FP and FCP only), and notify that paint timing has changed, which |
| // triggers UMAs and UKMS. |
| // |stamp| is the swap timestamp used for tracing, UMA, UKM, and Web Perf API. |
| void SetFirstPaintSwap(base::TimeTicks stamp); |
| void SetFirstContentfulPaintSwap(base::TimeTicks stamp); |
| void SetFirstImagePaintSwap(base::TimeTicks stamp); |
| |
| // When quickly navigating back and forward between the pages in the cache |
| // paint events might race with navigations. Pass explicit bfcache restore |
| // index to avoid confusing the data from different navigations. |
| void SetFirstPaintAfterBackForwardCacheRestoreSwap(base::TimeTicks stamp, |
| size_t index); |
| |
| void RegisterNotifySwapTime(PaintEvent); |
| void RegisterNotifyFirstPaintAfterBackForwardCacheRestoreSwapTime( |
| size_t index); |
| |
| base::TimeTicks FirstPaintRendered() const { return first_paint_; } |
| |
| base::TimeTicks FirstContentfulPaintRendered() const { |
| return first_contentful_paint_; |
| } |
| |
| // TODO(crbug/738235): Non first_*_swap_ variables are only being tracked to |
| // compute deltas for reporting histograms and should be removed once we |
| // confirm the deltas and discrepancies look reasonable. |
| base::TimeTicks first_paint_; |
| base::TimeTicks first_paint_swap_; |
| WTF::Vector<base::TimeTicks> |
| first_paints_after_back_forward_cache_restore_swap_; |
| base::TimeTicks first_image_paint_; |
| base::TimeTicks first_image_paint_swap_; |
| base::TimeTicks first_contentful_paint_; |
| base::TimeTicks first_contentful_paint_swap_; |
| base::TimeTicks first_meaningful_paint_swap_; |
| base::TimeTicks first_meaningful_paint_candidate_; |
| base::TimeTicks first_eligible_to_paint_; |
| |
| base::TimeTicks last_portal_activated_swap_; |
| |
| Member<FirstMeaningfulPaintDetector> fmp_detector_; |
| |
| const base::TickClock* clock_; |
| |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, NoFirstPaint); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, OneLayout); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| TwoLayoutsSignificantSecond); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| TwoLayoutsSignificantFirst); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| FirstMeaningfulPaintCandidate); |
| FRIEND_TEST_ALL_PREFIXES( |
| FirstMeaningfulPaintDetectorTest, |
| OnlyOneFirstMeaningfulPaintCandidateBeforeNetworkStable); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| NetworkStableBeforeFirstContentfulPaint); |
| FRIEND_TEST_ALL_PREFIXES( |
| FirstMeaningfulPaintDetectorTest, |
| FirstMeaningfulPaintShouldNotBeBeforeFirstContentfulPaint); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| Network2QuietThen0Quiet); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| Network0QuietThen2Quiet); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| Network0QuietTimer); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| Network2QuietTimer); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| FirstMeaningfulPaintAfterUserInteraction); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| UserInteractionBeforeFirstPaint); |
| FRIEND_TEST_ALL_PREFIXES( |
| FirstMeaningfulPaintDetectorTest, |
| WaitForSingleOutstandingSwapPromiseAfterNetworkStable); |
| FRIEND_TEST_ALL_PREFIXES( |
| FirstMeaningfulPaintDetectorTest, |
| WaitForMultipleOutstandingSwapPromisesAfterNetworkStable); |
| FRIEND_TEST_ALL_PREFIXES(FirstMeaningfulPaintDetectorTest, |
| WaitForFirstContentfulPaintSwapAfterNetworkStable); |
| FRIEND_TEST_ALL_PREFIXES( |
| FirstMeaningfulPaintDetectorTest, |
| ProvisionalTimestampChangesAfterNetworkQuietWithOutstandingSwapPromise); |
| }; |
| |
| } // namespace blink |
| |
| #endif |