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 @@
#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/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/timing/dom_window_performance.h"
#include "third_party/blink/renderer/core/timing/window_performance.h"
......@@ -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.
if (mouse_capture_node_ &&
input_event.GetType() == WebInputEvent::kPointerRawMove)
......
......@@ -96,7 +96,7 @@ bool IsLoaded(const LayoutObject& object) {
} // namespace
// 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;
static bool LargeImageFirst(const base::WeakPtr<ImageRecord>& a,
......@@ -205,6 +205,8 @@ void ImagePaintTimingDetector::OnPrePaintFinished() {
}
void ImagePaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) {
if (!is_recording_)
return;
if (id_record_map_.Contains(node_id)) {
// 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
......@@ -352,8 +354,13 @@ void ImagePaintTimingDetector::RecordImage(const LayoutObject& object,
record->first_size = rect_size;
size_ordered_set_.insert(record->AsWeakPtr());
id_record_map_.insert(node_id, std::move(record));
if (id_record_map_.size() + size_zero_ids_.size() > kImageNodeNumberLimit)
Deactivate();
if (id_record_map_.size() + size_zero_ids_.size() > kImageNodeNumberLimit) {
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 &&
......@@ -374,11 +381,7 @@ void ImagePaintTimingDetector::RecordImage(const LayoutObject& object,
}
}
void ImagePaintTimingDetector::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());
void ImagePaintTimingDetector::StopRecordEntries() {
is_recording_ = false;
}
......
......@@ -69,6 +69,13 @@ class CORE_EXPORT ImagePaintTimingDetector final
void NotifyNodeRemoved(DOMNodeId);
base::TimeTicks LargestImagePaint() const { return largest_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*);
private:
......@@ -88,8 +95,6 @@ class CORE_EXPORT ImagePaintTimingDetector final
void RegisterNotifySwapTime();
void OnLargestImagePaintDetected(const ImageRecord&);
void OnLastImagePaintDetected(const ImageRecord&);
void Deactivate();
void Analyze();
base::RepeatingCallback<void(WebLayerTreeView::ReportTimeCallback)>
......@@ -122,6 +127,8 @@ class CORE_EXPORT ImagePaintTimingDetector final
unsigned frame_index_ = 1;
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;
base::TimeTicks largest_image_paint_;
......
......@@ -12,6 +12,7 @@
#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/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/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
......@@ -122,6 +123,8 @@ class ImagePaintTimingDetectorTest
WebString::FromUTF8(file_name));
}
void SimulateScroll() { GetPaintTimingDetector().NotifyScroll(kUserScroll); }
private:
void FakeNotifySwapTime(WebLayerTreeView::ReportTimeCallback callback) {
callback_queue_.push(std::move(callback));
......@@ -692,4 +695,16 @@ TEST_F(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreGradient) {
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
......@@ -2,6 +2,7 @@
// 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_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/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
......@@ -53,6 +54,28 @@ void PaintTimingDetector::NotifyNodeRemoved(const LayoutObject& object) {
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() {
Document* document = frame_view_->GetFrame().GetDocument();
if (!document)
......
......@@ -5,8 +5,10 @@
#ifndef 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/platform/heap/member.h"
#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
namespace blink {
......@@ -32,6 +34,9 @@ class CORE_EXPORT PaintTimingDetector
const PaintLayer& painting_layer);
void NotifyNodeRemoved(const LayoutObject& object);
void NotifyPrePaintFinished();
void NotifyInputEvent(WebInputEvent::Type);
bool NeedToNotifyInputOrScroll();
void NotifyScroll(ScrollType scroll_type);
void DidChangePerformanceTiming();
uint64_t CalculateVisualSize(const LayoutRect& invalidated_rect,
const PaintLayer& painting_layer) const;
......
......@@ -117,6 +117,8 @@ void TextPaintTimingDetector::OnPrePaintFinished() {
}
void TextPaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) {
if (!is_recording_)
return;
for (TextRecord& record : texts_to_record_swap_time_) {
if (record.node_id == node_id)
record.node_id = kInvalidDOMNodeId;
......@@ -215,15 +217,15 @@ void TextPaintTimingDetector::RecordText(const LayoutObject& object,
if (recorded_text_node_ids_.size() + size_zero_node_ids_.size() +
texts_to_record_swap_time_.size() >=
kTextNodeNumberLimit) {
Deactivate();
}
}
void TextPaintTimingDetector::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::StopRecordEntries() {
timer_.Stop();
is_recording_ = false;
}
......
......@@ -63,6 +63,8 @@ class CORE_EXPORT TextPaintTimingDetector final
void Dispose() { timer_.Stop(); }
base::TimeTicks LargestTextPaint() const { return largest_text_paint_; }
base::TimeTicks LastTextPaint() const { return last_text_paint_; }
void StopRecordEntries();
bool IsRecording() const { return is_recording_; }
void Trace(blink::Visitor*);
private:
......@@ -77,7 +79,6 @@ class CORE_EXPORT TextPaintTimingDetector final
void RegisterNotifySwapTime(ReportTimeCallback callback);
void OnLargestTextDetected(const TextRecord&);
void OnLastTextDetected(const TextRecord&);
void Deactivate();
HashSet<DOMNodeId> recorded_text_node_ids_;
HashSet<DOMNodeId> size_zero_node_ids_;
......
......@@ -34,7 +34,10 @@
#include "build/build_config.h"
#include "cc/layers/picture_layer.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/paint/paint_timing_detector.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/scrollbar_theme.h"
......@@ -323,6 +326,18 @@ void ScrollableArea::ScrollOffsetChanged(const ScrollOffset& offset,
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);
}
......
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