0

[Event Timing] Post launch cleanup for 2 Experiments

* Remove EventTimingKeypressAndCompositionInteractionId flag
* Remove EventTimingFallbackToModalDialogStart flag
* Update Key_interaction_state_machine doc
* Fix FlushKeydown to cover key_code_to_interaction_info_map_

Preview of the state machine doc: https://chromium.googlesource.com/chromium/src/+/refs/changes/50/5854350/3/third_party/blink/renderer/core/timing/Key_interaction_state_machine.md

Bug: 1435448,1456384
Change-Id: I862d444a2adcd5dc0d77e09ed49cc660e0b3e86d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5854350
Commit-Queue: Aoyuan Zuo <zuoaoyuan@chromium.org>
Reviewed-by: Michal Mocny <mmocny@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1354832}
This commit is contained in:
Aoyuan Zuo
2024-09-12 21:45:37 +00:00
committed by Chromium LUCI CQ
parent e80fa82591
commit 97db5bc75e
10 changed files with 214 additions and 414 deletions

@@ -876,13 +876,6 @@ public final class ProductionSupportedFlagList {
Flag.baseFeature( Flag.baseFeature(
BlinkFeatures.BACK_FORWARD_CACHE_SEND_NOT_RESTORED_REASONS, BlinkFeatures.BACK_FORWARD_CACHE_SEND_NOT_RESTORED_REASONS,
"Expose NotRestoredReasons via PerformanceNavigationTiming API."), "Expose NotRestoredReasons via PerformanceNavigationTiming API."),
Flag.baseFeature(
BlinkFeatures.EVENT_TIMING_FALLBACK_TO_MODAL_DIALOG_START,
"Enable reporting the modal dialog start time as an alternative end time for"
+ " duration measurement in performance event timing."),
Flag.baseFeature(
BlinkFeatures.EVENT_TIMING_KEYPRESS_AND_COMPOSITION_INTERACTION_ID,
"Exposes Event Timing keyboard InteractionId of composition and keypress events."),
Flag.baseFeature("SkipUnnecessaryThreadHopsForParseHeaders"), Flag.baseFeature("SkipUnnecessaryThreadHopsForParseHeaders"),
Flag.commandLine( Flag.commandLine(
AwSwitches.WEBVIEW_FPS_COMPONENT, AwSwitches.WEBVIEW_FPS_COMPONENT,

Binary file not shown.

After

(image error) Size: 182 KiB

File diff suppressed because one or more lines are too long

Before

(image error) Size: 12 KiB

@@ -786,12 +786,6 @@ BASE_FEATURE(kEstablishGpuChannelAsync,
#endif #endif
); );
// Exposes Event Timing keyboard InteractionId of composition and keypress
// events.
BASE_FEATURE(kEventTimingKeypressAndCompositionInteractionId,
"EventTimingKeypressAndCompositionInteractionId",
base::FEATURE_ENABLED_BY_DEFAULT);
// Enables unload handler deprecation via Permissions-Policy. // Enables unload handler deprecation via Permissions-Policy.
// https://crbug.com/1324111 // https://crbug.com/1324111
BASE_FEATURE(kDeprecateUnload, BASE_FEATURE(kDeprecateUnload,
@@ -816,12 +810,6 @@ BASE_FEATURE(kDeprecateUnloadByAllowList,
const base::FeatureParam<std::string> kDeprecateUnloadAllowlist{ const base::FeatureParam<std::string> kDeprecateUnloadAllowlist{
&kDeprecateUnloadByAllowList, "allowlist", ""}; &kDeprecateUnloadByAllowList, "allowlist", ""};
// Enable reporting the modal dialog start time as an alternative end time for
// duration measurement in performance event timing.
BASE_FEATURE(kEventTimingFallbackToModalDialogStart,
"EventTimingFallbackToModalDialogStart",
base::FEATURE_ENABLED_BY_DEFAULT);
// Enable not reporting orphan pointerup (pointerup not accompanied by // Enable not reporting orphan pointerup (pointerup not accompanied by
// pointerdown) as an interaction in performance event timing. // pointerdown) as an interaction in performance event timing.
BASE_FEATURE(kEventTimingHandleOrphanPointerup, BASE_FEATURE(kEventTimingHandleOrphanPointerup,

@@ -458,11 +458,6 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kDropInputEventsBeforeFirstPaint);
// layer tree frame sink. // layer tree frame sink.
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kEstablishGpuChannelAsync); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kEstablishGpuChannelAsync);
// Exposes Event Timing interactionId of keypress/keyboard events under
// composition.
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
kEventTimingKeypressAndCompositionInteractionId);
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kDeprecateUnload); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kDeprecateUnload);
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kDeprecateUnloadByAllowList); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kDeprecateUnloadByAllowList);
BLINK_COMMON_EXPORT extern const base::FeatureParam<int> BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
@@ -471,9 +466,6 @@ BLINK_COMMON_EXPORT extern const base::FeatureParam<int> kDeprecateUnloadBucket;
BLINK_COMMON_EXPORT extern const base::FeatureParam<std::string> BLINK_COMMON_EXPORT extern const base::FeatureParam<std::string>
kDeprecateUnloadAllowlist; kDeprecateUnloadAllowlist;
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
kEventTimingFallbackToModalDialogStart);
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kEventTimingHandleOrphanPointerup); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kEventTimingHandleOrphanPointerup);
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kExcludeLowEntropyImagesFromLCP); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kExcludeLowEntropyImagesFromLCP);

