diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 8aec0f1cf10f9..077fdcf61ca07 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -134,3 +134,5 @@ fix_add_support_for_skipping_first_2_no-op_refreshes_in_thumb_cap.patch remove_dxdiag_telemetry_code.patch cherry-pick-2607ddacd643.patch cherry-pick-1b1f34234346.patch +bindings_refactor_domdatastore.patch +merge_fix_domarraybuffer_isdetached_and_comment_out_a_check.patch diff --git a/patches/chromium/bindings_refactor_domdatastore.patch b/patches/chromium/bindings_refactor_domdatastore.patch new file mode 100644 index 0000000000000..734947f74c6ff --- /dev/null +++ b/patches/chromium/bindings_refactor_domdatastore.patch @@ -0,0 +1,1231 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Lippautz +Date: Tue, 30 Jan 2024 16:05:33 +0000 +Subject: bindings: Refactor DOMDataStore + +Make DOMDataStore the entity that can set wrapper references in all +worlds, using inline storage where supported and otherwise fall back +to the wrapper map. + +This is a refactoring that does not change any semantics but should +make fast paths more obvious. + +Bug: chromium:1520621 +Change-Id: Id8fd21d7598d818d3e820cc728f7f9357c1ec2d6 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5245546 +Commit-Queue: Michael Lippautz +Reviewed-by: Kentaro Hara +Cr-Commit-Position: refs/heads/main@{#1253935} + +diff --git a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc +index c15355bf971294b99e9bb484faad84ff760398eb..770fd0a15c4a3521315b70e46a3646e361432508 100644 +--- a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc ++++ b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc +@@ -122,9 +122,17 @@ void LocalWindowProxy::DisposeContext(Lifecycle next_status, + ToScriptWrappable(global->GetPrototype().As())); + } + V8DOMWrapper::ClearNativeInfo(GetIsolate(), global); +- DOMWrapperWorld::ClearWrapperIfEqualTo(GetFrame()->DomWindow(), global); ++ script_state_->World().DomDataStore().ClearIfEqualTo( ++ GetFrame()->DomWindow(), global); ++#if DCHECK_IS_ON() ++ Vector> all_worlds; ++ DOMWrapperWorld::AllWorldsInIsolate(script_state_->GetIsolate(), ++ all_worlds); ++ for (auto& world : all_worlds) { ++ DCHECK(!world->DomDataStore().EqualTo(GetFrame()->DomWindow(), global)); ++ } ++#endif // DCHECK_IS_ON() + script_state_->DetachGlobalObject(); +- + #if DCHECK_IS_ON() + DidDetachGlobalObject(); + #endif +@@ -534,7 +542,7 @@ void LocalWindowProxy::NamedItemAdded(HTMLDocument* document, + + ScriptState::Scope scope(script_state_); + v8::Local document_wrapper = +- world_->DomDataStore().Get(document, GetIsolate()); ++ world_->DomDataStore().Get(GetIsolate(), document).ToLocalChecked(); + // When a non-configurable own property (e.g. unforgeable attribute) already + // exists, `SetAccessor` fails and throws. Ignore the exception because own + // properties have priority over named properties. +@@ -561,7 +569,7 @@ void LocalWindowProxy::NamedItemRemoved(HTMLDocument* document, + return; + ScriptState::Scope scope(script_state_); + v8::Local document_wrapper = +- world_->DomDataStore().Get(document, GetIsolate()); ++ world_->DomDataStore().Get(GetIsolate(), document).ToLocalChecked(); + document_wrapper + ->Delete(GetIsolate()->GetCurrentContext(), V8String(GetIsolate(), name)) + .ToChecked(); +diff --git a/third_party/blink/renderer/bindings/core/v8/observable_array_exotic_object_impl.cc b/third_party/blink/renderer/bindings/core/v8/observable_array_exotic_object_impl.cc +index d53c06ed5d436bd22898470c3de297d80e6ebae3..f1bbf27750c6d269a3d02f08348d78a2bd4ee375 100644 +--- a/third_party/blink/renderer/bindings/core/v8/observable_array_exotic_object_impl.cc ++++ b/third_party/blink/renderer/bindings/core/v8/observable_array_exotic_object_impl.cc +@@ -64,7 +64,7 @@ ObservableArrayExoticObjectImpl::ObservableArrayExoticObjectImpl( + v8::Local ObservableArrayExoticObjectImpl::Wrap( + ScriptState* script_state) { + v8::Isolate* isolate = script_state->GetIsolate(); +- DCHECK(!DOMDataStore::ContainsWrapper(this, isolate)); ++ DCHECK(!DOMDataStore::ContainsWrapper(isolate, this)); + + // The proxy target object must be a JS Array (v8::Array) by definition. + // Especially it's important that IsArray(proxy) evaluates to true. +diff --git a/third_party/blink/renderer/bindings/core/v8/remote_window_proxy.cc b/third_party/blink/renderer/bindings/core/v8/remote_window_proxy.cc +index 3caa238f10adc7294cac000aedf326d097758362..89211fcb8cb37549446ea9bbb14e1ae3a21afe1c 100644 +--- a/third_party/blink/renderer/bindings/core/v8/remote_window_proxy.cc ++++ b/third_party/blink/renderer/bindings/core/v8/remote_window_proxy.cc +@@ -75,10 +75,16 @@ void RemoteWindowProxy::DisposeContext(Lifecycle next_status, + v8::HandleScope handle_scope(GetIsolate()); + v8::Local global = global_proxy_.Get(GetIsolate()); + V8DOMWrapper::ClearNativeInfo(GetIsolate(), global); +- DOMWrapperWorld::ClearWrapperIfEqualTo(GetFrame()->DomWindow(), global); ++ world_->DomDataStore().ClearIfEqualTo(GetFrame()->DomWindow(), global); + #if DCHECK_IS_ON() ++ Vector> all_worlds; ++ DOMWrapperWorld::AllWorldsInIsolate(GetIsolate(), all_worlds); ++ for (auto& world : all_worlds) { ++ DCHECK(!world->DomDataStore().EqualTo(GetFrame()->DomWindow(), global)); ++ } ++ + DidDetachGlobalObject(); +-#endif ++#endif // DCHECK_IS_ON() + } + + DCHECK_EQ(lifecycle_, Lifecycle::kContextIsInitialized); +diff --git a/third_party/blink/renderer/bindings/core/v8/to_v8_traits.h b/third_party/blink/renderer/bindings/core/v8/to_v8_traits.h +index 30ddc5b219c7d6d7630557e90d209a90bd0e00cd..45f4c36c20cc2db00ff6769c08271e629de99634 100644 +--- a/third_party/blink/renderer/bindings/core/v8/to_v8_traits.h ++++ b/third_party/blink/renderer/bindings/core/v8/to_v8_traits.h +@@ -240,9 +240,10 @@ namespace bindings { + ScriptState* script_state, + ScriptWrappable* script_wrappable) { + CHECK(script_wrappable); +- v8::Local wrapper = +- DOMDataStore::GetWrapper(script_wrappable, script_state->GetIsolate()); +- if (LIKELY(!wrapper.IsEmpty())) { ++ v8::Local wrapper; ++ if (LIKELY( ++ DOMDataStore::GetWrapper(script_state->GetIsolate(), script_wrappable) ++ .ToLocal(&wrapper))) { + return wrapper; + } + +@@ -255,9 +256,9 @@ namespace bindings { + ScriptWrappable* script_wrappable, + v8::Local creation_context_object) { + CHECK(script_wrappable); +- v8::Local wrapper = +- DOMDataStore::GetWrapper(script_wrappable, isolate); +- if (LIKELY(!wrapper.IsEmpty())) { ++ v8::Local wrapper; ++ if (LIKELY(DOMDataStore::GetWrapper(isolate, script_wrappable) ++ .ToLocal(&wrapper))) { + return wrapper; + } + +diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_context_snapshot_impl.cc b/third_party/blink/renderer/bindings/modules/v8/v8_context_snapshot_impl.cc +index a8d881630c81218d231e955eb1dabb266b81b309..e6ed0f24ea30453ae35a7d5e140085f6c5fdb432 100644 +--- a/third_party/blink/renderer/bindings/modules/v8/v8_context_snapshot_impl.cc ++++ b/third_party/blink/renderer/bindings/modules/v8/v8_context_snapshot_impl.cc +@@ -15,6 +15,7 @@ + #include "third_party/blink/renderer/bindings/modules/v8/v8_window.h" + #include "third_party/blink/renderer/core/execution_context/execution_context.h" + #include "third_party/blink/renderer/core/html/html_document.h" ++#include "third_party/blink/renderer/platform/bindings/dom_data_store.h" + #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" + #include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h" + #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +@@ -245,9 +246,10 @@ void DeserializeInternalFieldCallback(v8::Local object, + V8DOMWrapper::SetNativeInfo(deserializer_data->isolate, object, + V8HTMLDocument::GetWrapperTypeInfo(), + deserializer_data->html_document); +- bool result = deserializer_data->html_document->SetWrapper( +- deserializer_data->isolate, V8HTMLDocument::GetWrapperTypeInfo(), +- object); ++ const bool result = ++ DOMDataStore::SetWrapperInInlineStorage( ++ deserializer_data->isolate, deserializer_data->html_document, ++ V8HTMLDocument::GetWrapperTypeInfo(), object); + CHECK(result); + break; + } +diff --git a/third_party/blink/renderer/core/dom/text.cc b/third_party/blink/renderer/core/dom/text.cc +index fbbfa23941811c4dd6e56cd6a13244b77cc39783..f79f59a91bf66ce07b4401accab88f1f3dbc3e53 100644 +--- a/third_party/blink/renderer/core/dom/text.cc ++++ b/third_party/blink/renderer/core/dom/text.cc +@@ -144,7 +144,7 @@ Text* Text::splitText(unsigned offset, ExceptionState& exception_state) { + + // [NewObject] must always create a new wrapper. Check that a wrapper + // does not exist yet. +- DCHECK(DOMDataStore::GetWrapper(new_text, GetDocument().GetAgent().isolate()) ++ DCHECK(DOMDataStore::GetWrapper(GetDocument().GetAgent().isolate(), new_text) + .IsEmpty()); + + return new_text; +diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc +index 41dfa6067538064d4b7f21c143baaee21dae5528..db557dc5f118cb0190085f61a799709791f7a606 100644 +--- a/third_party/blink/renderer/core/frame/dom_window.cc ++++ b/third_party/blink/renderer/core/frame/dom_window.cc +@@ -106,7 +106,8 @@ v8::Local DOMWindow::AssociateWithWrapper( + v8::Local wrapper) { + // Using the world directly avoids fetching it from a potentially + // half-initialized context. +- if (world->DomDataStore().Set(isolate, this, wrapper_type_info, wrapper)) { ++ if (world->DomDataStore().Set( ++ isolate, this, wrapper_type_info, wrapper)) { + V8DOMWrapper::SetNativeInfo(isolate, wrapper, wrapper_type_info, this); + DCHECK(V8DOMWrapper::HasInternalFieldsSet(wrapper)); + } +diff --git a/third_party/blink/renderer/core/frame/location.cc b/third_party/blink/renderer/core/frame/location.cc +index f9dbd16a03d80224b0d3e28a46917fbbe337d8a2..31676b163494573f77e65f1362678385bb1bb68e 100644 +--- a/third_party/blink/renderer/core/frame/location.cc ++++ b/third_party/blink/renderer/core/frame/location.cc +@@ -56,7 +56,7 @@ v8::Local Location::Wrap(ScriptState* script_state) { + // the cross-origin status changes by changing properties like + // |document.domain|. + if (IsA(dom_window_.Get())) { +- DCHECK(!DOMDataStore::ContainsWrapper(this, script_state->GetIsolate())); ++ DCHECK(!DOMDataStore::ContainsWrapper(script_state->GetIsolate(), this)); + + DOMWrapperWorld& world = script_state->World(); + v8::Isolate* isolate = script_state->GetIsolate(); +diff --git a/third_party/blink/renderer/core/script/detect_javascript_frameworks.cc b/third_party/blink/renderer/core/script/detect_javascript_frameworks.cc +index 15efc675f87b855e1b868be80cf5b98338ee2bbe..4fcace96ccfa5ec5897590480099fe60ccdfd921 100644 +--- a/third_party/blink/renderer/core/script/detect_javascript_frameworks.cc ++++ b/third_party/blink/renderer/core/script/detect_javascript_frameworks.cc +@@ -95,12 +95,14 @@ inline void CheckPropertyMatches(Element& element, + v8::Local context, + v8::Isolate* isolate, + JavaScriptFrameworkDetectionResult& result) { +- v8::Local v8_element = dom_data_store.Get(&element, isolate); +- if (v8_element.IsEmpty()) ++ v8::Local v8_element; ++ if (!dom_data_store.Get(isolate, &element).ToLocal(&v8_element)) { + return; ++ } + v8::Local property_names; +- if (!v8_element->GetOwnPropertyNames(context).ToLocal(&property_names)) ++ if (!v8_element->GetOwnPropertyNames(context).ToLocal(&property_names)) { + return; ++ } + + DEFINE_STATIC_LOCAL(AtomicString, vue_string, ("__vue__")); + DEFINE_STATIC_LOCAL(AtomicString, vue_app_string, ("__vue_app__")); +diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc +index 69060a1e5f6937896d385b5ceb70f2d49ef8669c..44e2849c8e4dfc38834c88937dc561858c13e726 100644 +--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc ++++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc +@@ -51,9 +51,12 @@ static void AccumulateArrayBuffersForAllWorlds( + Vector> worlds; + DOMWrapperWorld::AllWorldsInIsolate(isolate, worlds); + for (const auto& world : worlds) { +- v8::Local wrapper = world->DomDataStore().Get(object, isolate); +- if (!wrapper.IsEmpty()) ++ v8::Local wrapper; ++ if (world->DomDataStore() ++ .Get(isolate, object) ++ .ToLocal(&wrapper)) { + buffers.push_back(v8::Local::Cast(wrapper)); ++ } + } + } + +@@ -237,7 +240,7 @@ DOMArrayBuffer* DOMArrayBuffer::CreateUninitializedOrNull( + } + + v8::Local DOMArrayBuffer::Wrap(ScriptState* script_state) { +- DCHECK(!DOMDataStore::ContainsWrapper(this, script_state->GetIsolate())); ++ DCHECK(!DOMDataStore::ContainsWrapper(script_state->GetIsolate(), this)); + + const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo(); + +diff --git a/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc b/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc +index 59d8e8553b3c43ea68f43019974703d6fc0e83b8..fe5f1104ea156361f397cfa69944e6c19eb4ceb3 100644 +--- a/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc ++++ b/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc +@@ -50,7 +50,7 @@ DOMDataView* DOMDataView::Create(DOMArrayBufferBase* buffer, + } + + v8::Local DOMDataView::Wrap(ScriptState* script_state) { +- DCHECK(!DOMDataStore::ContainsWrapper(this, script_state->GetIsolate())); ++ DCHECK(!DOMDataStore::ContainsWrapper(script_state->GetIsolate(), this)); + + const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo(); + v8::Local v8_buffer = +diff --git a/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc +index 62278876ae55934c878dc22b97c8c6df1731e350..4ad4a143e1b08cea59a79171a0f7521f4e5aaa72 100644 +--- a/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc ++++ b/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc +@@ -38,7 +38,7 @@ const WrapperTypeInfo& DOMSharedArrayBuffer::wrapper_type_info_ = + #endif + + v8::Local DOMSharedArrayBuffer::Wrap(ScriptState* script_state) { +- DCHECK(!DOMDataStore::ContainsWrapper(this, script_state->GetIsolate())); ++ DCHECK(!DOMDataStore::ContainsWrapper(script_state->GetIsolate(), this)); + + const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo(); + v8::Local wrapper; +diff --git a/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc b/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc +index 2d874ba75ab27369f34ee9e51d6989fbf2e6da46..ba88280f7a7931dd7c23949ab2e9dd9ec424c31a 100644 +--- a/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc ++++ b/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc +@@ -12,7 +12,7 @@ namespace blink { + template + v8::Local DOMTypedArray::Wrap( + ScriptState* script_state) { +- DCHECK(!DOMDataStore::ContainsWrapper(this, script_state->GetIsolate())); ++ DCHECK(!DOMDataStore::ContainsWrapper(script_state->GetIsolate(), this)); + + const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo(); + DOMArrayBufferBase* buffer = BufferBase(); +diff --git a/third_party/blink/renderer/platform/bindings/dom_data_store.cc b/third_party/blink/renderer/platform/bindings/dom_data_store.cc +index f6b574ba2460b149f45379117e52a9d82b3bcad4..5bc84b868dbf072e9e2cb8c4aeee22a5ed4a9c57 100644 +--- a/third_party/blink/renderer/platform/bindings/dom_data_store.cc ++++ b/third_party/blink/renderer/platform/bindings/dom_data_store.cc +@@ -6,8 +6,8 @@ + + namespace blink { + +-DOMDataStore::DOMDataStore(v8::Isolate* isolate, bool is_main_world) +- : is_main_world_(is_main_world) {} ++DOMDataStore::DOMDataStore(v8::Isolate* isolate, bool can_use_inline_storage) ++ : can_use_inline_storage_(can_use_inline_storage) {} + + void DOMDataStore::Dispose() { + for (auto& it : wrapper_map_) { +diff --git a/third_party/blink/renderer/platform/bindings/dom_data_store.h b/third_party/blink/renderer/platform/bindings/dom_data_store.h +index db1c7e1592b01ed6b29a607a598b810b81aadde4..7153e8db04789ef2961b8de5e2a3d47fe747237a 100644 +--- a/third_party/blink/renderer/platform/bindings/dom_data_store.h ++++ b/third_party/blink/renderer/platform/bindings/dom_data_store.h +@@ -44,175 +44,389 @@ + + namespace blink { + +-// Holds a map specialized to map between ScriptWrappable objects and their +-// wrappers and provides an API to perform common operations with this map and +-// manage wrappers in a single world. Each world (DOMWrapperWorld) holds a +-// single map instance to hold wrappers only for that world. ++// DOMDataStore is the entity responsible for managing wrapper references (C++ ++// to JS) in different worlds and settings (get/set wrapper, set return value). ++// Such references are stored in two ways: ++// - Inline in ScriptWrappable (or CustomWrappable); or ++// - In an ephemeron map in the DOMDataStore instance that is tied to a context ++// in a V8 Isolate. ++// ++// The methods on DOMDataStore generally come in two forms: ++// - static methods: Have fast paths for inline storage (e.g. main world on the ++// main rendering thread) and otherwise consider the current world, i.e., the ++// world that corresponds to the current context ++// (`v8::Isolate::GetCurrentContext()`). ++// - instance methods: Have fast paths for inline storage and consider the ++// world the methods are invoked on. The calls are faster than the static ++// calls if the DOMDataStore is already around. ++// ++// Exceptions are methods that operate on all worlds instead of the current ++// world. The methods mention this fact explicitly. + class DOMDataStore final : public GarbageCollected { + public: + static DOMDataStore& Current(v8::Isolate* isolate) { + return DOMWrapperWorld::Current(isolate).DomDataStore(); + } + +- static bool SetReturnValue(v8::ReturnValue return_value, +- ScriptWrappable* object) { +- if (CanUseMainWorldWrapper()) +- return object->SetReturnValue(return_value); +- return Current(return_value.GetIsolate()) +- .SetReturnValueFrom(return_value, object); +- } ++ // Sets the `return_value` from `value`. Can be used from any world. Will only ++ // consider the current world. ++ static inline bool SetReturnValue(v8::ReturnValue return_value, ++ ScriptWrappable* value); + +- static bool SetReturnValueForMainWorld( ++ // Sets the `return_value` from `value` in a world that can use inline ++ // storage. ++ static inline bool SetReturnValueFromInlineStorage( + v8::ReturnValue return_value, +- ScriptWrappable* object) { +- return object->SetReturnValue(return_value); +- } +- +- static bool SetReturnValueFast(v8::ReturnValue return_value, +- ScriptWrappable* object, +- v8::Local holder, +- const ScriptWrappable* wrappable) { +- if (HolderContainsWrapperForMainWorld(holder, wrappable)) { +- // Verify our assumptions about the main world. +- DCHECK(Current(return_value.GetIsolate()).is_main_world_); +- return object->SetReturnValue(return_value); +- } +- return Current(return_value.GetIsolate()) +- .SetReturnValueFrom(return_value, object); +- } ++ const ScriptWrappable* value); + +- static v8::Local GetWrapper(ScriptWrappable* object, +- v8::Isolate* isolate) { +- if (CanUseMainWorldWrapper()) +- return object->MainWorldWrapper(isolate); +- return Current(isolate).Get(object, isolate); +- } ++ // Sets the `return_value` from `value` if already wrapped and returns false ++ // otherwise. Will use the `v8_receiver` and `blink_receiver` to check whether ++ // inline storage can be used. Can be used from any world. Will only consider ++ // the current world. ++ static inline bool SetReturnValueFast(v8::ReturnValue return_value, ++ ScriptWrappable* value, ++ v8::Local v8_receiver, ++ const ScriptWrappable* blink_receiver); + +- // Associates the given |object| with the given |wrapper| if the object is +- // not yet associated with any wrapper. Returns true if the given wrapper +- // is associated with the object, or false if the object is already +- // associated with a wrapper. In the latter case, |wrapper| will be updated +- // to the existing wrapper. +- [[nodiscard]] static bool SetWrapper(v8::Isolate* isolate, +- ScriptWrappable* object, +- const WrapperTypeInfo* wrapper_type_info, +- v8::Local& wrapper) { +- if (CanUseMainWorldWrapper()) +- return object->SetWrapper(isolate, wrapper_type_info, wrapper); +- return Current(isolate).Set(isolate, object, wrapper_type_info, wrapper); +- } ++ // Returns the wrapper for `object` in the world corresponding to `isolate`. ++ // Can be used from any world. Will only consider the current world. ++ static inline v8::MaybeLocal GetWrapper( ++ v8::Isolate* isolate, ++ const ScriptWrappable* object); + +- static bool ContainsWrapper(const ScriptWrappable* object, +- v8::Isolate* isolate) { +- return Current(isolate).ContainsWrapper(object); +- } ++ // Associates the given `object` with the given `wrapper` if the object is not ++ // yet associated with any wrapper. Returns true if the given wrapper is ++ // associated with the object, or false if the object is already associated ++ // with a wrapper. In the latter case, `wrapper` will be updated to the ++ // existing wrapper. Can be used from any world. Will only consider the ++ // current world. ++ [[nodiscard]] static inline bool SetWrapper( ++ v8::Isolate* isolate, ++ ScriptWrappable* object, ++ const WrapperTypeInfo* wrapper_type_info, ++ v8::Local& wrapper); ++ ++ // Same as `SetWrapper()` with the difference that it only considers inline ++ // storage. Can be used from any world. ++ template ++ [[nodiscard]] static inline bool SetWrapperInInlineStorage( ++ v8::Isolate* isolate, ++ ScriptWrappable* object, ++ const WrapperTypeInfo* wrapper_type_info, ++ v8::Local& wrapper); ++ ++ // Checks for the wrapper pair in all worlds and clears the first found pair. ++ // This should only be needed for garbage collection. All other callsites ++ // should know their worlds. ++ template ++ static inline bool ClearWrapperInAnyWorldIfEqualTo(ScriptWrappable* object, ++ const HandleType& handle); + +- DOMDataStore(v8::Isolate* isolate, bool is_main_world); ++ // Clears the inline storage wrapper if it is equal to `handle`. Can be used ++ // from any world. ++ template ++ static inline bool ClearInlineStorageWrapperIfEqualTo( ++ ScriptWrappable* object, ++ const HandleType& handle); ++ ++ // Checks whether a wrapper for `object` exists. Can be used from any world. ++ // Will only consider the current world. ++ static inline bool ContainsWrapper(v8::Isolate* isolate, ++ const ScriptWrappable* object); ++ ++ DOMDataStore(v8::Isolate* isolate, bool); + DOMDataStore(const DOMDataStore&) = delete; + DOMDataStore& operator=(const DOMDataStore&) = delete; + + // Clears all references. + void Dispose(); + +- v8::Local Get(ScriptWrappable* object, v8::Isolate* isolate) { +- if (is_main_world_) +- return object->MainWorldWrapper(isolate); +- auto it = wrapper_map_.find(object); +- if (it != wrapper_map_.end()) +- return it->value.Get(isolate); +- return v8::Local(); +- } +- +- [[nodiscard]] bool Set(v8::Isolate* isolate, +- ScriptWrappable* object, +- const WrapperTypeInfo* wrapper_type_info, +- v8::Local& wrapper) { +- DCHECK(object); +- DCHECK(!wrapper.IsEmpty()); +- if (is_main_world_) +- return object->SetWrapper(isolate, wrapper_type_info, wrapper); +- +- auto result = wrapper_map_.insert( +- object, wrapper_type_info->SupportsDroppingWrapper() +- ? TraceWrapperV8Reference( +- isolate, wrapper, +- TraceWrapperV8Reference::IsDroppable{}) +- : TraceWrapperV8Reference(isolate, wrapper)); +- // TODO(mlippautz): Check whether there's still recursive cases of +- // Wrap()/AssociateWithWrapper() that can run into the case of an existing +- // entry. +- if (UNLIKELY(!result.is_new_entry)) { +- DCHECK(!result.stored_value->value.IsEmpty()); +- wrapper = result.stored_value->value.Get(isolate); +- } +- return result.is_new_entry; +- } ++ // Same as `GetWrapper()` but for a single world. ++ template ++ inline v8::MaybeLocal Get(v8::Isolate* isolate, ++ const ScriptWrappable* object); ++ ++ // Same as `SetWrapper()` but for a single world. ++ template ++ [[nodiscard]] inline bool Set(v8::Isolate* isolate, ++ ScriptWrappable* object, ++ const WrapperTypeInfo* wrapper_type_info, ++ v8::Local& wrapper); ++ ++ // Same as `ContainsWrapper()` but for a single world. ++ inline bool Contains(const ScriptWrappable* object) const; ++ ++ // Returns true if the pair {object, handle} exists in the current world. ++ template ++ inline bool EqualTo(const ScriptWrappable* object, const HandleType& handle); + ++ // Clears the connection between object and handle in the current world. + template +- bool ClearWrapperIfEqualTo(ScriptWrappable* object, ++ inline bool ClearIfEqualTo(ScriptWrappable* object, + const HandleType& handle) { +- DCHECK(!is_main_world_); +- const auto& it = wrapper_map_.find(object); +- if (it != wrapper_map_.end() && it->value == handle) { +- it->value.Reset(); +- wrapper_map_.erase(it); +- return true; ++ if (can_use_inline_storage_) { ++ return ClearInlineStorageWrapperIfEqualTo(object, handle); + } +- return false; ++ return ClearInMapIfEqualTo(object, handle); + } + +- bool SetReturnValueFrom(v8::ReturnValue return_value, +- ScriptWrappable* object) { +- if (is_main_world_) +- return object->SetReturnValue(return_value); +- auto it = wrapper_map_.find(object); +- if (it != wrapper_map_.end()) { +- return_value.SetNonEmpty(it->value); ++ // Clears the connection between object and handle in the current world ++ // assuming no inline storage is available for this world. ++ template ++ inline bool ClearInMapIfEqualTo(const ScriptWrappable* object, ++ const HandleType& handle) { ++ DCHECK(!can_use_inline_storage_); ++ if (const auto& it = wrapper_map_.find(object); ++ it != wrapper_map_.end() && it->value == handle) { ++ it->value.Reset(); ++ wrapper_map_.erase(it); + return true; + } + return false; + } + +- bool ContainsWrapper(const ScriptWrappable* object) { +- if (is_main_world_) +- return object->ContainsWrapper(); +- return base::Contains(wrapper_map_, object); +- } +- + virtual void Trace(Visitor*) const; + + private: +- // We can use a wrapper stored in a ScriptWrappable when we're in the main +- // world. This method does the fast check if we're in the main world. If this ++ // We can use the inline storage in a ScriptWrappable when we're in the main ++ // world. This method does the fast check if we're in the main world. If this + // method returns true, it is guaranteed that we're in the main world. On the + // other hand, if this method returns false, nothing is guaranteed (we might + // be in the main world). +- static bool CanUseMainWorldWrapper() { ++ static bool CanUseInlineStorageForWrapper() { + return !WTF::MayNotBeMainThread() && + !DOMWrapperWorld::NonMainWorldsExistInMainThread(); + } + +- static bool HolderContainsWrapperForMainWorld( +- v8::Local holder, ++ inline bool SetReturnValueFrom(v8::ReturnValue return_value, ++ const ScriptWrappable* value); ++ ++ using WrapperRefType = decltype(ScriptWrappable::wrapper_); ++ ++ // Convenience methods for accessing the inlined storage. ++ static inline WrapperRefType& GetUncheckedInlineStorage( ++ ScriptWrappable* wrappable) { ++ return wrappable->wrapper_; ++ } ++ static inline const WrapperRefType& GetUncheckedInlineStorage( + const ScriptWrappable* wrappable) { +- // The first fastest way is to check that there is only the main world +- // on the main thread. +- if (CanUseMainWorldWrapper()) { +- return true; +- } +- // The second fastest way to check if we're in the main world is to +- // check if the wrappable's wrapper is the same as the holder. +- DCHECK(wrappable); +- return wrappable->IsEqualTo(holder); ++ return GetUncheckedInlineStorage(const_cast(wrappable)); ++ } ++ static inline WrapperRefType& GetInlineStorage(v8::Isolate* isolate, ++ ScriptWrappable* wrappable) { ++ // The following will crash if no context is entered. This is by design. The ++ // validation can be skipped with `SetWrapperInInlineStorage()`. ++ DCHECK(Current(isolate).can_use_inline_storage_); ++ return GetUncheckedInlineStorage(wrappable); ++ } ++ static inline const WrapperRefType& GetInlineStorage( ++ v8::Isolate* isolate, ++ const ScriptWrappable* wrappable) { ++ return GetInlineStorage(isolate, const_cast(wrappable)); + } + +- bool is_main_world_; ++ // Specifies whether this data store is allowed to use inline storage of a ++ // ScriptWrappable and can avoid using the ephemeron map below. ++ const bool can_use_inline_storage_; + // Ephemeron map: V8 wrapper will be kept alive as long as ScriptWrappable is. + HeapHashMap, + TraceWrapperV8Reference> + wrapper_map_; + }; + ++// static ++bool DOMDataStore::SetReturnValue(v8::ReturnValue return_value, ++ ScriptWrappable* value) { ++ if (CanUseInlineStorageForWrapper()) { ++ return SetReturnValueFromInlineStorage(return_value, value); ++ } ++ return Current(return_value.GetIsolate()) ++ .SetReturnValueFrom(return_value, value); ++} ++ ++// static ++bool DOMDataStore::SetReturnValueFromInlineStorage( ++ v8::ReturnValue return_value, ++ const ScriptWrappable* value) { ++ const auto& ref = GetInlineStorage(return_value.GetIsolate(), value); ++ if (!ref.IsEmpty()) { ++ return_value.SetNonEmpty(ref); ++ return true; ++ } ++ return false; ++} ++ ++// static ++bool DOMDataStore::SetReturnValueFast(v8::ReturnValue return_value, ++ ScriptWrappable* object, ++ v8::Local v8_receiver, ++ const ScriptWrappable* blink_receiver) { ++ // Fast checks for using inline storage: ++ // 1. Fast check for inline storage. ++ // 2. If the receiver receiver's inline wrapper is the V8 receiver. In this ++ // case we know we are in the world that can use inline storage. ++ DCHECK(blink_receiver); ++ DCHECK(!v8_receiver.IsEmpty()); ++ if (CanUseInlineStorageForWrapper() || ++ v8_receiver == GetUncheckedInlineStorage(blink_receiver)) { ++ return SetReturnValueFromInlineStorage(return_value, object); ++ } ++ return Current(return_value.GetIsolate()) ++ .SetReturnValueFrom(return_value, object); ++} ++ ++bool DOMDataStore::SetReturnValueFrom(v8::ReturnValue return_value, ++ const ScriptWrappable* value) { ++ if (can_use_inline_storage_) { ++ return SetReturnValueFromInlineStorage(return_value, value); ++ } ++ if (const auto it = wrapper_map_.find(value); it != wrapper_map_.end()) { ++ return_value.SetNonEmpty(it->value); ++ return true; ++ } ++ return false; ++} ++ ++// static ++v8::MaybeLocal DOMDataStore::GetWrapper( ++ v8::Isolate* isolate, ++ const ScriptWrappable* object) { ++ if (CanUseInlineStorageForWrapper()) { ++ return GetInlineStorage(isolate, object).Get(isolate); ++ } ++ return Current(isolate).Get(isolate, object); ++} ++ ++template ++v8::MaybeLocal DOMDataStore::Get(v8::Isolate* isolate, ++ const ScriptWrappable* object) { ++ if (can_use_inline_storage_) { ++ return entered_context ? GetInlineStorage(isolate, object).Get(isolate) ++ : GetUncheckedInlineStorage(object).Get(isolate); ++ } ++ if (const auto it = wrapper_map_.find(object); it != wrapper_map_.end()) { ++ return it->value.Get(isolate); ++ } ++ return {}; ++} ++ ++// static ++bool DOMDataStore::SetWrapper(v8::Isolate* isolate, ++ ScriptWrappable* object, ++ const WrapperTypeInfo* wrapper_type_info, ++ v8::Local& wrapper) { ++ if (CanUseInlineStorageForWrapper()) { ++ return SetWrapperInInlineStorage(isolate, object, wrapper_type_info, ++ wrapper); ++ } ++ return Current(isolate).Set(isolate, object, wrapper_type_info, wrapper); ++} ++ ++// static ++template ++bool DOMDataStore::SetWrapperInInlineStorage( ++ v8::Isolate* isolate, ++ ScriptWrappable* object, ++ const WrapperTypeInfo* wrapper_type_info, ++ v8::Local& wrapper) { ++ DCHECK(!wrapper.IsEmpty()); ++ auto& ref = entered_context ? GetInlineStorage(isolate, object) ++ : GetUncheckedInlineStorage(object); ++ if (UNLIKELY(!ref.IsEmpty())) { ++ wrapper = ref.Get(isolate); ++ return false; ++ } ++ if (wrapper_type_info->SupportsDroppingWrapper()) { ++ ref.Reset(isolate, wrapper, ++ TraceWrapperV8Reference::IsDroppable{}); ++ } else { ++ ref.Reset(isolate, wrapper); ++ } ++ DCHECK(!ref.IsEmpty()); ++ return true; ++} ++ ++template ++bool DOMDataStore::Set(v8::Isolate* isolate, ++ ScriptWrappable* object, ++ const WrapperTypeInfo* wrapper_type_info, ++ v8::Local& wrapper) { ++ DCHECK(object); ++ DCHECK(!wrapper.IsEmpty()); ++ if (can_use_inline_storage_) { ++ return SetWrapperInInlineStorage( ++ isolate, object, wrapper_type_info, wrapper); ++ } ++ auto result = wrapper_map_.insert( ++ object, wrapper_type_info->SupportsDroppingWrapper() ++ ? TraceWrapperV8Reference( ++ isolate, wrapper, ++ TraceWrapperV8Reference::IsDroppable{}) ++ : TraceWrapperV8Reference(isolate, wrapper)); ++ // TODO(mlippautz): Check whether there's still recursive cases of ++ // Wrap()/AssociateWithWrapper() that can run into the case of an existing ++ // entry. ++ if (UNLIKELY(!result.is_new_entry)) { ++ DCHECK(!result.stored_value->value.IsEmpty()); ++ wrapper = result.stored_value->value.Get(isolate); ++ } ++ return result.is_new_entry; ++} ++ ++// static ++bool DOMDataStore::ContainsWrapper(v8::Isolate* isolate, ++ const ScriptWrappable* object) { ++ if (CanUseInlineStorageForWrapper()) { ++ return !GetInlineStorage(isolate, object).IsEmpty(); ++ } ++ return Current(isolate).Contains(object); ++} ++ ++// Same as `ContainsWrapper()` but for a single world. ++bool DOMDataStore::Contains(const ScriptWrappable* object) const { ++ if (can_use_inline_storage_) { ++ return !GetUncheckedInlineStorage(object).IsEmpty(); ++ } ++ return base::Contains(wrapper_map_, object); ++} ++ ++// static ++template ++bool DOMDataStore::ClearWrapperInAnyWorldIfEqualTo(ScriptWrappable* object, ++ const HandleType& handle) { ++ if (LIKELY(ClearInlineStorageWrapperIfEqualTo(object, handle))) { ++ return true; ++ } ++ return DOMWrapperWorld::ClearWrapperInAnyNonInlineStorageWorldIfEqualTo( ++ object, handle); ++} ++ ++// static ++template ++bool DOMDataStore::ClearInlineStorageWrapperIfEqualTo( ++ ScriptWrappable* object, ++ const HandleType& handle) { ++ auto& ref = GetUncheckedInlineStorage(object); ++ if (ref == handle) { ++ ref.Reset(); ++ return true; ++ } ++ return false; ++} ++ ++template ++bool DOMDataStore::EqualTo(const ScriptWrappable* object, ++ const HandleType& handle) { ++ if (can_use_inline_storage_) { ++ return GetUncheckedInlineStorage(object) == handle; ++ } ++ if (const auto& it = wrapper_map_.find(object); ++ it != wrapper_map_.end() && it->value == handle) { ++ return true; ++ } ++ return false; ++} ++ + } // namespace blink + + #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_DATA_STORE_H_ +diff --git a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc +index e330dd46f1ed0bee4f209ab0ce2c058ca52f4029..663d5ecbfe3e2b7440e34e6c90d9738f666a8905 100644 +--- a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc ++++ b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc +@@ -291,12 +291,12 @@ int DOMWrapperWorld::GenerateWorldIdForType(WorldType world_type) { + } + + // static +-bool DOMWrapperWorld::ClearNonMainWorldWrapperIfEqualTo( ++bool DOMWrapperWorld::ClearWrapperInAnyNonInlineStorageWorldIfEqualTo( + ScriptWrappable* object, + const v8::Local& handle) { + for (DOMWrapperWorld* world : GetWorldMap().Values()) { + DOMDataStore& data_store = world->DomDataStore(); +- if (data_store.ClearWrapperIfEqualTo(object, handle)) { ++ if (data_store.ClearInMapIfEqualTo(object, handle)) { + return true; + } + } +@@ -304,12 +304,12 @@ bool DOMWrapperWorld::ClearNonMainWorldWrapperIfEqualTo( + } + + // static +-bool DOMWrapperWorld::ClearNonMainWorldWrapperIfEqualTo( ++bool DOMWrapperWorld::ClearWrapperInAnyNonInlineStorageWorldIfEqualTo( + ScriptWrappable* object, + const v8::TracedReference& handle) { + for (DOMWrapperWorld* world : GetWorldMap().Values()) { + DOMDataStore& data_store = world->DomDataStore(); +- if (data_store.ClearWrapperIfEqualTo(object, handle)) { ++ if (data_store.ClearInMapIfEqualTo(object, handle)) { + return true; + } + } +diff --git a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h +index b8fccf776cb01c120ac589072f5183d0f4318bdf..ec51d8bbed1af2a192211d19a89ced7da643dda8 100644 +--- a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h ++++ b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h +@@ -171,24 +171,17 @@ class PLATFORM_EXPORT DOMWrapperWorld : public RefCounted { + return *v8_object_data_store_; + } + +- // Clear the reference pointing from |object| to |handle| in any world. +- template +- inline static bool ClearWrapperIfEqualTo(ScriptWrappable* object, +- const HandleType& handle); +- +- // Clear the reference pointing from |object| to |handle| in the main world. +- template +- inline static bool ClearMainWorldWrapperIfEqualTo(ScriptWrappable* object, +- const HandleType& handle); +- +- private: +- static bool ClearNonMainWorldWrapperIfEqualTo( ++ // Methods iterate all worlds and invokes the clearing methods on ++ // DOMDataStore. The WorldMap is only known to the DOMWrapperWorld and as such ++ // the iteration cannot be folded into DOMDataStore. ++ static bool ClearWrapperInAnyNonInlineStorageWorldIfEqualTo( + ScriptWrappable* object, + const v8::Local& handle); +- static bool ClearNonMainWorldWrapperIfEqualTo( ++ static bool ClearWrapperInAnyNonInlineStorageWorldIfEqualTo( + ScriptWrappable* object, + const v8::TracedReference& handle); + ++private: + DOMWrapperWorld(v8::Isolate*, WorldType, int32_t world_id); + + static unsigned number_of_non_main_worlds_in_main_thread_; +@@ -204,24 +197,6 @@ class PLATFORM_EXPORT DOMWrapperWorld : public RefCounted { + Persistent v8_object_data_store_; + }; + +-// static +-template +-bool DOMWrapperWorld::ClearMainWorldWrapperIfEqualTo(ScriptWrappable* object, +- const HandleType& handle) { +- return object->ClearMainWorldWrapperIfEqualTo(handle); +-} +- +-// static +-template +-bool DOMWrapperWorld::ClearWrapperIfEqualTo(ScriptWrappable* object, +- const HandleType& handle) { +- if (ClearMainWorldWrapperIfEqualTo(object, handle)) { +- return true; +- } +- // Slow path: |object| may point to |handle| in any non-main DOM world. +- return DOMWrapperWorld::ClearNonMainWorldWrapperIfEqualTo(object, handle); +-} +- + } // namespace blink + + #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_WRAPPER_WORLD_H_ +diff --git a/third_party/blink/renderer/platform/bindings/frozen_array_base.cc b/third_party/blink/renderer/platform/bindings/frozen_array_base.cc +index 4af0f5ef920e981e6cf0b7ff1e53d69d1bd41a29..5d01d17390f4de24190956f56e2784929266c21d 100644 +--- a/third_party/blink/renderer/platform/bindings/frozen_array_base.cc ++++ b/third_party/blink/renderer/platform/bindings/frozen_array_base.cc +@@ -39,9 +39,9 @@ v8::Local FrozenArrayBase::ToV8(ScriptState* script_state) const { + } + + v8::Local FrozenArrayBase::ToV8(ScriptState* script_state) { +- v8::Local wrapper = script_state->World().DomDataStore().Get( +- this, script_state->GetIsolate()); +- if (LIKELY(!wrapper.IsEmpty())) { ++ v8::Local wrapper; ++ if (LIKELY(DOMDataStore::GetWrapper(script_state->GetIsolate(), this) ++ .ToLocal(&wrapper))) { + return wrapper; + } + +@@ -49,7 +49,7 @@ v8::Local FrozenArrayBase::ToV8(ScriptState* script_state) { + } + + v8::Local FrozenArrayBase::Wrap(ScriptState* script_state) { +- DCHECK(!script_state->World().DomDataStore().ContainsWrapper(this)); ++ DCHECK(!DOMDataStore::ContainsWrapper(script_state->GetIsolate(), this)); + + v8::Local wrapper = MakeV8ArrayToBeFrozen(script_state); + +diff --git a/third_party/blink/renderer/platform/bindings/script_wrappable.cc b/third_party/blink/renderer/platform/bindings/script_wrappable.cc +index 92fa7df37915a3f456562f9cc0ef20436bfcad5f..9733be3a28b1eb315e7774dcb5a197143457cf8c 100644 +--- a/third_party/blink/renderer/platform/bindings/script_wrappable.cc ++++ b/third_party/blink/renderer/platform/bindings/script_wrappable.cc +@@ -20,9 +20,9 @@ struct SameSizeAsScriptWrappable { + ASSERT_SIZE(ScriptWrappable, SameSizeAsScriptWrappable); + + v8::Local ScriptWrappable::ToV8(ScriptState* script_state) { +- v8::Local wrapper = +- DOMDataStore::GetWrapper(this, script_state->GetIsolate()); +- if (LIKELY(!wrapper.IsEmpty())) { ++ v8::Local wrapper; ++ if (LIKELY(DOMDataStore::GetWrapper(script_state->GetIsolate(), this) ++ .ToLocal(&wrapper))) { + return wrapper; + } + return Wrap(script_state); +@@ -31,8 +31,8 @@ v8::Local ScriptWrappable::ToV8(ScriptState* script_state) { + v8::Local ScriptWrappable::ToV8( + v8::Isolate* isolate, + v8::Local creation_context_object) { +- v8::Local wrapper = DOMDataStore::GetWrapper(this, isolate); +- if (LIKELY(!wrapper.IsEmpty())) { ++ v8::Local wrapper; ++ if (LIKELY(DOMDataStore::GetWrapper(isolate, this).ToLocal(&wrapper))) { + return wrapper; + } + CHECK(!creation_context_object.IsEmpty()); +@@ -44,7 +44,7 @@ v8::Local ScriptWrappable::ToV8( + v8::Local ScriptWrappable::Wrap(ScriptState* script_state) { + const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo(); + +- DCHECK(!DOMDataStore::ContainsWrapper(this, script_state->GetIsolate())); ++ DCHECK(!DOMDataStore::ContainsWrapper(script_state->GetIsolate(), this)); + + v8::Local wrapper = + V8DOMWrapper::CreateWrapper(script_state, wrapper_type_info); +@@ -61,7 +61,7 @@ v8::Local ScriptWrappable::AssociateWithWrapper( + } + + void ScriptWrappable::Trace(Visitor* visitor) const { +- visitor->Trace(main_world_wrapper_); ++ visitor->Trace(wrapper_); + } + + const char* ScriptWrappable::NameInHeapSnapshot() const { +diff --git a/third_party/blink/renderer/platform/bindings/script_wrappable.h b/third_party/blink/renderer/platform/bindings/script_wrappable.h +index ba1b49d06bd26f4f04767a953ffa2d918a5d93e9..858ae35448ba23ddc200cc8a44d3b77f47e69e35 100644 +--- a/third_party/blink/renderer/platform/bindings/script_wrappable.h ++++ b/third_party/blink/renderer/platform/bindings/script_wrappable.h +@@ -42,6 +42,7 @@ + + namespace blink { + ++class DOMDataStore; + class ScriptState; + + // ScriptWrappable provides a way to map from/to C++ DOM implementation to/from +@@ -174,78 +175,22 @@ class PLATFORM_EXPORT ScriptWrappable + const WrapperTypeInfo*, + v8::Local wrapper); + +- // Associates this instance with the given |wrapper| if this instance is not +- // yet associated with any wrapper. Returns true if the given wrapper is +- // associated with this instance, or false if this instance is already +- // associated with a wrapper. In the latter case, |wrapper| will be updated +- // to the existing wrapper. +- [[nodiscard]] bool SetWrapper(v8::Isolate* isolate, +- const WrapperTypeInfo* wrapper_type_info, +- v8::Local& wrapper) { +- DCHECK(!wrapper.IsEmpty()); +- if (UNLIKELY(ContainsWrapper())) { +- wrapper = MainWorldWrapper(isolate); +- return false; +- } +- if (wrapper_type_info->SupportsDroppingWrapper()) { +- main_world_wrapper_.Reset( +- isolate, wrapper, TraceWrapperV8Reference::IsDroppable{}); +- } else { +- main_world_wrapper_.Reset(isolate, wrapper); +- } +- DCHECK(ContainsWrapper()); +- return true; +- } +- +- bool IsEqualTo(const v8::Local& other) const { +- return main_world_wrapper_ == other; +- } +- +- bool SetReturnValue(v8::ReturnValue return_value) { +- const bool contains_wrapper = ContainsWrapper(); +- if (contains_wrapper) { +- return_value.SetNonEmpty(main_world_wrapper_); +- } +- return contains_wrapper; +- } +- +- + protected: + ScriptWrappable() = default; + + private: +- bool ContainsWrapper() const { return !main_world_wrapper_.IsEmpty(); } +- +- v8::Local MainWorldWrapper(v8::Isolate* isolate) const { +- return main_world_wrapper_.Get(isolate); +- } +- +- // Clear the main world wrapper if it is set to |handle|. +- template +- inline bool ClearMainWorldWrapperIfEqualTo(const HandleType& handle); +- + static_assert( + std::is_trivially_destructible< + TraceWrapperV8Reference>::value, + "TraceWrapperV8Reference should be trivially destructible."); + +- TraceWrapperV8Reference main_world_wrapper_; +- +- // These classes are exceptionally allowed to directly interact with the main +- // world wrapper. ++ // Inline storage for the a single wrapper reference. Only ++ // `DOMDataStore::UncheckedInlineStorageForWrappable()` should access this ++ // field. ++ TraceWrapperV8Reference wrapper_; + friend class DOMDataStore; +- friend class DOMWrapperWorld; + }; + +-template +-bool ScriptWrappable::ClearMainWorldWrapperIfEqualTo(const HandleType& handle) { +- if (main_world_wrapper_ == handle) { +- main_world_wrapper_.Reset(); +- return true; +- } +- return false; +-} +- + // Defines |GetWrapperTypeInfo| virtual method which returns the WrapperTypeInfo + // of the instance. Also declares a static member of type WrapperTypeInfo, of + // which the definition is given by the IDL code generator. +diff --git a/third_party/blink/renderer/platform/bindings/v8_set_return_value.h b/third_party/blink/renderer/platform/bindings/v8_set_return_value.h +index 6cf3278d7a186ad97b49841fb9bddd584582b9f7..2987c64bdbed2a7b797cd04a9b35a95c3f3f7aa9 100644 +--- a/third_party/blink/renderer/platform/bindings/v8_set_return_value.h ++++ b/third_party/blink/renderer/platform/bindings/v8_set_return_value.h +@@ -50,16 +50,16 @@ struct V8ReturnValue { + enum InterfaceObject { kInterfaceObject }; + enum NamespaceObject { kNamespaceObject }; + +- // Selects the appropriate creation context. +- static v8::Local CreationContext( ++ // Selects the appropriate receiver from which e.g. the creation context can ++ // be retrieved. ++ static v8::Local GetReceiver( + const v8::FunctionCallbackInfo& info) { + return info.This(); + } +- static v8::Local CreationContext( ++ static v8::Local GetReceiver( + const v8::PropertyCallbackInfo& info) { + return info.Holder(); + } +- + // Helper function for ScriptWrappable + template + static void SetWrapper(const CallbackInfo& info, +@@ -290,12 +290,13 @@ void V8SetReturnValue(const CallbackInfo& info, + if (UNLIKELY(!value)) + return info.GetReturnValue().SetNull(); + ScriptWrappable* wrappable = const_cast(value); +- if (DOMDataStore::SetReturnValueForMainWorld(info.GetReturnValue(), +- wrappable)) ++ if (DOMDataStore::SetReturnValueFromInlineStorage(info.GetReturnValue(), ++ wrappable)) { + return; ++ } + V8ReturnValue::SetWrapper( + info, wrappable, +- V8ReturnValue::CreationContext(info)->GetCreationContextChecked()); ++ V8ReturnValue::GetReceiver(info)->GetCreationContextChecked()); + } + + template +@@ -304,12 +305,13 @@ void V8SetReturnValue(const CallbackInfo& info, + V8ReturnValue::MainWorld) { + DCHECK(DOMWrapperWorld::Current(info.GetIsolate()).IsMainWorld()); + ScriptWrappable* wrappable = const_cast(&value); +- if (DOMDataStore::SetReturnValueForMainWorld(info.GetReturnValue(), +- wrappable)) ++ if (DOMDataStore::SetReturnValueFromInlineStorage(info.GetReturnValue(), ++ wrappable)) { + return; ++ } + V8ReturnValue::SetWrapper( + info, wrappable, +- V8ReturnValue::CreationContext(info)->GetCreationContextChecked()); ++ V8ReturnValue::GetReceiver(info)->GetCreationContextChecked()); + } + + template +@@ -320,13 +322,13 @@ void V8SetReturnValue(const CallbackInfo& info, + return info.GetReturnValue().SetNull(); + ScriptWrappable* wrappable = const_cast(value); + if (DOMDataStore::SetReturnValueFast(info.GetReturnValue(), wrappable, +- V8ReturnValue::CreationContext(info), ++ V8ReturnValue::GetReceiver(info), + receiver)) { + return; + } + V8ReturnValue::SetWrapper( + info, wrappable, +- V8ReturnValue::CreationContext(info)->GetCreationContextChecked()); ++ V8ReturnValue::GetReceiver(info)->GetCreationContextChecked()); + } + + template +@@ -335,13 +337,13 @@ void V8SetReturnValue(const CallbackInfo& info, + const ScriptWrappable* receiver) { + ScriptWrappable* wrappable = const_cast(&value); + if (DOMDataStore::SetReturnValueFast(info.GetReturnValue(), wrappable, +- V8ReturnValue::CreationContext(info), ++ V8ReturnValue::GetReceiver(info), + receiver)) { + return; + } + V8ReturnValue::SetWrapper( + info, wrappable, +- V8ReturnValue::CreationContext(info)->GetCreationContextChecked()); ++ V8ReturnValue::GetReceiver(info)->GetCreationContextChecked()); + } + + template +@@ -353,7 +355,7 @@ void V8SetReturnValue(const CallbackInfo& info, + return info.GetReturnValue().SetNull(); + ScriptWrappable* wrappable = const_cast(value); + if (DOMDataStore::SetReturnValueFast(info.GetReturnValue(), wrappable, +- V8ReturnValue::CreationContext(info), ++ V8ReturnValue::GetReceiver(info), + receiver)) { + return; + } +@@ -369,7 +371,7 @@ void V8SetReturnValue(const CallbackInfo& info, + // with the current context will still have the correct v8::Isolate and + // DOMWrapperWorld. + v8::Local context; +- if (!V8ReturnValue::CreationContext(info)->GetCreationContext().ToLocal( ++ if (!V8ReturnValue::GetReceiver(info)->GetCreationContext().ToLocal( + &context)) { + context = info.GetIsolate()->GetCurrentContext(); + } +@@ -383,7 +385,7 @@ void V8SetReturnValue(const CallbackInfo& info, + V8ReturnValue::MaybeCrossOrigin) { + ScriptWrappable* wrappable = const_cast(&value); + if (DOMDataStore::SetReturnValueFast(info.GetReturnValue(), wrappable, +- V8ReturnValue::CreationContext(info), ++ V8ReturnValue::GetReceiver(info), + receiver)) { + return; + } +@@ -399,7 +401,7 @@ void V8SetReturnValue(const CallbackInfo& info, + // with the current context will still have the correct v8::Isolate and + // DOMWrapperWorld. + v8::Local context; +- if (!V8ReturnValue::CreationContext(info)->GetCreationContext().ToLocal( ++ if (!V8ReturnValue::GetReceiver(info)->GetCreationContext().ToLocal( + &context)) { + context = info.GetIsolate()->GetCurrentContext(); + } +diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc +index 55434e8c3a6d57ff8088821e7c4777334d9119b8..f68b52c7fd23f2b296abf8493fa52b216d68fe98 100644 +--- a/third_party/blink/renderer/platform/heap/thread_state.cc ++++ b/third_party/blink/renderer/platform/heap/thread_state.cc +@@ -10,6 +10,7 @@ + #include "base/functional/callback.h" + #include "base/notreached.h" + #include "gin/public/v8_platform.h" ++#include "third_party/blink/renderer/platform/bindings/dom_data_store.h" + #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" + #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" + #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h" +@@ -46,7 +47,7 @@ class BlinkRootsHandler final : public v8::EmbedderRootsHandler { + // generation garbage collections. + void ResetRoot(const v8::TracedReference& handle) final { + const v8::TracedReference& traced = handle.As(); +- bool success = DOMWrapperWorld::ClearWrapperIfEqualTo( ++ bool success = DOMDataStore::ClearWrapperInAnyWorldIfEqualTo( + ToScriptWrappable(traced), traced); + // Since V8 found a handle, Blink needs to find it as well when trying to + // remove it. +@@ -55,7 +56,7 @@ class BlinkRootsHandler final : public v8::EmbedderRootsHandler { + + bool TryResetRoot(const v8::TracedReference& handle) final { + const v8::TracedReference& traced = handle.As(); +- return DOMWrapperWorld::ClearMainWorldWrapperIfEqualTo( ++ return DOMDataStore::ClearInlineStorageWrapperIfEqualTo( + ToScriptWrappable(traced), traced); + } + }; diff --git a/patches/chromium/merge_fix_domarraybuffer_isdetached_and_comment_out_a_check.patch b/patches/chromium/merge_fix_domarraybuffer_isdetached_and_comment_out_a_check.patch new file mode 100644 index 0000000000000..293347a763522 --- /dev/null +++ b/patches/chromium/merge_fix_domarraybuffer_isdetached_and_comment_out_a_check.patch @@ -0,0 +1,206 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marja=20H=C3=B6ltt=C3=A4?= +Date: Tue, 9 Apr 2024 08:32:27 +0000 +Subject: Merge "Fix DOMArrayBuffer::IsDetached()" and "Comment out a CHECK + that a DOMAB has maximally one non-detached JSAB" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +1) + +A DOMArrayBuffer was maintaining its own "is_detached_" state, and +would consider itself non-detached even if the corresponding +JSArrayBuffer (or, all of them, in case there are several) was +detached. + +Piping in the v8::Isolate would be a too big change for this fix, so this is using v8::Isolate::GetCurrent() for now. + +2) + +Comment out a CHECK that a DOMAB has maximally one non-detached JSAB + +Based on crash reports, this assumption is not true and has to be +investigated. + +Removing this newly introduced CHECK to be able to merge fixes in this +area - we still violate this invariant but the fixes are a step into +the right direction. + +Fix in question: +https://chromium-review.googlesource.com/5387887 +which also introduced this CHECK. + +(cherry picked from commit 04e7550d7aa3bf4ac4e49d7074972d357de139e6) + +Change-Id: I6a46721e24c6f04fe8252bc4a5e94caeec3a8b51 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5435035 +Commit-Queue: Marja Hölttä +Reviewed-by: Michael Lippautz +Cr-Commit-Position: refs/branch-heads/6367@{#667} +Cr-Branched-From: d158c6dc6e3604e6f899041972edf26087a49740-refs/heads/main@{#1274542} + +diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc +index 44e2849c8e4dfc38834c88937dc561858c13e726..f26b6de5baded254d1ae5c3db458d0405e9751cb 100644 +--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc ++++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc +@@ -46,8 +46,19 @@ const WrapperTypeInfo& DOMArrayBuffer::wrapper_type_info_ = + + static void AccumulateArrayBuffersForAllWorlds( + v8::Isolate* isolate, +- DOMArrayBuffer* object, ++ const DOMArrayBuffer* object, + v8::LocalVector& buffers) { ++ if (!object->has_non_main_world_wrappers() && IsMainThread()) { ++ const DOMWrapperWorld& world = DOMWrapperWorld::MainWorld(isolate); ++ v8::Local wrapper; ++ if (world.DomDataStore() ++ .Get(isolate, object) ++ .ToLocal(&wrapper)) { ++ buffers.push_back(v8::Local::Cast(wrapper)); ++ } ++ return; ++ } ++ + Vector> worlds; + DOMWrapperWorld::AllWorldsInIsolate(isolate, worlds); + for (const auto& world : worlds) { +@@ -259,6 +270,52 @@ v8::Local DOMArrayBuffer::Wrap(ScriptState* script_state) { + wrapper); + } + ++bool DOMArrayBuffer::IsDetached() const { ++ if (contents_.BackingStore() == nullptr) { ++ return is_detached_; ++ } ++ if (is_detached_) { ++ return true; ++ } ++ ++ v8::Isolate* isolate = v8::Isolate::GetCurrent(); ++ v8::HandleScope handle_scope(isolate); ++ v8::LocalVector buffer_handles(isolate); ++ AccumulateArrayBuffersForAllWorlds(isolate, this, buffer_handles); ++ ++ // There may be several v8::ArrayBuffers corresponding to the DOMArrayBuffer, ++ // but at most one of them may be non-detached. ++ int nondetached_count = 0; ++ int detached_count = 0; ++ ++ for (const auto& buffer_handle : buffer_handles) { ++ if (buffer_handle->WasDetached()) { ++ ++detached_count; ++ } else { ++ ++nondetached_count; ++ } ++ } ++ ++ // This CHECK fires even though it should not. TODO(330759272): Investigate ++ // under which conditions we end up with multiple non-detached JSABs for the ++ // same DOMAB and potentially restore this check. ++ ++ // CHECK_LE(nondetached_count, 1); ++ ++ return nondetached_count == 0 && detached_count > 0; ++} ++ ++v8::Local DOMArrayBuffer::AssociateWithWrapper( ++ v8::Isolate* isolate, ++ const WrapperTypeInfo* wrapper_type_info, ++ v8::Local wrapper) { ++ if (!DOMWrapperWorld::Current(isolate).IsMainWorld()) { ++ has_non_main_world_wrappers_ = true; ++ } ++ return ScriptWrappable::AssociateWithWrapper(isolate, wrapper_type_info, ++ wrapper); ++} ++ + DOMArrayBuffer* DOMArrayBuffer::Slice(size_t begin, size_t end) const { + begin = std::min(begin, ByteLength()); + end = std::min(end, ByteLength()); +diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h +index a7d4bac99e608bcfaa02dc9bfcef20b5a29d0ddc..9d4827f548ca7db3be85011c68d8346fc8dfa909 100644 +--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h ++++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h +@@ -91,6 +91,17 @@ class CORE_EXPORT DOMArrayBuffer : public DOMArrayBufferBase { + + void Trace(Visitor*) const override; + ++ bool IsDetached() const override; ++ ++ v8::Local AssociateWithWrapper( ++ v8::Isolate* isolate, ++ const WrapperTypeInfo* wrapper_type_info, ++ v8::Local wrapper) override; ++ ++ bool has_non_main_world_wrappers() const { ++ return has_non_main_world_wrappers_; ++ } ++ + private: + v8::Maybe TransferDetachable(v8::Isolate*, + v8::Local detach_key, +@@ -101,6 +112,8 @@ class CORE_EXPORT DOMArrayBuffer : public DOMArrayBufferBase { + // support only v8::String as the detach key type. It's also convenient that + // we can write `array_buffer->SetDetachKey(isolate, "my key")`. + TraceWrapperV8Reference detach_key_; ++ ++ bool has_non_main_world_wrappers_ = false; + }; + + } // namespace blink +diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h +index 90c4c70755babdc8c88a7c6bf02803c5858c2194..43618b8ef4b831678b45c72ca47f5729c4f2aaef 100644 +--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h ++++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h +@@ -27,7 +27,9 @@ class CORE_EXPORT DOMArrayBufferBase : public ScriptWrappable { + + size_t ByteLength() const { return contents_.DataLength(); } + +- bool IsDetached() const { return is_detached_; } ++ // TODO(331348222): It doesn't make sense to detach DomSharedArrayBuffers, ++ // remove that possibility. ++ virtual bool IsDetached() const { return is_detached_; } + + void Detach() { is_detached_ = true; } + +diff --git a/third_party/blink/renderer/modules/gamepad/BUILD.gn b/third_party/blink/renderer/modules/gamepad/BUILD.gn +index 572d8ce27fa808707ae17ed05f14e2ed103f131e..a871cbd002795bf49ad48f0002ac4996135f8b10 100644 +--- a/third_party/blink/renderer/modules/gamepad/BUILD.gn ++++ b/third_party/blink/renderer/modules/gamepad/BUILD.gn +@@ -55,6 +55,7 @@ source_set("unit_tests") { + "//testing/gtest", + "//third_party/blink/renderer/modules", + "//third_party/blink/renderer/platform", ++ "//third_party/blink/renderer/platform:test_support", + "//third_party/blink/renderer/platform/wtf", + ] + } +diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc b/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc +index e0a7f48630ba423b19641232c026d72ba71dfc4b..b9f422e6fff36da62575d803f132573a24d03c05 100644 +--- a/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc ++++ b/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc +@@ -4,9 +4,11 @@ + + #include "third_party/blink/renderer/modules/gamepad/gamepad_comparisons.h" + ++#include "base/test/task_environment.h" + #include "device/gamepad/public/cpp/gamepad.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "third_party/blink/renderer/modules/gamepad/gamepad.h" ++#include "third_party/blink/renderer/platform/testing/main_thread_isolate.h" + + namespace blink { + +@@ -241,6 +243,11 @@ class GamepadComparisonsTest : public testing::Test { + list[0] = gamepad; + return list; + } ++ ++ private: ++ // Needed so we can do v8::Isolate::GetCurrent(). ++ base::test::TaskEnvironment task_environment_; ++ blink::test::MainThreadIsolate isolate_; + }; + + TEST_F(GamepadComparisonsTest, EmptyListCausesNoActivation) {