mirror of
https://github.com/lexmount/moli.git
synced 2026-10-06 08:00:59 +00:00
fix(renderer): use receiver host for NamedNodeMap wrappers
This commit is contained in:
@@ -23,15 +23,15 @@ fn set_live_named_node_map_cache<'s>(
|
||||
|
||||
pub(crate) fn live_named_node_map_wrapper<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
runtime_ptr: *mut JsContextHost,
|
||||
element: v8::Local<'s, v8::Object>,
|
||||
) -> v8::Local<'s, v8::Object> {
|
||||
if let Some(wrapper) = live_named_node_map_cache(scope, element) {
|
||||
refresh_named_node_map_wrapper(scope, wrapper, element);
|
||||
return wrapper;
|
||||
}
|
||||
let bridge = global_bridge_object(scope).expect("NamedNodeMap requires the native bridge");
|
||||
let runtime_ptr = runtime_ptr_from_object(scope, bridge)
|
||||
.expect("NamedNodeMap bridge must expose its runtime pointer");
|
||||
// The attributes getter already resolved this host from the receiver.
|
||||
// The public bridge can be absent while that host is still installed.
|
||||
let template = unsafe { &mut *runtime_ptr }
|
||||
.native_bridge_mut()
|
||||
.named_node_map_wrapper_template();
|
||||
|
||||
@@ -1326,12 +1326,12 @@ fn element_attributes_getter_function<'s>(
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'s, v8::Value>,
|
||||
) {
|
||||
let Some((_runtime_ptr, _handle)) = element_getter_receiver(scope, args.this(), "attributes")
|
||||
let Some((runtime_ptr, _handle)) = element_getter_receiver(scope, args.this(), "attributes")
|
||||
else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
let wrapper = super::document::live_named_node_map_wrapper(scope, args.this());
|
||||
let wrapper = super::document::live_named_node_map_wrapper(scope, runtime_ptr, args.this());
|
||||
rv.set(wrapper.into());
|
||||
}
|
||||
|
||||
|
||||
+80
@@ -995,6 +995,86 @@ fn access_key_label_matches_chromium_single_key_surface() {
|
||||
r#"{"descriptor":[null,true,true],"valid":"Alt+b","missing":"","empty":"","multiple":"","nonBmp":"","incompatible":"TypeError"}"#
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn named_node_map_uses_receiver_host_when_global_bridge_is_missing() {
|
||||
assert_named_node_map_uses_receiver_host("delete globalThis.__moliNativeBridge");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn named_node_map_uses_receiver_host_when_global_bridge_is_replaced() {
|
||||
assert_named_node_map_uses_receiver_host("globalThis.__moliNativeBridge = {}; true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn named_node_map_uses_receiver_host_without_reading_global_bridge_accessor() {
|
||||
assert_named_node_map_uses_receiver_host(
|
||||
r#"Object.defineProperty(globalThis, '__moliNativeBridge', {
|
||||
configurable: true,
|
||||
get() { throw new Error('the public bridge accessor must not run'); }
|
||||
}); true"#,
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_named_node_map_uses_receiver_host(bridge_change: &str) {
|
||||
let mut vm = new_storage_test_vm("https://named-node-map-receiver-host.test/");
|
||||
vm.eval(
|
||||
r#"
|
||||
globalThis.liveElement = document.createElement('div');
|
||||
liveElement.setAttribute('data-real', 'one');
|
||||
globalThis.detachedElement = new DOMParser()
|
||||
.parseFromString('<div data-real="one"></div>', 'text/html')
|
||||
.querySelector('div');
|
||||
"#,
|
||||
)
|
||||
.expect("live and detached elements should be created before the bridge changes");
|
||||
assert_eq!(
|
||||
vm.eval(bridge_change)
|
||||
.expect("the public bridge should be removable or replaceable"),
|
||||
"true"
|
||||
);
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const probe = element => {
|
||||
// The first access must construct a wrapper without the public bridge.
|
||||
const attributes = element.attributes;
|
||||
const attribute = attributes.item(0);
|
||||
// Mutate through Attr so detached Element's JS bridge shim is not involved.
|
||||
attribute.value = 'two';
|
||||
return {
|
||||
tag: Object.prototype.toString.call(attributes),
|
||||
prototype: Object.getPrototypeOf(attributes) === NamedNodeMap.prototype,
|
||||
sameWrapper: attributes === element.attributes,
|
||||
sameAttr: attribute === attributes.item(0),
|
||||
length: attributes.length,
|
||||
indexedValue: attributes[0].value,
|
||||
namedValue: attributes.getNamedItem('data-real').value,
|
||||
ownerElement: attribute.ownerElement === element
|
||||
};
|
||||
};
|
||||
return JSON.stringify([probe(liveElement), probe(detachedElement)]);
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("NamedNodeMap should use the validated receiver host without the public bridge");
|
||||
let expected = serde_json::json!({
|
||||
"tag": "[object NamedNodeMap]",
|
||||
"prototype": true,
|
||||
"sameWrapper": true,
|
||||
"sameAttr": true,
|
||||
"length": 1,
|
||||
"indexedValue": "two",
|
||||
"namedValue": "two",
|
||||
"ownerElement": true
|
||||
});
|
||||
assert_eq!(
|
||||
serde_json::from_str::<serde_json::Value>(&result).expect("probe result should be JSON"),
|
||||
serde_json::json!([expected.clone(), expected])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn named_node_map_accessor_expandos_remain_writable_through_setters() {
|
||||
let mut vm = new_storage_test_vm("https://named-node-map-accessor-expando.test/");
|
||||
|
||||
Reference in New Issue
Block a user