[go: nahoru, domu]

blob: 7aa710e349b9bc2a4fdbba2061406b5e4eca2188 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_RESPONSIVENESS_METRICS_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_RESPONSIVENESS_METRICS_H_
#include <optional>
#include "base/time/time.h"
#include "base/trace_event/typed_macros.h"
#include "base/tracing/protos/chrome_track_event.pbzero.h"
#include "third_party/blink/public/common/responsiveness_metrics/user_interaction_latency.h"
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
#include "third_party/blink/renderer/core/events/pointer_event.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
#include "third_party/blink/renderer/platform/wtf/vector_traits.h"
#include "third_party/perfetto/include/perfetto/tracing/event_context.h"
namespace blink {
class PerformanceEventTiming;
class WindowPerformance;
class CORE_EXPORT ResponsivenessMetrics
: public GarbageCollected<ResponsivenessMetrics> {
public:
// Timestamps for input events.
struct EventTimestamps {
// The duration of the event (creation --> first display update it caused).
base::TimeDelta duration() const { return end_time - start_time; }
// The event creation time.
base::TimeTicks start_time;
// The time when the first display update caused by the input event was
// performed.
base::TimeTicks end_time;
// The time when the original WebInputEvent was queued on main thread.
base::TimeTicks main_thread_queued_time;
};
// Wrapper class to store interactionId, interaction offset, and timestamps
// of an entry on a HashMap. It is optimized and used only in the experimental
// SetKeyIdAndRecordLatency function. (SetKeyIdAndRecordLatencyExperimental)
class InteractionInfo {
public:
InteractionInfo(uint32_t interaction_id,
uint32_t interaction_offset,
EventTimestamps timestamps)
: interaction_id_(interaction_id),
interaction_offset_(interaction_offset),
timestamps_({timestamps}) {}
InteractionInfo() = default;
~InteractionInfo() = default;
uint32_t GetInteractionId() const { return interaction_id_; }
uint32_t GetInteractionOffset() const { return interaction_offset_; }
void SetInteractionIdAndOffset(uint32_t interaction_id,
uint32_t interaction_offset) {
interaction_id_ = interaction_id;
interaction_offset_ = interaction_offset;
}
Vector<EventTimestamps> const& GetTimeStamps() { return timestamps_; }
bool Empty() { return timestamps_.empty(); }
void AddTimestamps(EventTimestamps timestamp) {
timestamps_.push_back(timestamp);
}
void Clear() {
interaction_id_ = 0;
interaction_offset_ = 0;
timestamps_.clear();
}
private:
// InteractionId associated with the entry.
uint32_t interaction_id_ = 0;
uint32_t interaction_offset_ = 0;
// Timestamps associated with the entries of the same interaction.
Vector<EventTimestamps> timestamps_;
};
// Wrapper class to store PerformanceEventTiming and timestamps
// on a HeapHashMap.
class KeyboardEntryAndTimestamps
: public GarbageCollected<KeyboardEntryAndTimestamps> {
public:
KeyboardEntryAndTimestamps(PerformanceEventTiming* entry,
EventTimestamps timestamps)
: entry_(entry), timestamps_({timestamps}) {}
static KeyboardEntryAndTimestamps* Create(PerformanceEventTiming* entry,
EventTimestamps timestamps) {
return MakeGarbageCollected<KeyboardEntryAndTimestamps>(entry,
timestamps);
}
~KeyboardEntryAndTimestamps() = default;
void Trace(Visitor*) const;
PerformanceEventTiming* GetEntry() const { return entry_.Get(); }
EventTimestamps GetTimeStamps() { return timestamps_; }
private:
// The PerformanceEventTiming entry that has not been sent to observers
// yet: the event dispatch has been completed but the presentation promise
// used to determine |duration| has not yet been resolved, or the
// interactionId has not yet been computed yet.
Member<PerformanceEventTiming> entry_;
// Timestamps associated with the entry.
EventTimestamps timestamps_;
};
// Wrapper class to store PerformanceEventTiming, pointerdown and pointerup
// timestamps, and whether drag has been detected on a HeapHashMap.
class PointerEntryAndInfo : public GarbageCollected<PointerEntryAndInfo> {
public:
PointerEntryAndInfo(PerformanceEventTiming* entry,
EventTimestamps timestamps)
: entry_(entry), timestamps_({timestamps}) {}
static PointerEntryAndInfo* Create(PerformanceEventTiming* entry,
EventTimestamps timestamps) {
return MakeGarbageCollected<PointerEntryAndInfo>(entry, timestamps);
}
~PointerEntryAndInfo() = default;
void Trace(Visitor*) const;
PerformanceEventTiming* GetEntry() const { return entry_.Get(); }
Vector<EventTimestamps>& GetTimeStamps() { return timestamps_; }
void SetIsDrag() { is_drag_ = true; }
bool IsDrag() const { return is_drag_; }
private:
// The PerformanceEventTiming entry that has not been sent to observers
// yet: the event dispatch has been completed but the presentation promise
// used to determine |duration| has not yet been resolved, , or the
// interactionId has not yet been computed yet.
Member<PerformanceEventTiming> entry_;
// Timestamps associated with the entry. The first should always be
// for a pointerdown, the second for a pointerup, and optionally the third
// for a click.
Vector<EventTimestamps> timestamps_;
// Whether drag has been detected.
bool is_drag_;
};
explicit ResponsivenessMetrics(WindowPerformance*);
~ResponsivenessMetrics();
// Flush UKM timestamps of composition events for testing.
void FlushAllEventsForTesting();
// Stop UKM sampling for testing.
void StopUkmSamplingForTesting() { sampling_ = false; }
// The use might be dragging. The function will be called whenever we have a
// pointermove.
void NotifyPotentialDrag(PointerId pointer_id);
// Assigns an interactionId and records interaction latency for pointer
// events. Returns true if the entry is ready to be surfaced in
// PerformanceObservers and the Performance Timeline.
bool SetPointerIdAndRecordLatency(PerformanceEventTiming* entry,
PointerId pointer_id,
EventTimestamps event_timestamps);
// Assigns interactionId and records interaction latency for keyboard events.
// We care about input, compositionstart, and compositionend events, so
// |key_code| will be std::nullopt in those cases. Returns true if the entry
// would be ready to be surfaced in PerformanceObservers and the Performance
// Timeline.
bool SetKeyIdAndRecordLatency(PerformanceEventTiming* entry,
std::optional<int> key_code,
EventTimestamps event_timestamps);
// Experimental function that in addition to SetKeyIdAndRecordLatency()
// exposes interactionId for keypress and keyup/keydown under composition.
bool SetKeyIdAndRecordLatencyExperimental(PerformanceEventTiming* entry,
std::optional<int> key_code,
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();
uint32_t GetInteractionCount() const;
void Trace(Visitor*) const;
perfetto::protos::pbzero::WebContentInteraction::Type
UserInteractionTypeToProto(UserInteractionType interaction_type) const;
void EmitInteractionToNextPaintTraceEvent(
const ResponsivenessMetrics::EventTimestamps& event,
UserInteractionType interaction_type,
base::TimeDelta total_event_duration);
void SetCurrentInteractionEventQueuedTimestamp(base::TimeTicks queued_time);
base::TimeTicks CurrentInteractionEventQueuedTimestamp() const;
private:
// Record UKM for user interaction latencies.
void RecordUserInteractionUKM(
LocalDOMWindow* window,
UserInteractionType interaction_type,
const WTF::Vector<ResponsivenessMetrics::EventTimestamps>& timestamps,
uint32_t interaction_offset);
void RecordDragTapOrClickUKM(LocalDOMWindow*, PointerEntryAndInfo&);
void RecordKeyboardUKM(LocalDOMWindow* window,
const WTF::Vector<EventTimestamps>& event_timestamps,
uint32_t interaction_offset);
// Updates the interactionId counter which is used by Event Timing.
void UpdateInteractionId();
uint32_t GetCurrentInteractionId() const;
// Method called when |pointer_flush_timer_| fires. Ensures that the last
// interaction of any given pointerId is reported, even if it does not receive
// a click.
void FlushPointerTimerFired(TimerBase*);
// Method called when |contextmenu_flush_timer_| fires. Ensures that the last
// pointerdown or keydown is reported, even if it does not receive a pointerup
// nor keyup.
void ContextmenuFlushTimerFired(TimerBase*);
// Used to flush any entries in |pointer_id_entry_map_| which already have
// pointerup. We either know there is no click happening or waited long enough
// for a click to occur.
void FlushPointerup();
// Used to flush all entries in |pointer_id_entry_map_|.
void FlushPointerdownAndPointerup();
// Method called when |composition_end_| fires. Ensures that the last
// interaction of compositoin events is reported, even if
// there is no following keydown.
void FlushCompositionEndTimerFired(TimerBase*);
// Used to flush any entries in |keyboard_sequence_based_timestamps_to_UKM_|
void FlushSequenceBasedKeyboardEvents();
void NotifyPointerdown(PerformanceEventTiming* entry) const;
// Indicates if a key is being held for a sustained period of time
bool IsHoldingKey(std::optional<int> key_code);
Member<WindowPerformance> window_performance_;
// Map from keyCodes to interaction info (ID, offset, and timestamps).
HashMap<int, InteractionInfo, IntWithZeroKeyHashTraits<int>>
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
// interactionId for keydown and keyup events. When we are composing, we set
// interactionId for input events.
bool composition_started_ = false;
enum CompositionState {
kNonComposition,
kCompositionContinueOngoingInteraction,
kCompositionStartNewInteractionOnKeydown,
kCompositionStartNewInteractionOnInput,
kEndCompositionOnKeydown
};
CompositionState composition_state_ = kNonComposition;
std::optional<int> last_keydown_keycode_;
// InteractionInfo storing interactionId, interaction offset, and timestamps
// of entries for reporting them to UKM in 3 main cases:
// 1) Pressing a key under composition.
// 2) Holding a key under composition.
// 3) Holding a key under no composition.
InteractionInfo sequence_based_keyboard_interaction_info_;
// Map from pointerId to the first pointer event entry seen for the user
// interaction, and other information.
HeapHashMap<PointerId,
Member<PointerEntryAndInfo>,
IntWithZeroKeyHashTraits<PointerId>>
pointer_id_entry_map_;
HeapTaskRunnerTimer<ResponsivenessMetrics> pointer_flush_timer_;
HeapTaskRunnerTimer<ResponsivenessMetrics> contextmenu_flush_timer_;
HeapTaskRunnerTimer<ResponsivenessMetrics> composition_end_flush_timer_;
// The PointerId of the last pointerdown or pointerup event processed. Used to
// know which interactionId to use for click events. If pointecancel or
// keyboard events are seen, the value is reset. TODO(crbug.com/1264930):
// remove this attribute once PointerId for clicks correctly points to the
// same value as its corresponding pointerdown and pointerup.
std::optional<PointerId> last_pointer_id_;
// Queued timestamp of current event being dispatched.
base::TimeTicks current_interaction_event_queued_timestamp_;
uint32_t current_interaction_id_for_event_timing_;
uint32_t interaction_count_ = 0;
// Whether to perform UKM sampling.
bool sampling_ = true;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_RESPONSIVENESS_METRICS_H_