Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(809)

Side by Side Diff: third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp

Issue 2711893002: Loosen FirstMeaningfulPaint UMA network-idle heuristics (Closed)
Patch Set: add tests, rebase Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/paint/FirstMeaningfulPaintDetector.h" 5 #include "core/paint/FirstMeaningfulPaintDetector.h"
6 6
7 #include "core/css/FontFaceSet.h" 7 #include "core/css/FontFaceSet.h"
8 #include "core/dom/TaskRunnerHelper.h" 8 #include "core/dom/TaskRunnerHelper.h"
9 #include "core/paint/PaintTiming.h" 9 #include "core/paint/PaintTiming.h"
10 #include "platform/Histogram.h"
10 #include "platform/instrumentation/tracing/TraceEvent.h" 11 #include "platform/instrumentation/tracing/TraceEvent.h"
11 #include "platform/loader/fetch/ResourceFetcher.h" 12 #include "platform/loader/fetch/ResourceFetcher.h"
12 13
13 namespace blink { 14 namespace blink {
14 15
15 namespace { 16 namespace {
16 17
17 // Web fonts that laid out more than this number of characters block First 18 // Web fonts that laid out more than this number of characters block First
18 // Meaningful Paint. 19 // Meaningful Paint.
19 const int kBlankCharactersThreshold = 200; 20 const int kBlankCharactersThreshold = 200;
20 21
21 // FirstMeaningfulPaintDetector stops observing layouts and reports First 22 // The page is n-quiet if there are no more than n active network requests for
22 // Meaningful Paint when this duration passed from last network activity. 23 // this duration of time.
23 const double kSecondsWithoutNetworkActivityThreshold = 0.5; 24 const double kNetwork2QuietWindowSeconds = 3;
25 const double kNetwork0QuietWindowSeconds = 0.5;
24 26
25 } // namespace 27 } // namespace
26 28
27 FirstMeaningfulPaintDetector& FirstMeaningfulPaintDetector::from( 29 FirstMeaningfulPaintDetector& FirstMeaningfulPaintDetector::from(
28 Document& document) { 30 Document& document) {
29 return PaintTiming::from(document).firstMeaningfulPaintDetector(); 31 return PaintTiming::from(document).firstMeaningfulPaintDetector();
30 } 32 }
31 33
32 FirstMeaningfulPaintDetector::FirstMeaningfulPaintDetector( 34 FirstMeaningfulPaintDetector::FirstMeaningfulPaintDetector(
33 PaintTiming* paintTiming, 35 PaintTiming* paintTiming,
34 Document& document) 36 Document& document)
35 : m_paintTiming(paintTiming), 37 : m_paintTiming(paintTiming),
36 m_networkStableTimer( 38 m_network0QuietTimer(
37 TaskRunnerHelper::get(TaskType::UnspecedTimer, &document), 39 TaskRunnerHelper::get(TaskType::UnspecedTimer, &document),
38 this, 40 this,
39 &FirstMeaningfulPaintDetector::networkStableTimerFired) {} 41 &FirstMeaningfulPaintDetector::network0QuietTimerFired),
42 m_network2QuietTimer(
43 TaskRunnerHelper::get(TaskType::UnspecedTimer, &document),
44 this,
45 &FirstMeaningfulPaintDetector::network2QuietTimerFired) {}
40 46
41 Document* FirstMeaningfulPaintDetector::document() { 47 Document* FirstMeaningfulPaintDetector::document() {
42 return m_paintTiming->supplementable(); 48 return m_paintTiming->supplementable();
43 } 49 }
44 50
45 // Computes "layout significance" (http://goo.gl/rytlPL) of a layout operation. 51 // Computes "layout significance" (http://goo.gl/rytlPL) of a layout operation.
46 // Significance of a layout is the number of layout objects newly added to the 52 // Significance of a layout is the number of layout objects newly added to the
47 // layout tree, weighted by page height (before and after the layout). 53 // layout tree, weighted by page height (before and after the layout).
48 // A paint after the most significance layout during page load is reported as 54 // A paint after the most significance layout during page load is reported as
49 // First Meaningful Paint. 55 // First Meaningful Paint.
50 void FirstMeaningfulPaintDetector::markNextPaintAsMeaningfulIfNeeded( 56 void FirstMeaningfulPaintDetector::markNextPaintAsMeaningfulIfNeeded(
51 const LayoutObjectCounter& counter, 57 const LayoutObjectCounter& counter,
52 int contentsHeightBeforeLayout, 58 int contentsHeightBeforeLayout,
53 int contentsHeightAfterLayout, 59 int contentsHeightAfterLayout,
54 int visibleHeight) { 60 int visibleHeight) {
55 if (m_state == Reported) 61 if (m_network0QuietReached && m_network2QuietReached)
56 return; 62 return;
57 63
58 unsigned delta = counter.count() - m_prevLayoutObjectCount; 64 unsigned delta = counter.count() - m_prevLayoutObjectCount;
59 m_prevLayoutObjectCount = counter.count(); 65 m_prevLayoutObjectCount = counter.count();
60 66
61 if (visibleHeight == 0) 67 if (visibleHeight == 0)
62 return; 68 return;
63 69
64 double ratioBefore = std::max( 70 double ratioBefore = std::max(
65 1.0, static_cast<double>(contentsHeightBeforeLayout) / visibleHeight); 71 1.0, static_cast<double>(contentsHeightBeforeLayout) / visibleHeight);
66 double ratioAfter = std::max( 72 double ratioAfter = std::max(
67 1.0, static_cast<double>(contentsHeightAfterLayout) / visibleHeight); 73 1.0, static_cast<double>(contentsHeightAfterLayout) / visibleHeight);
68 double significance = delta / ((ratioBefore + ratioAfter) / 2); 74 double significance = delta / ((ratioBefore + ratioAfter) / 2);
69 75
70 // If the page has many blank characters, the significance value is 76 // If the page has many blank characters, the significance value is
71 // accumulated until the text become visible. 77 // accumulated until the text become visible.
72 int approximateBlankCharacterCount = 78 int approximateBlankCharacterCount =
73 FontFaceSet::approximateBlankCharacterCount(*document()); 79 FontFaceSet::approximateBlankCharacterCount(*document());
74 if (approximateBlankCharacterCount > kBlankCharactersThreshold) { 80 if (approximateBlankCharacterCount > kBlankCharactersThreshold) {
75 m_accumulatedSignificanceWhileHavingBlankText += significance; 81 m_accumulatedSignificanceWhileHavingBlankText += significance;
76 } else { 82 } else {
77 significance += m_accumulatedSignificanceWhileHavingBlankText; 83 significance += m_accumulatedSignificanceWhileHavingBlankText;
78 m_accumulatedSignificanceWhileHavingBlankText = 0; 84 m_accumulatedSignificanceWhileHavingBlankText = 0;
79 if (significance > m_maxSignificanceSoFar) { 85 if (significance > m_maxSignificanceSoFar) {
80 m_state = NextPaintIsMeaningful; 86 m_nextPaintIsMeaningful = true;
81 m_maxSignificanceSoFar = significance; 87 m_maxSignificanceSoFar = significance;
82 } 88 }
83 } 89 }
84 } 90 }
85 91
86 void FirstMeaningfulPaintDetector::notifyPaint() { 92 void FirstMeaningfulPaintDetector::notifyPaint() {
87 if (m_state != NextPaintIsMeaningful) 93 if (!m_nextPaintIsMeaningful)
88 return; 94 return;
89 95
90 // Skip document background-only paints. 96 // Skip document background-only paints.
91 if (m_paintTiming->firstPaint() == 0.0) 97 if (m_paintTiming->firstPaint() == 0.0)
92 return; 98 return;
93 99
94 m_provisionalFirstMeaningfulPaint = monotonicallyIncreasingTime(); 100 m_provisionalFirstMeaningfulPaint = monotonicallyIncreasingTime();
95 m_state = NextPaintIsNotMeaningful; 101 m_nextPaintIsMeaningful = false;
102
103 if (m_network2QuietReached)
104 return;
96 105
97 TRACE_EVENT_MARK_WITH_TIMESTAMP1( 106 TRACE_EVENT_MARK_WITH_TIMESTAMP1(
98 "loading", "firstMeaningfulPaintCandidate", 107 "loading", "firstMeaningfulPaintCandidate",
99 TraceEvent::toTraceTimestamp(m_provisionalFirstMeaningfulPaint), "frame", 108 TraceEvent::toTraceTimestamp(m_provisionalFirstMeaningfulPaint), "frame",
100 document()->frame()); 109 document()->frame());
101 // Ignore the first meaningful paint candidate as this generally is the first 110 // Ignore the first meaningful paint candidate as this generally is the first
102 // contentful paint itself. 111 // contentful paint itself.
103 if (!m_seenFirstMeaningfulPaintCandidate) { 112 if (!m_seenFirstMeaningfulPaintCandidate) {
104 m_seenFirstMeaningfulPaintCandidate = true; 113 m_seenFirstMeaningfulPaintCandidate = true;
105 return; 114 return;
106 } 115 }
107 m_paintTiming->markFirstMeaningfulPaintCandidate(); 116 m_paintTiming->markFirstMeaningfulPaintCandidate();
108 } 117 }
109 118
110 void FirstMeaningfulPaintDetector::checkNetworkStable() { 119 bool FirstMeaningfulPaintDetector::isNetworkQuiet(int maxActiveConnections) {
111 DCHECK(document()); 120 DCHECK(document());
112 if (m_state == Reported || document()->fetcher()->hasPendingRequest()) 121 if (!document()->hasFinishedParsing())
113 return; 122 return false;
114 123 ResourceFetcher* fetcher = document()->fetcher();
115 m_networkStableTimer.startOneShot(kSecondsWithoutNetworkActivityThreshold, 124 return fetcher->blockingRequestCount() + fetcher->nonblockingRequestCount() <=
116 BLINK_FROM_HERE); 125 maxActiveConnections;
117 } 126 }
118 127
119 void FirstMeaningfulPaintDetector::networkStableTimerFired(TimerBase*) { 128 void FirstMeaningfulPaintDetector::checkNetworkStable() {
120 if (m_state == Reported || !document() || 129 if (!m_network0QuietReached && isNetworkQuiet(0)) {
121 document()->fetcher()->hasPendingRequest() || 130 m_network0QuietTimer.startOneShot(kNetwork0QuietWindowSeconds,
131 BLINK_FROM_HERE);
132 }
133 if (!m_network2QuietReached && isNetworkQuiet(2)) {
134 m_network2QuietTimer.startOneShot(kNetwork2QuietWindowSeconds,
135 BLINK_FROM_HERE);
136 }
137 }
138
139 void FirstMeaningfulPaintDetector::network0QuietTimerFired(TimerBase*) {
140 if (!document() || m_network0QuietReached || !isNetworkQuiet(0) ||
122 !m_paintTiming->firstContentfulPaint()) 141 !m_paintTiming->firstContentfulPaint())
123 return; 142 return;
143 m_network0QuietReached = true;
124 144
125 if (m_provisionalFirstMeaningfulPaint) { 145 if (m_provisionalFirstMeaningfulPaint) {
126 // Enforce FirstContentfulPaint <= FirstMeaningfulPaint. 146 // Enforce FirstContentfulPaint <= FirstMeaningfulPaint.
127 double timestamp = std::max(m_provisionalFirstMeaningfulPaint, 147 m_firstMeaningfulPaint0Quiet =
128 m_paintTiming->firstContentfulPaint()); 148 std::max(m_provisionalFirstMeaningfulPaint,
129 m_paintTiming->setFirstMeaningfulPaint(timestamp); 149 m_paintTiming->firstContentfulPaint());
130 } 150 }
131 m_state = Reported; 151 reportHistograms();
152 }
153
154 void FirstMeaningfulPaintDetector::network2QuietTimerFired(TimerBase*) {
155 if (!document() || m_network2QuietReached || !isNetworkQuiet(2) ||
156 !m_paintTiming->firstContentfulPaint())
157 return;
158 m_network2QuietReached = true;
159
160 if (m_provisionalFirstMeaningfulPaint) {
161 // Enforce FirstContentfulPaint <= FirstMeaningfulPaint.
162 m_firstMeaningfulPaint2Quiet =
163 std::max(m_provisionalFirstMeaningfulPaint,
164 m_paintTiming->firstContentfulPaint());
165 // Report FirstMeaningfulPaint when the page reached network 2-quiet.
166 m_paintTiming->setFirstMeaningfulPaint(m_firstMeaningfulPaint2Quiet);
167 }
168 reportHistograms();
169 }
170
171 void FirstMeaningfulPaintDetector::reportHistograms() {
172 enum HadNetworkQuiet {
Ilya Sherman 2017/03/08 09:19:46 Please document that this enum should be treated a
Kunihiko Sakamoto 2017/03/08 10:16:10 Done.
173 HadNetwork0Quiet,
174 HadNetwork2Quiet,
175 HadNetworkQuietEnumMax
176 };
177 DEFINE_STATIC_LOCAL(
178 EnumerationHistogram, hadNetworkQuietHistogram,
179 ("FirstMeaningfulPaintDetector.HadNetworkQuiet", HadNetworkQuietEnumMax));
180
181 enum FMPOrderingEnum {
Ilya Sherman 2017/03/08 09:19:46 And this one too =)
Kunihiko Sakamoto 2017/03/08 10:16:10 Done.
182 FMP0QuietFirst,
183 FMP2QuietFirst,
184 FMP0QuietEqualFMP2Quiet,
185 FMPOrderingEnumMax
186 };
187 DEFINE_STATIC_LOCAL(
188 EnumerationHistogram, firstMeaningfulPaintOrderingHistogram,
189 ("FirstMeaningfulPaintDetector.FirstMeaningfulPaintOrdering",
190 FMPOrderingEnumMax));
191
192 if (m_firstMeaningfulPaint0Quiet && m_firstMeaningfulPaint2Quiet) {
193 int sample;
194 if (m_firstMeaningfulPaint2Quiet < m_firstMeaningfulPaint0Quiet) {
195 sample = FMP0QuietFirst;
196 } else if (m_firstMeaningfulPaint2Quiet > m_firstMeaningfulPaint0Quiet) {
197 sample = FMP2QuietFirst;
198 } else {
199 sample = FMP0QuietEqualFMP2Quiet;
200 }
201 firstMeaningfulPaintOrderingHistogram.count(sample);
202 } else if (m_firstMeaningfulPaint0Quiet) {
203 hadNetworkQuietHistogram.count(HadNetwork0Quiet);
204 } else if (m_firstMeaningfulPaint2Quiet) {
205 hadNetworkQuietHistogram.count(HadNetwork2Quiet);
206 }
132 } 207 }
133 208
134 DEFINE_TRACE(FirstMeaningfulPaintDetector) { 209 DEFINE_TRACE(FirstMeaningfulPaintDetector) {
135 visitor->trace(m_paintTiming); 210 visitor->trace(m_paintTiming);
136 } 211 }
137 212
138 } // namespace blink 213 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698