continue messageport, transferable, postmessage options

This commit is contained in:
Gregory Terzian 2019-06-26 00:25:48 +08:00
parent c3b17c1201
commit 2f8932a6a1
100 changed files with 2456 additions and 1171 deletions

View file

@ -30,6 +30,10 @@ DOMInterfaces = {
'weakReferenceable': True,
},
'MessagePort': {
'weakReferenceable': True,
},
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
'TestBinding': {
'inCompartments': ['PromiseAttribute', 'PromiseNativeHandler'],

View file

@ -6,7 +6,7 @@
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).
use crate::compartments::enter_realm;
use crate::dom::bindings::conversions::root_from_handleobject;
use crate::dom::bindings::conversions::{root_from_object, ToJSValConvertible};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
@ -14,6 +14,7 @@ use crate::dom::bindings::transferable::Transferable;
use crate::dom::blob::{Blob, BlobImpl};
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
use crate::script_runtime::JSContext as SafeJSContext;
use js::glue::CopyJSStructuredCloneData;
use js::glue::DeleteJSAutoStructuredCloneBuffer;
use js::glue::GetLengthOfJSStructuredCloneData;
@ -30,12 +31,15 @@ use js::jsapi::{JSObject, JS_ClearPendingException};
use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter};
use js::jsapi::{JS_ReadBytes, JS_WriteBytes};
use js::jsapi::{JS_ReadUint32Pair, JS_WriteUint32Pair};
use js::jsval::UndefinedValue;
use js::rust::wrappers::{JS_ReadStructuredClone, JS_WriteStructuredClone};
use js::rust::{Handle, HandleValue, MutableHandleValue};
use libc::size_t;
use js::rust::{CustomAutoRooterGuard, HandleValue, MutableHandleValue};
use msg::constellation_msg::MessagePortId;
use script_traits::transferable::MessagePortImpl;
use script_traits::StructuredSerializedData;
use std::collections::HashMap;
use std::os::raw;
use std::ptr;
use std::slice;
// TODO: Should we add Min and Max const to https://github.com/servo/rust-mozjs/blob/master/src/consts.rs?
// TODO: Determine for sure which value Min and Max should have.
@ -129,19 +133,24 @@ impl StructuredCloneReader {
unsafe fn read_blob(
cx: *mut JSContext,
r: *mut JSStructuredCloneReader,
sc_holder: &mut StructuredCloneHolder,
sc_holder: &mut StructuredDataHolder,
) -> *mut JSObject {
let structured_reader = StructuredCloneReader { r: r };
let blob_buffer = structured_reader.read_bytes();
let type_str = structured_reader.read_str();
let target_global = GlobalScope::from_context(cx);
let blob = Blob::new(
let read_blob = Blob::new(
&target_global,
BlobImpl::new_from_bytes(blob_buffer),
type_str,
);
let js_object = blob.reflector().get_jsobject().get();
sc_holder.blob = Some(blob);
let js_object = read_blob.reflector().get_jsobject().get();
match sc_holder {
StructuredDataHolder::Read { blob, .. } => {
*blob = Some(read_blob);
},
_ => panic!("Unexpected variant of StructuredDataHolder"),
}
js_object
}
@ -174,7 +183,7 @@ unsafe extern "C" fn read_callback(
"tag should be higher than StructuredCloneTags::Min"
);
if tag == StructuredCloneTags::DomBlob as u32 {
return read_blob(cx, r, &mut *(closure as *mut StructuredCloneHolder));
return read_blob(cx, r, &mut *(closure as *mut StructuredDataHolder));
}
return ptr::null_mut();
}
@ -185,7 +194,7 @@ unsafe extern "C" fn write_callback(
obj: RawHandleObject,
_closure: *mut raw::c_void,
) -> bool {
if let Ok(blob) = root_from_handleobject::<Blob>(Handle::from_raw(obj), cx) {
if let Ok(blob) = root_from_object::<Blob>(*obj, cx) {
return write_blob(blob, w).is_ok();
}
return false;
@ -193,39 +202,44 @@ unsafe extern "C" fn write_callback(
unsafe extern "C" fn read_transfer_callback(
cx: *mut JSContext,
r: *mut JSStructuredCloneReader,
_r: *mut JSStructuredCloneReader,
tag: u32,
content: *mut raw::c_void,
_content: *mut raw::c_void,
extra_data: u64,
closure: *mut raw::c_void,
return_object: RawMutableHandleObject,
) -> bool {
if tag == StructuredCloneTags::MessagePort as u32 {
<MessagePort as Transferable>::transfer_receive(cx, r, closure, content, extra_data, return_object)
} else {
false
let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
let owner = GlobalScope::from_context(cx);
if let Ok(_) = <MessagePort as Transferable>::transfer_receive(
&owner,
&mut sc_holder,
extra_data,
return_object,
) {
return true;
}
}
false
}
/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
unsafe extern "C" fn write_transfer_callback(
_cx: *mut JSContext,
cx: *mut JSContext,
obj: RawHandleObject,
closure: *mut raw::c_void,
tag: *mut u32,
ownership: *mut TransferableOwnership,
content: *mut *mut raw::c_void,
_content: *mut *mut raw::c_void,
extra_data: *mut u64,
) -> bool {
if let Ok(port) = root_from_handleobject::<MessagePort>(Handle::from_raw(obj)) {
if let Some(true) = port.detached() {
return false;
}
if let Ok(port) = root_from_object::<MessagePort>(*obj, cx) {
*tag = StructuredCloneTags::MessagePort as u32;
*ownership = TransferableOwnership::SCTAG_TMO_CUSTOM;
if port.transfer(closure, content, extra_data) {
port.set_detached(true);
let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
if let Ok(data) = port.transfer(&mut sc_holder) {
*extra_data = data;
return true;
}
}
@ -242,10 +256,13 @@ unsafe extern "C" fn free_transfer_callback(
}
unsafe extern "C" fn can_transfer_callback(
_cx: *mut JSContext,
_obj: RawHandleObject,
cx: *mut JSContext,
obj: RawHandleObject,
_closure: *mut raw::c_void,
) -> bool {
if let Ok(_port) = root_from_object::<MessagePort>(*obj, cx) {
return true;
}
false
}
@ -261,123 +278,143 @@ static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredClon
canTransfer: Some(can_transfer_callback),
};
struct StructuredCloneHolder {
blob: Option<DomRoot<Blob>>,
/// A data holder for results from, and inputs to, structured-data read/write operations.
/// https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data
pub enum StructuredDataHolder {
Read {
/// A deserialized blob, stored temporarily here to keep it rooted.
blob: Option<DomRoot<Blob>>,
/// A vec of transfer-received DOM ports,
/// to be made available to script through a message event.
message_ports: Option<Vec<DomRoot<MessagePort>>>,
/// A map of port implementations,
/// used as part of the "transfer-receiving" steps of ports,
/// to produce the DOM ports stored in `message_ports` above.
port_impls: Option<HashMap<MessagePortId, MessagePortImpl>>,
},
/// A data holder into which transferred ports
/// can be written as part of their transfer steps.
Write(Option<HashMap<MessagePortId, MessagePortImpl>>),
}
/// A buffer for a structured clone.
pub enum StructuredCloneData {
/// A non-serializable (default) variant
Struct(*mut u64, size_t),
/// A variant that can be serialized
Vector(Vec<u8>),
}
impl StructuredCloneData {
// TODO: should this be unsafe?
/// Writes a structured clone. Returns a `DataClone` error if that fails.
pub fn write(
cx: *mut JSContext,
message: HandleValue,
transfer: HandleValue,
) -> Fallible<StructuredCloneData> {
unsafe {
let scbuf = NewJSAutoStructuredCloneBuffer(
StructuredCloneScope::DifferentProcess,
&STRUCTURED_CLONE_CALLBACKS,
);
let scdata = &mut ((*scbuf).data_);
let policy = CloneDataPolicy {
// TODO: SAB?
sharedArrayBuffer_: false,
};
let result = JS_WriteStructuredClone(
cx,
message,
scdata,
StructuredCloneScope::DifferentProcess,
policy,
&STRUCTURED_CLONE_CALLBACKS,
ptr::null_mut(),
transfer,
);
if !result {
JS_ClearPendingException(cx);
return Err(Error::DataClone);
}
let nbytes = GetLengthOfJSStructuredCloneData(scdata);
let mut data = Vec::with_capacity(nbytes);
CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
data.set_len(nbytes);
DeleteJSAutoStructuredCloneBuffer(scbuf);
Ok(StructuredCloneData::Vector(data))
/// Writes a structured clone. Returns a `DataClone` error if that fails.
pub fn write(
cx: SafeJSContext,
message: HandleValue,
transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
) -> Fallible<StructuredSerializedData> {
unsafe {
rooted!(in(*cx) let mut val = UndefinedValue());
if let Some(transfer) = transfer {
transfer.to_jsval(*cx, val.handle_mut());
}
}
/// Converts a StructuredCloneData to Vec<u8> for inter-thread sharing
pub fn move_to_arraybuffer(self) -> Vec<u8> {
match self {
StructuredCloneData::Struct(data, nbytes) => unsafe {
slice::from_raw_parts(data as *mut u8, nbytes).to_vec()
},
StructuredCloneData::Vector(msg) => msg,
}
}
/// Reads a structured clone.
///
/// Panics if `JS_ReadStructuredClone` fails.
fn read_clone(
global: &GlobalScope,
data: *mut u64,
nbytes: size_t,
rval: MutableHandleValue,
) -> bool {
let cx = global.get_cx();
let _ac = enter_realm(&*global);
let mut sc_holder = StructuredCloneHolder { blob: None };
let mut sc_holder = StructuredDataHolder::Write(None);
let sc_holder_ptr = &mut sc_holder as *mut _;
unsafe {
let scbuf = NewJSAutoStructuredCloneBuffer(
StructuredCloneScope::DifferentProcess,
&STRUCTURED_CLONE_CALLBACKS,
);
let scdata = &mut ((*scbuf).data_);
WriteBytesToJSStructuredCloneData(data as *const u8, nbytes, scdata);
let result = JS_ReadStructuredClone(
*cx,
scdata,
JS_STRUCTURED_CLONE_VERSION,
StructuredCloneScope::DifferentProcess,
rval,
&STRUCTURED_CLONE_CALLBACKS,
sc_holder_ptr as *mut raw::c_void
);
DeleteJSAutoStructuredCloneBuffer(scbuf);
result
let scbuf = NewJSAutoStructuredCloneBuffer(
StructuredCloneScope::DifferentProcess,
&STRUCTURED_CLONE_CALLBACKS,
);
let scdata = &mut ((*scbuf).data_);
let policy = CloneDataPolicy {
// TODO: SAB?
sharedArrayBuffer_: false,
};
let result = JS_WriteStructuredClone(
*cx,
message,
scdata,
StructuredCloneScope::DifferentProcess,
policy,
&STRUCTURED_CLONE_CALLBACKS,
sc_holder_ptr as *mut raw::c_void,
val.handle(),
);
if !result {
JS_ClearPendingException(*cx);
return Err(Error::DataClone);
}
}
/// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone.
pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) -> bool {
match self {
StructuredCloneData::Vector(mut vec_msg) => {
let nbytes = vec_msg.len();
let data = vec_msg.as_mut_ptr() as *mut u64;
StructuredCloneData::read_clone(global, data, nbytes, rval)
}
StructuredCloneData::Struct(data, nbytes) => {
StructuredCloneData::read_clone(global, data, nbytes, rval)
}
}
let nbytes = GetLengthOfJSStructuredCloneData(scdata);
let mut data = Vec::with_capacity(nbytes);
CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
data.set_len(nbytes);
DeleteJSAutoStructuredCloneBuffer(scbuf);
let mut port_impls = match sc_holder {
StructuredDataHolder::Write(port_impls) => port_impls,
_ => panic!("Unexpected variant of StructuredDataHolder"),
};
let data = StructuredSerializedData {
serialized: data,
ports: port_impls.take(),
};
Ok(data)
}
}
unsafe impl Send for StructuredCloneData {}
/// Read structured serialized data, possibly containing transferred objects.
/// Returns a vec of rooted transfer-received ports, or an error.
pub fn read(
global: &GlobalScope,
mut data: StructuredSerializedData,
rval: MutableHandleValue,
) -> Result<Vec<DomRoot<MessagePort>>, ()> {
let cx = global.get_cx();
let _ac = enter_realm(&*global);
let mut sc_holder = StructuredDataHolder::Read {
blob: None,
message_ports: None,
port_impls: data.ports.take(),
};
let sc_holder_ptr = &mut sc_holder as *mut _;
unsafe {
let scbuf = NewJSAutoStructuredCloneBuffer(
StructuredCloneScope::DifferentProcess,
&STRUCTURED_CLONE_CALLBACKS,
);
let scdata = &mut ((*scbuf).data_);
WriteBytesToJSStructuredCloneData(
data.serialized.as_mut_ptr() as *const u8,
data.serialized.len(),
scdata,
);
let result = JS_ReadStructuredClone(
*cx,
scdata,
JS_STRUCTURED_CLONE_VERSION,
StructuredCloneScope::DifferentProcess,
rval,
&STRUCTURED_CLONE_CALLBACKS,
sc_holder_ptr as *mut raw::c_void,
);
DeleteJSAutoStructuredCloneBuffer(scbuf);
if result {
let (mut message_ports, port_impls) = match sc_holder {
StructuredDataHolder::Read {
message_ports,
port_impls,
..
} => (message_ports, port_impls),
_ => panic!("Unexpected variant of StructuredDataHolder"),
};
// Any transfer-received port-impls should have been taken out.
assert!(port_impls.is_none());
match message_ports.take() {
Some(ports) => return Ok(ports),
None => return Ok(Vec::with_capacity(0)),
}
}
Err(())
}
}

View file

@ -79,7 +79,8 @@ use media::WindowGLContext;
use metrics::{InteractiveMetrics, InteractiveWindow};
use mime::Mime;
use msg::constellation_msg::{
BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId,
BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId, PipelineId,
TopLevelBrowsingContextId,
};
use net_traits::filemanager_thread::RelativePos;
use net_traits::image::base::{Image, ImageMetadata};
@ -93,6 +94,7 @@ use profile_traits::mem::ProfilerChan as MemProfilerChan;
use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_layout_interface::rpc::LayoutRPC;
use script_layout_interface::OpaqueStyleAndLayoutData;
use script_traits::transferable::MessagePortImpl;
use script_traits::DrawAPaintImageResult;
use script_traits::{DocumentActivity, ScriptToConstellationChan, TimerEventId, TimerSource};
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
@ -154,6 +156,11 @@ pub unsafe trait JSTraceable {
unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>, Box<dyn EventLoopWaker>);
unsafe_no_jsmanaged_fields!(MessagePortImpl);
unsafe_no_jsmanaged_fields!(MessagePortId);
unsafe_no_jsmanaged_fields!(RefCell<Option<MessagePortId>>);
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
unsafe_no_jsmanaged_fields!(CSSError);
unsafe_no_jsmanaged_fields!(&'static Encoding);

View file

@ -4,26 +4,19 @@
//! Trait representing the concept of [transferable objects]
//! (https://html.spec.whatwg.org/multipage/#transferable-objects).
use crate::dom::bindings::reflector::DomObject;
use js::jsapi::{JSContext, JSStructuredCloneReader, MutableHandleObject};
use std::os::raw;
pub trait Transferable : DomObject {
fn transfer(
&self,
closure: *mut raw::c_void,
content: *mut *mut raw::c_void,
extra_data: *mut u64,
) -> bool;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::structuredclone::StructuredDataHolder;
use crate::dom::globalscope::GlobalScope;
use js::jsapi::MutableHandleObject;
pub trait Transferable: DomObject {
fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()>;
fn transfer_receive(
cx: *mut JSContext,
r: *mut JSStructuredCloneReader,
closure: *mut raw::c_void,
content: *mut raw::c_void,
owner: &DomRoot<GlobalScope>,
sc_holder: &mut StructuredDataHolder,
extra_data: u64,
return_object: MutableHandleObject,
) -> bool;
fn detached(&self) -> Option<bool> { None }
fn set_detached(&self, _value: bool) { }
fn transferable(&self) -> bool { false }
) -> Result<(), ()>;
}

View file

@ -10,10 +10,13 @@ use crate::dom::bindings::codegen::PrototypeList::{MAX_PROTO_CHAIN_LENGTH, PROTO
use crate::dom::bindings::conversions::{jsstring_to_str, private_from_proto_check};
use crate::dom::bindings::error::throw_invalid_this;
use crate::dom::bindings::inheritance::TopTypeId;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::trace_object;
use crate::dom::messageport::MessagePort;
use crate::dom::windowproxy;
use crate::script_runtime::JSContext as SafeJSContext;
use js::conversions::ToJSValConvertible;
use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper};
use js::glue::{GetCrossCompartmentWrapper, JS_GetReservedSlot, WrapperNew};
use js::glue::{UnwrapObjectDynamic, RUST_JSID_TO_INT, RUST_JSID_TO_STRING};
@ -22,7 +25,7 @@ use js::jsapi::HandleId as RawHandleId;
use js::jsapi::HandleObject as RawHandleObject;
use js::jsapi::MutableHandleObject as RawMutableHandleObject;
use js::jsapi::{AutoIdVector, CallArgs, DOMCallbacks, GetNonCCWObjectGlobal};
use js::jsapi::{Heap, JSAutoRealm, JSContext};
use js::jsapi::{Heap, JSAutoRealm, JSContext, JS_FreezeObject};
use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks};
use js::jsapi::{JS_EnumerateStandardClasses, JS_GetLatin1StringCharsAndLength};
use js::jsapi::{JS_IsExceptionPending, JS_IsGlobalObject};
@ -117,6 +120,19 @@ impl Clone for DOMJSClass {
}
unsafe impl Sync for DOMJSClass {}
/// Returns a JSVal representing a frozen array of ports
pub fn message_ports_to_frozen_array(
message_ports: &[DomRoot<MessagePort>],
cx: SafeJSContext,
) -> JSVal {
rooted!(in(*cx) let mut ports = UndefinedValue());
unsafe { message_ports.to_jsval(*cx, ports.handle_mut()) };
rooted!(in(*cx) let obj = ports.to_object());
unsafe { JS_FreezeObject(*cx, RawHandleObject::from(obj.handle())) };
*ports
}
/// Returns the ProtoOrIfaceArray for the given global object.
/// Fails if `global` is not a DOM global object.
pub fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray {