Skip to content

Commit

Permalink
Record per-interaction latency UKM.
Browse files Browse the repository at this point in the history
This CL is for calculating per-interaction latency and record some UKM
based on the durations of input events associated with the user
interaction. The user interactions covered by this CL are keyboard, tap,
click and drag. And we only take into account input events that
typically cause UI updates. We do the calculation and record the UKM on
the renderer side. In the future, we will aggregate all interaction
latency data on the browser side and create a new page load level
responsiveness CWV.

For more details, please check the design doc:
https://docs.google.com/document/d/1SGoguf6UtIKuGD7818ESf3j1eRMYG1Fj7XnYo1U7XXg/edit?usp=sharing&resourcekey=0-23D5qwmjV_KFFIuk_bNXdw


Ukm privacy review: https://docs.google.com/document/d/1kfGW_pg9OAhrVs4r7kW5LPp2HE5twOxlwzZ5hb1d7RQ/edit?usp=sharing

Bug: 1215159
Change-Id: I820f90e14e9a7a807433592f778fdce4cbecfee9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2941046
Commit-Queue: Hongbo Song <hbsong@google.com>
Reviewed-by: Robert Kaplow <rkaplow@chromium.org>
Reviewed-by: Sadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: Nicolás Peña Moreno <npm@chromium.org>
Reviewed-by: Robert Flack <flackr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#901551}
  • Loading branch information
hbsong235 authored and Chromium LUCI CQ committed Jul 14, 2021
1 parent 8fdfaac commit 5a5584f
Show file tree
Hide file tree
Showing 13 changed files with 1,031 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,12 @@ DispatchEventResult EventDispatcher::Dispatch() {
}
std::unique_ptr<EventTiming> eventTiming;
LocalFrame* frame = node_->GetDocument().GetFrame();
if (frame && frame->DomWindow())
if (frame && frame->DomWindow()) {
eventTiming = EventTiming::Create(frame->DomWindow(), *event_);
// TODO(hbsong): Calculate First Input Delay for filtered events.
EventTiming::HandleInputDelay(frame->DomWindow(), *event_);
}

if (event_->type() == event_type_names::kChange && event_->isTrusted() &&
view_) {
view_->GetLayoutShiftTracker().NotifyChangeEvent();
Expand Down Expand Up @@ -226,8 +230,6 @@ DispatchEventResult EventDispatcher::Dispatch() {
}
DispatchEventPostProcess(activation_target,
pre_dispatch_event_handler_result);
if (eventTiming)
eventTiming->DidDispatchEvent(*event_, node_->GetDocument());

return EventTarget::GetDispatchEventResult(*event_);
}
Expand Down
32 changes: 21 additions & 11 deletions third_party/blink/renderer/core/input/mouse_event_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/svg/svg_document_extensions.h"
#include "third_party/blink/renderer/core/timing/event_timing.h"
#include "third_party/blink/renderer/platform/geometry/float_quad.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-blink.h"
#include "ui/display/screen_info.h"
Expand Down Expand Up @@ -296,8 +297,9 @@ WebInputEventResult MouseEventManager::DispatchMouseEvent(
mouse_event_type == event_type_names::kClick ||
mouse_event_type == event_type_names::kAuxclick);

