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

[FCP++] Stop recording new entries upon user input

Currently, FCP++ records new image and text for the whole life cycle of a tab.
However, FCP++ should only be interested in the elements attached before user
input. This is because user input can cause changes to the DOM. That's why
FCP++ need to stop recording new elements upon user input.

The CL causes behavior changes to FCP++.
* When users give input other than kMouseMove, kMouseEnter, kMouseLeave and
pinches, FCP++ deactivate its detectors. Note that the user inputs include
both those handled by mainthread (click, touch, etc) and compositor thread
(off-mainthread scrolling, gesture, etc). But these exclude the input events
that have been filtered from the browser side.
* When deactivated, the text and image detector stops to record new entries
and node removal. But they still observe the loading status. In other words,
if an image is recorded before deactivation, and finish loading after
deactivation, the image paint detector can still observe the loading being
finished.

Bug: 915804
Change-Id: I49d3d9aa0b2e73a4775147ff1a2bb4b065c5b9b2
Reviewed-on: https://chromium-review.googlesource.com/c/1380947Reviewed-by: default avatarSteve Kobes <skobes@chromium.org>
Reviewed-by: default avatarBryan McQuade <bmcquade@chromium.org>
Commit-Queue: Liquan (Max) Gu <maxlg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#619960}
parent 9adcf511
...@@ -139,6 +139,7 @@ ...@@ -139,6 +139,7 @@
#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h" #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
#include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h" #include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h" #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
#include "third_party/blink/renderer/core/timing/dom_window_performance.h" #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
#include "third_party/blink/renderer/core/timing/window_performance.h" #include "third_party/blink/renderer/core/timing/window_performance.h"
...@@ -1739,6 +1740,22 @@ WebInputEventResult WebViewImpl::HandleInputEvent( ...@@ -1739,6 +1740,22 @@ WebInputEventResult WebViewImpl::HandleInputEvent(
} }
} }
if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled()) {
// Notify the focus frame of the input. Note that the other frames are not
// notified as input is only handled by the focused frame.
Frame* frame = FocusedCoreFrame();
if (frame && frame->IsLocalFrame()) {
LocalFrame* local_frame = ToLocalFrame(frame);
if (local_frame && local_frame->View() &&
local_frame->View()
->GetPaintTimingDetector()
.NeedToNotifyInputOrScroll()) {
local_frame->View()->GetPaintTimingDetector().NotifyInputEvent(
input_event.GetType());
}
}
}
// Skip the pointerrawmove for mouse capture case. // Skip the pointerrawmove for mouse capture case.
if (mouse_capture_node_ && if (mouse_capture_node_ &&
input_event.GetType() == WebInputEvent::kPointerRawMove) input_event.GetType() == WebInputEvent::kPointerRawMove)
......
...@@ -96,7 +96,7 @@ bool IsLoaded(const LayoutObject& object) { ...@@ -96,7 +96,7 @@ bool IsLoaded(const LayoutObject& object) {
} // namespace } // namespace
// Set a big enough limit for the number of nodes to ensure memory usage is // Set a big enough limit for the number of nodes to ensure memory usage is
// capped. Exceeding such limit will deactivate the algorithm. // capped. Exceeding such limit will make the detactor stops recording entries.
constexpr size_t kImageNodeNumberLimit = 5000; constexpr size_t kImageNodeNumberLimit = 5000;
static bool LargeImageFirst(const base::WeakPtr<ImageRecord>& a, static bool LargeImageFirst(const base::WeakPtr<ImageRecord>& a,
...@@ -205,6 +205,8 @@ void ImagePaintTimingDetector::OnPrePaintFinished() { ...@@ -205,6 +205,8 @@ void ImagePaintTimingDetector::OnPrePaintFinished() {
} }
void ImagePaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) { void ImagePaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) {
if (!is_recording_)
return;
if (id_record_map_.Contains(node_id)) { if (id_record_map_.Contains(node_id)) {
// We assume that the removed node's id wouldn't be recycled, so we don't // We assume that the removed node's id wouldn't be recycled, so we don't
// bother to remove these records from size_ordered_set_ or // bother to remove these records from size_ordered_set_ or
...@@ -352,8 +354,13 @@ void ImagePaintTimingDetector::RecordImage(const LayoutObject& object, ...@@ -352,8 +354,13 @@ void ImagePaintTimingDetector::RecordImage(const LayoutObject& object,
record->first_size = rect_size; record->first_size = rect_size;
size_ordered_set_.insert(record->AsWeakPtr()); size_ordered_set_.insert(record->AsWeakPtr());
id_record_map_.insert(node_id, std::move(record)); id_record_map_.insert(node_id, std::move(record));
if (id_record_map_.size() + size_zero_ids_.size() > kImageNodeNumberLimit) if (id_record_map_.size() + size_zero_ids_.size() > kImageNodeNumberLimit) {
Deactivate(); TRACE_EVENT_INSTANT2("loading", "ImagePaintTimingDetector::OverNodeLimit",
TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
id_record_map_.size(), "size_zero_node_count",
size_zero_ids_.size());
StopRecordEntries();
}
} }
if (id_record_map_.Contains(node_id) && !id_record_map_.at(node_id)->loaded && if (id_record_map_.Contains(node_id) && !id_record_map_.at(node_id)->loaded &&
...@@ -374,11 +381,7 @@ void ImagePaintTimingDetector::RecordImage(const LayoutObject& object, ...@@ -374,11 +381,7 @@ void ImagePaintTimingDetector::RecordImage(const LayoutObject& object,
} }
} }
void ImagePaintTimingDetector::Deactivate() { void ImagePaintTimingDetector::StopRecordEntries() {
TRACE_EVENT_INSTANT2("loading", "ImagePaintTimingDetector::OverNodeLimit",
TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
id_record_map_.size(), "size_zero_node_count",
size_zero_ids_.size());
is_recording_ = false; is_recording_ = false;
} }
......
...@@ -69,6 +69,13 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -69,6 +69,13 @@ class CORE_EXPORT ImagePaintTimingDetector final
void NotifyNodeRemoved(DOMNodeId); void NotifyNodeRemoved(DOMNodeId);
base::TimeTicks LargestImagePaint() const { return largest_image_paint_; } base::TimeTicks LargestImagePaint() const { return largest_image_paint_; }
base::TimeTicks LastImagePaint() const { return last_image_paint_; } base::TimeTicks LastImagePaint() const { return last_image_paint_; }
// After the method being called, the detector stops to record new entries and
// node removal. But it still observe the loading status. In other words, if
// an image is recorded before stopping recording, and finish loading after
// stopping recording, the detector can still observe the loading being
// finished.
void StopRecordEntries();
bool IsRecording() const { return is_recording_; }
void Trace(blink::Visitor*); void Trace(blink::Visitor*);
private: private:
...@@ -88,8 +95,6 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -88,8 +95,6 @@ class CORE_EXPORT ImagePaintTimingDetector final
void RegisterNotifySwapTime(); void RegisterNotifySwapTime();
void OnLargestImagePaintDetected(const ImageRecord&); void OnLargestImagePaintDetected(const ImageRecord&);
void OnLastImagePaintDetected(const ImageRecord&); void OnLastImagePaintDetected(const ImageRecord&);
void Deactivate();
void Analyze(); void Analyze();
base::RepeatingCallback<void(WebLayerTreeView::ReportTimeCallback)> base::RepeatingCallback<void(WebLayerTreeView::ReportTimeCallback)>
...@@ -122,6 +127,8 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -122,6 +127,8 @@ 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;
// Used to control if we record new image entries and image removal, but has
// no effect on recording the loading status.
bool is_recording_ = true; bool is_recording_ = true;
base::TimeTicks largest_image_paint_; base::TimeTicks largest_image_paint_;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h" #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/url_test_helpers.h" #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
...@@ -122,6 +123,8 @@ class ImagePaintTimingDetectorTest ...@@ -122,6 +123,8 @@ class ImagePaintTimingDetectorTest
WebString::FromUTF8(file_name)); WebString::FromUTF8(file_name));
} }
void SimulateScroll() { GetPaintTimingDetector().NotifyScroll(kUserScroll); }
private: private:
void FakeNotifySwapTime(WebLayerTreeView::ReportTimeCallback callback) { void FakeNotifySwapTime(WebLayerTreeView::ReportTimeCallback callback) {
callback_queue_.push(std::move(callback)); callback_queue_.push(std::move(callback));
...@@ -692,4 +695,16 @@ TEST_F(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreGradient) { ...@@ -692,4 +695,16 @@ TEST_F(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreGradient) {
EXPECT_EQ(CountRecords(), 0u); EXPECT_EQ(CountRecords(), 0u);
} }
TEST_F(ImagePaintTimingDetectorTest, DeactivateAfterUserInput) {
SetBodyInnerHTML(R"HTML(
<div id="parent">
<img id="target"></img>
</div>
)HTML");
SimulateScroll();
SetImageAndPaint("target", 5, 5);
UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
EXPECT_EQ(CountRecords(), 0u);
}
} // namespace blink } // namespace blink
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// 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_timing_detector.h" #include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
#include "third_party/blink/public/platform/web_input_event.h"
#include "third_party/blink/renderer/core/dom/document.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"
...@@ -53,6 +54,28 @@ void PaintTimingDetector::NotifyNodeRemoved(const LayoutObject& object) { ...@@ -53,6 +54,28 @@ void PaintTimingDetector::NotifyNodeRemoved(const LayoutObject& object) {
DOMNodeIds::IdForNode(object.GetNode())); DOMNodeIds::IdForNode(object.GetNode()));
} }
void PaintTimingDetector::NotifyInputEvent(WebInputEvent::Type type) {
if (type == WebInputEvent::kMouseMove || type == WebInputEvent::kMouseEnter ||
type == WebInputEvent::kMouseLeave ||
WebInputEvent::IsPinchGestureEventType(type)) {
return;
}
text_paint_timing_detector_->StopRecordEntries();
image_paint_timing_detector_->StopRecordEntries();
}
void PaintTimingDetector::NotifyScroll(ScrollType scroll_type) {
if (scroll_type != kUserScroll && scroll_type != kCompositorScroll)
return;
text_paint_timing_detector_->StopRecordEntries();
image_paint_timing_detector_->StopRecordEntries();
}
bool PaintTimingDetector::NeedToNotifyInputOrScroll() {
return text_paint_timing_detector_->IsRecording() ||
image_paint_timing_detector_->IsRecording();
}
void PaintTimingDetector::DidChangePerformanceTiming() { void PaintTimingDetector::DidChangePerformanceTiming() {
Document* document = frame_view_->GetFrame().GetDocument(); Document* document = frame_view_->GetFrame().GetDocument();
if (!document) if (!document)
......
...@@ -5,8 +5,10 @@ ...@@ -5,8 +5,10 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_
#include "third_party/blink/public/platform/web_input_event.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/heap/member.h" #include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
namespace blink { namespace blink {
...@@ -32,6 +34,9 @@ class CORE_EXPORT PaintTimingDetector ...@@ -32,6 +34,9 @@ class CORE_EXPORT PaintTimingDetector
const PaintLayer& painting_layer); const PaintLayer& painting_layer);
void NotifyNodeRemoved(const LayoutObject& object); void NotifyNodeRemoved(const LayoutObject& object);
void NotifyPrePaintFinished(); void NotifyPrePaintFinished();
void NotifyInputEvent(WebInputEvent::Type);
bool NeedToNotifyInputOrScroll();
void NotifyScroll(ScrollType scroll_type);
void DidChangePerformanceTiming(); void DidChangePerformanceTiming();
uint64_t CalculateVisualSize(const LayoutRect& invalidated_rect, uint64_t CalculateVisualSize(const LayoutRect& invalidated_rect,
const PaintLayer& painting_layer) const; const PaintLayer& painting_layer) const;
......
...@@ -117,6 +117,8 @@ void TextPaintTimingDetector::OnPrePaintFinished() { ...@@ -117,6 +117,8 @@ void TextPaintTimingDetector::OnPrePaintFinished() {
} }
void TextPaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) { void TextPaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) {
if (!is_recording_)
return;
for (TextRecord& record : texts_to_record_swap_time_) { for (TextRecord& record : texts_to_record_swap_time_) {
if (record.node_id == node_id) if (record.node_id == node_id)
record.node_id = kInvalidDOMNodeId; record.node_id = kInvalidDOMNodeId;
...@@ -215,15 +217,15 @@ void TextPaintTimingDetector::RecordText(const LayoutObject& object, ...@@ -215,15 +217,15 @@ void TextPaintTimingDetector::RecordText(const LayoutObject& object,
if (recorded_text_node_ids_.size() + size_zero_node_ids_.size() + if (recorded_text_node_ids_.size() + size_zero_node_ids_.size() +
texts_to_record_swap_time_.size() >= texts_to_record_swap_time_.size() >=
kTextNodeNumberLimit) { kTextNodeNumberLimit) {
Deactivate(); TRACE_EVENT_INSTANT2("loading", "TextPaintTimingDetector::OverNodeLimit",
TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
recorded_text_node_ids_.size(), "size_zero_node_count",
size_zero_node_ids_.size());
StopRecordEntries();
} }
} }
void TextPaintTimingDetector::Deactivate() { void TextPaintTimingDetector::StopRecordEntries() {
TRACE_EVENT_INSTANT2("loading", "TextPaintTimingDetector::OverNodeLimit",
TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
recorded_text_node_ids_.size(), "size_zero_node_count",
size_zero_node_ids_.size());
timer_.Stop(); timer_.Stop();
is_recording_ = false; is_recording_ = false;
} }
......
...@@ -63,6 +63,8 @@ class CORE_EXPORT TextPaintTimingDetector final ...@@ -63,6 +63,8 @@ class CORE_EXPORT TextPaintTimingDetector final
void Dispose() { timer_.Stop(); } void Dispose() { timer_.Stop(); }
base::TimeTicks LargestTextPaint() const { return largest_text_paint_; } base::TimeTicks LargestTextPaint() const { return largest_text_paint_; }
base::TimeTicks LastTextPaint() const { return last_text_paint_; } base::TimeTicks LastTextPaint() const { return last_text_paint_; }
void StopRecordEntries();
bool IsRecording() const { return is_recording_; }
void Trace(blink::Visitor*); void Trace(blink::Visitor*);
private: private:
...@@ -77,7 +79,6 @@ class CORE_EXPORT TextPaintTimingDetector final ...@@ -77,7 +79,6 @@ class CORE_EXPORT TextPaintTimingDetector final
void RegisterNotifySwapTime(ReportTimeCallback callback); void RegisterNotifySwapTime(ReportTimeCallback callback);
void OnLargestTextDetected(const TextRecord&); void OnLargestTextDetected(const TextRecord&);
void OnLastTextDetected(const TextRecord&); void OnLastTextDetected(const TextRecord&);
void Deactivate();
HashSet<DOMNodeId> recorded_text_node_ids_; HashSet<DOMNodeId> recorded_text_node_ids_;
HashSet<DOMNodeId> size_zero_node_ids_; HashSet<DOMNodeId> size_zero_node_ids_;
......
...@@ -34,7 +34,10 @@ ...@@ -34,7 +34,10 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "cc/layers/picture_layer.h" #include "cc/layers/picture_layer.h"
#include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#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/paint/paint_timing_detector.h"
#include "third_party/blink/renderer/core/scroll/programmatic_scroll_animator.h" #include "third_party/blink/renderer/core/scroll/programmatic_scroll_animator.h"
#include "third_party/blink/renderer/core/scroll/scroll_animator_base.h" #include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h" #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
...@@ -323,6 +326,18 @@ void ScrollableArea::ScrollOffsetChanged(const ScrollOffset& offset, ...@@ -323,6 +326,18 @@ void ScrollableArea::ScrollOffsetChanged(const ScrollOffset& offset,
GetScrollOffset() - old_offset, scroll_type); GetScrollOffset() - old_offset, scroll_type);
} }
if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled()) {
if (GetScrollOffset() != old_offset && GetLayoutBox() &&
GetLayoutBox()->GetFrameView() &&
GetLayoutBox()
->GetFrameView()
->GetPaintTimingDetector()
.NeedToNotifyInputOrScroll()) {
GetLayoutBox()->GetFrameView()->GetPaintTimingDetector().NotifyScroll(
scroll_type);
}
}
GetScrollAnimator().SetCurrentOffset(offset); GetScrollAnimator().SetCurrentOffset(offset);
} }
......
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