blob: 2d7c7007f9007dd6e8a341731ec5c5858fb30220 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/paint/FirstMeaningfulPaintDetector.h"
#include "core/css/FontFaceSet.h"
#include "core/fetch/ResourceFetcher.h"
#include "core/paint/PaintTiming.h"
#include "platform/TraceEvent.h"
namespace blink {
namespace {
// Web fonts that laid out more than this number of characters block First
// 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;
} // namespace
FirstMeaningfulPaintDetector& FirstMeaningfulPaintDetector::from(Document& document)
{
return PaintTiming::from(document).firstMeaningfulPaintDetector();
}
FirstMeaningfulPaintDetector::FirstMeaningfulPaintDetector(PaintTiming* paintTiming)
: m_paintTiming(paintTiming)
, m_networkStableTimer(this, &FirstMeaningfulPaintDetector::networkStableTimerFired)
{
}
Document* FirstMeaningfulPaintDetector::document()
{
return m_paintTiming->document();
}
// Computes "layout significance" (http://goo.gl/rytlPL) of a layout operation.
// Significance of a layout is the number of layout objects newly added to the
// layout tree, weighted by page height (before and after the layout).
// A paint after the most significance layout during page load is reported as
// First Meaningful Paint.
void FirstMeaningfulPaintDetector::markNextPaintAsMeaningfulIfNeeded(
const LayoutObjectCounter& counter, int contentsHeightBeforeLayout,
int contentsHeightAfterLayout, int visibleHeight)
{
if (m_state == Reported)
return;
unsigned delta = counter.count() - m_prevLayoutObjectCount;
m_prevLayoutObjectCount = counter.count();
if (visibleHeight == 0)
return;
double ratioBefore = std::max(1.0, static_cast<double>(contentsHeightBeforeLayout) / visibleHeight);
double ratioAfter = std::max(1.0, static_cast<double>(contentsHeightAfterLayout) / visibleHeight);
double significance = delta / ((ratioBefore + ratioAfter) / 2);
// If the page has many blank characters, the significance value is
// accumulated until the text become visible.
int approximateBlankCharacterCount = FontFaceSet::approximateBlankCharacterCount(*document());
if (approximateBlankCharacterCount > kBlankCharactersThreshold) {
m_accumulatedSignificanceWhileHavingBlankText += significance;
} else {
significance += m_accumulatedSignificanceWhileHavingBlankText;
m_accumulatedSignificanceWhileHavingBlankText = 0;
if (significance > m_maxSignificanceSoFar) {
m_state = NextPaintIsMeaningful;
m_maxSignificanceSoFar = significance;
}
}
}
void FirstMeaningfulPaintDetector::notifyPaint()
{
if (m_state != NextPaintIsMeaningful)
return;
// Skip document background-only paints.
if (m_paintTiming->firstPaint() == 0.0)
return;
m_provisionalFirstMeaningfulPaint = monotonicallyIncreasingTime();
m_state = NextPaintIsNotMeaningful;
TRACE_EVENT_MARK_WITH_TIMESTAMP1("loading", "firstMeaningfulPaintCandidate", TraceEvent::toTraceTimestamp(m_provisionalFirstMeaningfulPaint), "frame", document()->frame());
}
void FirstMeaningfulPaintDetector::checkNetworkStable()
{
DCHECK(document());
if (m_state == Reported || document()->fetcher()->hasPendingRequest())
return;
m_networkStableTimer.startOneShot(kSecondsWithoutNetworkActivityThreshold, BLINK_FROM_HERE);
}
void FirstMeaningfulPaintDetector::networkStableTimerFired(TimerBase*)
{
if (m_state == Reported || !document() || document()->fetcher()->hasPendingRequest())
return;
if (m_provisionalFirstMeaningfulPaint)
m_paintTiming->setFirstMeaningfulPaint(m_provisionalFirstMeaningfulPaint);
m_state = Reported;
}
DEFINE_TRACE(FirstMeaningfulPaintDetector)
{
visitor->trace(m_paintTiming);
}
} // namespace blink