mirror of
https://github.com/lexmount/moli.git
synced 2026-11-01 16:05:37 +00:00
fix(events): preserve construction realms across wrapper worlds
This commit is contained in:
@@ -5,6 +5,23 @@
|
||||
use super::{events, platform_object_worlds, shared_event_targets, world_wrappers};
|
||||
use crate::native_bridge::identity::contexts_share_wrapper_world;
|
||||
|
||||
fn construction_context_in_world<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
world: v8::Local<'s, v8::Context>,
|
||||
) -> Option<v8::Local<'s, v8::Context>> {
|
||||
let creation = event.get_creation_context(scope)?;
|
||||
if contexts_share_wrapper_world(creation, world) {
|
||||
return Some(creation);
|
||||
}
|
||||
let host_ptr = crate::util::context_host_ptr_from_context_slot(creation)?;
|
||||
let host = unsafe { &*host_ptr };
|
||||
let identity = host.window_execution_context_identity_for_access_check(creation)?;
|
||||
let (_, default) =
|
||||
host.window_execution_context(scope, identity.owner(), identity.dispatch_scope())?;
|
||||
contexts_share_wrapper_world(default, world).then_some(default)
|
||||
}
|
||||
|
||||
pub(crate) fn target_in_realm<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
@@ -29,15 +46,19 @@ pub(crate) fn event_in_realm<'s>(
|
||||
}
|
||||
let backing = events::event_backing(scope, event);
|
||||
let target = target_in_realm(scope, target, context)?;
|
||||
// Preserve owner identity within a wrapper world. A foreign Window without
|
||||
// a local view must not move the Event into that Window's different world.
|
||||
let context = target
|
||||
.get_creation_context(scope)
|
||||
.filter(|creation| contexts_share_wrapper_world(*creation, context))
|
||||
.unwrap_or(context);
|
||||
if let Some(wrapper) = world_wrappers::get(scope, backing, context) {
|
||||
return Some(wrapper);
|
||||
}
|
||||
// Project into the construction Window's counterpart when it belongs to
|
||||
// the listener's world. Adoption retains the target's prototype realm,
|
||||
// which cannot determine an independently constructed Event's realm.
|
||||
let context = construction_context_in_world(scope, backing, context)
|
||||
.or_else(|| {
|
||||
target
|
||||
.get_creation_context(scope)
|
||||
.filter(|creation| contexts_share_wrapper_world(*creation, context))
|
||||
})
|
||||
.unwrap_or(context);
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
events::new_event_wrapper(scope, backing)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,64 @@ use super::*;
|
||||
|
||||
const WORLD_PROBE: &str = include_str!("dom_event_worlds.js");
|
||||
|
||||
#[tokio::test]
|
||||
async fn isolated_event_default_view_uses_its_construction_window() {
|
||||
let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).unwrap();
|
||||
let mut vm =
|
||||
new_storage_page_task_executor_test_vm_with_loader("https://event-worlds.test/", &loader);
|
||||
vm.eval(
|
||||
"document.body.innerHTML = '<iframe></iframe>'; \
|
||||
const frame = document.querySelector('iframe'); \
|
||||
frame.srcdoc = '<button id=target></button>'; void frame.contentWindow; 'ready'",
|
||||
)
|
||||
.unwrap();
|
||||
vm.run_one_child_frame_task_executor_turn(
|
||||
ChildFrameSemanticTurnKind::RealmMaterialization,
|
||||
&loader,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
vm.drain_ready_page_task_executor_turns_for_setup(&loader, 128)
|
||||
.await
|
||||
.unwrap();
|
||||
let child = vm
|
||||
.live_child_default_runtime_realm_inventory()
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
.context_id;
|
||||
vm.eval_in_child_default_context(
|
||||
child,
|
||||
r#"
|
||||
document.getElementById('target').addEventListener('construction', function(event) {
|
||||
globalThis.constructionFacts = [
|
||||
event instanceof parent.Event, !(event instanceof Event),
|
||||
event.createdIn === undefined, event.target === this,
|
||||
event.currentTarget === this, this === document.getElementById('target')
|
||||
];
|
||||
}); 'ready'
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let isolated = vm
|
||||
.create_isolated_world("construction-world", false)
|
||||
.unwrap();
|
||||
vm.eval_in_isolated_context(
|
||||
isolated,
|
||||
r#"
|
||||
const event = new Event('construction'); event.createdIn = 'isolated';
|
||||
document.querySelector('iframe').contentDocument.getElementById('target').dispatchEvent(event);
|
||||
'dispatched'
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
vm.eval_in_child_default_context(child, "JSON.stringify(constructionFacts)")
|
||||
.unwrap(),
|
||||
"[true,true,true,true,true,true]"
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_world_probe(result: &str, label: &str) {
|
||||
let facts: serde_json::Value = serde_json::from_str(result).unwrap();
|
||||
assert_eq!(facts["errors"], serde_json::json!([]), "{label}: {facts}");
|
||||
|
||||
Reference in New Issue
Block a user