Chromium Code Reviews| 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 |