From e6b08a4e30a5aef34abe7f87b8ce4b01910ae04f Mon Sep 17 00:00:00 2001 From: ldm0 Date: Wed, 16 Sep 2026 06:04:12 +0800 Subject: [PATCH] fix(dom): create wrappers in their receiver realm Cached child-document wrappers reapplied their current owner's native prototype, overwriting JavaScript changes and changing prototypes during adoption. Return cached wrappers directly and remove the custom-element exceptions and the owner-document prototype correction from wrapper creation. Create child Document wrappers in their owning context, deferring creation for unmaterialized initial empty frames. Choose the receiver's creation context at DOM return boundaries, including splitText, shadow and template access, forms, Range and XPath results, and named Window access. Collection indexing and named access use the collection's context. Keep traversal results and NodeFilter arguments in the node document's realm as required by NodeWrapInOwnContext. Wrapper creation uses the selected context's intrinsic interface prototype without reading author-visible constructors or repairing prototypes later. Cover changed and null prototypes, adoption, child Document and Range realms, borrowed DOM methods, property interceptors, collections, replaced public constructors, and isolated-world wrapper identity. Exercise first access before other APIs can hide an incorrect creation realm by populating the wrapper cache. Validation: - cargo fmt --all - cargo clippy --workspace --all-targets --all-features -- -D warnings - cargo nextest run --no-fail-fast (17,776 passed, 13 skipped) --- .../src/context_bootstrap/media_cues.rs | 7 +- .../range_algorithms/contents.rs | 13 +- .../range_algorithms/tree_ops.rs | 3 +- .../range_surface/content.rs | 4 +- .../window_accessors/interceptors.rs | 2 + .../src/native_bridge/bindings.rs | 74 +-- moli-renderer-v8/src/native_bridge/bridge.rs | 2 +- .../src/native_bridge/bridge/callbacks.rs | 28 +- .../src/native_bridge/bridge/wrappers.rs | 13 - .../src/native_bridge/collections.rs | 2 +- .../native_bridge/collections/iteration.rs | 4 + .../collections/live_handlers.rs | 31 +- .../child_frame_runtime/document.rs | 32 +- .../src/native_bridge/context_host/core.rs | 27 - .../src/native_bridge/document.rs | 12 +- .../native_bridge/document/construction.rs | 2 +- .../document/construction/bridge_callbacks.rs | 36 +- .../document/construction/character_nodes.rs | 9 +- .../document/construction/elements.rs | 27 +- .../document/construction/helpers.rs | 3 + .../document/construction/import_adopt.rs | 33 +- .../accessors/iframe_content.rs | 4 +- .../detached_surface/creation/clone_adopt.rs | 13 +- .../src/native_bridge/document/hit_test.rs | 45 +- .../document/live_collections/all/getter.rs | 2 + .../src/native_bridge/document/queries.rs | 2 +- .../native_bridge/document/queries/live.rs | 2 +- .../document/queries/xpath/callbacks.rs | 59 ++- .../src/native_bridge/document/state.rs | 2 +- .../src/native_bridge/document/state/focus.rs | 2 +- .../element/forms/form_element.rs | 24 +- .../element/forms/input/association.rs | 8 +- .../src/native_bridge/element/forms/labels.rs | 5 +- .../src/native_bridge/element/forms/owner.rs | 3 +- .../element/forms/select/option.rs | 3 +- .../element/forms/select/select_element.rs | 19 +- .../element/forms/simple_controls.rs | 11 +- .../native_bridge/element/geometry/metrics.rs | 9 +- .../element/html_elements/table.rs | 75 ++- .../src/native_bridge/element/query.rs | 44 +- .../element/shadow_dom/attach.rs | 16 +- .../element/shadow_dom/internals.rs | 27 +- .../element/shadow_dom/root_accessors.rs | 23 +- .../native_bridge/element/shadow_dom/slots.rs | 34 +- .../element/shadow_dom/template.rs | 18 +- .../element/tree_mutation/callbacks.rs | 5 +- moli-renderer-v8/src/native_bridge/node.rs | 35 +- .../native_bridge/node/character_data/text.rs | 15 +- .../src/native_bridge/node/tree/methods.rs | 14 +- .../node/tree/mutation_methods.rs | 3 +- .../src/native_bridge/traversal.rs | 40 ++ .../src/native_bridge/traversal/filters.rs | 7 +- .../native_bridge/traversal/node_iterator.rs | 25 +- .../native_bridge/traversal/tree_walker.rs | 65 ++- .../tests/dom_elements/dom_surface.rs | 8 +- .../src/script_vm/tests/dom_elements/mod.rs | 1 + .../tests/dom_elements/wrapper_identity.rs | 488 ++++++++++++++++++ 57 files changed, 1138 insertions(+), 382 deletions(-) create mode 100644 moli-renderer-v8/src/script_vm/tests/dom_elements/wrapper_identity.rs diff --git a/moli-renderer-v8/src/context_bootstrap/media_cues.rs b/moli-renderer-v8/src/context_bootstrap/media_cues.rs index 66a4f80434..1ba9b449b8 100644 --- a/moli-renderer-v8/src/context_bootstrap/media_cues.rs +++ b/moli-renderer-v8/src/context_bootstrap/media_cues.rs @@ -457,9 +457,10 @@ fn text_track_cue_get_cue_as_html_callback<'s>( let runtime = unsafe { &mut *runtime_ptr }; let fragment = runtime.create_document_fragment(); append_vtt_cue_fragment(scope, runtime_ptr, runtime, fragment, &text); - if let Some(fragment) = runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, fragment) + if let Some(fragment) = + runtime + .native_bridge_mut() + .wrap_handle_for_receiver(scope, runtime_ptr, receiver, fragment) { rv.set(fragment.into()); } diff --git a/moli-renderer-v8/src/context_bootstrap/range_algorithms/contents.rs b/moli-renderer-v8/src/context_bootstrap/range_algorithms/contents.rs index fa5aa6adb9..5506f53ecb 100644 --- a/moli-renderer-v8/src/context_bootstrap/range_algorithms/contents.rs +++ b/moli-renderer-v8/src/context_bootstrap/range_algorithms/contents.rs @@ -145,7 +145,7 @@ fn process_contents_from_snapshot<'s>( if snapshot.start_container == snapshot.end_container && snapshot.start_offset == snapshot.end_offset { - return finish_fragment(scope, fragment); + return finish_fragment(scope, range, fragment); } if snapshot.start_container == snapshot.end_container { @@ -166,7 +166,7 @@ fn process_contents_from_snapshot<'s>( Some(&snapshot), )?; } - return finish_fragment(scope, fragment); + return finish_fragment(scope, range, fragment); } let common_root = @@ -273,7 +273,7 @@ fn process_contents_from_snapshot<'s>( collapse_range_to_handle(scope, range, container, offset, Some(&snapshot))?; } - finish_fragment(scope, fragment) + finish_fragment(scope, range, fragment) } fn range_partially_contains_non_text_node( @@ -790,10 +790,15 @@ fn range_clone_character_data( fn finish_fragment<'s>( scope: &mut v8::PinScope<'s, '_>, + range: v8::Local<'s, v8::Object>, fragment: Option, ) -> Option>> { match fragment { - Some(fragment) => Some(Some(node_wrapper_for_handle(scope, fragment)?)), + Some(fragment) => { + let context = range.get_creation_context(scope)?; + let scope = &mut v8::ContextScope::new(scope, context); + Some(Some(node_wrapper_for_handle(scope, fragment)?)) + } None => Some(None), } } diff --git a/moli-renderer-v8/src/context_bootstrap/range_algorithms/tree_ops.rs b/moli-renderer-v8/src/context_bootstrap/range_algorithms/tree_ops.rs index bb22233eef..f33cc0cbf4 100644 --- a/moli-renderer-v8/src/context_bootstrap/range_algorithms/tree_ops.rs +++ b/moli-renderer-v8/src/context_bootstrap/range_algorithms/tree_ops.rs @@ -5,6 +5,7 @@ use crate::util::{ pub(in crate::context_bootstrap) fn create_contextual_fragment_internal<'s>( scope: &mut v8::PinScope<'s, '_>, + receiver: v8::Local<'s, v8::Object>, context_node: v8::Local<'s, v8::Object>, html: &str, ) -> Option> { @@ -24,7 +25,7 @@ pub(in crate::context_bootstrap) fn create_contextual_fragment_internal<'s>( )?; runtime .native_bridge_mut() - .wrap_handle(scope, host_ptr, fragment) + .wrap_handle_for_receiver(scope, host_ptr, receiver, fragment) } fn create_detached_contextual_fragment<'s>( diff --git a/moli-renderer-v8/src/context_bootstrap/range_surface/content.rs b/moli-renderer-v8/src/context_bootstrap/range_surface/content.rs index a21b9d6881..15704bf5e4 100644 --- a/moli-renderer-v8/src/context_bootstrap/range_surface/content.rs +++ b/moli-renderer-v8/src/context_bootstrap/range_surface/content.rs @@ -118,7 +118,9 @@ pub(super) fn range_create_contextual_fragment_callback<'s>( } else { start_container }; - let Some(fragment) = create_contextual_fragment_internal(scope, context_node, &markup) else { + let Some(fragment) = + create_contextual_fragment_internal(scope, args.this(), context_node, &markup) + else { rv.set_undefined(); return; }; diff --git a/moli-renderer-v8/src/context_bootstrap/window_accessors/interceptors.rs b/moli-renderer-v8/src/context_bootstrap/window_accessors/interceptors.rs index 4a416ab65d..e4311f9fed 100644 --- a/moli-renderer-v8/src/context_bootstrap/window_accessors/interceptors.rs +++ b/moli-renderer-v8/src/context_bootstrap/window_accessors/interceptors.rs @@ -216,6 +216,8 @@ fn window_named_access_value<'s>( stage = "window_named_property_lookup", ); } + let context = holder.get_creation_context(scope)?; + let scope = &mut v8::ContextScope::new(scope, context); match handles.as_slice() { [] => None, [handle] => unsafe { &mut *host_ptr } diff --git a/moli-renderer-v8/src/native_bridge/bindings.rs b/moli-renderer-v8/src/native_bridge/bindings.rs index 8083c15f51..63a48a80ec 100644 --- a/moli-renderer-v8/src/native_bridge/bindings.rs +++ b/moli-renderer-v8/src/native_bridge/bindings.rs @@ -24,31 +24,6 @@ struct NativeBridgeGlobalDeclaration<'scope> { bridge: v8::Local<'scope, v8::Object>, } -#[cfg(test)] -thread_local! { - static WRAPPER_OWNER_REALM_CUSTOM_ELEMENT_CHECKS: std::cell::Cell = - const { std::cell::Cell::new(0) }; -} - -#[cfg(test)] -pub(crate) fn reset_wrapper_owner_realm_custom_element_checks_for_test() { - WRAPPER_OWNER_REALM_CUSTOM_ELEMENT_CHECKS.set(0); -} - -#[cfg(test)] -pub(crate) fn wrapper_owner_realm_custom_element_checks_for_test() -> u64 { - WRAPPER_OWNER_REALM_CUSTOM_ELEMENT_CHECKS.get() -} - -#[cfg(test)] -fn record_wrapper_owner_realm_custom_element_check_for_test() { - WRAPPER_OWNER_REALM_CUSTOM_ELEMENT_CHECKS.set( - WRAPPER_OWNER_REALM_CUSTOM_ELEMENT_CHECKS - .get() - .saturating_add(1), - ); -} - pub(crate) struct NativeBridgeBindings { isolate_ptr: v8::UnsafeRawIsolatePtr, window_global_template: v8::Global, @@ -283,7 +258,9 @@ impl NativeBridgeBindings { web_api_interfaces::initialize(scope, wrapper, prototype_name) .expect("native wrapper identity should initialize"); set_named_constructor_prototype(scope, wrapper, prototype_name); - self.sync_wrapper_owner_realm_prototype(scope, host_ptr, &handle, wrapper); + if matches!(wrapper_kind, WrapperKind::Window) { + window::sync_window_wrapper_function_identity(scope, wrapper); + } if matches!(wrapper_kind, WrapperKind::Node) { let descriptor = node_bridge_descriptor(prototype_name).unwrap_or_else(|| { panic!("missing native bridge descriptor for `{prototype_name}`") @@ -297,51 +274,6 @@ impl NativeBridgeBindings { wrapper } - pub(super) fn sync_wrapper_owner_realm_prototype( - &mut self, - scope: &mut v8::PinScope<'_, '_>, - host_ptr: *mut JsContextHost, - handle: &BridgeHandle, - wrapper: v8::Local<'_, v8::Object>, - ) { - if matches!(handle, BridgeHandle::Window) { - window::sync_window_wrapper_function_identity(scope, wrapper); - return; - } - let BridgeHandle::Node(node_handle) = handle else { - return; - }; - let child_handle = { - let host = unsafe { &*host_ptr }; - let Some(document_handle) = host.dom_host().owner_document_handle(*node_handle) else { - return; - }; - let Some(child_handle) = - host.child_browsing_context_host_for_document_handle(document_handle) - else { - return; - }; - child_handle - }; - #[cfg(test)] - record_wrapper_owner_realm_custom_element_check_for_test(); - if unsafe { &*host_ptr }.custom_element_handle_is_upgraded(*node_handle) { - return; - } - let prototype_name = prototype_name_for_handle(host_ptr, handle); - if let Some(prototype) = unsafe { &mut *host_ptr } - .child_browsing_context_constructor_prototype(scope, child_handle, prototype_name) - { - let updated = wrapper.set_prototype(scope, prototype).unwrap_or_else(|| { - panic!("failed to set child-realm `{prototype_name}` wrapper prototype") - }); - assert!( - updated, - "V8 rejected the child-realm `{prototype_name}` wrapper prototype" - ); - } - } - pub(super) fn instantiate_window_shell<'s, 'i>( &mut self, scope: &mut v8::PinScope<'s, 'i>, diff --git a/moli-renderer-v8/src/native_bridge/bridge.rs b/moli-renderer-v8/src/native_bridge/bridge.rs index 8967c55a98..06cee8c836 100644 --- a/moli-renderer-v8/src/native_bridge/bridge.rs +++ b/moli-renderer-v8/src/native_bridge/bridge.rs @@ -13,7 +13,7 @@ mod wrappers; pub(crate) use callbacks::{ callback_arg_dom_handle, callback_value_dom_handle, runtime_ptr_from_object, set_wrapped_handle_array, set_wrapped_handle_or_null, set_wrapped_handle_or_null_for_receiver, - wrapped_handle_value, + wrapped_handle_value, wrapped_handle_value_for_receiver, }; pub(crate) use dom_exception::throw_dom_exception; pub(crate) use lifecycle::install_detached_bridge_methods; diff --git a/moli-renderer-v8/src/native_bridge/bridge/callbacks.rs b/moli-renderer-v8/src/native_bridge/bridge/callbacks.rs index 92cafb5570..5c0fc68c1f 100644 --- a/moli-renderer-v8/src/native_bridge/bridge/callbacks.rs +++ b/moli-renderer-v8/src/native_bridge/bridge/callbacks.rs @@ -27,27 +27,37 @@ pub(crate) fn set_wrapped_handle_or_null( } } -pub(crate) fn set_wrapped_handle_or_null_for_receiver<'s>( - scope: &mut v8::PinScope<'s, '_>, +pub(crate) fn set_wrapped_handle_or_null_for_receiver( + scope: &mut v8::PinScope<'_, '_>, rv: &mut v8::ReturnValue<'_, v8::Value>, runtime_ptr: *mut JsContextHost, - receiver: v8::Local<'s, v8::Object>, + receiver: v8::Local<'_, v8::Object>, handle: Option, ) { let Some(handle) = handle else { rv.set_null(); return; }; - let runtime = unsafe { &mut *runtime_ptr }; - match runtime - .native_bridge_mut() - .wrap_handle_for_receiver(scope, runtime_ptr, receiver, handle) - { - Some(node) => rv.set(node.into()), + match wrapped_handle_value_for_receiver(scope, runtime_ptr, receiver, handle) { + Some(node) => rv.set(node), None => rv.set_null(), } } +pub(crate) fn wrapped_handle_value_for_receiver<'s>( + scope: &mut v8::PinScope<'s, '_>, + runtime_ptr: *mut JsContextHost, + receiver: v8::Local<'_, v8::Object>, + handle: DomHandle, +) -> Option> { + let context = receiver.get_creation_context(scope)?; + if context == scope.get_current_context() { + return wrapped_handle_value(scope, runtime_ptr, handle); + } + let scope = &mut v8::ContextScope::new(scope, context); + wrapped_handle_value(scope, runtime_ptr, handle) +} + pub(crate) fn wrapped_handle_value<'s>( scope: &mut v8::PinScope<'s, '_>, runtime_ptr: *mut JsContextHost, diff --git a/moli-renderer-v8/src/native_bridge/bridge/wrappers.rs b/moli-renderer-v8/src/native_bridge/bridge/wrappers.rs index 88df063380..e58a220328 100644 --- a/moli-renderer-v8/src/native_bridge/bridge/wrappers.rs +++ b/moli-renderer-v8/src/native_bridge/bridge/wrappers.rs @@ -51,19 +51,6 @@ impl NativeDomBridge { ) -> Option> { let reflector_id = self.identity.reflector_id(&handle); if let Some(wrapper) = self.identity.cached_wrapper(scope, reflector_id) { - // Once custom element construction starts, preserve the prototype - // chosen by JavaScript, including when an upgrade fails. - let preserve_prototype = match &handle { - BridgeHandle::Window => true, - BridgeHandle::Node(handle) => { - unsafe { &*host_ptr }.custom_element_handle_preserves_wrapper_prototype(*handle) - } - _ => false, - }; - if !preserve_prototype { - self.bindings - .sync_wrapper_owner_realm_prototype(scope, host_ptr, &handle, wrapper); - } return Some(wrapper); } diff --git a/moli-renderer-v8/src/native_bridge/collections.rs b/moli-renderer-v8/src/native_bridge/collections.rs index 465c5aa51d..888ff1fee2 100644 --- a/moli-renderer-v8/src/native_bridge/collections.rs +++ b/moli-renderer-v8/src/native_bridge/collections.rs @@ -15,7 +15,7 @@ use super::element::{ use super::identity::{CollectionKind, LiveCollectionDescriptor, LiveCollectionQueryKind}; use super::{ JsContextHost, - bridge::wrapped_handle_value, + bridge::{wrapped_handle_value, wrapped_handle_value_for_receiver}, callback_arg_dom_handle, callback_arg_namespace, callback_arg_optional_string, callback_arg_string, callback_value_dom_handle, current_or_live_delegate_node_arg_handle, node::{ diff --git a/moli-renderer-v8/src/native_bridge/collections/iteration.rs b/moli-renderer-v8/src/native_bridge/collections/iteration.rs index 1cbc4d55de..39dbd83cc5 100644 --- a/moli-renderer-v8/src/native_bridge/collections/iteration.rs +++ b/moli-renderer-v8/src/native_bridge/collections/iteration.rs @@ -21,6 +21,8 @@ pub(in crate::native_bridge::collections) fn collection_value_at<'s>( object: v8::Local<'s, v8::Object>, index: usize, ) -> Option> { + let context = object.get_creation_context(scope)?; + let scope = &mut v8::ContextScope::new(scope, context); if let Ok((runtime_ptr, descriptor)) = live_collection_descriptor_from_object(scope, object) { let handle = descriptor .resolve(unsafe { &*runtime_ptr }) @@ -55,6 +57,8 @@ fn collection_named_value<'s>( object: v8::Local<'s, v8::Object>, key: &str, ) -> Option> { + let context = object.get_creation_context(scope)?; + let scope = &mut v8::ContextScope::new(scope, context); if let Ok((runtime_ptr, descriptor)) = live_collection_descriptor_from_object(scope, object) { let runtime = unsafe { &mut *runtime_ptr }; let matches = named_item_matches(runtime, &descriptor, key); diff --git a/moli-renderer-v8/src/native_bridge/collections/live_handlers.rs b/moli-renderer-v8/src/native_bridge/collections/live_handlers.rs index 5ab092ee9a..07ea506eba 100644 --- a/moli-renderer-v8/src/native_bridge/collections/live_handlers.rs +++ b/moli-renderer-v8/src/native_bridge/collections/live_handlers.rs @@ -16,7 +16,8 @@ pub(in crate::native_bridge::collections) fn live_collection_indexed_getter( let Some(handle) = handles.get(index as usize).copied() else { return v8::Intercepted::kNo; }; - let Some(node) = wrapped_handle_value(scope, runtime_ptr, handle) else { + let Some(node) = wrapped_handle_value_for_receiver(scope, runtime_ptr, args.holder(), handle) + else { return v8::Intercepted::kNo; }; rv.set(node); @@ -101,7 +102,8 @@ pub(in crate::native_bridge::collections) fn live_collection_indexed_descriptor( else { return v8::Intercepted::kNo; }; - let Some(value) = wrapped_handle_value(scope, runtime_ptr, handle) else { + let Some(value) = wrapped_handle_value_for_receiver(scope, runtime_ptr, args.holder(), handle) + else { return v8::Intercepted::kNo; }; let Ok(descriptor) = DataPropertyDescriptorDeclaration::new( @@ -209,7 +211,9 @@ pub(in crate::native_bridge::collections) fn live_collection_named_getter( if is_array_index_property_name(&key) || key == "length" { return v8::Intercepted::kNo; }; - let Some(value) = live_collection_named_value(scope, runtime_ptr, &descriptor, key) else { + let Some(value) = + live_collection_named_value(scope, args.holder(), runtime_ptr, &descriptor, key) + else { return v8::Intercepted::kNo; }; rv.set(value); @@ -301,7 +305,9 @@ pub(in crate::native_bridge::collections) fn live_collection_named_descriptor( if is_array_index_property_name(&key) || key == "length" { return v8::Intercepted::kNo; } - let Some(value) = live_collection_named_value(scope, runtime_ptr, &descriptor, key) else { + let Some(value) = + live_collection_named_value(scope, args.holder(), runtime_ptr, &descriptor, key) + else { return v8::Intercepted::kNo; }; let Ok(descriptor) = DataPropertyDescriptorDeclaration::new(value, false, false).bind(scope) @@ -358,10 +364,13 @@ fn live_collection_has_named_property( fn live_collection_named_value<'s>( scope: &mut v8::PinScope<'s, '_>, + receiver: v8::Local<'_, v8::Object>, runtime_ptr: *mut JsContextHost, descriptor: &LiveCollectionDescriptor, key: String, ) -> Option> { + let context = receiver.get_creation_context(scope)?; + let scope = &mut v8::ContextScope::new(scope, context); let runtime = unsafe { &mut *runtime_ptr }; let matches = named_item_matches(runtime, descriptor, &key); if descriptor.collection_kind == CollectionKind::FormControlsCollection && matches.len() > 1 { @@ -394,9 +403,10 @@ pub(in crate::native_bridge::collections) fn static_handle_collection_indexed_ge else { return v8::Intercepted::kNo; }; - let node = wrapped_handle_value(scope, runtime_ptr, handle).unwrap_or_else(|| { - panic!("failed to materialize handle-backed static NodeList index `{index}`") - }); + let node = wrapped_handle_value_for_receiver(scope, runtime_ptr, args.holder(), handle) + .unwrap_or_else(|| { + panic!("failed to materialize handle-backed static NodeList index `{index}`") + }); rv.set(node); v8::Intercepted::kYes } @@ -462,9 +472,10 @@ pub(in crate::native_bridge::collections) fn static_handle_collection_indexed_de else { return v8::Intercepted::kNo; }; - let value = wrapped_handle_value(scope, runtime_ptr, handle).unwrap_or_else(|| { - panic!("failed to materialize handle-backed static NodeList index `{index}`") - }); + let value = wrapped_handle_value_for_receiver(scope, runtime_ptr, args.holder(), handle) + .unwrap_or_else(|| { + panic!("failed to materialize handle-backed static NodeList index `{index}`") + }); let Ok(descriptor) = DataPropertyDescriptorDeclaration::new(value, false, true).bind(scope) else { return v8::Intercepted::kNo; diff --git a/moli-renderer-v8/src/native_bridge/context_host/child_frame_runtime/document.rs b/moli-renderer-v8/src/native_bridge/context_host/child_frame_runtime/document.rs index f8f97fde59..170dd66af1 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/child_frame_runtime/document.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/child_frame_runtime/document.rs @@ -1,12 +1,12 @@ -use super::super::JsContextHost; +use super::super::{JsContextHost, WindowExecutionContextOwner}; use super::document_slots::sync_child_document_window_slots; use crate::document_runtime::DomHandle; use crate::document_script_scheduler::FrameDocumentClassicScriptSchedulerWork; use crate::dom::native::Node; use crate::dom_parser::DOM_PARSER_FOREIGN_NODE_SLOT; use crate::native_bridge::{ - document::detached_native_handle_for_runtime, node::remove_child_to_current_reaction_queue, - throw_dom_exception, + OwnerDispatchScope, document::detached_native_handle_for_runtime, + node::remove_child_to_current_reaction_queue, throw_dom_exception, }; use crate::util::{context_host_ptr_from_global_bridge, set_private_value, v8str}; use moli_webapi_declare::WebApiObject; @@ -67,13 +67,31 @@ impl JsContextHost { .refresh_child_browsing_context(scope, handle) .into_iter() .collect::>(); - let live_window = self.child_window_proxy_records.live_window(scope, handle); let current_realm_child = crate::context_bootstrap::child_browsing_context_handle_for_current_realm_scope(scope); + let context = if current_realm_child == Some(handle) { + scope.get_current_context() + } else { + let context = self + .current_child_document_task_owner(handle) + .and_then(|owner| { + self.window_execution_context( + scope, + WindowExecutionContextOwner::Frame(owner.local_window_id), + OwnerDispatchScope::Child(handle), + ) + }); + let Some((_, context)) = context else { + // Lifecycle bookkeeping can run before an initial empty frame needs + // a realm. Defer its wrapper instead of creating it in the caller's realm. + return (None, ready_work); + }; + context + }; + let scope = &mut v8::ContextScope::new(scope, context); + let live_window = self.child_window_proxy_records.live_window(scope, handle); let current_global = scope.get_current_context().global(scope); - let window = live_window.filter(|window| { - current_realm_child != Some(handle) || window.strict_equals(current_global.into()) - }); + let window = live_window.filter(|window| window.strict_equals(current_global.into())); if let Some(document_handle) = self.child_browsing_context_document_handle(handle) .filter(|document_handle| { diff --git a/moli-renderer-v8/src/native_bridge/context_host/core.rs b/moli-renderer-v8/src/native_bridge/context_host/core.rs index 26cad85c4f..ef9ea2748b 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/core.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/core.rs @@ -2204,33 +2204,6 @@ impl JsContextHost { } } - pub(crate) fn custom_element_handle_is_upgraded(&self, handle: DomHandle) -> bool { - self.custom_elements_for_node_handle(handle) - .is_some_and(|store| store.is_upgraded_handle(handle)) - } - - pub(crate) fn custom_element_handle_preserves_wrapper_prototype( - &self, - handle: DomHandle, - ) -> bool { - let Some(element) = self - .dom_host() - .node(handle) - .and_then(|node| node.as_element()) - else { - return false; - }; - if element.custom_element_state() == crate::dom::native::CustomElementState::Uncustomized { - return false; - } - self.custom_elements_for_node_handle(handle) - .is_some_and(|store| { - store.is_upgraded_handle(handle) - || store.is_pending_construction_handle(handle) - || store.is_failed_construction_handle(handle) - }) - } - pub(crate) fn custom_elements_mut_for_node_handle( &mut self, handle: DomHandle, diff --git a/moli-renderer-v8/src/native_bridge/document.rs b/moli-renderer-v8/src/native_bridge/document.rs index cf97abb759..fca0cb7409 100644 --- a/moli-renderer-v8/src/native_bridge/document.rs +++ b/moli-renderer-v8/src/native_bridge/document.rs @@ -19,7 +19,7 @@ use super::node::{ use super::{ JsContextHost, callback_arg_namespace, callback_arg_string, collections, identity::{CollectionKind, LiveCollectionDescriptor, LiveCollectionQueryKind}, - runtime_ptr_from_object, set_wrapped_handle_or_null, throw_dom_exception, + runtime_ptr_from_object, set_wrapped_handle_or_null_for_receiver, throw_dom_exception, validate_attribute_name, validate_element_name, validate_qualified_element_name_and_namespace, validate_qualified_name_and_namespace, }; @@ -560,7 +560,7 @@ fn set_document_node_return_value_for_receiver<'s>( None => rv.set_null(), } } else { - set_wrapped_handle_or_null(scope, rv, runtime_ptr, Some(handle)); + set_wrapped_handle_or_null_for_receiver(scope, rv, runtime_ptr, receiver, Some(handle)); } } @@ -1064,7 +1064,13 @@ fn document_current_script_getter_function<'s>( return; } let current_script = current_script_handle_for_document(runtime, handle); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, current_script); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + current_script, + ); } fn current_script_handle_for_document( diff --git a/moli-renderer-v8/src/native_bridge/document/construction.rs b/moli-renderer-v8/src/native_bridge/document/construction.rs index 64659075c1..a714bbaf5c 100644 --- a/moli-renderer-v8/src/native_bridge/document/construction.rs +++ b/moli-renderer-v8/src/native_bridge/document/construction.rs @@ -5,7 +5,7 @@ use super::super::super::{ use super::super::node::{ node_arg_handle, node_is_document, node_or_foreign_arg_handle_allow_detached, node_runtime_and_handle_from_args, node_runtime_and_handle_from_args_or_detached, - node_runtime_and_handle_from_object, set_wrapped_node_or_null, + node_runtime_and_handle_from_object, }; use super::super::{ JsContextHost, throw_dom_exception, validate_attribute_name, validate_element_name, diff --git a/moli-renderer-v8/src/native_bridge/document/construction/bridge_callbacks.rs b/moli-renderer-v8/src/native_bridge/document/construction/bridge_callbacks.rs index 08e5b6a46e..107f5c979a 100644 --- a/moli-renderer-v8/src/native_bridge/document/construction/bridge_callbacks.rs +++ b/moli-renderer-v8/src/native_bridge/document/construction/bridge_callbacks.rs @@ -29,6 +29,7 @@ pub(in crate::native_bridge) fn bridge_create_element_callback<'s>( } match create_element_wrapper_for_document( scope, + args.this(), runtime_ptr, document_handle, &parsed.local_name, @@ -95,7 +96,7 @@ pub(in crate::native_bridge) fn bridge_create_element_ns_callback<'s>( unsafe { &mut *runtime_ptr }.capture_node_creation_stack_trace(scope, handle); match unsafe { &mut *runtime_ptr } .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, handle) + .wrap_handle_for_receiver(scope, runtime_ptr, args.this(), handle) { Some(element) => rv.set(element.into()), None => rv.set_null(), @@ -104,6 +105,7 @@ pub(in crate::native_bridge) fn bridge_create_element_ns_callback<'s>( } match create_element_wrapper_for_document( scope, + args.this(), runtime_ptr, document_handle, local_name, @@ -128,7 +130,7 @@ pub(in crate::native_bridge) fn bridge_create_element_ns_callback<'s>( unsafe { &mut *runtime_ptr }.capture_node_creation_stack_trace(scope, handle); match unsafe { &mut *runtime_ptr } .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, handle) + .wrap_handle_for_receiver(scope, runtime_ptr, args.this(), handle) { Some(element) => rv.set(element.into()), None => rv.set_null(), @@ -152,10 +154,12 @@ pub(in crate::native_bridge) fn bridge_create_text_node_callback<'s>( let runtime = unsafe { &mut *runtime_ptr }; let handle = runtime.create_text_node_for_document(document_handle, &parsed.data); runtime.capture_node_creation_stack_trace(scope, handle); - match runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, handle) - { + match runtime.native_bridge_mut().wrap_handle_for_receiver( + scope, + runtime_ptr, + args.this(), + handle, + ) { Some(node) => rv.set(node.into()), None => rv.set_null(), } @@ -178,10 +182,12 @@ pub(in crate::native_bridge) fn bridge_create_comment_callback<'s>( let runtime = unsafe { &mut *runtime_ptr }; let handle = runtime.create_comment_for_document(document_handle, &parsed.data); runtime.capture_node_creation_stack_trace(scope, handle); - match runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, handle) - { + match runtime.native_bridge_mut().wrap_handle_for_receiver( + scope, + runtime_ptr, + args.this(), + handle, + ) { Some(node) => rv.set(node.into()), None => rv.set_null(), } @@ -227,10 +233,12 @@ pub(in crate::native_bridge) fn bridge_create_processing_instruction_callback<'s &parsed.data, ); runtime.capture_node_creation_stack_trace(scope, handle); - match runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, handle) - { + match runtime.native_bridge_mut().wrap_handle_for_receiver( + scope, + runtime_ptr, + args.this(), + handle, + ) { Some(node) => rv.set(node.into()), None => rv.set_null(), } diff --git a/moli-renderer-v8/src/native_bridge/document/construction/character_nodes.rs b/moli-renderer-v8/src/native_bridge/document/construction/character_nodes.rs index a2083bf9c8..edfdf81ff9 100644 --- a/moli-renderer-v8/src/native_bridge/document/construction/character_nodes.rs +++ b/moli-renderer-v8/src/native_bridge/document/construction/character_nodes.rs @@ -4,6 +4,7 @@ use crate::native_bridge::document::{ detached_create_cdata_section_html_method_callback, detached_create_cdata_section_method_callback, }; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; pub(in crate::native_bridge) fn node_create_text_node_callback<'s>( scope: &mut v8::PinScope<'s, '_>, @@ -24,7 +25,7 @@ pub(in crate::native_bridge) fn node_create_text_node_callback<'s>( let runtime = unsafe { &mut *runtime_ptr }; let handle = runtime.create_text_node_for_document(document_handle, &parsed.data); runtime.capture_node_creation_stack_trace(scope, handle); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(handle)); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), Some(handle)); } pub(in crate::native_bridge) fn node_create_comment_callback<'s>( @@ -46,7 +47,7 @@ pub(in crate::native_bridge) fn node_create_comment_callback<'s>( let runtime = unsafe { &mut *runtime_ptr }; let handle = runtime.create_comment_for_document(document_handle, &parsed.data); runtime.capture_node_creation_stack_trace(scope, handle); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(handle)); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), Some(handle)); } pub(in crate::native_bridge) fn node_create_document_fragment_callback<'s>( @@ -65,7 +66,7 @@ pub(in crate::native_bridge) fn node_create_document_fragment_callback<'s>( let runtime = unsafe { &mut *runtime_ptr }; let handle = runtime.create_document_fragment_for_document(document_handle); runtime.capture_node_creation_stack_trace(scope, handle); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(handle)); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), Some(handle)); } pub(in crate::native_bridge) fn node_create_processing_instruction_callback<'s>( @@ -110,7 +111,7 @@ pub(in crate::native_bridge) fn node_create_processing_instruction_callback<'s>( &parsed.data, ); runtime.capture_node_creation_stack_trace(scope, handle); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(handle)); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), Some(handle)); } pub(in crate::native_bridge) fn node_create_cdata_section_callback<'s>( diff --git a/moli-renderer-v8/src/native_bridge/document/construction/elements.rs b/moli-renderer-v8/src/native_bridge/document/construction/elements.rs index 8abf675016..92d8ccefde 100644 --- a/moli-renderer-v8/src/native_bridge/document/construction/elements.rs +++ b/moli-renderer-v8/src/native_bridge/document/construction/elements.rs @@ -6,6 +6,7 @@ use super::helpers::{ }; use super::*; use crate::native_bridge::document::validate_registry_association_for_document; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; pub(in crate::native_bridge) fn node_create_element_callback<'s>( scope: &mut v8::PinScope<'s, '_>, @@ -68,11 +69,18 @@ pub(in crate::native_bridge) fn node_create_element_callback<'s>( return; }; unsafe { &mut *runtime_ptr }.capture_node_creation_stack_trace(scope, created_handle); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(created_handle)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(created_handle), + ); return; } match create_element_wrapper_for_document( scope, + args.this(), runtime_ptr, handle, &parsed.local_name, @@ -154,11 +162,18 @@ pub(in crate::native_bridge) fn node_create_element_ns_callback<'s>( return; }; unsafe { &mut *runtime_ptr }.capture_node_creation_stack_trace(scope, created_handle); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(created_handle)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(created_handle), + ); return; } match create_element_wrapper_for_document( scope, + args.this(), runtime_ptr, handle, local_name, @@ -181,5 +196,11 @@ pub(in crate::native_bridge) fn node_create_element_ns_callback<'s>( return; }; unsafe { &mut *runtime_ptr }.capture_node_creation_stack_trace(scope, created_handle); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(created_handle)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(created_handle), + ); } diff --git a/moli-renderer-v8/src/native_bridge/document/construction/helpers.rs b/moli-renderer-v8/src/native_bridge/document/construction/helpers.rs index fdc800cc0e..de59843996 100644 --- a/moli-renderer-v8/src/native_bridge/document/construction/helpers.rs +++ b/moli-renderer-v8/src/native_bridge/document/construction/helpers.rs @@ -2,6 +2,7 @@ use super::*; pub(super) fn create_element_wrapper_for_document<'s>( scope: &mut v8::PinScope<'s, '_>, + receiver: v8::Local<'s, v8::Object>, runtime_ptr: *mut JsContextHost, document_handle: crate::document_runtime::DomHandle, local_name: &str, @@ -9,6 +10,8 @@ pub(super) fn create_element_wrapper_for_document<'s>( registry_association: Option, post_construction_prefix: Option<&str>, ) -> Option> { + let context = receiver.get_creation_context(scope)?; + let scope = &mut v8::ContextScope::new(scope, context); let element = custom_elements::create_element_for_document_local_name_is_and_registry( scope, runtime_ptr, diff --git a/moli-renderer-v8/src/native_bridge/document/construction/import_adopt.rs b/moli-renderer-v8/src/native_bridge/document/construction/import_adopt.rs index 1c58f50462..cdd589f806 100644 --- a/moli-renderer-v8/src/native_bridge/document/construction/import_adopt.rs +++ b/moli-renderer-v8/src/native_bridge/document/construction/import_adopt.rs @@ -4,6 +4,7 @@ use crate::native_bridge::document::{ detached_native_handle_for_runtime, detached_node_type, detached_set_owner_document, object_is_shadow_root, parse_import_node_options, validate_registry_association_for_document, }; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; pub(in crate::native_bridge) fn node_import_node_callback<'a>( scope: &mut v8::PinScope<'a, '_>, @@ -48,7 +49,13 @@ pub(in crate::native_bridge) fn node_import_node_callback<'a>( if let Some(imported) = import_cross_runtime_node_with_shadow_roots(scope, runtime_ptr, handle, node, deep) { - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(imported)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(imported), + ); return; } if let Some(source_handle) = @@ -65,7 +72,13 @@ pub(in crate::native_bridge) fn node_import_node_callback<'a>( rv.set_null(); return; }; - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(imported)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(imported), + ); return; } if let Some(cloned) = @@ -98,7 +111,13 @@ pub(in crate::native_bridge) fn node_import_node_callback<'a>( rv.set_null(); return; }; - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(imported)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(imported), + ); } fn import_cross_runtime_node_with_shadow_roots( @@ -205,5 +224,11 @@ pub(in crate::native_bridge) fn node_adopt_node_callback<'a>( rv.set_null(); return; }; - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(adopted)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(adopted), + ); } diff --git a/moli-renderer-v8/src/native_bridge/document/detached_install/accessors/iframe_content.rs b/moli-renderer-v8/src/native_bridge/document/detached_install/accessors/iframe_content.rs index 04bf4b84b6..d7d1be210e 100644 --- a/moli-renderer-v8/src/native_bridge/document/detached_install/accessors/iframe_content.rs +++ b/moli-renderer-v8/src/native_bridge/document/detached_install/accessors/iframe_content.rs @@ -35,7 +35,9 @@ pub(in crate::native_bridge) fn detached_iframe_content_document<'s>( if let Some((runtime_ptr, handle)) = live_child_browsing_context_handle_for_detached_iframe(scope, iframe) { - return unsafe { &mut *runtime_ptr }.child_browsing_context_document_wrapper(scope, handle); + let runtime = unsafe { &mut *runtime_ptr }; + runtime.child_browsing_context_window_wrapper(scope, handle)?; + return runtime.child_browsing_context_document_wrapper(scope, handle); } let snapshot = detached_iframe_snapshot(scope, iframe)?; let parsed_base_url = snapshot_base_url(snapshot.url.clone(), &snapshot.markup); diff --git a/moli-renderer-v8/src/native_bridge/document/detached_surface/creation/clone_adopt.rs b/moli-renderer-v8/src/native_bridge/document/detached_surface/creation/clone_adopt.rs index 8912382108..d14844fe28 100644 --- a/moli-renderer-v8/src/native_bridge/document/detached_surface/creation/clone_adopt.rs +++ b/moli-renderer-v8/src/native_bridge/document/detached_surface/creation/clone_adopt.rs @@ -2,11 +2,10 @@ use super::super::*; use crate::native_bridge::document::{ parse_import_node_options, validate_registry_association_for_document, }; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; use crate::util::context_host_ptr_from_global_bridge; -use super::super::super::super::node::{ - node_runtime_and_handle_from_object, set_wrapped_node_or_null, -}; +use super::super::super::super::node::node_runtime_and_handle_from_object; fn native_clone_source_handle<'s>( scope: &mut v8::PinScope<'s, '_>, @@ -146,7 +145,13 @@ pub(in crate::native_bridge) fn bridge_clone_node_into_document_callback<'a>( rv.set_null(); return; }; - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(imported)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(imported), + ); return; } match clone_js_node_like_into_document_object(scope, document, node, deep) { diff --git a/moli-renderer-v8/src/native_bridge/document/hit_test.rs b/moli-renderer-v8/src/native_bridge/document/hit_test.rs index 64c2747059..8dc622c259 100644 --- a/moli-renderer-v8/src/native_bridge/document/hit_test.rs +++ b/moli-renderer-v8/src/native_bridge/document/hit_test.rs @@ -6,7 +6,7 @@ use super::super::element::{ }; use super::super::node::{node_is_document, node_runtime_and_handle_from_args}; use crate::document_runtime::DomHandle; -use crate::native_bridge::JsContextHost; +use crate::native_bridge::{JsContextHost, set_wrapped_handle_or_null_for_receiver}; use crate::util::{ context_host_ptr_from_global_bridge, get_private_value, node_wrapper_from_handle, serialize_v8_array, throw_type_error, v8_string, v8str, @@ -419,10 +419,13 @@ pub(crate) fn install_caret_position_template_bindings<'s>( fn build_caret_position_object<'s>( scope: &mut v8::PinScope<'s, '_>, + receiver: v8::Local<'s, v8::Object>, node: DomHandle, offset: u32, rect: ClientRect, ) -> Option> { + let context = receiver.get_creation_context(scope)?; + let scope = &mut v8::ContextScope::new(scope, context); let wrapper = node_wrapper_from_handle(scope, node)?; CaretPositionDeclaration::new( wrapper, @@ -595,8 +598,11 @@ fn throw_hit_test_layout_error(scope: &mut v8::PinScope<'_, '_>, error: moli_lay fn element_array<'s>( scope: &mut v8::PinScope<'s, '_>, + receiver: v8::Local<'s, v8::Object>, elements: impl IntoIterator, ) -> Option> { + let context = receiver.get_creation_context(scope)?; + let scope = &mut v8::ContextScope::new(scope, context); let wrappers = elements .into_iter() .filter_map(|handle| node_wrapper_from_handle(scope, handle)) @@ -623,13 +629,13 @@ pub(in crate::native_bridge) fn node_document_element_from_point_callback<'s>( let runtime = unsafe { &*runtime_ptr }; match element_at_point(runtime, handle, handle, parsed.x, parsed.y) { Ok(element) => { - if let Some(element) = - element.and_then(|element| node_wrapper_from_handle(scope, element)) - { - rv.set(element.into()); - } else { - rv.set_null(); - } + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + element, + ); } Err(error) => { throw_hit_test_layout_error(scope, error); @@ -657,7 +663,8 @@ pub(in crate::native_bridge) fn node_document_elements_from_point_callback<'s>( let runtime = unsafe { &*runtime_ptr }; match elements_at_point(runtime, handle, handle, parsed.x, parsed.y) { Ok(elements) => { - let array = element_array(scope, elements).unwrap_or_else(|| v8::Array::new(scope, 0)); + let array = element_array(scope, args.this(), elements) + .unwrap_or_else(|| v8::Array::new(scope, 0)); rv.set(array.into()); } Err(error) => { @@ -705,7 +712,8 @@ pub(in crate::native_bridge) fn node_document_caret_position_from_point_callback &shadow_roots, &position.ancestor_boxes, ); - if let Some(caret) = build_caret_position_object(scope, node, offset, rect) { + if let Some(caret) = build_caret_position_object(scope, args.this(), node, offset, rect) + { rv.set(caret.into()); } else { rv.set_null(); @@ -742,13 +750,13 @@ pub(in crate::native_bridge) fn node_shadow_root_element_from_point_callback<'s> }; match element_at_point(runtime, document, handle, parsed.x, parsed.y) { Ok(element) => { - if let Some(element) = - element.and_then(|element| node_wrapper_from_handle(scope, element)) - { - rv.set(element.into()); - } else { - rv.set_null(); - } + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + element, + ); } Err(error) => { throw_hit_test_layout_error(scope, error); @@ -780,7 +788,8 @@ pub(in crate::native_bridge) fn node_shadow_root_elements_from_point_callback<'s }; match elements_at_point(runtime, document, handle, parsed.x, parsed.y) { Ok(elements) => { - let array = element_array(scope, elements).unwrap_or_else(|| v8::Array::new(scope, 0)); + let array = element_array(scope, args.this(), elements) + .unwrap_or_else(|| v8::Array::new(scope, 0)); rv.set(array.into()); } Err(error) => { diff --git a/moli-renderer-v8/src/native_bridge/document/live_collections/all/getter.rs b/moli-renderer-v8/src/native_bridge/document/live_collections/all/getter.rs index 9ab41e65b4..a66476e14e 100644 --- a/moli-renderer-v8/src/native_bridge/document/live_collections/all/getter.rs +++ b/moli-renderer-v8/src/native_bridge/document/live_collections/all/getter.rs @@ -16,6 +16,8 @@ pub(crate) fn document_all_value_for_receiver<'s>( if !node_is_document(runtime, handle) { return None; } + let context = receiver.get_creation_context(scope)?; + let scope = &mut v8::ContextScope::new(scope, context); if detached_native_handle_for_runtime(scope, runtime_ptr, receiver).is_some() { return build_detached_document_all(scope, receiver).map(Into::into); } diff --git a/moli-renderer-v8/src/native_bridge/document/queries.rs b/moli-renderer-v8/src/native_bridge/document/queries.rs index cefe445a70..91025847ab 100644 --- a/moli-renderer-v8/src/native_bridge/document/queries.rs +++ b/moli-renderer-v8/src/native_bridge/document/queries.rs @@ -4,7 +4,7 @@ use super::super::super::{ }; use super::super::node::{ node_arg_handle, node_is_document, node_or_foreign_arg_handle_preserve_detached, - node_runtime_and_handle_from_args_or_detached, set_wrapped_node_or_null, + node_runtime_and_handle_from_args_or_detached, }; use super::super::traversal; use super::super::{ diff --git a/moli-renderer-v8/src/native_bridge/document/queries/live.rs b/moli-renderer-v8/src/native_bridge/document/queries/live.rs index 6712722d0b..59019153de 100644 --- a/moli-renderer-v8/src/native_bridge/document/queries/live.rs +++ b/moli-renderer-v8/src/native_bridge/document/queries/live.rs @@ -83,7 +83,7 @@ pub(in crate::native_bridge) fn node_get_element_by_id_callback<'s>( } return; } - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, result); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), result); } fn find_element_by_id_in_subtree( diff --git a/moli-renderer-v8/src/native_bridge/document/queries/xpath/callbacks.rs b/moli-renderer-v8/src/native_bridge/document/queries/xpath/callbacks.rs index adf0e1f45d..d50807eb7d 100644 --- a/moli-renderer-v8/src/native_bridge/document/queries/xpath/callbacks.rs +++ b/moli-renderer-v8/src/native_bridge/document/queries/xpath/callbacks.rs @@ -154,6 +154,7 @@ pub(super) fn install_xpath_evaluator_template_bindings<'s>( #[allow(clippy::too_many_arguments)] fn evaluate_xpath<'a>( scope: &mut v8::PinScope<'a, '_>, + receiver: v8::Local<'a, v8::Object>, root: v8::Local<'a, v8::Object>, live_runtime_ptr: Option<*mut JsContextHost>, expression: &str, @@ -172,36 +173,37 @@ fn evaluate_xpath<'a>( return; } - if let Some(runtime_ptr) = live_runtime_ptr - && let Some(context_handle) = node_arg_handle(scope, runtime_ptr, context_node.into()) - { + let Some(context) = receiver.get_creation_context(scope) else { + rv.set_null(); + return; + }; + let result = { + let scope = &mut v8::ContextScope::new(scope, context); let namespace_resolver = namespace_resolver.map(|callback| V8XPathNamespaceResolver::new(scope, callback)); - match evaluate_xpath_over_live_dom( - scope, - runtime_ptr, - expression, - context_handle, - namespace_resolver, - requested_result_type, - ) { - Ok(Some(result)) => rv.set(result.into()), - Ok(None) => rv.set_null(), - Err(error) => throw_xpath_evaluation_error(scope, error), + if let Some(runtime_ptr) = live_runtime_ptr + && let Some(context_handle) = node_arg_handle(scope, runtime_ptr, context_node.into()) + { + evaluate_xpath_over_live_dom( + scope, + runtime_ptr, + expression, + context_handle, + namespace_resolver, + requested_result_type, + ) + } else { + evaluate_xpath_over_object_tree( + scope, + root, + expression, + Some(context_node), + namespace_resolver, + requested_result_type, + ) } - return; - } - - let namespace_resolver = - namespace_resolver.map(|callback| V8XPathNamespaceResolver::new(scope, callback)); - match evaluate_xpath_over_object_tree( - scope, - root, - expression, - Some(context_node), - namespace_resolver, - requested_result_type, - ) { + }; + match result { Ok(Some(result)) => rv.set(result.into()), Ok(None) => rv.set_null(), Err(error) => throw_xpath_evaluation_error(scope, error), @@ -246,6 +248,7 @@ pub(in crate::native_bridge) fn bridge_detached_document_evaluate_callback<'a>( evaluate_xpath( scope, root, + root, None, &parsed.expression, parsed.context_node, @@ -270,6 +273,7 @@ pub(in crate::native_bridge) fn node_document_evaluate_callback<'a>( evaluate_xpath( scope, root, + root, live_runtime_ptr, &parsed.expression, parsed.context_node, @@ -297,6 +301,7 @@ fn xpath_evaluator_evaluate_callback<'a>( }; evaluate_xpath( scope, + args.this(), root, live_runtime_ptr, &parsed.expression, diff --git a/moli-renderer-v8/src/native_bridge/document/state.rs b/moli-renderer-v8/src/native_bridge/document/state.rs index 163ce099dd..c3e37dc0de 100644 --- a/moli-renderer-v8/src/native_bridge/document/state.rs +++ b/moli-renderer-v8/src/native_bridge/document/state.rs @@ -3,7 +3,7 @@ use crate::document_runtime::DomHandle; use super::super::{ JsContextHost, node::{node_is_document, node_runtime_and_handle_from_object_or_detached}, - set_wrapped_handle_or_null, throw_dom_exception, + set_wrapped_handle_or_null_for_receiver, throw_dom_exception, }; mod focus; diff --git a/moli-renderer-v8/src/native_bridge/document/state/focus.rs b/moli-renderer-v8/src/native_bridge/document/state/focus.rs index fd8a93c44f..f5492883cd 100644 --- a/moli-renderer-v8/src/native_bridge/document/state/focus.rs +++ b/moli-renderer-v8/src/native_bridge/document/state/focus.rs @@ -29,7 +29,7 @@ pub(in crate::native_bridge) fn node_document_active_element_getter_function<'s> .document_element_handle_for_document(handle) }) }); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, handle); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), handle); } fn retarget_active_element_to_document( diff --git a/moli-renderer-v8/src/native_bridge/element/forms/form_element.rs b/moli-renderer-v8/src/native_bridge/element/forms/form_element.rs index 994bf12721..2f4cbb4403 100644 --- a/moli-renderer-v8/src/native_bridge/element/forms/form_element.rs +++ b/moli-renderer-v8/src/native_bridge/element/forms/form_element.rs @@ -1,5 +1,6 @@ use super::*; use crate::custom_elements::is_form_associated_custom_element_handle; +use crate::native_bridge::bridge::wrapped_handle_value_for_receiver; use crate::native_bridge::element::{html_element_getter_receiver, html_element_setter_receiver}; use crate::util::throw_type_error; use moli_webapi_declare::DataPropertyDescriptorDeclaration; @@ -560,6 +561,10 @@ pub(in crate::native_bridge) fn form_named_getter( { return v8::Intercepted::kNo; } + let Some(context) = args.holder().get_creation_context(scope) else { + return v8::Intercepted::kNo; + }; + let scope = &mut v8::ContextScope::new(scope, context); let runtime = unsafe { &mut *runtime_ptr }; let matches = form_named_item_matches(runtime, handle, &key); if matches.len() > 1 { @@ -619,6 +624,10 @@ pub(in crate::native_bridge) fn form_named_descriptor( { return v8::Intercepted::kNo; } + let Some(context) = args.holder().get_creation_context(scope) else { + return v8::Intercepted::kNo; + }; + let scope = &mut v8::ContextScope::new(scope, context); let runtime = unsafe { &mut *runtime_ptr }; let matches = form_named_item_matches(runtime, handle, &key); let value = if matches.len() > 1 { @@ -809,13 +818,12 @@ pub(in crate::native_bridge) fn form_indexed_getter( else { return v8::Intercepted::kNo; }; - let Some(control) = runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, control_handle) + let Some(control) = + wrapped_handle_value_for_receiver(scope, runtime_ptr, args.holder(), control_handle) else { return v8::Intercepted::kNo; }; - rv.set(control.into()); + rv.set(control); v8::Intercepted::kYes } @@ -879,14 +887,12 @@ pub(in crate::native_bridge) fn form_indexed_descriptor( else { return v8::Intercepted::kNo; }; - let Some(control) = runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, control_handle) + let Some(control) = + wrapped_handle_value_for_receiver(scope, runtime_ptr, args.holder(), control_handle) else { return v8::Intercepted::kNo; }; - let Ok(descriptor) = - DataPropertyDescriptorDeclaration::new(control.into(), false, true).bind(scope) + let Ok(descriptor) = DataPropertyDescriptorDeclaration::new(control, false, true).bind(scope) else { return v8::Intercepted::kNo; }; diff --git a/moli-renderer-v8/src/native_bridge/element/forms/input/association.rs b/moli-renderer-v8/src/native_bridge/element/forms/input/association.rs index cd2e8ebf26..e2a4e8efae 100644 --- a/moli-renderer-v8/src/native_bridge/element/forms/input/association.rs +++ b/moli-renderer-v8/src/native_bridge/element/forms/input/association.rs @@ -47,5 +47,11 @@ fn set_wrapped_input_association_or_null<'s>( rv.set(object.into()); return; } - set_wrapped_node_or_null(scope, rv, runtime_ptr, Some(target)); + crate::native_bridge::set_wrapped_handle_or_null_for_receiver( + scope, + rv, + runtime_ptr, + source, + Some(target), + ); } diff --git a/moli-renderer-v8/src/native_bridge/element/forms/labels.rs b/moli-renderer-v8/src/native_bridge/element/forms/labels.rs index 2f9af7db4b..f90ccd7881 100644 --- a/moli-renderer-v8/src/native_bridge/element/forms/labels.rs +++ b/moli-renderer-v8/src/native_bridge/element/forms/labels.rs @@ -1,5 +1,6 @@ use super::*; use crate::native_bridge::document::{detached_form_owner_object, detached_label_control_object}; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; fn is_labelable_element(element: &Element) -> bool { if element.namespace() != "http://www.w3.org/1999/xhtml" { @@ -384,7 +385,7 @@ pub(in crate::native_bridge) fn label_control_getter_function<'s>( return; }; let owner = label_reflected_control_handle(unsafe { &*runtime_ptr }, handle); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, owner); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), owner); } pub(in crate::native_bridge) fn label_form_getter_function<'s>( @@ -408,7 +409,7 @@ pub(in crate::native_bridge) fn label_form_getter_function<'s>( let runtime = unsafe { &*runtime_ptr }; let owner = label_control_handle(runtime, handle) .and_then(|control| super::owner::form_associated_form_owner(runtime, control)); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, owner); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), owner); } pub(in crate::native_bridge) fn control_labels_getter_function<'s>( diff --git a/moli-renderer-v8/src/native_bridge/element/forms/owner.rs b/moli-renderer-v8/src/native_bridge/element/forms/owner.rs index 0b7b2f626a..79d00d1b4f 100644 --- a/moli-renderer-v8/src/native_bridge/element/forms/owner.rs +++ b/moli-renderer-v8/src/native_bridge/element/forms/owner.rs @@ -1,6 +1,7 @@ use super::*; use crate::custom_elements::is_form_associated_custom_element_handle; use crate::native_bridge::document::detached_form_owner_object; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; pub(crate) fn form_associated_form_owner( runtime: &JsContextHost, @@ -244,5 +245,5 @@ pub(in crate::native_bridge) fn form_associated_form_getter_function<'s>( return; }; let owner = form_associated_reflected_form_owner(unsafe { &*runtime_ptr }, handle); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, owner); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), owner); } diff --git a/moli-renderer-v8/src/native_bridge/element/forms/select/option.rs b/moli-renderer-v8/src/native_bridge/element/forms/select/option.rs index 75bd2117f6..003fe21a40 100644 --- a/moli-renderer-v8/src/native_bridge/element/forms/select/option.rs +++ b/moli-renderer-v8/src/native_bridge/element/forms/select/option.rs @@ -2,6 +2,7 @@ use super::helpers::{ effective_option_selected, element_option_value, select_is_multiple, select_option_handles, }; use super::*; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; use crate::native_bridge::{ document::{ detached_element_local_name, detached_form_owner_object, detached_parent_node_object, @@ -261,7 +262,7 @@ pub(in crate::native_bridge) fn option_form_getter_function<'s>( .is_some_and(Element::is_html_select) { let form = form_associated_form_owner(runtime, parent); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, form); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), form); return; } current = runtime.dom_host().parent_node(parent); diff --git a/moli-renderer-v8/src/native_bridge/element/forms/select/select_element.rs b/moli-renderer-v8/src/native_bridge/element/forms/select/select_element.rs index 70a37c8c91..442761026e 100644 --- a/moli-renderer-v8/src/native_bridge/element/forms/select/select_element.rs +++ b/moli-renderer-v8/src/native_bridge/element/forms/select/select_element.rs @@ -1,5 +1,7 @@ use super::helpers::{element_option_value, select_option_handles, selected_index_for_select}; use super::*; +use crate::native_bridge::bridge::wrapped_handle_value_for_receiver; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; use crate::{ native_bridge::{ callback_value_dom_handle, @@ -215,13 +217,11 @@ pub(in crate::native_bridge) fn select_indexed_getter<'s>( else { return v8::Intercepted::kNo; }; - let Some(node) = runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, option) + let Some(node) = wrapped_handle_value_for_receiver(scope, runtime_ptr, args.holder(), option) else { return v8::Intercepted::kNo; }; - rv.set(node.into()); + rv.set(node); v8::Intercepted::kYes } @@ -330,14 +330,11 @@ pub(in crate::native_bridge) fn select_indexed_descriptor<'s>( else { return v8::Intercepted::kNo; }; - let Some(value) = runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, option) + let Some(value) = wrapped_handle_value_for_receiver(scope, runtime_ptr, args.holder(), option) else { return v8::Intercepted::kNo; }; - let Ok(descriptor) = - DataPropertyDescriptorDeclaration::new(value.into(), true, true).bind(scope) + let Ok(descriptor) = DataPropertyDescriptorDeclaration::new(value, true, true).bind(scope) else { return v8::Intercepted::kNo; }; @@ -395,7 +392,7 @@ pub(in crate::native_bridge) fn select_item_callback<'s>( rv.set_null(); return; }; - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(option)); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), Some(option)); } pub(in crate::native_bridge) fn select_named_item_callback<'s>( @@ -423,7 +420,7 @@ pub(in crate::native_bridge) fn select_named_item_callback<'s>( .and_then(Node::as_element) .is_some_and(|element| element.matches_named_item_key(&parsed.name)) }); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, option); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), option); } pub(in crate::native_bridge) fn select_remove_callback<'s>( diff --git a/moli-renderer-v8/src/native_bridge/element/forms/simple_controls.rs b/moli-renderer-v8/src/native_bridge/element/forms/simple_controls.rs index f26e01d684..e519069941 100644 --- a/moli-renderer-v8/src/native_bridge/element/forms/simple_controls.rs +++ b/moli-renderer-v8/src/native_bridge/element/forms/simple_controls.rs @@ -4,6 +4,7 @@ use crate::native_bridge::document::{ detached_native_object_for_handle, detached_parent_node_object, }; use crate::native_bridge::element::{html_element_getter_receiver, html_element_setter_receiver}; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; use crate::util::{get_private_value, node_wrapper_from_handle, set_private_value}; use moli_dom::forms::{ MeterElementValues, ProgressElementValues, meter_element_values, progress_element_values, @@ -175,7 +176,7 @@ pub(in crate::native_bridge) fn legend_form_getter_function<'s>( let runtime = unsafe { &*runtime_ptr }; let form = legend_fieldset_ancestor(runtime, handle) .and_then(|fieldset| runtime.dom_host().form_control_owner(fieldset)); - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, form); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), form); } fn detached_legend_fieldset_object<'s>( @@ -1088,13 +1089,7 @@ fn set_wrapped_button_element_or_null<'s>( rv.set(object.into()); return; } - match unsafe { &mut *runtime_ptr } - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, target) - { - Some(value) => rv.set(value.into()), - None => rv.set_null(), - } + set_wrapped_handle_or_null_for_receiver(scope, rv, runtime_ptr, source, Some(target)); } pub(in crate::native_bridge) fn button_interest_for_element_getter_function<'s>( diff --git a/moli-renderer-v8/src/native_bridge/element/geometry/metrics.rs b/moli-renderer-v8/src/native_bridge/element/geometry/metrics.rs index b72e1545c0..72ac2560dd 100644 --- a/moli-renderer-v8/src/native_bridge/element/geometry/metrics.rs +++ b/moli-renderer-v8/src/native_bridge/element/geometry/metrics.rs @@ -3,6 +3,7 @@ use crate::{document_runtime::DomHandle, native_bridge::JsContextHost, util::v8s use super::super::super::{ document, node::{node_runtime_and_handle_from_object, node_runtime_and_handle_from_object_or_detached}, + set_wrapped_handle_or_null_for_receiver, }; use super::super::styles::raw_inline_style_property_value; use super::super::{queue_revealed_lazy_image_loads, queue_revealed_lazy_media_loads}; @@ -581,13 +582,7 @@ pub(in crate::native_bridge) fn node_offset_parent_getter_function<'s>( rv.set_null(); return; }; - match runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, parent) - { - Some(parent) => rv.set(parent.into()), - None => rv.set_null(), - } + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), Some(parent)); } pub(in crate::native_bridge) fn node_offset_top_getter_function<'s>( diff --git a/moli-renderer-v8/src/native_bridge/element/html_elements/table.rs b/moli-renderer-v8/src/native_bridge/element/html_elements/table.rs index 285813692a..7530aadac8 100644 --- a/moli-renderer-v8/src/native_bridge/element/html_elements/table.rs +++ b/moli-renderer-v8/src/native_bridge/element/html_elements/table.rs @@ -1,5 +1,6 @@ use crate::custom_elements; use crate::dom::native::Node; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; use crate::util::throw_type_error; use crate::webidl; @@ -10,12 +11,12 @@ use super::super::super::node::{ append_child_to_current_reaction_queue, insert_before_to_current_reaction_queue, node_or_existing_detached_arg_handle, node_runtime_and_handle_from_object_or_detached, remove_child_in_reaction_scope, remove_child_to_current_reaction_queue, - set_wrapped_node_or_null, throw_incompatible_getter_receiver, - throw_incompatible_method_receiver, throw_incompatible_setter_receiver, + throw_incompatible_getter_receiver, throw_incompatible_method_receiver, + throw_incompatible_setter_receiver, }; use super::super::super::{ CollectionKind, LiveCollectionQueryKind, collections::build_live_collection_for_node, - set_wrapped_handle_or_null, throw_dom_exception, + throw_dom_exception, }; use super::super::set_reflected_attribute; use super::{DomHandle, JsContextHost, parse_i32_attribute_or}; @@ -595,7 +596,15 @@ pub(in crate::native_bridge::element) fn table_create_t_body_callback<'s>( }; let tbody = unsafe { &mut *runtime_ptr }.create_element("tbody"); let reference = create_tbody_reference(unsafe { &*runtime_ptr }, table); - insert_table_child(scope, runtime_ptr, table, tbody, reference, &mut rv); + insert_table_child( + scope, + args.this(), + runtime_ptr, + table, + tbody, + reference, + &mut rv, + ); } pub(in crate::native_bridge::element) fn table_insert_row_callback<'s>( @@ -639,7 +648,13 @@ pub(in crate::native_bridge::element) fn table_insert_row_callback<'s>( throw_dom_exception(scope, "HierarchyRequestError", 3, "Hierarchy Error"); return; } - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(row)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(row), + ); return; } let reference = (index != -1 && index < len).then(|| rows[index as usize]); @@ -648,7 +663,15 @@ pub(in crate::native_bridge::element) fn table_insert_row_callback<'s>( .and_then(|row| unsafe { &*runtime_ptr }.dom_host().node(row)) .and_then(Node::parent_node) .unwrap_or(table); - insert_table_child(scope, runtime_ptr, parent, row, reference, &mut rv); + insert_table_child( + scope, + args.this(), + runtime_ptr, + parent, + row, + reference, + &mut rv, + ); } pub(in crate::native_bridge::element) fn table_delete_row_callback<'s>( @@ -701,7 +724,15 @@ pub(in crate::native_bridge::element) fn table_section_insert_row_callback<'s>( } let row = unsafe { &mut *runtime_ptr }.create_element("tr"); let reference = (index != -1 && index < len).then(|| rows[index as usize]); - insert_table_child(scope, runtime_ptr, section, row, reference, &mut rv); + insert_table_child( + scope, + args.this(), + runtime_ptr, + section, + row, + reference, + &mut rv, + ); } pub(in crate::native_bridge::element) fn table_section_delete_row_callback<'s>( @@ -753,7 +784,15 @@ pub(in crate::native_bridge::element) fn table_row_insert_cell_callback<'s>( } let cell = unsafe { &mut *runtime_ptr }.create_element("td"); let reference = (index != -1 && index < len).then(|| cells[index as usize]); - insert_table_child(scope, runtime_ptr, row, cell, reference, &mut rv); + insert_table_child( + scope, + args.this(), + runtime_ptr, + row, + cell, + reference, + &mut rv, + ); } pub(in crate::native_bridge::element) fn table_row_delete_cell_callback<'s>( @@ -812,6 +851,11 @@ fn set_table_collection_for_object<'s>( rv.set_null(); return; }; + let Some(context) = object.get_creation_context(scope) else { + rv.set_null(); + return; + }; + let scope = &mut v8::ContextScope::new(scope, context); let collection = build_live_collection_for_node( scope, runtime_ptr, @@ -866,7 +910,7 @@ fn set_table_slot_for_object<'s>( return; }; let child = first_direct_html_child(unsafe { &*runtime_ptr }, handle, local_name); - set_wrapped_handle_or_null(scope, rv, runtime_ptr, child); + set_wrapped_handle_or_null_for_receiver(scope, rv, runtime_ptr, object, child); } fn set_table_slot_on_object<'s>( @@ -985,12 +1029,18 @@ fn create_or_return_table_slot<'s>( return; }; if let Some(existing) = first_direct_html_child(unsafe { &*runtime_ptr }, table, local_name) { - set_wrapped_node_or_null(scope, rv, runtime_ptr, Some(existing)); + set_wrapped_handle_or_null_for_receiver( + scope, + rv, + runtime_ptr, + args.this(), + Some(existing), + ); return; } let child = unsafe { &mut *runtime_ptr }.create_element(local_name); let reference = table_slot_reference(unsafe { &*runtime_ptr }, table, placement, None); - insert_table_child(scope, runtime_ptr, table, child, reference, rv); + insert_table_child(scope, args.this(), runtime_ptr, table, child, reference, rv); } fn delete_table_slot<'s>( @@ -1020,6 +1070,7 @@ fn delete_direct_html_child( fn insert_table_child<'s>( scope: &mut v8::PinScope<'s, '_>, + receiver: v8::Local<'s, v8::Object>, runtime_ptr: *mut JsContextHost, parent: DomHandle, child: DomHandle, @@ -1040,7 +1091,7 @@ fn insert_table_child<'s>( throw_dom_exception(scope, "HierarchyRequestError", 3, "Hierarchy Error"); return; } - set_wrapped_node_or_null(scope, rv, runtime_ptr, Some(child)); + set_wrapped_handle_or_null_for_receiver(scope, rv, runtime_ptr, receiver, Some(child)); } fn insert_table_child_to_current_reaction_queue( diff --git a/moli-renderer-v8/src/native_bridge/element/query.rs b/moli-renderer-v8/src/native_bridge/element/query.rs index e6a96c503b..7321161405 100644 --- a/moli-renderer-v8/src/native_bridge/element/query.rs +++ b/moli-renderer-v8/src/native_bridge/element/query.rs @@ -4,11 +4,12 @@ use super::super::{ node_is_document, node_runtime_and_handle_from_args, node_runtime_and_handle_from_args_or_detached, node_runtime_and_handle_from_object_or_detached, receiver_has_detached_state, - require_element_method_receiver, require_parent_node_receiver, set_wrapped_node_or_null, + require_element_method_receiver, require_parent_node_receiver, throw_incompatible_method_receiver, throw_native_selector_error_for_selector, }, }; use super::forms::control_matches_validity_pseudo; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; use crate::{ util::{ call_object_method, object_number_property, object_property_as_object, v8_string, v8str, @@ -119,7 +120,13 @@ pub(in crate::native_bridge) fn node_query_selector_callback<'s>( return; }; match unsafe { &*runtime_ptr }.query_selector(Some(handle), &parsed.selectors) { - Ok(handle) => set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, handle), + Ok(handle) => set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + handle, + ), Err(error) => throw_native_selector_error_for_selector(scope, &parsed.selectors, &error), } } @@ -170,6 +177,11 @@ pub(in crate::native_bridge) fn node_query_selector_all_callback<'s>( }; match unsafe { &*runtime_ptr }.query_selector_all(Some(handle), &parsed.selectors) { Ok(handles) => { + let Some(context) = args.this().get_creation_context(scope) else { + rv.set_null(); + return; + }; + let scope = &mut v8::ContextScope::new(scope, context); let list = collections::build_node_list_from_handles(scope, runtime_ptr, &handles); rv.set(list.into()); } @@ -319,7 +331,13 @@ pub(in crate::native_bridge) fn node_closest_callback<'s>( None => rv.set_null(), } } - Ok(handle) => set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, handle), + Ok(handle) => set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + handle, + ), Err(error) => throw_native_selector_error_for_selector(scope, &parsed.selectors, &error), } } @@ -359,6 +377,11 @@ pub(in crate::native_bridge) fn node_get_elements_by_tag_name_callback<'s>( return; }; let include_root = node_is_document(unsafe { &*runtime_ptr }, handle); + let Some(context) = args.this().get_creation_context(scope) else { + rv.set_null(); + return; + }; + let scope = &mut v8::ContextScope::new(scope, context); let collection = collections::build_live_collection_for_node( scope, runtime_ptr, @@ -408,6 +431,11 @@ pub(in crate::native_bridge) fn node_get_elements_by_tag_name_ns_callback<'s>( return; }; let include_root = node_is_document(unsafe { &*runtime_ptr }, handle); + let Some(context) = args.this().get_creation_context(scope) else { + rv.set_null(); + return; + }; + let scope = &mut v8::ContextScope::new(scope, context); let collection = collections::build_live_collection_for_node( scope, runtime_ptr, @@ -460,6 +488,11 @@ pub(in crate::native_bridge) fn node_get_elements_by_class_name_callback<'s>( return; }; let include_root = node_is_document(unsafe { &*runtime_ptr }, handle); + let Some(context) = args.this().get_creation_context(scope) else { + rv.set_null(); + return; + }; + let scope = &mut v8::ContextScope::new(scope, context); let collection = collections::build_live_collection_for_node( scope, runtime_ptr, @@ -499,6 +532,11 @@ pub(in crate::native_bridge) fn node_get_elements_by_name_callback<'s>( return; }; let include_root = node_is_document(unsafe { &*runtime_ptr }, handle); + let Some(context) = args.this().get_creation_context(scope) else { + rv.set_null(); + return; + }; + let scope = &mut v8::ContextScope::new(scope, context); let collection = collections::build_live_collection_for_node( scope, runtime_ptr, diff --git a/moli-renderer-v8/src/native_bridge/element/shadow_dom/attach.rs b/moli-renderer-v8/src/native_bridge/element/shadow_dom/attach.rs index 2d1d790a26..71beb4f54e 100644 --- a/moli-renderer-v8/src/native_bridge/element/shadow_dom/attach.rs +++ b/moli-renderer-v8/src/native_bridge/element/shadow_dom/attach.rs @@ -5,7 +5,7 @@ use crate::util::{throw_type_error, v8str}; use super::super::super::{ document::{self, validate_registry_association_for_document}, node::node_runtime_and_handle_from_args, - throw_dom_exception, + set_wrapped_handle_or_null_for_receiver, throw_dom_exception, }; use super::super::property_string_value; @@ -151,11 +151,11 @@ pub(in crate::native_bridge) fn element_attach_shadow_callback<'s>( let root_registry_association = registry_association .unwrap_or_else(|| runtime.effective_custom_element_registry_association(document_handle)); runtime.set_custom_element_registry_association(root_handle, root_registry_association); - match runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, root_handle) - { - Some(root) => rv.set(root.into()), - None => rv.set_null(), - } + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(root_handle), + ); } diff --git a/moli-renderer-v8/src/native_bridge/element/shadow_dom/internals.rs b/moli-renderer-v8/src/native_bridge/element/shadow_dom/internals.rs index 53a5f30484..ab02bdb86f 100644 --- a/moli-renderer-v8/src/native_bridge/element/shadow_dom/internals.rs +++ b/moli-renderer-v8/src/native_bridge/element/shadow_dom/internals.rs @@ -683,12 +683,13 @@ fn element_internals_shadow_root_getter_callback<'s>( rv.set_null(); return; } - let shadow_root = runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, root_handle) - .map(Into::into) - .unwrap_or_else(|| v8::null(scope).into()); - rv.set(shadow_root); + crate::native_bridge::set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(root_handle), + ); } fn element_internals_states_getter_callback<'s>( @@ -1174,13 +1175,13 @@ fn element_internals_form_getter_callback<'s>( rv.set_null(); return; }; - match unsafe { &mut *runtime_ptr } - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, form_handle) - { - Some(form) => rv.set(form.into()), - None => rv.set_null(), - } + crate::native_bridge::set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(form_handle), + ); } fn element_internals_will_validate_getter_callback<'s>( diff --git a/moli-renderer-v8/src/native_bridge/element/shadow_dom/root_accessors.rs b/moli-renderer-v8/src/native_bridge/element/shadow_dom/root_accessors.rs index e6ab884376..841663eff3 100644 --- a/moli-renderer-v8/src/native_bridge/element/shadow_dom/root_accessors.rs +++ b/moli-renderer-v8/src/native_bridge/element/shadow_dom/root_accessors.rs @@ -13,7 +13,7 @@ use crate::{ install_adopted_style_sheets_array_mutation_methods, normalize_adopted_style_sheets_assignment, }, - native_bridge::{DomHandle, set_wrapped_handle_or_null, wrapped_handle_value}, + native_bridge::{DomHandle, set_wrapped_handle_or_null_for_receiver, wrapped_handle_value}, util::{ get_private_value, new_null_prototype_object, node_wrapper_from_handle, set_private_value, v8_string, v8str, @@ -168,7 +168,13 @@ pub(in crate::native_bridge) fn shadow_root_host_getter_function<'s>( rv.set_null(); return; }; - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, Some(host_handle)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(host_handle), + ); } pub(crate) fn shadow_root_adopted_style_sheets_getter_function<'s>( @@ -717,7 +723,13 @@ pub(in crate::native_bridge) fn shadow_root_active_element_getter_function<'s>( rv.set_null(); return; }; - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, Some(active_handle)); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(active_handle), + ); } fn shadow_root_retargeted_active_element( @@ -1005,6 +1017,11 @@ pub(in crate::native_bridge) fn element_shadow_root_getter_function<'s>( rv.set_null(); return; } + let Some(context) = args.this().get_creation_context(scope) else { + rv.set_null(); + return; + }; + let scope = &mut v8::ContextScope::new(scope, context); match runtime .native_bridge_mut() .wrap_handle(scope, runtime_ptr, root_handle) diff --git a/moli-renderer-v8/src/native_bridge/element/shadow_dom/slots.rs b/moli-renderer-v8/src/native_bridge/element/shadow_dom/slots.rs index 04bee2eba6..6527ebcab6 100644 --- a/moli-renderer-v8/src/native_bridge/element/shadow_dom/slots.rs +++ b/moli-renderer-v8/src/native_bridge/element/shadow_dom/slots.rs @@ -7,6 +7,7 @@ use crate::util::v8str; use super::super::super::{ JsContextHost, + bridge::wrapped_handle_value_for_receiver, node::{ node_runtime_and_handle_from_args_or_detached, node_runtime_and_handle_from_object_or_detached, @@ -77,10 +78,7 @@ fn assigned_slot_for_object<'s>( if document::detached_native_handle_for_runtime(scope, runtime_ptr, object).is_some() { document::detached_native_object_for_handle(scope, runtime_ptr, slot_handle).map(Into::into) } else { - runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, slot_handle) - .map(Into::into) + wrapped_handle_value_for_receiver(scope, runtime_ptr, object, slot_handle) } } @@ -100,7 +98,14 @@ pub(in crate::native_bridge) fn slot_assigned_nodes_callback<'s>( .assigned_nodes_for_slot_with_options(handle, flatten); let receiver_is_detached = document::detached_native_handle_for_runtime(scope, runtime_ptr, args.this()).is_some(); - set_slot_handle_array(scope, &mut rv, runtime_ptr, &handles, receiver_is_detached); + set_slot_handle_array( + scope, + &mut rv, + runtime_ptr, + args.this(), + &handles, + receiver_is_detached, + ); } pub(in crate::native_bridge) fn slot_assigned_elements_callback<'s>( @@ -127,7 +132,14 @@ pub(in crate::native_bridge) fn slot_assigned_elements_callback<'s>( .collect::>(); let receiver_is_detached = document::detached_native_handle_for_runtime(scope, runtime_ptr, args.this()).is_some(); - set_slot_handle_array(scope, &mut rv, runtime_ptr, &handles, receiver_is_detached); + set_slot_handle_array( + scope, + &mut rv, + runtime_ptr, + args.this(), + &handles, + receiver_is_detached, + ); } pub(in crate::native_bridge) fn slot_assign_callback<'s>( @@ -227,13 +239,19 @@ fn slot_flatten_option( }) } -fn set_slot_handle_array( - scope: &mut v8::PinScope<'_, '_>, +fn set_slot_handle_array<'s>( + scope: &mut v8::PinScope<'s, '_>, rv: &mut v8::ReturnValue<'_, v8::Value>, runtime_ptr: *mut JsContextHost, + receiver: v8::Local<'s, v8::Object>, handles: &[DomHandle], detached_objects: bool, ) { + let Some(context) = receiver.get_creation_context(scope) else { + rv.set_null(); + return; + }; + let scope = &mut v8::ContextScope::new(scope, context); let mut values = Vec::with_capacity(handles.len()); for handle in handles.iter().copied() { let node = if detached_objects { diff --git a/moli-renderer-v8/src/native_bridge/element/shadow_dom/template.rs b/moli-renderer-v8/src/native_bridge/element/shadow_dom/template.rs index d529b9e995..47901726c0 100644 --- a/moli-renderer-v8/src/native_bridge/element/shadow_dom/template.rs +++ b/moli-renderer-v8/src/native_bridge/element/shadow_dom/template.rs @@ -1,7 +1,9 @@ use crate::dom::native::Node; use crate::util::v8_string; -use super::super::super::node::node_runtime_and_handle_from_object_or_detached; +use super::super::super::{ + node::node_runtime_and_handle_from_object_or_detached, set_wrapped_handle_or_null_for_receiver, +}; use super::super::{ element_attribute, property_string_value, set_reflected_attribute, set_reflected_boolean_attribute, @@ -164,13 +166,13 @@ fn template_content_for_object<'s>( rv.set_null(); return; }; - match runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, contents_handle) - { - Some(contents) => rv.set(contents.into()), - None => rv.set_null(), - } + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + object, + Some(contents_handle), + ); } pub(in crate::native_bridge) fn template_content_getter_function<'s>( diff --git a/moli-renderer-v8/src/native_bridge/element/tree_mutation/callbacks.rs b/moli-renderer-v8/src/native_bridge/element/tree_mutation/callbacks.rs index 6d095a1ec3..a7cbde3376 100644 --- a/moli-renderer-v8/src/native_bridge/element/tree_mutation/callbacks.rs +++ b/moli-renderer-v8/src/native_bridge/element/tree_mutation/callbacks.rs @@ -1,11 +1,12 @@ use crate::dom::native::Node; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; use crate::util::throw_type_error; use crate::webidl; use super::super::super::{ node::{ node_is_element, node_or_foreign_arg_handle_allow_detached, - node_runtime_and_handle_from_args_or_detached, set_wrapped_node_or_null, + node_runtime_and_handle_from_args_or_detached, }, throw_dom_exception, }; @@ -146,7 +147,7 @@ pub(in crate::native_bridge) fn node_insert_adjacent_element_callback<'s>( } return; } - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(handle)); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), Some(handle)); } pub(in crate::native_bridge) fn node_insert_adjacent_text_callback<'s>( diff --git a/moli-renderer-v8/src/native_bridge/node.rs b/moli-renderer-v8/src/native_bridge/node.rs index 6396edb1b6..bf741802c4 100644 --- a/moli-renderer-v8/src/native_bridge/node.rs +++ b/moli-renderer-v8/src/native_bridge/node.rs @@ -15,7 +15,8 @@ use super::super::{ }; use super::{ CollectionKind, JsContextHost, LiveCollectionQueryKind, callback_arg_string, - runtime_ptr_from_object, set_wrapped_handle_or_null, throw_dom_exception, + runtime_ptr_from_object, set_wrapped_handle_or_null, set_wrapped_handle_or_null_for_receiver, + throw_dom_exception, }; mod bridge_callbacks; @@ -603,7 +604,7 @@ fn node_owner_document_getter_function<'s>( .dom_host() .node(handle) .and_then(Node::owner_document); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, owner); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), owner); } fn node_base_uri_getter_function<'s>( @@ -678,7 +679,7 @@ fn node_parent_node_getter_function<'s>( .dom_host() .node(handle) .and_then(Node::parent_node); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, parent); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), parent); } fn node_parent_element_getter_function<'s>( @@ -704,7 +705,7 @@ fn node_parent_element_getter_function<'s>( .node(handle) .and_then(Node::parent_node) .filter(|parent| node_is_element(unsafe { &*runtime_ptr }, *parent)); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, parent); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), parent); } fn node_child_nodes_getter_function<'s>( @@ -725,6 +726,11 @@ fn node_child_nodes_getter_function<'s>( rv.set_null(); return; }; + let Some(context) = args.this().get_creation_context(scope) else { + rv.set_null(); + return; + }; + let scope = &mut v8::ContextScope::new(scope, context); let collection = super::collections::build_live_collection_for_node( scope, runtime_ptr, @@ -759,7 +765,7 @@ fn node_first_child_getter_function<'s>( .dom_host() .node(handle) .and_then(Node::first_child); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, child); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), child); } fn node_last_child_getter_function<'s>( @@ -784,7 +790,7 @@ fn node_last_child_getter_function<'s>( .dom_host() .node(handle) .and_then(Node::last_child); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, child); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), child); } fn node_previous_sibling_getter_function<'s>( @@ -809,7 +815,7 @@ fn node_previous_sibling_getter_function<'s>( .dom_host() .node(handle) .and_then(Node::prev_sibling); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, sibling); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), sibling); } fn node_next_sibling_getter_function<'s>( @@ -834,7 +840,7 @@ fn node_next_sibling_getter_function<'s>( .dom_host() .node(handle) .and_then(Node::next_sibling); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, sibling); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), sibling); } pub(in crate::native_bridge) fn node_text_content_getter_function<'s>( @@ -960,6 +966,11 @@ fn parent_node_children_getter_function<'s>( rv.set_null(); return; } + let Some(context) = args.this().get_creation_context(scope) else { + rv.set_null(); + return; + }; + let scope = &mut v8::ContextScope::new(scope, context); let collection = super::collections::build_live_collection_for_node( scope, runtime_ptr, @@ -1004,7 +1015,7 @@ fn parent_node_first_element_child_getter_function<'s>( .dom_host() .node(handle) .and_then(|node| node.first_element_child(unsafe { &*runtime_ptr }.dom_host().dom())); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, child); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), child); } fn parent_node_last_element_child_getter_function<'s>( @@ -1039,7 +1050,7 @@ fn parent_node_last_element_child_getter_function<'s>( .dom_host() .node(handle) .and_then(|node| node.last_element_child(unsafe { &*runtime_ptr }.dom_host().dom())); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, child); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), child); } fn parent_node_child_element_count_getter_function<'s>( @@ -1113,7 +1124,7 @@ fn non_document_type_child_node_previous_element_sibling_getter_function<'s>( .dom_host() .node(handle) .and_then(|node| node.previous_element_sibling(unsafe { &*runtime_ptr }.dom_host().dom())); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, sibling); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), sibling); } fn non_document_type_child_node_next_element_sibling_getter_function<'s>( @@ -1147,7 +1158,7 @@ fn non_document_type_child_node_next_element_sibling_getter_function<'s>( .dom_host() .node(handle) .and_then(|node| node.next_element_sibling(unsafe { &*runtime_ptr }.dom_host().dom())); - set_wrapped_handle_or_null(scope, &mut rv, runtime_ptr, sibling); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), sibling); } fn node_append_child_prototype_callback<'s>( diff --git a/moli-renderer-v8/src/native_bridge/node/character_data/text.rs b/moli-renderer-v8/src/native_bridge/node/character_data/text.rs index 970a35edb2..97dc849761 100644 --- a/moli-renderer-v8/src/native_bridge/node/character_data/text.rs +++ b/moli-renderer-v8/src/native_bridge/node/character_data/text.rs @@ -81,12 +81,11 @@ pub(in crate::native_bridge) fn node_split_text_callback<'s>( rv.set_undefined(); return; }; - let Some(wrapper) = runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, new_handle) - else { - rv.set_undefined(); - return; - }; - rv.set(wrapper.into()); + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(new_handle), + ); } diff --git a/moli-renderer-v8/src/native_bridge/node/tree/methods.rs b/moli-renderer-v8/src/native_bridge/node/tree/methods.rs index 51077c62ab..efe97a8b58 100644 --- a/moli-renderer-v8/src/native_bridge/node/tree/methods.rs +++ b/moli-renderer-v8/src/native_bridge/node/tree/methods.rs @@ -478,11 +478,11 @@ pub(in crate::native_bridge) fn node_get_root_node_callback( } return; } - match runtime - .native_bridge_mut() - .wrap_handle(scope, runtime_ptr, root_handle) - { - Some(root) => rv.set(root.into()), - None => rv.set_null(), - } + set_wrapped_handle_or_null_for_receiver( + scope, + &mut rv, + runtime_ptr, + args.this(), + Some(root_handle), + ); } diff --git a/moli-renderer-v8/src/native_bridge/node/tree/mutation_methods.rs b/moli-renderer-v8/src/native_bridge/node/tree/mutation_methods.rs index 06b3038843..66b1fb12de 100644 --- a/moli-renderer-v8/src/native_bridge/node/tree/mutation_methods.rs +++ b/moli-renderer-v8/src/native_bridge/node/tree/mutation_methods.rs @@ -1,4 +1,5 @@ use super::*; +use crate::native_bridge::set_wrapped_handle_or_null_for_receiver; pub(in crate::native_bridge) fn node_normalize_callback( scope: &mut v8::PinScope<'_, '_>, @@ -39,5 +40,5 @@ pub(in crate::native_bridge) fn node_clone_node_callback<'s>( throw_dom_exception(scope, "NotSupportedError", 9, "Not supported"); return; }; - set_wrapped_node_or_null(scope, &mut rv, runtime_ptr, Some(clone)); + set_wrapped_handle_or_null_for_receiver(scope, &mut rv, runtime_ptr, args.this(), Some(clone)); } diff --git a/moli-renderer-v8/src/native_bridge/traversal.rs b/moli-renderer-v8/src/native_bridge/traversal.rs index 12afb44819..399d8177d6 100644 --- a/moli-renderer-v8/src/native_bridge/traversal.rs +++ b/moli-renderer-v8/src/native_bridge/traversal.rs @@ -6,6 +6,46 @@ mod state; mod tree_walker; mod wrappers; +// TreeWalker, NodeIterator, and NodeFilter use Blink's NodeWrapInOwnContext +// rule: choose the node's document realm before wrapping a native node. +fn wrapped_traversal_node_value<'s>( + scope: &mut v8::PinScope<'s, '_>, + runtime_ptr: *mut super::JsContextHost, + context_node: crate::document_runtime::DomHandle, + node: crate::document_runtime::DomHandle, +) -> Option> { + let document_handle = unsafe { &*runtime_ptr } + .dom_host() + .owner_document_handle(context_node); + // Look in this world's cache first so an isolated world keeps its own realm. + let context = document_handle + .and_then(|document| { + unsafe { &mut *runtime_ptr } + .native_bridge_mut() + .cached_handle_wrapper(scope, document) + }) + .and_then(|document| document.get_creation_context(scope)) + .or_else(|| { + super::node::node_owner_document_relevant_context(scope, runtime_ptr, context_node) + }) + .unwrap_or_else(|| scope.get_current_context()); + let scope = &mut v8::ContextScope::new(scope, context); + super::bridge::wrapped_handle_value(scope, runtime_ptr, node) +} + +fn set_wrapped_traversal_node_or_null( + scope: &mut v8::PinScope<'_, '_>, + rv: &mut v8::ReturnValue<'_, v8::Value>, + runtime_ptr: *mut super::JsContextHost, + root: crate::document_runtime::DomHandle, + node: Option, +) { + match node.and_then(|node| wrapped_traversal_node_value(scope, runtime_ptr, root, node)) { + Some(value) => rv.set(value), + None => rv.set_null(), + } +} + pub(super) use filters::TraversalFilter; pub(super) use state::{NodeIteratorSnapshot, TraversalStore, TreeWalkerSnapshot}; pub(crate) use wrappers::install_traversal_template_bindings; diff --git a/moli-renderer-v8/src/native_bridge/traversal/filters.rs b/moli-renderer-v8/src/native_bridge/traversal/filters.rs index 82531b4243..1b419fe3b6 100644 --- a/moli-renderer-v8/src/native_bridge/traversal/filters.rs +++ b/moli-renderer-v8/src/native_bridge/traversal/filters.rs @@ -6,9 +6,7 @@ use crate::{ dom::native::NodeType, { document_runtime::DomHandle, - native_bridge::{ - JsContextHost, WindowExecutionContextIdentity, bridge::wrapped_handle_value, - }, + native_bridge::{JsContextHost, WindowExecutionContextIdentity}, util::throw_type_error, webidl, }, @@ -146,7 +144,8 @@ fn call_traversal_filter( ); return TraversalFilterResult::Exception; } - let Some(node_wrapper) = wrapped_handle_value(scope, runtime_ptr, node) else { + let Some(node_wrapper) = super::wrapped_traversal_node_value(scope, runtime_ptr, node, node) + else { return TraversalFilterResult::Accept; }; let callback_this = v8::undefined(scope).into(); diff --git a/moli-renderer-v8/src/native_bridge/traversal/node_iterator.rs b/moli-renderer-v8/src/native_bridge/traversal/node_iterator.rs index 1d2d523b63..12ed12d503 100644 --- a/moli-renderer-v8/src/native_bridge/traversal/node_iterator.rs +++ b/moli-renderer-v8/src/native_bridge/traversal/node_iterator.rs @@ -1,8 +1,9 @@ +use super::set_wrapped_traversal_node_or_null; use super::{ algorithms::{node_iterator_next_node, node_iterator_previous_node}, identity::node_iterator_snapshot_from_object, }; -use crate::native_bridge::bridge::{set_wrapped_handle_or_null, throw_dom_exception}; +use crate::native_bridge::bridge::throw_dom_exception; pub(super) fn node_iterator_root_getter( scope: &mut v8::PinScope<'_, '_>, @@ -14,10 +15,11 @@ pub(super) fn node_iterator_root_getter( rv.set_null(); return; }; - set_wrapped_handle_or_null( + set_wrapped_traversal_node_or_null( scope, &mut rv, snapshot.runtime_ptr, + snapshot.state.root, Some(snapshot.state.root), ); } @@ -62,10 +64,11 @@ pub(super) fn node_iterator_reference_node_getter( rv.set_null(); return; }; - set_wrapped_handle_or_null( + set_wrapped_traversal_node_or_null( scope, &mut rv, snapshot.runtime_ptr, + snapshot.state.root, Some(snapshot.state.reference_node), ); } @@ -116,7 +119,13 @@ pub(super) fn node_iterator_next_node_callback( let bridge = unsafe { &mut *snapshot.runtime_ptr }.native_bridge_mut(); bridge.set_node_iterator_position(snapshot.id, reference_node, pointer_before_reference_node); bridge.node_iterator_end(snapshot.id); - set_wrapped_handle_or_null(scope, &mut rv, snapshot.runtime_ptr, result); + set_wrapped_traversal_node_or_null( + scope, + &mut rv, + snapshot.runtime_ptr, + snapshot.state.root, + result, + ); } pub(super) fn node_iterator_previous_node_callback( @@ -152,7 +161,13 @@ pub(super) fn node_iterator_previous_node_callback( let bridge = unsafe { &mut *snapshot.runtime_ptr }.native_bridge_mut(); bridge.set_node_iterator_position(snapshot.id, reference_node, pointer_before_reference_node); bridge.node_iterator_end(snapshot.id); - set_wrapped_handle_or_null(scope, &mut rv, snapshot.runtime_ptr, result); + set_wrapped_traversal_node_or_null( + scope, + &mut rv, + snapshot.runtime_ptr, + snapshot.state.root, + result, + ); } pub(super) fn node_iterator_detach_callback( diff --git a/moli-renderer-v8/src/native_bridge/traversal/tree_walker.rs b/moli-renderer-v8/src/native_bridge/traversal/tree_walker.rs index a84b6caa9d..5f02138243 100644 --- a/moli-renderer-v8/src/native_bridge/traversal/tree_walker.rs +++ b/moli-renderer-v8/src/native_bridge/traversal/tree_walker.rs @@ -1,3 +1,4 @@ +use super::set_wrapped_traversal_node_or_null; use super::{ TreeWalkerSnapshot, algorithms::{ @@ -9,7 +10,7 @@ use super::{ }; use crate::native_bridge::{ bridge::throw_dom_exception, callback_value_dom_handle, - node::node_or_foreign_arg_handle_preserve_detached, set_wrapped_handle_or_null, + node::node_or_foreign_arg_handle_preserve_detached, }; use crate::util::throw_type_error; @@ -48,10 +49,11 @@ pub(super) fn tree_walker_root_getter( rv.set_null(); return; }; - set_wrapped_handle_or_null( + set_wrapped_traversal_node_or_null( scope, &mut rv, snapshot.runtime_ptr, + snapshot.state.root, Some(snapshot.state.root), ); } @@ -96,10 +98,11 @@ pub(super) fn tree_walker_current_node_getter( rv.set_null(); return; }; - set_wrapped_handle_or_null( + set_wrapped_traversal_node_or_null( scope, &mut rv, snapshot.runtime_ptr, + snapshot.state.root, Some(snapshot.state.current_node), ); } @@ -151,7 +154,13 @@ pub(super) fn tree_walker_parent_node_callback( .set_tree_walker_current_node(snapshot.id, handle); } tree_walker_end(&snapshot); - set_wrapped_handle_or_null(scope, &mut rv, snapshot.runtime_ptr, result); + set_wrapped_traversal_node_or_null( + scope, + &mut rv, + snapshot.runtime_ptr, + snapshot.state.root, + result, + ); } pub(super) fn tree_walker_first_child_callback( @@ -183,7 +192,13 @@ pub(super) fn tree_walker_first_child_callback( .set_tree_walker_current_node(snapshot.id, handle); } tree_walker_end(&snapshot); - set_wrapped_handle_or_null(scope, &mut rv, snapshot.runtime_ptr, result); + set_wrapped_traversal_node_or_null( + scope, + &mut rv, + snapshot.runtime_ptr, + snapshot.state.root, + result, + ); } pub(super) fn tree_walker_last_child_callback( @@ -215,7 +230,13 @@ pub(super) fn tree_walker_last_child_callback( .set_tree_walker_current_node(snapshot.id, handle); } tree_walker_end(&snapshot); - set_wrapped_handle_or_null(scope, &mut rv, snapshot.runtime_ptr, result); + set_wrapped_traversal_node_or_null( + scope, + &mut rv, + snapshot.runtime_ptr, + snapshot.state.root, + result, + ); } pub(super) fn tree_walker_next_sibling_callback( @@ -241,7 +262,13 @@ pub(super) fn tree_walker_next_sibling_callback( .set_tree_walker_current_node(snapshot.id, handle); } tree_walker_end(&snapshot); - set_wrapped_handle_or_null(scope, &mut rv, snapshot.runtime_ptr, result); + set_wrapped_traversal_node_or_null( + scope, + &mut rv, + snapshot.runtime_ptr, + snapshot.state.root, + result, + ); } pub(super) fn tree_walker_previous_sibling_callback( @@ -268,7 +295,13 @@ pub(super) fn tree_walker_previous_sibling_callback( .set_tree_walker_current_node(snapshot.id, handle); } tree_walker_end(&snapshot); - set_wrapped_handle_or_null(scope, &mut rv, snapshot.runtime_ptr, result); + set_wrapped_traversal_node_or_null( + scope, + &mut rv, + snapshot.runtime_ptr, + snapshot.state.root, + result, + ); } pub(super) fn tree_walker_next_node_callback( @@ -294,7 +327,13 @@ pub(super) fn tree_walker_next_node_callback( .set_tree_walker_current_node(snapshot.id, handle); } tree_walker_end(&snapshot); - set_wrapped_handle_or_null(scope, &mut rv, snapshot.runtime_ptr, result); + set_wrapped_traversal_node_or_null( + scope, + &mut rv, + snapshot.runtime_ptr, + snapshot.state.root, + result, + ); } pub(super) fn tree_walker_previous_node_callback( @@ -320,5 +359,11 @@ pub(super) fn tree_walker_previous_node_callback( .set_tree_walker_current_node(snapshot.id, handle); } tree_walker_end(&snapshot); - set_wrapped_handle_or_null(scope, &mut rv, snapshot.runtime_ptr, result); + set_wrapped_traversal_node_or_null( + scope, + &mut rv, + snapshot.runtime_ptr, + snapshot.state.root, + result, + ); } diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs index 0cc2ed0ea1..89c84b45bd 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs @@ -6683,13 +6683,12 @@ fn small_static_nodelist_array_indexof_remains_fast_after_length_tamper() { } #[test] -fn main_document_static_nodelist_reads_skip_child_realm_prototype_sync() { +fn main_document_static_nodelist_reads_preserve_wrapper_identity() { let markup = format!( "{}", "".repeat(1_100) ); let mut vm = new_parsed_test_vm("https://example.test/", &markup); - crate::native_bridge::bindings::reset_wrapper_owner_realm_custom_element_checks_for_test(); let result = vm .eval( @@ -6709,11 +6708,6 @@ fn main_document_static_nodelist_reads_skip_child_realm_prototype_sync() { .expect("large static NodeList iteration should evaluate"); assert_eq!(result, "1100|2200|true"); - assert_eq!( - crate::native_bridge::bindings::wrapper_owner_realm_custom_element_checks_for_test(), - 0, - "main-document wrappers cannot require a child-realm prototype" - ); } #[test] diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/mod.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/mod.rs index 025fcf8b28..331e4ae8bd 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/mod.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/mod.rs @@ -6,4 +6,5 @@ mod dom_surface; mod focus; mod live_document; mod text_controls; +mod wrapper_identity; mod zhihu_probe; diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/wrapper_identity.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/wrapper_identity.rs new file mode 100644 index 0000000000..6ea87b0139 --- /dev/null +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/wrapper_identity.rs @@ -0,0 +1,488 @@ +use super::*; + +#[test] +fn cached_dom_wrapper_lookup_preserves_javascript_prototype() { + let mut vm = new_storage_test_vm("https://wrapper-lookup.test/"); + let result = vm.eval(r##" + (() => { + const html = document.documentElement || document.appendChild(document.createElement("html")); + const body = document.body || html.appendChild(document.createElement("body")); + const frame = body.appendChild(document.createElement("iframe")); + const failures = []; + let cases = 0; + for (const [realm, w] of [["main", window], ["child", frame.contentWindow]]) { + const doc = w.document; + const root = doc.body || doc.documentElement; + for (const kind of ["null", "object", "inherited"]) { + const element = doc.createElement("div"); + const initialRealm = Object.getPrototypeOf(element) === w.HTMLDivElement.prototype; + element.id = `wrapper-${kind}`; + root.appendChild(element); + const selected = kind === "null" ? null : + kind === "object" ? {} : Object.create(w.HTMLDivElement.prototype); + Object.setPrototypeOf(element, selected); + const lookups = [doc.querySelector(`#wrapper-${kind}`), + doc.getElementById(`wrapper-${kind}`), root.lastChild, + doc.querySelectorAll(`#wrapper-${kind}`)[0]]; + const same = lookups.every(value => value === element); + const preserved = lookups.every(value => Object.getPrototypeOf(value) === selected); + if (!initialRealm || !same || !preserved) { + failures.push({realm, kind, initialRealm, same, preserved}); + } + root.removeChild(element); + cases++; + } + } + return JSON.stringify({cases, failures}); + })() + "##).expect("DOM lookups should preserve the existing JavaScript object and its prototype"); + assert_eq!(result, r#"{"cases":6,"failures":[]}"#); +} + +#[test] +fn cached_dom_wrapper_adoption_preserves_identity_and_prototype() { + let mut vm = new_storage_test_vm("https://wrapper-adoption.test/"); + let result = vm.eval(r##" + (() => { + const html = document.documentElement || document.appendChild(document.createElement("html")); + const body = document.body || html.appendChild(document.createElement("body")); + const a = body.appendChild(document.createElement("iframe")).contentWindow; + const b = body.appendChild(document.createElement("iframe")).contentWindow; + const ownerDocument = Object.getOwnPropertyDescriptor(Node.prototype, "ownerDocument").get; + const failures = []; + let cases = 0; + for (const [direction, source, target] of [ + ["main-child", window, a], ["child-main", a, window], + ["child-sibling", a, b], ["sibling-child", b, a]]) { + for (const kind of ["native", "null", "inherited"]) { + const element = source.document.createElement("div"); + element.id = `adopted-${cases}`; + (source.document.body || source.document.documentElement).appendChild(element); + const selected = kind === "native" ? source.HTMLDivElement.prototype : + kind === "null" ? null : Object.create(source.HTMLDivElement.prototype); + if (kind !== "native") Object.setPrototypeOf(element, selected); + const adopted = target.document.adoptNode(element); + const afterAdopt = Object.getPrototypeOf(adopted) === selected; + const root = target.document.body || target.document.documentElement; + root.appendChild(adopted); + const lookedUp = target.document.querySelector(`#adopted-${cases}`); + const same = adopted === element && lookedUp === element; + const ownerChanged = ownerDocument.call(element) === target.document; + const afterLookup = Object.getPrototypeOf(lookedUp) === selected; + if (!same || !ownerChanged || !afterAdopt || !afterLookup) { + failures.push({direction, kind, same, ownerChanged, afterAdopt, afterLookup}); + } + root.removeChild(element); + cases++; + } + } + return JSON.stringify({cases, failures}); + })() + "##).expect("adoption should change the owner document while preserving the JavaScript wrapper"); + assert_eq!(result, r#"{"cases":12,"failures":[]}"#); +} + +#[test] +fn new_dom_wrappers_use_child_realm_prototypes() { + let mut vm = new_storage_test_vm("https://wrapper-initialization.test/"); + let result = vm.eval(r##" + (() => { + const html = document.documentElement || document.appendChild(document.createElement("html")); + const body = document.body || html.appendChild(document.createElement("body")); + const w = body.appendChild(document.createElement("iframe")).contentWindow; + const doc = w.document; + const created = [ + [doc, w.HTMLDocument.prototype], + [doc.createRange(), w.Range.prototype], + [doc.createElement("div"), w.HTMLDivElement.prototype], + [doc.createTextNode("created"), w.Text.prototype], + [doc.createComment("created"), w.Comment.prototype] + ]; + doc.body.innerHTML = "
text
"; + const parsed = doc.querySelector("#parsed"); + const nodes = [...created, [parsed, w.HTMLDivElement.prototype], + [parsed.firstChild, w.Text.prototype], [parsed.lastChild, w.Comment.prototype]]; + return nodes.map(([node, prototype]) => Object.getPrototypeOf(node) === prototype).join(":"); + })() + "##).expect("new DOM wrappers should receive their child-realm native prototypes on first access"); + assert_eq!(result, "true:true:true:true:true:true:true:true"); +} + +#[test] +fn child_dom_wrappers_do_not_read_public_constructor_properties() { + let mut vm = new_parsed_test_vm( + "https://wrapper-intrinsics.test/", + "", + ); + let result = vm.eval(r#" + (() => { + const w = document.body.appendChild(document.createElement("iframe")).contentWindow; + const doc = w.document; + const names = ["HTMLDivElement", "Text", "Comment"]; + const prototypes = names.map(name => w[name].prototype); + let reads = 0; + for (const name of names) { + Object.defineProperty(w, name, { + configurable: true, + get() { reads++; return {prototype: {}}; } + }); + } + const created = [doc.createElement("div"), doc.createTextNode("created"), + doc.createComment("created")]; + doc.body.innerHTML = "
parsed
"; + const div = doc.body.firstChild; + const parsed = [div, div.firstChild, div.lastChild]; + return [reads, ...[created, parsed].flatMap(nodes => + nodes.map((node, index) => Object.getPrototypeOf(node) === prototypes[index]))].join(":"); + })() + "#).expect("wrapper creation should use intrinsic prototypes without invoking author getters"); + assert_eq!(result, "0:true:true:true:true:true:true"); +} + +#[test] +fn child_dom_collections_wrap_nodes_in_the_collection_realm() { + let mut vm = new_parsed_test_vm( + "https://wrapper-collections.test/", + "", + ); + let result = vm.eval(r#" + (() => { + const w = document.body.appendChild(document.createElement("iframe")).contentWindow; + const doc = w.document; + doc.body.innerHTML = Array.from({length: 1100}, (_, i) => ``).join(""); + const list = doc.querySelectorAll("span"); + const live = doc.body.children; + const nodes = [list[0], NodeList.prototype.item.call(list, 1), + Object.getOwnPropertyDescriptor(list, "2").value, + live[3], HTMLCollection.prototype.item.call(live, 4), + live["node-5"], HTMLCollection.prototype.namedItem.call(live, "node-6"), + Object.getOwnPropertyDescriptor(live, "7").value, + Object.getOwnPropertyDescriptor(live, "node-8").value]; + return nodes.map(node => Object.getPrototypeOf(node) === w.HTMLSpanElement.prototype).join(":"); + })() + "#).expect("collection access should create node wrappers in the collection's realm"); + assert_eq!(result, "true:true:true:true:true:true:true:true:true"); +} + +#[test] +fn borrowed_dom_methods_create_wrappers_in_the_receiver_realm() { + let mut vm = new_parsed_test_vm( + "https://wrapper-receiver.test/", + "", + ); + let result = vm.eval(r#" + (() => { + const child = document.body.appendChild(document.createElement("iframe")).contentWindow; + const failures = []; + let cases = 0; + for (const [source, target] of [[window, child], [child, window]]) { + const doc = target.document; + const methods = source.Document.prototype; + const nodes = [ + [methods.createElement.call(doc, "div"), target.HTMLDivElement.prototype], + [methods.createElementNS.call(doc, "http://www.w3.org/1999/xhtml", "span"), target.HTMLSpanElement.prototype], + [methods.createTextNode.call(doc, "text"), target.Text.prototype], + [methods.createComment.call(doc, "comment"), target.Comment.prototype], + [methods.createDocumentFragment.call(doc), target.DocumentFragment.prototype] + ]; + const root = doc.createElement("section"); + root.innerHTML = "
"; + const firstChild = Object.getOwnPropertyDescriptor(source.Node.prototype, "firstChild").get; + nodes.push([firstChild.call(root), target.HTMLDivElement.prototype]); + nodes.push([source.Element.prototype.querySelector.call(root, "span"), target.HTMLSpanElement.prototype]); + nodes.push([source.Element.prototype.querySelectorAll.call(root, "span"), target.NodeList.prototype]); + const childNodes = Object.getOwnPropertyDescriptor(source.Node.prototype, "childNodes").get; + nodes.push([childNodes.call(root), target.NodeList.prototype]); + nodes.push([methods.getElementsByTagName.call(doc, "span"), target.HTMLCollection.prototype]); + const table = doc.createElement("table"); + nodes.push([source.HTMLTableElement.prototype.createTBody.call(table), target.HTMLTableSectionElement.prototype]); + for (const [node, prototype] of nodes) { + if (Object.getPrototypeOf(node) !== prototype) failures.push(cases); + cases++; + } + } + return JSON.stringify({cases, failures}); + })() + "#).expect("borrowed DOM methods should wrap returned nodes in their receiver's realm"); + assert_eq!(result, r#"{"cases":22,"failures":[]}"#); +} + +#[test] +fn borrowed_split_text_preserves_receiver_realm_after_adoption() { + let mut vm = new_parsed_test_vm( + "https://wrapper-split-text.test/", + "", + ); + let result = vm + .eval( + r#" + (() => { + const child = document.body.appendChild(document.createElement("iframe")).contentWindow; + const failures = []; + for (const [source, receiverRealm] of [[window, child], [child, window]]) { + for (const adopt of [false, true]) { + const text = receiverRealm.document.createTextNode("ab"); + const owner = adopt ? source.document : receiverRealm.document; + if (adopt) owner.adoptNode(text); + const parent = owner.createElement("div"); + parent.appendChild(text); + const result = source.Text.prototype.splitText.call(text, 1); + if (Object.getPrototypeOf(result) !== receiverRealm.Text.prototype || + result.ownerDocument !== owner || text.data !== "a" || result.data !== "b" || + text.nextSibling !== result || parent.lastChild !== result) { + failures.push({childReceiver: receiverRealm === child, adopt}); + } + } + } + return JSON.stringify(failures); + })() + "#, + ) + .expect("splitText should create its result in the receiver's realm even after adoption"); + assert_eq!(result, "[]"); +} + +#[test] +fn borrowed_shadow_and_template_accessors_use_the_receiver_realm() { + let mut vm = new_parsed_test_vm( + "https://wrapper-shadow-template.test/", + "", + ); + let result = vm.eval(r#" + (() => { + const child = document.body.appendChild(document.createElement("iframe")).contentWindow; + const failures = []; + for (const [source, target] of [[window, child], [child, window]]) { + const host = target.document.createElement("div"); + const root = source.Element.prototype.attachShadow.call(host, {mode: "open"}); + const slotRoot = target.document.createElement("div").attachShadow({mode: "open"}); + slotRoot.innerHTML = ""; + const slot = slotRoot.firstChild; + const assigned = source.HTMLSlotElement.prototype.assignedNodes.call(slot, {flatten: true}); + const template = target.document.createElement("template"); + const content = Object.getOwnPropertyDescriptor(source.HTMLTemplateElement.prototype, "content").get.call(template); + for (const [name, object, prototype] of [ + ["attachShadow", root, target.ShadowRoot.prototype], + ["assignedNodes", assigned[0], target.HTMLSpanElement.prototype], + ["template.content", content, target.DocumentFragment.prototype]]) { + if (!object || Object.getPrototypeOf(object) !== prototype) failures.push(name); + } + } + return JSON.stringify(failures); + })() + "#).expect("shadow and template results should use the receiver's realm on first access"); + assert_eq!(result, "[]"); +} + +#[test] +fn child_form_indexed_and_named_access_wraps_unseen_controls_in_its_realm() { + let mut vm = new_parsed_test_vm( + "https://wrapper-form-access.test/", + "", + ); + let result = vm.eval(r#" + (() => { + const child = document.body.appendChild(document.createElement("iframe")).contentWindow; + const doc = child.document; + const form = doc.createElement("form"); + form.innerHTML = ""; + const select = doc.createElement("select"); + select.innerHTML = ""; + const nodes = [ + [form[0], child.HTMLInputElement.prototype], + [Object.getOwnPropertyDescriptor(form, "1").value, child.HTMLInputElement.prototype], + [form.third, child.HTMLInputElement.prototype], + [Object.getOwnPropertyDescriptor(form, "fourth").value, child.HTMLInputElement.prototype], + [select[0], child.HTMLOptionElement.prototype], + [Object.getOwnPropertyDescriptor(select, "1").value, child.HTMLOptionElement.prototype]]; + return nodes.map(([node, prototype]) => Object.getPrototypeOf(node) === prototype).join(":"); + })() + "#).expect("form and select property interceptors should use their holder's realm"); + assert_eq!(result, "true:true:true:true:true:true"); +} + +#[test] +fn child_window_named_access_wraps_unseen_nodes_in_the_window_realm() { + let mut vm = new_parsed_test_vm( + "https://wrapper-window-named.test/", + "", + ); + let result = vm.eval(r#" + (() => { + const w = document.body.appendChild(document.createElement("iframe")).contentWindow; + w.document.body.innerHTML = "
"; + const node = w.namedNode; + const images = w.namedImages; + return [Object.getPrototypeOf(node) === w.HTMLDivElement.prototype, + Object.getPrototypeOf(images) === w.HTMLCollection.prototype, + Object.getPrototypeOf(images[0]) === w.HTMLImageElement.prototype, + Object.getPrototypeOf(images[1]) === w.HTMLImageElement.prototype].join(":"); + })() + "#).expect("Window named access should wrap nodes and collections in that Window's realm"); + assert_eq!(result, "true:true:true:true"); +} + +#[test] +fn borrowed_range_xpath_and_document_all_results_use_the_receiver_realm() { + let mut vm = new_parsed_test_vm( + "https://wrapper-result-containers.test/", + "", + ); + let result = vm.eval(r#" + (() => { + const child = document.body.appendChild(document.createElement("iframe")).contentWindow; + const failures = []; + for (const [source, target] of [[window, child], [child, window]]) { + const doc = target.document; + const root = doc.createElement("section"); + root.innerHTML = "

contents

"; + doc.body.appendChild(root); + const range = doc.createRange(); + range.selectNodeContents(root); + const clone = source.Range.prototype.cloneContents.call(range); + const fragment = source.Range.prototype.createContextualFragment.call(range, ""); + const extracted = source.Range.prototype.extractContents.call(range); + const xpathRoot = doc.createElement("section"); + xpathRoot.innerHTML = "

found

"; + const xpath = source.Document.prototype.evaluate.call(doc, ".//p", xpathRoot, null, 9, null); + root.innerHTML = ""; + const all = Object.getOwnPropertyDescriptor(source.Document.prototype, "all").get.call(doc); + for (const [name, object, prototype] of [ + ["cloneContents", clone, target.DocumentFragment.prototype], + ["createContextualFragment", fragment, target.DocumentFragment.prototype], + ["extractContents", extracted, target.DocumentFragment.prototype], + ["XPathResult", xpath, target.XPathResult.prototype], + ["singleNodeValue", xpath.singleNodeValue, target.HTMLParagraphElement.prototype], + ["document.all", all, target.HTMLAllCollection.prototype], + ["document.all.item", all.namedItem("all-result"), target.HTMLSpanElement.prototype]]) { + if (object === null || object === undefined || Object.getPrototypeOf(object) !== prototype) failures.push(name); + } + root.remove(); + } + return JSON.stringify(failures); + })() + "#).expect("returned containers and their unseen nodes should use the receiver's realm"); + assert_eq!(result, "[]"); +} + +#[test] +fn adopted_element_wraps_unseen_children_in_its_original_realm() { + let mut vm = new_parsed_test_vm( + "https://wrapper-adopted-children.test/", + "", + ); + let result = vm.eval(r#" + (() => { + const child = document.body.appendChild(document.createElement("iframe")).contentWindow; + const results = []; + for (const [source, target] of [[window, child], [child, window]]) { + const root = source.document.createElement("section"); + root.innerHTML = "
"; + target.document.adoptNode(root); + const first = root.firstChild; + const last = root.querySelector("span"); + results.push(Object.getPrototypeOf(root) === source.HTMLElement.prototype, + Object.getPrototypeOf(first) === source.HTMLDivElement.prototype, + Object.getPrototypeOf(last) === source.HTMLSpanElement.prototype, + first.ownerDocument === target.document, last.ownerDocument === target.document); + } + return results.every(Boolean); + })() + "#).expect("adoption should not change the realm used by an existing receiver to wrap its children"); + assert_eq!(result, "true"); +} + +#[test] +fn traversal_filter_wraps_unseen_nodes_in_their_document_realm() { + let mut vm = new_parsed_test_vm( + "https://wrapper-traversal.test/", + "", + ); + let result = vm + .eval( + r#" + (() => { + const results = []; + for (const kind of ["TreeWalker", "NodeIterator"]) { + const w = document.body.appendChild(document.createElement("iframe")).contentWindow; + const root = w.document.body; + root.innerHTML = "
"; + let filtered; + const traversal = document[`create${kind}`](root, NodeFilter.SHOW_ELEMENT, { + acceptNode(node) { + if (node === root) return NodeFilter.FILTER_SKIP; + filtered = node; + return NodeFilter.FILTER_ACCEPT; + } + }); + const node = traversal.nextNode(); + results.push(Object.getPrototypeOf(traversal) === window[kind].prototype, + node === filtered, Object.getPrototypeOf(node) === w.HTMLDivElement.prototype); + } + return results.join(":"); + })() + "#, + ) + .expect( + "NodeFilter arguments should be wrapped in the node's document realm on first access", + ); + assert_eq!(result, "true:true:true:true:true:true"); +} + +#[test] +fn isolated_child_dom_wrappers_use_isolated_intrinsic_prototypes() { + let mut vm = new_parsed_test_vm( + "https://wrapper-isolated.test/", + "", + ); + vm.eval( + r#" + globalThis.frame = document.body.appendChild(document.createElement("iframe")); + frame.contentDocument.body.innerHTML = "
"; + "#, + ) + .expect("child document should initialize"); + let child_context_id = + materialize_single_child_default_realm_for_test(&mut vm, "isolated wrapper setup"); + let frame_id = vm + .child_default_frame_id_for_execution_context_id(child_context_id) + .expect("child frame id should exist"); + let isolated_context_id = vm + .create_isolated_world_for_frame(&frame_id, "wrapper-utility", false) + .expect("isolated world should initialize"); + let result = vm + .eval_in_isolated_context( + isolated_context_id, + r#" + (() => { + const element = document.getElementById("shared-node"); + const created = document.createElement("span"); + const traversed = document.createTreeWalker(document, NodeFilter.SHOW_ELEMENT).nextNode(); + const correct = Object.getPrototypeOf(document) === HTMLDocument.prototype && + Object.getPrototypeOf(element) === HTMLDivElement.prototype && + Object.getPrototypeOf(created) === HTMLSpanElement.prototype && + Object.getPrototypeOf(traversed) === HTMLHtmlElement.prototype; + element.isolatedMarker = true; + Object.setPrototypeOf(element, null); + return correct && document.getElementById("shared-node") === element && + Object.getPrototypeOf(element) === null; + })() + "#, + ) + .expect("isolated DOM wrappers should use and preserve their own world's prototypes"); + assert_eq!(result, "true"); + assert_eq!( + vm.eval( + r#" + (() => { + const w = frame.contentWindow; + const element = w.document.getElementById("shared-node"); + return Object.getPrototypeOf(element) === w.HTMLDivElement.prototype && + element.isolatedMarker === undefined; + })() + "# + ) + .expect("default-world wrapper identity should remain separate"), + "true" + ); +}