Chromium Code Reviews| 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..c544c7ad14ef81fd575fbe9cc04b61b1bbb8ea24 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,94 @@ void FirstMeaningfulPaintDetector::notifyPaint() { |
| m_paintTiming->markFirstMeaningfulPaintCandidate(); |
| } |
| -void FirstMeaningfulPaintDetector::checkNetworkStable() { |
| +bool FirstMeaningfulPaintDetector::isNetworkQuiet(int maxActiveConnections) { |
| DCHECK(document()); |
| - if (m_state == Reported || document()->fetcher()->hasPendingRequest()) |
| + if (!document()->hasFinishedParsing()) |
| + return false; |
| + ResourceFetcher* fetcher = document()->fetcher(); |
| + return fetcher->blockingRequestCount() + fetcher->nonblockingRequestCount() <= |
| + maxActiveConnections; |
| +} |
| + |
| +void FirstMeaningfulPaintDetector::checkNetworkStable() { |
| + if (!m_network0QuietReached && isNetworkQuiet(0)) { |
| + m_network0QuietTimer.startOneShot(kNetwork0QuietWindowSeconds, |
| + BLINK_FROM_HERE); |
| + } |
| + if (!m_network2QuietReached && isNetworkQuiet(2)) { |
| + m_network2QuietTimer.startOneShot(kNetwork2QuietWindowSeconds, |
| + BLINK_FROM_HERE); |
| + } |
| +} |
| + |
| +void FirstMeaningfulPaintDetector::network0QuietTimerFired(TimerBase*) { |
| + if (!document() || m_network0QuietReached || !isNetworkQuiet(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 || !isNetworkQuiet(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() { |
| + 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.
|
| + HadNetwork0Quiet, |
| + HadNetwork2Quiet, |
| + HadNetworkQuietEnumMax |
| + }; |
| + DEFINE_STATIC_LOCAL( |
| + EnumerationHistogram, hadNetworkQuietHistogram, |
| + ("FirstMeaningfulPaintDetector.HadNetworkQuiet", HadNetworkQuietEnumMax)); |
| + |
| + enum FMPOrderingEnum { |
|
Ilya Sherman
2017/03/08 09:19:46
And this one too =)
Kunihiko Sakamoto
2017/03/08 10:16:10
Done.
|
| + FMP0QuietFirst, |
| + FMP2QuietFirst, |
| + FMP0QuietEqualFMP2Quiet, |
| + FMPOrderingEnumMax |
| + }; |
| + DEFINE_STATIC_LOCAL( |
| + EnumerationHistogram, firstMeaningfulPaintOrderingHistogram, |
| + ("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) { |