Commit 8d628983 authored by Liquan(Max) Gu's avatar Liquan(Max) Gu Committed by Commit Bot

[FCP++] Allow WebPerformance to report Largest/LastImagePaint

In order to report Largest/LastImagePaint to UKM, this CL is to plumb
Largest/LastImagePaint to WebPerformance, but a follow-up CL has to be
done to pick up the metric results from the browser side.

Before this CL, the metrics report the result by dumping a trace event.
This CL adds a new way of reporting. When the metrics fire new results.
The results will be stored in the detector class. At the same time,
the detector will notify web performance of the new result so that
the web performance will pick up the metric results.

This CL also refactors the OnLargest/LastImagePaintDetected function in
ImagePaintTimingDetector for better readability.

Bug: 869924
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I9b198b8bacac52e5dae66ea3ab9c2bedc56cfb8f
Reviewed-on: https://chromium-review.googlesource.com/c/1280844Reviewed-by: default avatarSteve Kobes <skobes@chromium.org>
Reviewed-by: default avatarBryan McQuade <bmcquade@chromium.org>
Commit-Queue: Liquan (Max) Gǔ <maxlg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#599976}
parent 6ce83ed8
......@@ -92,6 +92,8 @@ class WebPerformance {
BLINK_EXPORT double FirstContentfulPaint() const;
BLINK_EXPORT double FirstMeaningfulPaint() const;
BLINK_EXPORT double FirstMeaningfulPaintCandidate() const;
BLINK_EXPORT double LargestImagePaint() const;
BLINK_EXPORT double LastImagePaint() const;
BLINK_EXPORT double PageInteractive() const;
BLINK_EXPORT double PageInteractiveDetection() const;
BLINK_EXPORT double FirstInputInvalidatingInteractive() const;
......
......@@ -175,6 +175,14 @@ double WebPerformance::FirstMeaningfulPaintCandidate() const {
private_->timing()->FirstMeaningfulPaintCandidate());
}
double WebPerformance::LargestImagePaint() const {
return MillisecondsToSeconds(private_->timing()->LargestImagePaint());
}
double WebPerformance::LastImagePaint() const {
return MillisecondsToSeconds(private_->timing()->LastImagePaint());
}
double WebPerformance::PageInteractive() const {
return MillisecondsToSeconds(private_->timing()->PageInteractive());
}
......
......@@ -679,7 +679,7 @@ class CORE_EXPORT LocalFrameView final
void ScrollAndFocusFragmentAnchor();
JankTracker& GetJankTracker() { return *jank_tracker_; }
PaintTracker& GetPaintTracker() { return *paint_tracker_; }
PaintTracker& GetPaintTracker() const { return *paint_tracker_; }
protected:
void NotifyFrameRectsChangedIfNeeded();
......
......@@ -11,6 +11,7 @@
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_tracker.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
......@@ -84,30 +85,49 @@ IntRect ImagePaintTimingDetector::CalculateTransformedRect(
return invalidated_rect_in_viewport;
}
void ImagePaintTimingDetector::OnLargestImagePaintDetected(
const ImageRecord& largest_image_record) {
if (largest_image_record.first_paint_time_after_loaded ==
largest_image_paint_)
return;
largest_image_paint_ = largest_image_record.first_paint_time_after_loaded;
std::unique_ptr<TracedValue> value = TracedValue::Create();
PopulateTraceValue(*value, largest_image_record,
++largest_image_candidate_index_max_);
TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(
"loading", "LargestImagePaint::Candidate", TRACE_EVENT_SCOPE_THREAD,
largest_image_record.first_paint_time_after_loaded, "data",
std::move(value));
frame_view_->GetPaintTracker().DidChangePerformanceTiming();
}
void ImagePaintTimingDetector::OnLastImagePaintDetected(
const ImageRecord& last_image_record) {
if (last_image_record.first_paint_time_after_loaded == last_image_paint_)
return;
last_image_paint_ = last_image_record.first_paint_time_after_loaded;
std::unique_ptr<TracedValue> value = TracedValue::Create();
PopulateTraceValue(*value, last_image_record,
++last_image_candidate_index_max_);
TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(
"loading", "LastImagePaint::Candidate", TRACE_EVENT_SCOPE_THREAD,
last_image_record.first_paint_time_after_loaded, "data",
std::move(value));
frame_view_->GetPaintTracker().DidChangePerformanceTiming();
}
void ImagePaintTimingDetector::Analyze() {
ImageRecord* largest_image_record = FindLargestPaintCandidate();
// In cases where largest/last image is still pending for timing, we discard
// the result and wait for the next analysis.
if (largest_image_record &&
!largest_image_record->first_paint_time_after_loaded.is_null()) {
std::unique_ptr<TracedValue> value = TracedValue::Create();
PopulateTraceValue(*value, *largest_image_record,
++largest_image_candidate_index_max_);
TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(
"loading", "LargestImagePaint::Candidate", TRACE_EVENT_SCOPE_THREAD,
largest_image_record->first_paint_time_after_loaded, "data",
std::move(value));
OnLargestImagePaintDetected(*largest_image_record);
}
ImageRecord* last_image_record = FindLastPaintCandidate();
if (last_image_record &&
!last_image_record->first_paint_time_after_loaded.is_null()) {
std::unique_ptr<TracedValue> value = TracedValue::Create();
PopulateTraceValue(*value, *last_image_record,
++last_image_candidate_index_max_);
TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(
"loading", "LastImagePaint::Candidate", TRACE_EVENT_SCOPE_THREAD,
last_image_record->first_paint_time_after_loaded, "data",
std::move(value));
OnLastImagePaintDetected(*last_image_record);
}
}
......
......@@ -69,6 +69,8 @@ class CORE_EXPORT ImagePaintTimingDetector final
const PaintLayer& painting_layer);
void OnPrePaintFinished();
void NotifyNodeRemoved(DOMNodeId);
base::TimeTicks LargestImagePaint() { return largest_image_paint_; }
base::TimeTicks LastImagePaint() { return last_image_paint_; }
void Trace(blink::Visitor*);
private:
......@@ -84,7 +86,8 @@ class CORE_EXPORT ImagePaintTimingDetector final
WebLayerTreeView::SwapResult,
base::TimeTicks);
void RegisterNotifySwapTime();
void InvokeCallback();
void OnLargestImagePaintDetected(const ImageRecord&);
void OnLastImagePaintDetected(const ImageRecord&);
bool IsJustLoaded(const LayoutImage*, const ImageRecord&) const;
void Analyze();
......@@ -121,6 +124,9 @@ class CORE_EXPORT ImagePaintTimingDetector final
unsigned frame_index_ = 1;
unsigned last_frame_index_queued_for_timing_ = 0;
base::TimeTicks largest_image_paint_;
base::TimeTicks last_image_paint_;
Member<LocalFrameView> frame_view_;
};
} // namespace blink
......
......@@ -46,6 +46,14 @@ class ImagePaintTimingDetectorTest : public PageTestBase,
.FindLastPaintCandidate();
}
TimeTicks LargestPaintStoredResult() {
return GetPaintTracker().GetImagePaintTimingDetector().largest_image_paint_;
}
TimeTicks LastPaintStoredResult() {
return GetPaintTracker().GetImagePaintTimingDetector().last_image_paint_;
}
void UpdateAllLifecyclePhasesAndInvokeCallbackIfAny() {
GetFrameView().UpdateAllLifecyclePhases();
if (callback_queue_.size() > 0) {
......@@ -214,6 +222,30 @@ TEST_F(ImagePaintTimingDetectorTest,
EXPECT_FALSE(record->first_paint_time_after_loaded.is_null());
}
TEST_F(ImagePaintTimingDetectorTest,
LargestImagePaint_UpdateResultWhenLargestChanged) {
TimeTicks time1 = CurrentTimeTicks();
SetBodyInnerHTML(R"HTML(
<div id="parent">
<img id="target1"></img>
<img id="target2"></img>
</div>
)HTML");
SetImageAndPaint("target1", 5, 5);
UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
TimeTicks time2 = CurrentTimeTicks();
TimeTicks result1 = LargestPaintStoredResult();
EXPECT_GE(result1, time1);
EXPECT_GE(time2, result1);
SetImageAndPaint("target2", 10, 10);
UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
TimeTicks time3 = CurrentTimeTicks();
TimeTicks result2 = LargestPaintStoredResult();
EXPECT_GE(result2, time2);
EXPECT_GE(time3, result2);
}
TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_NoImage) {
SetBodyInnerHTML(R"HTML(
<div></div>
......@@ -346,4 +378,30 @@ TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_OneSwapPromiseForOneFrame) {
EXPECT_FALSE(record->first_paint_time_after_loaded.is_null());
}
TEST_F(ImagePaintTimingDetectorTest,
LastImagePaint_UpdateResultWhenLastChanged) {
TimeTicks time1 = CurrentTimeTicks();
SetBodyInnerHTML(R"HTML(
<div id="parent">
<img id="target1"></img>
</div>
)HTML");
SetImageAndPaint("target1", 5, 5);
UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
TimeTicks time2 = CurrentTimeTicks();
TimeTicks result1 = LastPaintStoredResult();
EXPECT_GE(result1, time1);
EXPECT_GE(time2, result1);
Element* image = GetDocument().CreateRawElement(HTMLNames::imgTag);
image->setAttribute(HTMLNames::idAttr, "target2");
GetDocument().getElementById("parent")->appendChild(image);
SetImageAndPaint("target2", 2, 2);
UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
TimeTicks time3 = CurrentTimeTicks();
TimeTicks result2 = LastPaintStoredResult();
EXPECT_GE(result2, time2);
EXPECT_GE(time3, result2);
}
} // namespace blink
......@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/paint/paint_tracker.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/paint/image_paint_timing_detector.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/text_paint_timing_detector.h"
......@@ -45,6 +47,14 @@ void PaintTracker::NotifyNodeRemoved(const LayoutObject& object) {
DOMNodeIds::IdForNode(object.GetNode()));
}
void PaintTracker::DidChangePerformanceTiming() {
Document* document = frame_view_->GetFrame().GetDocument();
if (!document)
return;
DocumentLoader* loader = document->Loader();
loader->DidChangePerformanceTiming();
}
void PaintTracker::Dispose() {
text_paint_timing_detector_->Dispose();
}
......
......@@ -30,6 +30,7 @@ class CORE_EXPORT PaintTracker : public GarbageCollected<PaintTracker> {
const PaintLayer& painting_layer);
void NotifyNodeRemoved(const LayoutObject& object);
void NotifyPrePaintFinished();
void DidChangePerformanceTiming();
void Dispose();
TextPaintTimingDetector& GetTextPaintTimingDetector() {
......
......@@ -40,7 +40,9 @@
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/loader/frame_loader.h"
#include "third_party/blink/renderer/core/loader/interactive_detector.h"
#include "third_party/blink/renderer/core/paint/image_paint_timing_detector.h"
#include "third_party/blink/renderer/core/paint/paint_timing.h"
#include "third_party/blink/renderer/core/paint/paint_tracker.h"
#include "third_party/blink/renderer/core/timing/performance.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
......@@ -364,6 +366,24 @@ unsigned long long PerformanceTiming::FirstMeaningfulPaintCandidate() const {
timing->FirstMeaningfulPaintCandidate());
}
unsigned long long PerformanceTiming::LargestImagePaint() const {
PaintTracker* paint_tracker = GetPaintTracker();
if (!paint_tracker)
return 0;
return MonotonicTimeToIntegerMilliseconds(
paint_tracker->GetImagePaintTimingDetector().LargestImagePaint());
}
unsigned long long PerformanceTiming::LastImagePaint() const {
PaintTracker* paint_tracker = GetPaintTracker();
if (!paint_tracker)
return 0;
return MonotonicTimeToIntegerMilliseconds(
paint_tracker->GetImagePaintTimingDetector().LastImagePaint());
}
unsigned long long PerformanceTiming::PageInteractive() const {
InteractiveDetector* interactive_detector = GetInteractiveDetector();
if (!interactive_detector)
......@@ -548,6 +568,17 @@ InteractiveDetector* PerformanceTiming::GetInteractiveDetector() const {
return InteractiveDetector::From(*document);
}
PaintTracker* PerformanceTiming::GetPaintTracker() const {
if (!GetFrame())
return nullptr;
LocalFrameView* view = GetFrame()->View();
if (!view)
return nullptr;
return &view->GetPaintTracker();
}
ScriptValue PerformanceTiming::toJSONForBinding(
ScriptState* script_state) const {
V8ObjectBuilder result(script_state);
......
......@@ -47,6 +47,7 @@ class DocumentTiming;
class InteractiveDetector;
class LocalFrame;
class PaintTiming;
class PaintTracker;
class ResourceLoadTiming;
class ScriptState;
class ScriptValue;
......@@ -108,6 +109,12 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable,
// TODO(crbug.com/848639): This function is exposed as an experiment, and if
// not useful, this function can be removed.
unsigned long long FirstMeaningfulPaintCandidate() const;
// The time of the first paint after the largest image within viewport being
// fully loaded.
unsigned long long LargestImagePaint() const;
// The time of the first paint after the last image within viewport being
// fully loaded.
unsigned long long LastImagePaint() const;
// The first time the page is considered 'interactive'. This is determined
// using heuristics based on main thread and network activity.
unsigned long long PageInteractive() const;
......@@ -151,6 +158,7 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable,
const CSSTiming* CssTiming() const;
const DocumentParserTiming* GetDocumentParserTiming() const;
const PaintTiming* GetPaintTiming() const;
PaintTracker* GetPaintTracker() const;
DocumentLoader* GetDocumentLoader() const;
DocumentLoadTiming* GetDocumentLoadTiming() const;
ResourceLoadTiming* GetResourceLoadTiming() const;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment