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 { ...@@ -92,6 +92,8 @@ class WebPerformance {
BLINK_EXPORT double FirstContentfulPaint() const; BLINK_EXPORT double FirstContentfulPaint() const;
BLINK_EXPORT double FirstMeaningfulPaint() const; BLINK_EXPORT double FirstMeaningfulPaint() const;
BLINK_EXPORT double FirstMeaningfulPaintCandidate() const; BLINK_EXPORT double FirstMeaningfulPaintCandidate() const;
BLINK_EXPORT double LargestImagePaint() const;
BLINK_EXPORT double LastImagePaint() const;
BLINK_EXPORT double PageInteractive() const; BLINK_EXPORT double PageInteractive() const;
BLINK_EXPORT double PageInteractiveDetection() const; BLINK_EXPORT double PageInteractiveDetection() const;
BLINK_EXPORT double FirstInputInvalidatingInteractive() const; BLINK_EXPORT double FirstInputInvalidatingInteractive() const;
......
...@@ -175,6 +175,14 @@ double WebPerformance::FirstMeaningfulPaintCandidate() const { ...@@ -175,6 +175,14 @@ double WebPerformance::FirstMeaningfulPaintCandidate() const {
private_->timing()->FirstMeaningfulPaintCandidate()); 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 { double WebPerformance::PageInteractive() const {
return MillisecondsToSeconds(private_->timing()->PageInteractive()); return MillisecondsToSeconds(private_->timing()->PageInteractive());
} }
......
...@@ -679,7 +679,7 @@ class CORE_EXPORT LocalFrameView final ...@@ -679,7 +679,7 @@ class CORE_EXPORT LocalFrameView final
void ScrollAndFocusFragmentAnchor(); void ScrollAndFocusFragmentAnchor();
JankTracker& GetJankTracker() { return *jank_tracker_; } JankTracker& GetJankTracker() { return *jank_tracker_; }
PaintTracker& GetPaintTracker() { return *paint_tracker_; } PaintTracker& GetPaintTracker() const { return *paint_tracker_; }
protected: protected:
void NotifyFrameRectsChangedIfNeeded(); void NotifyFrameRectsChangedIfNeeded();
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "third_party/blink/renderer/core/page/chrome_client.h" #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/page/page.h"
#include "third_party/blink/renderer/core/paint/paint_layer.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/geometry/layout_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
...@@ -84,30 +85,49 @@ IntRect ImagePaintTimingDetector::CalculateTransformedRect( ...@@ -84,30 +85,49 @@ IntRect ImagePaintTimingDetector::CalculateTransformedRect(
return invalidated_rect_in_viewport; 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() { void ImagePaintTimingDetector::Analyze() {
ImageRecord* largest_image_record = FindLargestPaintCandidate(); ImageRecord* largest_image_record = FindLargestPaintCandidate();
// In cases where largest/last image is still pending for timing, we discard // In cases where largest/last image is still pending for timing, we discard
// the result and wait for the next analysis. // the result and wait for the next analysis.
if (largest_image_record && if (largest_image_record &&
!largest_image_record->first_paint_time_after_loaded.is_null()) { !largest_image_record->first_paint_time_after_loaded.is_null()) {
std::unique_ptr<TracedValue> value = TracedValue::Create(); OnLargestImagePaintDetected(*largest_image_record);
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));
} }
ImageRecord* last_image_record = FindLastPaintCandidate(); ImageRecord* last_image_record = FindLastPaintCandidate();
if (last_image_record && if (last_image_record &&
!last_image_record->first_paint_time_after_loaded.is_null()) { !last_image_record->first_paint_time_after_loaded.is_null()) {
std::unique_ptr<TracedValue> value = TracedValue::Create(); OnLastImagePaintDetected(*last_image_record);
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));
} }
} }
......
...@@ -69,6 +69,8 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -69,6 +69,8 @@ class CORE_EXPORT ImagePaintTimingDetector final
const PaintLayer& painting_layer); const PaintLayer& painting_layer);
void OnPrePaintFinished(); void OnPrePaintFinished();
void NotifyNodeRemoved(DOMNodeId); void NotifyNodeRemoved(DOMNodeId);
base::TimeTicks LargestImagePaint() { return largest_image_paint_; }
base::TimeTicks LastImagePaint() { return last_image_paint_; }
void Trace(blink::Visitor*); void Trace(blink::Visitor*);
private: private:
...@@ -84,7 +86,8 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -84,7 +86,8 @@ class CORE_EXPORT ImagePaintTimingDetector final
WebLayerTreeView::SwapResult, WebLayerTreeView::SwapResult,
base::TimeTicks); base::TimeTicks);
void RegisterNotifySwapTime(); void RegisterNotifySwapTime();
void InvokeCallback(); void OnLargestImagePaintDetected(const ImageRecord&);
void OnLastImagePaintDetected(const ImageRecord&);
bool IsJustLoaded(const LayoutImage*, const ImageRecord&) const; bool IsJustLoaded(const LayoutImage*, const ImageRecord&) const;
void Analyze(); void Analyze();
...@@ -121,6 +124,9 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -121,6 +124,9 @@ class CORE_EXPORT ImagePaintTimingDetector final
unsigned frame_index_ = 1; unsigned frame_index_ = 1;
unsigned last_frame_index_queued_for_timing_ = 0; unsigned last_frame_index_queued_for_timing_ = 0;
base::TimeTicks largest_image_paint_;
base::TimeTicks last_image_paint_;
Member<LocalFrameView> frame_view_; Member<LocalFrameView> frame_view_;
}; };
} // namespace blink } // namespace blink
......
...@@ -46,6 +46,14 @@ class ImagePaintTimingDetectorTest : public PageTestBase, ...@@ -46,6 +46,14 @@ class ImagePaintTimingDetectorTest : public PageTestBase,
.FindLastPaintCandidate(); .FindLastPaintCandidate();
} }
TimeTicks LargestPaintStoredResult() {
return GetPaintTracker().GetImagePaintTimingDetector().largest_image_paint_;
}
TimeTicks LastPaintStoredResult() {
return GetPaintTracker().GetImagePaintTimingDetector().last_image_paint_;
}
void UpdateAllLifecyclePhasesAndInvokeCallbackIfAny() { void UpdateAllLifecyclePhasesAndInvokeCallbackIfAny() {
GetFrameView().UpdateAllLifecyclePhases(); GetFrameView().UpdateAllLifecyclePhases();
if (callback_queue_.size() > 0) { if (callback_queue_.size() > 0) {
...@@ -214,6 +222,30 @@ TEST_F(ImagePaintTimingDetectorTest, ...@@ -214,6 +222,30 @@ TEST_F(ImagePaintTimingDetectorTest,
EXPECT_FALSE(record->first_paint_time_after_loaded.is_null()); 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) { TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_NoImage) {
SetBodyInnerHTML(R"HTML( SetBodyInnerHTML(R"HTML(
<div></div> <div></div>
...@@ -346,4 +378,30 @@ TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_OneSwapPromiseForOneFrame) { ...@@ -346,4 +378,30 @@ TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_OneSwapPromiseForOneFrame) {
EXPECT_FALSE(record->first_paint_time_after_loaded.is_null()); 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 } // namespace blink
...@@ -2,9 +2,11 @@ ...@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "third_party/blink/renderer/core/paint/paint_tracker.h" #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.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.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/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/image_paint_timing_detector.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/text_paint_timing_detector.h" #include "third_party/blink/renderer/core/paint/text_paint_timing_detector.h"
...@@ -45,6 +47,14 @@ void PaintTracker::NotifyNodeRemoved(const LayoutObject& object) { ...@@ -45,6 +47,14 @@ void PaintTracker::NotifyNodeRemoved(const LayoutObject& object) {
DOMNodeIds::IdForNode(object.GetNode())); 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() { void PaintTracker::Dispose() {
text_paint_timing_detector_->Dispose(); text_paint_timing_detector_->Dispose();
} }
......
...@@ -30,6 +30,7 @@ class CORE_EXPORT PaintTracker : public GarbageCollected<PaintTracker> { ...@@ -30,6 +30,7 @@ class CORE_EXPORT PaintTracker : public GarbageCollected<PaintTracker> {
const PaintLayer& painting_layer); const PaintLayer& painting_layer);
void NotifyNodeRemoved(const LayoutObject& object); void NotifyNodeRemoved(const LayoutObject& object);
void NotifyPrePaintFinished(); void NotifyPrePaintFinished();
void DidChangePerformanceTiming();
void Dispose(); void Dispose();
TextPaintTimingDetector& GetTextPaintTimingDetector() { TextPaintTimingDetector& GetTextPaintTimingDetector() {
......
...@@ -40,7 +40,9 @@ ...@@ -40,7 +40,9 @@
#include "third_party/blink/renderer/core/loader/document_loader.h" #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/frame_loader.h"
#include "third_party/blink/renderer/core/loader/interactive_detector.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_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/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_load_timing.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
...@@ -364,6 +366,24 @@ unsigned long long PerformanceTiming::FirstMeaningfulPaintCandidate() const { ...@@ -364,6 +366,24 @@ unsigned long long PerformanceTiming::FirstMeaningfulPaintCandidate() const {
timing->FirstMeaningfulPaintCandidate()); 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 { unsigned long long PerformanceTiming::PageInteractive() const {
InteractiveDetector* interactive_detector = GetInteractiveDetector(); InteractiveDetector* interactive_detector = GetInteractiveDetector();
if (!interactive_detector) if (!interactive_detector)
...@@ -548,6 +568,17 @@ InteractiveDetector* PerformanceTiming::GetInteractiveDetector() const { ...@@ -548,6 +568,17 @@ InteractiveDetector* PerformanceTiming::GetInteractiveDetector() const {
return InteractiveDetector::From(*document); 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( ScriptValue PerformanceTiming::toJSONForBinding(
ScriptState* script_state) const { ScriptState* script_state) const {
V8ObjectBuilder result(script_state); V8ObjectBuilder result(script_state);
......
...@@ -47,6 +47,7 @@ class DocumentTiming; ...@@ -47,6 +47,7 @@ class DocumentTiming;
class InteractiveDetector; class InteractiveDetector;
class LocalFrame; class LocalFrame;
class PaintTiming; class PaintTiming;
class PaintTracker;
class ResourceLoadTiming; class ResourceLoadTiming;
class ScriptState; class ScriptState;
class ScriptValue; class ScriptValue;
...@@ -108,6 +109,12 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable, ...@@ -108,6 +109,12 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable,
// TODO(crbug.com/848639): This function is exposed as an experiment, and if // TODO(crbug.com/848639): This function is exposed as an experiment, and if
// not useful, this function can be removed. // not useful, this function can be removed.
unsigned long long FirstMeaningfulPaintCandidate() const; 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 // The first time the page is considered 'interactive'. This is determined
// using heuristics based on main thread and network activity. // using heuristics based on main thread and network activity.
unsigned long long PageInteractive() const; unsigned long long PageInteractive() const;
...@@ -151,6 +158,7 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable, ...@@ -151,6 +158,7 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable,
const CSSTiming* CssTiming() const; const CSSTiming* CssTiming() const;
const DocumentParserTiming* GetDocumentParserTiming() const; const DocumentParserTiming* GetDocumentParserTiming() const;
const PaintTiming* GetPaintTiming() const; const PaintTiming* GetPaintTiming() const;
PaintTracker* GetPaintTracker() const;
DocumentLoader* GetDocumentLoader() const; DocumentLoader* GetDocumentLoader() const;
DocumentLoadTiming* GetDocumentLoadTiming() const; DocumentLoadTiming* GetDocumentLoadTiming() const;
ResourceLoadTiming* GetResourceLoadTiming() 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