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 { |
| 173 HadNetwork0Quiet, |
| 174 HadNetwork2Quiet, |
| 175 HadNetworkQuietEnumMax |
| 176 }; |
| 177 DEFINE_STATIC_LOCAL( |
| 178 EnumerationHistogram, hadNetworkQuietHistogram, |
| 179 ("FirstMeaningfulPaintDetector.HadNetworkQuiet", HadNetworkQuietEnumMax)); |
| 180 |
| 181 enum FMPOrderingEnum { |
| 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 |