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

@ -34,6 +34,7 @@ tinyfiledialogs = "3.0"
app_units = "0.7"
backtrace = {version = "0.3", optional = true}
base64 = "0.10.1"
bincode = "1"
bitflags = "1.0"
bluetooth_traits = {path = "../bluetooth_traits"}
canvas_traits = {path = "../canvas_traits"}
@ -97,7 +98,6 @@ servo_config = {path = "../config"}
servo_geometry = {path = "../geometry" }
servo-media = {git = "https://github.com/servo/media"}
servo_rand = {path = "../rand"}
servo_remutex = {path = "../remutex"}
servo_url = {path = "../url"}
sparkle = "0.1"
smallvec = { version = "0.6", features = ["std", "union"] }

View file

@ -4,8 +4,9 @@
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::script_runtime::CommonScriptMsg;
use script_traits::StructuredSerializedData;
use servo_url::ImmutableOrigin;
/// Messages used to control the worker event loops
pub enum WorkerScriptMsg {
@ -13,9 +14,9 @@ pub enum WorkerScriptMsg {
Common(CommonScriptMsg),
/// Message sent through Worker.postMessage
DOMMessage {
origin: String,
data: StructuredCloneData,
}
origin: ImmutableOrigin,
data: StructuredSerializedData,
},
}
pub struct SimpleWorkerErrorHandler<T: DomObject> {

View file

@ -155,4 +155,7 @@ pub fn run_worker_event_loop<T, TimerMsg, WorkerMsg, Event>(
.upcast::<GlobalScope>()
.perform_a_microtask_checkpoint();
}
worker_scope
.upcast::<GlobalScope>()
.perform_a_message_port_garbage_collection_checkpoint();
}

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 {

View file

@ -10,13 +10,15 @@ use crate::dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThre
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
use crate::dom::bindings::error::{ErrorInfo, ErrorResult};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventtarget::EventTarget;
@ -36,10 +38,10 @@ use devtools_traits::DevtoolScriptControlMsg;
use dom_struct::dom_struct;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use js::jsapi::JSContext;
use js::jsapi::JS_AddInterruptCallback;
use js::jsapi::{Heap, JSContext, JSObject};
use js::jsval::UndefinedValue;
use js::rust::HandleValue;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId};
use net_traits::image_cache::ImageCache;
use net_traits::request::{CredentialsMode, Destination, ParserMetadata};
@ -467,15 +469,19 @@ impl DedicatedWorkerGlobalScope {
let target = self.upcast();
let _ac = enter_realm(self);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
assert!(data.read(scope.upcast(), message.handle_mut()));
MessageEvent::dispatch_jsval(
target,
scope.upcast(),
message.handle(),
Some(&origin),
None,
vec![],
);
if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut())
{
MessageEvent::dispatch_jsval(
target,
scope.upcast(),
message.handle(),
Some(&origin.ascii_serialization()),
None,
ports,
);
} else {
MessageEvent::dispatch_error(target, scope.upcast());
}
},
WorkerScriptMsg::Common(msg) => {
self.upcast::<WorkerGlobalScope>().process_event(msg);
@ -554,6 +560,32 @@ impl DedicatedWorkerGlobalScope {
))
.unwrap();
}
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn post_message_impl(
&self,
cx: SafeJSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
let data = structuredclone::write(cx, message, Some(transfer))?;
let worker = self.worker.borrow().as_ref().unwrap().clone();
let global_scope = self.upcast::<GlobalScope>();
let pipeline_id = global_scope.pipeline_id();
let origin = global_scope.origin().immutable().ascii_serialization();
let task = Box::new(task!(post_worker_message: move || {
Worker::handle_message(worker, origin, data);
}));
self.parent_sender
.send(CommonScriptMsg::Task(
WorkerEvent,
task,
Some(pipeline_id),
TaskSourceName::DOMManipulation,
))
.unwrap();
Ok(())
}
}
#[allow(unsafe_code)]
@ -567,26 +599,34 @@ unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool {
}
impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope {
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn PostMessage(&self, cx: SafeJSContext, message: HandleValue) -> ErrorResult {
rooted!(in(*cx) let transfer = UndefinedValue());
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
let worker = self.worker.borrow().as_ref().unwrap().clone();
let pipeline_id = self.global().pipeline_id();
let origin = self.global().origin().immutable().ascii_serialization();
let task = Box::new(task!(post_worker_message: move || {
Worker::handle_message(worker, origin, data);
}));
// TODO: Change this task source to a new `unshipped-port-message-queue` task source
self.parent_sender
.send(CommonScriptMsg::Task(
WorkerEvent,
task,
Some(pipeline_id),
TaskSourceName::DOMManipulation,
))
.unwrap();
Ok(())
/// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn PostMessage(
&self,
cx: SafeJSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
self.post_message_impl(cx, message, transfer)
}
/// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn PostMessage_(
&self,
cx: SafeJSContext,
message: HandleValue,
options: RootedTraceableBox<PostMessageOptions>,
) -> ErrorResult {
let mut rooted = CustomAutoRooter::new(
options
.transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
self.post_message_impl(cx, message, guard)
}
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-close

View file

@ -4,21 +4,23 @@
use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding;
use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding::DissimilarOriginWindowMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowPostMessageOptions;
use crate::dom::bindings::error::{Error, ErrorResult};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::str::USVString;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::dissimilaroriginlocation::DissimilarOriginLocation;
use crate::dom::globalscope::GlobalScope;
use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
use ipc_channel::ipc;
use js::jsapi::{Heap, JSObject};
use js::jsval::{JSVal, UndefinedValue};
use js::rust::HandleValue;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use msg::constellation_msg::PipelineId;
use script_traits::ScriptMsg;
use servo_url::ImmutableOrigin;
use script_traits::{ScriptMsg, StructuredSerializedData};
use servo_url::ServoUrl;
/// Represents a dissimilar-origin `Window` that exists in another script thread.
@ -133,29 +135,42 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
false
}
// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult {
// Step 3-5.
let origin = match &origin[..] {
"*" => None,
"/" => {
// TODO: Should be the origin of the incumbent settings object.
None
},
url => match ServoUrl::parse(&url) {
Ok(url) => Some(url.origin()),
Err(_) => return Err(Error::Syntax),
},
};
/// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
fn PostMessage(
&self,
cx: JSContext,
message: HandleValue,
target_origin: USVString,
mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
) -> ErrorResult {
if transfer.is_some() {
let mut rooted = CustomAutoRooter::new(transfer.take().unwrap());
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
self.post_message_impl(&target_origin, cx, message, transfer)
} else {
self.post_message_impl(&target_origin, cx, message, None)
}
}
// Step 1-2, 6-8.
// TODO(#12717): Should implement the `transfer` argument.
rooted!(in(*cx) let transfer = UndefinedValue());
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
/// https://html.spec.whatwg.org/multipage/#dom-window-postmessage-options
fn PostMessage_(
&self,
cx: JSContext,
message: HandleValue,
options: RootedTraceableBox<WindowPostMessageOptions>,
) -> ErrorResult {
let mut rooted = CustomAutoRooter::new(
options
.transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
// Step 9.
self.post_message(origin, data);
Ok(())
self.post_message_impl(&options.targetOrigin, cx, message, transfer)
}
// https://html.spec.whatwg.org/multipage/#dom-opener
@ -187,17 +202,54 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
}
impl DissimilarOriginWindow {
pub fn post_message(&self, origin: Option<ImmutableOrigin>, data: StructuredCloneData) {
/// https://html.spec.whatwg.org/multipage/#window-post-message-steps
fn post_message_impl(
&self,
target_origin: &USVString,
cx: JSContext,
message: HandleValue,
transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
) -> ErrorResult {
// Step 6-7.
let data = structuredclone::write(cx, message, transfer)?;
self.post_message(target_origin, data)
}
/// https://html.spec.whatwg.org/multipage/#window-post-message-steps
pub fn post_message(
&self,
target_origin: &USVString,
data: StructuredSerializedData,
) -> ErrorResult {
// Step 1.
let target = self.window_proxy.browsing_context_id();
// Step 2.
let incumbent = match GlobalScope::incumbent() {
None => return warn!("postMessage called with no incumbent global"),
None => panic!("postMessage called with no incumbent global"),
Some(incumbent) => incumbent,
};
let msg = ScriptMsg::PostMessage {
target: self.window_proxy.browsing_context_id(),
source: incumbent.pipeline_id(),
target_origin: origin,
data: data.move_to_arraybuffer(),
let source_origin = incumbent.origin().immutable().clone();
// Step 3-5.
let target_origin = match target_origin.0[..].as_ref() {
"*" => None,
"/" => Some(source_origin.clone()),
url => match ServoUrl::parse(&url) {
Ok(url) => Some(url.origin().clone()),
Err(_) => return Err(Error::Syntax),
},
};
let msg = ScriptMsg::PostMessage {
target,
source: incumbent.pipeline_id(),
source_origin,
target_origin,
data: data,
};
// Step 8
let _ = incumbent.script_to_constellation_chan().send(msg);
Ok(())
}
}

View file

@ -1917,10 +1917,11 @@ impl Document {
}
}
}
let global_scope = self.window.upcast::<GlobalScope>();
// Step 10, 14
// https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps
if !self.salvageable.get() {
// https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps
let global_scope = self.window.upcast::<GlobalScope>();
// Step 1 of clean-up steps.
global_scope.close_event_sources();
let msg = ScriptMsg::DiscardDocument;

View file

@ -236,7 +236,7 @@ impl EventSourceContext {
DOMString::from(self.origin.clone()),
None,
event_source.last_event_id.borrow().clone(),
vec![],
Vec::with_capacity(0),
)
};
// Step 7

