OLD | NEW |
---|---|
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 |
OLD | NEW |