diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-26 13:57:00 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-11-02 11:31:01 +0000 |
commit | 1943b3c2a1dcee36c233724fc4ee7613d71b9cf6 (patch) | |
tree | 8c1b5f12357025c197da5427ae02cfdc2f3570d6 /chromium/third_party/blink/renderer/core/timing | |
parent | 21ba0c5d4bf8fba15dddd97cd693bad2358b77fd (diff) | |
download | qtwebengine-chromium-1943b3c2a1dcee36c233724fc4ee7613d71b9cf6.tar.gz |
BASELINE: Update Chromium to 94.0.4606.111
Change-Id: I924781584def20fc800bedf6ff41fdb96c438193
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/core/timing')
38 files changed, 1188 insertions, 331 deletions
diff --git a/chromium/third_party/blink/renderer/core/timing/background_tracing_helper.cc b/chromium/third_party/blink/renderer/core/timing/background_tracing_helper.cc index 0202a46217c..319f6562b67 100644 --- a/chromium/third_party/blink/renderer/core/timing/background_tracing_helper.cc +++ b/chromium/third_party/blink/renderer/core/timing/background_tracing_helper.cc @@ -4,9 +4,9 @@ #include "third_party/blink/renderer/core/timing/background_tracing_helper.h" +#include "base/cxx17_backports.h" #include "base/feature_list.h" #include "base/hash/md5.h" -#include "base/stl_util.h" #include "base/sys_byteorder.h" #include "base/trace_event/typed_macros.h" #include "third_party/blink/public/common/features.h" @@ -173,17 +173,26 @@ void BackgroundTracingHelper::MaybeEmitBackgroundTracingPerformanceMarkEvent( if (!mark_hashes_->Contains(mark_hash)) return; - // Emit the trace event. We emit hashes and strings to facilitate local trace + // Emit the trace events. We emit hashes and strings to facilitate local trace // consumption. However, the strings will be stripped and only the hashes // shipped externally. - TRACE_EVENT("blink", "performance.mark", [&](perfetto::EventContext ctx) { + + auto event_lambda = [&](perfetto::EventContext ctx) { auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>(); auto* data = event->set_chrome_hashed_performance_mark(); data->set_site_hash(site_hash_); data->set_site(site_.Ascii()); data->set_mark_hash(mark_hash); data->set_mark(mark_name_ascii); - }); + }; + + // For additional context, also emit a paired event marking *when* the + // performance.mark was actually created. + TRACE_EVENT_INSTANT("blink", "performance.mark.created", event_lambda); + + // Emit an event with the actual timestamp associated with the mark. + TRACE_EVENT_INSTANT("blink", "performance.mark", mark.UnsafeTimeForTraces(), + event_lambda); // If this is a slow-reports trigger then fire it. if (MarkNameIsTrigger(mark_name)) { diff --git a/chromium/third_party/blink/renderer/core/timing/build.gni b/chromium/third_party/blink/renderer/core/timing/build.gni index c12d93beb64..c39d5d343c2 100644 --- a/chromium/third_party/blink/renderer/core/timing/build.gni +++ b/chromium/third_party/blink/renderer/core/timing/build.gni @@ -39,6 +39,8 @@ blink_core_sources_timing = [ "performance_navigation.h", "performance_navigation_timing.cc", "performance_navigation_timing.h", + "performance_navigation_timing_activation_start.cc", + "performance_navigation_timing_activation_start.h", "performance_observer.cc", "performance_observer.h", "performance_observer_entry_list.cc", @@ -59,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", diff --git a/chromium/third_party/blink/renderer/core/timing/dom_window_performance.h b/chromium/third_party/blink/renderer/core/timing/dom_window_performance.h index 26be96f656c..bcb8383b6f6 100644 --- a/chromium/third_party/blink/renderer/core/timing/dom_window_performance.h +++ b/chromium/third_party/blink/renderer/core/timing/dom_window_performance.h @@ -5,7 +5,6 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_DOM_WINDOW_PERFORMANCE_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_DOM_WINDOW_PERFORMANCE_H_ -#include "base/macros.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/timing/window_performance.h" #include "third_party/blink/renderer/platform/heap/handle.h" @@ -25,6 +24,8 @@ class CORE_EXPORT DOMWindowPerformance final static WindowPerformance* performance(LocalDOMWindow&); explicit DOMWindowPerformance(LocalDOMWindow&); + DOMWindowPerformance(const DOMWindowPerformance&) = delete; + DOMWindowPerformance& operator=(const DOMWindowPerformance&) = delete; void Trace(Visitor*) const override; @@ -32,7 +33,6 @@ class CORE_EXPORT DOMWindowPerformance final WindowPerformance* performance(); Member<WindowPerformance> performance_; - DISALLOW_COPY_AND_ASSIGN(DOMWindowPerformance); }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/event_counts.idl b/chromium/third_party/blink/renderer/core/timing/event_counts.idl index a78ac1fdc8a..9da127ce83c 100644 --- a/chromium/third_party/blink/renderer/core/timing/event_counts.idl +++ b/chromium/third_party/blink/renderer/core/timing/event_counts.idl @@ -1,4 +1,4 @@ -[Exposed=Window, RuntimeEnabled=EventTiming] +[Exposed=Window] interface EventCounts { readonly maplike<DOMString, unsigned long long>; }; diff --git a/chromium/third_party/blink/renderer/core/timing/event_timing.cc b/chromium/third_party/blink/renderer/core/timing/event_timing.cc index 4f57dcb2401..95143ba9c06 100644 --- a/chromium/third_party/blink/renderer/core/timing/event_timing.cc +++ b/chromium/third_party/blink/renderer/core/timing/event_timing.cc @@ -14,7 +14,6 @@ #include "third_party/blink/renderer/core/loader/interactive_detector.h" #include "third_party/blink/renderer/core/timing/dom_window_performance.h" #include "third_party/blink/renderer/core/timing/performance_event_timing.h" -#include "third_party/blink/renderer/platform/runtime_enabled_features.h" namespace blink { namespace { @@ -30,17 +29,14 @@ bool ShouldLogEvent(const Event& event) { event.type() == event_type_names::kPointerup || event.type() == event_type_names::kClick || event.type() == event_type_names::kKeydown || - event.type() == event_type_names::kMousedown; + event.type() == event_type_names::kMousedown || + event.type() == event_type_names::kMouseup; } bool ShouldReportForEventTiming(WindowPerformance* performance) { if (!performance->FirstInputDetected()) return true; - if (!RuntimeEnabledFeatures::EventTimingEnabled( - performance->GetExecutionContext())) - return false; - return (!performance->IsEventTimingBufferFull() || performance->HasObserverFor(PerformanceEntry::kEvent)); } @@ -48,13 +44,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) { @@ -80,7 +94,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); @@ -89,39 +112,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 diff --git a/chromium/third_party/blink/renderer/core/timing/event_timing.h b/chromium/third_party/blink/renderer/core/timing/event_timing.h index aa83fd3097c..59a8627d92f 100644 --- a/chromium/third_party/blink/renderer/core/timing/event_timing.h +++ b/chromium/third_party/blink/renderer/core/timing/event_timing.h @@ -26,16 +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); - - // Notifies the Performance object that the event has been dispatched. - void DidDispatchEvent(const Event&, Document& document); + const Event& event); + ~EventTiming(); + EventTiming(const EventTiming&) = delete; + EventTiming& operator=(const EventTiming&) = delete; + 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); @@ -50,9 +53,7 @@ class CORE_EXPORT EventTiming final { Persistent<WindowPerformance> performance_; - bool should_log_event_; - - DISALLOW_COPY_AND_ASSIGN(EventTiming); + Persistent<const Event> event_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc b/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc index 66a0568caf0..79ce17f9aaa 100644 --- a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc +++ b/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc @@ -11,7 +11,7 @@ #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" -#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h" +#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h" #include "third_party/blink/renderer/bindings/core/v8/v8_memory_attribution.h" #include "third_party/blink/renderer/bindings/core/v8/v8_memory_attribution_container.h" #include "third_party/blink/renderer/bindings/core/v8/v8_memory_breakdown_entry.h" @@ -46,6 +46,7 @@ namespace { // String constants used for building the result. constexpr const char* kCrossOriginUrl = "cross-origin-url"; +constexpr const char* kMemoryTypeCanvas = "Canvas"; constexpr const char* kMemoryTypeDom = "DOM"; constexpr const char* kMemoryTypeJavaScript = "JavaScript"; constexpr const char* kMemoryTypeShared = "Shared"; @@ -184,7 +185,6 @@ ScriptPromise MeasureMemoryController::StartMeasurement( return ScriptPromise(script_state, promise_resolver->GetPromise()); } - namespace { // Satisfies the requirements of UniformRandomBitGenerator from C++ standard. @@ -237,11 +237,13 @@ MemoryAttribution* ConvertAttribution( result->setUrl(kCrossOriginUrl); } result->setScope(ConvertScope(attribution->scope)); - result->setContainer(ConvertContainer(attribution)); + if (auto* container = ConvertContainer(attribution)) { + result->setContainer(container); + } return result; } -MemoryBreakdownEntry* ConvertBreakdown( +MemoryBreakdownEntry* ConvertJavaScriptBreakdown( const WebMemoryBreakdownEntryPtr& breakdown_entry) { auto* result = MemoryBreakdownEntry::Create(); DCHECK(breakdown_entry->memory); @@ -255,6 +257,20 @@ MemoryBreakdownEntry* ConvertBreakdown( return result; } +MemoryBreakdownEntry* ConvertCanvasBreakdown( + const WebMemoryBreakdownEntryPtr& breakdown_entry) { + auto* result = MemoryBreakdownEntry::Create(); + DCHECK(breakdown_entry->canvas_memory); + result->setBytes(breakdown_entry->canvas_memory->bytes); + HeapVector<Member<MemoryAttribution>> attribution; + for (const auto& entry : breakdown_entry->attribution) { + attribution.push_back(ConvertAttribution(entry)); + } + result->setAttribution(attribution); + result->setTypes({WTF::AtomicString(kMemoryTypeCanvas)}); + return result; +} + MemoryBreakdownEntry* CreateUnattributedBreakdown( const WebMemoryUsagePtr& memory, const WTF::String& memory_type) { @@ -280,8 +296,13 @@ MemoryMeasurement* ConvertResult(const WebMemoryMeasurementPtr& measurement) { HeapVector<Member<MemoryBreakdownEntry>> breakdown; for (const auto& entry : measurement->breakdown) { // Skip breakdowns that didn't get a measurement. - if (entry->memory) - breakdown.push_back(ConvertBreakdown(entry)); + if (entry->memory) { + breakdown.push_back(ConvertJavaScriptBreakdown(entry)); + } + // Skip breakdowns that didn't get a measurement. + if (entry->canvas_memory) { + breakdown.push_back(ConvertCanvasBreakdown(entry)); + } } // Add breakdowns for memory that isn't attributed to an execution context. breakdown.push_back(CreateUnattributedBreakdown(measurement->shared_memory, @@ -369,14 +390,16 @@ void MeasureMemoryController::MeasurementComplete( } v8::HandleScope handle_scope(isolate_); v8::Local<v8::Context> context = context_.NewLocal(isolate_); + ScriptState* script_state = ScriptState::From(context); v8::Context::Scope context_scope(context); v8::MicrotasksScope microtasks_scope( isolate_, v8::MicrotasksScope::kDoNotRunMicrotasks); - auto* result = ConvertResult(measurement); + MemoryMeasurement* result = ConvertResult(measurement); v8::Local<v8::Promise::Resolver> promise_resolver = promise_resolver_.NewLocal(isolate_); - promise_resolver->Resolve(context, ToV8(result, promise_resolver, isolate_)) - .ToChecked(); + v8::MaybeLocal<v8::Value> v8_result = + ToV8Traits<MemoryMeasurement>::ToV8(script_state, result); + promise_resolver->Resolve(context, v8_result.ToLocalChecked()).ToChecked(); promise_resolver_.Clear(); RecordWebMemoryUkm(context, measurement); } diff --git a/chromium/third_party/blink/renderer/core/timing/memory_info.cc b/chromium/third_party/blink/renderer/core/timing/memory_info.cc index 9f8a31e1883..99df56a0dd5 100644 --- a/chromium/third_party/blink/renderer/core/timing/memory_info.cc +++ b/chromium/third_party/blink/renderer/core/timing/memory_info.cc @@ -32,7 +32,6 @@ #include <limits> -#include "base/macros.h" #include "base/time/default_tick_clock.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/settings.h" @@ -63,6 +62,8 @@ class HeapSizeCache { public: HeapSizeCache() : clock_(base::DefaultTickClock::GetInstance()) {} + HeapSizeCache(const HeapSizeCache&) = delete; + HeapSizeCache& operator=(const HeapSizeCache&) = delete; void GetCachedHeapSize(HeapInfo& info, MemoryInfo::Precision precision) { MaybeUpdate(precision); @@ -109,7 +110,6 @@ class HeapSizeCache { const base::TickClock* clock_; HeapInfo info_; - DISALLOW_COPY_AND_ASSIGN(HeapSizeCache); }; // We quantize the sizes to make it more difficult for an attacker to see diff --git a/chromium/third_party/blink/renderer/core/timing/performance.cc b/chromium/third_party/blink/renderer/core/timing/performance.cc index cc63f8a3bae..a262da3c512 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance.cc @@ -43,7 +43,6 @@ #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" -#include "third_party/blink/renderer/bindings/core/v8/string_or_performance_measure_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h" #include "third_party/blink/renderer/bindings/core/v8/v8_performance_mark_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_performance_measure_options.h" @@ -83,7 +82,6 @@ #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h" #include "third_party/blink/renderer/platform/network/http_parsers.h" -#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" #include "v8/include/v8-metrics.h" @@ -92,6 +90,10 @@ namespace blink { namespace { +// LongTask API can be a source of many events. Filter on Performance object +// level before reporting to UKM to smooth out recorded events over all pages. +constexpr size_t kLongTaskUkmSampleInterval = 100; + const SecurityOrigin* GetSecurityOrigin(ExecutionContext* context) { if (context) return context->GetSecurityOrigin(); @@ -129,32 +131,6 @@ void RecordLongTaskUkm(ExecutionContext* execution_context, .Record(execution_context->UkmRecorder()); } -#if !defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) -V8UnionPerformanceMeasureOptionsOrString* -StringOrPerformanceMeasureOptionsToNewV8Union( - const StringOrPerformanceMeasureOptions& value) { - if (value.IsString()) { - return MakeGarbageCollected<V8UnionPerformanceMeasureOptionsOrString>( - value.GetAsString()); - } - if (value.IsPerformanceMeasureOptions()) { - return MakeGarbageCollected<V8UnionPerformanceMeasureOptionsOrString>( - value.GetAsPerformanceMeasureOptions()); - } - return nullptr; -} -#endif // !defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) - -// TODO(crbug.com/1181288): Remove the old IDL union version. -V8UnionDoubleOrString* StringOrDoubleToV8UnionDoubleOrString( - const StringOrDouble& value) { - if (value.IsString()) - return MakeGarbageCollected<V8UnionDoubleOrString>(value.GetAsString()); - if (value.IsDouble()) - return MakeGarbageCollected<V8UnionDoubleOrString>(value.GetAsDouble()); - return nullptr; -} - } // namespace using PerformanceObserverVector = HeapVector<Member<PerformanceObserver>>; @@ -704,7 +680,6 @@ void Performance::AddElementTimingBuffer(PerformanceElementTiming& entry) { } void Performance::AddEventTimingBuffer(PerformanceEventTiming& entry) { - DCHECK(RuntimeEnabledFeatures::EventTimingEnabled(GetExecutionContext())); if (!IsEventTimingBufferFull()) { event_timing_buffer_.push_back(&entry); } @@ -772,9 +747,12 @@ void Performance::AddLongTaskTiming(base::TimeTicks start_time, } else { UseCounter::Count(execution_context, WebFeature::kLongTaskBufferFull); } - RecordLongTaskUkm(execution_context, - base::TimeDelta::FromMillisecondsD(dom_high_res_start_time), - end_time - start_time); + if ((++long_task_counter_ % kLongTaskUkmSampleInterval) == 0) { + RecordLongTaskUkm( + execution_context, + base::TimeDelta::FromMillisecondsD(dom_high_res_start_time), + end_time - start_time); + } NotifyObserversOfEntry(*entry); } @@ -788,12 +766,12 @@ PerformanceMark* Performance::mark(ScriptState* script_state, const AtomicString& mark_name, PerformanceMarkOptions* mark_options, ExceptionState& exception_state) { - DEFINE_STATIC_LOCAL(const AtomicString, mark_fully_loaded, - ("mark_fully_loaded")); - DEFINE_STATIC_LOCAL(const AtomicString, mark_fully_visible, - ("mark_fully_visible")); - DEFINE_STATIC_LOCAL(const AtomicString, mark_interactive, - ("mark_interactive")); + DEFINE_THREAD_SAFE_STATIC_LOCAL(const AtomicString, mark_fully_loaded, + ("mark_fully_loaded")); + DEFINE_THREAD_SAFE_STATIC_LOCAL(const AtomicString, mark_fully_visible, + ("mark_fully_visible")); + DEFINE_THREAD_SAFE_STATIC_LOCAL(const AtomicString, mark_interactive, + ("mark_interactive")); if (mark_options && (mark_options->hasStartTime() || mark_options->hasDetail())) { UseCounter::Count(GetExecutionContext(), WebFeature::kUserTimingL3); @@ -850,7 +828,6 @@ PerformanceMeasure* Performance::measure(ScriptState* script_state, exception_state); } -#if defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) PerformanceMeasure* Performance::measure( ScriptState* script_state, const AtomicString& measure_name, @@ -859,20 +836,7 @@ PerformanceMeasure* Performance::measure( return MeasureInternal(script_state, measure_name, start_or_options, absl::nullopt, exception_state); } -#else // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) -PerformanceMeasure* Performance::measure( - ScriptState* script_state, - const AtomicString& measure_name, - const StringOrPerformanceMeasureOptions& start_or_options, - ExceptionState& exception_state) { - return MeasureInternal( - script_state, measure_name, - StringOrPerformanceMeasureOptionsToNewV8Union(start_or_options), - absl::nullopt, exception_state); -} -#endif // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) -#if defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) PerformanceMeasure* Performance::measure( ScriptState* script_state, const AtomicString& measure_name, @@ -882,19 +846,6 @@ PerformanceMeasure* Performance::measure( return MeasureInternal(script_state, measure_name, start_or_options, absl::optional<String>(end), exception_state); } -#else // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) -PerformanceMeasure* Performance::measure( - ScriptState* script_state, - const AtomicString& measure_name, - const StringOrPerformanceMeasureOptions& start_or_options, - const String& end, - ExceptionState& exception_state) { - return MeasureInternal( - script_state, measure_name, - StringOrPerformanceMeasureOptionsToNewV8Union(start_or_options), - absl::optional<String>(end), exception_state); -} -#endif // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) // |MeasureInternal| exists to unify the arguments from different // `performance.measure()` overloads into a consistent form, then delegate to @@ -947,18 +898,12 @@ PerformanceMeasure* Performance::MeasureInternal( return nullptr; } - V8UnionDoubleOrString* start = nullptr; - if (options->hasStart()) { - start = StringOrDoubleToV8UnionDoubleOrString(options->start()); - } + V8UnionDoubleOrString* start = options->getStartOr(nullptr); absl::optional<double> duration; if (options->hasDuration()) { duration = options->duration(); } - V8UnionDoubleOrString* end = nullptr; - if (options->hasEnd()) { - end = StringOrDoubleToV8UnionDoubleOrString(options->end()); - } + V8UnionDoubleOrString* end = options->getEndOr(nullptr); return MeasureWithDetail( script_state, measure_name, start, duration, end, @@ -1004,34 +949,6 @@ void Performance::clearMeasures(const AtomicString& measure_name) { GetUserTiming().ClearMeasures(measure_name); } -ScriptPromise Performance::profile(ScriptState* script_state, - const ProfilerInitOptions* options, - ExceptionState& exception_state) { - auto* execution_context = ExecutionContext::From(script_state); - DCHECK(execution_context); - DCHECK( - RuntimeEnabledFeatures::ExperimentalJSProfilerEnabled(execution_context)); - - bool can_profile = false; - if (LocalDOMWindow* window = LocalDOMWindow::From(script_state)) { - can_profile = ProfilerGroup::CanProfile(window, &exception_state, - ReportOptions::kReportOnFailure); - } - - if (!can_profile) - return ScriptPromise(); - - auto* profiler_group = ProfilerGroup::From(script_state->GetIsolate()); - DCHECK(profiler_group); - - auto* profiler = profiler_group->CreateProfiler( - script_state, *options, time_origin_, exception_state); - if (exception_state.HadException()) - return ScriptPromise(); - - return ScriptPromise::Cast(script_state, ToV8(profiler, script_state)); -} - void Performance::RegisterPerformanceObserver(PerformanceObserver& observer) { observer_filter_options_ |= observer.FilterOptions(); observers_.insert(&observer); @@ -1051,8 +968,6 @@ void Performance::UpdatePerformanceObserverFilterOptions() { } void Performance::NotifyObserversOfEntry(PerformanceEntry& entry) const { - DCHECK(entry.EntryTypeEnum() != PerformanceEntry::kEvent || - RuntimeEnabledFeatures::EventTimingEnabled(GetExecutionContext())); bool observer_found = false; for (auto& observer : observers_) { if (observer->FilterOptions() & entry.EntryTypeEnum() && diff --git a/chromium/third_party/blink/renderer/core/timing/performance.h b/chromium/third_party/blink/renderer/core/timing/performance.h index a3c9924825b..32c9894e3d8 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance.h +++ b/chromium/third_party/blink/renderer/core/timing/performance.h @@ -34,7 +34,6 @@ #include "base/single_thread_task_runner.h" #include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h" -#include "third_party/blink/renderer/bindings/core/v8/string_or_double.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h" #include "third_party/blink/renderer/core/dom/events/event_target.h" @@ -73,14 +72,12 @@ class PerformanceMeasure; class PerformanceNavigation; class PerformanceObserver; class PerformanceTiming; -class ProfilerInitOptions; class ResourceResponse; class ResourceTimingInfo; class ScriptPromise; class ScriptState; class ScriptValue; class SecurityOrigin; -class StringOrPerformanceMeasureOptions; class UserTiming; class V8ObjectBuilder; class V8UnionDoubleOrString; @@ -145,9 +142,7 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { DOMHighResTimeStamp timeOrigin() const; // Internal getter method for the time origin value. - double GetTimeOrigin() const { - return time_origin_.since_origin().InSecondsF(); - } + base::TimeTicks GetTimeOriginInternal() const { return time_origin_; } PerformanceEntryVector getEntries(); // Get BufferedEntriesByType will return all entries in the buffer regardless @@ -268,43 +263,22 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { const AtomicString& measure_name, ExceptionState&); -#if defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) PerformanceMeasure* measure( ScriptState* script_state, const AtomicString& measure_name, const V8UnionPerformanceMeasureOptionsOrString* start_or_options, ExceptionState& exception_state); -#else // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) - PerformanceMeasure* measure( - ScriptState*, - const AtomicString& measure_name, - const StringOrPerformanceMeasureOptions& start_or_options, - ExceptionState&); -#endif // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) -#if defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) PerformanceMeasure* measure( ScriptState* script_state, const AtomicString& measure_name, const V8UnionPerformanceMeasureOptionsOrString* start_or_options, const String& end, ExceptionState& exception_state); -#else // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) - PerformanceMeasure* measure( - ScriptState*, - const AtomicString& measure_name, - const StringOrPerformanceMeasureOptions& start_or_options, - const String& end, - ExceptionState&); -#endif // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION) void clearMeasures(const AtomicString& measure_name); void clearMeasures() { return clearMeasures(AtomicString()); } - ScriptPromise profile(ScriptState*, - const ProfilerInitOptions*, - ExceptionState&); - void UnregisterPerformanceObserver(PerformanceObserver&); void RegisterPerformanceObserver(PerformanceObserver&); void UpdatePerformanceObserverFilterOptions(); @@ -431,6 +405,9 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { // See crbug.com/1181774. Member<BackgroundTracingHelper> background_tracing_helper_; + + // Running counter for LongTask observations. + size_t long_task_counter_ = 0; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/performance.idl b/chromium/third_party/blink/renderer/core/timing/performance.idl index 7088dff953c..c5d098ae474 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance.idl +++ b/chromium/third_party/blink/renderer/core/timing/performance.idl @@ -71,12 +71,8 @@ interface Performance : EventTarget { // a dedicated worker is included in the result of the API call in Window. [MeasureAs=MeasureMemory, Exposed=(Window,ServiceWorker,SharedWorker), CallWith=ScriptState, RuntimeEnabled=MeasureMemory, RaisesException, CrossOriginIsolated] Promise<MemoryMeasurement> measureUserAgentSpecificMemory(); - // JS Self-Profiling API - // https://github.com/WICG/js-self-profiling/ - [MeasureAs=JSSelfProfiling, CallWith=ScriptState, RuntimeEnabled=ExperimentalJSProfiler, RaisesException] Promise<Profiler> profile(ProfilerInitOptions options); - // Event Timing - [Exposed=Window, SameObject, SaveSameObject, RuntimeEnabled=EventTiming] readonly attribute EventCounts eventCounts; + [Exposed=Window, SameObject, SaveSameObject] readonly attribute EventCounts eventCounts; [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON(); }; diff --git a/chromium/third_party/blink/renderer/core/timing/performance_event_timing.idl b/chromium/third_party/blink/renderer/core/timing/performance_event_timing.idl index 17b3db120b1..68cef848abc 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_event_timing.idl +++ b/chromium/third_party/blink/renderer/core/timing/performance_event_timing.idl @@ -8,7 +8,7 @@ interface PerformanceEventTiming : PerformanceEntry { readonly attribute DOMHighResTimeStamp processingStart; readonly attribute DOMHighResTimeStamp processingEnd; readonly attribute boolean cancelable; - [RuntimeEnabled=EventTiming] readonly attribute Node? target; + readonly attribute Node? target; [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON(); }; diff --git a/chromium/third_party/blink/renderer/core/timing/performance_mark.cc b/chromium/third_party/blink/renderer/core/timing/performance_mark.cc index 487e2f98a69..30ec9b574f0 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_mark.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_mark.cc @@ -58,8 +58,8 @@ PerformanceMark* PerformanceMark::Create(ScriptState* script_state, // GetTimeOrigin() returns seconds from the monotonic clock's origin.. // Trace events timestamps accept seconds (as a double) based on // CurrentTime::monotonicallyIncreasingTime(). - unsafe_start_for_traces = trace_event::ToTraceTimestamp( - performance->GetTimeOrigin() + start / 1000.0); + unsafe_start_for_traces = performance->GetTimeOriginInternal() + + base::TimeDelta::FromMillisecondsD(start); } else { start = performance->now(); unsafe_start_for_traces = base::TimeTicks::Now(); diff --git a/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing.cc b/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing.cc index fa42819274c..67ee52f97be 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing.cc @@ -13,7 +13,9 @@ #include "third_party/blink/renderer/core/loader/document_loader.h" #include "third_party/blink/renderer/core/performance_entry_names.h" #include "third_party/blink/renderer/core/timing/performance.h" +#include "third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" namespace blink { @@ -318,5 +320,12 @@ void PerformanceNavigationTiming::BuildJSONValue( builder.AddNumber("loadEventEnd", loadEventEnd()); builder.AddString("type", type()); builder.AddNumber("redirectCount", redirectCount()); + + if (RuntimeEnabledFeatures::Prerender2Enabled( + ExecutionContext::From(builder.GetScriptState()))) { + builder.AddNumber( + "activationStart", + PerformanceNavigationTimingActivationStart::activationStart(*this)); + } } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing.h b/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing.h index 1c5f76ef0b1..ca71acbeef2 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing.h +++ b/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing.h @@ -69,6 +69,8 @@ class CORE_EXPORT PerformanceNavigationTiming final void BuildJSONValue(V8ObjectBuilder&) const override; private: + friend class PerformanceNavigationTimingActivationStart; + static AtomicString GetNavigationType(WebNavigationType, const Document*); const DocumentTiming* GetDocumentTiming() const; diff --git a/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.cc b/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.cc new file mode 100644 index 00000000000..504088e7d71 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.cc @@ -0,0 +1,25 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.h" + +#include "third_party/blink/renderer/core/loader/document_load_timing.h" +#include "third_party/blink/renderer/core/timing/performance.h" + +namespace blink { + +// static +DOMHighResTimeStamp PerformanceNavigationTimingActivationStart::activationStart( + const PerformanceNavigationTiming& performance_navigation_timing) { + DocumentLoadTiming* timing = + performance_navigation_timing.GetDocumentLoadTiming(); + if (!timing) + return 0.0; + return Performance::MonotonicTimeToDOMHighResTimeStamp( + performance_navigation_timing.TimeOrigin(), timing->ActivationStart(), + false /* allow_negative_value */, + performance_navigation_timing.CrossOriginIsolatedCapability()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.h b/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.h new file mode 100644 index 00000000000..c42f599967c --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.h @@ -0,0 +1,20 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// 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_PERFORMANCE_NAVIGATION_TIMING_ACTIVATION_START_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_PERFORMANCE_NAVIGATION_TIMING_ACTIVATION_START_H_ + +#include "third_party/blink/renderer/core/timing/performance_navigation_timing.h" + +namespace blink { + +class PerformanceNavigationTimingActivationStart final { + public: + static DOMHighResTimeStamp activationStart( + const PerformanceNavigationTiming& performance_navigation_timing); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_PERFORMANCE_NAVIGATION_TIMING_ACTIVATION_START_H_ diff --git a/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.idl b/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.idl new file mode 100644 index 00000000000..62f799de902 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/performance_navigation_timing_activation_start.idl @@ -0,0 +1,11 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://jeremyroman.github.io/alternate-loading-modes/#performance-navigation-timing-extension +[ + ImplementedAs=PerformanceNavigationTimingActivationStart, + RuntimeEnabled=Prerender2 +] partial interface PerformanceNavigationTiming { + readonly attribute DOMHighResTimeStamp activationStart; +};
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/core/timing/performance_observer.cc b/chromium/third_party/blink/renderer/core/timing/performance_observer.cc index 60a2ac86d78..bbff4d6bd3c 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_observer.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_observer.cc @@ -59,8 +59,7 @@ Vector<AtomicString> PerformanceObserver::supportedEntryTypes( auto* execution_context = ExecutionContext::From(script_state); if (execution_context->IsWindow()) { supportedEntryTypes.push_back(performance_entry_names::kElement); - if (RuntimeEnabledFeatures::EventTimingEnabled(execution_context)) - supportedEntryTypes.push_back(performance_entry_names::kEvent); + supportedEntryTypes.push_back(performance_entry_names::kEvent); supportedEntryTypes.push_back(performance_entry_names::kFirstInput); supportedEntryTypes.push_back( performance_entry_names::kLargestContentfulPaint); diff --git a/chromium/third_party/blink/renderer/core/timing/performance_observer_init.idl b/chromium/third_party/blink/renderer/core/timing/performance_observer_init.idl index f4169cd8cfa..ee124eae41a 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_observer_init.idl +++ b/chromium/third_party/blink/renderer/core/timing/performance_observer_init.idl @@ -8,5 +8,5 @@ dictionary PerformanceObserverInit { sequence<DOMString> entryTypes; DOMString type; boolean buffered = false; - [RuntimeEnabled=EventTiming] DOMHighResTimeStamp durationThreshold; + DOMHighResTimeStamp durationThreshold; }; diff --git a/chromium/third_party/blink/renderer/core/timing/performance_test.cc b/chromium/third_party/blink/renderer/core/timing/performance_test.cc index 6555f4e7b06..09efdb6cc7c 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_test.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_test.cc @@ -7,8 +7,6 @@ #include "base/test/metrics/histogram_tester.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/task_type.h" -#include "third_party/blink/renderer/bindings/core/v8/string_or_double.h" -#include "third_party/blink/renderer/bindings/core/v8/string_or_performance_measure_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h" #include "third_party/blink/renderer/bindings/core/v8/v8_performance_observer_callback.h" #include "third_party/blink/renderer/bindings/core/v8/v8_performance_observer_init.h" diff --git a/chromium/third_party/blink/renderer/core/timing/performance_timing.cc b/chromium/third_party/blink/renderer/core/timing/performance_timing.cc index 78383485335..7a3716503a7 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_timing.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_timing.cc @@ -63,7 +63,8 @@ static uint64_t ToIntegerMilliseconds(base::TimeDelta duration, PerformanceTiming::PerformanceTiming(ExecutionContext* context) : ExecutionContextClient(context) { - cross_origin_isolated_capability_ = context->CrossOriginIsolatedCapability(); + cross_origin_isolated_capability_ = + context && context->CrossOriginIsolatedCapability(); } uint64_t PerformanceTiming::navigationStart() const { @@ -354,12 +355,12 @@ PerformanceTiming::BackForwardCacheRestore() const { WTF::Vector<BackForwardCacheRestoreTiming> restore_timings( navigation_starts.size()); - for (size_t i = 0; i < restore_timings.size(); i++) { + for (wtf_size_t i = 0; i < restore_timings.size(); i++) { restore_timings[i].navigation_start = MonotonicTimeToIntegerMilliseconds(navigation_starts[i]); restore_timings[i].first_paint = MonotonicTimeToIntegerMilliseconds(first_paints[i]); - for (size_t j = 0; j < request_animation_frames[i].size(); j++) { + for (wtf_size_t j = 0; j < request_animation_frames[i].size(); j++) { restore_timings[i].request_animation_frames[j] = MonotonicTimeToIntegerMilliseconds(request_animation_frames[i][j]); } @@ -639,6 +640,19 @@ absl::optional<base::TimeTicks> PerformanceTiming::LastPortalActivatedPaint() return timing->LastPortalActivatedPaint(); } +absl::optional<base::TimeDelta> PerformanceTiming::PrerenderActivationStart() + const { + DocumentLoadTiming* timing = GetDocumentLoadTiming(); + if (!timing) + return absl::nullopt; + + base::TimeTicks activation_start = timing->ActivationStart(); + if (activation_start.is_null()) + return absl::nullopt; + + return timing->MonotonicTimeToZeroBasedDocumentTime(activation_start); +} + absl::optional<base::TimeTicks> PerformanceTiming::UnloadStart() const { DocumentLoadTiming* timing = GetDocumentLoadTiming(); if (!timing) diff --git a/chromium/third_party/blink/renderer/core/timing/performance_timing.h b/chromium/third_party/blink/renderer/core/timing/performance_timing.h index 484844bc784..a5c7172ae0a 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_timing.h +++ b/chromium/third_party/blink/renderer/core/timing/performance_timing.h @@ -196,6 +196,8 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable, // The time of the first paint after a portal activation. absl::optional<base::TimeTicks> LastPortalActivatedPaint() const; + // The start time of the prerender activation navigation. + absl::optional<base::TimeDelta> PrerenderActivationStart() const; typedef uint64_t (PerformanceTiming::*PerformanceTimingGetter)() const; using NameToAttributeMap = HashMap<AtomicString, PerformanceTimingGetter>; diff --git a/chromium/third_party/blink/renderer/core/timing/performance_user_timing.cc b/chromium/third_party/blink/renderer/core/timing/performance_user_timing.cc index 2bf5070bc21..887e3330450 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_user_timing.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_user_timing.cc @@ -109,7 +109,8 @@ double UserTiming::FindExistingMarkStartTime(const AtomicString& mark_name, } PerformanceTiming::PerformanceTimingGetter timing_function = - PerformanceTiming::GetAttributeMapping().at(mark_name); + PerformanceTiming::GetAttributeMapping().DeprecatedAtOrEmptyValue( + mark_name); if (!timing_function) { exception_state.ThrowDOMException( DOMExceptionCode::kSyntaxError, @@ -176,15 +177,8 @@ base::TimeTicks UserTiming::GetPerformanceMarkUnsafeTimeForTraces( return mark->UnsafeTimeForTraces(); } } - - // User timing events are stored as integer milliseconds from the start of - // navigation. - // GetTimeOrigin() returns seconds from the monotonic clock's origin.. - // Trace events timestamps accept seconds (as a double) based on - // CurrentTime::monotonicallyIncreasingTime(). - double start_time_in_seconds = start_time / 1000.0; - return trace_event::ToTraceTimestamp(performance_->GetTimeOrigin() + - start_time_in_seconds); + return performance_->GetTimeOriginInternal() + + base::TimeDelta::FromMillisecondsD(start_time); } PerformanceMeasure* UserTiming::Measure(ScriptState* script_state, diff --git a/chromium/third_party/blink/renderer/core/timing/performance_user_timing.h b/chromium/third_party/blink/renderer/core/timing/performance_user_timing.h index c4a983f6e2c..2ed451c7f3a 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_user_timing.h +++ b/chromium/third_party/blink/renderer/core/timing/performance_user_timing.h @@ -26,6 +26,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_PERFORMANCE_USER_TIMING_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_PERFORMANCE_USER_TIMING_H_ +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/renderer/core/timing/performance.h" #include "third_party/blink/renderer/core/timing/performance_timing.h" #include "third_party/blink/renderer/platform/heap/heap_allocator.h" diff --git a/chromium/third_party/blink/renderer/core/timing/profiler.cc b/chromium/third_party/blink/renderer/core/timing/profiler.cc index a48c173fa73..0bee7aedb0b 100644 --- a/chromium/third_party/blink/renderer/core/timing/profiler.cc +++ b/chromium/third_party/blink/renderer/core/timing/profiler.cc @@ -6,15 +6,52 @@ #include "third_party/blink/renderer/core/dom/dom_exception.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/frame/local_dom_window.h" +#include "third_party/blink/renderer/core/timing/dom_window_performance.h" #include "third_party/blink/renderer/core/timing/profiler_group.h" #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h" namespace blink { +Profiler* Profiler::Create(ScriptState* script_state, + const ProfilerInitOptions* options, + ExceptionState& exception_state) { + auto* execution_context = ExecutionContext::From(script_state); + DCHECK(execution_context); + DCHECK( + RuntimeEnabledFeatures::ExperimentalJSProfilerEnabled(execution_context)); + + Performance* performance = nullptr; + bool can_profile = false; + if (LocalDOMWindow* window = LocalDOMWindow::From(script_state)) { + can_profile = ProfilerGroup::CanProfile(window, &exception_state, + ReportOptions::kReportOnFailure); + performance = DOMWindowPerformance::performance(*window); + } + + if (!can_profile) { + DCHECK(exception_state.HadException()); + return nullptr; + } + + DCHECK(performance); + + auto* profiler_group = ProfilerGroup::From(script_state->GetIsolate()); + DCHECK(profiler_group); + + auto* profiler = profiler_group->CreateProfiler( + script_state, *options, performance->GetTimeOriginInternal(), + exception_state); + if (exception_state.HadException()) + return nullptr; + + return profiler; +} + void Profiler::Trace(Visitor* visitor) const { visitor->Trace(profiler_group_); visitor->Trace(script_state_); - ScriptWrappable::Trace(visitor); + EventTargetWithInlineData::Trace(visitor); } void Profiler::DisposeAsync() { diff --git a/chromium/third_party/blink/renderer/core/timing/profiler.h b/chromium/third_party/blink/renderer/core/timing/profiler.h index b5e9074a052..da8590ca532 100644 --- a/chromium/third_party/blink/renderer/core/timing/profiler.h +++ b/chromium/third_party/blink/renderer/core/timing/profiler.h @@ -19,7 +19,9 @@ namespace blink { +class ExceptionState; class ScriptState; +class ProfilerInitOptions; // A web-exposed JS sampling profiler created via blink::ProfilerGroup, // wrapping a handle to v8::CpuProfiler. Records samples periodically from the @@ -44,6 +46,10 @@ class CORE_EXPORT Profiler final : public EventTargetWithInlineData { ~Profiler() override = default; + static Profiler* Create(ScriptState*, + const ProfilerInitOptions*, + ExceptionState&); + void Trace(Visitor* visitor) const override; void DisposeAsync(); diff --git a/chromium/third_party/blink/renderer/core/timing/profiler.idl b/chromium/third_party/blink/renderer/core/timing/profiler.idl index 4ffdc531ad7..649f08798aa 100644 --- a/chromium/third_party/blink/renderer/core/timing/profiler.idl +++ b/chromium/third_party/blink/renderer/core/timing/profiler.idl @@ -3,9 +3,11 @@ // found in the LICENSE file. // https://wicg.github.io/js-self-profiling/#the-profiler-interface -[Exposed=(Window,Worker), RuntimeEnabled=ExperimentalJSProfiler] +[Exposed=Window, RuntimeEnabled=ExperimentalJSProfiler] interface Profiler : EventTarget { readonly attribute DOMHighResTimeStamp sampleInterval; readonly attribute boolean stopped; + + [MeasureAs=JSSelfProfiling, CallWith=ScriptState, RaisesException] constructor(ProfilerInitOptions options); [CallWith=ScriptState] Promise<ProfilerTrace> stop(); }; diff --git a/chromium/third_party/blink/renderer/core/timing/profiler_group.cc b/chromium/third_party/blink/renderer/core/timing/profiler_group.cc index d404c852317..090068ee1dc 100644 --- a/chromium/third_party/blink/renderer/core/timing/profiler_group.cc +++ b/chromium/third_party/blink/renderer/core/timing/profiler_group.cc @@ -80,18 +80,6 @@ bool ProfilerGroup::CanProfile(LocalDOMWindow* local_window, return false; } - // Bypass COOP/COEP checks when the |--disable-web-security| flag is present. - auto* local_frame = local_window->GetFrame(); - DCHECK(local_frame); - if (local_frame->GetSettings()->GetWebSecurityEnabled() && - !local_window->CrossOriginIsolatedCapability()) { - if (exception_state) { - exception_state->ThrowSecurityError( - "performance.profile() requires COOP+COEP (web.dev/coop-coep)"); - } - return false; - } - return true; } @@ -182,9 +170,7 @@ Profiler* ProfilerGroup::CreateProfiler(ScriptState* script_state, String profiler_id = NextProfilerId(); v8::CpuProfilingOptions options( - v8::kLeafNodeLineNumbers, - init_options.hasMaxBufferSize() ? init_options.maxBufferSize() - : v8::CpuProfilingOptions::kNoSampleLimit, + v8::kLeafNodeLineNumbers, init_options.maxBufferSize(), static_cast<int>(sample_interval_us), script_state->GetContext()); v8::CpuProfilingStatus status = cpu_profiler_->StartProfiling( diff --git a/chromium/third_party/blink/renderer/core/timing/profiler_group.h b/chromium/third_party/blink/renderer/core/timing/profiler_group.h index 5b91224cc64..d673d0d6ec6 100644 --- a/chromium/third_party/blink/renderer/core/timing/profiler_group.h +++ b/chromium/third_party/blink/renderer/core/timing/profiler_group.h @@ -5,7 +5,6 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_PROFILER_GROUP_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_PROFILER_GROUP_H_ -#include "base/macros.h" #include "base/time/time.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/execution_context/security_context.h" @@ -47,6 +46,8 @@ class CORE_EXPORT ProfilerGroup static base::TimeDelta GetBaseSampleInterval(); ProfilerGroup(v8::Isolate* isolate); + ProfilerGroup(const ProfilerGroup&) = delete; + ProfilerGroup& operator=(const ProfilerGroup&) = delete; ~ProfilerGroup() override; Profiler* CreateProfiler(ScriptState* script_state, @@ -93,8 +94,6 @@ class CORE_EXPORT ProfilerGroup // A set of observers, one for each ExecutionContext that has profiling // enabled. HeapHashSet<Member<ProfilingContextObserver>> context_observers_; - - DISALLOW_COPY_AND_ASSIGN(ProfilerGroup); }; class DiscardedSamplesDelegate : public v8::DiscardedSamplesDelegate { diff --git a/chromium/third_party/blink/renderer/core/timing/profiler_init_options.idl b/chromium/third_party/blink/renderer/core/timing/profiler_init_options.idl index a9f2ab00498..e904e48c9b9 100644 --- a/chromium/third_party/blink/renderer/core/timing/profiler_init_options.idl +++ b/chromium/third_party/blink/renderer/core/timing/profiler_init_options.idl @@ -5,5 +5,5 @@ // https://wicg.github.io/js-self-profiling/#dom-profilerinitoptions dictionary ProfilerInitOptions { required DOMHighResTimeStamp sampleInterval; - unsigned long maxBufferSize; + required unsigned long maxBufferSize; }; diff --git a/chromium/third_party/blink/renderer/core/timing/responsiveness_metrics.cc b/chromium/third_party/blink/renderer/core/timing/responsiveness_metrics.cc new file mode 100644 index 00000000000..7b638a1650d --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/responsiveness_metrics.cc @@ -0,0 +1,204 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/timing/responsiveness_metrics.h" + +#include "base/rand_util.h" +#include "services/metrics/public/cpp/ukm_builders.h" +#include "services/metrics/public/cpp/ukm_recorder.h" + +namespace blink { + +namespace { +// Minimum potentially generated value for UKM sampling. +constexpr int kMinValueForSampling = 1; +// Maximum potentially generated value for UKM sampling. +constexpr int kMaxValueForSampling = 100; +// UKM sampling rate. The sampling strategy is 1/N. +constexpr int kUkmSamplingRate = 10; + +base::TimeDelta MaxEventDuration( + WTF::Vector<ResponsivenessMetrics::EventTimestamps> timestamps) { + base::TimeDelta max_duration = + timestamps[0].end_time - timestamps[0].start_time; + for (WTF::wtf_size_t i = 1; i < timestamps.size(); ++i) { + max_duration = std::max(max_duration, + timestamps[i].end_time - timestamps[i].start_time); + } + return max_duration; +} + +base::TimeDelta TotalEventDuration( + // timestamps is sorted by the start_time of EventTimestamps. + WTF::Vector<ResponsivenessMetrics::EventTimestamps> timestamps) { + // TODO(crbug.com/1229668): Once the event timestamp bug is fixed, add a + // DCHECK(IsSorted) here. + base::TimeDelta total_duration = + timestamps[0].end_time - timestamps[0].start_time; + base::TimeTicks current_end_time = timestamps[0].end_time; + for (WTF::wtf_size_t i = 1; i < timestamps.size(); ++i) { + total_duration += timestamps[i].end_time - timestamps[i].start_time; + if (timestamps[i].start_time < current_end_time) { + total_duration -= std::min(current_end_time, timestamps[i].end_time) - + timestamps[i].start_time; + } + current_end_time = std::max(current_end_time, timestamps[i].end_time); + } + return total_duration; +} +} // namespace + +ResponsivenessMetrics::ResponsivenessMetrics() = default; +ResponsivenessMetrics::~ResponsivenessMetrics() = default; + +void ResponsivenessMetrics::RecordUserInteractionUKM( + LocalDOMWindow* window, + UserInteractionType interaction_type, + base::TimeDelta max_event_duration, + base::TimeDelta total_event_duration) { + if (!window) + return; + + ukm::UkmRecorder* ukm_recorder = window->UkmRecorder(); + ukm::SourceId source_id = window->UkmSourceID(); + if (source_id != ukm::kInvalidSourceId && + (!sampling_ || base::RandInt(kMinValueForSampling, + kMaxValueForSampling) <= kUkmSamplingRate)) { + ukm::builders::Responsiveness_UserInteraction(source_id) + .SetInteractionType(static_cast<int>(interaction_type)) + .SetMaxEventDuration(max_event_duration.InMilliseconds()) + .SetTotalEventDuration(total_event_duration.InMilliseconds()) + .Record(ukm_recorder); + } +} + +void ResponsivenessMetrics::NotifyPotentialDrag() { + is_drag_ = pending_pointer_down_timestamps_.has_value() && + !pending_pointer_up_timestamps_.has_value(); +} + +void ResponsivenessMetrics::RecordPerInteractionLatency( + LocalDOMWindow* window, + const AtomicString& event_type, + absl::optional<int> key_code, + absl::optional<PointerId> pointer_id, + EventTimestamps event_timestamps) { + // Keyboard interactions. + if (key_code.has_value()) { + RecordKeyboardInteractions(window, event_type, key_code.value(), + event_timestamps); + } + // Tap(Click) or Drag. + if (pointer_id.has_value()) { + RecordTapOrClickOrDrag(window, event_type, event_timestamps); + } +} + +void ResponsivenessMetrics::FlushPendingInteraction(LocalDOMWindow* window) { + // For tap delay, the click can be dropped. We will measure the latency + // without any click data. + if (pending_pointer_down_timestamps_.has_value() && + pending_pointer_up_timestamps_.has_value()) { + WTF::Vector<EventTimestamps> timestamps; + // Insertion order matters for latency computation. + timestamps.push_back(pending_pointer_down_timestamps_.value()); + timestamps.push_back(pending_pointer_up_timestamps_.value()); + RecordUserInteractionUKM(window, + is_drag_ ? UserInteractionType::kDrag + : UserInteractionType::kTapOrClick, + MaxEventDuration(timestamps), + TotalEventDuration(timestamps)); + } + ResetPendingPointers(); +} + +void ResponsivenessMetrics::ResetPendingPointers() { + is_drag_ = false; + pending_pointer_down_timestamps_.reset(); + pending_pointer_up_timestamps_.reset(); +} + +// For multi-finger touch, we record the innermost pair of pointerdown and +// pointerup. +// TODO(hbsong): Record one interaction per pointer id. +void ResponsivenessMetrics::RecordTapOrClickOrDrag( + LocalDOMWindow* window, + const AtomicString& event_type, + EventTimestamps event_timestamps) { + if (event_type == event_type_names::kPointercancel) { + pending_pointer_down_timestamps_.reset(); + } else if (event_type == event_type_names::kPointerdown) { + FlushPendingInteraction(window); + pending_pointer_down_timestamps_ = event_timestamps; + } else if (event_type == event_type_names::kPointerup && + pending_pointer_down_timestamps_.has_value() && + !pending_pointer_up_timestamps_.has_value()) { + pending_pointer_up_timestamps_ = event_timestamps; + } else if (event_type == event_type_names::kClick) { + WTF::Vector<EventTimestamps> timestamps; + // Insertion order matters for latency computation. + if (pending_pointer_down_timestamps_.has_value()) { + timestamps.push_back(pending_pointer_down_timestamps_.value()); + } + if (pending_pointer_up_timestamps_.has_value()) { + timestamps.push_back(pending_pointer_up_timestamps_.value()); + } + timestamps.push_back(event_timestamps); + RecordUserInteractionUKM(window, + is_drag_ ? UserInteractionType::kDrag + : UserInteractionType::kTapOrClick, + MaxEventDuration(timestamps), + TotalEventDuration(timestamps)); + ResetPendingPointers(); + } +} + +void ResponsivenessMetrics::RecordKeyboardInteractions( + LocalDOMWindow* window, + const AtomicString& event_type, + int key_code, + EventTimestamps event_timestamps) { + if (event_type == event_type_names::kKeydown) { + if (key_down_timestamps_map_.find(key_code) != + key_down_timestamps_map_.end()) { + // Found a previous key_down with the same keycode, which means a key is + // being held down. We regard the duration of the keydown as an + // interaction level latency. + EventTimestamps key_down_timestamps = + key_down_timestamps_map_.at(key_code); + base::TimeDelta event_duration = + key_down_timestamps.end_time - key_down_timestamps.start_time; + RecordUserInteractionUKM(window, UserInteractionType::kKeyboard, + event_duration, event_duration); + } + key_down_timestamps_map_[key_code] = event_timestamps; + } else if (event_type == event_type_names::kKeyup) { + if (key_down_timestamps_map_.find(key_code) != + key_down_timestamps_map_.end()) { + // Found a previous key_down with the same keycode as keyup. + // We calculate the interaction latency based on the durations of keydown + // and keyup. + EventTimestamps key_down_timestamps = + key_down_timestamps_map_.at(key_code); + WTF::Vector<EventTimestamps> timestamps; + // Insertion order matters for latency computation. + timestamps.push_back(key_down_timestamps); + timestamps.push_back(event_timestamps); + RecordUserInteractionUKM(window, UserInteractionType::kKeyboard, + MaxEventDuration(timestamps), + TotalEventDuration(timestamps)); + // Remove the stale keydown. + key_down_timestamps_map_.erase(key_code); + } else { + // Can't find a corresponding keydown. We regard the duration of the keyup + // as an interaction latency. + base::TimeDelta event_duration = + event_timestamps.end_time - event_timestamps.start_time; + RecordUserInteractionUKM(window, UserInteractionType::kKeyboard, + event_duration, event_duration); + } + } +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/responsiveness_metrics.h b/chromium/third_party/blink/renderer/core/timing/responsiveness_metrics.h new file mode 100644 index 00000000000..0f18ada4f98 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/responsiveness_metrics.h @@ -0,0 +1,84 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// 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 <unordered_map> + +#include "third_party/blink/renderer/core/events/pointer_event.h" +#include "third_party/blink/renderer/core/frame/local_dom_window.h" + +namespace blink { + +// Enum class for user interaction types. It's used in UKM and should not be +// changed. +enum class UserInteractionType { kKeyboard = 0, kTapOrClick = 1, kDrag = 2 }; + +class ResponsivenessMetrics { + public: + ResponsivenessMetrics(); + ~ResponsivenessMetrics(); + + // Timestamps for input events. + struct EventTimestamps { + // 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; + }; + + // Track ongoing user interactions and calculate the latency when an + // interaction is completed. The latency data for each interaction will be + // recored in UKM. + void RecordPerInteractionLatency(LocalDOMWindow* window, + const AtomicString& event_type, + absl::optional<int> key_code, + absl::optional<PointerId> pointer_id, + EventTimestamps event_timestamps); + + // 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(); + + private: + // Record UKM for user interaction latencies. + void RecordUserInteractionUKM(LocalDOMWindow* window, + UserInteractionType interaction_type, + base::TimeDelta max_event_duration, + base::TimeDelta total_devent_duration); + + void RecordKeyboardInteractions(LocalDOMWindow* window, + const AtomicString& event_type, + int key_code, + EventTimestamps event_timestamps); + + // Might not be accurate for multi-fingers touch. + void RecordTapOrClickOrDrag(LocalDOMWindow* window, + const AtomicString& event_type, + EventTimestamps event_timestamps); + // Flush the latency data for pending tap or drag. + void FlushPendingInteraction(LocalDOMWindow* window); + + // Reset the latency data for pointer events. + void ResetPendingPointers(); + + // Variables for per-interaction latencies. + std::unordered_map<int, EventTimestamps> key_down_timestamps_map_; + + absl::optional<EventTimestamps> pending_pointer_up_timestamps_; + absl::optional<EventTimestamps> pending_pointer_down_timestamps_; + bool is_drag_ = false; + + // Whether to perform UKM sampling. + bool sampling_ = true; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_RESPONSIVENESS_METRICS_H_ diff --git a/chromium/third_party/blink/renderer/core/timing/time_clamper.h b/chromium/third_party/blink/renderer/core/timing/time_clamper.h index 7504ab11664..05908af8f07 100644 --- a/chromium/third_party/blink/renderer/core/timing/time_clamper.h +++ b/chromium/third_party/blink/renderer/core/timing/time_clamper.h @@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_TIME_CLAMPER_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_TIME_CLAMPER_H_ -#include "base/macros.h" +#include "base/time/time.h" #include "build/build_config.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -22,6 +22,8 @@ class CORE_EXPORT TimeClamper { static constexpr int kFineResolutionMicroseconds = 5; TimeClamper(); + TimeClamper(const TimeClamper&) = delete; + TimeClamper& operator=(const TimeClamper&) = delete; // Deterministically clamp the time value |time_microseconds| to a fixed // interval to prevent timing attacks. See @@ -41,8 +43,6 @@ class CORE_EXPORT TimeClamper { static inline uint64_t MurmurHash3(uint64_t value); uint64_t secret_; - - DISALLOW_COPY_AND_ASSIGN(TimeClamper); }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/window_performance.cc b/chromium/third_party/blink/renderer/core/timing/window_performance.cc index eddf6d27a08..4c1a84ecf36 100644 --- a/chromium/third_party/blink/renderer/core/timing/window_performance.cc +++ b/chromium/third_party/blink/renderer/core/timing/window_performance.cc @@ -41,6 +41,8 @@ #include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/qualified_name.h" +#include "third_party/blink/renderer/core/events/keyboard_event.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/core/frame/local_frame.h" #include "third_party/blink/renderer/core/html/html_frame_owner_element.h" @@ -56,6 +58,7 @@ #include "third_party/blink/renderer/core/timing/performance_event_timing.h" #include "third_party/blink/renderer/core/timing/performance_observer.h" #include "third_party/blink/renderer/core/timing/performance_timing.h" +#include "third_party/blink/renderer/core/timing/responsiveness_metrics.h" #include "third_party/blink/renderer/core/timing/visibility_state_entry.h" #include "third_party/blink/renderer/platform/heap/persistent.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" @@ -94,6 +97,8 @@ AtomicString GetFrameOwnerType(HTMLFrameOwnerElement* frame_owner) { return "frame"; case mojom::blink::FrameOwnerElementType::kPortal: return "portal"; + case mojom::blink::FrameOwnerElementType::kFencedframe: + return "fencedframe"; } NOTREACHED(); return ""; @@ -169,6 +174,10 @@ WindowPerformance::WindowPerformance(LocalDOMWindow* window) } } +void WindowPerformance::EventData::Trace(Visitor* visitor) const { + visitor->Trace(event_timing_); +} + WindowPerformance::~WindowPerformance() = default; ExecutionContext* WindowPerformance::GetExecutionContext() const { @@ -237,11 +246,12 @@ void WindowPerformance::BuildJSONValue(V8ObjectBuilder& builder) const { } void WindowPerformance::Trace(Visitor* visitor) const { - visitor->Trace(event_timings_); + visitor->Trace(events_data_); visitor->Trace(first_pointer_down_event_timing_); visitor->Trace(event_counts_); visitor->Trace(navigation_); visitor->Trace(timing_); + visitor->Trace(current_event_); Performance::Trace(visitor); PerformanceMonitor::Client::Trace(visitor); ExecutionContextClient::Trace(visitor); @@ -342,43 +352,35 @@ void WindowPerformance::ReportLongTask(base::TimeTicks start_time, } } -void WindowPerformance::RegisterEventTiming(const AtomicString& event_type, - base::TimeTicks start_time, - base::TimeTicks processing_start, - base::TimeTicks processing_end, - bool cancelable, - Node* target) { +void WindowPerformance::RegisterEventTiming( + const AtomicString& event_type, + base::TimeTicks start_time, + base::TimeTicks processing_start, + base::TimeTicks processing_end, + bool cancelable, + Node* target, + absl::optional<int> key_code, + absl::optional<PointerId> pointer_id) { // |start_time| could be null in some tests that inject input. DCHECK(!processing_start.is_null()); DCHECK(!processing_end.is_null()); DCHECK_GE(processing_end, processing_start); if (!DomWindow()) return; - - // Count non-pointerevent Events. Avoid double counting pointerevents - // because we count them in pointer_event_manager.cc. Note click, auxclick and - // contextmenu are PointerEvent but the dispatch process of them are different - // from other PointerEvent. - if (event_type != event_type_names::kPointerover && - event_type != event_type_names::kPointerenter && - event_type != event_type_names::kPointerdown && - event_type != event_type_names::kPointerup && - event_type != event_type_names::kPointercancel && - event_type != event_type_names::kPointerout && - event_type != event_type_names::kPointerleave && - event_type != event_type_names::kGotpointercapture && - event_type != event_type_names::kLostpointercapture) { - eventCounts()->Add(event_type); + if (event_type == event_type_names::kPointermove) { + NotifyPotentialDrag(); + SetCurrentEventTimingEvent(nullptr); + return; } - + eventCounts()->Add(event_type); PerformanceEventTiming* entry = PerformanceEventTiming::Create( event_type, MonotonicTimeToDOMHighResTimeStamp(start_time), MonotonicTimeToDOMHighResTimeStamp(processing_start), MonotonicTimeToDOMHighResTimeStamp(processing_end), cancelable, target); // Add |entry| to the end of the queue along with the frame index at which is // is being queued to know when to queue a presentation promise for it. - event_timings_.push_back(entry); - event_frames_.push_back(frame_index_); + events_data_.push_back( + EventData::Create(entry, frame_index_, start_time, key_code, pointer_id)); bool should_queue_presentation_promise = false; // If there are no pending presentation promises, we should queue one. This // ensures that |event_timings_| are processed even if the Blink lifecycle @@ -400,37 +402,43 @@ void WindowPerformance::RegisterEventTiming(const AtomicString& event_type, last_registered_frame_index_ = frame_index_; ++pending_presentation_promise_count_; } + SetCurrentEventTimingEvent(nullptr); } -void WindowPerformance::ReportEventTimings(uint64_t frame_index, - WebSwapResult result, - base::TimeTicks timestamp) { +void WindowPerformance::ReportEventTimings( + uint64_t frame_index, + WebSwapResult result, + base::TimeTicks presentation_timestamp) { DCHECK(pending_presentation_promise_count_); --pending_presentation_promise_count_; - // |event_timings_| and |event_frames_| should always have the same size. - DCHECK(event_timings_.size() == event_frames_.size()); - if (event_timings_.IsEmpty()) + if (events_data_.IsEmpty()) return; if (!DomWindow()) return; InteractiveDetector* interactive_detector = InteractiveDetector::From(*(DomWindow()->document())); - bool event_timing_enabled = - RuntimeEnabledFeatures::EventTimingEnabled(GetExecutionContext()); - DOMHighResTimeStamp end_time = MonotonicTimeToDOMHighResTimeStamp(timestamp); - while (!event_timings_.IsEmpty()) { - PerformanceEventTiming* entry = event_timings_.front(); - uint64_t entry_frame_index = event_frames_.front(); + DOMHighResTimeStamp end_time = + MonotonicTimeToDOMHighResTimeStamp(presentation_timestamp); + while (!events_data_.IsEmpty()) { + auto event_data = events_data_.front(); + PerformanceEventTiming* entry = event_data->GetEventTiming(); + uint64_t entry_frame_index = event_data->GetFrameIndex(); + base::TimeTicks event_timestamp = event_data->GetEventTimestamp(); + absl::optional<int> key_code = event_data->GetKeyCode(); + absl::optional<PointerId> pointer_id = event_data->GetPointerId(); // If the entry was queued at a frame index that is larger than // |frame_index|, then we've reached the end of the entries that we can // process during this callback. if (entry_frame_index > frame_index) break; - event_timings_.pop_front(); - event_frames_.pop_front(); + events_data_.pop_front(); + ResponsivenessMetrics::EventTimestamps event_timestamps = { + event_timestamp, presentation_timestamp}; + responsiveness_metrics_.RecordPerInteractionLatency( + DomWindow(), entry->name(), key_code, pointer_id, event_timestamps); int duration_in_ms = std::round((end_time - entry->startTime()) / 8) * 8; base::TimeDelta input_delay = base::TimeDelta::FromMillisecondsD( entry->processingStart() - entry->startTime()); @@ -473,8 +481,6 @@ void WindowPerformance::ReportEventTimings(uint64_t frame_index, PerformanceEventTiming::CreateFirstInputTiming(entry)); } } - if (!event_timing_enabled) - continue; if (HasObserverFor(PerformanceEntry::kEvent)) { UseCounter::Count(GetExecutionContext(), @@ -555,7 +561,6 @@ void WindowPerformance::PageVisibilityChanged() { } EventCounts* WindowPerformance::eventCounts() { - DCHECK(RuntimeEnabledFeatures::EventTimingEnabled(GetExecutionContext())); if (!event_counts_) event_counts_ = MakeGarbageCollected<EventCounts>(); return event_counts_; @@ -584,4 +589,8 @@ void WindowPerformance::OnPaintFinished() { ++frame_index_; } +void WindowPerformance::NotifyPotentialDrag() { + responsiveness_metrics_.NotifyPotentialDrag(); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/window_performance.h b/chromium/third_party/blink/renderer/core/timing/window_performance.h index 44bac04a32c..93d1f69191d 100644 --- a/chromium/third_party/blink/renderer/core/timing/window_performance.h +++ b/chromium/third_party/blink/renderer/core/timing/window_performance.h @@ -35,14 +35,17 @@ #include "base/rand_util.h" #include "third_party/blink/public/web/web_swap_result.h" #include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/events/pointer_event.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" #include "third_party/blink/renderer/core/frame/performance_monitor.h" #include "third_party/blink/renderer/core/page/page_visibility_observer.h" #include "third_party/blink/renderer/core/timing/event_counts.h" +#include "third_party/blink/renderer/core/timing/event_timing.h" #include "third_party/blink/renderer/core/timing/memory_info.h" #include "third_party/blink/renderer/core/timing/performance.h" #include "third_party/blink/renderer/core/timing/performance_navigation.h" #include "third_party/blink/renderer/core/timing/performance_timing.h" +#include "third_party/blink/renderer/core/timing/responsiveness_metrics.h" #include "third_party/blink/renderer/platform/heap/heap_allocator.h" namespace blink { @@ -55,6 +58,52 @@ class CORE_EXPORT WindowPerformance final : public Performance, public PageVisibilityObserver { friend class WindowPerformanceTest; + class EventData : public GarbageCollected<EventData> { + public: + EventData(PerformanceEventTiming* event_timing, + uint64_t frame, + base::TimeTicks event_timestamp, + absl::optional<int> key_code, + absl::optional<PointerId> pointer_id) + : event_timing_(event_timing), + frame_(frame), + event_timestamp_(event_timestamp), + key_code_(key_code), + pointer_id_(pointer_id) {} + + static EventData* Create(PerformanceEventTiming* event_timing, + uint64_t frame, + base::TimeTicks event_timestamp, + absl::optional<int> key_code, + absl::optional<PointerId> pointer_id) { + return MakeGarbageCollected<EventData>( + event_timing, frame, event_timestamp, key_code, pointer_id); + } + ~EventData() = default; + void Trace(Visitor*) const; + PerformanceEventTiming* GetEventTiming() { return event_timing_; } + uint64_t GetFrameIndex() { return frame_; } + base::TimeTicks GetEventTimestamp() { return event_timestamp_; } + absl::optional<int> GetKeyCode() { return key_code_; } + absl::optional<PointerId> GetPointerId() { return pointer_id_; } + + private: + // Event 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. + Member<PerformanceEventTiming> event_timing_; + // Frame index in which the entry in |event_timing_| were added. + uint64_t frame_; + // The event creation timestamp. + base::TimeTicks event_timestamp_; + // Keycode for the event. If the event is not a keyboard event, the keycode + // wouldn't be set. + absl::optional<int> key_code_; + // PointerId for the event. If the event is not a pointer event, the + // PointerId wouldn't be set. + absl::optional<PointerId> pointer_id_; + }; + public: explicit WindowPerformance(LocalDOMWindow*); ~WindowPerformance() override; @@ -78,7 +127,9 @@ class CORE_EXPORT WindowPerformance final : public Performance, base::TimeTicks processing_start, base::TimeTicks processing_end, bool cancelable, - Node*); + Node*, + absl::optional<int> key_code, + absl::optional<PointerId> pointer_id); void OnPaintFinished(); @@ -107,6 +158,17 @@ class CORE_EXPORT WindowPerformance final : public Performance, void Trace(Visitor*) const override; + ResponsivenessMetrics& GetResponsivenessMetrics() { + return responsiveness_metrics_; + } + + void NotifyPotentialDrag(); + + void SetCurrentEventTimingEvent(const Event* event) { + current_event_ = event; + } + const Event* GetCurrentEventTimingEvent() { return current_event_; } + private: PerformanceNavigationTiming* CreateNavigationTimingInstance() override; @@ -123,11 +185,12 @@ class CORE_EXPORT WindowPerformance final : public Performance, void BuildJSONValue(V8ObjectBuilder&) const override; - // Method called once presentation promise is resolved. It will add all event - // timings that have not been added since the last presentation promise. + // Method called once presentation promise for a frame is resolved. It will + // add all event timings that have not been added since the last presentation + // promise. void ReportEventTimings(uint64_t frame_index, WebSwapResult result, - base::TimeTicks timestamp); + base::TimeTicks presentation_timestamp); void DispatchFirstInputTiming(PerformanceEventTiming* entry); @@ -138,19 +201,10 @@ class CORE_EXPORT WindowPerformance final : public Performance, uint64_t last_registered_frame_index_ = 0; // Number of pending presentation promises. uint16_t pending_presentation_promise_count_ = 0; - // PerformanceEventTiming entries that have 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. It is handled as a queue: - // FIFO. - HeapDeque<Member<PerformanceEventTiming>> event_timings_; - // Entries corresponding to frame indices in which the entries in - // |event_timings_| were added. This could be combined with |event_timings_| - // into a single deque, but PerformanceEventTiming is GarbageCollected so it - // would need to be a HeapDeque. HeapDeque does not allow std::pair as its - // type, so we would have to add a new wrapper GarbageCollected class that - // contains the PerformanceEventTiming object as well as the frame index. This - // is more work than having two separate deques. - Deque<uint64_t> event_frames_; + // Store all event timing and latency related data, including + // PerformanceEventTiming, frame_index, keycode and pointerId. We use the data + // to calculate events latencies. + HeapDeque<Member<EventData>> events_data_; Member<PerformanceEventTiming> first_pointer_down_event_timing_; Member<EventCounts> event_counts_; mutable Member<PerformanceNavigation> navigation_; @@ -158,6 +212,11 @@ class CORE_EXPORT WindowPerformance final : public Performance, absl::optional<base::TimeDelta> pending_pointer_down_input_delay_; absl::optional<base::TimeDelta> pending_pointer_down_processing_time_; absl::optional<base::TimeDelta> pending_pointer_down_time_to_next_paint_; + + // Calculate responsiveness metrics and record UKM for them. + ResponsivenessMetrics responsiveness_metrics_; + // The event we are currently processing. + WeakMember<const Event> current_event_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/window_performance.idl b/chromium/third_party/blink/renderer/core/timing/window_performance.idl index b55f5b40642..efebb32ca90 100644 --- a/chromium/third_party/blink/renderer/core/timing/window_performance.idl +++ b/chromium/third_party/blink/renderer/core/timing/window_performance.idl @@ -9,5 +9,5 @@ [ ImplementedAs=DOMWindowPerformance ] partial interface Window { - [Affects=Nothing, Replaceable] readonly attribute Performance performance; + [Replaceable] readonly attribute Performance performance; }; diff --git a/chromium/third_party/blink/renderer/core/timing/window_performance_test.cc b/chromium/third_party/blink/renderer/core/timing/window_performance_test.cc index 62ca695d3b7..37336dd18f1 100644 --- a/chromium/third_party/blink/renderer/core/timing/window_performance_test.cc +++ b/chromium/third_party/blink/renderer/core/timing/window_performance_test.cc @@ -5,8 +5,8 @@ #include "third_party/blink/renderer/core/timing/window_performance.h" #include "base/test/test_mock_time_task_runner.h" +#include "services/metrics/public/cpp/ukm_builders.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/bindings/core/v8/string_or_double.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h" #include "third_party/blink/renderer/core/dom/document_init.h" @@ -18,18 +18,25 @@ #include "third_party/blink/renderer/core/loader/document_loader.h" #include "third_party/blink/renderer/core/testing/dummy_page_holder.h" #include "third_party/blink/renderer/core/testing/mock_policy_container_host.h" +#include "third_party/blink/renderer/core/testing/scoped_fake_ukm_recorder.h" #include "third_party/blink/renderer/core/timing/dom_window_performance.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" -#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" +#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" namespace blink { +using test::RunPendingTasks; + namespace { base::TimeTicks GetTimeOrigin() { return base::TimeTicks() + base::TimeDelta::FromSeconds(500); } +base::TimeTicks GetTimeStamp(int64_t time) { + return GetTimeOrigin() + base::TimeDelta::FromMilliseconds(time); +} + } // namespace class WindowPerformanceTest : public testing::Test { @@ -90,16 +97,23 @@ class WindowPerformanceTest : public testing::Test { performance_->SetClocksForTesting(test_task_runner_->GetMockClock(), test_task_runner_->GetMockTickClock()); performance_->time_origin_ = GetTimeOrigin(); + // Stop UKM sampling for testing. + performance_->GetResponsivenessMetrics().StopUkmSamplingForTesting(); } ScriptState* GetScriptState() const { return ToScriptStateForMainWorld(page_holder_->GetDocument().GetFrame()); } + ukm::TestUkmRecorder* GetUkmRecorder() { + return scoped_fake_ukm_recorder_.recorder(); + } + uint64_t frame_counter = 1; Persistent<WindowPerformance> performance_; std::unique_ptr<DummyPageHolder> page_holder_; scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_; + ScopedFakeUkmRecorder scoped_fake_ukm_recorder_; }; TEST_F(WindowPerformanceTest, LongTaskObserverInstrumentation) { @@ -237,7 +251,6 @@ TEST_F(WindowPerformanceTest, EnsureEntryListOrder) { } TEST_F(WindowPerformanceTest, EventTimingEntryBuffering) { - ScopedEventTimingForTest event_timing(true); EXPECT_TRUE(page_holder_->GetFrame().Loader().GetDocumentLoader()); base::TimeTicks start_time = @@ -247,7 +260,7 @@ TEST_F(WindowPerformanceTest, EventTimingEntryBuffering) { base::TimeTicks processing_end = GetTimeOrigin() + base::TimeDelta::FromSecondsD(3.8); performance_->RegisterEventTiming("click", start_time, processing_start, - processing_end, false, nullptr); + processing_end, false, nullptr, 4, 4); base::TimeTicks swap_time = GetTimeOrigin() + base::TimeDelta::FromSecondsD(6.0); SimulateSwapPromise(swap_time); @@ -259,7 +272,7 @@ TEST_F(WindowPerformanceTest, EventTimingEntryBuffering) { ->GetTiming() .MarkLoadEventStart(); performance_->RegisterEventTiming("click", start_time, processing_start, - processing_end, true, nullptr); + processing_end, true, nullptr, 4, 4); SimulateSwapPromise(swap_time); EXPECT_EQ(2u, performance_->getBufferedEntriesByType("event").size()); @@ -267,13 +280,12 @@ TEST_F(WindowPerformanceTest, EventTimingEntryBuffering) { GetFrame()->DetachDocument(); EXPECT_FALSE(page_holder_->GetFrame().Loader().GetDocumentLoader()); performance_->RegisterEventTiming("click", start_time, processing_start, - processing_end, false, nullptr); + processing_end, false, nullptr, 4, 4); SimulateSwapPromise(swap_time); EXPECT_EQ(3u, performance_->getBufferedEntriesByType("event").size()); } TEST_F(WindowPerformanceTest, Expose100MsEvents) { - ScopedEventTimingForTest event_timing(true); base::TimeTicks start_time = GetTimeOrigin() + base::TimeDelta::FromSeconds(1); base::TimeTicks processing_start = @@ -281,12 +293,12 @@ TEST_F(WindowPerformanceTest, Expose100MsEvents) { base::TimeTicks processing_end = processing_start + base::TimeDelta::FromMilliseconds(10); performance_->RegisterEventTiming("mousedown", start_time, processing_start, - processing_end, false, nullptr); + processing_end, false, nullptr, 4, 4); base::TimeTicks start_time2 = start_time + base::TimeDelta::FromMicroseconds(200); performance_->RegisterEventTiming("click", start_time2, processing_start, - processing_end, false, nullptr); + processing_end, false, nullptr, 4, 4); // The swap time is 100.1 ms after |start_time| but only 99.9 ms after // |start_time2|. @@ -300,8 +312,6 @@ TEST_F(WindowPerformanceTest, Expose100MsEvents) { } TEST_F(WindowPerformanceTest, EventTimingDuration) { - ScopedEventTimingForTest event_timing(true); - base::TimeTicks start_time = GetTimeOrigin() + base::TimeDelta::FromMilliseconds(1000); base::TimeTicks processing_start = @@ -309,24 +319,24 @@ TEST_F(WindowPerformanceTest, EventTimingDuration) { base::TimeTicks processing_end = GetTimeOrigin() + base::TimeDelta::FromMilliseconds(1002); performance_->RegisterEventTiming("click", start_time, processing_start, - processing_end, false, nullptr); + processing_end, false, nullptr, 4, 4); base::TimeTicks short_swap_time = GetTimeOrigin() + base::TimeDelta::FromMilliseconds(1003); SimulateSwapPromise(short_swap_time); EXPECT_EQ(0u, performance_->getBufferedEntriesByType("event").size()); performance_->RegisterEventTiming("click", start_time, processing_start, - processing_end, true, nullptr); + processing_end, true, nullptr, 4, 4); base::TimeTicks long_swap_time = GetTimeOrigin() + base::TimeDelta::FromMilliseconds(2000); SimulateSwapPromise(long_swap_time); EXPECT_EQ(1u, performance_->getBufferedEntriesByType("event").size()); performance_->RegisterEventTiming("click", start_time, processing_start, - processing_end, true, nullptr); + processing_end, true, nullptr, 4, 4); SimulateSwapPromise(short_swap_time); performance_->RegisterEventTiming("click", start_time, processing_start, - processing_end, false, nullptr); + processing_end, false, nullptr, 4, 4); SimulateSwapPromise(long_swap_time); EXPECT_EQ(2u, performance_->getBufferedEntriesByType("event").size()); } @@ -334,8 +344,6 @@ TEST_F(WindowPerformanceTest, EventTimingDuration) { // Test the case where multiple events are registered and then their swap // promise is resolved. TEST_F(WindowPerformanceTest, MultipleEventsThenSwap) { - ScopedEventTimingForTest event_timing(true); - size_t num_events = 10; for (size_t i = 0; i < num_events; ++i) { base::TimeTicks start_time = @@ -345,7 +353,7 @@ TEST_F(WindowPerformanceTest, MultipleEventsThenSwap) { base::TimeTicks processing_end = start_time + base::TimeDelta::FromMilliseconds(200); performance_->RegisterEventTiming("click", start_time, processing_start, - processing_end, false, nullptr); + processing_end, false, nullptr, 4, 4); EXPECT_EQ(0u, performance_->getBufferedEntriesByType("event").size()); } base::TimeTicks swap_time = @@ -367,7 +375,8 @@ TEST_F(WindowPerformanceTest, FirstInput) { performance_->RegisterEventTiming( input.event_type, GetTimeOrigin(), GetTimeOrigin() + base::TimeDelta::FromMilliseconds(1), - GetTimeOrigin() + base::TimeDelta::FromMilliseconds(2), false, nullptr); + GetTimeOrigin() + base::TimeDelta::FromMilliseconds(2), false, nullptr, + 4, 4); SimulateSwapPromise(GetTimeOrigin() + base::TimeDelta::FromMilliseconds(3)); PerformanceEntryVector firstInputs = performance_->getEntriesByType("first-input"); @@ -385,7 +394,8 @@ TEST_F(WindowPerformanceTest, FirstInputAfterIgnored) { performance_->RegisterEventTiming( event, GetTimeOrigin(), GetTimeOrigin() + base::TimeDelta::FromMilliseconds(1), - GetTimeOrigin() + base::TimeDelta::FromMilliseconds(2), false, nullptr); + GetTimeOrigin() + base::TimeDelta::FromMilliseconds(2), false, nullptr, + 4, 4); SimulateSwapPromise(GetTimeOrigin() + base::TimeDelta::FromMilliseconds(3)); } ASSERT_EQ(1u, performance_->getEntriesByType("first-input").size()); @@ -395,19 +405,16 @@ TEST_F(WindowPerformanceTest, FirstInputAfterIgnored) { // Test that pointerdown followed by pointerup works as a 'firstInput'. TEST_F(WindowPerformanceTest, FirstPointerUp) { - base::TimeTicks start_time = GetTimeOrigin(); - base::TimeTicks processing_start = - GetTimeOrigin() + base::TimeDelta::FromMilliseconds(1); - base::TimeTicks processing_end = - GetTimeOrigin() + base::TimeDelta::FromMilliseconds(2); - base::TimeTicks swap_time = - GetTimeOrigin() + base::TimeDelta::FromMilliseconds(3); + base::TimeTicks start_time = GetTimeStamp(0); + base::TimeTicks processing_start = GetTimeStamp(1); + base::TimeTicks processing_end = GetTimeStamp(2); + base::TimeTicks swap_time = GetTimeStamp(3); performance_->RegisterEventTiming("pointerdown", start_time, processing_start, - processing_end, false, nullptr); + processing_end, false, nullptr, 4, 4); SimulateSwapPromise(swap_time); EXPECT_EQ(0u, performance_->getEntriesByType("first-input").size()); performance_->RegisterEventTiming("pointerup", start_time, processing_start, - processing_end, false, nullptr); + processing_end, false, nullptr, 4, 4); SimulateSwapPromise(swap_time); EXPECT_EQ(1u, performance_->getEntriesByType("first-input").size()); // The name of the entry should be "pointerdown". @@ -415,4 +422,446 @@ TEST_F(WindowPerformanceTest, FirstPointerUp) { 1u, performance_->getEntriesByName("pointerdown", "first-input").size()); } +TEST_F(WindowPerformanceTest, OneKeyboardInteraction) { + base::TimeTicks keydown_timestamp = GetTimeStamp(0); + // Keydown + base::TimeTicks processing_start_keydown = GetTimeStamp(1); + base::TimeTicks processing_end_keydown = GetTimeStamp(2); + base::TimeTicks swap_time_keydown = GetTimeStamp(5); + absl::optional<PointerId> pointer_id = absl::nullopt; + absl::optional<int> key_code = 2; + performance_->RegisterEventTiming( + "keydown", keydown_timestamp, processing_start_keydown, + processing_end_keydown, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_keydown); + // Keyup + base::TimeTicks keyup_timestamp = GetTimeStamp(3); + base::TimeTicks processing_start_keyup = GetTimeStamp(5); + base::TimeTicks processing_end_keyup = GetTimeStamp(6); + base::TimeTicks swap_time_keyup = GetTimeStamp(10); + performance_->RegisterEventTiming( + "keyup", keyup_timestamp, processing_start_keyup, processing_end_keyup, + false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_keyup); + + // Flush UKM logging mojo request. + RunPendingTasks(); + + // Check UKM recording. + auto merged_entries = GetUkmRecorder()->GetMergedEntriesByName( + ukm::builders::Responsiveness_UserInteraction::kEntryName); + EXPECT_EQ(1u, merged_entries.size()); + for (const auto& kv : merged_entries) { + const ukm::mojom::UkmEntry* ukm_entry = kv.second.get(); + GetUkmRecorder()->ExpectEntryMetric( + ukm_entry, + ukm::builders::Responsiveness_UserInteraction::kMaxEventDurationName, + 7); + GetUkmRecorder()->ExpectEntryMetric( + ukm_entry, + ukm::builders::Responsiveness_UserInteraction::kTotalEventDurationName, + 10); + GetUkmRecorder()->ExpectEntryMetric( + ukm_entry, + ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 0); + } +} + +TEST_F(WindowPerformanceTest, HoldingDownAKey) { + auto entries = GetUkmRecorder()->GetEntriesByName( + ukm::builders::Responsiveness_UserInteraction::kEntryName); + EXPECT_EQ(0u, entries.size()); + base::TimeTicks keydown_timestamp = GetTimeOrigin(); + base::TimeTicks processing_start_keydown = GetTimeStamp(1); + base::TimeTicks processing_end_keydown = GetTimeStamp(2); + base::TimeTicks swap_time_keydown = GetTimeStamp(5); + absl::optional<PointerId> pointer_id = absl::nullopt; + absl::optional<int> key_code = 2; + performance_->RegisterEventTiming( + "keydown", keydown_timestamp, processing_start_keydown, + processing_end_keydown, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_keydown); + + // Second Keydown + keydown_timestamp = GetTimeStamp(1); + processing_start_keydown = GetTimeStamp(2); + processing_end_keydown = GetTimeStamp(3); + swap_time_keydown = GetTimeStamp(7); + performance_->RegisterEventTiming( + "keydown", keydown_timestamp, processing_start_keydown, + processing_end_keydown, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_keydown); + + // Third Keydown + keydown_timestamp = GetTimeStamp(2); + processing_start_keydown = GetTimeStamp(3); + processing_end_keydown = GetTimeStamp(5); + swap_time_keydown = GetTimeStamp(9); + performance_->RegisterEventTiming( + "keydown", keydown_timestamp, processing_start_keydown, + processing_end_keydown, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_keydown); + + // Keyup + base::TimeTicks keyup_timestamp = GetTimeStamp(3); + base::TimeTicks processing_start_keyup = GetTimeStamp(5); + base::TimeTicks processing_end_keyup = GetTimeStamp(6); + base::TimeTicks swap_time_keyup = GetTimeStamp(13); + performance_->RegisterEventTiming( + "keyup", keyup_timestamp, processing_start_keyup, processing_end_keyup, + false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_keyup); + + // Flush UKM logging mojo request. + RunPendingTasks(); + + // Check UKM recording. + entries = GetUkmRecorder()->GetEntriesByName( + ukm::builders::Responsiveness_UserInteraction::kEntryName); + EXPECT_EQ(3u, entries.size()); + std::vector<std::pair<int, int>> expected_durations; + expected_durations.emplace_back(std::make_pair(5, 5)); + expected_durations.emplace_back(std::make_pair(6, 6)); + expected_durations.emplace_back(std::make_pair(10, 11)); + for (std::size_t i = 0; i < entries.size(); ++i) { + auto* entry = entries[i]; + GetUkmRecorder()->ExpectEntryMetric( + entry, + ukm::builders::Responsiveness_UserInteraction::kMaxEventDurationName, + expected_durations[i].first); + GetUkmRecorder()->ExpectEntryMetric( + entry, + ukm::builders::Responsiveness_UserInteraction::kTotalEventDurationName, + expected_durations[i].second); + GetUkmRecorder()->ExpectEntryMetric( + entry, + ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 0); + } +} + +TEST_F(WindowPerformanceTest, PressMultipleKeys) { + auto entries = GetUkmRecorder()->GetEntriesByName( + ukm::builders::Responsiveness_UserInteraction::kEntryName); + EXPECT_EQ(0u, entries.size()); + // Press the first key. + base::TimeTicks keydown_timestamp = GetTimeOrigin(); + base::TimeTicks processing_start_keydown = GetTimeStamp(1); + base::TimeTicks processing_end_keydown = GetTimeStamp(2); + base::TimeTicks swap_time_keydown = GetTimeStamp(5); + absl::optional<PointerId> pointer_id = absl::nullopt; + absl::optional<int> first_key_code = 2; + performance_->RegisterEventTiming( + "keydown", keydown_timestamp, processing_start_keydown, + processing_end_keydown, false, nullptr, first_key_code, pointer_id); + SimulateSwapPromise(swap_time_keydown); + + // Press the second key. + processing_start_keydown = GetTimeStamp(2); + processing_end_keydown = GetTimeStamp(3); + swap_time_keydown = GetTimeStamp(7); + absl::optional<int> second_key_code = 4; + performance_->RegisterEventTiming( + "keydown", keydown_timestamp, processing_start_keydown, + processing_end_keydown, false, nullptr, second_key_code, pointer_id); + SimulateSwapPromise(swap_time_keydown); + + // Release the first key. + base::TimeTicks keyup_timestamp = GetTimeStamp(3); + base::TimeTicks processing_start_keyup = GetTimeStamp(5); + base::TimeTicks processing_end_keyup = GetTimeStamp(6); + base::TimeTicks swap_time_keyup = GetTimeStamp(13); + performance_->RegisterEventTiming( + "keyup", keyup_timestamp, processing_start_keyup, processing_end_keyup, + false, nullptr, first_key_code, pointer_id); + SimulateSwapPromise(swap_time_keyup); + + // Release the second key. + keyup_timestamp = GetTimeStamp(5); + processing_start_keyup = GetTimeStamp(5); + processing_end_keyup = GetTimeStamp(6); + swap_time_keyup = GetTimeStamp(20); + performance_->RegisterEventTiming( + "keyup", keyup_timestamp, processing_start_keyup, processing_end_keyup, + false, nullptr, second_key_code, pointer_id); + SimulateSwapPromise(swap_time_keyup); + + // Flush UKM logging mojo request. + RunPendingTasks(); + + // Check UKM recording. + entries = GetUkmRecorder()->GetEntriesByName( + ukm::builders::Responsiveness_UserInteraction::kEntryName); + EXPECT_EQ(2u, entries.size()); + std::vector<std::pair<int, int>> expected_durations; + expected_durations.emplace_back(std::make_pair(10, 13)); + expected_durations.emplace_back(std::make_pair(15, 20)); + for (std::size_t i = 0; i < entries.size(); ++i) { + auto* entry = entries[i]; + GetUkmRecorder()->ExpectEntryMetric( + entry, + ukm::builders::Responsiveness_UserInteraction::kMaxEventDurationName, + expected_durations[i].first); + GetUkmRecorder()->ExpectEntryMetric( + entry, + ukm::builders::Responsiveness_UserInteraction::kTotalEventDurationName, + expected_durations[i].second); + GetUkmRecorder()->ExpectEntryMetric( + entry, + ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 0); + } +} + +TEST_F(WindowPerformanceTest, TapOrClick) { + // Pointerdown + base::TimeTicks pointerdwon_timestamp = GetTimeOrigin(); + base::TimeTicks processing_start_pointerdown = GetTimeStamp(1); + base::TimeTicks processing_end_pointerdown = GetTimeStamp(2); + base::TimeTicks swap_time_pointerdown = GetTimeStamp(5); + absl::optional<PointerId> pointer_id = 4; + absl::optional<int> key_code = absl::nullopt; + performance_->RegisterEventTiming( + "pointerdown", pointerdwon_timestamp, processing_start_pointerdown, + processing_end_pointerdown, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_pointerdown); + // Pointerup + base::TimeTicks pointerup_timestamp = GetTimeStamp(3); + base::TimeTicks processing_start_pointerup = GetTimeStamp(5); + base::TimeTicks processing_end_pointerup = GetTimeStamp(6); + base::TimeTicks swap_time_pointerup = GetTimeStamp(10); + performance_->RegisterEventTiming( + "pointerup", pointerup_timestamp, processing_start_pointerup, + processing_end_pointerup, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_pointerup); + // Click + base::TimeTicks click_timestamp = GetTimeStamp(13); + base::TimeTicks processing_start_click = GetTimeStamp(15); + base::TimeTicks processing_end_click = GetTimeStamp(16); + base::TimeTicks swap_time_click = GetTimeStamp(20); + performance_->RegisterEventTiming( + "click", click_timestamp, processing_start_click, processing_end_click, + false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_click); + + // Flush UKM logging mojo request. + RunPendingTasks(); + + // Check UKM recording. + std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries = + GetUkmRecorder()->GetMergedEntriesByName( + ukm::builders::Responsiveness_UserInteraction::kEntryName); + EXPECT_EQ(1u, merged_entries.size()); + for (const auto& kv : merged_entries) { + const ukm::mojom::UkmEntry* ukm_entry = kv.second.get(); + GetUkmRecorder()->ExpectEntryMetric( + ukm_entry, + ukm::builders::Responsiveness_UserInteraction::kMaxEventDurationName, + 7); + GetUkmRecorder()->ExpectEntryMetric( + ukm_entry, + ukm::builders::Responsiveness_UserInteraction::kTotalEventDurationName, + 17); + GetUkmRecorder()->ExpectEntryMetric( + ukm_entry, + ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 1); + } +} + +TEST_F(WindowPerformanceTest, Drag) { + // Pointerdown + base::TimeTicks pointerdwon_timestamp = GetTimeOrigin(); + base::TimeTicks processing_start_pointerdown = GetTimeStamp(1); + base::TimeTicks processing_end_pointerdown = GetTimeStamp(2); + base::TimeTicks swap_time_pointerdown = GetTimeStamp(5); + absl::optional<PointerId> pointer_id = 4; + absl::optional<int> key_code = absl::nullopt; + performance_->RegisterEventTiming( + "pointerdown", pointerdwon_timestamp, processing_start_pointerdown, + processing_end_pointerdown, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_pointerdown); + // Notify drag. + performance_->NotifyPotentialDrag(); + // Pointerup + base::TimeTicks pointerup_timestamp = GetTimeStamp(3); + base::TimeTicks processing_start_pointerup = GetTimeStamp(5); + base::TimeTicks processing_end_pointerup = GetTimeStamp(6); + base::TimeTicks swap_time_pointerup = GetTimeStamp(10); + performance_->RegisterEventTiming( + "pointerup", pointerup_timestamp, processing_start_pointerup, + processing_end_pointerup, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_pointerup); + // Click + base::TimeTicks click_timestamp = GetTimeStamp(13); + base::TimeTicks processing_start_click = GetTimeStamp(15); + base::TimeTicks processing_end_click = GetTimeStamp(16); + base::TimeTicks swap_time_click = GetTimeStamp(20); + performance_->RegisterEventTiming( + "click", click_timestamp, processing_start_click, processing_end_click, + false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_click); + + // Flush UKM logging mojo request. + RunPendingTasks(); + + // Check UKM recording. + std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries = + GetUkmRecorder()->GetMergedEntriesByName( + ukm::builders::Responsiveness_UserInteraction::kEntryName); + EXPECT_EQ(1u, merged_entries.size()); + for (const auto& kv : merged_entries) { + const ukm::mojom::UkmEntry* ukm_entry = kv.second.get(); + GetUkmRecorder()->ExpectEntryMetric( + ukm_entry, + ukm::builders::Responsiveness_UserInteraction::kMaxEventDurationName, + 7); + GetUkmRecorder()->ExpectEntryMetric( + ukm_entry, + ukm::builders::Responsiveness_UserInteraction::kTotalEventDurationName, + 17); + GetUkmRecorder()->ExpectEntryMetric( + ukm_entry, + ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 2); + } +} + +TEST_F(WindowPerformanceTest, Scroll) { + // Pointerdown + base::TimeTicks pointerdown_timestamp = GetTimeOrigin(); + base::TimeTicks processing_start_keydown = GetTimeStamp(1); + base::TimeTicks processing_end_keydown = GetTimeStamp(2); + base::TimeTicks swap_time_keydown = GetTimeStamp(5); + absl::optional<PointerId> pointer_id = 5; + absl::optional<int> key_code = absl::nullopt; + performance_->RegisterEventTiming( + "pointerdown", pointerdown_timestamp, processing_start_keydown, + processing_end_keydown, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_keydown); + // Pointercancel + base::TimeTicks pointerup_timestamp = GetTimeStamp(3); + base::TimeTicks processing_start_keyup = GetTimeStamp(5); + base::TimeTicks processing_end_keyup = GetTimeStamp(6); + base::TimeTicks swap_time_keyup = GetTimeStamp(10); + performance_->RegisterEventTiming( + "pointercancel", pointerup_timestamp, processing_start_keyup, + processing_end_keyup, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_keyup); + + // Flush UKM logging mojo request. + RunPendingTasks(); + + // Check UKM recording. + std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries = + GetUkmRecorder()->GetMergedEntriesByName( + ukm::builders::Responsiveness_UserInteraction::kEntryName); + EXPECT_EQ(0u, merged_entries.size()); +} + +TEST_F(WindowPerformanceTest, TouchesWithoutClick) { + base::TimeTicks pointerdown_timestamp = GetTimeOrigin(); + // First Pointerdown + base::TimeTicks processing_start_pointerdown = GetTimeStamp(1); + base::TimeTicks processing_end_pointerdown = GetTimeStamp(2); + base::TimeTicks swap_time_pointerdown = GetTimeStamp(5); + absl::optional<PointerId> pointer_id = 4; + absl::optional<int> key_code = absl::nullopt; + performance_->RegisterEventTiming( + "pointerdown", pointerdown_timestamp, processing_start_pointerdown, + processing_end_pointerdown, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_pointerdown); + + // Second Pointerdown + pointerdown_timestamp = GetTimeStamp(6); + processing_start_pointerdown = GetTimeStamp(7); + processing_end_pointerdown = GetTimeStamp(8); + swap_time_pointerdown = GetTimeStamp(15); + performance_->RegisterEventTiming( + "pointerdown", pointerdown_timestamp, processing_start_pointerdown, + processing_end_pointerdown, false, nullptr, key_code, pointer_id); + SimulateSwapPromise(swap_time_pointerdown); + + // Flush UKM logging mojo request. + RunPendingTasks(); + + // Check UKM recording. + std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries = + GetUkmRecorder()->GetMergedEntriesByName( + ukm::builders::Responsiveness_UserInteraction::kEntryName); + EXPECT_EQ(0u, merged_entries.size()); +} + +// For multi-touch, we only record the innermost pair of pointerdown and +// pointerup. +// TODO(hbsong): Record each touch by pointer_id separately. +TEST_F(WindowPerformanceTest, MultiTouch) { + // First Pointerdown + base::TimeTicks pointerdown_timestamp = GetTimeOrigin(); + base::TimeTicks processing_start_pointerdown = GetTimeStamp(1); + base::TimeTicks processing_end_pointerdown = GetTimeStamp(2); + base::TimeTicks swap_time_pointerdown = GetTimeStamp(5); + absl::optional<PointerId> pointer_id_1 = 4; + absl::optional<int> key_code = absl::nullopt; + performance_->RegisterEventTiming( + "pointerdown", pointerdown_timestamp, processing_start_pointerdown, + processing_end_pointerdown, false, nullptr, key_code, pointer_id_1); + SimulateSwapPromise(swap_time_pointerdown); + // Second Pointerdown + pointerdown_timestamp = GetTimeOrigin(); + processing_start_pointerdown = GetTimeStamp(1); + processing_end_pointerdown = GetTimeStamp(2); + swap_time_pointerdown = GetTimeStamp(6); + absl::optional<PointerId> pointer_id_2 = 6; + performance_->RegisterEventTiming( + "pointerdown", pointerdown_timestamp, processing_start_pointerdown, + processing_end_pointerdown, false, nullptr, key_code, pointer_id_2); + SimulateSwapPromise(swap_time_pointerdown); + + // First Pointerup + base::TimeTicks pointerup_timestamp = GetTimeStamp(3); + base::TimeTicks processing_start_pointerup = GetTimeStamp(5); + base::TimeTicks processing_end_pointerup = GetTimeStamp(6); + base::TimeTicks swap_time_pointerup = GetTimeStamp(9); + performance_->RegisterEventTiming( + "pointerup", pointerup_timestamp, processing_start_pointerup, + processing_end_pointerup, false, nullptr, key_code, pointer_id_2); + SimulateSwapPromise(swap_time_pointerup); + + // Second Pointerup + pointerup_timestamp = GetTimeStamp(5); + processing_start_pointerup = GetTimeStamp(6); + processing_end_pointerup = GetTimeStamp(7); + swap_time_pointerup = GetTimeStamp(13); + performance_->RegisterEventTiming( + "pointerup", pointerup_timestamp, processing_start_pointerup, + processing_end_pointerup, false, nullptr, key_code, pointer_id_1); + SimulateSwapPromise(swap_time_pointerup); + + // Click + base::TimeTicks click_timestamp = GetTimeStamp(13); + base::TimeTicks processing_start_click = GetTimeStamp(15); + base::TimeTicks processing_end_click = GetTimeStamp(16); + base::TimeTicks swap_time_click = GetTimeStamp(20); + performance_->RegisterEventTiming( + "click", click_timestamp, processing_start_click, processing_end_click, + false, nullptr, key_code, pointer_id_2); + SimulateSwapPromise(swap_time_click); + + // Flush UKM logging mojo request. + RunPendingTasks(); + + // Check UKM recording. + auto entries = GetUkmRecorder()->GetEntriesByName( + ukm::builders::Responsiveness_UserInteraction::kEntryName); + EXPECT_EQ(1u, entries.size()); + auto* entry = entries[0]; + GetUkmRecorder()->ExpectEntryMetric( + entry, + ukm::builders::Responsiveness_UserInteraction::kMaxEventDurationName, 7); + GetUkmRecorder()->ExpectEntryMetric( + entry, + ukm::builders::Responsiveness_UserInteraction::kTotalEventDurationName, + 16); + GetUkmRecorder()->ExpectEntryMetric( + entry, + ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 1); +} + } // namespace blink |