View file

@ -10,10 +10,12 @@ use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::utils::message_ports_to_frozen_array;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::extendableevent::ExtendableEvent;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
@ -29,6 +31,7 @@ pub struct ExtendableMessageEvent {
data: Heap<JSVal>,
origin: DOMString,
lastEventId: DOMString,
ports: Vec<DomRoot<MessagePort>>,
}
impl ExtendableMessageEvent {
@ -40,12 +43,14 @@ impl ExtendableMessageEvent {
data: HandleValue,
origin: DOMString,
lastEventId: DOMString,
ports: Vec<DomRoot<MessagePort>>,
) -> DomRoot<ExtendableMessageEvent> {
let ev = Box::new(ExtendableMessageEvent {
event: ExtendableEvent::new_inherited(),
data: Heap::default(),
origin: origin,
lastEventId: lastEventId,
origin,
lastEventId,
ports,
});
let ev = reflect_dom_object(ev, global, ExtendableMessageEventBinding::Wrap);
{
@ -71,13 +76,19 @@ impl ExtendableMessageEvent {
init.data.handle(),
init.origin.clone().unwrap(),
init.lastEventId.clone().unwrap(),
vec![],
);
Ok(ev)
}
}
impl ExtendableMessageEvent {
pub fn dispatch_jsval(target: &EventTarget, scope: &GlobalScope, message: HandleValue) {
pub fn dispatch_jsval(
target: &EventTarget,
scope: &GlobalScope,
message: HandleValue,
ports: Vec<DomRoot<MessagePort>>,
) {
let Extendablemessageevent = ExtendableMessageEvent::new(
scope,
atom!("message"),
@ -86,6 +97,7 @@ impl ExtendableMessageEvent {
message,
DOMString::new(),
DOMString::new(),
ports,
);
Extendablemessageevent.upcast::<Event>().fire(target);
}
@ -111,4 +123,9 @@ impl ExtendableMessageEventMethods for ExtendableMessageEvent {
fn IsTrusted(&self) -> bool {
self.event.IsTrusted()
}
/// https://w3c.github.io/ServiceWorker/#extendablemessage-event-ports
fn Ports(&self, cx: JSContext) -> JSVal {
message_ports_to_frozen_array(self.ports.as_slice(), cx)
}
}

View file

@ -9,17 +9,21 @@ use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlo
use crate::dom::bindings::conversions::{root_from_object, root_from_object_static};
use crate::dom::bindings::error::{report_pending_exception, ErrorInfo};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::weakref::DOMTracker;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
use crate::dom::crypto::Crypto;
use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
use crate::dom::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventsource::EventSource;
use crate::dom::eventtarget::EventTarget;
use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::MessagePort;
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
use crate::dom::performance::Performance;
use crate::dom::window::Window;
@ -36,34 +40,40 @@ use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
use crate::task_source::port_message::PortMessageQueue;
use crate::task_source::remote_event::RemoteEventTaskSource;
use crate::task_source::websocket::WebsocketTaskSource;
use crate::task_source::TaskSource;
use crate::task_source::TaskSourceName;
use crate::timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle};
use crate::timers::{OneshotTimers, TimerCallback};
use content_security_policy::CspList;
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use dom_struct::dom_struct;
use ipc_channel::ipc::IpcSender;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::glue::{IsWrapper, UnwrapObjectDynamic};
use js::jsapi::JSObject;
use js::jsapi::{CurrentGlobalOrNull, GetNonCCWObjectGlobal};
use js::jsapi::{HandleObject, Heap};
use js::jsapi::{JSAutoRealm, JSContext};
use js::jsval::UndefinedValue;
use js::panic::maybe_resume_unwind;
use js::rust::wrappers::EvaluateUtf8;
use js::rust::{get_object_class, CompileOptionsWrapper, ParentRuntime, Runtime};
use js::rust::{HandleValue, MutableHandleValue};
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::{MessagePortId, MessagePortRouterId, PipelineId};
use net_traits::image_cache::ImageCache;
use net_traits::{CoreResourceThread, IpcSend, ResourceThreads};
use profile_traits::{mem as profile_mem, time as profile_time};
use script_traits::{MsDuration, ScriptToConstellationChan, TimerEvent};
use script_traits::transferable::MessagePortImpl;
use script_traits::{
MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg, ScriptToConstellationChan, TimerEvent,
};
use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource};
use servo_url::{MutableOrigin, ServoUrl};
use std::borrow::Cow;
use std::cell::Cell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::collections::{HashMap, VecDeque};
use std::ffi::CString;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
@ -85,6 +95,9 @@ pub struct GlobalScope {
crypto: MutNullableDom<Crypto>,
next_worker_id: Cell<WorkerId>,
/// The message-port router id for this global, if it is managing ports.
message_port_state: DomRefCell<MessagePortState>,
/// Pipeline id associated with this global.
pipeline_id: PipelineId,
@ -168,6 +181,77 @@ pub struct GlobalScope {
user_agent: Cow<'static, str>,
}
/// A wrapper for glue-code between the ipc router and the event-loop.
struct MessageListener {
canceller: TaskCanceller,
task_source: PortMessageQueue,
context: Trusted<GlobalScope>,
}
/// Data representing a message-port managed by this global.
#[derive(JSTraceable, MallocSizeOf)]
pub enum ManagedMessagePort {
/// We keep ports pending when they are first transfer-received,
/// and only add them, and ask the constellation to complete the transfer,
/// in a subsequent task if the port hasn't been re-transfered.
Pending(MessagePortImpl, WeakRef<MessagePort>),
/// A port who was transferred into, or initially created in, this realm,
/// and that hasn't been re-transferred in the same task it was noted.
Added(MessagePortImpl, WeakRef<MessagePort>),
}
/// State representing whether this global is currently managing messageports.
#[derive(JSTraceable, MallocSizeOf)]
pub enum MessagePortState {
/// The message-port router id for this global, and a map of managed ports.
Managed(
MessagePortRouterId,
HashMap<MessagePortId, ManagedMessagePort>,
),
/// This global is not managing any ports at this time.
UnManaged,
}
impl MessageListener {
/// A new message came in, handle it via a task enqueued on the event-loop.
/// A task is required, since we are using a trusted globalscope,
/// and we can only access the root from the event-loop.
fn notify(&self, msg: MessagePortMsg) {
match msg {
MessagePortMsg::CompleteTransfer(port_id, tasks) => {
let context = self.context.clone();
let _ = self.task_source.queue_with_canceller(
task!(process_complete_transfer: move || {
let global = context.root();
global.complete_port_transfer(port_id, tasks);
}),
&self.canceller,
);
},
MessagePortMsg::NewTask(port_id, task) => {
let context = self.context.clone();
let _ = self.task_source.queue_with_canceller(
task!(process_new_task: move || {
let global = context.root();
global.route_task_to_port(port_id, task);
}),
&self.canceller,
);
},
MessagePortMsg::RemoveMessagePort(port_id) => {
let context = self.context.clone();
let _ = self.task_source.queue_with_canceller(
task!(process_remove_message_port: move || {
let global = context.root();
global.remove_message_port(&port_id);
}),
&self.canceller,
);
},
}
}
}
impl GlobalScope {
pub fn new_inherited(
pipeline_id: PipelineId,
@ -184,6 +268,7 @@ impl GlobalScope {
user_agent: Cow<'static, str>,
) -> Self {
Self {
message_port_state: DomRefCell::new(MessagePortState::UnManaged),
eventtarget: EventTarget::new_inherited(),
crypto: Default::default(),
next_worker_id: Cell::new(WorkerId(0)),
@ -209,6 +294,397 @@ impl GlobalScope {
}
}
/// Complete the transfer of a message-port.
fn complete_port_transfer(&self, port_id: MessagePortId, tasks: VecDeque<PortMessageTask>) {
let should_start = if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
match message_ports.get_mut(&port_id) {
None => {
panic!("CompleteTransfer msg received in a global not managing the port.");
},
Some(ManagedMessagePort::Pending(_, _)) => {
panic!("CompleteTransfer msg received for a pending port.");
},
Some(ManagedMessagePort::Added(port_impl, _port)) => {
port_impl.complete_transfer(tasks);
port_impl.enabled()
},
}
} else {
return warn!("CompleteTransfer msg received in a global not managing any ports.");
};
if should_start {
self.start_message_port(&port_id);
}
}
/// Update our state to un-managed,
/// and tell the constellation to drop the sender to our message-port router.
pub fn remove_message_ports_router(&self) {
if let MessagePortState::Managed(router_id, _message_ports) =
&*self.message_port_state.borrow()
{
let _ = self
.script_to_constellation_chan()
.send(ScriptMsg::RemoveMessagePortRouter(router_id.clone()));
}
*self.message_port_state.borrow_mut() = MessagePortState::UnManaged;
}
/// <https://html.spec.whatwg.org/multipage/#entangle>
pub fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) {
if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
for (port_id, entangled_id) in &[(port1, port2), (port2, port1)] {
match message_ports.get_mut(&port_id) {
None => {
return warn!("entangled_ports called on a global not managing the port.");
},
Some(ManagedMessagePort::Pending(port_impl, dom_port)) => {
dom_port
.root()
.expect("Port to be entangled to not have been GC'ed")
.entangle(entangled_id.clone());
port_impl.entangle(entangled_id.clone());
},
Some(ManagedMessagePort::Added(port_impl, dom_port)) => {
dom_port
.root()
.expect("Port to be entangled to not have been GC'ed")
.entangle(entangled_id.clone());
port_impl.entangle(entangled_id.clone());
},
}
}
} else {
panic!("entangled_ports called on a global not managing any ports.");
}
let _ = self
.script_to_constellation_chan()
.send(ScriptMsg::EntanglePorts(port1, port2));
}
/// Remove all referrences to a port.
pub fn remove_message_port(&self, port_id: &MessagePortId) {
let is_empty = if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
match message_ports.remove(&port_id) {
None => panic!("remove_message_port called on a global not managing the port."),
Some(_) => message_ports.is_empty(),
}
} else {
return warn!("remove_message_port called on a global not managing any ports.");
};
if is_empty {
// Remove our port router,
// it will be setup again if we start managing ports again.
self.remove_message_ports_router();
}
}
/// Handle the transfer of a port in the current task.
pub fn mark_port_as_transferred(&self, port_id: &MessagePortId) -> MessagePortImpl {
if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
let mut port = match message_ports.remove(&port_id) {
None => {
panic!("mark_port_as_transferred called on a global not managing the port.")
},
Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
};
port.set_has_been_shipped();
let _ = self
.script_to_constellation_chan()
.send(ScriptMsg::MessagePortShipped(port_id.clone()));
port
} else {
panic!("mark_port_as_transferred called on a global not managing any ports.");
}
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
pub fn start_message_port(&self, port_id: &MessagePortId) {
if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
let port = match message_ports.get_mut(&port_id) {
None => panic!("start_message_port called on a unknown port."),
Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
};
if let Some(message_buffer) = port.start() {
for task in message_buffer {
let port_id = port_id.clone();
let this = Trusted::new(&*self);
let _ = self.port_message_queue().queue(
task!(process_pending_port_messages: move || {
let target_global = this.root();
target_global.route_task_to_port(port_id, task);
}),
&self,
);
}
}
} else {
return warn!("start_message_port called on a global not managing any ports.");
}
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
pub fn close_message_port(&self, port_id: &MessagePortId) {
if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
let port = match message_ports.get_mut(&port_id) {
None => panic!("close_message_port called on an unknown port."),
Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
};
port.close();
} else {
return warn!("close_message_port called on a global not managing any ports.");
}
}
/// <https://html.spec.whatwg.org/multipage/#message-port-post-message-steps>
// Steps 6 and 7
pub fn post_messageport_msg(&self, port_id: MessagePortId, task: PortMessageTask) {
if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
let port = match message_ports.get_mut(&port_id) {
None => panic!("post_messageport_msg called on an unknown port."),
Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
};
if let Some(entangled_id) = port.entangled_port_id() {
// Step 7
let this = Trusted::new(&*self);
let _ = self.port_message_queue().queue(
task!(post_message: move || {
let global = this.root();
// Note: we do this in a task, as this will ensure the global and constellation
// are aware of any transfer that might still take place in the current task.
global.route_task_to_port(entangled_id, task);
}),
self,
);
}
} else {
return warn!("post_messageport_msg called on a global not managing any ports.");
}
}
/// If we don't know about the port,
/// send the message to the constellation for routing.
fn re_route_port_task(&self, port_id: MessagePortId, task: PortMessageTask) {
let _ = self
.script_to_constellation_chan()
.send(ScriptMsg::RerouteMessagePort(port_id, task));
}
/// Route the task to be handled by the relevant port.
pub fn route_task_to_port(&self, port_id: MessagePortId, task: PortMessageTask) {
let should_dispatch = if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
if !message_ports.contains_key(&port_id) {
self.re_route_port_task(port_id, task);
return;
}
let (port_impl, dom_port) = match message_ports.get_mut(&port_id) {
None => panic!("route_task_to_port called for an unknown port."),
Some(ManagedMessagePort::Pending(port_impl, dom_port)) => (port_impl, dom_port),
Some(ManagedMessagePort::Added(port_impl, dom_port)) => (port_impl, dom_port),
};
// If the port is not enabled yet, or if is awaiting the completion of it's transfer,
// the task will be buffered and dispatched upon enablement or completion of the transfer.
if let Some(task_to_dispatch) = port_impl.handle_incoming(task) {
// Get a corresponding DOM message-port object.
let dom_port = match dom_port.root() {
Some(dom_port) => dom_port,
None => panic!("Messageport Gc'ed too early"),
};
Some((dom_port, task_to_dispatch))
} else {
None
}
} else {
self.re_route_port_task(port_id, task);
return;
};
if let Some((dom_port, PortMessageTask { origin, data })) = should_dispatch {
// Substep 3-4
rooted!(in(*self.get_cx()) let mut message_clone = UndefinedValue());
if let Ok(ports) = structuredclone::read(self, data, message_clone.handle_mut()) {
// Substep 6
// Dispatch the event, using the dom message-port.
MessageEvent::dispatch_jsval(
&dom_port.upcast(),
self,
message_clone.handle(),
Some(&origin.ascii_serialization()),
None,
ports,
);
} else {
// Step 4, fire messageerror event.
MessageEvent::dispatch_error(&dom_port.upcast(), self);
}
}
}
/// Check all ports that have been transfer-received in the previous task,
/// and complete their transfer if they haven't been re-transferred.
pub fn maybe_add_pending_ports(&self) {
if let MessagePortState::Managed(router_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
let to_be_added: Vec<MessagePortId> = message_ports
.iter()
.filter_map(|(id, port_info)| match port_info {
ManagedMessagePort::Pending(_, _) => Some(id.clone()),
_ => None,
})
.collect();
for id in to_be_added {
let (id, port_info) = message_ports
.remove_entry(&id)
.expect("Collected port-id to match an entry");
if let ManagedMessagePort::Pending(port_impl, dom_port) = port_info {
let _ = self
.script_to_constellation_chan()
.send(ScriptMsg::NewMessagePort(
router_id.clone(),
port_impl.message_port_id().clone(),
));
let new_port_info = ManagedMessagePort::Added(port_impl, dom_port);
let present = message_ports.insert(id, new_port_info);
assert!(present.is_none());
}
}
} else {
warn!("maybe_add_pending_ports called on a global not managing any ports.");
}
}
/// https://html.spec.whatwg.org/multipage/#ports-and-garbage-collection
pub fn perform_a_message_port_garbage_collection_checkpoint(&self) {
let is_empty = if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
let to_be_removed: Vec<MessagePortId> = message_ports
.iter()
.filter_map(|(id, port_info)| {
if let ManagedMessagePort::Added(_port_impl, dom_port) = port_info {
if dom_port.root().is_none() {
// Let the constellation know to drop this port and the one it is entangled with,
// and to forward this message to the script-process where the entangled is found.
let _ = self
.script_to_constellation_chan()
.send(ScriptMsg::RemoveMessagePort(id.clone()));
return Some(id.clone());
}
}
None
})
.collect();
for id in to_be_removed {
message_ports.remove(&id);
}
message_ports.is_empty()
} else {
false
};
if is_empty {
self.remove_message_ports_router();
}
}
/// Start tracking a message-port
pub fn track_message_port(&self, dom_port: &MessagePort, port_impl: Option<MessagePortImpl>) {
let mut current_state = self.message_port_state.borrow_mut();
if let MessagePortState::UnManaged = &*current_state {
// Setup a route for IPC, for messages from the constellation to our ports.
let (port_control_sender, port_control_receiver) =
ipc::channel().expect("ipc channel failure");
let context = Trusted::new(self);
let (task_source, canceller) = (
self.port_message_queue(),
self.task_canceller(TaskSourceName::PortMessage),
);
let listener = MessageListener {
canceller,
task_source,
context,
};
ROUTER.add_route(
port_control_receiver.to_opaque(),
Box::new(move |message| {
let msg = message.to();
match msg {
Ok(msg) => listener.notify(msg),
Err(err) => warn!("Error receiving a MessagePortMsg: {:?}", err),
}
}),
);
let router_id = MessagePortRouterId::new();
*current_state = MessagePortState::Managed(router_id.clone(), HashMap::new());
let _ = self
.script_to_constellation_chan()
.send(ScriptMsg::NewMessagePortRouter(
router_id,
port_control_sender,
));
}
if let MessagePortState::Managed(router_id, message_ports) = &mut *current_state {
if let Some(port_impl) = port_impl {
// We keep transfer-received ports as "pending",
// and only ask the constellation to complete the transfer
// if they're not re-shipped in the current task.
message_ports.insert(
dom_port.message_port_id().clone(),
ManagedMessagePort::Pending(port_impl, WeakRef::new(dom_port)),
);
// Queue a task to complete the transfer,
// unless the port is re-transferred in the current task.
let this = Trusted::new(&*self);
let _ = self.port_message_queue().queue(
task!(process_pending_port_messages: move || {
let target_global = this.root();
target_global.maybe_add_pending_ports();
}),
&self,
);
} else {
// If this is a newly-created port, let the constellation immediately know.
let port_impl = MessagePortImpl::new(dom_port.message_port_id().clone());
message_ports.insert(
dom_port.message_port_id().clone(),
ManagedMessagePort::Added(port_impl, WeakRef::new(dom_port)),
);
let _ = self
.script_to_constellation_chan()
.send(ScriptMsg::NewMessagePort(
router_id.clone(),
dom_port.message_port_id().clone(),
));
};
} else {
panic!("track_message_port should have first switched the state to managed.");
}
}
pub fn track_worker(&self, closing_worker: Arc<AtomicBool>) {
self.list_auto_close_worker
.borrow_mut()
@ -550,7 +1026,7 @@ impl GlobalScope {
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
return worker.websocket_task_source();
}
unreachable!()
unreachable!();
}
/// Evaluate JS code on this global scope.

