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" + ); +}