diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/timing')
44 files changed, 631 insertions, 617 deletions
diff --git a/chromium/third_party/blink/renderer/core/timing/DIR_METADATA b/chromium/third_party/blink/renderer/core/timing/DIR_METADATA new file mode 100644 index 00000000000..8f13e49a253 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/DIR_METADATA @@ -0,0 +1,5 @@ +monorail { + component: "Blink>PerformanceAPIs" +} + +team_email: "speed-metrics-dev@chromium.org" diff --git a/chromium/third_party/blink/renderer/core/timing/OWNERS b/chromium/third_party/blink/renderer/core/timing/OWNERS index afc8230f65f..9e3a2546556 100644 --- a/chromium/third_party/blink/renderer/core/timing/OWNERS +++ b/chromium/third_party/blink/renderer/core/timing/OWNERS @@ -1,4 +1 @@ npm@chromium.org - -# TEAM: speed-metrics-dev@chromium.org -# COMPONENT: Blink>PerformanceAPIs diff --git a/chromium/third_party/blink/renderer/core/timing/build.gni b/chromium/third_party/blink/renderer/core/timing/build.gni index f0b692b53a3..016200a206b 100644 --- a/chromium/third_party/blink/renderer/core/timing/build.gni +++ b/chromium/third_party/blink/renderer/core/timing/build.gni @@ -17,8 +17,6 @@ blink_core_sources_timing = [ "layout_shift_attribution.h", "measure_memory/measure_memory_controller.cc", "measure_memory/measure_memory_controller.h", - "measure_memory/measure_memory_delegate.cc", - "measure_memory/measure_memory_delegate.h", "memory_info.cc", "memory_info.h", "performance.cc", 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 3b3b1fa63b9..ad1207b4190 100644 --- a/chromium/third_party/blink/renderer/core/timing/event_timing.cc +++ b/chromium/third_party/blink/renderer/core/timing/event_timing.cc @@ -116,14 +116,6 @@ void EventTiming::DidDispatchEvent(const Event& event, Document& document) { performance_->RegisterEventTiming(event.type(), event_timestamp_, processing_start_, processing_end, event.cancelable(), target); - if (should_log_event_) { - InteractiveDetector* interactive_detector = - InteractiveDetector::From(document); - if (interactive_detector) { - interactive_detector->RecordInputEventTimingUKM( - event, event_timestamp_, processing_start_, processing_end); - } - } } // static diff --git a/chromium/third_party/blink/renderer/core/timing/largest_contentful_paint.h b/chromium/third_party/blink/renderer/core/timing/largest_contentful_paint.h index bbebf97d0ca..97b66eb1e53 100644 --- a/chromium/third_party/blink/renderer/core/timing/largest_contentful_paint.h +++ b/chromium/third_party/blink/renderer/core/timing/largest_contentful_paint.h @@ -8,6 +8,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/timing/performance_entry.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -51,6 +52,14 @@ class CORE_EXPORT LargestContentfulPaint final : public PerformanceEntry { WeakMember<Element> element_; }; +template <> +struct DowncastTraits<LargestContentfulPaint> { + static bool AllowFrom(const PerformanceEntry& entry) { + return entry.EntryTypeEnum() == + PerformanceEntry::EntryType::kLargestContentfulPaint; + } +}; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_LARGEST_CONTENTFUL_PAINT_H_ diff --git a/chromium/third_party/blink/renderer/core/timing/layout_shift.h b/chromium/third_party/blink/renderer/core/timing/layout_shift.h index f20e951713e..fcf9987b5ca 100644 --- a/chromium/third_party/blink/renderer/core/timing/layout_shift.h +++ b/chromium/third_party/blink/renderer/core/timing/layout_shift.h @@ -9,6 +9,7 @@ #include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h" #include "third_party/blink/renderer/core/timing/layout_shift_attribution.h" #include "third_party/blink/renderer/core/timing/performance_entry.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -45,7 +46,7 @@ class CORE_EXPORT LayoutShift final : public PerformanceEntry { bool hadRecentInput() const { return had_recent_input_; } double lastInputTime() const { return most_recent_input_timestamp_; } - AttributionList sources() const { return sources_; } + const AttributionList& sources() const { return sources_; } void Trace(Visitor*) const override; @@ -58,6 +59,13 @@ class CORE_EXPORT LayoutShift final : public PerformanceEntry { AttributionList sources_; }; +template <> +struct DowncastTraits<LayoutShift> { + static bool AllowFrom(const PerformanceEntry& entry) { + return entry.EntryTypeEnum() == PerformanceEntry::EntryType::kLayoutShift; + } +}; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_LAYOUT_SHIFT_H_ diff --git a/chromium/third_party/blink/renderer/core/timing/layout_shift_attribution.cc b/chromium/third_party/blink/renderer/core/timing/layout_shift_attribution.cc index 1da6afd1bf0..791652df91f 100644 --- a/chromium/third_party/blink/renderer/core/timing/layout_shift_attribution.cc +++ b/chromium/third_party/blink/renderer/core/timing/layout_shift_attribution.cc @@ -31,6 +31,10 @@ Node* LayoutShiftAttribution::node() const { return Performance::CanExposeNode(node_) ? node_ : nullptr; } +Node* LayoutShiftAttribution::rawNodeForInspector() const { + return node_; +} + DOMRectReadOnly* LayoutShiftAttribution::previousRect() const { return previous_rect_; } diff --git a/chromium/third_party/blink/renderer/core/timing/layout_shift_attribution.h b/chromium/third_party/blink/renderer/core/timing/layout_shift_attribution.h index 62fa9253d2e..8b4fef906ea 100644 --- a/chromium/third_party/blink/renderer/core/timing/layout_shift_attribution.h +++ b/chromium/third_party/blink/renderer/core/timing/layout_shift_attribution.h @@ -11,6 +11,7 @@ namespace blink { class DOMRectReadOnly; +class Node; class ScriptState; class ScriptValue; @@ -27,6 +28,9 @@ class CORE_EXPORT LayoutShiftAttribution : public ScriptWrappable { ~LayoutShiftAttribution() override; Node* node() const; + // Return node_ unconditionally, skipping the checks that apply + // to exposing it through bindings. + Node* rawNodeForInspector() const; DOMRectReadOnly* previousRect() const; DOMRectReadOnly* currentRect() const; diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/OWNERS b/chromium/third_party/blink/renderer/core/timing/measure_memory/OWNERS new file mode 100644 index 00000000000..ccae9082083 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/measure_memory/OWNERS @@ -0,0 +1 @@ +ulan@chromium.org diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory.idl b/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory.idl deleted file mode 100644 index d319cef8df4..00000000000 --- a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory.idl +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2019 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://github.com/WICG/performance-measure-memory - -// The result of performance.measureMemory(). -dictionary MeasureMemory { - required unsigned long long bytes; - required sequence<MeasureMemoryBreakdown> breakdown; -}; diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_breakdown.idl b/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_breakdown.idl deleted file mode 100644 index e90820e6bda..00000000000 --- a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_breakdown.idl +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020 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://github.com/ulan/performance-measure-memory. - -// A single entry of performance.measureMemory() result. -dictionary MeasureMemoryBreakdown { - unsigned long long bytes; - sequence<DOMString> attribution; - sequence<DOMString> userAgentSpecificTypes; -}; 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 1a0aab598a2..3759664a803 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 @@ -4,38 +4,56 @@ #include "third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.h" -#include "third_party/blink/renderer/core/timing/measure_memory/measure_memory_delegate.h" - +#include <algorithm> +#include "base/rand_util.h" +#include "components/performance_manager/public/mojom/coordination_unit.mojom-blink.h" +#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/v8_measure_memory.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_measure_memory_breakdown.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" +#include "third_party/blink/renderer/bindings/core/v8/v8_memory_measurement.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/frame/local_frame.h" +#include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/heap/heap_allocator.h" #include "third_party/blink/renderer/platform/heap/member.h" #include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/functional.h" #include "v8/include/v8.h" +using performance_manager::mojom::blink::WebMemoryAttribution; +using performance_manager::mojom::blink::WebMemoryAttributionPtr; +using performance_manager::mojom::blink::WebMemoryBreakdownEntryPtr; +using performance_manager::mojom::blink::WebMemoryMeasurement; +using performance_manager::mojom::blink::WebMemoryMeasurementPtr; +using performance_manager::mojom::blink::WebMemoryUsagePtr; + namespace blink { -static MeasureMemoryController::V8MemoryReporter* - g_dedicated_worker_memory_reporter_ = nullptr; +namespace { -void MeasureMemoryController::SetDedicatedWorkerMemoryReporter( - V8MemoryReporter* reporter) { - g_dedicated_worker_memory_reporter_ = reporter; -} +// String constants used for building the result. +constexpr const char* kCrossOriginUrl = "cross-origin-url"; +constexpr const char* kMemoryTypeDom = "DOM"; +constexpr const char* kMemoryTypeJavaScript = "JavaScript"; +constexpr const char* kMemoryTypeShared = "Shared"; +constexpr const char* kScopeCrossOriginAggregated = "cross-origin-aggregated"; +constexpr const char* kScopeDedicatedWorker = "DedicatedWorker"; +constexpr const char* kScopeWindow = "Window"; + +} // anonymous namespace MeasureMemoryController::MeasureMemoryController( - util::PassKey<MeasureMemoryController>, + base::PassKey<MeasureMemoryController>, v8::Isolate* isolate, v8::Local<v8::Context> context, v8::Local<v8::Promise::Resolver> promise_resolver) @@ -51,17 +69,77 @@ MeasureMemoryController::MeasureMemoryController( void MeasureMemoryController::Trace(Visitor* visitor) const { visitor->Trace(promise_resolver_); - visitor->Trace(main_result_); - visitor->Trace(worker_result_); } +namespace { + +enum class ApiStatus { + kAvailable, + kNotAvailableDueToFlag, + kNotAvailableDueToDetachedContext, + kNotAvailableDueToCrossOriginContext, + kNotAvailableDueToCrossOriginIsolation, + kNotAvailableDueToResourceCoordinator, +}; + +ApiStatus CheckMeasureMemoryAvailability(LocalDOMWindow* window) { + if (!base::FeatureList::IsEnabled( + features::kWebMeasureMemoryViaPerformanceManager)) { + return ApiStatus::kNotAvailableDueToFlag; + } + if (!window) { + return ApiStatus::kNotAvailableDueToDetachedContext; + } + LocalFrame* local_frame = window->GetFrame(); + if (!local_frame) { + return ApiStatus::kNotAvailableDueToDetachedContext; + } + if (!window->CrossOriginIsolatedCapability() && + local_frame->GetSettings()->GetWebSecurityEnabled()) { + return ApiStatus::kNotAvailableDueToCrossOriginIsolation; + } + + // We need DocumentResourceCoordinator to query PerformanceManager. + if (!window->document()) { + return ApiStatus::kNotAvailableDueToDetachedContext; + } + + if (!window->document()->GetResourceCoordinator()) { + return ApiStatus::kNotAvailableDueToResourceCoordinator; + } + + return ApiStatus::kAvailable; +} + +} // anonymous namespace + ScriptPromise MeasureMemoryController::StartMeasurement( ScriptState* script_state, ExceptionState& exception_state) { - if (!IsMeasureMemoryAvailable(LocalDOMWindow::From(script_state))) { - exception_state.ThrowSecurityError( - "performance.measureMemory is not available in this context"); - return ScriptPromise(); + switch (auto status = CheckMeasureMemoryAvailability( + LocalDOMWindow::From(script_state))) { + case ApiStatus::kAvailable: + break; + case ApiStatus::kNotAvailableDueToFlag: + case ApiStatus::kNotAvailableDueToResourceCoordinator: + exception_state.ThrowSecurityError( + "performance.measureUserAgentSpecificMemory is not available."); + return ScriptPromise(); + case ApiStatus::kNotAvailableDueToDetachedContext: + exception_state.ThrowSecurityError( + "performance.measureUserAgentSpecificMemory is not supported" + " in detached iframes."); + return ScriptPromise(); + case ApiStatus::kNotAvailableDueToCrossOriginContext: + exception_state.ThrowSecurityError( + "performance.measureUserAgentSpecificMemory is not supported" + " in cross-origin iframes."); + return ScriptPromise(); + case ApiStatus::kNotAvailableDueToCrossOriginIsolation: + exception_state.ThrowSecurityError( + "performance.measureUserAgentSpecificMemory requires" + " cross-origin isolation."); + return ScriptPromise(); } v8::Isolate* isolate = script_state->GetIsolate(); v8::TryCatch try_catch(isolate); @@ -71,73 +149,143 @@ ScriptPromise MeasureMemoryController::StartMeasurement( exception_state.RethrowV8Exception(try_catch.Exception()); return ScriptPromise(); } - v8::MeasureMemoryExecution execution = + auto measurement_mode = RuntimeEnabledFeatures::ForceEagerMeasureMemoryEnabled( ExecutionContext::From(script_state)) - ? v8::MeasureMemoryExecution::kEager - : v8::MeasureMemoryExecution::kDefault; + ? WebMemoryMeasurement::Mode::kEager + : WebMemoryMeasurement::Mode::kDefault; auto* impl = MakeGarbageCollected<MeasureMemoryController>( - util::PassKey<MeasureMemoryController>(), isolate, context, + base::PassKey<MeasureMemoryController>(), isolate, context, promise_resolver); + Document* document = LocalDOMWindow::From(script_state)->document(); + document->GetResourceCoordinator()->OnWebMemoryMeasurementRequested( + measurement_mode, WTF::Bind(&MeasureMemoryController::MeasurementComplete, + WrapPersistent(impl))); - isolate->MeasureMemory( - std::make_unique<MeasureMemoryDelegate>( - isolate, context, - WTF::Bind(&MeasureMemoryController::MainMeasurementComplete, - WrapPersistent(impl))), - execution); - if (g_dedicated_worker_memory_reporter_) { - g_dedicated_worker_memory_reporter_->GetMemoryUsage( - WTF::Bind(&MeasureMemoryController::WorkerMeasurementComplete, - WrapPersistent(impl)), - - execution); - } else { - HeapVector<Member<MeasureMemoryBreakdown>> result; - impl->WorkerMeasurementComplete(result); - } return ScriptPromise(script_state, promise_resolver->GetPromise()); } -bool MeasureMemoryController::IsMeasureMemoryAvailable(LocalDOMWindow* window) { - // TODO(ulan): We should check for window.crossOriginIsolated when it ships. - // Until then we enable the API only for processes locked to a site - // similar to the precise mode of the legacy performance.memory API. - if (!Platform::Current()->IsLockedToSite()) { - return false; + +namespace { + +// Satisfies the requirements of UniformRandomBitGenerator from C++ standard. +// It is used in std::shuffle calls below. +struct RandomBitGenerator { + using result_type = size_t; + static constexpr size_t min() { return 0; } + static constexpr size_t max() { + return static_cast<size_t>(std::numeric_limits<int>::max()); + } + size_t operator()() { + return static_cast<size_t>(base::RandInt(min(), max())); + } +}; + +// These functions convert WebMemory* mojo structs to IDL and JS values. +WTF::AtomicString ConvertScope(WebMemoryAttribution::Scope scope) { + using Scope = WebMemoryAttribution::Scope; + switch (scope) { + case Scope::kDedicatedWorker: + return kScopeDedicatedWorker; + case Scope::kWindow: + return kScopeWindow; + case Scope::kCrossOriginAggregated: + return kScopeCrossOriginAggregated; + } +} + +MemoryAttributionContainer* ConvertContainer( + const WebMemoryAttributionPtr& attribution) { + if (!attribution->src && !attribution->id) { + return nullptr; } - // The window.crossOriginIsolated will be true only for the top-level frame. - // Until the flag is available we check for the top-level condition manually. - if (!window) { - return false; + auto* result = MemoryAttributionContainer::Create(); + result->setSrc(attribution->src); + result->setId(attribution->id); + return result; +} + +MemoryAttribution* ConvertAttribution( + const WebMemoryAttributionPtr& attribution) { + auto* result = MemoryAttribution::Create(); + if (attribution->url) { + result->setUrl(attribution->url); + } else { + result->setUrl(kCrossOriginUrl); } - LocalFrame* local_frame = window->GetFrame(); - if (!local_frame || !local_frame->IsMainFrame()) { - return false; + result->setScope(ConvertScope(attribution->scope)); + result->setContainer(ConvertContainer(attribution)); + return result; +} + +MemoryBreakdownEntry* ConvertBreakdown( + const WebMemoryBreakdownEntryPtr& breakdown_entry) { + auto* result = MemoryBreakdownEntry::Create(); + DCHECK(breakdown_entry->memory); + result->setBytes(breakdown_entry->memory->bytes); + HeapVector<Member<MemoryAttribution>> attribution; + for (const auto& entry : breakdown_entry->attribution) { + attribution.push_back(ConvertAttribution(entry)); } - return true; + result->setAttribution(attribution); + result->setTypes({WTF::AtomicString(kMemoryTypeJavaScript)}); + return result; } -void MeasureMemoryController::MainMeasurementComplete(Result result) { - DCHECK(!main_measurement_completed_); - main_result_ = result; - main_measurement_completed_ = true; - MaybeResolvePromise(); +MemoryBreakdownEntry* CreateUnattributedBreakdown( + const WebMemoryUsagePtr& memory, + const WTF::String& memory_type) { + auto* result = MemoryBreakdownEntry::Create(); + DCHECK(memory); + result->setBytes(memory->bytes); + result->setAttribution({}); + Vector<String> types; + types.push_back(memory_type); + result->setTypes(types); + return result; } -void MeasureMemoryController::WorkerMeasurementComplete(Result result) { - DCHECK(!worker_measurement_completed_); - worker_result_ = result; - worker_measurement_completed_ = true; - MaybeResolvePromise(); +MemoryBreakdownEntry* EmptyBreakdown() { + auto* result = MemoryBreakdownEntry::Create(); + result->setBytes(0); + result->setAttribution({}); + result->setTypes({}); + return result; } -void MeasureMemoryController::MaybeResolvePromise() { - if (!main_measurement_completed_ || !worker_measurement_completed_) { - // Wait until we have all results. - return; +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)); } + // Add breakdowns for memory that isn't attributed to an execution context. + breakdown.push_back(CreateUnattributedBreakdown(measurement->shared_memory, + kMemoryTypeShared)); + breakdown.push_back( + CreateUnattributedBreakdown(measurement->blink_memory, kMemoryTypeDom)); + // TODO(1085129): Report memory usage of detached frames once implemented. + // Add an empty breakdown entry as required by the spec. + // See https://github.com/WICG/performance-measure-memory/issues/10. + breakdown.push_back(EmptyBreakdown()); + // Randomize the order of the entries as required by the spec. + std::shuffle(breakdown.begin(), breakdown.end(), RandomBitGenerator{}); + size_t bytes = 0; + for (auto entry : breakdown) { + bytes += entry->bytes(); + } + auto* result = MemoryMeasurement::Create(); + result->setBreakdown(breakdown); + result->setBytes(bytes); + return result; +} + +} // anonymous namespace + +void MeasureMemoryController::MeasurementComplete( + WebMemoryMeasurementPtr measurement) { if (context_.IsEmpty()) { // The context was garbage collected in the meantime. return; @@ -145,15 +293,7 @@ void MeasureMemoryController::MaybeResolvePromise() { v8::HandleScope handle_scope(isolate_); v8::Local<v8::Context> context = context_.NewLocal(isolate_); v8::Context::Scope context_scope(context); - MeasureMemory* result = MeasureMemory::Create(); - auto breakdown = main_result_; - breakdown.AppendVector(worker_result_); - size_t total_size = 0; - for (auto entry : breakdown) { - total_size += entry->bytes(); - } - result->setBytes(total_size); - result->setBreakdown(breakdown); + auto* result = ConvertResult(measurement); v8::Local<v8::Promise::Resolver> promise_resolver = promise_resolver_.NewLocal(isolate_); promise_resolver->Resolve(context, ToV8(result, promise_resolver, isolate_)) diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.h b/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.h index 6a59e0c99b3..b20ab9ed6ff 100644 --- a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.h +++ b/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.h @@ -5,7 +5,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_MEASURE_MEMORY_MEASURE_MEMORY_CONTROLLER_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_MEASURE_MEMORY_MEASURE_MEMORY_CONTROLLER_H_ -#include "base/util/type_safety/pass_key.h" +#include "base/types/pass_key.h" +#include "components/performance_manager/public/mojom/coordination_unit.mojom-blink.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/bindings/scoped_persistent.h" @@ -16,12 +17,10 @@ namespace blink { -class LocalDOMWindow; -class MeasureMemoryBreakdown; class ScriptState; class ExceptionState; -// The implementation of Performance.measureMemory() Web API. +// The implementation of performance.measureUserAgentSpecificMemory() Web API. // It is responsible for: // 1. Starting an asynchronous memory measurement of the main V8 isolate. // 2. Starting an asynchronous memory measurement of dedicated workers. @@ -30,24 +29,9 @@ class ExceptionState; class MeasureMemoryController final : public GarbageCollected<MeasureMemoryController> { public: - using Result = HeapVector<Member<MeasureMemoryBreakdown>>; - using ResultCallback = base::OnceCallback<void(Result)>; - - // PerformanceManager in blink/renderer/controller uses this interface - // to provide an implementation of memory measurement for dedicated workers. - // - // It will be removed in the future when performance.measureMemory switches - // to a mojo-based implementation that queries PerformanceManager in the - // browser process. - class V8MemoryReporter { - public: - virtual void GetMemoryUsage(MeasureMemoryController::ResultCallback, - v8::MeasureMemoryExecution) = 0; - }; - // Private constructor guarded by PassKey. Use the StartMeasurement() wrapper // to construct the object and start the measurement. - MeasureMemoryController(util::PassKey<MeasureMemoryController>, + MeasureMemoryController(base::PassKey<MeasureMemoryController>, v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Promise::Resolver>); @@ -58,25 +42,14 @@ class MeasureMemoryController final void Trace(Visitor* visitor) const; - // The entry point for injecting dependency on PerformanceManager. - CORE_EXPORT static void SetDedicatedWorkerMemoryReporter(V8MemoryReporter*); - private: - static bool IsMeasureMemoryAvailable(LocalDOMWindow* window); // Invoked when the memory of the main V8 isolate is measured. - void MainMeasurementComplete(Result); - // Invoked when the memory of all dedicated workers is measured. - void WorkerMeasurementComplete(Result); - // Resolves the JS promise if both pending measurements are done. - void MaybeResolvePromise(); + void MeasurementComplete( + performance_manager::mojom::blink::WebMemoryMeasurementPtr); v8::Isolate* isolate_; ScopedPersistent<v8::Context> context_; TraceWrapperV8Reference<v8::Promise::Resolver> promise_resolver_; - Result main_result_; - Result worker_result_; - bool main_measurement_completed_ = false; - bool worker_measurement_completed_ = false; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_delegate.cc b/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_delegate.cc deleted file mode 100644 index 28f6202d87e..00000000000 --- a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_delegate.cc +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2020 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/measure_memory/measure_memory_delegate.h" - -#include "third_party/blink/public/platform/platform.h" -#include "third_party/blink/public/platform/web_security_origin.h" -#include "third_party/blink/public/web/web_frame.h" -#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_measure_memory.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_measure_memory_breakdown.h" -#include "third_party/blink/renderer/core/dom/document.h" -#include "third_party/blink/renderer/core/execution_context/execution_context.h" -#include "third_party/blink/renderer/core/frame/frame.h" -#include "third_party/blink/renderer/core/frame/frame_owner.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" -#include "third_party/blink/renderer/platform/bindings/script_state.h" -#include "third_party/blink/renderer/platform/weborigin/kurl.h" -#include "third_party/blink/renderer/platform/weborigin/security_origin.h" - -namespace blink { - -MeasureMemoryDelegate::MeasureMemoryDelegate(v8::Isolate* isolate, - v8::Local<v8::Context> context, - ResultCallback callback) - : isolate_(isolate), - context_(isolate, context), - callback_(std::move(callback)) { - context_.SetPhantom(); -} - -// Returns true if the given context should be included in the current memory -// measurement. Currently it is very conservative and allows only the same -// origin contexts that belong to the same JavaScript origin. -// With COOP/COEP we will be able to relax this restriction for the contexts -// that opt-in into memory measurement. -bool MeasureMemoryDelegate::ShouldMeasure(v8::Local<v8::Context> context) { - if (context_.IsEmpty()) { - // The original context was garbage collected in the meantime. - return false; - } - v8::Local<v8::Context> original_context = context_.NewLocal(isolate_); - ExecutionContext* original_execution_context = - ExecutionContext::From(original_context); - ExecutionContext* execution_context = ExecutionContext::From(context); - if (!original_execution_context || !execution_context) { - // One of the contexts is detached or is created by DevTools. - return false; - } - if (original_execution_context->GetAgent() != execution_context->GetAgent()) { - // Context do not belong to the same JavaScript agent. - return false; - } - if (ScriptState::From(context)->World().IsIsolatedWorld()) { - // Context belongs to an extension. Skip it. - return false; - } - const SecurityOrigin* original_security_origin = - original_execution_context->GetSecurityContext().GetSecurityOrigin(); - const SecurityOrigin* security_origin = - execution_context->GetSecurityContext().GetSecurityOrigin(); - if (!original_security_origin->IsSameOriginWith(security_origin)) { - // TODO(ulan): Check for COOP/COEP and allow cross-origin contexts that - // opted in for memory measurement. - // Until then we allow cross-origin measurement only for site-isolated - // web pages. - return Platform::Current()->IsLockedToSite(); - } - return true; -} - -namespace { -// Helper functions for constructing a memory measurement result. - -LocalFrame* GetLocalFrame(v8::Local<v8::Context> context) { - LocalDOMWindow* window = ToLocalDOMWindow(context); - if (!window) { - // The context was detached. Ignore it. - return nullptr; - } - return window->GetFrame(); -} - -// Returns true if all frames on the path from the main frame to -// the given frame (excluding the given frame) have the same origin. -bool AllAncestorsAndOpenersAreSameOrigin(const WebFrame* main_frame, - const WebFrame* frame) { - while (frame != main_frame) { - frame = frame->Parent() ? frame->Parent() : frame->Opener(); - if (!main_frame->GetSecurityOrigin().CanAccess(frame->GetSecurityOrigin())) - return false; - } - return true; -} - -// Returns the URL corresponding to the given frame. It is: -// - document's URL if the frame is a same-origin top frame. -// - the src attribute of the owner iframe element if the frame is -// an iframe. -// - nullopt, otherwise. -// Preconditions: -// - If the frame is cross-origin, then all its ancestors/openers -// must be of the same origin as the main frame. -// - The frame must be attached to the DOM tree and the main frame -// must be reachable via child => parent and openee => opener edges. -String GetUrl(const WebFrame* main_frame, const WebFrame* frame) { - DCHECK(AllAncestorsAndOpenersAreSameOrigin(main_frame, frame)); - if (!frame->Parent()) { - // TODO(ulan): Turn this conditional into a DCHECK once the API - // is gated behind COOP+COEP. Only same-origin frames can appear here. - if (!main_frame->GetSecurityOrigin().CanAccess(frame->GetSecurityOrigin())) - return {}; - // The frame must be local because it is in the same browsing context - // group as the main frame and has the same origin. - // Check to avoid memory corruption in the case if our invariant is off. - CHECK(IsA<LocalFrame>(WebFrame::ToCoreFrame(*frame))); - LocalFrame* local = To<LocalFrame>(WebFrame::ToCoreFrame(*frame)); - return local->GetDocument()->Url().GetString(); - } - FrameOwner* frame_owner = WebFrame::ToCoreFrame(*frame)->Owner(); - // The frame owner must be local because the parent of the frame has - // the same origin as the main frame. Also the frame cannot be provisional - // here because it is attached and has a document. - // Check to avoid of memory corruption in the case if our invariant is off. - CHECK(IsA<HTMLFrameOwnerElement>(frame_owner)); - HTMLFrameOwnerElement* owner_element = To<HTMLFrameOwnerElement>(frame_owner); - switch (owner_element->OwnerType()) { - case mojom::blink::FrameOwnerElementType::kIframe: - return owner_element->getAttribute(html_names::kSrcAttr); - case mojom::blink::FrameOwnerElementType::kObject: - case mojom::blink::FrameOwnerElementType::kEmbed: - case mojom::blink::FrameOwnerElementType::kFrame: - case mojom::blink::FrameOwnerElementType::kPortal: - // TODO(ulan): return the data/src attribute after adding tests. - return {}; - case mojom::blink::FrameOwnerElementType::kNone: - // The main frame was handled as a local frame above. - NOTREACHED(); - return {}; - } -} - -// To avoid information leaks cross-origin iframes are considered opaque for -// the purposes of attribution. This means the memory of all iframes nested -// in a cross-origin iframe is attributed to the cross-origin iframe. -// See https://github.com/WICG/performance-measure-memory for more details. -// -// Given the main frame and a frame, this function find the first cross-origin -// frame in the path from the main frame to the given frame. Edges in the path -// are parent/child and opener/openee edges. -// If the path doesn't exist then it returns nullptr. -// If there are no cross-origin frames, then it returns the given frame. -// -// Precondition: the frame must be attached to the DOM tree. -const WebFrame* GetAttributionFrame(const WebFrame* main_frame, - const WebFrame* frame) { - WebSecurityOrigin main_security_origin = main_frame->GetSecurityOrigin(); - // Walk up the tree and the openers to find the first cross-origin frame - // on the path from the main frame to the given frame. - const WebFrame* result = frame; - while (frame != main_frame) { - if (frame->Parent()) { - frame = frame->Parent(); - } else if (frame->Opener()) { - frame = frame->Opener(); - } else { - // The opener was reset. We cannot get the attribution. - return nullptr; - } - if (!main_security_origin.CanAccess(frame->GetSecurityOrigin())) - result = frame; - } - // The result frame must be attached because we started from an attached - // frame (precondition) and followed the parent and opener references until - // the main frame, which is also attached. - DCHECK(WebFrame::ToCoreFrame(*result)->IsAttached()); - return result; -} - -// Return per-frame sizes based on the given per-context size. -// TODO(ulan): Revisit this after Origin Trial and see if the results -// are precise enough or if we need to additionally group by JS agent. -HashMap<const WebFrame*, size_t> GroupByFrame( - const WebFrame* main_frame, - const std::vector<std::pair<v8::Local<v8::Context>, size_t>>& context_sizes, - size_t& detached_size, - size_t& unknown_frame_size) { - detached_size = 0; - unknown_frame_size = 0; - HashMap<const WebFrame*, size_t> per_frame; - for (const auto& context_size : context_sizes) { - const WebFrame* frame = - WebFrame::FromFrame(GetLocalFrame(context_size.first)); - if (!frame) { - detached_size += context_size.second; - continue; - } - frame = GetAttributionFrame(main_frame, frame); - if (!frame) { - unknown_frame_size += context_size.second; - continue; - } - auto it = per_frame.find(frame); - if (it == per_frame.end()) { - per_frame.insert(frame, context_size.second); - } else { - it->value += context_size.second; - } - } - return per_frame; -} - -MeasureMemoryBreakdown* CreateMeasureMemoryBreakdown( - size_t bytes, - const Vector<String>& types, - const String& url) { - MeasureMemoryBreakdown* result = MeasureMemoryBreakdown::Create(); - result->setBytes(bytes); - result->setUserAgentSpecificTypes(types); - result->setAttribution(url.length() ? Vector<String>{url} : Vector<String>()); - return result; -} - -} // anonymous namespace - -// Constructs a memory measurement result based on the given list of (context, -// size) pairs and resolves the promise. -void MeasureMemoryDelegate::MeasurementComplete( - const std::vector<std::pair<v8::Local<v8::Context>, size_t>>& context_sizes, - size_t unattributed_size) { - if (context_.IsEmpty()) { - // The context was garbage collected in the meantime. - return; - } - v8::Local<v8::Context> context = context_.NewLocal(isolate_); - const WebFrame* main_frame = WebFrame::FromFrame(GetLocalFrame(context)); - if (!main_frame) { - // The context was detached in the meantime. - return; - } - DCHECK(!main_frame->Parent()); - v8::Context::Scope context_scope(context); - size_t total_size = 0; - for (const auto& context_size : context_sizes) { - total_size += context_size.second; - } - HeapVector<Member<MeasureMemoryBreakdown>> breakdown; - size_t detached_size; - size_t unknown_frame_size; - HashMap<const WebFrame*, size_t> per_frame(GroupByFrame( - main_frame, context_sizes, detached_size, unknown_frame_size)); - size_t attributed_size = 0; - const String kWindow("Window"); - const String kJS("JS"); - const Vector<String> js_window_types = {kWindow, kJS}; - for (const auto& it : per_frame) { - String url = GetUrl(main_frame, it.key); - if (url.IsNull()) { - unknown_frame_size += it.value; - continue; - } - attributed_size += it.value; - breakdown.push_back( - CreateMeasureMemoryBreakdown(it.value, js_window_types, url)); - } - if (detached_size) { - const String kDetached("Detached"); - breakdown.push_back(CreateMeasureMemoryBreakdown( - detached_size, Vector<String>{kWindow, kJS, kDetached}, "")); - } - if (unattributed_size) { - const String kShared("Shared"); - breakdown.push_back(CreateMeasureMemoryBreakdown( - unattributed_size, Vector<String>{kWindow, kJS, kShared}, "")); - } - if (unknown_frame_size) { - breakdown.push_back(CreateMeasureMemoryBreakdown( - unknown_frame_size, Vector<String>{kWindow, kJS}, "")); - } - std::move(callback_).Run(breakdown); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_delegate.h b/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_delegate.h deleted file mode 100644 index 23a1100784f..00000000000 --- a/chromium/third_party/blink/renderer/core/timing/measure_memory/measure_memory_delegate.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2020 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_MEASURE_MEMORY_MEASURE_MEMORY_DELEGATE_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_MEASURE_MEMORY_MEASURE_MEMORY_DELEGATE_H_ - -#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h" -#include "third_party/blink/renderer/platform/heap/heap_allocator.h" -#include "third_party/blink/renderer/platform/heap/member.h" -#include "v8/include/v8.h" - -namespace blink { - -class MeasureMemoryBreakdown; - -// Specifies V8 contexts to be measured and invokes the given callback once V8 -// completes the memory measurement. -class MeasureMemoryDelegate : public v8::MeasureMemoryDelegate { - public: - using ResultCallback = - base::OnceCallback<void(HeapVector<Member<MeasureMemoryBreakdown>>)>; - - MeasureMemoryDelegate(v8::Isolate* isolate, - v8::Local<v8::Context> context, - ResultCallback callback); - - // v8::MeasureMemoryDelegate overrides. - bool ShouldMeasure(v8::Local<v8::Context> context) override; - void MeasurementComplete( - const std::vector<std::pair<v8::Local<v8::Context>, size_t>>& - context_sizes, - size_t unattributed_size) override; - private: - v8::Isolate* isolate_; - ScopedPersistent<v8::Context> context_; - ResultCallback callback_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_MEASURE_MEMORY_MEASURE_MEMORY_DELEGATE_H_ diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_attribution.idl b/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_attribution.idl new file mode 100644 index 00000000000..5a8d9c27bb4 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_attribution.idl @@ -0,0 +1,12 @@ +// Copyright 2020 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://wicg.github.io/performance-measure-memory/#dictdef-memoryattribution + +// Describes a context to which the memory was attributed. +dictionary MemoryAttribution { + required USVString url; + MemoryAttributionContainer container; + required DOMString scope; +};
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_attribution_container.idl b/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_attribution_container.idl new file mode 100644 index 00000000000..bb47082f790 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_attribution_container.idl @@ -0,0 +1,11 @@ +// Copyright 2020 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://wicg.github.io/performance-measure-memory/#dictdef-memoryattribution + +// The attributes of the container iframe element. +dictionary MemoryAttributionContainer { + DOMString id; + USVString src; +};
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_breakdown_entry.idl b/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_breakdown_entry.idl new file mode 100644 index 00000000000..e3461faef8e --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_breakdown_entry.idl @@ -0,0 +1,12 @@ +// Copyright 2020 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://wicg.github.io/performance-measure-memory/#dictdef-memorybreakdownentry + +// A single entry of performance.measureUserAgentSpecificMemory() result. +dictionary MemoryBreakdownEntry { + required unsigned long long bytes; + required sequence<MemoryAttribution> attribution; + required sequence<DOMString> types; +};
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_measurement.idl b/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_measurement.idl new file mode 100644 index 00000000000..0f8feeb5797 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/timing/measure_memory/memory_measurement.idl @@ -0,0 +1,11 @@ +// Copyright 2019 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://wicg.github.io/performance-measure-memory/#dictdef-memorymeasurement + +// The result of performance.measureUserAgentSpecificMemory(). +dictionary MemoryMeasurement { + required unsigned long long bytes; + required sequence<MemoryBreakdownEntry> breakdown; +}; diff --git a/chromium/third_party/blink/renderer/core/timing/performance.cc b/chromium/third_party/blink/renderer/core/timing/performance.cc index 3f732b25d25..fb00b62a386 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance.cc @@ -36,6 +36,7 @@ #include "base/metrics/histogram_macros.h" #include "base/time/default_clock.h" #include "base/time/default_tick_clock.h" +#include "third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom-blink.h" #include "third_party/blink/public/mojom/timing/worker_timing_container.mojom-blink-forward.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" @@ -52,9 +53,11 @@ #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/frame/local_frame.h" +#include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/core/loader/document_load_timing.h" #include "third_party/blink/renderer/core/loader/document_loader.h" +#include "third_party/blink/renderer/core/probe/core_probes.h" #include "third_party/blink/renderer/core/timing/largest_contentful_paint.h" #include "third_party/blink/renderer/core/timing/layout_shift.h" #include "third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.h" @@ -150,7 +153,7 @@ EventCounts* Performance::eventCounts() { return nullptr; } -ScriptPromise Performance::measureMemory( +ScriptPromise Performance::measureUserAgentSpecificMemory( ScriptState* script_state, ExceptionState& exception_state) const { return MeasureMemoryController::StartMeasurement(script_state, @@ -532,6 +535,7 @@ mojom::blink::ResourceTimingInfoPtr Performance::GenerateResourceTiming( result->encoded_body_size = final_response.EncodedBodyLength(); result->decoded_body_size = final_response.DecodedBodyLength(); result->did_reuse_connection = final_response.ConnectionReused(); + // TODO(crbug.com/1153336) Use network::IsUrlPotentiallyTrustworthy(). result->is_secure_context = SecurityOrigin::IsSecure(final_response.ResponseUrl()); result->allow_negative_values = info.NegativeAllowed(); @@ -631,11 +635,13 @@ void Performance::AddEventTimingBuffer(PerformanceEventTiming& entry) { } void Performance::AddLayoutShiftBuffer(LayoutShift& entry) { + probe::PerformanceEntryAdded(GetExecutionContext(), &entry); if (layout_shift_buffer_.size() < kDefaultLayoutShiftBufferSize) layout_shift_buffer_.push_back(&entry); } void Performance::AddLargestContentfulPaint(LargestContentfulPaint* entry) { + probe::PerformanceEntryAdded(GetExecutionContext(), entry); if (largest_contentful_paint_buffer_.size() < kDefaultLargestContenfulPaintSize) { largest_contentful_paint_buffer_.push_back(entry); @@ -672,13 +678,18 @@ void Performance::AddLongTaskTiming(base::TimeTicks start_time, base::TimeTicks end_time, const AtomicString& name, const AtomicString& container_type, - const String& container_src, - const String& container_id, - const String& container_name) { + const AtomicString& container_src, + const AtomicString& container_id, + const AtomicString& container_name) { + double dom_high_res_start_time = + MonotonicTimeToDOMHighResTimeStamp(start_time); auto* entry = MakeGarbageCollected<PerformanceLongTaskTiming>( - MonotonicTimeToDOMHighResTimeStamp(start_time), - MonotonicTimeToDOMHighResTimeStamp(end_time), name, container_type, - container_src, container_id, container_name); + dom_high_res_start_time, + // Convert the delta between start and end times to an int to reduce the + // granularity of the duration to 1 ms. + static_cast<int>(MonotonicTimeToDOMHighResTimeStamp(end_time) - + dom_high_res_start_time), + name, container_type, container_src, container_id, container_name); if (longtask_buffer_.size() < kDefaultLongTaskBufferSize) { longtask_buffer_.push_back(entry); } else { @@ -861,7 +872,26 @@ ScriptPromise Performance::profile(ScriptState* script_state, DCHECK( RuntimeEnabledFeatures::ExperimentalJSProfilerEnabled(execution_context)); - if (!execution_context->CrossOriginIsolatedCapability()) { + if (!GetExecutionContext()->IsFeatureEnabled( + mojom::blink::DocumentPolicyFeature::kJSProfiling, + ReportOptions::kReportOnFailure)) { + exception_state.ThrowDOMException( + DOMExceptionCode::kNotAllowedError, + "JS profiling is disabled by Document Policy."); + return ScriptPromise(); + } + + // Bypass COOP/COEP checks when the |--disable-web-security| flag is present. + bool web_security_enabled = true; + if (LocalDOMWindow* window = LocalDOMWindow::From(script_state)) { + if (LocalFrame* local_frame = window->GetFrame()) { + web_security_enabled = + local_frame->GetSettings()->GetWebSecurityEnabled(); + } + } + + if (web_security_enabled && + !execution_context->CrossOriginIsolatedCapability()) { exception_state.ThrowSecurityError( "performance.profile() requires COOP+COEP (web.dev/coop-coep)"); return ScriptPromise(); @@ -1029,6 +1059,8 @@ void Performance::Trace(Visitor* visitor) const { visitor->Trace(observers_); visitor->Trace(active_observers_); visitor->Trace(suspended_observers_); + visitor->Trace(deliver_observations_timer_); + visitor->Trace(resource_timing_buffer_full_timer_); EventTargetWithInlineData::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/core/timing/performance.h b/chromium/third_party/blink/renderer/core/timing/performance.h index 8609a2a87b9..65898ea8131 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance.h +++ b/chromium/third_party/blink/renderer/core/timing/performance.h @@ -97,8 +97,9 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { virtual PerformanceTiming* timing() const; virtual PerformanceNavigation* navigation() const; virtual MemoryInfo* memory() const; - virtual ScriptPromise measureMemory(ScriptState*, - ExceptionState& exception_state) const; + virtual ScriptPromise measureUserAgentSpecificMemory( + ScriptState*, + ExceptionState& exception_state) const; virtual EventCounts* eventCounts(); // Reduce the resolution to prevent timing attacks. See: @@ -159,9 +160,9 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { base::TimeTicks end_time, const AtomicString& name, const AtomicString& container_type, - const String& container_src, - const String& container_id, - const String& container_name); + const AtomicString& container_src, + const AtomicString& container_id, + const AtomicString& container_name); // Generates and add a performance entry for the given ResourceTimingInfo. // |overridden_initiator_type| allows the initiator type to be overridden to @@ -388,8 +389,8 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { HeapLinkedHashSet<Member<PerformanceObserver>> active_observers_; HeapLinkedHashSet<Member<PerformanceObserver>> suspended_observers_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - TaskRunnerTimer<Performance> deliver_observations_timer_; - TaskRunnerTimer<Performance> resource_timing_buffer_full_timer_; + HeapTaskRunnerTimer<Performance> deliver_observations_timer_; + HeapTaskRunnerTimer<Performance> resource_timing_buffer_full_timer_; }; } // 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 4939cad8a26..44502c55e83 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance.idl +++ b/chromium/third_party/blink/renderer/core/timing/performance.idl @@ -67,7 +67,7 @@ interface Performance : EventTarget { // https://groups.google.com/a/chromium.org/d/msg/blink-dev/g5YRCGpC9vs/b4OJz71NmPwJ [Exposed=Window, Measure] readonly attribute MemoryInfo memory; - [MeasureAs=MeasureMemory, Exposed=Window, CallWith=ScriptState, RuntimeEnabled=MeasureMemory, RaisesException] Promise<MeasureMemory> measureMemory(); + [MeasureAs=MeasureMemory, Exposed=Window, CallWith=ScriptState, RuntimeEnabled=MeasureMemory, RaisesException] Promise<MemoryMeasurement> measureUserAgentSpecificMemory(); // JS Self-Profiling API // https://github.com/WICG/js-self-profiling/ diff --git a/chromium/third_party/blink/renderer/core/timing/performance_entry.cc b/chromium/third_party/blink/renderer/core/timing/performance_entry.cc index cda5624b9a0..8fe854540da 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_entry.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_entry.cc @@ -50,6 +50,16 @@ PerformanceEntry::PerformanceEntry(const AtomicString& name, start_time_(start_time), index_(index_seq.GetNext()) {} +PerformanceEntry::PerformanceEntry(double duration, + const AtomicString& name, + double start_time) + : duration_(duration), + name_(name), + start_time_(start_time), + index_(index_seq.GetNext()) { + DCHECK_GE(duration_, 0.0); +} + PerformanceEntry::~PerformanceEntry() = default; DOMHighResTimeStamp PerformanceEntry::startTime() const { diff --git a/chromium/third_party/blink/renderer/core/timing/performance_entry.h b/chromium/third_party/blink/renderer/core/timing/performance_entry.h index 82e50edb3e7..59679f11c16 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_entry.h +++ b/chromium/third_party/blink/renderer/core/timing/performance_entry.h @@ -124,6 +124,9 @@ class CORE_EXPORT PerformanceEntry : public ScriptWrappable { PerformanceEntry(const AtomicString& name, double start_time, double finish_time); + PerformanceEntry(double duration, + const AtomicString& name, + double start_time); virtual void BuildJSONValue(V8ObjectBuilder&) const; // Protected and not const because PerformanceEventTiming needs to modify it. diff --git a/chromium/third_party/blink/renderer/core/timing/performance_event_timing.cc b/chromium/third_party/blink/renderer/core/timing/performance_event_timing.cc index a5723822a4f..5777ecd3fd7 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_event_timing.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_event_timing.cc @@ -8,6 +8,7 @@ #include "third_party/blink/renderer/core/dom/document.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/platform/instrumentation/tracing/traced_value.h" namespace blink { @@ -85,7 +86,6 @@ void PerformanceEventTiming::BuildJSONValue(V8ObjectBuilder& builder) const { builder.AddNumber("processingStart", processingStart()); builder.AddNumber("processingEnd", processingEnd()); builder.AddBoolean("cancelable", cancelable_); - builder.Add("target", target()); } void PerformanceEventTiming::Trace(Visitor* visitor) const { @@ -93,4 +93,15 @@ void PerformanceEventTiming::Trace(Visitor* visitor) const { visitor->Trace(target_); } +std::unique_ptr<TracedValue> PerformanceEventTiming::ToTracedValue() const { + auto traced_value = std::make_unique<TracedValue>(); + traced_value->SetString("type", name()); + traced_value->SetInteger("timeStamp", startTime()); + traced_value->SetInteger("processingStart", processingStart()); + traced_value->SetInteger("processingEnd", processingEnd()); + traced_value->SetInteger("duration", duration()); + traced_value->SetBoolean("cancelable", cancelable()); + return traced_value; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/timing/performance_event_timing.h b/chromium/third_party/blink/renderer/core/timing/performance_event_timing.h index 78560ef5ed2..cf689f84da5 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_event_timing.h +++ b/chromium/third_party/blink/renderer/core/timing/performance_event_timing.h @@ -51,6 +51,8 @@ class CORE_EXPORT PerformanceEventTiming final : public PerformanceEntry { void Trace(Visitor*) const override; + std::unique_ptr<TracedValue> ToTracedValue() const; + private: AtomicString entry_type_; DOMHighResTimeStamp processing_start_; diff --git a/chromium/third_party/blink/renderer/core/timing/performance_long_task_timing.cc b/chromium/third_party/blink/renderer/core/timing/performance_long_task_timing.cc index 83ced4ebcb9..a901c81bf14 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_long_task_timing.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_long_task_timing.cc @@ -15,13 +15,13 @@ namespace blink { PerformanceLongTaskTiming::PerformanceLongTaskTiming( double start_time, - double end_time, + int duration, const AtomicString& name, const AtomicString& culprit_type, - const String& culprit_src, - const String& culprit_id, - const String& culprit_name) - : PerformanceEntry(name, start_time, end_time) { + const AtomicString& culprit_src, + const AtomicString& culprit_id, + const AtomicString& culprit_name) + : PerformanceEntry(duration, name, start_time) { auto* attribution_entry = MakeGarbageCollected<TaskAttributionTiming>( "unknown", culprit_type, culprit_src, culprit_id, culprit_name); attribution_.push_back(*attribution_entry); diff --git a/chromium/third_party/blink/renderer/core/timing/performance_long_task_timing.h b/chromium/third_party/blink/renderer/core/timing/performance_long_task_timing.h index 8dd3530ebcb..fb000654c81 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_long_task_timing.h +++ b/chromium/third_party/blink/renderer/core/timing/performance_long_task_timing.h @@ -19,13 +19,18 @@ class PerformanceLongTaskTiming final : public PerformanceEntry { DEFINE_WRAPPERTYPEINFO(); public: + // This constructor uses int for |duration| on purpose: to avoid exposing a + // high resolution value in its entry. Having it be int ensures 1 ms + // granularity, even though it is ultimately stored as a double in + // PerformanceEntry. PerformanceLongTaskTiming(double start_time, - double end_time, + int duration, const AtomicString& name, const AtomicString& culprit_type, - const String& culprit_src, - const String& culprit_id, - const String& culprit_name); + const AtomicString& culprit_src, + const AtomicString& culprit_id, + const AtomicString& culprit_name); + ~PerformanceLongTaskTiming() override; AtomicString entryType() const override; PerformanceEntryType EntryTypeEnum() const override; @@ -35,8 +40,6 @@ class PerformanceLongTaskTiming final : public PerformanceEntry { void Trace(Visitor*) const override; private: - ~PerformanceLongTaskTiming() override; - void BuildJSONValue(V8ObjectBuilder&) const override; TaskAttributionVector attribution_; diff --git a/chromium/third_party/blink/renderer/core/timing/performance_mark.h b/chromium/third_party/blink/renderer/core/timing/performance_mark.h index 2ae62a7e5f0..ea3a65062fa 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_mark.h +++ b/chromium/third_party/blink/renderer/core/timing/performance_mark.h @@ -58,6 +58,7 @@ class CORE_EXPORT PerformanceMark final : public PerformanceEntry { double start_time, scoped_refptr<SerializedScriptValue>, ExceptionState& exception_state); + ~PerformanceMark() override = default; AtomicString entryType() const override; PerformanceEntryType EntryTypeEnum() const override; @@ -69,8 +70,6 @@ class CORE_EXPORT PerformanceMark final : public PerformanceEntry { void Trace(Visitor*) const override; private: - ~PerformanceMark() override = default; - scoped_refptr<SerializedScriptValue> serialized_detail_; // In order to prevent cross-world reference leak, we create a copy of the // detail for each world. diff --git a/chromium/third_party/blink/renderer/core/timing/performance_measure.h b/chromium/third_party/blink/renderer/core/timing/performance_measure.h index f1258ced973..862b24de3fd 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_measure.h +++ b/chromium/third_party/blink/renderer/core/timing/performance_measure.h @@ -49,6 +49,7 @@ class CORE_EXPORT PerformanceMeasure final : public PerformanceEntry { double end_time, scoped_refptr<SerializedScriptValue>, ExceptionState&); + ~PerformanceMeasure() override = default; static PerformanceMeasure* Create(ScriptState*, const AtomicString& name, @@ -67,7 +68,6 @@ class CORE_EXPORT PerformanceMeasure final : public PerformanceEntry { void Trace(Visitor* visitor) const override; private: - ~PerformanceMeasure() override = default; scoped_refptr<SerializedScriptValue> serialized_detail_; // In order to prevent cross-world reference leak, we create a copy of the // detail for each world. 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 de84bfd7ab6..441620f5276 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 @@ -55,6 +55,7 @@ PerformanceNavigationTiming::PerformanceNavigationTiming( info->FinalResponse().CurrentRequestUrl().GetString()) : g_empty_atom, time_origin, + // TODO(crbug.com/1153336) Use network::IsUrlPotentiallyTrustworthy(). SecurityOrigin::IsSecure(window->Url()), std::move(server_timing), window), @@ -166,7 +167,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::unloadEventStart() const { DocumentLoadTiming* timing = GetDocumentLoadTiming(); if (!allow_redirect_details || !timing || - !timing->HasSameOriginAsPreviousDocument()) + !timing->CanRequestFromPreviousDocument()) return 0; return Performance::MonotonicTimeToDOMHighResTimeStamp( TimeOrigin(), timing->UnloadEventStart(), @@ -178,7 +179,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::unloadEventEnd() const { DocumentLoadTiming* timing = GetDocumentLoadTiming(); if (!allow_redirect_details || !timing || - !timing->HasSameOriginAsPreviousDocument()) + !timing->CanRequestFromPreviousDocument()) return 0; return Performance::MonotonicTimeToDOMHighResTimeStamp( TimeOrigin(), timing->UnloadEventEnd(), false /* allow_negative_value */); 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 c566ccb134b..22eb990a25e 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 @@ -35,6 +35,7 @@ class CORE_EXPORT PerformanceNavigationTiming final ResourceTimingInfo*, base::TimeTicks time_origin, HeapVector<Member<PerformanceServerTiming>>); + ~PerformanceNavigationTiming() override; // Attributes inherited from PerformanceEntry. DOMHighResTimeStamp duration() const override; @@ -67,8 +68,6 @@ class CORE_EXPORT PerformanceNavigationTiming final void BuildJSONValue(V8ObjectBuilder&) const override; private: - ~PerformanceNavigationTiming() override; - static AtomicString GetNavigationType(WebNavigationType, const Document*); const DocumentTiming* GetDocumentTiming() const; 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 d423813523f..ac07945835e 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_test.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_test.cc @@ -45,6 +45,8 @@ class TestPerformance : public Performance { class PerformanceTest : public PageTestBase { protected: + ~PerformanceTest() override { execution_context_->NotifyContextDestroyed(); } + void Initialize(ScriptState* script_state) { v8::Local<v8::Function> callback = v8::Function::New(script_state->GetContext(), nullptr).ToLocalChecked(); 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 e297230e2e1..589a2514be7 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_timing.cc +++ b/chromium/third_party/blink/renderer/core/timing/performance_timing.cc @@ -85,7 +85,7 @@ uint64_t PerformanceTiming::unloadEventStart() const { return 0; if (timing->HasCrossOriginRedirect() || - !timing->HasSameOriginAsPreviousDocument()) + !timing->CanRequestFromPreviousDocument()) return 0; return MonotonicTimeToIntegerMilliseconds(timing->UnloadEventStart()); @@ -97,7 +97,7 @@ uint64_t PerformanceTiming::unloadEventEnd() const { return 0; if (timing->HasCrossOriginRedirect() || - !timing->HasSameOriginAsPreviousDocument()) + !timing->CanRequestFromPreviousDocument()) return 0; return MonotonicTimeToIntegerMilliseconds(timing->UnloadEventEnd()); @@ -482,6 +482,15 @@ uint64_t PerformanceTiming::ExperimentalLargestTextPaintSize() const { return paint_timing_detector->ExperimentalLargestTextPaintSize(); } +base::TimeTicks PerformanceTiming::LargestContentfulPaintAsMonotonicTime() + const { + PaintTimingDetector* paint_timing_detector = GetPaintTimingDetector(); + if (!paint_timing_detector) + return base::TimeTicks(); + + return paint_timing_detector->LargestContentfulPaint(); +} + uint64_t PerformanceTiming::FirstEligibleToPaint() const { const PaintTiming* timing = GetPaintTiming(); if (!timing) @@ -624,6 +633,30 @@ base::Optional<base::TimeTicks> PerformanceTiming::LastPortalActivatedPaint() return timing->LastPortalActivatedPaint(); } +base::Optional<base::TimeTicks> PerformanceTiming::UnloadStart() const { + DocumentLoadTiming* timing = GetDocumentLoadTiming(); + if (!timing) + return base::nullopt; + + return timing->UnloadEventStart(); +} + +base::Optional<base::TimeTicks> PerformanceTiming::UnloadEnd() const { + DocumentLoadTiming* timing = GetDocumentLoadTiming(); + if (!timing) + return base::nullopt; + + return timing->UnloadEventEnd(); +} + +base::Optional<base::TimeTicks> PerformanceTiming::CommitNavigationEnd() const { + DocumentLoadTiming* timing = GetDocumentLoadTiming(); + if (!timing) + return base::nullopt; + + return timing->CommitNavigationEnd(); +} + DocumentLoader* PerformanceTiming::GetDocumentLoader() const { return DomWindow() ? DomWindow()->GetFrame()->Loader().GetDocumentLoader() : nullptr; 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 2d210014755..8afad757e2d 100644 --- a/chromium/third_party/blink/renderer/core/timing/performance_timing.h +++ b/chromium/third_party/blink/renderer/core/timing/performance_timing.h @@ -97,12 +97,13 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable, uint64_t loadEventStart() const; uint64_t loadEventEnd() const; - // The below are non-spec timings, for Page Load UMA metrics. + // The below are non-spec timings, for Page Load UMA metrics. Not to be + // exposed to JavaScript. // The time immediately after the user agent finishes prompting to unload the // previous document, or if there is no previous document, the same value as // fetchStart. Intended to be used for correlation with other events internal - // to blink. Not to be exposed to JavaScript. + // to blink. base::TimeTicks NavigationStartAsMonotonicTime() const; // The timings after the page is restored from back-forward cache. BackForwardCacheRestoreTimings BackForwardCacheRestore() const; @@ -114,8 +115,7 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable, // that includes content of some kind (for example, text or image content). uint64_t FirstContentfulPaint() const; // The first 'contentful' paint as full-resolution monotonic time. Intended to - // be used for correlation with other events internal to blink. Not to be - // exposed to JavaScript. + // be used for correlation with other events internal to blink. base::TimeTicks FirstContentfulPaintAsMonotonicTime() const; // The time of the first 'meaningful' paint, A meaningful paint is a paint // where the page's primary content is visible. @@ -138,6 +138,9 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable, // are the time and size of it. uint64_t LargestTextPaint() const; uint64_t LargestTextPaintSize() const; + // Largest Contentful Paint is the either the largest text paint time or the + // largest image paint time, whichever has the larger size. + base::TimeTicks LargestContentfulPaintAsMonotonicTime() const; // Experimental versions of the above metrics. Currently these are computed by // considering the largest content seen so far, regardless of DOM node // removal. @@ -169,6 +172,11 @@ class CORE_EXPORT PerformanceTiming final : public ScriptWrappable, base::Optional<base::TimeDelta> FirstScrollDelay() const; // The hardware timestamp of the first scroll. base::Optional<base::TimeDelta> FirstScrollTimestamp() const; + // TimeTicks for unload start and end. + base::Optional<base::TimeTicks> UnloadStart() const; + base::Optional<base::TimeTicks> UnloadEnd() const; + // The timestamp of when the commit navigation finished in the frame loader. + base::Optional<base::TimeTicks> CommitNavigationEnd() const; uint64_t ParseStart() const; uint64_t ParseStop() const; 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 e4aa78261c8..05da2ae0e57 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 @@ -42,11 +42,11 @@ void InsertPerformanceEntry(PerformanceEntryMap& performance_entry_map, PerformanceEntryMap::iterator it = performance_entry_map.find(entry.name()); if (it != performance_entry_map.end()) { DCHECK(it->value); - it->value->push_back(entry); + it->value->push_back(&entry); } else { PerformanceEntryVector* vector = MakeGarbageCollected<PerformanceEntryVector>(); - vector->push_back(entry); + vector->push_back(&entry); performance_entry_map.Set(entry.name(), vector); } } 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 4404ed897f1..790ebdc3311 100644 --- a/chromium/third_party/blink/renderer/core/timing/profiler_group.cc +++ b/chromium/third_party/blink/renderer/core/timing/profiler_group.cc @@ -90,34 +90,50 @@ Profiler* ProfilerGroup::CreateProfiler(ScriptState* script_state, : v8::CpuProfilingOptions::kNoSampleLimit, static_cast<int>(sample_interval_us)); - cpu_profiler_->StartProfiling(V8String(isolate_, profiler_id), options); - - // Limit non-crossorigin script frames to the origin that started the - // profiler. - auto* execution_context = ExecutionContext::From(script_state); - scoped_refptr<const SecurityOrigin> source_origin( - execution_context->GetSecurityOrigin()); - - // The V8 CPU profiler ticks in multiples of the base sampling interval. This - // effectively means that we gather samples at the multiple of the base - // sampling interval that's greater than or equal to the requested interval. - int effective_sample_interval_ms = - static_cast<int>(sample_interval.InMilliseconds()); - if (effective_sample_interval_ms % kBaseSampleIntervalMs != 0 || - effective_sample_interval_ms == 0) { - effective_sample_interval_ms += - (kBaseSampleIntervalMs - - effective_sample_interval_ms % kBaseSampleIntervalMs); + v8::CpuProfilingStatus status = + cpu_profiler_->StartProfiling(V8String(isolate_, profiler_id), options); + + switch (status) { + case v8::CpuProfilingStatus::kErrorTooManyProfilers: { + exception_state.ThrowTypeError( + "Reached maximum concurrent amount of profilers"); + return nullptr; + } + case v8::CpuProfilingStatus::kAlreadyStarted: { + // Since we increment the profiler id for every invocation of + // StartProfiling, we do not expect to hit kAlreadyStarted status + DCHECK(false); + return nullptr; + } + case v8::CpuProfilingStatus::kStarted: { + // Limit non-crossorigin script frames to the origin that started the + // profiler. + auto* execution_context = ExecutionContext::From(script_state); + scoped_refptr<const SecurityOrigin> source_origin( + execution_context->GetSecurityOrigin()); + + // The V8 CPU profiler ticks in multiples of the base sampling interval. + // This effectively means that we gather samples at the multiple of the + // base sampling interval that's greater than or equal to the requested + // interval. + int effective_sample_interval_ms = + static_cast<int>(sample_interval.InMilliseconds()); + if (effective_sample_interval_ms % kBaseSampleIntervalMs != 0 || + effective_sample_interval_ms == 0) { + effective_sample_interval_ms += + (kBaseSampleIntervalMs - + effective_sample_interval_ms % kBaseSampleIntervalMs); + } + + auto* profiler = MakeGarbageCollected<Profiler>( + this, script_state, profiler_id, effective_sample_interval_ms, + source_origin, time_origin); + profilers_.insert(profiler); + + num_active_profilers_++; + return profiler; + } } - - auto* profiler = MakeGarbageCollected<Profiler>( - this, script_state, profiler_id, effective_sample_interval_ms, - source_origin, time_origin); - profilers_.insert(profiler); - - num_active_profilers_++; - - return profiler; } ProfilerGroup::~ProfilerGroup() { 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 c775f27c1be..da540b773de 100644 --- a/chromium/third_party/blink/renderer/core/timing/profiler_group.h +++ b/chromium/third_party/blink/renderer/core/timing/profiler_group.h @@ -53,7 +53,7 @@ class CORE_EXPORT ProfilerGroup // Cancels a profiler, discarding its associated trace. void CancelProfiler(Profiler*); - // Asynchronously cancels a profiler. Invoked on Profiler descrution. + // Asynchronously cancels a profiler. Invoked on Profiler destruction. void CancelProfilerAsync(ScriptState*, Profiler*); // Internal implementation of cancel. void CancelProfilerImpl(String profiler_id); diff --git a/chromium/third_party/blink/renderer/core/timing/profiler_group_test.cc b/chromium/third_party/blink/renderer/core/timing/profiler_group_test.cc index c0af8fdd41c..eb477f3852b 100644 --- a/chromium/third_party/blink/renderer/core/timing/profiler_group_test.cc +++ b/chromium/third_party/blink/renderer/core/timing/profiler_group_test.cc @@ -16,25 +16,10 @@ namespace blink { namespace { static constexpr int kLargeProfilerCount = 128; +static constexpr int kMaxConcurrentProfilerCount = 100; } // namespace -// Tests that a leaked profiler doesn't crash the isolate on heap teardown. -TEST(ProfilerGroupTest, LeakProfiler) { - V8TestingScope scope; - - ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate()); - - ProfilerInitOptions* init_options = ProfilerInitOptions::Create(); - init_options->setSampleInterval(0); - init_options->setMaxBufferSize(0); - Profiler* profiler = profiler_group->CreateProfiler( - scope.GetScriptState(), *init_options, base::TimeTicks(), - scope.GetExceptionState()); - - EXPECT_FALSE(profiler->stopped()); -} - TEST(ProfilerGroupTest, StopProfiler) { V8TestingScope scope; @@ -83,6 +68,9 @@ TEST(ProfilerGroupTest, CreateProfiler) { EXPECT_FALSE(profiler->stopped()); EXPECT_FALSE(scope.GetExceptionState().HadException()); + + // clean up + profiler->stop(scope.GetScriptState()); } TEST(ProfilerGroupTest, ClampedSamplingIntervalZero) { @@ -102,6 +90,9 @@ TEST(ProfilerGroupTest, ClampedSamplingIntervalZero) { // interval. EXPECT_EQ(profiler->sampleInterval(), ProfilerGroup::GetBaseSampleInterval().InMilliseconds()); + + // clean up + profiler->stop(scope.GetScriptState()); } TEST(ProfilerGroupTest, ClampedSamplingIntervalNext) { @@ -123,6 +114,42 @@ TEST(ProfilerGroupTest, ClampedSamplingIntervalNext) { // interval. EXPECT_EQ(profiler->sampleInterval(), (ProfilerGroup::GetBaseSampleInterval() * 2).InMilliseconds()); + + // clean up + profiler->stop(scope.GetScriptState()); +} + +TEST(ProfilerGroupTest, V8ProfileLimitThrowsExceptionWhenMaxConcurrentReached) { + V8TestingScope scope; + + HeapVector<Member<Profiler>> profilers; + ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate()); + ProfilerInitOptions* init_options = ProfilerInitOptions::Create(); + + for (auto i = 0; i < kMaxConcurrentProfilerCount; i++) { + init_options->setSampleInterval(i); + profilers.push_back(profiler_group->CreateProfiler( + scope.GetScriptState(), *init_options, base::TimeTicks(), + scope.GetExceptionState())); + EXPECT_FALSE(scope.GetExceptionState().HadException()); + } + + // check kErrorTooManyProfilers + ProfilerGroup* extra_profiler_group = ProfilerGroup::From(scope.GetIsolate()); + ProfilerInitOptions* extra_init_options = ProfilerInitOptions::Create(); + extra_init_options->setSampleInterval(100); + for (auto i = kMaxConcurrentProfilerCount; i < kLargeProfilerCount; i++) { + extra_profiler_group->CreateProfiler(scope.GetScriptState(), + *extra_init_options, base::TimeTicks(), + scope.GetExceptionState()); + EXPECT_TRUE(scope.GetExceptionState().HadException()); + EXPECT_EQ(scope.GetExceptionState().Message(), + "Reached maximum concurrent amount of profilers"); + } + + for (auto profiler : profilers) { + profiler->stop(scope.GetScriptState()); + } } TEST(ProfilerGroupTest, NegativeSamplingInterval) { @@ -152,30 +179,6 @@ TEST(ProfilerGroupTest, OverflowSamplingInterval) { EXPECT_TRUE(scope.GetExceptionState().HadException()); } -// Tests behaviour when exceeding the maximum number of concurrent profiles -// supported by the V8 profiling API (https://crbug.com/1052341). -TEST(ProfilerGroupTest, V8ProfileLimit) { - V8TestingScope scope; - - ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate()); - - ProfilerInitOptions* init_options = ProfilerInitOptions::Create(); - init_options->setSampleInterval(0); - - HeapVector<Member<Profiler>> profilers; - for (auto i = 0; i < kLargeProfilerCount; i++) { - // TODO(acomminos): The V8 public API should likely be changed to expose - // exceeding the profile limit during creation. This would enable - // instantiation of profiles to cause a promise rejection instead. - profilers.push_back(profiler_group->CreateProfiler( - scope.GetScriptState(), *init_options, base::TimeTicks(), - scope.GetExceptionState())); - } - for (auto profiler : profilers) { - profiler->stop(scope.GetScriptState()); - } -} - TEST(ProfilerGroupTest, Bug1119865) { class ExpectNoCallFunction : public ScriptFunction { public: @@ -208,6 +211,27 @@ TEST(ProfilerGroupTest, Bug1119865) { profiler->stop(scope.GetScriptState()).Then(function); } +/* + * LEAK TESTS - SHOULD RUN LAST + */ + +// Tests that a leaked profiler doesn't crash the isolate on heap teardown. +// These should run last +TEST(ProfilerGroupTest, LeakProfiler) { + V8TestingScope scope; + + ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate()); + + ProfilerInitOptions* init_options = ProfilerInitOptions::Create(); + init_options->setSampleInterval(0); + init_options->setMaxBufferSize(0); + Profiler* profiler = profiler_group->CreateProfiler( + scope.GetScriptState(), *init_options, base::TimeTicks(), + scope.GetExceptionState()); + + EXPECT_FALSE(profiler->stopped()); +} + // Tests that a leaked profiler doesn't crash when disposed alongside its // context. TEST(ProfilerGroupTest, LeakProfilerWithContext) { diff --git a/chromium/third_party/blink/renderer/core/timing/task_attribution_timing.cc b/chromium/third_party/blink/renderer/core/timing/task_attribution_timing.cc index 2588b14206b..67c00aa7793 100644 --- a/chromium/third_party/blink/renderer/core/timing/task_attribution_timing.cc +++ b/chromium/third_party/blink/renderer/core/timing/task_attribution_timing.cc @@ -12,9 +12,9 @@ namespace blink { TaskAttributionTiming::TaskAttributionTiming(const AtomicString& name, const AtomicString& container_type, - const String& container_src, - const String& container_id, - const String& container_name) + const AtomicString& container_src, + const AtomicString& container_id, + const AtomicString& container_name) : PerformanceEntry(name, 0.0, 0.0), container_type_(container_type), container_src_(container_src), diff --git a/chromium/third_party/blink/renderer/core/timing/task_attribution_timing.h b/chromium/third_party/blink/renderer/core/timing/task_attribution_timing.h index 2c2ead9d6e1..7f33034e4c0 100644 --- a/chromium/third_party/blink/renderer/core/timing/task_attribution_timing.h +++ b/chromium/third_party/blink/renderer/core/timing/task_attribution_timing.h @@ -28,9 +28,9 @@ class TaskAttributionTiming final : public PerformanceEntry { TaskAttributionTiming(const AtomicString& type, const AtomicString& container_type, - const String& container_src, - const String& container_id, - const String& container_name); + const AtomicString& container_src, + const AtomicString& container_id, + const AtomicString& container_name); ~TaskAttributionTiming() override; private: 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 bf39c372991..8f068c7d8b9 100644 --- a/chromium/third_party/blink/renderer/core/timing/window_performance.cc +++ b/chromium/third_party/blink/renderer/core/timing/window_performance.cc @@ -31,6 +31,7 @@ #include "third_party/blink/renderer/core/timing/window_performance.h" +#include "base/trace_event/common/trace_event_common.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/task_type.h" @@ -44,6 +45,7 @@ #include "third_party/blink/renderer/core/html/html_frame_owner_element.h" #include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/core/loader/document_loader.h" +#include "third_party/blink/renderer/core/loader/interactive_detector.h" #include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/page/page_hidden_state.h" @@ -201,6 +203,10 @@ WindowPerformance::CreateNavigationTimingInstance() { if (!DomWindow()) return nullptr; DocumentLoader* document_loader = DomWindow()->document()->Loader(); + // TODO(npm): figure out when |document_loader| can be null and add tests. + DCHECK(document_loader); + if (!document_loader) + return nullptr; ResourceTimingInfo* info = document_loader->GetNavigationTimingInfo(); if (!info) return nullptr; @@ -314,7 +320,7 @@ void WindowPerformance::ReportLongTask(base::TimeTicks start_time, if (!culprit_dom_window || !culprit_dom_window->GetFrame() || !culprit_dom_window->GetFrame()->DeprecatedLocalOwner()) { AddLongTaskTiming(start_time, end_time, attribution.first, "window", - g_empty_string, g_empty_string, g_empty_string); + g_empty_atom, g_empty_atom, g_empty_atom); } else { HTMLFrameOwnerElement* frame_owner = culprit_dom_window->GetFrame()->DeprecatedLocalOwner(); @@ -346,40 +352,46 @@ void WindowPerformance::RegisterEventTiming(const AtomicString& event_type, 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 swap promise for it. + // is being queued to know when to queue a presentation promise for it. event_timings_.push_back(entry); event_frames_.push_back(frame_index_); - bool should_queue_swap_promise = false; - // If there are no pending swap promises, we should queue one. This ensures - // that |event_timings_| are processed even if the Blink lifecycle does not - // occur due to no DOM updates. - if (pending_swap_promise_count_ == 0u) { - should_queue_swap_promise = true; + 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 + // does not occur due to no DOM updates. + if (pending_presentation_promise_count_ == 0u) { + should_queue_presentation_promise = true; } else { - // There are pending swap promises, so only queue one if the event - // corresponds to a later frame than the one of the latest queued swap - // promise. - should_queue_swap_promise = frame_index_ > last_registered_frame_index_; + // There are pending presentation promises, so only queue one if the event + // corresponds to a later frame than the one of the latest queued + // presentation promise. + should_queue_presentation_promise = + frame_index_ > last_registered_frame_index_; } - if (should_queue_swap_promise) { - DomWindow()->GetFrame()->GetChromeClient().NotifySwapTime( + if (should_queue_presentation_promise) { + DomWindow()->GetFrame()->GetChromeClient().NotifyPresentationTime( *DomWindow()->GetFrame(), CrossThreadBindOnce(&WindowPerformance::ReportEventTimings, WrapCrossThreadWeakPersistent(this), frame_index_)); last_registered_frame_index_ = frame_index_; - ++pending_swap_promise_count_; + ++pending_presentation_promise_count_; } } void WindowPerformance::ReportEventTimings(uint64_t frame_index, WebSwapResult result, base::TimeTicks timestamp) { - DCHECK(pending_swap_promise_count_); - --pending_swap_promise_count_; + 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()) return; + + if (!DomWindow()) + return; + InteractiveDetector* interactive_detector = + InteractiveDetector::From(*(DomWindow()->document())); bool event_timing_enabled = RuntimeEnabledFeatures::EventTimingEnabled(GetExecutionContext()); DOMHighResTimeStamp end_time = MonotonicTimeToDOMHighResTimeStamp(timestamp); @@ -396,7 +408,35 @@ void WindowPerformance::ReportEventTimings(uint64_t frame_index, event_frames_.pop_front(); int duration_in_ms = std::round((end_time - entry->startTime()) / 8) * 8; + base::TimeDelta input_delay = base::TimeDelta::FromMillisecondsD( + entry->processingStart() - entry->startTime()); + base::TimeDelta processing_time = base::TimeDelta::FromMillisecondsD( + entry->processingEnd() - entry->processingStart()); + base::TimeDelta time_to_next_paint = + base::TimeDelta::FromMillisecondsD(end_time - entry->processingEnd()); entry->SetDuration(duration_in_ms); + TRACE_EVENT2("devtools.timeline", "EventTiming", "data", + entry->ToTracedValue(), "frame", + ToTraceValue(DomWindow()->GetFrame())); + if (entry->name() == "pointerdown") { + pending_pointer_down_input_delay_ = input_delay; + pending_pointer_down_processing_time_ = processing_time; + pending_pointer_down_time_to_next_paint_ = time_to_next_paint; + } else if (entry->name() == "pointerup") { + if (pending_pointer_down_time_to_next_paint_.has_value() && + interactive_detector) { + interactive_detector->RecordInputEventTimingUKM( + pending_pointer_down_input_delay_.value(), + pending_pointer_down_processing_time_.value(), + pending_pointer_down_time_to_next_paint_.value(), entry->name()); + } + } else if ((entry->name() == "click" || entry->name() == "keydown" || + entry->name() == "mousedown") && + interactive_detector) { + interactive_detector->RecordInputEventTimingUKM( + input_delay, processing_time, time_to_next_paint, entry->name()); + } + if (!first_input_timing_) { if (entry->name() == "pointerdown") { first_pointer_down_event_timing_ = 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 be9e535390b..7c33989791a 100644 --- a/chromium/third_party/blink/renderer/core/timing/window_performance.h +++ b/chromium/third_party/blink/renderer/core/timing/window_performance.h @@ -70,9 +70,9 @@ class CORE_EXPORT WindowPerformance final : public Performance, bool FirstInputDetected() const { return !!first_input_timing_; } - // This method creates a PerformanceEventTiming and if needed creates a swap - // promise to calculate the |duration| attribute when such promise is - // resolved. + // This method creates a PerformanceEventTiming and if needed creates a + // presentation promise to calculate the |duration| attribute when such + // promise is resolved. void RegisterEventTiming(const AtomicString& event_type, base::TimeTicks start_time, base::TimeTicks processing_start, @@ -123,8 +123,8 @@ class CORE_EXPORT WindowPerformance final : public Performance, void BuildJSONValue(V8ObjectBuilder&) const override; - // Method called once swap promise is resolved. It will add all event timings - // that have not been added since the last swap promise. + // Method called once presentation promise 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); @@ -133,13 +133,13 @@ class CORE_EXPORT WindowPerformance final : public Performance, // Counter of the current frame index, based on calls to OnPaintFinished(). uint64_t frame_index_ = 1; - // Monotonically increasing value with the last frame index on which a swap - // promise was queued; + // Monotonically increasing value with the last frame index on which a + // presentation promise was queued; uint64_t last_registered_frame_index_ = 0; - // Number of pending swap promises. - uint16_t pending_swap_promise_count_ = 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 swap promise used to + // 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_; @@ -155,6 +155,9 @@ class CORE_EXPORT WindowPerformance final : public Performance, Member<EventCounts> event_counts_; mutable Member<PerformanceNavigation> navigation_; mutable Member<PerformanceTiming> timing_; + base::Optional<base::TimeDelta> pending_pointer_down_input_delay_; + base::Optional<base::TimeDelta> pending_pointer_down_processing_time_; + base::Optional<base::TimeDelta> pending_pointer_down_time_to_next_paint_; }; } // namespace blink 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 070c3f609e3..c2ae1bfcb68 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 @@ -184,7 +184,8 @@ TEST(PerformanceLifetimeTest, SurviveContextSwitch) { // Simulate changing the document while keeping the window. std::unique_ptr<WebNavigationParams> params = - WebNavigationParams::CreateWithHTMLBuffer(SharedBuffer::Create(), url); + WebNavigationParams::CreateWithHTMLBufferForTesting( + SharedBuffer::Create(), url); page_holder->GetFrame().Loader().CommitNavigation(std::move(params), nullptr); EXPECT_EQ(perf, DOMWindowPerformance::performance( |