Auto merge of #23637 - gterzian:continue-message-port, r=jdm

Continue message port

<!-- Please describe your changes on the following line: -->
Fixes #7457.
Fixes #12715.
Fixes #12717.
Fixes #16095.
Fixes #18969.

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [ ] `./mach build -d` does not report any errors
- [ ] `./mach test-tidy` does not report any errors
- [ ] These changes fix #___ (GitHub issue number if applicable)

<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because ___

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23637)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-10-19 13:19:01 -04:00 committed by GitHub
commit a905916ede
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
113 changed files with 2792 additions and 787 deletions

View file

@ -4,15 +4,19 @@
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 {
/// Common variants associated with the script messages
Common(CommonScriptMsg),
/// Message sent through Worker.postMessage
DOMMessage(StructuredCloneData),
DOMMessage {
origin: ImmutableOrigin,
data: StructuredSerializedData,
},
}
pub struct SimpleWorkerErrorHandler<T: DomObject> {

View file

@ -75,7 +75,7 @@ impl ScriptPort for Receiver<DedicatedWorkerScriptMsg> {
};
match common_msg {
WorkerScriptMsg::Common(script_msg) => Ok(script_msg),
WorkerScriptMsg::DOMMessage(_) => panic!("unexpected worker event message!"),
WorkerScriptMsg::DOMMessage { .. } => panic!("unexpected worker event message!"),
}
}
}
@ -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

@ -154,6 +154,7 @@ pub mod settings_stack;
pub mod str;
pub mod structuredclone;
pub mod trace;
pub mod transferable;
pub mod utils;
pub mod weakref;
pub mod xmlname;

View file

