| 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, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 45 // Computes "layout significance" (http://goo.gl/rytlPL) of a layout operation. | 47 // 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 | 48 // 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). | 49 // 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 | 50 // A paint after the most significance layout during page load is reported as |
| 49 // First Meaningful Paint. | 51 // First Meaningful Paint. |
| 50 void FirstMeaningfulPaintDetector::markNextPaintAsMeaningfulIfNeeded( | 52 void FirstMeaningfulPaintDetector::markNextPaintAsMeaningfulIfNeeded( |
| 51 const LayoutObjectCounter& counter, | 53 const LayoutObjectCounter& counter, |
| 52 int contentsHeightBeforeLayout, | 54 int contentsHeightBeforeLayout, |
| 53 int contentsHeightAfterLayout, | 55 int contentsHeightAfterLayout, |
| 54 int visibleHeight) { | 56 int visibleHeight) { |
| 55 if (m_state == Reported) | 57 if (m_networkState == Network0Quiet) |
| 56 return; | 58 return; |
| 57 | 59 |
| 58 unsigned delta = counter.count() - m_prevLayoutObjectCount; | 60 unsigned delta = counter.count() - m_prevLayoutObjectCount; |
| 59 m_prevLayoutObjectCount = counter.count(); | 61 m_prevLayoutObjectCount = counter.count(); |
| 60 | 62 |
| 61 if (visibleHeight == 0) | 63 if (visibleHeight == 0) |
| 62 return; | 64 return; |
| 63 | 65 |
| 64 double ratioBefore = std::max( | 66 double ratioBefore = std::max( |
| 65 1.0, static_cast<double>(contentsHeightBeforeLayout) / visibleHeight); | 67 1.0, static_cast<double>(contentsHeightBeforeLayout) / visibleHeight); |
| 66 double ratioAfter = std::max( | 68 double ratioAfter = std::max( |
| 67 1.0, static_cast<double>(contentsHeightAfterLayout) / visibleHeight); | 69 1.0, static_cast<double>(contentsHeightAfterLayout) / visibleHeight); |
| 68 double significance = delta / ((ratioBefore + ratioAfter) / 2); | 70 double significance = delta / ((ratioBefore + ratioAfter) / 2); |
| 69 | 71 |
| 70 // If the page has many blank characters, the significance value is | 72 // If the page has many blank characters, the significance value is |
| 71 // accumulated until the text become visible. | 73 // accumulated until the text become visible. |
| 72 int approximateBlankCharacterCount = | 74 int approximateBlankCharacterCount = |
| 73 FontFaceSet::approximateBlankCharacterCount(*document()); | 75 FontFaceSet::approximateBlankCharacterCount(*document()); |
| 74 if (approximateBlankCharacterCount > kBlankCharactersThreshold) { | 76 if (approximateBlankCharacterCount > kBlankCharactersThreshold) { |
| 75 m_accumulatedSignificanceWhileHavingBlankText += significance; | 77 m_accumulatedSignificanceWhileHavingBlankText += significance; |
| 76 } else { | 78 } else { |
| 77 significance += m_accumulatedSignificanceWhileHavingBlankText; | 79 significance += m_accumulatedSignificanceWhileHavingBlankText; |
| 78 m_accumulatedSignificanceWhileHavingBlankText = 0; | 80 m_accumulatedSignificanceWhileHavingBlankText = 0; |
| 79 if (significance > m_maxSignificanceSoFar) { | 81 if (significance > m_maxSignificanceSoFar) { |
| 80 m_state = NextPaintIsMeaningful; | 82 m_nextPaintIsMeaningful = true; |
| 81 m_maxSignificanceSoFar = significance; | 83 m_maxSignificanceSoFar = significance; |
| 82 } | 84 } |
| 83 } | 85 } |
| 84 } | 86 } |
| 85 | 87 |
| 86 void FirstMeaningfulPaintDetector::notifyPaint() { | 88 void FirstMeaningfulPaintDetector::notifyPaint() { |
| 87 if (m_state != NextPaintIsMeaningful) | 89 if (!m_nextPaintIsMeaningful) |
| 88 return; | 90 return; |
| 89 | 91 |
| 90 // Skip document background-only paints. | 92 // Skip document background-only paints. |
| 91 if (m_paintTiming->firstPaint() == 0.0) | 93 if (m_paintTiming->firstPaint() == 0.0) |
| 92 return; | 94 return; |
| 93 | 95 |
| 94 m_provisionalFirstMeaningfulPaint = monotonicallyIncreasingTime(); | 96 m_provisionalFirstMeaningfulPaint = monotonicallyIncreasingTime(); |
| 95 m_state = NextPaintIsNotMeaningful; | 97 m_nextPaintIsMeaningful = false; |
| 98 m_networkStateAtFirstMeaningfulPaint = m_networkState; |
| 99 |
| 100 if (m_networkState != NetworkActive) |
| 101 return; |
| 96 | 102 |
| 97 TRACE_EVENT_MARK_WITH_TIMESTAMP1( | 103 TRACE_EVENT_MARK_WITH_TIMESTAMP1( |
| 98 "loading", "firstMeaningfulPaintCandidate", | 104 "loading", "firstMeaningfulPaintCandidate", |
| 99 TraceEvent::toTraceTimestamp(m_provisionalFirstMeaningfulPaint), "frame", | 105 TraceEvent::toTraceTimestamp(m_provisionalFirstMeaningfulPaint), "frame", |
| 100 document()->frame()); | 106 document()->frame()); |
| 101 // Ignore the first meaningful paint candidate as this generally is the first | 107 // Ignore the first meaningful paint candidate as this generally is the first |
| 102 // contentful paint itself. | 108 // contentful paint itself. |
| 103 if (!m_seenFirstMeaningfulPaintCandidate) { | 109 if (!m_seenFirstMeaningfulPaintCandidate) { |
| 104 m_seenFirstMeaningfulPaintCandidate = true; | 110 m_seenFirstMeaningfulPaintCandidate = true; |
| 105 return; | 111 return; |
| 106 } | 112 } |
| 107 m_paintTiming->markFirstMeaningfulPaintCandidate(); | 113 m_paintTiming->markFirstMeaningfulPaintCandidate(); |
| 108 } | 114 } |
| 109 | 115 |
| 116 bool FirstMeaningfulPaintDetector::isNetworkQuiet(int maxActiveConnections) { |
| 117 DCHECK(document()); |
| 118 if (!document()->hasFinishedParsing()) |
| 119 return false; |
| 120 ResourceFetcher* fetcher = document()->fetcher(); |
| 121 return fetcher->blockingRequestCount() + fetcher->nonblockingRequestCount() <= |
| 122 maxActiveConnections; |
| 123 } |
| 124 |
| 110 void FirstMeaningfulPaintDetector::checkNetworkStable() { | 125 void FirstMeaningfulPaintDetector::checkNetworkStable() { |
| 111 DCHECK(document()); | 126 switch (m_networkState) { |
| 112 if (m_state == Reported || document()->fetcher()->hasPendingRequest()) | 127 case NetworkActive: |
| 113 return; | 128 if (!isNetworkQuiet(2)) |
| 129 return; |
| 130 m_networkStableTimer.startOneShot(kNetwork2QuietWindowSeconds, |
| 131 BLINK_FROM_HERE); |
| 132 break; |
| 114 | 133 |
| 115 m_networkStableTimer.startOneShot(kSecondsWithoutNetworkActivityThreshold, | 134 case Network2Quiet: |
| 116 BLINK_FROM_HERE); | 135 if (!isNetworkQuiet(0)) |
| 136 return; |
| 137 m_networkStableTimer.startOneShot(kNetwork0QuietWindowSeconds, |
| 138 BLINK_FROM_HERE); |
| 139 break; |
| 140 |
| 141 case Network0Quiet: |
| 142 break; |
| 143 } |
| 117 } | 144 } |
| 118 | 145 |
| 119 void FirstMeaningfulPaintDetector::networkStableTimerFired(TimerBase*) { | 146 void FirstMeaningfulPaintDetector::networkStableTimerFired(TimerBase*) { |
| 120 if (m_state == Reported || !document() || | 147 // This histogram is logged when the page reached network 0-quiet, so only |
| 121 document()->fetcher()->hasPendingRequest() || | 148 // possible values are NetworkActive and Network2Quiet. |
| 122 !m_paintTiming->firstContentfulPaint()) | 149 DEFINE_STATIC_LOCAL( |
| 150 EnumerationHistogram, networkStateAtFirstMeaningfulPaintHistogram, |
| 151 ("PageLoad.Experimental.PaintTiming.NetworkStateAtFirstMeaningfulPaint", |
| 152 Network0Quiet)); |
| 153 |
| 154 if (!document()) |
| 123 return; | 155 return; |
| 124 | 156 |
| 125 if (m_provisionalFirstMeaningfulPaint) { | 157 switch (m_networkState) { |
| 126 // Enforce FirstContentfulPaint <= FirstMeaningfulPaint. | 158 case NetworkActive: |
| 127 double timestamp = std::max(m_provisionalFirstMeaningfulPaint, | 159 if (!isNetworkQuiet(2) || !m_paintTiming->firstContentfulPaint()) |
| 128 m_paintTiming->firstContentfulPaint()); | 160 return; |
| 129 m_paintTiming->setFirstMeaningfulPaint(timestamp); | 161 m_networkState = Network2Quiet; |
| 162 // Report FirstMeaningfulPaint when the page reached network 2-quiet. |
| 163 if (m_provisionalFirstMeaningfulPaint) { |
| 164 // Enforce FirstContentfulPaint <= FirstMeaningfulPaint. |
| 165 double timestamp = std::max(m_provisionalFirstMeaningfulPaint, |
| 166 m_paintTiming->firstContentfulPaint()); |
| 167 m_paintTiming->setFirstMeaningfulPaint(timestamp); |
| 168 } |
| 169 checkNetworkStable(); |
| 170 break; |
| 171 |
| 172 case Network2Quiet: |
| 173 if (!isNetworkQuiet(0)) |
| 174 return; |
| 175 m_networkState = Network0Quiet; |
| 176 networkStateAtFirstMeaningfulPaintHistogram.count( |
| 177 m_networkStateAtFirstMeaningfulPaint); |
| 178 break; |
| 179 |
| 180 case Network0Quiet: |
| 181 NOTREACHED(); |
| 182 break; |
| 130 } | 183 } |
| 131 m_state = Reported; | |
| 132 } | 184 } |
| 133 | 185 |
| 134 DEFINE_TRACE(FirstMeaningfulPaintDetector) { | 186 DEFINE_TRACE(FirstMeaningfulPaintDetector) { |
| 135 visitor->trace(m_paintTiming); | 187 visitor->trace(m_paintTiming); |
| 136 } | 188 } |
| 137 | 189 |
| 138 } // namespace blink | 190 } // namespace blink |
| OLD | NEW |