mirror of
https://github.com/lexmount/moli.git
synced 2026-10-08 16:01:05 +00:00
fix(html): deserialize clones in receiver realm
This commit is contained in:
@@ -301,8 +301,17 @@ pub(crate) fn structured_clone_value_with_options<'s>(
|
||||
value: v8::Local<'s, v8::Value>,
|
||||
options: v8::Local<'s, v8::Value>,
|
||||
) -> Option<v8::Local<'s, v8::Value>> {
|
||||
let payload = structured_serialize_value_with_options(scope, value, options)?;
|
||||
structured_deserialize_value(scope, &payload)
|
||||
}
|
||||
|
||||
pub(crate) fn structured_serialize_value_with_options<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
value: v8::Local<'s, v8::Value>,
|
||||
options: v8::Local<'s, v8::Value>,
|
||||
) -> Option<V8StructuredClonePayload> {
|
||||
let transfers = parse_structured_clone_options_transfer_list(scope, options)?;
|
||||
let payload = serialize_for_wire_for_runtime_with_transfers(
|
||||
serialize_for_wire_for_runtime_with_transfers(
|
||||
scope,
|
||||
value,
|
||||
&transfers.array_buffers,
|
||||
@@ -310,8 +319,7 @@ pub(crate) fn structured_clone_value_with_options<'s>(
|
||||
&transfers.readable_streams,
|
||||
&transfers.writable_streams,
|
||||
&transfers.transform_streams,
|
||||
)?;
|
||||
structured_deserialize_value(scope, &payload)
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn structured_clone_value_for_storage<'s>(
|
||||
|
||||
@@ -5,9 +5,59 @@ pub(in crate::context_bootstrap) fn window_structured_clone_callback<'s>(
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if let Some(value) = structured_clone_value_with_options(scope, args.get(0), args.get(1)) {
|
||||
rv.set(value);
|
||||
} else {
|
||||
let Some(host_ptr) = context_host_ptr_from_global_bridge(scope) else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
let receiver = match crate::native_bridge::WindowOperationReceiver::capture_and_authorize(
|
||||
scope,
|
||||
args.this(),
|
||||
unsafe { &*host_ptr },
|
||||
) {
|
||||
Ok(receiver) => receiver,
|
||||
Err(crate::native_bridge::WindowOperationReceiverCaptureError::IllegalInvocation) => {
|
||||
throw_type_error(scope, "Illegal invocation");
|
||||
return;
|
||||
}
|
||||
Err(crate::native_bridge::WindowOperationReceiverCaptureError::CrossOrigin) => {
|
||||
crate::native_bridge::throw_cross_origin_location_security_error(scope);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if args.length() < 1 {
|
||||
throw_type_error(
|
||||
scope,
|
||||
&crate::webidl::WebIdlError::missing_required(crate::webidl::Context::argument(
|
||||
"Window.structuredClone",
|
||||
1,
|
||||
))
|
||||
.to_string(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Web IDL converts `options`, and structured serialization can invoke
|
||||
// author getters, while the operation function's Realm is still current.
|
||||
// Only deserialization uses `this`'s relevant Realm.
|
||||
let Some(payload) = structured_serialize_value_with_options(scope, args.get(0), args.get(1))
|
||||
else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
let Some(binding) = receiver.resolve_live_binding(unsafe { &*host_ptr }) else {
|
||||
// Chromium returns undefined when the receiver's Window has already
|
||||
// been discarded; more importantly, no retired V8 context is entered.
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
let cloned = binding.with_current_scope(scope, host_ptr, |scope, _dispatch_scope| {
|
||||
structured_deserialize_value(scope, &payload).map(|value| v8::Global::new(scope, value))
|
||||
});
|
||||
let Some(Some(cloned)) = cloned else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
let cloned = v8::Local::new(scope, cloned);
|
||||
rv.set(cloned);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,82 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn structured_clone_deserializes_in_the_receiver_realm() {
|
||||
let mut vm = new_storage_test_vm("https://structured-clone-receiver-realm.test/");
|
||||
|
||||
vm.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const frame = document.createElement("iframe");
|
||||
(document.body || document.documentElement || document).appendChild(frame);
|
||||
globalThis.__structuredCloneRealmFrame = frame;
|
||||
return "ready";
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("structuredClone receiver Realm setup should evaluate");
|
||||
vm.drain_pending_child_frame_work_for_test();
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const frame = globalThis.__structuredCloneRealmFrame;
|
||||
const child = frame.contentWindow;
|
||||
const constructors = ["Object", "Array", "Date", "RegExp"];
|
||||
const intoTop = constructors.map(name => {
|
||||
const clone = child.structuredClone.call(window, new child[name]);
|
||||
return Object.getPrototypeOf(clone) === window[name].prototype;
|
||||
});
|
||||
const intoChild = constructors.map(name => {
|
||||
const clone = window.structuredClone.call(child, new window[name]);
|
||||
return Object.getPrototypeOf(clone) === child[name].prototype;
|
||||
});
|
||||
|
||||
const buffer = new child.ArrayBuffer(8);
|
||||
const transferred = child.structuredClone.call(window, buffer, {
|
||||
transfer: [buffer]
|
||||
});
|
||||
|
||||
const exceptionRealm = callback => {
|
||||
try {
|
||||
callback();
|
||||
return "missing";
|
||||
} catch (error) {
|
||||
return error instanceof child.TypeError ? "child" : "other";
|
||||
}
|
||||
};
|
||||
const illegalInvocation = exceptionRealm(() =>
|
||||
child.structuredClone.call({}, 1));
|
||||
const missingArgument = exceptionRealm(() =>
|
||||
child.structuredClone.call(window));
|
||||
|
||||
frame.remove();
|
||||
const detached = child.structuredClone("detached") === undefined;
|
||||
|
||||
return JSON.stringify({
|
||||
intoTop,
|
||||
intoChild,
|
||||
transfer: [
|
||||
buffer.byteLength,
|
||||
transferred.byteLength,
|
||||
transferred instanceof window.ArrayBuffer
|
||||
],
|
||||
illegalInvocation,
|
||||
missingArgument,
|
||||
detached
|
||||
});
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("cross-Realm structuredClone probe should evaluate");
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
r#"{"intoTop":[true,true,true,true],"intoChild":[true,true,true,true],"transfer":[0,8,true],"illegalInvocation":"child","missingArgument":"child","detached":true}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structured_clone_preserves_webassembly_module() {
|
||||
let mut vm = new_storage_test_vm("https://example.com/wasm-clone");
|
||||
|
||||
Reference in New Issue
Block a user