@@ -1,15 +1,18 @@
# Event Timing - Key Interaction State Machine # Event Timing - Key Interaction State Machine
Patricija Cerkaite, May 29 2023
Patricija Cerkaite, created on May 29 2023
Aoyuan Zuo, updated on Sept. 09 2024
[TOC] [TOC]
## Background ## Background
A keyboard interaction is a group of event handlers that fire when pressing a key on a keyboard. For example, A single "key pressed" interaction should include set order of events, such as `keydown`, `input`, and `keyup`. [EventTiming](https://w3c.github.io/event-timing/) group up certain events as interactions by assigning the same & non-trivial [interactionId](https://www.w3.org/TR/2022/WD-event-timing-20220524/#dom-performanceeventtiming-interactionid) following a state machine logic located in [`responsiveness_metrics.cc -> ResponsivenessMetrics::SetKeyIdAndRecordLatency()`](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/core/timing/responsiveness_metrics.cc#327). This doc visualizes this state machine to help people understand its logic. A keyboard interaction is a group of event handlers that fire when pressing a key on a keyboard. For example, A single "key pressed" interaction should include set order of events, such as `keydown`, `keypress`, `input`, and `keyup`. [EventTiming](https://w3c.github.io/event-timing/) group up certain events as interactions by assigning the same & non-trivial [interactionId](https://www.w3.org/TR/2022/WD-event-timing-20220524/#dom-performanceeventtiming-interactionid) following a state machine logic located in [`responsiveness_metrics.cc -> ResponsivenessMetrics::SetKeyIdAndRecordLatency()`](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/core/timing/responsiveness_metrics.cc#327). This doc visualizes this state machine to help people understand its logic.
- - - - - - - -
## Diagram ## Diagram
![state diagram](/docs/images/Key_interaction_state_machine_diagram.svg) ![state diagram](/docs/images/Key_interaction_state_machine_diagram.png)
*** note *** note
### Note: ### Note:
@@ -21,46 +24,92 @@ A keyboard interaction is a group of event handlers that fire when pressing a ke
## States ## States
### `[1]` No entry ### `[1]` Non Composition
The initial state. Either no entry of the key_code has been seen or previous ones have been cancelled. The initial state. No entry of the key_code has been seen. `key_code_to_interaction_info_map_` does not contain any entry with a key equal to key_code.
`key_code_entry_map_` does not contain any entry with a key equal to key_code.
### `[2]` Have keydown entry ### `[2]` Have Keydown
An intermediate state. In this state, we have seen the `keydown` entry for the current interaction, and are waiting for the matching `keyup` entry. An intermediate state. In this state, we have seen the `keydown` entry for the current interaction, and are waiting for potentially `keypress` or `contextmenu` events, and a matching `keyup` entry.
`key_code_entry_map_` currently contains the `keydown` entry of the interaction that this state machine represent. In this state, `keydown` entry waiting for a **matching** `keyup` entry to finish the current interaction. `key_code_to_interaction_info_map_` and `sequence_based_keyboard_interaction_info_` currently contains the `keydown` entry.
### `[3]` Composition Event ### `[3]` Have Keypress
The state indicates that `keydown` has initiated the composition. Since the composition events are not part of the keyboard interactions this intermediate state holds until the interactionId is produced. An intermediate state. In this state, we have seen the `keydown` and `keypress` entries for the current interaction, and are waiting for the matching `keyup` entry.
`key_code_to_interaction_info_map_` currently contains the timestamps of both `keydown` and `keypress` entries of the interaction that this state machine represent. In this state, we are waiting for the **matching** `keyup` entry to finish the current interaction.
### `[4]` Interaction finished ### `[4]` Have Contextmenu
This is the end of an interaction lifecycle. The `keydown` entry was paired with the corresponding `keyup` entry and the key_code from the `key_code_entry_map_` was errased. When pressing the menu key on a keyboard, a `contextmenu` event would get dispatched right after the `keydown` event. This is a valid user interaction, but the `keyup` event could possibly be dropped due to the showing of contextmenu overlay. Thus, while we waiting for the potentially coming `keyup` event, we also setup a 1 second timer to wrap up this interaction in case `keyup` is not coming.
### `[5]` Composition Continue Ongoing Interaction
The state indicates that `compositionstart` has initiated the composition. Since the composition events (`compositionstart`, `compositionupdate`, `compositionend`) are not part of the keyboard interactions this intermediate state holds until the interactionId is produced.
### `[6]` Composition Start New Interaction On Keydown
This state means we're currently under composition, and we're ready to start recording a new interaction upon next `keydown`.
### `[7]` Composition Start New Interaction On Input
This state means we're currently under composition, and we have not seen `keydown` thus no interactionId has been generated yet for the current interaction. As a result, we will generate one on `input`.
### `[8]` End Composition On Keydown
We have get out of the composition session, but are still waiting for potentially coming `keyup`s to wrap up the last interaction in composition.
### `[9]` Interaction finished
This is the end of an interaction lifecycle. The `keydown` entry was paired with the corresponding `keyup` entry and the key_code from the `key_code_to_interaction_info_map_` was erased.
- - - - - - - -
## Transitions ## Transitions
### `[5]` keydown ### `[10]` Keydown
Generate a new interactionId. Assign it to the `keydown` entry. Save the `keydown`'s key_code, interactionId, timestamps into `key_code_to_interaction_info_map_` for UKM reporting later once the interaction is fully finished..
Save the `keydown` key_code value to the `key_code_entry_map_`. ### `[11]` Key Holding
When holding a key down for an extended period. A serious of `keydown` events with the same key_code will be dispatched. We treat each individual `keydown` event as a valid interaction and assign a unique interactionId. Only the last one will be matched with `keyup`.
### `[6]` keydown ### `[12]` Keypress
A `keypress` event following a `keydown` event belongs to the same interaction as the `keydown`. We assign them the same interactionId and also save `keypress`'s timestamp into `key_code_to_interaction_info_map_` for UKM reporting later.
If key_code value is not equal to 229, then generate a new interactionId for the [`|previous_entry|`](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/blink/renderer/core/timing/responsiveness_metrics.cc;l=329;drc=2425fac374aaa944c34b2340b8f53c9c7fc49533#:~:text=if%20(key_code_entry_map_.Contains(*key_code))%20%7B). This transition could be triggered by holding a key down for an extended period. ### `[13]` Keyup
A `keyup` event always finish up an interaction by assigning the same interactionId as `keydown`'s and report interaction duration calculated by all events belongs to the interaction to UKM. The `key_code` will be removed from `key_code_to_interaction_info_map_` after reporting, and `sequence_based_keyboard_interaction_info_` will be cleared.
### `[7]` compositionstart ### `[14]` Contextmenu
The keydown event initiates the composition session. The `isComposition` parameter is set to true. When pressing the menu key on a keyboard, a `contextmenu` event would get dispatched right after the `keydown` event. This is a valid user interaction, but the `keyup` event could possibly be dropped due to the showing of contextmenu overlay. Thus, while we waiting for the potentially coming `keyup` event, we also setup a 1 second timer to wrap up this interaction in case `keyup` is not coming.
### `[8]` input ### `[15]` Keyup (after contextmenu)
The input event within the composition finishes the interaction and produces the interactionId. A `keyup` after contextmenu stops the timer and wraps up the interaction immediately, which means getting the same interactionId assigned; reporting the interaction to UKM; erasing the interaction info from `key_code_to_interaction_info_map_` and clears `sequence_based_keyboard_interaction_info_`.
### `[9]` [keyup key_code = keydown key_code] keyup ### `[16]` Compositionstart
The `compositionstart` event following a `keydown` event initiates the composition session.
The transition occurs if the keyup event is fired and there is a matching key_code of a keydown event. In this transition the following steps are executed: ### `[17]` Keydown (under composition)
1. Generate a new interactionId for the keydown-keyup pair (`keydown` and `keyup`). Some IME (Input Method Editor) could repeatedly dispatch keydowns for the same user interaction. When it happens, we will assign the same interactionId to all of them.
2. Delete the key_code of the pair from the `key_code_entry_map_`.
### `[10]` MaybeFlushKeyboardEntries ### `[18]` Compositionupdate
[MaybeFlushKeyboardEntries](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/blink/renderer/core/timing/window_performance.cc;l=677;drc=66941d1f0cfe9155b400aef887fe39a403c1f518;bpv=1;bpt=1) free up `keydown` entries stuck in `key_code_entry_map_` upon the first event timing entry after 500ms. `Keydown` gets stuck in the map for reasons like [crbug/1428603](https://bugs.chromium.org/p/chromium/issues/detail?id=1428603). This flush mechanism has a known [issue](https://bugs.chromium.org/p/chromium/issues/detail?id=1420716). `Compositionupdate` is the center of a keyboard user interaction under composition. Once we see it, we'll start wrapping up this interaction with matching the interactionId to the rest of events including `Input` and `keyup`.
### `[19]` Compositionstart
This is an edge case that exists for some IMEs where `keydown` events are missing. `Compositionstart` would initiate the composition session without a `keydown`.
### `[20]` Compositionupdate (keydown missing)
This is an edge case that exists for some IMEs where `keydown` events are missing. Since interactionIds are usually generated with `keydown` events, we'll fallback to generate a new interactionId on `input` to make sure we can still capture and report this interaction.
### `[21]` Input (keydown missing)
A new interactionId is generated since one wasn't generated before due to the missing `keydown`.
### `[22]` Input
Assign the current interactionId (previously generated from `keydown`) to the `input` event.
### `[23]` Keyup
Assign the current interactionId (previously generated from `keydown`) to the `keyup` event.
### `[24]` Keydown (next interaction)
Under composition session, we interpret a new keydown as the start of a new discrete user interaction on a keyboard, which also marks the end of the current interaction.
### `[25]` Compositionend
`Compositionend` marks the end of an user interaction under composition through a one second timer, in case when more than one `keyup` events are dispatched we can still group them up into the same user interaction, which could happen to some IMEs as an edge case.
### `[26]` Keyup
A `keyup` from the same user interaction could come after `compositionend`, and it will get the same interactionId assigned.
### `[27]` Keydown (next interaction) / timer
Either after 1 second by a timer, or the next `keydown` event coming in before the timer times up, we'll wrap up the current interaction and report to UKM.
- - - - - - - -
## Mermaid diagram source file ## Mermaid diagram source file
@@ -77,18 +126,35 @@ date as well.
``` ```
stateDiagram-v2 stateDiagram-v2
no_entry : No entry [1] non_composition : Non Composition [1]
have_key_down : Have keydown entry [2] have_keydown : Have keydown [2]
have_composition : Composition Event [3] have_keypress : Have Keypress [3]
interaction_finished: Interaction finished [4] have_contextmenu : Have Contextmenu [4]
composition_continue_ongoing_interaction : Composition Continue Ongoing Interaction [5]
composition_start_new_interaction_on_keydown : Composition Start New Interaction On Keydown [6]
composition_start_new_interaction_on_input : Composition Start New Interaction On Input [7]
end_composition_on_keydown : End Composition On Keydown [8]
interaction_finished: Interaction finished [9]
[*] --> no_entry [*] --> non_composition
no_entry --> have_key_down : keydown [5] non_composition --> have_keydown : Keydown [10]
have_key_down --> have_key_down : keydown [6] have_keydown --> have_keydown : Key Holding [11]
no_entry --> have_composition: compositionstart [7] have_keydown --> have_keypress : Keypress [12]
have_composition --> interaction_finished : input [8] have_keypress --> interaction_finished : Keyup [13]
have_key_down --> interaction_finished : [keyup key_code = keydown key_code] keyup [9] have_keydown --> have_contextmenu : Contextmenu [14]
have_key_down --> interaction_finished : MaybeFlushKeyboardEntries [10] have_contextmenu --> interaction_finished : Keyup [15]
have_keydown --> composition_continue_ongoing_interaction : Compositionstart [16]
composition_continue_ongoing_interaction --> composition_continue_ongoing_interaction : Keydown [17]
composition_continue_ongoing_interaction --> composition_start_new_interaction_on_keydown : Compositionupdate [18]
non_composition --> composition_continue_ongoing_interaction : Compositionstart [19]
composition_continue_ongoing_interaction --> composition_start_new_interaction_on_input : Compositionupdate (keydown missing) [20]
composition_start_new_interaction_on_input --> composition_start_new_interaction_on_keydown : Input [21]
composition_start_new_interaction_on_keydown --> composition_start_new_interaction_on_keydown : Input [22]
composition_start_new_interaction_on_keydown --> composition_start_new_interaction_on_keydown : Keyup [23]
composition_start_new_interaction_on_keydown --> interaction_finished : Keydown (next interaction) [24]
composition_start_new_interaction_on_keydown --> end_composition_on_keydown : Compositionend [25]
end_composition_on_keydown --> end_composition_on_keydown : Keyup [26]
end_composition_on_keydown --> interaction_finished : Keydown (next interaction) / timer [27]
interaction_finished --> [*] interaction_finished --> [*]
``` ```

@@ -48,9 +48,6 @@ constexpr uint32_t kMaxFirstInteractionID = 10000;
// interactions. This is consistent with the spec, which allows the increasing // interactions. This is consistent with the spec, which allows the increasing
// the user interaction value by a small number chosen by the user agent. // the user interaction value by a small number chosen by the user agent.
constexpr uint32_t kInteractionIdIncrement = 7; constexpr uint32_t kInteractionIdIncrement = 7;
// The maximum tap delay we can handle for assigning interaction id.
constexpr blink::DOMHighResTimeStamp kMaxDelayForEntries =
blink::DOMHighResTimeStamp(500);
// The length of the timer to flush entries from the time pointerup occurs. // The length of the timer to flush entries from the time pointerup occurs.
constexpr base::TimeDelta kFlushTimerLength = base::Seconds(1); constexpr base::TimeDelta kFlushTimerLength = base::Seconds(1);
// The name for the histogram which records interaction timings, and the names // The name for the histogram which records interaction timings, and the names
@@ -440,9 +437,7 @@ bool ResponsivenessMetrics::SetPointerIdAndRecordLatency(
} }
} }
if (base::FeatureList::IsEnabled( if (RuntimeEnabledFeaturesBase::
features::kEventTimingKeypressAndCompositionInteractionId) &&
RuntimeEnabledFeaturesBase::
EventTimingHandleKeyboardEventSimulatedClickEnabled()) { EventTimingHandleKeyboardEventSimulatedClickEnabled()) {
// Try handle keyboard event simulated click. // Try handle keyboard event simulated click.
if (TryHandleKeyboardEventSimulatedClick(entry, pointer_id)) { if (TryHandleKeyboardEventSimulatedClick(entry, pointer_id)) {
@@ -529,112 +524,7 @@ void ResponsivenessMetrics::RecordKeyboardUKM(
// (https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/core/timing/Key_interaction_state_machine.md) // (https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/core/timing/Key_interaction_state_machine.md)
// to help understand the logic below that how event timing group up keyboard // to help understand the logic below that how event timing group up keyboard
// events as interactions. // events as interactions.
bool ResponsivenessMetrics::SetKeyIdAndRecordLatency( void ResponsivenessMetrics::SetKeyIdAndRecordLatency(
PerformanceEventTiming* entry,
EventTimestamps event_timestamps) {
if (base::FeatureList::IsEnabled(
features::kEventTimingKeypressAndCompositionInteractionId)) {
return SetKeyIdAndRecordLatencyExperimental(entry, event_timestamps);
}
last_pointer_id_ = std::nullopt;
auto event_type = entry->name();
if (event_type == event_type_names::kKeydown) {
// If we were waiting for matching pointerup/keyup after a contextmenu, they
// won't show up at this point.
if (contextmenu_flush_timer_.IsActive()) {
contextmenu_flush_timer_.Stop();
FlushPointerdownAndPointerup();
FlushKeydown();
}
// During compositions, we ignore keydowns/keyups and look at input events.
if (composition_started_) {
return true;
}
DCHECK(entry->GetEventTimingReportingInfo()->key_code.has_value());
auto key_code = entry->GetEventTimingReportingInfo()->key_code.value();
if (key_code_entry_map_.Contains(key_code)) {
auto* previous_entry = key_code_entry_map_.at(key_code);
// Ignore repeat IME keydowns. See
// https://w3c.github.io/uievents/#determine-keydown-keyup-keyCode.
// Reasoning: we cannot ignore all IME keydowns because on Android in
// some languages the events received are 'keydown', 'input', 'keyup',
// and since we are not composing then the 'input' event is ignored, so
// we must consider the key events with 229 keyCode as the user
// interaction. Besides this, we cannot consider repeat 229 keydowns
// because we may get those on ChromeOS when we should ignore them. This
// may be related to crbug.com/1252856.
if (key_code != 229) {
// Generate a new interaction id for |previous_entry|. This case could
// be caused by keeping a key pressed for a while.
UpdateInteractionId();
previous_entry->GetEntry()->SetInteractionIdAndOffset(
GetCurrentInteractionId(), GetInteractionCount());
RecordKeyboardUKM(window_performance_->DomWindow(),
{previous_entry->GetTimeStamps()},
previous_entry->GetEntry()->interactionOffset());
}
window_performance_->NotifyAndAddEventTimingBuffer(
previous_entry->GetEntry());
}
key_code_entry_map_.Set(
key_code, KeyboardEntryAndTimestamps::Create(entry, event_timestamps));
// Similar to pointerdown, we need to wait a bit before knowing the
// interactionId of keydowns.
return false;
} else if (event_type == event_type_names::kKeyup) {
if (contextmenu_flush_timer_.IsActive()) {
contextmenu_flush_timer_.Stop();
}
DCHECK(entry->GetEventTimingReportingInfo()->key_code.has_value());
auto key_code = entry->GetEventTimingReportingInfo()->key_code.value();
if (composition_started_ || !key_code_entry_map_.Contains(key_code)) {
return true;
}
auto* previous_entry = key_code_entry_map_.at(key_code);
// Generate a new interaction id for the keydown-keyup pair.
UpdateInteractionId();
previous_entry->GetEntry()->SetInteractionIdAndOffset(
GetCurrentInteractionId(), GetInteractionCount());
window_performance_->NotifyAndAddEventTimingBuffer(
previous_entry->GetEntry());
if (previous_entry->GetEntry()->HasKnownInteractionID()) {
entry->SetInteractionIdAndOffset(
previous_entry->GetEntry()->interactionId(),
previous_entry->GetEntry()->interactionOffset());
}
RecordKeyboardUKM(window_performance_->DomWindow(),
{previous_entry->GetTimeStamps(), event_timestamps},
entry->interactionOffset());
key_code_entry_map_.erase(key_code);
} else if (event_type == event_type_names::kCompositionstart) {
composition_started_ = true;
for (auto key_entry : key_code_entry_map_.Values()) {
window_performance_->NotifyAndAddEventTimingBuffer(key_entry->GetEntry());
}
key_code_entry_map_.clear();
} else if (event_type == event_type_names::kCompositionend) {
composition_started_ = false;
} else if (event_type == event_type_names::kInput) {
if (!composition_started_) {
return true;
}
// We are in the case of a text input event while compositing with
// non-trivial data, so we want to increase interactionId.
// TODO(crbug.com/1252856): fix counts in ChromeOS due to duplicate
// events.
UpdateInteractionId();
entry->SetInteractionIdAndOffset(GetCurrentInteractionId(),
GetInteractionCount());
RecordKeyboardUKM(window_performance_->DomWindow(), {event_timestamps},
entry->interactionOffset());
}
return true;
}
bool ResponsivenessMetrics::SetKeyIdAndRecordLatencyExperimental(
PerformanceEventTiming* entry, PerformanceEventTiming* entry,
EventTimestamps event_timestamps) { EventTimestamps event_timestamps) {
last_pointer_id_ = std::nullopt; last_pointer_id_ = std::nullopt;
@@ -685,7 +575,7 @@ bool ResponsivenessMetrics::SetKeyIdAndRecordLatencyExperimental(
CHECK(entry->GetEventTimingReportingInfo()->key_code.has_value()); CHECK(entry->GetEventTimingReportingInfo()->key_code.has_value());
auto key_code = entry->GetEventTimingReportingInfo()->key_code.value(); auto key_code = entry->GetEventTimingReportingInfo()->key_code.value();
if (!key_code_to_interaction_info_map_.Contains(key_code)) { if (!key_code_to_interaction_info_map_.Contains(key_code)) {
return true; return;
} }
// Match the keydown entry with the keyup entry using keycode. // Match the keydown entry with the keyup entry using keycode.
@@ -734,7 +624,7 @@ bool ResponsivenessMetrics::SetKeyIdAndRecordLatencyExperimental(
} else if (event_type == event_type_names::kInput) { } else if (event_type == event_type_names::kInput) {
// Expose interactionId for Input events only under composition // Expose interactionId for Input events only under composition
if (composition_state_ == kNonComposition) { if (composition_state_ == kNonComposition) {
return true; return;
} }
// Update Interaction Id when input is selected using IME suggestion without // Update Interaction Id when input is selected using IME suggestion without
// pressing a key. In this case Input event starts and finishes interaction // pressing a key. In this case Input event starts and finishes interaction
@@ -759,44 +649,18 @@ bool ResponsivenessMetrics::SetKeyIdAndRecordLatencyExperimental(
} }
last_keydown_keycode_info_.reset(); last_keydown_keycode_info_.reset();
} }
return true;
}
void ResponsivenessMetrics::FlushExpiredKeydown(
DOMHighResTimeStamp current_time) {
if (base::FeatureList::IsEnabled(
features::kEventTimingKeypressAndCompositionInteractionId)) {
// Do nothing. Experimenting not cleaning up keydown/keypress that was not
// successfully paired with a keyup thus get stuck in the map.
} else {
// We cannot delete from a HashMap while iterating.
Vector<int> key_codes_to_remove;
for (const auto& entry : key_code_entry_map_) {
PerformanceEventTiming* key_down = entry.value->GetEntry();
if (current_time - key_down->processingEnd() > kMaxDelayForEntries) {
window_performance_->NotifyAndAddEventTimingBuffer(key_down);
key_codes_to_remove.push_back(entry.key);
}
}
key_code_entry_map_.RemoveAll(key_codes_to_remove);
}
} }
void ResponsivenessMetrics::FlushKeydown() { void ResponsivenessMetrics::FlushKeydown() {
for (const auto& entry : key_code_entry_map_) { for (auto& entry : key_code_to_interaction_info_map_) {
PerformanceEventTiming* key_down = entry.value->GetEntry();
// Keydowns triggered contextmenu, though missing pairing keyups due to a // Keydowns triggered contextmenu, though missing pairing keyups due to a
// known issue - https://github.com/w3c/pointerevents/issues/408, should // known issue - https://github.com/w3c/pointerevents/issues/408, should
// still be counted as a valid interaction and get a valid id assigned. // still be counted as a valid interaction and get reported to UKM.
UpdateInteractionId();
key_down->SetInteractionIdAndOffset(GetCurrentInteractionId(),
GetInteractionCount());
window_performance_->NotifyAndAddEventTimingBuffer(key_down);
RecordKeyboardUKM(window_performance_->DomWindow(), RecordKeyboardUKM(window_performance_->DomWindow(),
{entry.value->GetTimeStamps()}, entry.value.GetTimeStamps(),
key_down->interactionOffset()); entry.value.GetInteractionOffset());
} }
key_code_entry_map_.clear(); key_code_to_interaction_info_map_.clear();
} }
void ResponsivenessMetrics::FlushAllEventsAtPageHidden() { void ResponsivenessMetrics::FlushAllEventsAtPageHidden() {
@@ -958,7 +822,6 @@ void ResponsivenessMetrics::Trace(Visitor* visitor) const {
visitor->Trace(pointer_id_entry_map_); visitor->Trace(pointer_id_entry_map_);
visitor->Trace(pointer_flush_timer_); visitor->Trace(pointer_flush_timer_);
visitor->Trace(contextmenu_flush_timer_); visitor->Trace(contextmenu_flush_timer_);
visitor->Trace(key_code_entry_map_);
visitor->Trace(composition_end_flush_timer_); visitor->Trace(composition_end_flush_timer_);
} }

@@ -43,8 +43,7 @@ class CORE_EXPORT ResponsivenessMetrics
}; };
// Wrapper class to store interactionId, interaction offset, and timestamps // Wrapper class to store interactionId, interaction offset, and timestamps
// of an entry on a HashMap. It is optimized and used only in the experimental // of an entry on a HashMap.
// SetKeyIdAndRecordLatency function. (SetKeyIdAndRecordLatencyExperimental)
class InteractionInfo { class InteractionInfo {
public: public:
InteractionInfo(uint32_t interaction_id, InteractionInfo(uint32_t interaction_id,
@@ -167,21 +166,12 @@ class CORE_EXPORT ResponsivenessMetrics
// Assigns interactionId and records interaction latency for keyboard events. // Assigns interactionId and records interaction latency for keyboard events.
// We care about input, compositionstart, and compositionend events, so // We care about input, compositionstart, and compositionend events, so
// |key_code| will be std::nullopt in those cases. Returns true if the entry // |key_code| will be std::nullopt in those cases.
// would be ready to be surfaced in PerformanceObservers and the Performance void SetKeyIdAndRecordLatency(PerformanceEventTiming* entry,
// Timeline.
bool SetKeyIdAndRecordLatency(PerformanceEventTiming* entry,
EventTimestamps event_timestamps); EventTimestamps event_timestamps);
// Experimental function that in addition to SetKeyIdAndRecordLatency() // Clears all keydowns in |key_code_to_interaction_info_map_| and report to
// exposes interactionId for keypress and keyup/keydown under composition. // UKM.
bool SetKeyIdAndRecordLatencyExperimental(PerformanceEventTiming* entry,
EventTimestamps event_timestamps);
// Clear keydowns in |key_codes_to_remove| if we have stored them for a while.
void FlushExpiredKeydown(DOMHighResTimeStamp end_time);
// Clears all keydowns in |key_codes_to_remove| no matter how long we have
// stored them.
void FlushKeydown(); void FlushKeydown();
uint32_t GetInteractionCount() const; uint32_t GetInteractionCount() const;
@@ -266,12 +256,6 @@ class CORE_EXPORT ResponsivenessMetrics
HashMap<int, InteractionInfo, IntWithZeroKeyHashTraits<int>> HashMap<int, InteractionInfo, IntWithZeroKeyHashTraits<int>>
key_code_to_interaction_info_map_; key_code_to_interaction_info_map_;
// Map from keyCodes to keydown entries and keydown timestamps.
HeapHashMap<int,
Member<KeyboardEntryAndTimestamps>,
IntWithZeroKeyHashTraits<int>>
key_code_entry_map_;
// Whether we are composing or not. When we are not composing, we set // Whether we are composing or not. When we are not composing, we set
// interactionId for keydown and keyup events. When we are composing, we set // interactionId for keydown and keyup events. When we are composing, we set
// interactionId for input events. // interactionId for input events.

@@ -627,11 +627,6 @@ void WindowPerformance::OnPresentationPromiseResolved(
pending_event_presentation_time_map_.Set(presentation_index, pending_event_presentation_time_map_.Set(presentation_index,
presentation_timestamp); presentation_timestamp);
ReportEventTimings(); ReportEventTimings();
// Use |end_time| as a proxy for the current time to flush expired keydowns.
DOMHighResTimeStamp end_time =
MonotonicTimeToDOMHighResTimeStamp(presentation_timestamp);
responsiveness_metrics_->FlushExpiredKeydown(end_time);
} }
void WindowPerformance::FlushEventTimingsOnPageHidden() { void WindowPerformance::FlushEventTimingsOnPageHidden() {
@@ -941,10 +936,7 @@ void WindowPerformance::SetFallbackTime(PerformanceEventTiming* entry) {
if (!show_modal_dialog_timestamps_.empty() && if (!show_modal_dialog_timestamps_.empty() &&
show_modal_dialog_timestamps_.front() < show_modal_dialog_timestamps_.front() <
entry->GetEventTimingReportingInfo()->presentation_time) { entry->GetEventTimingReportingInfo()->presentation_time) {
if (base::FeatureList::IsEnabled( fallback_end_time_to_dialog_time = true;
features::kEventTimingFallbackToModalDialogStart)) {
fallback_end_time_to_dialog_time = true;
}
first_modal_dialog_timestamp = show_modal_dialog_timestamps_.front(); first_modal_dialog_timestamp = show_modal_dialog_timestamps_.front();
} }
@@ -982,8 +974,8 @@ bool WindowPerformance::SetInteractionIdAndRecordLatency(
return responsiveness_metrics_->SetPointerIdAndRecordLatency( return responsiveness_metrics_->SetPointerIdAndRecordLatency(
entry, event_timestamps); entry, event_timestamps);
} }
return responsiveness_metrics_->SetKeyIdAndRecordLatency(entry, responsiveness_metrics_->SetKeyIdAndRecordLatency(entry, event_timestamps);
event_timestamps); return true;
} }
void WindowPerformance::ReportLongAnimationFrameTiming( void WindowPerformance::ReportLongAnimationFrameTiming(

@@ -13,7 +13,6 @@
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_mock_time_task_runner.h" #include "base/test/test_mock_time_task_runner.h"
#include "base/test/trace_event_analyzer.h" #include "base/test/trace_event_analyzer.h"
#include "base/time/time.h" #include "base/time/time.h"
@@ -67,14 +66,9 @@ base::TimeTicks GetTimeStamp(int64_t time) {
} // namespace } // namespace
class WindowPerformanceTest : public testing::Test, class WindowPerformanceTest : public testing::Test {
public ::testing::WithParamInterface<bool> {
protected: protected:
void SetUp() override { void SetUp() override {
if (GetParam()) {
features_.InitAndEnableFeature(
blink::features::kEventTimingKeypressAndCompositionInteractionId);
}
test_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); test_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
ResetPerformance(); ResetPerformance();
} }
@@ -220,10 +214,9 @@ class WindowPerformanceTest : public testing::Test,
scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_; scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
ScopedFakeUkmRecorder scoped_fake_ukm_recorder_; ScopedFakeUkmRecorder scoped_fake_ukm_recorder_;
base::HistogramTester histogram_tester_; base::HistogramTester histogram_tester_;
base::test::ScopedFeatureList features_;
}; };
TEST_P(WindowPerformanceTest, SanitizedLongTaskName) { TEST_F(WindowPerformanceTest, SanitizedLongTaskName) {
// Unable to attribute, when no execution contents are available. // Unable to attribute, when no execution contents are available.
EXPECT_EQ("unknown", SanitizedAttribution(nullptr, false, GetFrame())); EXPECT_EQ("unknown", SanitizedAttribution(nullptr, false, GetFrame()));
@@ -235,7 +228,7 @@ TEST_P(WindowPerformanceTest, SanitizedLongTaskName) {
SanitizedAttribution(GetWindow(), true, GetFrame())); SanitizedAttribution(GetWindow(), true, GetFrame()));
} }
TEST_P(WindowPerformanceTest, SanitizedLongTaskName_CrossOrigin) { TEST_F(WindowPerformanceTest, SanitizedLongTaskName_CrossOrigin) {
// Create another dummy page holder and pretend it is an iframe. // Create another dummy page holder and pretend it is an iframe.
DummyPageHolder another_page(gfx::Size(400, 300)); DummyPageHolder another_page(gfx::Size(400, 300));
another_page.GetDocument().SetURL(KURL("https://iframed.com/bar")); another_page.GetDocument().SetURL(KURL("https://iframed.com/bar"));
@@ -252,7 +245,7 @@ TEST_P(WindowPerformanceTest, SanitizedLongTaskName_CrossOrigin) {
// https://crbug.com/706798: Checks that after navigation that have replaced the // https://crbug.com/706798: Checks that after navigation that have replaced the
// window object, calls to not garbage collected yet WindowPerformance belonging // window object, calls to not garbage collected yet WindowPerformance belonging
// to the old window do not cause a crash. // to the old window do not cause a crash.
TEST_P(WindowPerformanceTest, NavigateAway) { TEST_F(WindowPerformanceTest, NavigateAway) {
AddLongTaskObserver(); AddLongTaskObserver();
// Simulate navigation commit. // Simulate navigation commit.
@@ -307,7 +300,7 @@ TEST(PerformanceLifetimeTest, SurviveContextSwitch) {
// Make sure the output entries with the same timestamps follow the insertion // Make sure the output entries with the same timestamps follow the insertion
// order. (http://crbug.com/767560) // order. (http://crbug.com/767560)
TEST_P(WindowPerformanceTest, EnsureEntryListOrder) { TEST_F(WindowPerformanceTest, EnsureEntryListOrder) {
// Need to have an active V8 context for ScriptValues to operate. // Need to have an active V8 context for ScriptValues to operate.
v8::HandleScope handle_scope(GetScriptState()->GetIsolate()); v8::HandleScope handle_scope(GetScriptState()->GetIsolate());
v8::Local<v8::Context> context = GetScriptState()->GetContext(); v8::Local<v8::Context> context = GetScriptState()->GetContext();
@@ -340,7 +333,7 @@ TEST_P(WindowPerformanceTest, EnsureEntryListOrder) {
} }
} }
TEST_P(WindowPerformanceTest, EventTimingEntryBuffering) { TEST_F(WindowPerformanceTest, EventTimingEntryBuffering) {
EXPECT_TRUE(page_holder_->GetFrame().Loader().GetDocumentLoader()); EXPECT_TRUE(page_holder_->GetFrame().Loader().GetDocumentLoader());
base::TimeTicks start_time = GetTimeOrigin() + base::Seconds(1.1); base::TimeTicks start_time = GetTimeOrigin() + base::Seconds(1.1);
@@ -377,7 +370,7 @@ TEST_P(WindowPerformanceTest, EventTimingEntryBuffering) {
.size()); .size());
} }
TEST_P(WindowPerformanceTest, Expose100MsEvents) { TEST_F(WindowPerformanceTest, Expose100MsEvents) {
base::TimeTicks start_time = GetTimeOrigin() + base::Seconds(1); base::TimeTicks start_time = GetTimeOrigin() + base::Seconds(1);
base::TimeTicks processing_start = start_time + base::Milliseconds(10); base::TimeTicks processing_start = start_time + base::Milliseconds(10);
base::TimeTicks processing_end = processing_start + base::Milliseconds(10); base::TimeTicks processing_end = processing_start + base::Milliseconds(10);
@@ -399,7 +392,7 @@ TEST_P(WindowPerformanceTest, Expose100MsEvents) {
EXPECT_EQ(event_type_names::kMousedown, entries.at(0)->name()); EXPECT_EQ(event_type_names::kMousedown, entries.at(0)->name());
} }
TEST_P(WindowPerformanceTest, EventTimingDuration) { TEST_F(WindowPerformanceTest, EventTimingDuration) {
base::TimeTicks start_time = GetTimeOrigin() + base::Milliseconds(1000); base::TimeTicks start_time = GetTimeOrigin() + base::Milliseconds(1000);
base::TimeTicks processing_start = GetTimeOrigin() + base::Milliseconds(1001); base::TimeTicks processing_start = GetTimeOrigin() + base::Milliseconds(1001);
base::TimeTicks processing_end = GetTimeOrigin() + base::Milliseconds(1002); base::TimeTicks processing_end = GetTimeOrigin() + base::Milliseconds(1002);
@@ -434,7 +427,7 @@ TEST_P(WindowPerformanceTest, EventTimingDuration) {
// Test the case where multiple events are registered and then their // Test the case where multiple events are registered and then their
// presentation promise is resolved. // presentation promise is resolved.
TEST_P(WindowPerformanceTest, MultipleEventsThenPresent) { TEST_F(WindowPerformanceTest, MultipleEventsThenPresent) {
size_t num_events = 10; size_t num_events = 10;
for (size_t i = 0; i < num_events; ++i) { for (size_t i = 0; i < num_events; ++i) {
base::TimeTicks start_time = GetTimeOrigin() + base::Seconds(i); base::TimeTicks start_time = GetTimeOrigin() + base::Seconds(i);
@@ -458,7 +451,7 @@ TEST_P(WindowPerformanceTest, MultipleEventsThenPresent) {
// Test the case where commit finish timestamps are recorded on all pending // Test the case where commit finish timestamps are recorded on all pending
// EventTimings. // EventTimings.
TEST_P(WindowPerformanceTest, TEST_F(WindowPerformanceTest,
CommitFinishTimeRecordedOnAllPendingEventTimings) { CommitFinishTimeRecordedOnAllPendingEventTimings) {
size_t num_events = 3; size_t num_events = 3;
for (size_t i = 0; i < num_events; ++i) { for (size_t i = 0; i < num_events; ++i) {
@@ -484,7 +477,7 @@ TEST_P(WindowPerformanceTest,
// Test the case where a new commit finish timestamps does not affect previous // Test the case where a new commit finish timestamps does not affect previous
// EventTiming who has already seen a commit finish. // EventTiming who has already seen a commit finish.
TEST_P(WindowPerformanceTest, NewCommitNotOverwritePreviousEventTimings) { TEST_F(WindowPerformanceTest, NewCommitNotOverwritePreviousEventTimings) {
base::TimeTicks start_time = GetTimeOrigin() + base::Seconds(1); base::TimeTicks start_time = GetTimeOrigin() + base::Seconds(1);
base::TimeTicks processing_start = start_time + base::Milliseconds(100); base::TimeTicks processing_start = start_time + base::Milliseconds(100);
base::TimeTicks processing_end = start_time + base::Milliseconds(200); base::TimeTicks processing_end = start_time + base::Milliseconds(200);
@@ -512,7 +505,7 @@ TEST_P(WindowPerformanceTest, NewCommitNotOverwritePreviousEventTimings) {
} }
// Test for existence of 'first-input' given different types of first events. // Test for existence of 'first-input' given different types of first events.
TEST_P(WindowPerformanceTest, FirstInput) { TEST_F(WindowPerformanceTest, FirstInput) {
struct { struct {
AtomicString event_type; AtomicString event_type;
bool should_report; bool should_report;
@@ -546,7 +539,7 @@ TEST_P(WindowPerformanceTest, FirstInput) {
// Test that the 'first-input' is populated after some irrelevant events are // Test that the 'first-input' is populated after some irrelevant events are
// ignored. // ignored.
TEST_P(WindowPerformanceTest, FirstInputAfterIgnored) { TEST_F(WindowPerformanceTest, FirstInputAfterIgnored) {
AtomicString several_events[] = {event_type_names::kMouseover, AtomicString several_events[] = {event_type_names::kMouseover,
event_type_names::kMousedown, event_type_names::kMousedown,
event_type_names::kPointerup}; event_type_names::kPointerup};
@@ -567,7 +560,7 @@ TEST_P(WindowPerformanceTest, FirstInputAfterIgnored) {
} }
// Test that pointerdown followed by pointerup works as a 'firstInput'. // Test that pointerdown followed by pointerup works as a 'firstInput'.
TEST_P(WindowPerformanceTest, FirstPointerUp) { TEST_F(WindowPerformanceTest, FirstPointerUp) {
base::TimeTicks start_time = GetTimeStamp(0); base::TimeTicks start_time = GetTimeStamp(0);
base::TimeTicks processing_start = GetTimeStamp(1); base::TimeTicks processing_start = GetTimeStamp(1);
base::TimeTicks processing_end = GetTimeStamp(2); base::TimeTicks processing_end = GetTimeStamp(2);
@@ -593,7 +586,7 @@ TEST_P(WindowPerformanceTest, FirstPointerUp) {
// When the pointerdown is optimized out, the mousedown works as a // When the pointerdown is optimized out, the mousedown works as a
// 'first-input'. // 'first-input'.
TEST_P(WindowPerformanceTest, PointerdownOptimizedOut) { TEST_F(WindowPerformanceTest, PointerdownOptimizedOut) {
base::TimeTicks start_time = GetTimeStamp(0); base::TimeTicks start_time = GetTimeStamp(0);
base::TimeTicks processing_start = GetTimeStamp(1); base::TimeTicks processing_start = GetTimeStamp(1);
base::TimeTicks processing_end = GetTimeStamp(2); base::TimeTicks processing_end = GetTimeStamp(2);
@@ -613,7 +606,7 @@ TEST_P(WindowPerformanceTest, PointerdownOptimizedOut) {
// Test that pointerdown followed by mousedown, pointerup works as a // Test that pointerdown followed by mousedown, pointerup works as a
// 'first-input'. // 'first-input'.
TEST_P(WindowPerformanceTest, PointerdownOnDesktop) { TEST_F(WindowPerformanceTest, PointerdownOnDesktop) {
base::TimeTicks start_time = GetTimeStamp(0); base::TimeTicks start_time = GetTimeStamp(0);
base::TimeTicks processing_start = GetTimeStamp(1); base::TimeTicks processing_start = GetTimeStamp(1);
base::TimeTicks processing_end = GetTimeStamp(2); base::TimeTicks processing_end = GetTimeStamp(2);
@@ -643,7 +636,7 @@ TEST_P(WindowPerformanceTest, PointerdownOnDesktop) {
.size()); .size());
} }
TEST_P(WindowPerformanceTest, OneKeyboardInteraction) { TEST_F(WindowPerformanceTest, OneKeyboardInteraction) {
base::TimeTicks keydown_timestamp = GetTimeStamp(0); base::TimeTicks keydown_timestamp = GetTimeStamp(0);
// Keydown // Keydown
base::TimeTicks processing_start_keydown = GetTimeStamp(1); base::TimeTicks processing_start_keydown = GetTimeStamp(1);
@@ -693,7 +686,7 @@ TEST_P(WindowPerformanceTest, OneKeyboardInteraction) {
"Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0); "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0);
} }
TEST_P(WindowPerformanceTest, HoldingDownAKey) { TEST_F(WindowPerformanceTest, HoldingDownAKey) {
auto entries = GetUkmRecorder()->GetEntriesByName( auto entries = GetUkmRecorder()->GetEntriesByName(
ukm::builders::Responsiveness_UserInteraction::kEntryName); ukm::builders::Responsiveness_UserInteraction::kEntryName);
EXPECT_EQ(0u, entries.size()); EXPECT_EQ(0u, entries.size());
@@ -773,7 +766,7 @@ TEST_P(WindowPerformanceTest, HoldingDownAKey) {
"Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0); "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0);
} }
TEST_P(WindowPerformanceTest, PressMultipleKeys) { TEST_F(WindowPerformanceTest, PressMultipleKeys) {
auto entries = GetUkmRecorder()->GetEntriesByName( auto entries = GetUkmRecorder()->GetEntriesByName(
ukm::builders::Responsiveness_UserInteraction::kEntryName); ukm::builders::Responsiveness_UserInteraction::kEntryName);
EXPECT_EQ(0u, entries.size()); EXPECT_EQ(0u, entries.size());
@@ -847,7 +840,7 @@ TEST_P(WindowPerformanceTest, PressMultipleKeys) {
// Test a real world scenario, where keydown got presented first but its // Test a real world scenario, where keydown got presented first but its
// callback got invoked later than keyup's due to multi processes & threading // callback got invoked later than keyup's due to multi processes & threading
// overhead. // overhead.
TEST_P(WindowPerformanceTest, KeyupFinishLastButCallbackInvokedFirst) { TEST_F(WindowPerformanceTest, KeyupFinishLastButCallbackInvokedFirst) {
// Arbitrary keycode picked for testing from // Arbitrary keycode picked for testing from
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#value_of_keycode // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#value_of_keycode
int digit_1_key_code = 0x31; int digit_1_key_code = 0x31;
@@ -908,7 +901,7 @@ TEST_P(WindowPerformanceTest, KeyupFinishLastButCallbackInvokedFirst) {
"Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0); "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0);
} }
TEST_P(WindowPerformanceTest, TapOrClick) { TEST_F(WindowPerformanceTest, TapOrClick) {
// Pointerdown // Pointerdown
base::TimeTicks pointerdown_timestamp = GetTimeOrigin(); base::TimeTicks pointerdown_timestamp = GetTimeOrigin();
base::TimeTicks processing_start_pointerdown = GetTimeStamp(1); base::TimeTicks processing_start_pointerdown = GetTimeStamp(1);
@@ -968,7 +961,7 @@ TEST_P(WindowPerformanceTest, TapOrClick) {
"Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0); "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0);
} }
TEST_P(WindowPerformanceTest, PageVisibilityChanged) { TEST_F(WindowPerformanceTest, PageVisibilityChanged) {
// Pointerdown // Pointerdown
base::TimeTicks pointerdown_timestamp = GetTimeOrigin(); base::TimeTicks pointerdown_timestamp = GetTimeOrigin();
base::TimeTicks processing_start_pointerdown = GetTimeStamp(1); base::TimeTicks processing_start_pointerdown = GetTimeStamp(1);
@@ -1033,7 +1026,7 @@ TEST_P(WindowPerformanceTest, PageVisibilityChanged) {
EXPECT_EQ(1ul, performance_->interactionCount()); EXPECT_EQ(1ul, performance_->interactionCount());
} }
TEST_P(WindowPerformanceTest, Drag) { TEST_F(WindowPerformanceTest, Drag) {
// Pointerdown // Pointerdown
base::TimeTicks pointerdwon_timestamp = GetTimeOrigin(); base::TimeTicks pointerdwon_timestamp = GetTimeOrigin();
base::TimeTicks processing_start_pointerdown = GetTimeStamp(1); base::TimeTicks processing_start_pointerdown = GetTimeStamp(1);
@@ -1095,7 +1088,7 @@ TEST_P(WindowPerformanceTest, Drag) {
"Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 1); "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 1);
} }
TEST_P(WindowPerformanceTest, Scroll) { TEST_F(WindowPerformanceTest, Scroll) {
// Pointerdown // Pointerdown
base::TimeTicks pointerdown_timestamp = GetTimeOrigin(); base::TimeTicks pointerdown_timestamp = GetTimeOrigin();
base::TimeTicks processing_start_keydown = GetTimeStamp(1); base::TimeTicks processing_start_keydown = GetTimeStamp(1);
@@ -1135,7 +1128,7 @@ TEST_P(WindowPerformanceTest, Scroll) {
"Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0); "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0);
} }
TEST_P(WindowPerformanceTest, TouchesWithoutClick) { TEST_F(WindowPerformanceTest, TouchesWithoutClick) {
base::TimeTicks pointerdown_timestamp = GetTimeOrigin(); base::TimeTicks pointerdown_timestamp = GetTimeOrigin();
// First Pointerdown // First Pointerdown
base::TimeTicks processing_start_pointerdown = GetTimeStamp(1); base::TimeTicks processing_start_pointerdown = GetTimeStamp(1);
@@ -1170,7 +1163,7 @@ TEST_P(WindowPerformanceTest, TouchesWithoutClick) {
// Test artificial pointerup and click on MacOS fall back to use processingEnd // Test artificial pointerup and click on MacOS fall back to use processingEnd
// as event duration ending time. // as event duration ending time.
// See crbug.com/1321819 // See crbug.com/1321819
TEST_P(WindowPerformanceTest, ArtificialPointerupOrClick) { TEST_F(WindowPerformanceTest, ArtificialPointerupOrClick) {
// Arbitrary pointerId picked for testing // Arbitrary pointerId picked for testing
PointerId pointer_id = 4; PointerId pointer_id = 4;
@@ -1235,7 +1228,7 @@ TEST_P(WindowPerformanceTest, ArtificialPointerupOrClick) {
// The trace_analyzer does not work on platforms on which the migration of // The trace_analyzer does not work on platforms on which the migration of
// tracing into Perfetto has not completed. // tracing into Perfetto has not completed.
TEST_P(WindowPerformanceTest, PerformanceMarkTraceEvent) { TEST_F(WindowPerformanceTest, PerformanceMarkTraceEvent) {
v8::HandleScope handle_scope(GetScriptState()->GetIsolate()); v8::HandleScope handle_scope(GetScriptState()->GetIsolate());
v8::Local<v8::Context> context = GetScriptState()->GetContext(); v8::Local<v8::Context> context = GetScriptState()->GetContext();
v8::Context::Scope context_scope(context); v8::Context::Scope context_scope(context);
@@ -1270,7 +1263,7 @@ TEST_P(WindowPerformanceTest, PerformanceMarkTraceEvent) {
ASSERT_TRUE(navigation_id); ASSERT_TRUE(navigation_id);
} }
TEST_P(WindowPerformanceTest, ElementTimingTraceEvent) { TEST_F(WindowPerformanceTest, ElementTimingTraceEvent) {
using trace_analyzer::Query; using trace_analyzer::Query;
trace_analyzer::Start("*"); trace_analyzer::Start("*");
// |element| needs to be non-null to prevent a crash. // |element| needs to be non-null to prevent a crash.
@@ -1311,7 +1304,7 @@ TEST_P(WindowPerformanceTest, ElementTimingTraceEvent) {
EXPECT_EQ(*url, "url"); EXPECT_EQ(*url, "url");
} }
TEST_P(WindowPerformanceTest, EventTimingTraceEvents) { TEST_F(WindowPerformanceTest, EventTimingTraceEvents) {
using trace_analyzer::Query; using trace_analyzer::Query;
trace_analyzer::Start("*"); trace_analyzer::Start("*");
base::TimeTicks start_time = GetTimeOrigin() + base::Seconds(1); base::TimeTicks start_time = GetTimeOrigin() + base::Seconds(1);
@@ -1410,7 +1403,7 @@ TEST_P(WindowPerformanceTest, EventTimingTraceEvents) {
EXPECT_FALSE(click_begin->other_event->HasDictArg("data")); EXPECT_FALSE(click_begin->other_event->HasDictArg("data"));
} }
TEST_P(WindowPerformanceTest, SlowInteractionToNextPaintTraceEvents) { TEST_F(WindowPerformanceTest, SlowInteractionToNextPaintTraceEvents) {
using trace_analyzer::Query; using trace_analyzer::Query;
trace_analyzer::Start("*"); trace_analyzer::Start("*");
@@ -1528,7 +1521,7 @@ TEST_P(WindowPerformanceTest, SlowInteractionToNextPaintTraceEvents) {
EXPECT_EQ(base::ClampRound(events[2]->GetAbsTimeToOtherEvent()), 600000); EXPECT_EQ(base::ClampRound(events[2]->GetAbsTimeToOtherEvent()), 600000);
} }
TEST_P(WindowPerformanceTest, InteractionID) { TEST_F(WindowPerformanceTest, InteractionID) {
// Keyboard with max duration 25, total duration 40. // Keyboard with max duration 25, total duration 40.
PerformanceEventTiming* keydown_entry = PerformanceEventTiming* keydown_entry =
CreatePerformanceEventTiming(event_type_names::kKeydown, 1, std::nullopt, CreatePerformanceEventTiming(event_type_names::kKeydown, 1, std::nullopt,
@@ -1618,8 +1611,6 @@ TEST_P(WindowPerformanceTest, InteractionID) {
} }
} }
INSTANTIATE_TEST_SUITE_P(All, WindowPerformanceTest, ::testing::Bool());
class InteractionIdTest : public WindowPerformanceTest { class InteractionIdTest : public WindowPerformanceTest {
public: public:
struct EventForInteraction { struct EventForInteraction {
@@ -1693,7 +1684,7 @@ class InteractionIdTest : public WindowPerformanceTest {
}; };
// Tests English typing. // Tests English typing.
TEST_P(InteractionIdTest, InputOutsideComposition) { TEST_F(InteractionIdTest, InputOutsideComposition) {
// Insert "a" with a max duration of 50 and total of 50. // Insert "a" with a max duration of 50 and total of 50.
std::vector<EventForInteraction> events1 = { std::vector<EventForInteraction> events1 = {
{event_type_names::kKeydown, 65, std::nullopt, GetTimeStamp(100), {event_type_names::kKeydown, 65, std::nullopt, GetTimeStamp(100),
@@ -1745,7 +1736,7 @@ TEST_P(InteractionIdTest, InputOutsideComposition) {
} }
// Tests Japanese on Mac. // Tests Japanese on Mac.
TEST_P(InteractionIdTest, CompositionSingleKeydown) { TEST_F(InteractionIdTest, CompositionSingleKeydown) {
// Insert "a" with a duration of 20. // Insert "a" with a duration of 20.
std::vector<EventForInteraction> events1 = { std::vector<EventForInteraction> events1 = {
{event_type_names::kKeydown, 229, std::nullopt, GetTimeStamp(100), {event_type_names::kKeydown, 229, std::nullopt, GetTimeStamp(100),
@@ -1770,49 +1761,31 @@ TEST_P(InteractionIdTest, CompositionSingleKeydown) {
{event_type_names::kCompositionend, std::nullopt, std::nullopt}}; {event_type_names::kCompositionend, std::nullopt, std::nullopt}};
std::vector<uint32_t> ids2 = SimulateInteractionIds(events2); std::vector<uint32_t> ids2 = SimulateInteractionIds(events2);
if (base::FeatureList::IsEnabled( performance_->GetResponsivenessMetrics().FlushAllEventsForTesting();
features::kEventTimingKeypressAndCompositionInteractionId)) {
performance_->GetResponsivenessMetrics().FlushAllEventsForTesting();
EXPECT_GT(ids1[0], 0u) << "Keydown interactionId was nonzero"; EXPECT_GT(ids1[0], 0u) << "Keydown interactionId was nonzero";
EXPECT_EQ(ids1[1], 0u) << "Compositionstart interactionId was zero"; EXPECT_EQ(ids1[1], 0u) << "Compositionstart interactionId was zero";
EXPECT_GT(ids1[3], 0u) << "Input interactionId was nonzero"; EXPECT_GT(ids1[3], 0u) << "Input interactionId was nonzero";
EXPECT_GT(ids1[4], 0u) << "Keyup interactionId was nonzero"; EXPECT_GT(ids1[4], 0u) << "Keyup interactionId was nonzero";
EXPECT_EQ(ids1[0], ids1[3]) EXPECT_EQ(ids1[0], ids1[3])
<< "Keydown and Input have the same interactionIds"; << "Keydown and Input have the same interactionIds";
EXPECT_GT(ids2[0], 0u) << "Second keydown interactionId was nonzero"; EXPECT_GT(ids2[0], 0u) << "Second keydown interactionId was nonzero";
EXPECT_GT(ids2[2], 0u) << "Second input interactionId was nonzero"; EXPECT_GT(ids2[2], 0u) << "Second input interactionId was nonzero";
EXPECT_GT(ids2[3], 0u) << "Second keyup interactionId was non zero"; EXPECT_GT(ids2[3], 0u) << "Second keyup interactionId was non zero";
EXPECT_EQ(ids2[4], 0u) << "Compositionend interactionId was zero"; EXPECT_EQ(ids2[4], 0u) << "Compositionend interactionId was zero";
EXPECT_EQ(ids2[0], ids2[2]) EXPECT_EQ(ids2[0], ids2[2])
<< "Keydown and Input have the same interactionIds"; << "Keydown and Input have the same interactionIds";
EXPECT_NE(ids1[3], ids2[2]) EXPECT_NE(ids1[3], ids2[2])
<< "First and second inputs have different interactionIds"; << "First and second inputs have different interactionIds";
CheckUKMValues({{100, 120, UserInteractionType::kKeyboard}, CheckUKMValues({{100, 120, UserInteractionType::kKeyboard},
{100, 170, UserInteractionType::kKeyboard}}); {100, 170, UserInteractionType::kKeyboard}});
} else {
EXPECT_EQ(ids1[0], 0u) << "Keydown interactionId was zero";
EXPECT_EQ(ids1[1], 0u) << "Compositionstart interactionId was zero";
EXPECT_GT(ids1[3], 0u) << "Input interactionId was nonzero";
EXPECT_EQ(ids1[4], 0u) << "Keyup interactionId was zero";
EXPECT_EQ(ids2[0], 0u) << "Second keydown interactionId was zero";
EXPECT_GT(ids2[2], 0u) << "Second input interactionId was nonzero";
EXPECT_EQ(ids2[3], 0u) << "Second keyup interactionId was zero";
EXPECT_EQ(ids2[4], 0u) << "Compositionend interactionId was zero";
EXPECT_NE(ids1[3], ids2[2])
<< "First and second inputs have different interactionIds";
CheckUKMValues({{20, 20, UserInteractionType::kKeyboard},
{30, 30, UserInteractionType::kKeyboard}});
}
} }
// Tests Chinese on Mac. Windows is similar, but has more keyups inside the // Tests Chinese on Mac. Windows is similar, but has more keyups inside the
// composition. // composition.
TEST_P(InteractionIdTest, CompositionToFinalInput) { TEST_F(InteractionIdTest, CompositionToFinalInput) {
// Insert "a" with a duration of 25. // Insert "a" with a duration of 25.
std::vector<EventForInteraction> events1 = { std::vector<EventForInteraction> events1 = {
{event_type_names::kKeydown, 229, std::nullopt, GetTimeStamp(100), {event_type_names::kKeydown, 229, std::nullopt, GetTimeStamp(100),
@@ -1854,22 +1827,15 @@ TEST_P(InteractionIdTest, CompositionToFinalInput) {
EXPECT_NE(ids2[2], ids3[1]) EXPECT_NE(ids2[2], ids3[1])
<< "Second and third inputs have different interactionIds"; << "Second and third inputs have different interactionIds";
if (base::FeatureList::IsEnabled( performance_->GetResponsivenessMetrics().FlushAllEventsForTesting();
features::kEventTimingKeypressAndCompositionInteractionId)) {
performance_->GetResponsivenessMetrics().FlushAllEventsForTesting();
CheckUKMValues({{90, 90, UserInteractionType::kKeyboard}, CheckUKMValues({{90, 90, UserInteractionType::kKeyboard},
{90, 90, UserInteractionType::kKeyboard}, {90, 90, UserInteractionType::kKeyboard},
{140, 140, UserInteractionType::kKeyboard}}); {140, 140, UserInteractionType::kKeyboard}});
} else {
CheckUKMValues({{25, 25, UserInteractionType::kKeyboard},
{35, 35, UserInteractionType::kKeyboard},
{140, 140, UserInteractionType::kKeyboard}});
}
} }
// Tests Chinese on Windows. // Tests Chinese on Windows.
TEST_P(InteractionIdTest, CompositionToFinalInputMultipleKeyUps) { TEST_F(InteractionIdTest, CompositionToFinalInputMultipleKeyUps) {
// Insert "a" with a duration of 66. // Insert "a" with a duration of 66.
std::vector<EventForInteraction> events1 = { std::vector<EventForInteraction> events1 = {
{event_type_names::kKeydown, 229, std::nullopt, GetTimeStamp(0), {event_type_names::kKeydown, 229, std::nullopt, GetTimeStamp(0),
@@ -1905,52 +1871,29 @@ TEST_P(InteractionIdTest, CompositionToFinalInputMultipleKeyUps) {
{event_type_names::kCompositionend, std::nullopt, std::nullopt}}; {event_type_names::kCompositionend, std::nullopt, std::nullopt}};
std::vector<uint32_t> ids3 = SimulateInteractionIds(events3); std::vector<uint32_t> ids3 = SimulateInteractionIds(events3);
if (base::FeatureList::IsEnabled( performance_->GetResponsivenessMetrics().FlushAllEventsForTesting();
features::kEventTimingKeypressAndCompositionInteractionId)) { EXPECT_GT(ids1[3], 0u) << "First input nonzero";
performance_->GetResponsivenessMetrics().FlushAllEventsForTesting(); EXPECT_GT(ids1[4], 0u) << "First keyup has nonzero interactionId";
EXPECT_GT(ids1[3], 0u) << "First input nonzero"; EXPECT_GT(ids1[5], 0u) << "Second keyup has nonzero interactionId";
EXPECT_GT(ids1[4], 0u) << "First keyup has nonzero interactionId";
EXPECT_GT(ids1[5], 0u) << "Second keyup has nonzero interactionId";
EXPECT_GT(ids2[2], 0u) << "Second input nonzero"; EXPECT_GT(ids2[2], 0u) << "Second input nonzero";
EXPECT_NE(ids1[3], ids2[2]) EXPECT_NE(ids1[3], ids2[2])
<< "First and second input have different interactionIds"; << "First and second input have different interactionIds";
EXPECT_GT(ids2[3], 0u) << "Third keyup has nonzero interactionId"; EXPECT_GT(ids2[3], 0u) << "Third keyup has nonzero interactionId";
EXPECT_GT(ids2[4], 0u) << "Fourth keyup has nonzero interactionId"; EXPECT_GT(ids2[4], 0u) << "Fourth keyup has nonzero interactionId";
EXPECT_GT(ids3[1], 0u) << "Third input has nonzero interactionId"; EXPECT_GT(ids3[1], 0u) << "Third input has nonzero interactionId";
EXPECT_NE(ids1[3], ids3[1]) EXPECT_NE(ids1[3], ids3[1])
<< "First and third inputs have different interactionIds"; << "First and third inputs have different interactionIds";
EXPECT_NE(ids2[2], ids3[1]) EXPECT_NE(ids2[2], ids3[1])
<< "Second and third inputs have different interactionIds"; << "Second and third inputs have different interactionIds";
CheckUKMValues({{100, 100, UserInteractionType::kKeyboard}, CheckUKMValues({{100, 100, UserInteractionType::kKeyboard},
{100, 100, UserInteractionType::kKeyboard}, {100, 100, UserInteractionType::kKeyboard},
{85, 85, UserInteractionType::kKeyboard}}); {85, 85, UserInteractionType::kKeyboard}});
} else {
EXPECT_GT(ids1[3], 0u) << "First input nonzero";
EXPECT_EQ(ids1[4], 0u) << "First keyup has zero interactionId";
EXPECT_EQ(ids1[5], 0u) << "Second keyup has zero interactionId";
EXPECT_GT(ids2[2], 0u) << "Second input nonzero";
EXPECT_NE(ids1[3], ids2[2])
<< "First and second input have different interactionIds";
EXPECT_EQ(ids2[3], 0u) << "Third keyup has zero interactionId";
EXPECT_EQ(ids2[4], 0u) << "Fourth keyup has zero interactionId";
EXPECT_GT(ids3[1], 0u) << "Third input has nonzero interactionId";
EXPECT_NE(ids1[3], ids3[1])
<< "First and third inputs have different interactionIds";
EXPECT_NE(ids2[2], ids3[1])
<< "Second and third inputs have different interactionIds";
CheckUKMValues({{66, 66, UserInteractionType::kKeyboard},
{51, 51, UserInteractionType::kKeyboard},
{85, 85, UserInteractionType::kKeyboard}});
}
} }
// Tests Android smart suggestions (similar to Android Chinese). // Tests Android smart suggestions (similar to Android Chinese).
TEST_P(InteractionIdTest, SmartSuggestion) { TEST_F(InteractionIdTest, SmartSuggestion) {
// Insert "A" with a duration of 9. // Insert "A" with a duration of 9.
std::vector<EventForInteraction> events1 = { std::vector<EventForInteraction> events1 = {
{event_type_names::kKeydown, 229, std::nullopt, GetTimeStamp(0), {event_type_names::kKeydown, 229, std::nullopt, GetTimeStamp(0),
@@ -1982,42 +1925,24 @@ TEST_P(InteractionIdTest, SmartSuggestion) {
GetTimeStamp(270)}}; GetTimeStamp(270)}};
std::vector<uint32_t> ids3 = SimulateInteractionIds(events3); std::vector<uint32_t> ids3 = SimulateInteractionIds(events3);
if (base::FeatureList::IsEnabled( performance_->GetResponsivenessMetrics().FlushAllEventsForTesting();
features::kEventTimingKeypressAndCompositionInteractionId)) { EXPECT_GT(ids1[3], 0u) << "First input nonzero";
performance_->GetResponsivenessMetrics().FlushAllEventsForTesting(); EXPECT_EQ(ids1[0], ids1[3]) << "Keydown and input have the same id";
EXPECT_GT(ids1[3], 0u) << "First input nonzero"; EXPECT_EQ(ids1[0], ids1[3]) << "Keydown and keyup have the same id";
EXPECT_EQ(ids1[0], ids1[3]) << "Keydown and input have the same id";
EXPECT_EQ(ids1[0], ids1[3]) << "Keydown and keyup have the same id";
EXPECT_GT(ids2[1], 0u) << "Second input nonzero"; EXPECT_GT(ids2[1], 0u) << "Second input nonzero";
EXPECT_NE(ids1[3], ids2[1]) EXPECT_NE(ids1[3], ids2[1])
<< "First and second input have different interactionIds"; << "First and second input have different interactionIds";
EXPECT_GT(ids3[0], 0u) << "Keydown nonzero"; EXPECT_GT(ids3[0], 0u) << "Keydown nonzero";
EXPECT_EQ(ids3[0], ids3[2]) << "Keydown and keyup have some id"; EXPECT_EQ(ids3[0], ids3[2]) << "Keydown and keyup have some id";
EXPECT_EQ(ids3[1], 0u) << "Third input has zero id"; EXPECT_EQ(ids3[1], 0u) << "Third input has zero id";
CheckUKMValues({{16, 16, UserInteractionType::kKeyboard}, CheckUKMValues({{16, 16, UserInteractionType::kKeyboard},
{14, 14, UserInteractionType::kKeyboard}, {14, 14, UserInteractionType::kKeyboard},
{43, 70, UserInteractionType::kKeyboard}}); {43, 70, UserInteractionType::kKeyboard}});
} else {
EXPECT_GT(ids1[3], 0u) << "First input nonzero";
EXPECT_GT(ids2[1], 0u) << "Second input nonzero";
EXPECT_NE(ids1[3], ids2[1])
<< "First and second input have different interactionIds";
EXPECT_GT(ids3[0], 0u) << "Keydown nonzero";
EXPECT_EQ(ids3[0], ids3[2]) << "Keydown and keyup have some id";
EXPECT_EQ(ids3[1], 0u) << "Third input has zero id";
CheckUKMValues({{9, 9, UserInteractionType::kKeyboard},
{14, 14, UserInteractionType::kKeyboard},
{43, 70, UserInteractionType::kKeyboard}});
}
} }
TEST_P(InteractionIdTest, TapWithoutClick) { TEST_F(InteractionIdTest, TapWithoutClick) {
std::vector<EventForInteraction> events = { std::vector<EventForInteraction> events = {
{event_type_names::kPointerdown, std::nullopt, 1, GetTimeStamp(100), {event_type_names::kPointerdown, std::nullopt, 1, GetTimeStamp(100),
GetTimeStamp(140)}, GetTimeStamp(140)},
@@ -2038,7 +1963,7 @@ TEST_P(InteractionIdTest, TapWithoutClick) {
CheckUKMValues({{40, 50, UserInteractionType::kTapOrClick}}); CheckUKMValues({{40, 50, UserInteractionType::kTapOrClick}});
} }
TEST_P(InteractionIdTest, PointerupClick) { TEST_F(InteractionIdTest, PointerupClick) {
std::vector<EventForInteraction> events = { std::vector<EventForInteraction> events = {
{event_type_names::kPointerup, std::nullopt, 1, GetTimeStamp(100), {event_type_names::kPointerup, std::nullopt, 1, GetTimeStamp(100),
GetTimeStamp(140)}, GetTimeStamp(140)},
@@ -2052,7 +1977,7 @@ TEST_P(InteractionIdTest, PointerupClick) {
CheckUKMValues({{30, 30, UserInteractionType::kTapOrClick}}); CheckUKMValues({{30, 30, UserInteractionType::kTapOrClick}});
} }
TEST_P(InteractionIdTest, JustClick) { TEST_F(InteractionIdTest, JustClick) {
// Hitting enter on a keyboard may cause just a trusted click event. // Hitting enter on a keyboard may cause just a trusted click event.
std::vector<EventForInteraction> events = { std::vector<EventForInteraction> events = {
{event_type_names::kClick, std::nullopt, 0, GetTimeStamp(120), {event_type_names::kClick, std::nullopt, 0, GetTimeStamp(120),
@@ -2064,7 +1989,7 @@ TEST_P(InteractionIdTest, JustClick) {
CheckUKMValues({{30, 30, UserInteractionType::kTapOrClick}}); CheckUKMValues({{30, 30, UserInteractionType::kTapOrClick}});
} }
TEST_P(InteractionIdTest, PointerdownClick) { TEST_F(InteractionIdTest, PointerdownClick) {
// Contextmenus may cause us to only see pointerdown and click (no pointerup). // Contextmenus may cause us to only see pointerdown and click (no pointerup).
std::vector<EventForInteraction> events = { std::vector<EventForInteraction> events = {
{event_type_names::kPointerdown, std::nullopt, 1, GetTimeStamp(100), {event_type_names::kPointerdown, std::nullopt, 1, GetTimeStamp(100),
@@ -2079,7 +2004,7 @@ TEST_P(InteractionIdTest, PointerdownClick) {
CheckUKMValues({{40, 50, UserInteractionType::kTapOrClick}}); CheckUKMValues({{40, 50, UserInteractionType::kTapOrClick}});
} }
TEST_P(InteractionIdTest, MultiTouch) { TEST_F(InteractionIdTest, MultiTouch) {
// In multitouch, we report an interaction per pointerId. We do not see // In multitouch, we report an interaction per pointerId. We do not see
// clicks. // clicks.
std::vector<EventForInteraction> events = { std::vector<EventForInteraction> events = {
@@ -2104,7 +2029,7 @@ TEST_P(InteractionIdTest, MultiTouch) {
{50, 60, UserInteractionType::kTapOrClick}}); {50, 60, UserInteractionType::kTapOrClick}});
} }
TEST_P(InteractionIdTest, ClickIncorrectPointerId) { TEST_F(InteractionIdTest, ClickIncorrectPointerId) {
// On mobile, in cases where touchstart is skipped, click does not get the // On mobile, in cases where touchstart is skipped, click does not get the
// correct pointerId. See crbug.com/1264930 for more details. // correct pointerId. See crbug.com/1264930 for more details.
// TODO crbug.com/359679950: remove this test and event timing workaround // TODO crbug.com/359679950: remove this test and event timing workaround
@@ -2122,6 +2047,4 @@ TEST_P(InteractionIdTest, ClickIncorrectPointerId) {
CheckUKMValues({{40, 40, UserInteractionType::kTapOrClick}}); CheckUKMValues({{40, 40, UserInteractionType::kTapOrClick}});
} }
INSTANTIATE_TEST_SUITE_P(All, InteractionIdTest, ::testing::Bool());
} // namespace blink } // namespace blink