if (target && target->ToNode() &&
(!check_for_listener || target->HasEventListeners(mouse_event_type))) {
WebInputEventResult input_event_result = WebInputEventResult::kNotHandled;

if (target && target->ToNode()) {
Node* target_node = target->ToNode();
int click_count = 0;
if (mouse_event_type == event_type_names::kMouseup ||
Expand All @@ -306,9 +308,9 @@ WebInputEventResult MouseEventManager::DispatchMouseEvent(
mouse_event_type == event_type_names::kAuxclick) {
click_count = click_count_;
}

DispatchEventResult dispatch_result;

std::unique_ptr<EventTiming> event_timing;
bool should_dispatch =
!check_for_listener || target->HasEventListeners(mouse_event_type);
if (RuntimeEnabledFeatures::ClickPointerEventEnabled() &&
(mouse_event_type == event_type_names::kContextmenu ||
mouse_event_type == event_type_names::kClick ||
Expand All @@ -324,7 +326,12 @@ WebInputEventResult MouseEventManager::DispatchMouseEvent(
mouse_event.FromTouch() ? MouseEvent::kFromTouch
: MouseEvent::kRealOrIndistinguishable,
mouse_event.menu_source_type);
dispatch_result = target->DispatchEvent(*event);
if (frame_ && frame_->DomWindow())
event_timing = EventTiming::Create(frame_->DomWindow(), *event);
if (should_dispatch) {
input_event_result = event_handling_util::ToWebInputEventResult(
target->DispatchEvent(*event));
}
} else {
MouseEventInit* initializer = MouseEventInit::Create();
SetMouseEventAttributes(initializer, target_node, mouse_event_type,
Expand All @@ -335,13 +342,16 @@ WebInputEventResult MouseEventManager::DispatchMouseEvent(
mouse_event.FromTouch() ? MouseEvent::kFromTouch
: MouseEvent::kRealOrIndistinguishable,
mouse_event.menu_source_type);

dispatch_result = target->DispatchEvent(*event);
if (frame_ && frame_->DomWindow())
event_timing = EventTiming::Create(frame_->DomWindow(), *event);
if (should_dispatch) {
input_event_result = event_handling_util::ToWebInputEventResult(
target->DispatchEvent(*event));
}
}

return event_handling_util::ToWebInputEventResult(dispatch_result);
}
return WebInputEventResult::kNotHandled;

return input_event_result;
}

WebInputEventResult MouseEventManager::SetMousePositionAndDispatchMouseEvent(
Expand Down
11 changes: 3 additions & 8 deletions third_party/blink/renderer/core/input/pointer_event_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,9 @@ WebInputEventResult PointerEventManager::DispatchPointerEvent(
bool should_filter = ShouldFilterEvent(pointer_event);
// We are about to dispatch this event. It has to be trusted at this point.
pointer_event->SetTrusted(true);

if (frame_ && frame_->DomWindow()) {
WindowPerformance* performance =
DOMWindowPerformance::performance(*(frame_->DomWindow()));
if (performance && EventTiming::IsEventTypeForEventTiming(*pointer_event)) {
performance->eventCounts()->Add(event_type);
}
}
std::unique_ptr<EventTiming> event_timing;
if (frame_ && frame_->DomWindow())
event_timing = EventTiming::Create(frame_->DomWindow(), *pointer_event);

if (should_filter &&
!HasPointerEventListener(frame_->GetEventHandlerRegistry()))
Expand Down
2 changes: 2 additions & 0 deletions third_party/blink/renderer/core/timing/build.gni
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ blink_core_sources_timing = [
"profiler_group.h",
"background_tracing_helper.cc",
"background_tracing_helper.h",
"responsiveness_metrics.cc",
"responsiveness_metrics.h",
"task_attribution_timing.cc",
"task_attribution_timing.h",
"time_clamper.cc",
Expand Down
86 changes: 56 additions & 30 deletions third_party/blink/renderer/core/timing/event_timing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,31 @@ bool ShouldReportForEventTiming(WindowPerformance* performance) {
} // namespace

EventTiming::EventTiming(base::TimeTicks processing_start,
base::TimeTicks event_timestamp,
WindowPerformance* performance,
bool should_log_event)
const Event& event)
: processing_start_(processing_start),
event_timestamp_(event_timestamp),
performance_(performance),
should_log_event_(should_log_event) {}
event_(&event) {
performance_->SetCurrentEventTimingEvent(&event);
}

// static
void EventTiming::HandleInputDelay(LocalDOMWindow* window, const Event& event) {
auto* pointer_event = DynamicTo<PointerEvent>(&event);
base::TimeTicks event_timestamp =
pointer_event ? pointer_event->OldestPlatformTimeStamp()
: event.PlatformTimeStamp();

base::TimeTicks processing_start = Now();
if (ShouldLogEvent(event) && event.isTrusted()) {
InteractiveDetector* interactive_detector =
InteractiveDetector::From(*window->document());
if (interactive_detector) {
interactive_detector->HandleForInputDelay(event, event_timestamp,
processing_start);
}
}
}

// static
bool EventTiming::IsEventTypeForEventTiming(const Event& event) {
Expand All @@ -81,7 +99,16 @@ bool EventTiming::IsEventTypeForEventTiming(const Event& event) {
std::unique_ptr<EventTiming> EventTiming::Create(LocalDOMWindow* window,
const Event& event) {
auto* performance = DOMWindowPerformance::performance(*window);
if (!performance || !IsEventTypeForEventTiming(event))
if (!performance || !event.isTrusted() ||
(!IsEventTypeForEventTiming(event) &&
event.type() != event_type_names::kPointermove))
return nullptr;

// Most events track their performance in EventDispatcher::Dispatch but
// some event types which can be filtered are tracked at the point
// where they may be filtered. This condition check ensures we don't create
// two EventTiming objects for the same Event.
if (performance->GetCurrentEventTimingEvent() == &event)
return nullptr;

bool should_report_for_event_timing = ShouldReportForEventTiming(performance);
Expand All @@ -90,39 +117,38 @@ std::unique_ptr<EventTiming> EventTiming::Create(LocalDOMWindow* window,
if (!should_report_for_event_timing && !should_log_event)
return nullptr;

auto* pointer_event = DynamicTo<PointerEvent>(&event);
base::TimeTicks event_timestamp =
pointer_event ? pointer_event->OldestPlatformTimeStamp()
: event.PlatformTimeStamp();

base::TimeTicks processing_start = Now();

if (should_log_event) {
InteractiveDetector* interactive_detector =
InteractiveDetector::From(*window->document());
if (interactive_detector) {
interactive_detector->HandleForInputDelay(event, event_timestamp,
processing_start);
}
}

return should_report_for_event_timing
? std::make_unique<EventTiming>(processing_start, event_timestamp,
performance, should_log_event)
? std::make_unique<EventTiming>(processing_start, performance,
event)
: nullptr;
}

void EventTiming::DidDispatchEvent(const Event& event, Document& document) {
Node* target = event.target() ? event.target()->ToNode() : nullptr;
base::TimeTicks processing_end = Now();
performance_->RegisterEventTiming(event.type(), event_timestamp_,
processing_start_, processing_end,
event.cancelable(), target);
}

// static
void EventTiming::SetTickClockForTesting(const base::TickClock* clock) {
g_clock_for_testing = clock;
}

EventTiming::~EventTiming() {
absl::optional<int> key_code = absl::nullopt;
if (event_->IsKeyboardEvent())
key_code = DynamicTo<KeyboardEvent>(event_.Get())->keyCode();

absl::optional<PointerId> pointer_id = absl::nullopt;
const PointerEvent* pointer_event = DynamicTo<PointerEvent>(event_.Get());
if (pointer_event)
pointer_id = pointer_event->pointerId();

base::TimeTicks event_timestamp =
pointer_event ? pointer_event->OldestPlatformTimeStamp()
: event_->PlatformTimeStamp();

// Register Event Timing for the event.
performance_->RegisterEventTiming(
event_->type(), event_timestamp, processing_start_, Now(),
event_->cancelable(),
event_->target() ? event_->target()->ToNode() : nullptr, key_code,
pointer_id);
}

} // namespace blink
13 changes: 7 additions & 6 deletions third_party/blink/renderer/core/timing/event_timing.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,19 @@ class CORE_EXPORT EventTiming final {
// Processes an event that will be dispatched. Notifies the
// InteractiveDetector if it needs to be logged into input delay histograms.
// Returns an object only if the event is relevant for the EventTiming API.
// This object should be constructed before the event is dispatched and
// destructed after dispatch so that we can calculate the input delay and
// other latency values correctly.
static std::unique_ptr<EventTiming> Create(LocalDOMWindow*, const Event&);

explicit EventTiming(base::TimeTicks processing_start,
base::TimeTicks event_timestamp,
WindowPerformance* performance,
bool should_log);
const Event& event);
~EventTiming();
EventTiming(const EventTiming&) = delete;
EventTiming& operator=(const EventTiming&) = delete;

// Notifies the Performance object that the event has been dispatched.
void DidDispatchEvent(const Event&, Document& document);

static void HandleInputDelay(LocalDOMWindow* window, const Event& event);
// The caller owns the |clock| which must outlive the EventTiming.
static void SetTickClockForTesting(const base::TickClock* clock);

Expand All @@ -52,7 +53,7 @@ class CORE_EXPORT EventTiming final {

Persistent<WindowPerformance> performance_;

bool should_log_event_;
Persistent<const Event> event_;
};

} // namespace blink
Expand Down
Loading

0 comments on commit 5a5584f

Please sign in to comment.