@ -6,12 +6,15 @@
//! (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;
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;
@ -28,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.
@ -44,6 +50,7 @@ enum StructuredCloneTags {
/// To support additional types, add new tags with values incremented from the last one before Max.
Min = 0xFFFF8000,
DomBlob = 0xFFFF8001,
MessagePort = 0xFFFF8002,
Max = 0xFFFFFFFF,
}
@ -126,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
}
@ -171,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();
}
@ -182,33 +194,55 @@ 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;
}
unsafe extern "C" fn read_transfer_callback(
_cx: *mut JSContext,
cx: *mut JSContext,
_r: *mut JSStructuredCloneReader,
_tag: u32,
tag: u32,
_content: *mut raw::c_void,
_extra_data: u64,
_closure: *mut raw::c_void,
_return_object: RawMutableHandleObject,
extra_data: u64,
closure: *mut raw::c_void,
return_object: RawMutableHandleObject,
) -> bool {
if tag == StructuredCloneTags::MessagePort as u32 {
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,
_obj: RawHandleObject,
_closure: *mut raw::c_void,
_tag: *mut u32,
_ownership: *mut TransferableOwnership,
cx: *mut JSContext,
obj: RawHandleObject,
closure: *mut raw::c_void,
tag: *mut u32,
ownership: *mut TransferableOwnership,
_content: *mut *mut raw::c_void,
_extra_data: *mut u64,
extra_data: *mut u64,
) -> bool {
if let Ok(port) = root_from_object::<MessagePort>(*obj, cx) {
*tag = StructuredCloneTags::MessagePort as u32;
*ownership = TransferableOwnership::SCTAG_TMO_CUSTOM;
let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
if let Ok(data) = port.transfer(&mut sc_holder) {
*extra_data = data;
return true;
}
}
false
}
@ -222,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
}
@ -241,112 +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) -> 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(),
HandleValue::undefined(),
);
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) {
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);
assert!(JS_ReadStructuredClone(
*cx,
scdata,
JS_STRUCTURED_CLONE_VERSION,
StructuredCloneScope::DifferentProcess,
rval,
&STRUCTURED_CLONE_CALLBACKS,
sc_holder_ptr as *mut raw::c_void
));
DeleteJSAutoStructuredCloneBuffer(scbuf);
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) {
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

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Trait representing the concept of [transferable objects]
//! (https://html.spec.whatwg.org/multipage/#transferable-objects).
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(
owner: &DomRoot<GlobalScope>,
sc_holder: &mut StructuredDataHolder,
extra_data: u64,
return_object: MutableHandleObject,
) -> 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};
@ -462,13 +464,24 @@ impl DedicatedWorkerGlobalScope {
fn handle_script_event(&self, msg: WorkerScriptMsg) {
match msg {
WorkerScriptMsg::DOMMessage(data) => {
WorkerScriptMsg::DOMMessage { origin, data } => {
let scope = self.upcast::<WorkerGlobalScope>();
let target = self.upcast();
let _ac = enter_realm(self);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
data.read(scope.upcast(), message.handle_mut());
MessageEvent::dispatch_jsval(target, scope.upcast(), message.handle(), None, None);
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);
@ -547,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)]
@ -560,24 +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 {
let data = StructuredCloneData::write(*cx, message)?;
let worker = self.worker.borrow().as_ref().unwrap().clone();
let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
let task = Box::new(task!(post_worker_message: move || {
Worker::handle_message(worker, 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,28 +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.
let data = StructuredCloneData::write(*cx, message)?;
/// 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
@ -186,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,6 +236,7 @@ impl EventSourceContext {
DOMString::from(self.origin.clone()),
None,
event_source.last_event_id.borrow().clone(),
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;
@ -33,36 +37,43 @@ use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
use crate::task_source::file_reading::FileReadingTaskSource;
use crate::task_source::networking::NetworkingTaskSource;
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};
@ -84,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,
@ -167,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,
@ -183,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)),
@ -208,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()
@ -504,7 +981,7 @@ impl GlobalScope {
unreachable!();
}
/// `ScriptChan` to send messages to the networking task source of
/// `TaskSource` to send messages to the networking task source of
/// this global scope.
pub fn networking_task_source(&self) -> NetworkingTaskSource {
if let Some(window) = self.downcast::<Window>() {
@ -516,7 +993,19 @@ impl GlobalScope {
unreachable!();
}
/// `ScriptChan` to send messages to the remote-event task source of
/// `TaskSource` to send messages to the port message queue of
/// this global scope.
pub fn port_message_queue(&self) -> PortMessageQueue {
if let Some(window) = self.downcast::<Window>() {
return window.task_manager().port_message_queue();
}
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
return worker.port_message_queue();
}
unreachable!();
}
/// `TaskSource` to send messages to the remote-event task source of
/// this global scope.
pub fn remote_event_task_source(&self) -> RemoteEventTaskSource {
if let Some(window) = self.downcast::<Window>() {
@ -528,7 +1017,7 @@ impl GlobalScope {
unreachable!();
}
/// `ScriptChan` to send messages to the websocket task source of
/// `TaskSource` to send messages to the websocket task source of
/// this global scope.
pub fn websocket_task_source(&self) -> WebsocketTaskSource {
if let Some(window) = self.downcast::<Window>() {

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,7 +190,7 @@ impl History {
// TODO: Step 4
// Step 5
let serialized_data = StructuredCloneData::write(*cx, data)?.move_to_arraybuffer();
let serialized_data = structuredclone::write(cx, data, None)?;
let new_url: ServoUrl = match url {
// Step 6
@ -254,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
@ -266,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

@ -0,0 +1,66 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::MessageChannelBinding::{MessageChannelMethods, Wrap};
use crate::dom::bindings::error::{Error, Fallible};
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;
use dom_struct::dom_struct;
#[dom_struct]
pub struct MessageChannel {
reflector_: Reflector,
port1: Dom<MessagePort>,
port2: Dom<MessagePort>,
}
impl MessageChannel {
/// <https://html.spec.whatwg.org/multipage/#dom-messagechannel>
pub fn Constructor(global: &GlobalScope) -> Fallible<DomRoot<MessageChannel>> {
let incumbent = GlobalScope::incumbent().ok_or(Error::InvalidState)?;
// Step 1
let port1 = MessagePort::new(&incumbent);
// Step 2
let port2 = MessagePort::new(&incumbent);
incumbent.track_message_port(&*port1, None);
incumbent.track_message_port(&*port2, None);
// Step 3
incumbent.entangle_ports(
port1.message_port_id().clone(),
port2.message_port_id().clone(),
);
// Steps 4-6
let channel = reflect_dom_object(
Box::new(MessageChannel {
reflector_: Reflector::new(),
port1: Dom::from_ref(&port1),
port2: Dom::from_ref(&port2),
}),
global,
Wrap,
);
// Step 7
Ok(channel)
}
}
impl MessageChannelMethods for MessageChannel {
/// <https://html.spec.whatwg.org/multipage/#dom-messagechannel-port1>
fn Port1(&self) -> DomRoot<MessagePort> {
DomRoot::from_ref(&*self.port1)
}
/// <https://html.spec.whatwg.org/multipage/#dom-messagechannel-port2>
fn Port2(&self) -> DomRoot<MessagePort> {
DomRoot::from_ref(&*self.port2)
}
}

View file

@ -11,9 +11,11 @@ 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;
use crate::dom::messageport::MessagePort;
use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
@ -31,6 +33,7 @@ pub struct MessageEvent {
origin: DOMString,
source: Option<Dom<WindowProxy>>,
lastEventId: DOMString,
ports: Vec<DomRoot<MessagePort>>,
}
impl MessageEvent {
@ -41,6 +44,7 @@ impl MessageEvent {
DOMString::new(),
None,
DOMString::new(),
vec![],
)
}
@ -50,13 +54,15 @@ impl MessageEvent {
origin: DOMString,
source: Option<&WindowProxy>,
lastEventId: DOMString,
ports: Vec<DomRoot<MessagePort>>,
) -> DomRoot<MessageEvent> {
let ev = Box::new(MessageEvent {
event: Event::new_inherited(),
data: Heap::default(),
origin: origin,
source: source.map(Dom::from_ref),
lastEventId: lastEventId,
origin,
lastEventId,
ports,
});
let ev = reflect_dom_object(ev, global, MessageEventBinding::Wrap);
ev.data.set(data.get());
@ -73,8 +79,9 @@ impl MessageEvent {
origin: DOMString,
source: Option<&WindowProxy>,
lastEventId: DOMString,
ports: Vec<DomRoot<MessagePort>>,
) -> DomRoot<MessageEvent> {
let ev = MessageEvent::new_initialized(global, data, origin, source, lastEventId);
let ev = MessageEvent::new_initialized(global, data, origin, source, lastEventId, ports);
{
let event = ev.upcast::<Event>();
event.init_event(type_, bubbles, cancelable);
@ -100,6 +107,7 @@ impl MessageEvent {
init.origin.clone(),
source.as_ref().map(|source| &**source),
init.lastEventId.clone(),
init.ports.clone().unwrap_or(vec![]),
);
Ok(ev)
}
@ -112,6 +120,7 @@ impl MessageEvent {
message: HandleValue,
origin: Option<&str>,
source: Option<&WindowProxy>,
ports: Vec<DomRoot<MessagePort>>,
) {
let messageevent = MessageEvent::new(
scope,
@ -122,18 +131,39 @@ impl MessageEvent {
DOMString::from(origin.unwrap_or("")),
source,
DOMString::new(),
ports,
);
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 {
// https://html.spec.whatwg.org/multipage/#dom-messageevent-data
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-data>
fn Data(&self, _cx: JSContext) -> JSVal {
self.data.get()
}
// https://html.spec.whatwg.org/multipage/#dom-messageevent-origin
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-origin>
fn Origin(&self) -> DOMString {
self.origin.clone()
}
@ -145,13 +175,18 @@ impl MessageEventMethods for MessageEvent {
.and_then(|source| NonNull::new(source.reflector().get_jsobject().get()))
}
// https://html.spec.whatwg.org/multipage/#dom-messageevent-lasteventid
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-lasteventid>
fn LastEventId(&self) -> DOMString {
self.lastEventId.clone()
}
// https://dom.spec.whatwg.org/#dom-event-istrusted
/// <https://dom.spec.whatwg.org/#dom-event-istrusted>
fn IsTrusted(&self) -> bool {
self.event.IsTrusted()
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-ports>
fn Ports(&self, cx: JSContext) -> JSVal {
message_ports_to_frozen_array(self.ports.as_slice(), cx)
}
}

View file

@ -0,0 +1,345 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* 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, PostMessageOptions, Wrap,
};
use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::error::{Error, ErrorResult};
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::{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::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::HashMap;
use std::convert::TryInto;
use std::num::NonZeroU32;
use std::rc::Rc;
#[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>,
}
impl 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_id,
}
}
/// <https://html.spec.whatwg.org/multipage/#create-a-new-messageport-object>
pub fn new(owner: &GlobalScope) -> DomRoot<MessagePort> {
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_id: MessagePortId) {
*self.entangled_port.borrow_mut() = Some(other_id);
}
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>
fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()> {
if self.detached.get() {
return Err(());
}
let port_impls = match sc_holder {
StructuredDataHolder::Write(port_impls) => port_impls,
_ => panic!("Unexpected variant of StructuredDataHolder"),
};
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);
}
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
fn transfer_receive(
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"),
};
// 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);
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"),
);
let id = MessagePortId {
namespace_id,
index,
};
// 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) {
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) {
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>>) {
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

@ -401,7 +401,9 @@ pub mod mediaquerylist;
pub mod mediaquerylistevent;
pub mod mediastream;
pub mod mediastreamtrack;
pub mod messagechannel;
pub mod messageevent;
pub mod messageport;
pub mod mimetype;
pub mod mimetypearray;
pub mod mouseevent;

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,13 +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::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;
@ -77,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 {
@ -90,23 +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
let data = StructuredCloneData::write(*cx, message)?;
let msg_vec = DOMMessage(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;
@ -409,13 +411,22 @@ impl ServiceWorkerGlobalScope {
use self::ServiceWorkerScriptMsg::*;
match msg {
CommonWorker(WorkerScriptMsg::DOMMessage(data)) => {
CommonWorker(WorkerScriptMsg::DOMMessage { data, .. }) => {
let scope = self.upcast::<WorkerGlobalScope>();
let target = self.upcast();
let _ac = enter_realm(&*scope);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
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

@ -0,0 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* The origin of this IDL file is:
* https://html.spec.whatwg.org/multipage/#messagechannel
*/
[Exposed=(Window,Worker)]
interface MessageChannel {
[Throws] constructor();
readonly attribute MessagePort port1;
readonly attribute MessagePort port2;
};

View file

@ -12,7 +12,7 @@ interface MessageEvent : Event {
// FIXME(#22617): WindowProxy is not exposed in Worker globals
readonly attribute object? source;
//readonly attribute (WindowProxy or MessagePort)? source;
//readonly attribute MessagePort[]? ports;
readonly attribute /*FrozenArray<MessagePort>*/any ports;
};
dictionary MessageEventInit : EventInit {
@ -22,5 +22,7 @@ dictionary MessageEventInit : EventInit {
//DOMString channel;
Window? source;
//(WindowProxy or MessagePort)? source;
//sequence<MessagePort> ports;
sequence<MessagePort> ports;
};
typedef (/*WindowProxy or */MessagePort or ServiceWorker) MessageEventSource;

View file

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* The origin of this IDL file is:
* https://html.spec.whatwg.org/multipage/#messageport
*/
[Exposed=(Window,Worker)]
interface MessagePort : EventTarget {
[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

@ -63,9 +63,10 @@
unsigned long requestAnimationFrame(FrameRequestCallback callback);
void cancelAnimationFrame(unsigned long handle);
//void postMessage(any message, DOMString targetOrigin, optional sequence<Transferable> transfer);
[Throws]
void postMessage(any message, DOMString targetOrigin);
void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/);
[Throws]
void postMessage(any message, optional WindowPostMessageOptions options = {});
// also has obsolete members
};
@ -173,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

@ -598,6 +598,7 @@ impl TaskOnce for MessageReceivedTask {
message.handle(),
Some(&ws.origin().ascii_serialization()),
None,
vec![],
);
}
}

View file

@ -13,7 +13,7 @@ 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;
@ -24,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;
@ -79,13 +79,15 @@ 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;
use js::jsapi::{GCReason, JS_GC};
use js::jsval::UndefinedValue;
use js::jsval::{JSVal, NullValue};
use js::rust::wrappers::JS_DefineProperty;
use js::rust::HandleValue;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use media::WindowGLContext;
use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
@ -104,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};
@ -969,27 +974,57 @@ impl WindowMethods for Window {
}
// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult {
let source_global = GlobalScope::incumbent().expect("no incumbent global??");
let source = source_global.as_window();
fn PostMessage(
&self,
cx: JSContext,
message: HandleValue,
target_origin: USVString,
mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
) -> ErrorResult {
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[..] {
"*" => 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.
// TODO(#12717): Should implement the `transfer` argument.
let data = StructuredCloneData::write(*cx, message)?;
/// <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));
// Step 9.
self.post_message(origin, &*source.window_proxy(), data);
Ok(())
let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
let source = incumbent.as_window();
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
@ -1287,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())
@ -1333,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() {
@ -2334,18 +2400,20 @@ 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);
let task = task!(post_serialised_message: move || {
let this = this.root();
let source = source.root();
let document = this.Document();
// Step 7.1.
if let Some(target_origin) = target_origin {
if !target_origin.same_origin(this.Document().origin()) {
if let Some(ref target_origin) = target_origin {
if !target_origin.same_origin(document.origin()) {
return;
}
}
@ -2355,23 +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());
serialize_with_transfer_result.read(
this.upcast(),
message_clone.handle_mut(),
);
// Step 7.6.
// TODO: MessagePort array.
// Step 7.7.
// TODO(#12719): Set the other attributes.
MessageEvent::dispatch_jsval(
this.upcast(),
this.upcast(),
message_clone.handle(),
None,
Some(&*source),
);
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;
@ -137,7 +139,11 @@ impl Worker {
self.terminated.get()
}
pub fn handle_message(address: TrustedWorkerAddress, data: StructuredCloneData) {
pub fn handle_message(
address: TrustedWorkerAddress,
origin: String,
data: StructuredSerializedData,
) {
let worker = address.root();
if worker.is_terminated() {
@ -148,30 +154,79 @@ impl Worker {
let target = worker.upcast();
let _ac = enter_realm(target);
rooted!(in(*global.get_cx()) let mut message = UndefinedValue());
data.read(&global, message.handle_mut());
MessageEvent::dispatch_jsval(target, &global, message.handle(), None, None);
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 {
let data = StructuredCloneData::write(*cx, message)?;
/// 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
// indicates that a nonexistent communication channel should result in a silent error.
let _ = self.sender.send(DedicatedWorkerScriptMsg::CommonWorker(
address,
WorkerScriptMsg::DOMMessage(data),
WorkerScriptMsg::DOMMessage {
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

@ -33,6 +33,7 @@ use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
use crate::task_source::file_reading::FileReadingTaskSource;
use crate::task_source::networking::NetworkingTaskSource;
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::timers::{IsInterval, TimerCallback};
@ -457,6 +458,10 @@ impl WorkerGlobalScope {
PerformanceTimelineTaskSource(self.script_chan(), self.pipeline_id())
}
pub fn port_message_queue(&self) -> PortMessageQueue {
PortMessageQueue(self.script_chan(), self.pipeline_id())
}
pub fn remote_event_task_source(&self) -> RemoteEventTaskSource {
RemoteEventTaskSource(self.script_chan(), self.pipeline_id())
}