Index: third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp |
diff --git a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp |
index 9fca4136977ee48f1cbc383fcf7596b21f0748de..5b75deca1ecaa2adbd3f5da0cf28d46e95bae8de 100644 |
--- a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp |
+++ b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp |
@@ -7,6 +7,7 @@ |
#include "core/css/FontFaceSet.h" |
#include "core/dom/TaskRunnerHelper.h" |
#include "core/paint/PaintTiming.h" |
+#include "platform/Histogram.h" |
#include "platform/instrumentation/tracing/TraceEvent.h" |
#include "platform/loader/fetch/ResourceFetcher.h" |
@@ -18,9 +19,10 @@ namespace { |
// Meaningful Paint. |
const int kBlankCharactersThreshold = 200; |
-// FirstMeaningfulPaintDetector stops observing layouts and reports First |
-// Meaningful Paint when this duration passed from last network activity. |
-const double kSecondsWithoutNetworkActivityThreshold = 0.5; |
+// The page is n-quiet if there are no more than n active network requests for |
+// this duration of time. |
+const double kNetwork2QuietWindowSeconds = 3; |
+const double kNetwork0QuietWindowSeconds = 0.5; |
} // namespace |
@@ -33,10 +35,14 @@ FirstMeaningfulPaintDetector::FirstMeaningfulPaintDetector( |
PaintTiming* paintTiming, |
Document& document) |
: m_paintTiming(paintTiming), |
- m_networkStableTimer( |
+ m_network0QuietTimer( |
TaskRunnerHelper::get(TaskType::UnspecedTimer, &document), |
this, |
- &FirstMeaningfulPaintDetector::networkStableTimerFired) {} |
+ &FirstMeaningfulPaintDetector::network0QuietTimerFired), |
+ m_network2QuietTimer( |
+ TaskRunnerHelper::get(TaskType::UnspecedTimer, &document), |
+ this, |
+ &FirstMeaningfulPaintDetector::network2QuietTimerFired) {} |
Document* FirstMeaningfulPaintDetector::document() { |
return m_paintTiming->supplementable(); |
@@ -52,7 +58,7 @@ void FirstMeaningfulPaintDetector::markNextPaintAsMeaningfulIfNeeded( |
int contentsHeightBeforeLayout, |
int contentsHeightAfterLayout, |
int visibleHeight) { |
- if (m_state == Reported) |
+ if (m_network0QuietReached && m_network2QuietReached) |
return; |
unsigned delta = counter.count() - m_prevLayoutObjectCount; |
@@ -77,14 +83,14 @@ void FirstMeaningfulPaintDetector::markNextPaintAsMeaningfulIfNeeded( |
significance += m_accumulatedSignificanceWhileHavingBlankText; |
m_accumulatedSignificanceWhileHavingBlankText = 0; |
if (significance > m_maxSignificanceSoFar) { |
- m_state = NextPaintIsMeaningful; |
+ m_nextPaintIsMeaningful = true; |
m_maxSignificanceSoFar = significance; |
} |
} |
} |
void FirstMeaningfulPaintDetector::notifyPaint() { |
- if (m_state != NextPaintIsMeaningful) |
+ if (!m_nextPaintIsMeaningful) |
return; |
// Skip document background-only paints. |
@@ -92,7 +98,10 @@ void FirstMeaningfulPaintDetector::notifyPaint() { |
return; |
m_provisionalFirstMeaningfulPaint = monotonicallyIncreasingTime(); |
- m_state = NextPaintIsNotMeaningful; |
+ m_nextPaintIsMeaningful = false; |
+ |
+ if (m_network2QuietReached) |
+ return; |
TRACE_EVENT_MARK_WITH_TIMESTAMP1( |
"loading", "firstMeaningfulPaintCandidate", |
@@ -107,28 +116,110 @@ void FirstMeaningfulPaintDetector::notifyPaint() { |
m_paintTiming->markFirstMeaningfulPaintCandidate(); |
} |
+int FirstMeaningfulPaintDetector::activeConnections() { |
+ DCHECK(document()); |
+ ResourceFetcher* fetcher = document()->fetcher(); |
+ return fetcher->blockingRequestCount() + fetcher->nonblockingRequestCount(); |
+} |
+ |
+// This function is called when the number of active connections is decreased. |
void FirstMeaningfulPaintDetector::checkNetworkStable() { |
DCHECK(document()); |
- if (m_state == Reported || document()->fetcher()->hasPendingRequest()) |
+ if (!document()->hasFinishedParsing()) |
+ return; |
+ |
+ setNetworkQuietTimers(activeConnections()); |
+} |
+ |
+void FirstMeaningfulPaintDetector::setNetworkQuietTimers( |
+ int activeConnections) { |
+ if (!m_network0QuietReached && activeConnections == 0) { |
+ // This restarts 0-quiet timer if it's already running. |
+ m_network0QuietTimer.startOneShot(kNetwork0QuietWindowSeconds, |
+ BLINK_FROM_HERE); |
+ } |
+ if (!m_network2QuietReached && activeConnections <= 2) { |
+ // If activeConnections < 2 and the timer is already running, current |
+ // 2-quiet window continues; the timer shouldn't be restarted. |
+ if (activeConnections == 2 || !m_network2QuietTimer.isActive()) { |
+ m_network2QuietTimer.startOneShot(kNetwork2QuietWindowSeconds, |
+ BLINK_FROM_HERE); |
+ } |
+ } |
+} |
+ |
+void FirstMeaningfulPaintDetector::network0QuietTimerFired(TimerBase*) { |
+ if (!document() || m_network0QuietReached || activeConnections() > 0 || |
+ !m_paintTiming->firstContentfulPaint()) |
return; |
+ m_network0QuietReached = true; |
- m_networkStableTimer.startOneShot(kSecondsWithoutNetworkActivityThreshold, |
- BLINK_FROM_HERE); |
+ if (m_provisionalFirstMeaningfulPaint) { |
+ // Enforce FirstContentfulPaint <= FirstMeaningfulPaint. |
+ m_firstMeaningfulPaint0Quiet = |
+ std::max(m_provisionalFirstMeaningfulPaint, |
+ m_paintTiming->firstContentfulPaint()); |
+ } |
+ reportHistograms(); |
} |
-void FirstMeaningfulPaintDetector::networkStableTimerFired(TimerBase*) { |
- if (m_state == Reported || !document() || |
- document()->fetcher()->hasPendingRequest() || |
+void FirstMeaningfulPaintDetector::network2QuietTimerFired(TimerBase*) { |
+ if (!document() || m_network2QuietReached || activeConnections() > 2 || |
!m_paintTiming->firstContentfulPaint()) |
return; |
+ m_network2QuietReached = true; |
if (m_provisionalFirstMeaningfulPaint) { |
// Enforce FirstContentfulPaint <= FirstMeaningfulPaint. |
- double timestamp = std::max(m_provisionalFirstMeaningfulPaint, |
- m_paintTiming->firstContentfulPaint()); |
- m_paintTiming->setFirstMeaningfulPaint(timestamp); |
+ m_firstMeaningfulPaint2Quiet = |
+ std::max(m_provisionalFirstMeaningfulPaint, |
+ m_paintTiming->firstContentfulPaint()); |
+ // Report FirstMeaningfulPaint when the page reached network 2-quiet. |
+ m_paintTiming->setFirstMeaningfulPaint(m_firstMeaningfulPaint2Quiet); |
+ } |
+ reportHistograms(); |
+} |
+ |
+void FirstMeaningfulPaintDetector::reportHistograms() { |
+ // This enum backs an UMA histogram, and should be treated as append-only. |
+ enum HadNetworkQuiet { |
+ HadNetwork0Quiet, |
+ HadNetwork2Quiet, |
+ HadNetworkQuietEnumMax |
+ }; |
+ DEFINE_STATIC_LOCAL(EnumerationHistogram, hadNetworkQuietHistogram, |
+ ("PageLoad.Experimental.Renderer." |
+ "FirstMeaningfulPaintDetector.HadNetworkQuiet", |
+ HadNetworkQuietEnumMax)); |
+ |
+ // This enum backs an UMA histogram, and should be treated as append-only. |
+ enum FMPOrderingEnum { |
+ FMP0QuietFirst, |
+ FMP2QuietFirst, |
+ FMP0QuietEqualFMP2Quiet, |
+ FMPOrderingEnumMax |
+ }; |
+ DEFINE_STATIC_LOCAL( |
+ EnumerationHistogram, firstMeaningfulPaintOrderingHistogram, |
+ ("PageLoad.Experimental.Renderer.FirstMeaningfulPaintDetector." |
+ "FirstMeaningfulPaintOrdering", |
+ FMPOrderingEnumMax)); |
+ |
+ if (m_firstMeaningfulPaint0Quiet && m_firstMeaningfulPaint2Quiet) { |
+ int sample; |
+ if (m_firstMeaningfulPaint2Quiet < m_firstMeaningfulPaint0Quiet) { |
+ sample = FMP0QuietFirst; |
+ } else if (m_firstMeaningfulPaint2Quiet > m_firstMeaningfulPaint0Quiet) { |
+ sample = FMP2QuietFirst; |
+ } else { |
+ sample = FMP0QuietEqualFMP2Quiet; |
+ } |
+ firstMeaningfulPaintOrderingHistogram.count(sample); |
+ } else if (m_firstMeaningfulPaint0Quiet) { |
+ hadNetworkQuietHistogram.count(HadNetwork0Quiet); |
+ } else if (m_firstMeaningfulPaint2Quiet) { |
+ hadNetworkQuietHistogram.count(HadNetwork2Quiet); |
} |
- m_state = Reported; |
} |
DEFINE_TRACE(FirstMeaningfulPaintDetector) { |