mirror of
https://github.com/lexmount/moli.git
synced 2026-10-05 00:00:55 +00:00
fix(dom): create wrappers in their receiver realm
Cached child-document wrappers reapplied their current owner's native prototype, overwriting JavaScript changes and changing prototypes during adoption. Return cached wrappers directly and remove the custom-element exceptions and the owner-document prototype correction from wrapper creation. Create child Document wrappers in their owning context, deferring creation for unmaterialized initial empty frames. Choose the receiver's creation context at DOM return boundaries, including splitText, shadow and template access, forms, Range and XPath results, and named Window access. Collection indexing and named access use the collection's context. Keep traversal results and NodeFilter arguments in the node document's realm as required by NodeWrapInOwnContext. Wrapper creation uses the selected context's intrinsic interface prototype without reading author-visible constructors or repairing prototypes later. Cover changed and null prototypes, adoption, child Document and Range realms, borrowed DOM methods, property interceptors, collections, replaced public constructors, and isolated-world wrapper identity. Exercise first access before other APIs can hide an incorrect creation realm by populating the wrapper cache. Validation: - cargo fmt --all - cargo clippy --workspace --all-targets --all-features -- -D warnings - cargo nextest run --no-fail-fast (17,776 passed, 13 skipped)
This commit is contained in:
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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<DomHandle>,
|
||||
) -> Option<Option<v8::Local<'s, v8::Object>>> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<v8::Local<'s, v8::Object>> {
|
||||
@@ -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>(
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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<u64> =
|
||||
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<v8::ObjectTemplate>,
|
||||
@@ -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>,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<DomHandle>,
|
||||
) {
|
||||
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<v8::Local<'s, v8::Value>> {
|
||||
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,
|
||||
|
||||
@@ -51,19 +51,6 @@ impl NativeDomBridge {
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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::{
|
||||
|
||||
@@ -21,6 +21,8 @@ pub(in crate::native_bridge::collections) fn collection_value_at<'s>(
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
index: usize,
|
||||
) -> Option<v8::Local<'s, v8::Value>> {
|
||||
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<v8::Local<'s, v8::Value>> {
|
||||
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);
|
||||
|
||||
@@ -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<v8::Local<'s, v8::Value>> {
|
||||
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;
|
||||
|
||||
@@ -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::<Vec<_>>();
|
||||
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| {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<custom_elements::CustomElementRegistryAssociation>,
|
||||
post_construction_prefix: Option<&str>,
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
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,
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
+3
-1
@@ -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);
|
||||
|
||||
+9
-4
@@ -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) {
|
||||
|
||||
@@ -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<v8::Local<'s, v8::Object>> {
|
||||
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<Item = DomHandle>,
|
||||
) -> Option<v8::Local<'s, v8::Array>> {
|
||||
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) => {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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::{
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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::<Vec<_>>();
|
||||
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 {
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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<v8::Local<'s, v8::Value>> {
|
||||
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<crate::document_runtime::DomHandle>,
|
||||
) {
|
||||
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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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!(
|
||||
"<!doctype html><html><body>{}</body></html>",
|
||||
"<span class=hit></span>".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]
|
||||
|
||||
@@ -6,4 +6,5 @@ mod dom_surface;
|
||||
mod focus;
|
||||
mod live_document;
|
||||
mod text_controls;
|
||||
mod wrapper_identity;
|
||||
mod zhihu_probe;
|
||||
|
||||
@@ -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 = "<div id='parsed'>text<!--comment--></div>";
|
||||
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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
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 = "<div>parsed<!--comment--></div>";
|
||||
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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
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) => `<span id='node-${i}'></span>`).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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
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 = "<div></div><span></span>";
|
||||
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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
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 = "<slot><span></span></slot>";
|
||||
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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
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 = "<input name='first'><input name='second'><input name='third'><input name='fourth'>";
|
||||
const select = doc.createElement("select");
|
||||
select.innerHTML = "<option>first</option><option>second</option>";
|
||||
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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
let result = vm.eval(r#"
|
||||
(() => {
|
||||
const w = document.body.appendChild(document.createElement("iframe")).contentWindow;
|
||||
w.document.body.innerHTML = "<div id='namedNode'></div><img name='namedImages'><img name='namedImages'>";
|
||||
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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
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 = "<p>contents</p>";
|
||||
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, "<span></span>");
|
||||
const extracted = source.Range.prototype.extractContents.call(range);
|
||||
const xpathRoot = doc.createElement("section");
|
||||
xpathRoot.innerHTML = "<p>found</p>";
|
||||
const xpath = source.Document.prototype.evaluate.call(doc, ".//p", xpathRoot, null, 9, null);
|
||||
root.innerHTML = "<span id='all-result'></span>";
|
||||
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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
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 = "<div></div><span></span>";
|
||||
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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
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 = "<div></div>";
|
||||
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/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
vm.eval(
|
||||
r#"
|
||||
globalThis.frame = document.body.appendChild(document.createElement("iframe"));
|
||||
frame.contentDocument.body.innerHTML = "<div id='shared-node'></div>";
|
||||
"#,
|
||||
)
|
||||
.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"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user