blob: 60923e0ce23c35760391ffefe1433cd337e56add [file] [log] [blame]
// Copyright 2015 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/PaintTiming.h"
#include "core/dom/Document.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalDOMWindow.h"
#include "core/frame/LocalFrame.h"
#include "core/loader/DocumentLoader.h"
#include "core/timing/DOMWindowPerformance.h"
#include "core/timing/Performance.h"
#include "platform/WebFrameScheduler.h"
#include "platform/instrumentation/tracing/TraceEvent.h"
namespace blink {
namespace {
Performance* getPerformanceInstance(LocalFrame* frame) {
Performance* performance = nullptr;
if (frame && frame->domWindow()) {
performance = DOMWindowPerformance::performance(*frame->domWindow());
}
return performance;
}
} // namespace
static const char kSupplementName[] = "PaintTiming";
PaintTiming& PaintTiming::from(Document& document) {
PaintTiming* timing = static_cast<PaintTiming*>(
Supplement<Document>::from(document, kSupplementName));
if (!timing) {
timing = new PaintTiming(document);
Supplement<Document>::provideTo(document, kSupplementName, timing);
}
return *timing;
}
void PaintTiming::markFirstPaint() {
// Test that m_firstPaint is non-zero here, as well as in setFirstPaint, so
// we avoid invoking monotonicallyIncreasingTime() on every call to
// markFirstPaint().
if (m_firstPaint != 0.0)
return;
setFirstPaint(monotonicallyIncreasingTime());
notifyPaintTimingChanged();
}
void PaintTiming::markFirstContentfulPaint() {
// Test that m_firstContentfulPaint is non-zero here, as well as in
// setFirstContentfulPaint, so we avoid invoking
// monotonicallyIncreasingTime() on every call to
// markFirstContentfulPaint().
if (m_firstContentfulPaint != 0.0)
return;
setFirstContentfulPaint(monotonicallyIncreasingTime());
notifyPaintTimingChanged();
}
void PaintTiming::markFirstTextPaint() {
if (m_firstTextPaint != 0.0)
return;
m_firstTextPaint = monotonicallyIncreasingTime();
setFirstContentfulPaint(m_firstTextPaint);
TRACE_EVENT_MARK_WITH_TIMESTAMP1(
"blink.user_timing,rail", "firstTextPaint",
TraceEvent::toTraceTimestamp(m_firstTextPaint), "frame", frame());
notifyPaintTimingChanged();
}
void PaintTiming::markFirstImagePaint() {
if (m_firstImagePaint != 0.0)
return;
m_firstImagePaint = monotonicallyIncreasingTime();
setFirstContentfulPaint(m_firstImagePaint);
TRACE_EVENT_MARK_WITH_TIMESTAMP1(
"blink.user_timing,rail", "firstImagePaint",
TraceEvent::toTraceTimestamp(m_firstImagePaint), "frame", frame());
notifyPaintTimingChanged();
}
void PaintTiming::markFirstMeaningfulPaintCandidate() {
if (m_firstMeaningfulPaintCandidate)
return;
m_firstMeaningfulPaintCandidate = monotonicallyIncreasingTime();
if (frame() && frame()->view() && !frame()->view()->parent()) {
frame()->frameScheduler()->onFirstMeaningfulPaint();
}
}
void PaintTiming::setFirstMeaningfulPaint(double stamp) {
DCHECK_EQ(m_firstMeaningfulPaint, 0.0);
m_firstMeaningfulPaint = stamp;
TRACE_EVENT_MARK_WITH_TIMESTAMP1(
"blink.user_timing", "firstMeaningfulPaint",
TraceEvent::toTraceTimestamp(m_firstMeaningfulPaint), "frame", frame());
notifyPaintTimingChanged();
}
void PaintTiming::notifyPaint(bool isFirstPaint,
bool textPainted,
bool imagePainted) {
if (isFirstPaint)
markFirstPaint();
if (textPainted)
markFirstTextPaint();
if (imagePainted)
markFirstImagePaint();
m_fmpDetector->notifyPaint();
}
DEFINE_TRACE(PaintTiming) {
visitor->trace(m_fmpDetector);
Supplement<Document>::trace(visitor);
}
PaintTiming::PaintTiming(Document& document)
: Supplement<Document>(document),
m_fmpDetector(new FirstMeaningfulPaintDetector(this, document)) {}
LocalFrame* PaintTiming::frame() const {
return supplementable()->frame();
}
void PaintTiming::notifyPaintTimingChanged() {
if (supplementable()->loader())
supplementable()->loader()->didChangePerformanceTiming();
}
void PaintTiming::setFirstPaint(double stamp) {
if (m_firstPaint != 0.0)
return;
m_firstPaint = stamp;
Performance* performance = getPerformanceInstance(frame());
if (performance)
performance->addFirstPaintTiming(m_firstPaint);
TRACE_EVENT_INSTANT1("blink.user_timing,rail", "firstPaint",
TRACE_EVENT_SCOPE_PROCESS, "frame", frame());
}
void PaintTiming::setFirstContentfulPaint(double stamp) {
if (m_firstContentfulPaint != 0.0)
return;
setFirstPaint(stamp);
m_firstContentfulPaint = stamp;
Performance* performance = getPerformanceInstance(frame());
if (performance)
performance->addFirstContentfulPaintTiming(m_firstContentfulPaint);
TRACE_EVENT_INSTANT1("blink.user_timing,rail", "firstContentfulPaint",
TRACE_EVENT_SCOPE_PROCESS, "frame", frame());
}
} // namespace blink