fix(dom): resolve detached nodes in their owning host

This commit is contained in:
ldm0
2026-10-11 18:06:49 +08:00
parent 3b5aa19931
commit 8eabb29a48
3 changed files with 29 additions and 5 deletions
+8 -3
View File
@@ -10,7 +10,7 @@ use moli_webapi_declare::WebApiFunctionTemplate;
use super::super::{
document_runtime::DomHandle,
util::{
call_global_bridge_method, context_host_ptr_from_global_bridge, get_private_value,
call_global_bridge_method, context_host_ptr_from_context_slot, get_private_value,
throw_type_error, v8_string, v8str,
},
};
@@ -813,8 +813,13 @@ pub(crate) fn node_runtime_and_handle_from_object_or_detached<'s>(
if let Ok(node) = node_runtime_and_handle_from_object(scope, object) {
return Ok(node);
}
let runtime_ptr = context_host_ptr_from_global_bridge(scope)
.context("current context has no JsContextHost")?;
// Detached node indices are host-local too. Borrowed bindings and
// structuredClone can run in another Page, so resolve the producer's
// lifecycle-checked context instead of looking in the caller's arena.
let runtime_ptr = object
.get_creation_context(scope)
.and_then(context_host_ptr_from_context_slot)
.context("detached node's context has no JsContextHost")?;
let handle = super::document::detached_native_handle_for_runtime(scope, runtime_ptr, object)
.context("object is neither a Node wrapper nor a detached node")?;
Ok((runtime_ptr, handle))
@@ -10,7 +10,7 @@ fn svg_rect_uses_shared_geometry_in_live_and_windowless_realms() {
"[]"
);
assert_eq!(
vm.eval("__uiEventResults.complete && __uiEventResults.total === 3522 && __uiEventResults.passed === 3522")
vm.eval("__uiEventResults.complete && __uiEventResults.total === 4122 && __uiEventResults.passed === 4122")
.unwrap(),
"true"
);
@@ -60,7 +60,19 @@
if (kind === 'DOMRect') clone.width = .1;
return owner.getAttribute('viewBox') === previous && (kind !== 'DOMRect' || clone.width === .1);
});
check(`${prefix}/${kind}/cross realm JSON`, () => Object.getPrototypeOf(DOMRectReadOnly.prototype.toJSON.call(rect)) === Object.prototype);
check(`${prefix}/${kind}/cross realm JSON`, () => {
owner.setAttribute('viewBox','10 20 30 40');
const json = DOMRectReadOnly.prototype.toJSON.call(rect);
return Object.getPrototypeOf(json) === Object.prototype && fields.every((name,i) => json[name] === [10,20,30,40][i]) && json.right === 40 && json.bottom === 60;
});
check(`${prefix}/${kind}/borrowed coordinate getters`, () => {
owner.setAttribute('viewBox','11 22 33 44');
return fields.every((name,i) => Object.getOwnPropertyDescriptor(DOMRectReadOnly.prototype,name).get.call(rect) === [11,22,33,44][i]);
});
check(`${prefix}/${kind}/borrowed side getters`, () => {
owner.setAttribute('viewBox','-5 -7 3 4');
return ['left','top','right','bottom'].every((name,i) => Object.getOwnPropertyDescriptor(DOMRectReadOnly.prototype,name).get.call(rect) === [-5,-7,-2,-3][i]);
});
check(`${prefix}/${kind}/fromRect snapshot`, () => {
const copy = w.DOMRect.fromRect(rect);
const x = copy.x; owner.setAttribute('viewBox','5 6 7 8');
@@ -82,6 +94,13 @@
const expected = [10,20,30,40]; expected[fields.indexOf(name)] = .1;
return conversions === 1 && fields.every((key,i) => base[key] === expected[i] && anim[key] === expected[i]) && owner.getAttribute('viewBox') === expected.join(' ');
});
check(`${prefix}/${name}/borrowed setter reentrant mutation`, () => {
let conversions = 0;
const borrowed = Object.getOwnPropertyDescriptor(DOMRect.prototype,name).set;
borrowed.call(base,{valueOf(){conversions++;owner.setAttribute('viewBox','50 60 70 80');return .2;}});
const expected = [50,60,70,80]; expected[fields.indexOf(name)] = .2;
return conversions === 1 && fields.every((key,i) => base[key] === expected[i] && anim[key] === expected[i]) && owner.getAttribute('viewBox') === expected.join(' ');
});
check(`${prefix}/${name}/throwing conversion preserved`, () => {
const sentinel = {}, previous = owner.getAttribute('viewBox');
return thrown(() => setter.call(base,{valueOf(){throw sentinel;}})) === sentinel && owner.getAttribute('viewBox') === previous;