View file

@ -11,7 +11,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::structuredclone;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
@ -27,7 +27,7 @@ use msg::constellation_msg::{HistoryStateId, TraversalDirection};
use net_traits::{CoreResourceMsg, IpcSend};
use profile_traits::ipc;
use profile_traits::ipc::channel;
use script_traits::ScriptMsg;
use script_traits::{ScriptMsg, StructuredSerializedData};
use servo_url::ServoUrl;
use std::cell::Cell;
@ -115,11 +115,16 @@ impl History {
};
match serialized_data {
Some(serialized_data) => {
Some(data) => {
let data = StructuredSerializedData {
serialized: data,
ports: None,
};
let global_scope = self.window.upcast::<GlobalScope>();
rooted!(in(*global_scope.get_cx()) let mut state = UndefinedValue());
StructuredCloneData::Vector(serialized_data)
.read(&global_scope, state.handle_mut());
if let Err(_) = structuredclone::read(&global_scope, data, state.handle_mut()) {
warn!("Error reading structuredclone data");
}
self.state.set(state.get());
},
None => {
@ -185,8 +190,7 @@ impl History {
// TODO: Step 4
// Step 5
rooted!(in(cx) let transfer = UndefinedValue());
let serialized_data = StructuredCloneData::write(*cx, data, transfer.handle())?.move_to_arraybuffer();
let serialized_data = structuredclone::write(cx, data, None)?;
let new_url: ServoUrl = match url {
// Step 6
@ -255,7 +259,7 @@ impl History {
};
let _ = self.window.upcast::<GlobalScope>().resource_threads().send(
CoreResourceMsg::SetHistoryState(state_id, serialized_data.clone()),
CoreResourceMsg::SetHistoryState(state_id, serialized_data.serialized.clone()),
);
// TODO: Step 9 Update current entry to represent a GET request
@ -267,7 +271,9 @@ impl History {
// Step 11
let global_scope = self.window.upcast::<GlobalScope>();
rooted!(in(*cx) let mut state = UndefinedValue());
StructuredCloneData::Vector(serialized_data).read(&global_scope, state.handle_mut());
if let Err(_) = structuredclone::read(&global_scope, serialized_data, state.handle_mut()) {
warn!("Error reading structuredclone data");
}
// Step 12
self.state.set(state.get());

View file

@ -4,7 +4,7 @@
use crate::dom::bindings::codegen::Bindings::MessageChannelBinding::{MessageChannelMethods, Wrap};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
@ -28,18 +28,24 @@ impl MessageChannel {
// Step 2
let port2 = MessagePort::new(&incumbent);
incumbent.track_message_port(&*port1, None);
incumbent.track_message_port(&*port2, None);
// Step 3
port1.entangle(&port2);
incumbent.entangle_ports(
port1.message_port_id().clone(),
port2.message_port_id().clone(),
);
// Steps 4-6
let channel = reflect_dom_object(Box::new(
MessageChannel {
let channel = reflect_dom_object(
Box::new(MessageChannel {
reflector_: Reflector::new(),
port1: Dom::from_ref(&port1),
port2: Dom::from_ref(&port2),
}),
global,
Wrap
Wrap,
);
// Step 7

View file

@ -11,6 +11,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::utils::message_ports_to_frozen_array;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
@ -18,10 +19,8 @@ use crate::dom::messageport::MessagePort;
use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
use js::jsapi::{Heap, JS_FreezeObject, JSContext, JSObject};
use js::jsapi::HandleObject as RawHandleObject;
use js::jsval::{JSVal, UndefinedValue};
use js::jsapi::{Heap, JSObject};
use js::jsval::JSVal;
use js::rust::HandleValue;
use servo_atoms::Atom;
use std::ptr::NonNull;
@ -108,7 +107,7 @@ impl MessageEvent {
init.origin.clone(),
source.as_ref().map(|source| &**source),
init.lastEventId.clone(),
init.ports.clone().unwrap_or(vec![])
init.ports.clone().unwrap_or(vec![]),
);
Ok(ev)
}
@ -136,6 +135,26 @@ impl MessageEvent {
);
messageevent.upcast::<Event>().fire(target);
}
pub fn dispatch_error(target: &EventTarget, scope: &GlobalScope) {
let init = MessageEventBinding::MessageEventInit::empty();
let source = init
.source
.as_ref()
.and_then(|inner| inner.as_ref().map(|source| source.window_proxy()));
let messageevent = MessageEvent::new(
scope,
atom!("messageerror"),
init.parent.bubbles,
init.parent.cancelable,
init.data.handle(),
init.origin.clone(),
source.as_ref().map(|source| &**source),
init.lastEventId.clone(),
init.ports.clone().unwrap_or(vec![]),
);
messageevent.upcast::<Event>().fire(target);
}
}
impl MessageEventMethods for MessageEvent {
@ -166,14 +185,8 @@ impl MessageEventMethods for MessageEvent {
self.event.IsTrusted()
}
#[allow(unsafe_code)]
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-ports>
unsafe fn Ports(&self, cx: *mut JSContext) -> JSVal {
rooted!(in(cx) let mut ports = UndefinedValue());
self.ports.to_jsval(cx, ports.handle_mut());
rooted!(in(cx) let obj = ports.to_object());
JS_FreezeObject(cx, RawHandleObject::from(obj.handle()));
*ports
fn Ports(&self, cx: JSContext) -> JSVal {
message_ports_to_frozen_array(self.ports.as_slice(), cx)
}
}

View file

@ -3,393 +3,343 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{MessagePortMethods, Wrap};
use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_object};
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{
MessagePortMethods, PostMessageOptions, Wrap,
};
use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::error::{Error, ErrorResult};
use crate::dom::bindings::inheritance::{Castable, HasParent};
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{DomObject, reflect_dom_object};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::bindings::structuredclone::{self, StructuredDataHolder};
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::transferable::Transferable;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageevent::MessageEvent;
use crate::task_source::TaskSource;
use crate::task_source::port_message::PortMessageQueue;
use js::jsapi::{JSContext, JSStructuredCloneReader, JSObject, JSTracer, MutableHandleObject};
use js::jsval::UndefinedValue;
use js::rust::{CustomAutoRooterGuard, HandleValue};
use servo_remutex::ReentrantMutex;
use crate::script_runtime::JSContext as SafeJSContext;
use dom_struct::dom_struct;
use js::jsapi::Heap;
use js::jsapi::{JSObject, MutableHandleObject};
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use msg::constellation_msg::{MessagePortId, MessagePortIndex, PipelineNamespaceId};
use script_traits::PortMessageTask;
use std::cell::{Cell, RefCell};
use std::collections::VecDeque;
use std::mem;
use std::os::raw;
use std::collections::HashMap;
use std::convert::TryInto;
use std::num::NonZeroU32;
use std::rc::Rc;
use std::sync::Arc;
// FIXME: This is wrong, we need to figure out a better way of collecting message port objects per transfer
thread_local! {
pub static TRANSFERRED_MESSAGE_PORTS: RefCell<Vec<DomRoot<MessagePort>>> = RefCell::new(Vec::new())
}
struct PortMessageTask {
origin: String,
data: Vec<u8>,
}
pub struct MessagePortInternal {
dom_port: RefCell<Option<Trusted<MessagePort>>>,
port_message_queue: RefCell<PortMessageQueue>,
enabled: Cell<bool>,
has_been_shipped: Cell<bool>,
entangled_port: RefCell<Option<Arc<ReentrantMutex<MessagePortInternal>>>>,
pending_port_messages: RefCell<VecDeque<PortMessageTask>>,
}
impl MessagePortInternal {
fn new(port_message_queue: PortMessageQueue) -> MessagePortInternal {
MessagePortInternal {
dom_port: RefCell::new(None),
port_message_queue: RefCell::new(port_message_queue),
enabled: Cell::new(false),
has_been_shipped: Cell::new(false),
entangled_port: RefCell::new(None),
pending_port_messages: RefCell::new(VecDeque::new()),
}
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
// Step 7 substeps
#[allow(unrooted_must_root)]
fn process_pending_port_messages(&self) {
let PortMessageTask { origin, data } = match self.pending_port_messages.borrow_mut().pop_front() {
Some(task) => task,
None => return,
};
// Substep 1
let final_target_port = self.dom_port.borrow().as_ref().unwrap().root();
// Substep 2
let target_global = final_target_port.global();
// Substep 3-4
rooted!(in(target_global.get_cx()) let mut message_clone = UndefinedValue());
let deserialize_result = StructuredCloneData::Vector(data).read(
&target_global,
message_clone.handle_mut()
);
if !deserialize_result {
return;
}
// Substep 5
let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| {
mem::replace(&mut *list.borrow_mut(), vec![])
});
// Substep 6
MessageEvent::dispatch_jsval(
final_target_port.upcast(),
&target_global,
message_clone.handle(),
Some(&origin),
None,
new_ports,
);
}
}
#[derive(DenyPublicFields, DomObject, MallocSizeOf)]
#[must_root]
#[repr(C)]
#[dom_struct]
/// The MessagePort used in the DOM.
pub struct MessagePort {
eventtarget: EventTarget,
message_port_id: MessagePortId,
entangled_port: RefCell<Option<MessagePortId>>,
detached: Cell<bool>,
#[ignore_malloc_size_of = "Defined in std"]
message_port_internal: Arc<ReentrantMutex<MessagePortInternal>>,
}
#[allow(unsafe_code)]
unsafe impl JSTraceable for MessagePort {
unsafe fn trace(&self, trc: *mut JSTracer) {
if !self.detached.get() {
self.eventtarget.trace(trc);
}
// Otherwise, do nothing.
}
}
impl HasParent for MessagePort {
type Parent = EventTarget;
fn as_parent(&self) -> &EventTarget {
&self.eventtarget
}
}
impl MessagePort {
fn new_inherited(global: &GlobalScope) -> MessagePort {
fn new_inherited(message_port_id: MessagePortId) -> MessagePort {
MessagePort {
eventtarget: EventTarget::new_inherited(),
entangled_port: RefCell::new(None),
detached: Cell::new(false),
message_port_internal: Arc::new(
ReentrantMutex::new(
MessagePortInternal::new(global.port_message_queue().clone())
)
),
}
}
fn new_transferred(message_port_internal: Arc<ReentrantMutex<MessagePortInternal>>) -> MessagePort {
MessagePort {
eventtarget: EventTarget::new_inherited(),
detached: Cell::new(false),
message_port_internal,
message_port_id,
}
}
/// <https://html.spec.whatwg.org/multipage/#create-a-new-messageport-object>
pub fn new(owner: &GlobalScope) -> DomRoot<MessagePort> {
let message_port = reflect_dom_object(Box::new(MessagePort::new_inherited(owner)), owner, Wrap);
{
let internal = message_port.message_port_internal.lock().unwrap();
*internal.dom_port.borrow_mut() = Some(Trusted::new(&*message_port));
}
message_port
let port_id = MessagePortId::new();
reflect_dom_object(Box::new(MessagePort::new_inherited(port_id)), owner, Wrap)
}
/// Create a new port for an incoming transfer-received one.
fn new_transferred(
owner: &GlobalScope,
transferred_port: MessagePortId,
entangled_port: Option<MessagePortId>,
) -> DomRoot<MessagePort> {
reflect_dom_object(
Box::new(MessagePort {
message_port_id: transferred_port,
eventtarget: EventTarget::new_inherited(),
detached: Cell::new(false),
entangled_port: RefCell::new(entangled_port),
}),
owner,
Wrap,
)
}
/// <https://html.spec.whatwg.org/multipage/#entangle>
pub fn entangle(&self, other: &MessagePort) {
{
let internal = self.message_port_internal.lock().unwrap();
*internal.entangled_port.borrow_mut() = Some(other.message_port_internal.clone());
}
let internal = other.message_port_internal.lock().unwrap();
*internal.entangled_port.borrow_mut() = Some(self.message_port_internal.clone());
pub fn entangle(&self, other_id: MessagePortId) {
*self.entangled_port.borrow_mut() = Some(other_id);
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
// Step 7 substeps
fn process_pending_port_messages(&self) {
if self.detached.get() { return; }
let internal = self.message_port_internal.lock().unwrap();
internal.process_pending_port_messages();
pub fn message_port_id(&self) -> &MessagePortId {
&self.message_port_id
}
pub fn detached(&self) -> bool {
self.detached.get()
}
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
fn set_onmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
let eventtarget = self.upcast::<EventTarget>();
eventtarget.set_event_handler_common("message", listener);
}
/// <https://html.spec.whatwg.org/multipage/#message-port-post-message-steps>
fn post_message_impl(
&self,
cx: SafeJSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
if self.detached.get() {
return Ok(());
}
// Step 1 is the transfer argument.
let target_port = self.entangled_port.borrow();
// Step 3
let mut doomed = false;
let ports = transfer
.iter()
.filter_map(|&obj| root_from_object::<MessagePort>(obj, *cx).ok());
for port in ports {
// Step 2
if port.message_port_id() == self.message_port_id() {
return Err(Error::DataClone);
}
// Step 4
if let Some(target_id) = target_port.as_ref() {
if port.message_port_id() == target_id {
doomed = true;
}
}
}
// Step 5
let data = structuredclone::write(cx, message, Some(transfer))?;
if doomed {
// TODO: The spec says to optionally report such a case to a dev console.
return Ok(());
}
// Step 6, done in MessagePortImpl.
let incumbent = match GlobalScope::incumbent() {
None => unreachable!("postMessage called with no incumbent global"),
Some(incumbent) => incumbent,
};
// Step 7
let task = PortMessageTask {
origin: incumbent.origin().immutable().clone(),
data,
};
// Have the global proxy this call to the corresponding MessagePortImpl.
self.global()
.post_messageport_msg(self.message_port_id().clone(), task);
Ok(())
}
}
impl Transferable for MessagePort {
/// <https://html.spec.whatwg.org/multipage/#message-ports:transfer-steps>
#[allow(unsafe_code)]
fn transfer(
&self,
_closure: *mut raw::c_void,
content: *mut *mut raw::c_void,
extra_data: *mut u64
) -> bool {
{
let internal = self.message_port_internal.lock().unwrap();
// Step 1
internal.has_been_shipped.set(true);
// Step 3
if let Some(ref other_port) = *internal.entangled_port.borrow() {
let entangled_internal = other_port.lock().unwrap();
// Substep 1
entangled_internal.has_been_shipped.set(true);
}; // This line MUST contain a semicolon, due to the strict drop check rule
fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()> {
if self.detached.get() {
return Err(());
}
unsafe {
// Steps 2, 3.2 and 4
*content = Arc::into_raw(self.message_port_internal.clone()) as *mut raw::c_void;
let port_impls = match sc_holder {
StructuredDataHolder::Write(port_impls) => port_impls,
_ => panic!("Unexpected variant of StructuredDataHolder"),
};
*extra_data = 0;
self.detached.set(true);
let id = self.message_port_id();
// 1. Run local transfer logic, and return the object to be transferred.
let transferred_port = self.global().mark_port_as_transferred(id);
// 2. Store the transferred object at a given key.
if let Some(ports) = port_impls.as_mut() {
ports.insert(id.clone(), transferred_port);
} else {
let mut ports = HashMap::new();
ports.insert(id.clone(), transferred_port);
*port_impls = Some(ports);
}
true
let PipelineNamespaceId(name_space) = id.clone().namespace_id;
let MessagePortIndex(index) = id.clone().index;
let index = index.get();
let mut big: [u8; 8] = [0; 8];
let name_space = name_space.to_ne_bytes();
let index = index.to_ne_bytes();
let (left, right) = big.split_at_mut(4);
left.copy_from_slice(&name_space);
right.copy_from_slice(&index);
// 3. Return a u64 representation of the key where the object is stored.
Ok(u64::from_ne_bytes(big))
}
/// https://html.spec.whatwg.org/multipage/#message-ports:transfer-receiving-steps
#[allow(unrooted_must_root, unsafe_code)]
fn transfer_receive(
cx: *mut JSContext,
_r: *mut JSStructuredCloneReader,
_closure: *mut raw::c_void,
content: *mut raw::c_void,
_extra_data: u64,
return_object: MutableHandleObject
) -> bool {
let internal = unsafe { Arc::from_raw(content as *const ReentrantMutex<MessagePortInternal>) };
let value = MessagePort::new_transferred(internal);
// Step 2
let owner = unsafe { GlobalScope::from_context(cx) };
let message_port = reflect_dom_object(Box::new(value), &*owner, Wrap);
{
let internal = message_port.message_port_internal.lock().unwrap();
// Step 1
internal.has_been_shipped.set(true);
let dom_port = Trusted::new(&*message_port);
internal.enabled.set(false);
*internal.dom_port.borrow_mut() = Some(dom_port);
*internal.port_message_queue.borrow_mut() = owner.port_message_queue().clone();
}
return_object.set(message_port.reflector().rootable().get());
TRANSFERRED_MESSAGE_PORTS.with(|list| {
list.borrow_mut().push(message_port);
});
true
}
fn detached(&self) -> Option<bool> {
Some(self.detached.get())
}
fn set_detached(&self, value: bool) {
self.detached.set(value);
}
fn transferable(&self) -> bool {
!self.detached.get()
}
}
impl MessagePortMethods for MessagePort {
#[allow(unsafe_code)]
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
unsafe fn PostMessage(
&self,
cx: *mut JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
) -> ErrorResult {
if self.detached.get() { return Ok(()); }
let internal = self.message_port_internal.lock().unwrap();
// Step 1
let target_port = internal.entangled_port.borrow();
// Step 3
let mut doomed = false;
rooted!(in(cx) let mut val = UndefinedValue());
let transfer = match *transfer {
Some(ref vec) => {
let ports = vec.iter().filter_map(|&obj| root_from_object::<MessagePort>(obj).ok());
for port in ports {
// Step 2
if Arc::ptr_eq(&port.message_port_internal, &self.message_port_internal) {
return Err(Error::DataClone);
}
// Step 4
if let Some(target) = target_port.as_ref() {
if Arc::ptr_eq(&port.message_port_internal, target) {
doomed = true;
}
}
}
vec.to_jsval(cx, val.handle_mut());
val
}
None => {
Vec::<*mut JSObject>::new().to_jsval(cx, val.handle_mut());
val
}
owner: &DomRoot<GlobalScope>,
sc_holder: &mut StructuredDataHolder,
extra_data: u64,
return_object: MutableHandleObject,
) -> Result<(), ()> {
let (message_ports, port_impls) = match sc_holder {
StructuredDataHolder::Read {
message_ports,
port_impls,
..
} => (message_ports, port_impls),
_ => panic!("Unexpected variant of StructuredDataHolder"),
};
// Step 5
let data = StructuredCloneData::write(cx, message, transfer.handle())?.move_to_arraybuffer();
// 1. Re-build the key for the storage location
// of the transferred object.
let big: [u8; 8] = extra_data.to_ne_bytes();
let (name_space, index) = big.split_at(4);
// Step 6
if target_port.is_none() || doomed { return Ok(()); }
let namespace_id = PipelineNamespaceId(u32::from_ne_bytes(
name_space
.try_into()
.expect("name_space to be a slice of four."),
));
let index = MessagePortIndex(
NonZeroU32::new(u32::from_ne_bytes(
index.try_into().expect("index to be a slice of four."),
))
.expect("Index to be non-zero"),
);
// Step 7
let task = PortMessageTask {
origin: self.global().origin().immutable().ascii_serialization(),
data,
let id = MessagePortId {
namespace_id,
index,
};
{
let target_port = target_port.as_ref().unwrap();
let target_internal = target_port.lock().unwrap();
target_internal.pending_port_messages.borrow_mut().push_back(task);
if target_internal.enabled.get() {
let target_port = target_port.clone();
let _ = target_internal.port_message_queue.borrow().queue(
task!(process_pending_port_messages: move || {
let internal = target_port.lock().unwrap();
internal.process_pending_port_messages();
}),
&self.global()
);
// 2. Get the transferred object from its storage, using the key.
// Assign the transfer-received port-impl, and total number of transferred ports.
let (ports_len, port_impl) = if let Some(ports) = port_impls.as_mut() {
let ports_len = ports.len();
let port_impl = ports.remove(&id).expect("Transferred port to be stored");
if ports.is_empty() {
*port_impls = None;
}
(ports_len, port_impl)
} else {
panic!("A messageport was transfer-received, yet the SC holder does not have any port impls");
};
let transferred_port =
MessagePort::new_transferred(&**owner, id.clone(), port_impl.entangled_port_id());
owner.track_message_port(&transferred_port, Some(port_impl));
return_object.set(transferred_port.reflector().rootable().get());
// Store the DOM port where it will be passed along to script in the message-event.
if let Some(ports) = message_ports.as_mut() {
ports.push(transferred_port);
} else {
let mut ports = Vec::with_capacity(ports_len);
ports.push(transferred_port);
*message_ports = Some(ports);
}
Ok(())
}
}
impl MessagePortMethods for MessagePort {
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
fn PostMessage(
&self,
cx: SafeJSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
if self.detached.get() {
return Ok(());
}
self.post_message_impl(cx, message, transfer)
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
fn PostMessage_(
&self,
cx: SafeJSContext,
message: HandleValue,
options: RootedTraceableBox<PostMessageOptions>,
) -> ErrorResult {
if self.detached.get() {
return Ok(());
}
let mut rooted = CustomAutoRooter::new(
options
.transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
self.post_message_impl(cx, message, guard)
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
fn Start(&self) {
let len = {
let internal = self.message_port_internal.lock().unwrap();
if internal.enabled.get() {
return;
}
internal.enabled.set(true);
let messages = internal.pending_port_messages.borrow();
messages.len()
};
let global = self.global();
for _ in 0..len {
let port = Trusted::new(self);
let _ = global.port_message_queue().queue(
task!(process_pending_port_messages: move || {
let this = port.root();
this.process_pending_port_messages();
}),
&global
);
if self.detached.get() {
return;
}
self.global().start_message_port(self.message_port_id());
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
fn Close(&self) {
// Step 1
self.detached.set(true);
// Step 2
let maybe_port = {
let internal = self.message_port_internal.lock().unwrap();
let mut maybe_port = internal.entangled_port.borrow_mut();
maybe_port.take()
};
if let Some(other) = maybe_port {
let other_internal = other.lock().unwrap();
*other_internal.entangled_port.borrow_mut() = None;
if self.detached.get() {
return;
}
self.detached.set(true);
self.global().close_message_port(self.message_port_id());
}
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
fn GetOnmessage(&self) -> Option<Rc<EventHandlerNonNull>> {
if self.detached.get() {
return None;
}
let eventtarget = self.upcast::<EventTarget>();
eventtarget.get_event_handler_common("message")
}
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
fn SetOnmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
self.Start();
let eventtarget = self.upcast::<EventTarget>();
eventtarget.set_event_handler_common("message", listener)
if self.detached.get() {
return;
}
self.set_onmessage(listener);
// Note: we cannot use the event_handler macro, due to the need to start the port.
self.global().start_message_port(self.message_port_id());
}
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessageerror>
event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
}

View file

@ -4,6 +4,7 @@
use crate::dom::abstractworker::SimpleWorkerErrorHandler;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
use crate::dom::bindings::codegen::Bindings::ServiceWorkerBinding::{
ServiceWorkerMethods, ServiceWorkerState, Wrap,
};
@ -13,15 +14,15 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::USVString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::JSContext;
use crate::task::TaskOnce;
use dom_struct::dom_struct;
use js::jsapi::JSContext;
use js::jsval::UndefinedValue;
use js::rust::HandleValue;
use js::jsapi::{Heap, JSObject};
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use script_traits::{DOMMessage, ScriptMsg};
use servo_url::ServoUrl;
use std::cell::Cell;
@ -79,6 +80,34 @@ impl ServiceWorker {
pub fn get_script_url(&self) -> ServoUrl {
ServoUrl::parse(&self.script_url.borrow().clone()).unwrap()
}
/// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
fn post_message_impl(
&self,
cx: JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
// Step 1
if let ServiceWorkerState::Redundant = self.state.get() {
return Err(Error::InvalidState);
}
// Step 7
let data = structuredclone::write(cx, message, Some(transfer))?;
let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
let msg_vec = DOMMessage {
origin: incumbent.origin().immutable().clone(),
data,
};
let _ = self
.global()
.script_to_constellation_chan()
.send(ScriptMsg::ForwardDOMMessage(
msg_vec,
self.scope_url.clone(),
));
Ok(())
}
}
impl ServiceWorkerMethods for ServiceWorker {
@ -92,27 +121,34 @@ impl ServiceWorkerMethods for ServiceWorker {
USVString(self.script_url.borrow().clone())
}
// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult {
// Step 1
if let ServiceWorkerState::Redundant = self.state.get() {
return Err(Error::InvalidState);
}
// Step 7
rooted!(in(*cx) let transfer = UndefinedValue());
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
let msg_vec = DOMMessage {
origin: self.global().origin().immutable().ascii_serialization(),
data: data.move_to_arraybuffer(),
};
let _ = self
.global()
.script_to_constellation_chan()
.send(ScriptMsg::ForwardDOMMessage(
msg_vec,
self.scope_url.clone()
));
Ok(())
/// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
fn PostMessage(
&self,
cx: JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
self.post_message_impl(cx, message, transfer)
}
/// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
fn PostMessage_(
&self,
cx: JSContext,
message: HandleValue,
options: RootedTraceableBox<PostMessageOptions>,
) -> ErrorResult {
let mut rooted = CustomAutoRooter::new(
options
.transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
self.post_message_impl(cx, message, guard)
}
// https://w3c.github.io/ServiceWorker/#service-worker-container-onerror-attribute

View file

@ -12,12 +12,14 @@ use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone;
use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::extendableevent::ExtendableEvent;
use crate::dom::extendablemessageevent::ExtendableMessageEvent;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageevent::MessageEvent;
use crate::dom::worker::TrustedWorkerAddress;
use crate::dom::workerglobalscope::WorkerGlobalScope;
use crate::fetch::load_whole_resource;
@ -414,8 +416,17 @@ impl ServiceWorkerGlobalScope {
let target = self.upcast();
let _ac = enter_realm(&*scope);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
assert!(data.read(scope.upcast(), message.handle_mut()));
ExtendableMessageEvent::dispatch_jsval(target, scope.upcast(), message.handle());
if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut())
{
ExtendableMessageEvent::dispatch_jsval(
target,
scope.upcast(),
message.handle(),
ports,
);
} else {
MessageEvent::dispatch_error(target, scope.upcast());
}
},
CommonWorker(WorkerScriptMsg::Common(msg)) => {
self.upcast::<WorkerGlobalScope>().process_event(msg);

View file

@ -5,9 +5,9 @@
// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
[Global=(Worker,DedicatedWorker), Exposed=DedicatedWorker]
/*sealed*/ interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
[Throws]
void postMessage(any message/*, optional sequence<Transferable> transfer*/);
attribute EventHandler onmessage;
[Throws] void postMessage(any message, sequence<object> transfer);
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
attribute EventHandler onmessage;
void close();
};

View file

@ -25,7 +25,8 @@ interface DissimilarOriginWindow : GlobalScope {
void close();
readonly attribute boolean closed;
[Throws] void postMessage(any message, DOMString targetOrigin);
[Throws] void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/);
[Throws] void postMessage(any message, optional WindowPostMessageOptions options = {});
attribute any opener;
void blur();
void focus();

View file

@ -12,7 +12,7 @@ interface ExtendableMessageEvent : ExtendableEvent {
readonly attribute DOMString origin;
readonly attribute DOMString lastEventId;
// [SameObject] readonly attribute (Client or ServiceWorker /*or MessagePort*/)? source;
// readonly attribute FrozenArray<MessagePort>? ports;
readonly attribute /*FrozenArray<MessagePort>*/any ports;
};
dictionary ExtendableMessageEventInit : ExtendableEventInit {

View file

@ -8,10 +8,16 @@
[Exposed=(Window,Worker)]
interface MessagePort : EventTarget {
[Throws] void postMessage(any message, optional sequence<object> transfer /*= []*/);
[Throws] void postMessage(any message, sequence<object> transfer /*= []*/);
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
void start();
void close();
// event handlers
attribute EventHandler onmessage;
attribute EventHandler onmessageerror;
};
dictionary PostMessageOptions {
sequence<object> transfer;
};

View file

@ -7,7 +7,8 @@
interface ServiceWorker : EventTarget {
readonly attribute USVString scriptURL;
readonly attribute ServiceWorkerState state;
[Throws] void postMessage(any message/*, optional sequence<object> transfer = []*/);
[Throws] void postMessage(any message, sequence<object> transfer);
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
// event
attribute EventHandler onstatechange;

View file

@ -65,6 +65,8 @@
[Throws]
void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/);
[Throws]
void postMessage(any message, optional WindowPostMessageOptions options = {});
// also has obsolete members
};
@ -172,3 +174,8 @@ partial interface Window {
[Pref="css.animations.testing.enabled"]
readonly attribute unsigned long runningAnimationCount;
};
dictionary WindowPostMessageOptions {
USVString targetOrigin = "/";
sequence<object> transfer;
};

View file

@ -14,8 +14,8 @@ interface Worker : EventTarget {
[Throws] constructor(USVString scriptURL, optional WorkerOptions options = {});
void terminate();
[Throws] void postMessage(any message/*, sequence<object> transfer*/);
// void postMessage(any message, optional PostMessageOptions options);
[Throws] void postMessage(any message, sequence<object> transfer);
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
attribute EventHandler onmessage;
attribute EventHandler onmessageerror;
};

View file

@ -13,11 +13,10 @@ use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryLi
use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState;
use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
self, FrameRequestCallback, WindowMethods,
self, FrameRequestCallback, WindowMethods, WindowPostMessageOptions,
};
use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString;
use crate::dom::bindings::conversions::ToJSValConvertible;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
@ -25,7 +24,7 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::utils::{GlobalStaticData, WindowProxyHandler};
use crate::dom::bindings::weakref::DOMTracker;
@ -44,7 +43,6 @@ use crate::dom::location::Location;
use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
use crate::dom::mediaquerylistevent::MediaQueryListEvent;
use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::TRANSFERRED_MESSAGE_PORTS;
use crate::dom::navigator::Navigator;
use crate::dom::node::{document_from_node, from_untrusted_node_address, Node, NodeDamage};
use crate::dom::performance::Performance;
@ -81,6 +79,7 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
use ipc_channel::ipc::{channel, IpcSender};
use ipc_channel::router::ROUTER;
use js::jsapi::Heap;
use js::jsapi::JSAutoRealm;
use js::jsapi::JSObject;
use js::jsapi::JSPROP_ENUMERATE;
@ -88,7 +87,7 @@ use js::jsapi::{GCReason, JS_GC};
use js::jsval::UndefinedValue;
use js::jsval::{JSVal, NullValue};
use js::rust::wrappers::JS_DefineProperty;
use js::rust::{CustomAutoRooterGuard, HandleValue};
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use media::WindowGLContext;
use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
@ -107,7 +106,10 @@ use script_layout_interface::rpc::{
use script_layout_interface::{PendingImageState, TrustedNodeAddress};
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use script_traits::{ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData};
use script_traits::{ScriptMsg, ScriptToConstellationChan, ScrollState, TimerEvent, TimerEventId};
use script_traits::{
ScriptMsg, ScriptToConstellationChan, ScrollState, StructuredSerializedData, TimerEvent,
TimerEventId,
};
use script_traits::{TimerSchedulerMsg, WindowSizeData, WindowSizeType};
use selectors::attr::CaseSensitivity;
use servo_geometry::{f32_rect_to_au_rect, MaxRect};
@ -976,31 +978,53 @@ impl WindowMethods for Window {
&self,
cx: JSContext,
message: HandleValue,
origin: USVString,
transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
target_origin: USVString,
mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
) -> ErrorResult {
let source_global = GlobalScope::incumbent().expect("no incumbent global??");
let source = source_global.as_window();
let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
let source = incumbent.as_window();
let source_origin = source.Document().origin().immutable().clone();
// Step 3-5.
let origin = match &origin.0[..] {
"*" => None,
"/" => Some(source.Document().origin().immutable().clone()),
url => match ServoUrl::parse(&url) {
Ok(url) => Some(url.origin().clone()),
Err(_) => return Err(Error::Syntax),
},
};
if transfer.is_some() {
let mut rooted = CustomAutoRooter::new(transfer.take().unwrap());
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
} else {
self.post_message_impl(&target_origin, source_origin, source, cx, message, None)
}
}
// Step 1-2, 6-8.
rooted!(in(*cx) let mut val = UndefinedValue());
(*transfer).as_ref().unwrap_or(&Vec::new()).to_jsval(*cx, val.handle_mut());
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
fn PostMessage_(
&self,
cx: JSContext,
message: HandleValue,
options: RootedTraceableBox<WindowPostMessageOptions>,
) -> ErrorResult {
let mut rooted = CustomAutoRooter::new(
options
.transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
let data = StructuredCloneData::write(*cx, message, val.handle())?;
let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
let source = incumbent.as_window();
// Step 9.
self.post_message(origin, &*source.window_proxy(), data);
Ok(())
let source_origin = source.Document().origin().immutable().clone();
self.post_message_impl(
&options.targetOrigin,
source_origin,
source,
cx,
message,
transfer,
)
}
// https://html.spec.whatwg.org/multipage/#dom-window-captureevents
@ -1298,6 +1322,34 @@ impl WindowMethods for Window {
}
impl Window {
/// https://html.spec.whatwg.org/multipage/#window-post-message-steps
fn post_message_impl(
&self,
target_origin: &USVString,
source_origin: ImmutableOrigin,
source: &Window,
cx: JSContext,
message: HandleValue,
transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
) -> ErrorResult {
// Step 1-2, 6-8.
let data = structuredclone::write(cx, message, transfer)?;
// Step 3-5.
let target_origin = match target_origin.0[..].as_ref() {
"*" => None,
"/" => Some(source_origin.clone()),
url => match ServoUrl::parse(&url) {
Ok(url) => Some(url.origin().clone()),
Err(_) => return Err(Error::Syntax),
},
};
// Step 9.
self.post_message(target_origin, source_origin, &*source.window_proxy(), data);
Ok(())
}
// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
pub fn paint_worklet(&self) -> DomRoot<Worklet> {
self.paint_worklet.or_init(|| self.new_paint_worklet())
@ -1344,6 +1396,9 @@ impl Window {
// thread, informing it that it can safely free the memory.
self.Document().upcast::<Node>().teardown();
// Tell the constellation to drop the sender to our message-port router, if there is any.
self.upcast::<GlobalScope>().remove_message_ports_router();
// Clean up any active promises
// https://github.com/servo/servo/issues/15318
if let Some(custom_elements) = self.custom_element_registry.get() {
@ -2345,8 +2400,9 @@ impl Window {
pub fn post_message(
&self,
target_origin: Option<ImmutableOrigin>,
source_origin: ImmutableOrigin,
source: &WindowProxy,
serialize_with_transfer_result: StructuredCloneData,
data: StructuredSerializedData,
) {
let this = Trusted::new(self);
let source = Trusted::new(source);
@ -2367,26 +2423,23 @@ impl Window {
let obj = this.reflector().get_jsobject();
let _ac = JSAutoRealm::new(*cx, obj.get());
rooted!(in(*cx) let mut message_clone = UndefinedValue());
assert!(serialize_with_transfer_result.read(
this.upcast(),
message_clone.handle_mut(),
));
// Step 7.6.
let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| {
mem::replace(&mut *list.borrow_mut(), vec![])
});
// Step 7.7.
// TODO(#12719): Set the other attributes.
MessageEvent::dispatch_jsval(
this.upcast(),
this.upcast(),
message_clone.handle(),
Some(&document.origin().immutable().ascii_serialization()),
Some(&*source),
new_ports,
);
if let Ok(ports) = structuredclone::read(this.upcast(), data, message_clone.handle_mut()) {
// Step 7.6, 7.7
MessageEvent::dispatch_jsval(
this.upcast(),
this.upcast(),
message_clone.handle(),
Some(&source_origin.ascii_serialization()),
Some(&*source),
ports,
);
} else {
// Step 4, fire messageerror.
MessageEvent::dispatch_error(
this.upcast(),
this.upcast(),
);
}
});
// FIXME(nox): Why are errors silenced here?
// TODO(#12718): Use the "posted message task source".

View file

@ -5,6 +5,7 @@
use crate::compartments::enter_realm;
use crate::dom::abstractworker::SimpleWorkerErrorHandler;
use crate::dom::abstractworker::WorkerScriptMsg;
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
use crate::dom::bindings::codegen::Bindings::WorkerBinding;
use crate::dom::bindings::codegen::Bindings::WorkerBinding::{WorkerMethods, WorkerOptions};
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
@ -13,7 +14,8 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::USVString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::dedicatedworkerglobalscope::{
DedicatedWorkerGlobalScope, DedicatedWorkerScriptMsg,
};
@ -27,10 +29,10 @@ use crossbeam_channel::{unbounded, Sender};
use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg};
use dom_struct::dom_struct;
use ipc_channel::ipc;
use js::jsapi::JS_RequestInterruptCallback;
use js::jsapi::{Heap, JSObject, JS_RequestInterruptCallback};
use js::jsval::UndefinedValue;
use js::rust::HandleValue;
use script_traits::WorkerScriptLoadOrigin;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use script_traits::{StructuredSerializedData, WorkerScriptLoadOrigin};
use std::cell::Cell;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
@ -140,7 +142,7 @@ impl Worker {
pub fn handle_message(
address: TrustedWorkerAddress,
origin: String,
data: StructuredCloneData,
data: StructuredSerializedData,
) {
let worker = address.root();
@ -152,21 +154,34 @@ impl Worker {
let target = worker.upcast();
let _ac = enter_realm(target);
rooted!(in(*global.get_cx()) let mut message = UndefinedValue());
assert!(data.read(&global, message.handle_mut()));
MessageEvent::dispatch_jsval(target, &global, message.handle(), Some(&origin), None, vec![]);
if let Ok(ports) = structuredclone::read(&global, data, message.handle_mut()) {
MessageEvent::dispatch_jsval(
target,
&global,
message.handle(),
Some(&origin),
None,
ports,
);
} else {
// Step 4 of the "port post message steps" of the implicit messageport, fire messageerror.
MessageEvent::dispatch_error(target, &global);
}
}
pub fn dispatch_simple_error(address: TrustedWorkerAddress) {
let worker = address.root();
worker.upcast().fire_event(atom!("error"));
}
}
impl WorkerMethods for Worker {
// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult {
rooted!(in(*cx) let transfer = UndefinedValue());
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
/// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn post_message_impl(
&self,
cx: JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
let data = structuredclone::write(cx, message, Some(transfer))?;
let address = Trusted::new(self);
// NOTE: step 9 of https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage
@ -174,12 +189,44 @@ impl WorkerMethods for Worker {
let _ = self.sender.send(DedicatedWorkerScriptMsg::CommonWorker(
address,
WorkerScriptMsg::DOMMessage {
origin: self.global().origin().immutable().ascii_serialization(),
origin: self.global().origin().immutable().clone(),
data,
},
));
Ok(())
}
}
impl WorkerMethods for Worker {
/// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
fn PostMessage(
&self,
cx: JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
self.post_message_impl(cx, message, transfer)
}
/// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
fn PostMessage_(
&self,
cx: JSContext,
message: HandleValue,
options: RootedTraceableBox<PostMessageOptions>,
) -> ErrorResult {
let mut rooted = CustomAutoRooter::new(
options
.transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
self.post_message_impl(cx, message, guard)
}
#[allow(unsafe_code)]
// https://html.spec.whatwg.org/multipage/#terminate-a-worker

View file

@ -38,7 +38,6 @@ use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::ThreadLocalStackRoots;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::bindings::utils::WRAP_CALLBACKS;
use crate::dom::customelementregistry::{
@ -133,6 +132,7 @@ use script_traits::CompositorEvent::{
CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent,
WheelEvent,
};
use script_traits::StructuredSerializedData;
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{
DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement,
@ -1589,6 +1589,11 @@ impl ScriptThread {
continue;
}
let window = document.window();
window
.upcast::<GlobalScope>()
.perform_a_message_port_garbage_collection_checkpoint();
let pending_reflows = window.get_pending_reflow_count();
if pending_reflows > 0 {
window.reflow(ReflowGoal::Full, ReflowReason::ImageLoaded);
@ -1867,12 +1872,14 @@ impl ScriptThread {
source: source_pipeline_id,
source_browsing_context,
target_origin: origin,
source_origin,
data,
} => self.handle_post_message_msg(
target_pipeline_id,
source_pipeline_id,
source_browsing_context,
origin,
source_origin,
data,
),
ConstellationControlMsg::UpdatePipelineId(
@ -2525,7 +2532,8 @@ impl ScriptThread {
source_pipeline_id: PipelineId,
source_browsing_context: TopLevelBrowsingContextId,
origin: Option<ImmutableOrigin>,
data: Vec<u8>,
source_origin: ImmutableOrigin,
data: StructuredSerializedData,
) {
match { self.documents.borrow().find_window(pipeline_id) } {
None => return warn!("postMessage after target pipeline {} closed.", pipeline_id),
@ -2547,7 +2555,7 @@ impl ScriptThread {
Some(source) => source,
};
// FIXME(#22512): enqueues a task; unnecessary delay.
window.post_message(origin, &*source, StructuredCloneData::Vector(data))
window.post_message(origin, source_origin, &*source, data)
},
}
}

View file

@ -8,7 +8,6 @@
//! active_workers map
use crate::dom::abstractworker::WorkerScriptMsg;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::serviceworkerglobalscope::{ServiceWorkerGlobalScope, ServiceWorkerScriptMsg};
use crate::dom::serviceworkerregistration::longest_prefix_match;
use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
@ -136,7 +135,6 @@ impl ServiceWorkerManager {
fn forward_message(&self, msg: DOMMessage, sender: &Sender<ServiceWorkerScriptMsg>) {
let DOMMessage { origin, data } = msg;
let data = StructuredCloneData::Vector(data);
let _ = sender.send(ServiceWorkerScriptMsg::CommonWorker(
WorkerScriptMsg::DOMMessage { origin, data },
));

View file

@ -9,7 +9,7 @@ use msg::constellation_msg::PipelineId;
use std::fmt;
#[derive(JSTraceable)]
pub struct PortMessageQueue(pub Box<ScriptChan + Send + 'static>, pub PipelineId);
pub struct PortMessageQueue(pub Box<dyn ScriptChan + Send + 'static>, pub PipelineId);
impl Clone for PortMessageQueue {
fn clone(&self) -> PortMessageQueue {
@ -26,11 +26,7 @@ impl fmt::Debug for PortMessageQueue {
impl TaskSource for PortMessageQueue {
const NAME: TaskSourceName = TaskSourceName::PortMessage;
fn queue_with_canceller<T>(
&self,
task: T,
canceller: &TaskCanceller,
) -> Result<(), ()>
fn queue_with_canceller<T>(&self, task: T, canceller: &TaskCanceller) -> Result<(), ()>
where
T: TaskOnce + 'static,
{