diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/timing/measure_memory')
11 files changed, 267 insertions, 458 deletions
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; +}; |