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 |