aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorGregory Terzian <gterzian@users.noreply.github.com>2019-06-26 00:25:48 +0800
committerGregory Terzian <gterzian@users.noreply.github.com>2019-10-19 14:28:18 +0800
commit2f8932a6a1e2666567435114383b3acd1899aca7 (patch)
tree8490d198d1c22015afeae84ad25f21ecca462415 /components/script
parentc3b17c1201441c9a24c4b272108aea0196fbf1b9 (diff)
downloadservo-2f8932a6a1e2666567435114383b3acd1899aca7.tar.gz
servo-2f8932a6a1e2666567435114383b3acd1899aca7.zip
continue messageport, transferable, postmessage options
Diffstat (limited to 'components/script')
-rw-r--r--components/script/Cargo.toml2
-rw-r--r--components/script/dom/abstractworker.rs9
-rw-r--r--components/script/dom/abstractworkerglobalscope.rs3
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf4
-rw-r--r--components/script/dom/bindings/structuredclone.rs301
-rw-r--r--components/script/dom/bindings/trace.rs9
-rw-r--r--components/script/dom/bindings/transferable.rs27
-rw-r--r--components/script/dom/bindings/utils.rs18
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs100
-rw-r--r--components/script/dom/dissimilaroriginwindow.rs114
-rw-r--r--components/script/dom/document.rs5
-rw-r--r--components/script/dom/eventsource.rs2
-rw-r--r--components/script/dom/extendablemessageevent.rs23
-rw-r--r--components/script/dom/globalscope.rs488
-rw-r--r--components/script/dom/history.rs24
-rw-r--r--components/script/dom/messagechannel.rs16
-rw-r--r--components/script/dom/messageevent.rs39
-rw-r--r--components/script/dom/messageport.rs568
-rw-r--r--components/script/dom/serviceworker.rs82
-rw-r--r--components/script/dom/serviceworkerglobalscope.rs15
-rw-r--r--components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl6
-rw-r--r--components/script/dom/webidls/DissimilarOriginWindow.webidl3
-rw-r--r--components/script/dom/webidls/ExtendableMessageEvent.webidl2
-rw-r--r--components/script/dom/webidls/MessagePort.webidl8
-rw-r--r--components/script/dom/webidls/ServiceWorker.webidl3
-rw-r--r--components/script/dom/webidls/Window.webidl7
-rw-r--r--components/script/dom/webidls/Worker.webidl4
-rw-r--r--components/script/dom/window.rs147
-rw-r--r--components/script/dom/worker.rs75
-rw-r--r--components/script/script_thread.rs14
-rw-r--r--components/script/serviceworker_manager.rs2
-rw-r--r--components/script/task_source/port_message.rs8
32 files changed, 1457 insertions, 671 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index efef253317c..cb30f437a11 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -34,6 +34,7 @@ tinyfiledialogs = "3.0"
app_units = "0.7"
backtrace = {version = "0.3", optional = true}
base64 = "0.10.1"
+bincode = "1"
bitflags = "1.0"
bluetooth_traits = {path = "../bluetooth_traits"}
canvas_traits = {path = "../canvas_traits"}
@@ -97,7 +98,6 @@ servo_config = {path = "../config"}
servo_geometry = {path = "../geometry" }
servo-media = {git = "https://github.com/servo/media"}
servo_rand = {path = "../rand"}
-servo_remutex = {path = "../remutex"}
servo_url = {path = "../url"}
sparkle = "0.1"
smallvec = { version = "0.6", features = ["std", "union"] }
diff --git a/components/script/dom/abstractworker.rs b/components/script/dom/abstractworker.rs
index 930f48cdcea..36ff18fe01b 100644
--- a/components/script/dom/abstractworker.rs
+++ b/components/script/dom/abstractworker.rs
@@ -4,8 +4,9 @@
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
-use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::script_runtime::CommonScriptMsg;
+use script_traits::StructuredSerializedData;
+use servo_url::ImmutableOrigin;
/// Messages used to control the worker event loops
pub enum WorkerScriptMsg {
@@ -13,9 +14,9 @@ pub enum WorkerScriptMsg {
Common(CommonScriptMsg),
/// Message sent through Worker.postMessage
DOMMessage {
- origin: String,
- data: StructuredCloneData,
- }
+ origin: ImmutableOrigin,
+ data: StructuredSerializedData,
+ },
}
pub struct SimpleWorkerErrorHandler<T: DomObject> {
diff --git a/components/script/dom/abstractworkerglobalscope.rs b/components/script/dom/abstractworkerglobalscope.rs
index 6ffeba85d5e..e8e41375b70 100644
--- a/components/script/dom/abstractworkerglobalscope.rs
+++ b/components/script/dom/abstractworkerglobalscope.rs
@@ -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();
}
diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf
index e3de60005bc..3ff7a044b53 100644
--- a/components/script/dom/bindings/codegen/Bindings.conf
+++ b/components/script/dom/bindings/codegen/Bindings.conf
@@ -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'],
diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs
index 2e33613b29d..25c440365c8 100644
--- a/components/script/dom/bindings/structuredclone.rs
+++ b/components/script/dom/bindings/structuredclone.rs
@@ -6,7 +6,7 @@
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).
use crate::compartments::enter_realm;
-use crate::dom::bindings::conversions::root_from_handleobject;
+use crate::dom::bindings::conversions::{root_from_object, ToJSValConvertible};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
@@ -14,6 +14,7 @@ use crate::dom::bindings::transferable::Transferable;
use crate::dom::blob::{Blob, BlobImpl};
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
+use crate::script_runtime::JSContext as SafeJSContext;
use js::glue::CopyJSStructuredCloneData;
use js::glue::DeleteJSAutoStructuredCloneBuffer;
use js::glue::GetLengthOfJSStructuredCloneData;
@@ -30,12 +31,15 @@ use js::jsapi::{JSObject, JS_ClearPendingException};
use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter};
use js::jsapi::{JS_ReadBytes, JS_WriteBytes};
use js::jsapi::{JS_ReadUint32Pair, JS_WriteUint32Pair};
+use js::jsval::UndefinedValue;
use js::rust::wrappers::{JS_ReadStructuredClone, JS_WriteStructuredClone};
-use js::rust::{Handle, HandleValue, MutableHandleValue};
-use libc::size_t;
+use js::rust::{CustomAutoRooterGuard, HandleValue, MutableHandleValue};
+use msg::constellation_msg::MessagePortId;
+use script_traits::transferable::MessagePortImpl;
+use script_traits::StructuredSerializedData;
+use std::collections::HashMap;
use std::os::raw;
use std::ptr;
-use std::slice;
// TODO: Should we add Min and Max const to https://github.com/servo/rust-mozjs/blob/master/src/consts.rs?
// TODO: Determine for sure which value Min and Max should have.
@@ -129,19 +133,24 @@ impl StructuredCloneReader {
unsafe fn read_blob(
cx: *mut JSContext,
r: *mut JSStructuredCloneReader,
- sc_holder: &mut StructuredCloneHolder,
+ sc_holder: &mut StructuredDataHolder,
) -> *mut JSObject {
let structured_reader = StructuredCloneReader { r: r };
let blob_buffer = structured_reader.read_bytes();
let type_str = structured_reader.read_str();
let target_global = GlobalScope::from_context(cx);
- let blob = Blob::new(
+ let read_blob = Blob::new(
&target_global,
BlobImpl::new_from_bytes(blob_buffer),
type_str,
);
- let js_object = blob.reflector().get_jsobject().get();
- sc_holder.blob = Some(blob);
+ let js_object = read_blob.reflector().get_jsobject().get();
+ match sc_holder {
+ StructuredDataHolder::Read { blob, .. } => {
+ *blob = Some(read_blob);
+ },
+ _ => panic!("Unexpected variant of StructuredDataHolder"),
+ }
js_object
}
@@ -174,7 +183,7 @@ unsafe extern "C" fn read_callback(
"tag should be higher than StructuredCloneTags::Min"
);
if tag == StructuredCloneTags::DomBlob as u32 {
- return read_blob(cx, r, &mut *(closure as *mut StructuredCloneHolder));
+ return read_blob(cx, r, &mut *(closure as *mut StructuredDataHolder));
}
return ptr::null_mut();
}
@@ -185,7 +194,7 @@ unsafe extern "C" fn write_callback(
obj: RawHandleObject,
_closure: *mut raw::c_void,
) -> bool {
- if let Ok(blob) = root_from_handleobject::<Blob>(Handle::from_raw(obj), cx) {
+ if let Ok(blob) = root_from_object::<Blob>(*obj, cx) {
return write_blob(blob, w).is_ok();
}
return false;
@@ -193,39 +202,44 @@ unsafe extern "C" fn write_callback(
unsafe extern "C" fn read_transfer_callback(
cx: *mut JSContext,
- r: *mut JSStructuredCloneReader,
+ _r: *mut JSStructuredCloneReader,
tag: u32,
- content: *mut raw::c_void,
+ _content: *mut raw::c_void,
extra_data: u64,
closure: *mut raw::c_void,
return_object: RawMutableHandleObject,
) -> bool {
if tag == StructuredCloneTags::MessagePort as u32 {
- <MessagePort as Transferable>::transfer_receive(cx, r, closure, content, extra_data, return_object)
- } else {
- false
+ let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
+ let owner = GlobalScope::from_context(cx);
+ if let Ok(_) = <MessagePort as Transferable>::transfer_receive(
+ &owner,
+ &mut sc_holder,
+ extra_data,
+ return_object,
+ ) {
+ return true;
+ }
}
+ false
}
/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
unsafe extern "C" fn write_transfer_callback(
- _cx: *mut JSContext,
+ cx: *mut JSContext,
obj: RawHandleObject,
closure: *mut raw::c_void,
tag: *mut u32,
ownership: *mut TransferableOwnership,
- content: *mut *mut raw::c_void,
+ _content: *mut *mut raw::c_void,
extra_data: *mut u64,
) -> bool {
- if let Ok(port) = root_from_handleobject::<MessagePort>(Handle::from_raw(obj)) {
- if let Some(true) = port.detached() {
- return false;
- }
-
+ if let Ok(port) = root_from_object::<MessagePort>(*obj, cx) {
*tag = StructuredCloneTags::MessagePort as u32;
*ownership = TransferableOwnership::SCTAG_TMO_CUSTOM;
- if port.transfer(closure, content, extra_data) {
- port.set_detached(true);
+ let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
+ if let Ok(data) = port.transfer(&mut sc_holder) {
+ *extra_data = data;
return true;
}
}
@@ -242,10 +256,13 @@ unsafe extern "C" fn free_transfer_callback(
}
unsafe extern "C" fn can_transfer_callback(
- _cx: *mut JSContext,
- _obj: RawHandleObject,
+ cx: *mut JSContext,
+ obj: RawHandleObject,
_closure: *mut raw::c_void,
) -> bool {
+ if let Ok(_port) = root_from_object::<MessagePort>(*obj, cx) {
+ return true;
+ }
false
}
@@ -261,123 +278,143 @@ static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredClon
canTransfer: Some(can_transfer_callback),
};
-struct StructuredCloneHolder {
- blob: Option<DomRoot<Blob>>,
+/// A data holder for results from, and inputs to, structured-data read/write operations.
+/// https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data
+pub enum StructuredDataHolder {
+ Read {
+ /// A deserialized blob, stored temporarily here to keep it rooted.
+ blob: Option<DomRoot<Blob>>,
+ /// A vec of transfer-received DOM ports,
+ /// to be made available to script through a message event.
+ message_ports: Option<Vec<DomRoot<MessagePort>>>,
+ /// A map of port implementations,
+ /// used as part of the "transfer-receiving" steps of ports,
+ /// to produce the DOM ports stored in `message_ports` above.
+ port_impls: Option<HashMap<MessagePortId, MessagePortImpl>>,
+ },
+ /// A data holder into which transferred ports
+ /// can be written as part of their transfer steps.
+ Write(Option<HashMap<MessagePortId, MessagePortImpl>>),
}
-/// A buffer for a structured clone.
-pub enum StructuredCloneData {
- /// A non-serializable (default) variant
- Struct(*mut u64, size_t),
- /// A variant that can be serialized
- Vector(Vec<u8>),
-}
+/// 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());
+ }
-impl StructuredCloneData {
- // TODO: should this be unsafe?
- /// Writes a structured clone. Returns a `DataClone` error if that fails.
- pub fn write(
- cx: *mut JSContext,
- message: HandleValue,
- transfer: HandleValue,
- ) -> Fallible<StructuredCloneData> {
- unsafe {
- let scbuf = NewJSAutoStructuredCloneBuffer(
- StructuredCloneScope::DifferentProcess,
- &STRUCTURED_CLONE_CALLBACKS,
- );
- let scdata = &mut ((*scbuf).data_);
- let policy = CloneDataPolicy {
- // TODO: SAB?
- sharedArrayBuffer_: false,
- };
- let result = JS_WriteStructuredClone(
- cx,
- message,
- scdata,
- StructuredCloneScope::DifferentProcess,
- policy,
- &STRUCTURED_CLONE_CALLBACKS,
- ptr::null_mut(),
- transfer,
- );
- if !result {
- JS_ClearPendingException(cx);
- return Err(Error::DataClone);
- }
+ let mut sc_holder = StructuredDataHolder::Write(None);
+ let sc_holder_ptr = &mut sc_holder as *mut _;
- let nbytes = GetLengthOfJSStructuredCloneData(scdata);
- let mut data = Vec::with_capacity(nbytes);
- CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
- data.set_len(nbytes);
+ 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);
+ }
- DeleteJSAutoStructuredCloneBuffer(scbuf);
+ let nbytes = GetLengthOfJSStructuredCloneData(scdata);
+ let mut data = Vec::with_capacity(nbytes);
+ CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
+ data.set_len(nbytes);
- Ok(StructuredCloneData::Vector(data))
- }
- }
+ DeleteJSAutoStructuredCloneBuffer(scbuf);
- /// 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,
- }
- }
+ let mut port_impls = match sc_holder {
+ StructuredDataHolder::Write(port_impls) => port_impls,
+ _ => panic!("Unexpected variant of StructuredDataHolder"),
+ };
- /// Reads a structured clone.
- ///
- /// Panics if `JS_ReadStructuredClone` fails.
- fn read_clone(
- global: &GlobalScope,
- data: *mut u64,
- nbytes: size_t,
- rval: MutableHandleValue,
- ) -> bool {
- let cx = global.get_cx();
- let _ac = enter_realm(&*global);
- let mut sc_holder = StructuredCloneHolder { blob: None };
- let sc_holder_ptr = &mut sc_holder as *mut _;
- unsafe {
- let scbuf = NewJSAutoStructuredCloneBuffer(
- StructuredCloneScope::DifferentProcess,
- &STRUCTURED_CLONE_CALLBACKS,
- );
- let scdata = &mut ((*scbuf).data_);
-
- WriteBytesToJSStructuredCloneData(data as *const u8, nbytes, scdata);
-
- let result = JS_ReadStructuredClone(
- *cx,
- scdata,
- JS_STRUCTURED_CLONE_VERSION,
- StructuredCloneScope::DifferentProcess,
- rval,
- &STRUCTURED_CLONE_CALLBACKS,
- sc_holder_ptr as *mut raw::c_void
- );
-
- DeleteJSAutoStructuredCloneBuffer(scbuf);
-
- result
- }
+ let data = StructuredSerializedData {
+ serialized: data,
+ ports: port_impls.take(),
+ };
+
+ Ok(data)
}
+}
- /// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone.
- pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) -> bool {
- match self {
- StructuredCloneData::Vector(mut vec_msg) => {
- let nbytes = vec_msg.len();
- let data = vec_msg.as_mut_ptr() as *mut u64;
- StructuredCloneData::read_clone(global, data, nbytes, rval)
- }
- StructuredCloneData::Struct(data, nbytes) => {
- StructuredCloneData::read_clone(global, data, nbytes, rval)
+/// 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(())
}
}
-
-unsafe impl Send for StructuredCloneData {}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index a914b8161a6..4b79bc7085b 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -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);
diff --git a/components/script/dom/bindings/transferable.rs b/components/script/dom/bindings/transferable.rs
index fcf3ad93ab8..fb2f4a991a2 100644
--- a/components/script/dom/bindings/transferable.rs
+++ b/components/script/dom/bindings/transferable.rs
@@ -4,26 +4,19 @@
//! Trait representing the concept of [transferable objects]
//! (https://html.spec.whatwg.org/multipage/#transferable-objects).
+
use crate::dom::bindings::reflector::DomObject;
-use js::jsapi::{JSContext, JSStructuredCloneReader, MutableHandleObject};
-use std::os::raw;
+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,
- closure: *mut raw::c_void,
- content: *mut *mut raw::c_void,
- extra_data: *mut u64,
- ) -> bool;
+pub trait Transferable: DomObject {
+ fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()>;
fn transfer_receive(
- cx: *mut JSContext,
- r: *mut JSStructuredCloneReader,
- closure: *mut raw::c_void,
- content: *mut raw::c_void,
+ owner: &DomRoot<GlobalScope>,
+ sc_holder: &mut StructuredDataHolder,
extra_data: u64,
return_object: MutableHandleObject,
- ) -> bool;
- fn detached(&self) -> Option<bool> { None }
- fn set_detached(&self, _value: bool) { }
- fn transferable(&self) -> bool { false }
+ ) -> Result<(), ()>;
}
diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs
index 7146e1533b7..ef3b270b130 100644
--- a/components/script/dom/bindings/utils.rs
+++ b/components/script/dom/bindings/utils.rs
@@ -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 {
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
index d0e8ba8258d..3745ef9f1ac 100644
--- a/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -10,13 +10,15 @@ use crate::dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThre
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
+use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
use crate::dom::bindings::error::{ErrorInfo, ErrorResult};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
use crate::dom::bindings::str::DOMString;
-use crate::dom::bindings::structuredclone::StructuredCloneData;
+use crate::dom::bindings::structuredclone;
+use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventtarget::EventTarget;
@@ -36,10 +38,10 @@ use devtools_traits::DevtoolScriptControlMsg;
use dom_struct::dom_struct;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
-use js::jsapi::JSContext;
use js::jsapi::JS_AddInterruptCallback;
+use js::jsapi::{Heap, JSContext, JSObject};
use js::jsval::UndefinedValue;
-use js::rust::HandleValue;
+use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId};
use net_traits::image_cache::ImageCache;
use net_traits::request::{CredentialsMode, Destination, ParserMetadata};
@@ -467,15 +469,19 @@ impl DedicatedWorkerGlobalScope {
let target = self.upcast();
let _ac = enter_realm(self);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
- assert!(data.read(scope.upcast(), message.handle_mut()));
- MessageEvent::dispatch_jsval(
- target,
- scope.upcast(),
- message.handle(),
- Some(&origin),
- None,
- vec![],
- );
+ if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut())
+ {
+ MessageEvent::dispatch_jsval(
+ target,
+ scope.upcast(),
+ message.handle(),
+ Some(&origin.ascii_serialization()),
+ None,
+ ports,
+ );
+ } else {
+ MessageEvent::dispatch_error(target, scope.upcast());
+ }
},
WorkerScriptMsg::Common(msg) => {
self.upcast::<WorkerGlobalScope>().process_event(msg);
@@ -554,30 +560,22 @@ impl DedicatedWorkerGlobalScope {
))
.unwrap();
}
-}
-
-#[allow(unsafe_code)]
-unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool {
- let worker = DomRoot::downcast::<WorkerGlobalScope>(GlobalScope::from_context(cx))
- .expect("global is not a worker scope");
- assert!(worker.is::<DedicatedWorkerGlobalScope>());
-
- // A false response causes the script to terminate
- !worker.is_closing()
-}
-impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope {
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
- fn PostMessage(&self, cx: SafeJSContext, message: HandleValue) -> ErrorResult {
- rooted!(in(*cx) let transfer = UndefinedValue());
- let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
+ 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 pipeline_id = self.global().pipeline_id();
- let origin = self.global().origin().immutable().ascii_serialization();
+ 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);
}));
- // TODO: Change this task source to a new `unshipped-port-message-queue` task source
self.parent_sender
.send(CommonScriptMsg::Task(
WorkerEvent,
@@ -588,6 +586,48 @@ impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope {
.unwrap();
Ok(())
}
+}
+
+#[allow(unsafe_code)]
+unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool {
+ let worker = DomRoot::downcast::<WorkerGlobalScope>(GlobalScope::from_context(cx))
+ .expect("global is not a worker scope");
+ assert!(worker.is::<DedicatedWorkerGlobalScope>());
+
+ // A false response causes the script to terminate
+ !worker.is_closing()
+}
+
+impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope {
+ /// 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
fn Close(&self) {
diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs
index 31bb13051f7..c628923604e 100644
--- a/components/script/dom/dissimilaroriginwindow.rs
+++ b/components/script/dom/dissimilaroriginwindow.rs
@@ -4,21 +4,23 @@
use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding;
use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding::DissimilarOriginWindowMethods;
+use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowPostMessageOptions;
use crate::dom::bindings::error::{Error, ErrorResult};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
-use crate::dom::bindings::str::DOMString;
-use crate::dom::bindings::structuredclone::StructuredCloneData;
+use crate::dom::bindings::str::USVString;
+use crate::dom::bindings::structuredclone;
+use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::dissimilaroriginlocation::DissimilarOriginLocation;
use crate::dom::globalscope::GlobalScope;
use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
use ipc_channel::ipc;
+use js::jsapi::{Heap, JSObject};
use js::jsval::{JSVal, UndefinedValue};
-use js::rust::HandleValue;
+use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use msg::constellation_msg::PipelineId;
-use script_traits::ScriptMsg;
-use servo_url::ImmutableOrigin;
+use script_traits::{ScriptMsg, StructuredSerializedData};
use servo_url::ServoUrl;
/// Represents a dissimilar-origin `Window` that exists in another script thread.
@@ -133,29 +135,42 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
false
}
- // https://html.spec.whatwg.org/multipage/#dom-window-postmessage
- fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult {
- // Step 3-5.
- let origin = match &origin[..] {
- "*" => None,
- "/" => {
- // TODO: Should be the origin of the incumbent settings object.
- None
- },
- url => match ServoUrl::parse(&url) {
- Ok(url) => Some(url.origin()),
- Err(_) => return Err(Error::Syntax),
- },
- };
+ /// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
+ fn PostMessage(
+ &self,
+ cx: JSContext,
+ message: HandleValue,
+ target_origin: USVString,
+ mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
+ ) -> ErrorResult {
+ if transfer.is_some() {
+ let mut rooted = CustomAutoRooter::new(transfer.take().unwrap());
+ let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
+ self.post_message_impl(&target_origin, cx, message, transfer)
+ } else {
+ self.post_message_impl(&target_origin, cx, message, None)
+ }
+ }
- // Step 1-2, 6-8.
- // TODO(#12717): Should implement the `transfer` argument.
- rooted!(in(*cx) let transfer = UndefinedValue());
- let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
+ /// https://html.spec.whatwg.org/multipage/#dom-window-postmessage-options
+ fn PostMessage_(
+ &self,
+ cx: JSContext,
+ message: HandleValue,
+ options: RootedTraceableBox<WindowPostMessageOptions>,
+ ) -> ErrorResult {
+ let mut rooted = CustomAutoRooter::new(
+ options
+ .transfer
+ .as_ref()
+ .unwrap_or(&Vec::with_capacity(0))
+ .iter()
+ .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
+ .collect(),
+ );
+ let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
- // Step 9.
- self.post_message(origin, data);
- Ok(())
+ self.post_message_impl(&options.targetOrigin, cx, message, transfer)
}
// https://html.spec.whatwg.org/multipage/#dom-opener
@@ -187,17 +202,54 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
}
impl DissimilarOriginWindow {
- pub fn post_message(&self, origin: Option<ImmutableOrigin>, data: StructuredCloneData) {
+ /// https://html.spec.whatwg.org/multipage/#window-post-message-steps
+ fn post_message_impl(
+ &self,
+ target_origin: &USVString,
+ cx: JSContext,
+ message: HandleValue,
+ transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
+ ) -> ErrorResult {
+ // Step 6-7.
+ let data = structuredclone::write(cx, message, transfer)?;
+
+ self.post_message(target_origin, data)
+ }
+
+ /// https://html.spec.whatwg.org/multipage/#window-post-message-steps
+ pub fn post_message(
+ &self,
+ target_origin: &USVString,
+ data: StructuredSerializedData,
+ ) -> ErrorResult {
+ // Step 1.
+ let target = self.window_proxy.browsing_context_id();
+ // Step 2.
let incumbent = match GlobalScope::incumbent() {
- None => return warn!("postMessage called with no incumbent global"),
+ None => panic!("postMessage called with no incumbent global"),
Some(incumbent) => incumbent,
};
+
+ let 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: self.window_proxy.browsing_context_id(),
+ target,
source: incumbent.pipeline_id(),
- target_origin: origin,
- data: data.move_to_arraybuffer(),
+ source_origin,
+ target_origin,
+ data: data,
};
+ // Step 8
let _ = incumbent.script_to_constellation_chan().send(msg);
+ Ok(())
}
}
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 6581dbd54bb..ce88906deff 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -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;
diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs
index 57d8eaf8ff2..bbda6c24396 100644
--- a/components/script/dom/eventsource.rs
+++ b/components/script/dom/eventsource.rs
@@ -236,7 +236,7 @@ impl EventSourceContext {
DOMString::from(self.origin.clone()),
None,
event_source.last_event_id.borrow().clone(),
- vec![],
+ Vec::with_capacity(0),
)
};
// Step 7
diff --git a/components/script/dom/extendablemessageevent.rs b/components/script/dom/extendablemessageevent.rs
index c2ede65745a..dab06b7d79d 100644
--- a/components/script/dom/extendablemessageevent.rs
+++ b/components/script/dom/extendablemessageevent.rs
@@ -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)
+ }
}
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index f1b6c0000b9..7d07e67f9ec 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -9,17 +9,21 @@ use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlo
use crate::dom::bindings::conversions::{root_from_object, root_from_object_static};
use crate::dom::bindings::error::{report_pending_exception, ErrorInfo};
use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript};
use crate::dom::bindings::str::DOMString;
-use crate::dom::bindings::weakref::DOMTracker;
+use crate::dom::bindings::structuredclone;
+use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
use crate::dom::crypto::Crypto;
use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
use crate::dom::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventsource::EventSource;
use crate::dom::eventtarget::EventTarget;
+use crate::dom::messageevent::MessageEvent;
+use crate::dom::messageport::MessagePort;
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
use crate::dom::performance::Performance;
use crate::dom::window::Window;
@@ -36,34 +40,40 @@ use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
use crate::task_source::port_message::PortMessageQueue;
use crate::task_source::remote_event::RemoteEventTaskSource;
use crate::task_source::websocket::WebsocketTaskSource;
+use crate::task_source::TaskSource;
use crate::task_source::TaskSourceName;
use crate::timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle};
use crate::timers::{OneshotTimers, TimerCallback};
use content_security_policy::CspList;
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use dom_struct::dom_struct;
-use ipc_channel::ipc::IpcSender;
+use ipc_channel::ipc::{self, IpcSender};
+use ipc_channel::router::ROUTER;
use js::glue::{IsWrapper, UnwrapObjectDynamic};
use js::jsapi::JSObject;
use js::jsapi::{CurrentGlobalOrNull, GetNonCCWObjectGlobal};
use js::jsapi::{HandleObject, Heap};
use js::jsapi::{JSAutoRealm, JSContext};
+use js::jsval::UndefinedValue;
use js::panic::maybe_resume_unwind;
use js::rust::wrappers::EvaluateUtf8;
use js::rust::{get_object_class, CompileOptionsWrapper, ParentRuntime, Runtime};
use js::rust::{HandleValue, MutableHandleValue};
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
-use msg::constellation_msg::PipelineId;
+use msg::constellation_msg::{MessagePortId, MessagePortRouterId, PipelineId};
use net_traits::image_cache::ImageCache;
use net_traits::{CoreResourceThread, IpcSend, ResourceThreads};
use profile_traits::{mem as profile_mem, time as profile_time};
-use script_traits::{MsDuration, ScriptToConstellationChan, TimerEvent};
+use script_traits::transferable::MessagePortImpl;
+use script_traits::{
+ MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg, ScriptToConstellationChan, TimerEvent,
+};
use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource};
use servo_url::{MutableOrigin, ServoUrl};
use std::borrow::Cow;
use std::cell::Cell;
use std::collections::hash_map::Entry;
-use std::collections::HashMap;
+use std::collections::{HashMap, VecDeque};
use std::ffi::CString;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -85,6 +95,9 @@ pub struct GlobalScope {
crypto: MutNullableDom<Crypto>,
next_worker_id: Cell<WorkerId>,
+ /// The message-port router id for this global, if it is managing ports.
+ message_port_state: DomRefCell<MessagePortState>,
+
/// Pipeline id associated with this global.
pipeline_id: PipelineId,
@@ -168,6 +181,77 @@ pub struct GlobalScope {
user_agent: Cow<'static, str>,
}
+/// A wrapper for glue-code between the ipc router and the event-loop.
+struct MessageListener {
+ canceller: TaskCanceller,
+ task_source: PortMessageQueue,
+ context: Trusted<GlobalScope>,
+}
+
+/// Data representing a message-port managed by this global.
+#[derive(JSTraceable, MallocSizeOf)]
+pub enum ManagedMessagePort {
+ /// We keep ports pending when they are first transfer-received,
+ /// and only add them, and ask the constellation to complete the transfer,
+ /// in a subsequent task if the port hasn't been re-transfered.
+ Pending(MessagePortImpl, WeakRef<MessagePort>),
+ /// A port who was transferred into, or initially created in, this realm,
+ /// and that hasn't been re-transferred in the same task it was noted.
+ Added(MessagePortImpl, WeakRef<MessagePort>),
+}
+
+/// State representing whether this global is currently managing messageports.
+#[derive(JSTraceable, MallocSizeOf)]
+pub enum MessagePortState {
+ /// The message-port router id for this global, and a map of managed ports.
+ Managed(
+ MessagePortRouterId,
+ HashMap<MessagePortId, ManagedMessagePort>,
+ ),
+ /// This global is not managing any ports at this time.
+ UnManaged,
+}
+
+impl MessageListener {
+ /// A new message came in, handle it via a task enqueued on the event-loop.
+ /// A task is required, since we are using a trusted globalscope,
+ /// and we can only access the root from the event-loop.
+ fn notify(&self, msg: MessagePortMsg) {
+ match msg {
+ MessagePortMsg::CompleteTransfer(port_id, tasks) => {
+ let context = self.context.clone();
+ let _ = self.task_source.queue_with_canceller(
+ task!(process_complete_transfer: move || {
+ let global = context.root();
+ global.complete_port_transfer(port_id, tasks);
+ }),
+ &self.canceller,
+ );
+ },
+ MessagePortMsg::NewTask(port_id, task) => {
+ let context = self.context.clone();
+ let _ = self.task_source.queue_with_canceller(
+ task!(process_new_task: move || {
+ let global = context.root();
+ global.route_task_to_port(port_id, task);
+ }),
+ &self.canceller,
+ );
+ },
+ MessagePortMsg::RemoveMessagePort(port_id) => {
+ let context = self.context.clone();
+ let _ = self.task_source.queue_with_canceller(
+ task!(process_remove_message_port: move || {
+ let global = context.root();
+ global.remove_message_port(&port_id);
+ }),
+ &self.canceller,
+ );
+ },
+ }
+ }
+}
+
impl GlobalScope {
pub fn new_inherited(
pipeline_id: PipelineId,
@@ -184,6 +268,7 @@ impl GlobalScope {
user_agent: Cow<'static, str>,
) -> Self {
Self {
+ message_port_state: DomRefCell::new(MessagePortState::UnManaged),
eventtarget: EventTarget::new_inherited(),
crypto: Default::default(),
next_worker_id: Cell::new(WorkerId(0)),
@@ -209,6 +294,397 @@ impl GlobalScope {
}
}
+ /// Complete the transfer of a message-port.
+ fn complete_port_transfer(&self, port_id: MessagePortId, tasks: VecDeque<PortMessageTask>) {
+ let should_start = if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ match message_ports.get_mut(&port_id) {
+ None => {
+ panic!("CompleteTransfer msg received in a global not managing the port.");
+ },
+ Some(ManagedMessagePort::Pending(_, _)) => {
+ panic!("CompleteTransfer msg received for a pending port.");
+ },
+ Some(ManagedMessagePort::Added(port_impl, _port)) => {
+ port_impl.complete_transfer(tasks);
+ port_impl.enabled()
+ },
+ }
+ } else {
+ return warn!("CompleteTransfer msg received in a global not managing any ports.");
+ };
+ if should_start {
+ self.start_message_port(&port_id);
+ }
+ }
+
+ /// Update our state to un-managed,
+ /// and tell the constellation to drop the sender to our message-port router.
+ pub fn remove_message_ports_router(&self) {
+ if let MessagePortState::Managed(router_id, _message_ports) =
+ &*self.message_port_state.borrow()
+ {
+ let _ = self
+ .script_to_constellation_chan()
+ .send(ScriptMsg::RemoveMessagePortRouter(router_id.clone()));
+ }
+ *self.message_port_state.borrow_mut() = MessagePortState::UnManaged;
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#entangle>
+ pub fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) {
+ if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ for (port_id, entangled_id) in &[(port1, port2), (port2, port1)] {
+ match message_ports.get_mut(&port_id) {
+ None => {
+ return warn!("entangled_ports called on a global not managing the port.");
+ },
+ Some(ManagedMessagePort::Pending(port_impl, dom_port)) => {
+ dom_port
+ .root()
+ .expect("Port to be entangled to not have been GC'ed")
+ .entangle(entangled_id.clone());
+ port_impl.entangle(entangled_id.clone());
+ },
+ Some(ManagedMessagePort::Added(port_impl, dom_port)) => {
+ dom_port
+ .root()
+ .expect("Port to be entangled to not have been GC'ed")
+ .entangle(entangled_id.clone());
+ port_impl.entangle(entangled_id.clone());
+ },
+ }
+ }
+ } else {
+ panic!("entangled_ports called on a global not managing any ports.");
+ }
+
+ let _ = self
+ .script_to_constellation_chan()
+ .send(ScriptMsg::EntanglePorts(port1, port2));
+ }
+
+ /// Remove all referrences to a port.
+ pub fn remove_message_port(&self, port_id: &MessagePortId) {
+ let is_empty = if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ match message_ports.remove(&port_id) {
+ None => panic!("remove_message_port called on a global not managing the port."),
+ Some(_) => message_ports.is_empty(),
+ }
+ } else {
+ return warn!("remove_message_port called on a global not managing any ports.");
+ };
+ if is_empty {
+ // Remove our port router,
+ // it will be setup again if we start managing ports again.
+ self.remove_message_ports_router();
+ }
+ }
+
+ /// Handle the transfer of a port in the current task.
+ pub fn mark_port_as_transferred(&self, port_id: &MessagePortId) -> MessagePortImpl {
+ if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ let mut port = match message_ports.remove(&port_id) {
+ None => {
+ panic!("mark_port_as_transferred called on a global not managing the port.")
+ },
+ Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
+ Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
+ };
+ port.set_has_been_shipped();
+ let _ = self
+ .script_to_constellation_chan()
+ .send(ScriptMsg::MessagePortShipped(port_id.clone()));
+ port
+ } else {
+ panic!("mark_port_as_transferred called on a global not managing any ports.");
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
+ pub fn start_message_port(&self, port_id: &MessagePortId) {
+ if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ let port = match message_ports.get_mut(&port_id) {
+ None => panic!("start_message_port called on a unknown port."),
+ Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
+ Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
+ };
+ if let Some(message_buffer) = port.start() {
+ for task in message_buffer {
+ let port_id = port_id.clone();
+ let this = Trusted::new(&*self);
+ let _ = self.port_message_queue().queue(
+ task!(process_pending_port_messages: move || {
+ let target_global = this.root();
+ target_global.route_task_to_port(port_id, task);
+ }),
+ &self,
+ );
+ }
+ }
+ } else {
+ return warn!("start_message_port called on a global not managing any ports.");
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
+ pub fn close_message_port(&self, port_id: &MessagePortId) {
+ if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ let port = match message_ports.get_mut(&port_id) {
+ None => panic!("close_message_port called on an unknown port."),
+ Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
+ Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
+ };
+ port.close();
+ } else {
+ return warn!("close_message_port called on a global not managing any ports.");
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#message-port-post-message-steps>
+ // Steps 6 and 7
+ pub fn post_messageport_msg(&self, port_id: MessagePortId, task: PortMessageTask) {
+ if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ let port = match message_ports.get_mut(&port_id) {
+ None => panic!("post_messageport_msg called on an unknown port."),
+ Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
+ Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
+ };
+ if let Some(entangled_id) = port.entangled_port_id() {
+ // Step 7
+ let this = Trusted::new(&*self);
+ let _ = self.port_message_queue().queue(
+ task!(post_message: move || {
+ let global = this.root();
+ // Note: we do this in a task, as this will ensure the global and constellation
+ // are aware of any transfer that might still take place in the current task.
+ global.route_task_to_port(entangled_id, task);
+ }),
+ self,
+ );
+ }
+ } else {
+ return warn!("post_messageport_msg called on a global not managing any ports.");
+ }
+ }
+
+ /// If we don't know about the port,
+ /// send the message to the constellation for routing.
+ fn re_route_port_task(&self, port_id: MessagePortId, task: PortMessageTask) {
+ let _ = self
+ .script_to_constellation_chan()
+ .send(ScriptMsg::RerouteMessagePort(port_id, task));
+ }
+
+ /// Route the task to be handled by the relevant port.
+ pub fn route_task_to_port(&self, port_id: MessagePortId, task: PortMessageTask) {
+ let should_dispatch = if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ if !message_ports.contains_key(&port_id) {
+ self.re_route_port_task(port_id, task);
+ return;
+ }
+ let (port_impl, dom_port) = match message_ports.get_mut(&port_id) {
+ None => panic!("route_task_to_port called for an unknown port."),
+ Some(ManagedMessagePort::Pending(port_impl, dom_port)) => (port_impl, dom_port),
+ Some(ManagedMessagePort::Added(port_impl, dom_port)) => (port_impl, dom_port),
+ };
+
+ // If the port is not enabled yet, or if is awaiting the completion of it's transfer,
+ // the task will be buffered and dispatched upon enablement or completion of the transfer.
+ if let Some(task_to_dispatch) = port_impl.handle_incoming(task) {
+ // Get a corresponding DOM message-port object.
+ let dom_port = match dom_port.root() {
+ Some(dom_port) => dom_port,
+ None => panic!("Messageport Gc'ed too early"),
+ };
+ Some((dom_port, task_to_dispatch))
+ } else {
+ None
+ }
+ } else {
+ self.re_route_port_task(port_id, task);
+ return;
+ };
+ if let Some((dom_port, PortMessageTask { origin, data })) = should_dispatch {
+ // Substep 3-4
+ rooted!(in(*self.get_cx()) let mut message_clone = UndefinedValue());
+ if let Ok(ports) = structuredclone::read(self, data, message_clone.handle_mut()) {
+ // Substep 6
+ // Dispatch the event, using the dom message-port.
+ MessageEvent::dispatch_jsval(
+ &dom_port.upcast(),
+ self,
+ message_clone.handle(),
+ Some(&origin.ascii_serialization()),
+ None,
+ ports,
+ );
+ } else {
+ // Step 4, fire messageerror event.
+ MessageEvent::dispatch_error(&dom_port.upcast(), self);
+ }
+ }
+ }
+
+ /// Check all ports that have been transfer-received in the previous task,
+ /// and complete their transfer if they haven't been re-transferred.
+ pub fn maybe_add_pending_ports(&self) {
+ if let MessagePortState::Managed(router_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ let to_be_added: Vec<MessagePortId> = message_ports
+ .iter()
+ .filter_map(|(id, port_info)| match port_info {
+ ManagedMessagePort::Pending(_, _) => Some(id.clone()),
+ _ => None,
+ })
+ .collect();
+ for id in to_be_added {
+ let (id, port_info) = message_ports
+ .remove_entry(&id)
+ .expect("Collected port-id to match an entry");
+ if let ManagedMessagePort::Pending(port_impl, dom_port) = port_info {
+ let _ = self
+ .script_to_constellation_chan()
+ .send(ScriptMsg::NewMessagePort(
+ router_id.clone(),
+ port_impl.message_port_id().clone(),
+ ));
+ let new_port_info = ManagedMessagePort::Added(port_impl, dom_port);
+ let present = message_ports.insert(id, new_port_info);
+ assert!(present.is_none());
+ }
+ }
+ } else {
+ warn!("maybe_add_pending_ports called on a global not managing any ports.");
+ }
+ }
+
+ /// https://html.spec.whatwg.org/multipage/#ports-and-garbage-collection
+ pub fn perform_a_message_port_garbage_collection_checkpoint(&self) {
+ let is_empty = if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ let to_be_removed: Vec<MessagePortId> = message_ports
+ .iter()
+ .filter_map(|(id, port_info)| {
+ if let ManagedMessagePort::Added(_port_impl, dom_port) = port_info {
+ if dom_port.root().is_none() {
+ // Let the constellation know to drop this port and the one it is entangled with,
+ // and to forward this message to the script-process where the entangled is found.
+ let _ = self
+ .script_to_constellation_chan()
+ .send(ScriptMsg::RemoveMessagePort(id.clone()));
+ return Some(id.clone());
+ }
+ }
+ None
+ })
+ .collect();
+ for id in to_be_removed {
+ message_ports.remove(&id);
+ }
+ message_ports.is_empty()
+ } else {
+ false
+ };
+ if is_empty {
+ self.remove_message_ports_router();
+ }
+ }
+
+ /// Start tracking a message-port
+ pub fn track_message_port(&self, dom_port: &MessagePort, port_impl: Option<MessagePortImpl>) {
+ let mut current_state = self.message_port_state.borrow_mut();
+
+ if let MessagePortState::UnManaged = &*current_state {
+ // Setup a route for IPC, for messages from the constellation to our ports.
+ let (port_control_sender, port_control_receiver) =
+ ipc::channel().expect("ipc channel failure");
+ let context = Trusted::new(self);
+ let (task_source, canceller) = (
+ self.port_message_queue(),
+ self.task_canceller(TaskSourceName::PortMessage),
+ );
+ let listener = MessageListener {
+ canceller,
+ task_source,
+ context,
+ };
+ ROUTER.add_route(
+ port_control_receiver.to_opaque(),
+ Box::new(move |message| {
+ let msg = message.to();
+ match msg {
+ Ok(msg) => listener.notify(msg),
+ Err(err) => warn!("Error receiving a MessagePortMsg: {:?}", err),
+ }
+ }),
+ );
+ let router_id = MessagePortRouterId::new();
+ *current_state = MessagePortState::Managed(router_id.clone(), HashMap::new());
+ let _ = self
+ .script_to_constellation_chan()
+ .send(ScriptMsg::NewMessagePortRouter(
+ router_id,
+ port_control_sender,
+ ));
+ }
+
+ if let MessagePortState::Managed(router_id, message_ports) = &mut *current_state {
+ if let Some(port_impl) = port_impl {
+ // We keep transfer-received ports as "pending",
+ // and only ask the constellation to complete the transfer
+ // if they're not re-shipped in the current task.
+ message_ports.insert(
+ dom_port.message_port_id().clone(),
+ ManagedMessagePort::Pending(port_impl, WeakRef::new(dom_port)),
+ );
+
+ // Queue a task to complete the transfer,
+ // unless the port is re-transferred in the current task.
+ let this = Trusted::new(&*self);
+ let _ = self.port_message_queue().queue(
+ task!(process_pending_port_messages: move || {
+ let target_global = this.root();
+ target_global.maybe_add_pending_ports();
+ }),
+ &self,
+ );
+ } else {
+ // If this is a newly-created port, let the constellation immediately know.
+ let port_impl = MessagePortImpl::new(dom_port.message_port_id().clone());
+ message_ports.insert(
+ dom_port.message_port_id().clone(),
+ ManagedMessagePort::Added(port_impl, WeakRef::new(dom_port)),
+ );
+ let _ = self
+ .script_to_constellation_chan()
+ .send(ScriptMsg::NewMessagePort(
+ router_id.clone(),
+ dom_port.message_port_id().clone(),
+ ));
+ };
+ } else {
+ panic!("track_message_port should have first switched the state to managed.");
+ }
+ }
+
pub fn track_worker(&self, closing_worker: Arc<AtomicBool>) {
self.list_auto_close_worker
.borrow_mut()
@@ -550,7 +1026,7 @@ impl GlobalScope {
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
return worker.websocket_task_source();
}
- unreachable!()
+ unreachable!();
}
/// Evaluate JS code on this global scope.
diff --git a/components/script/dom/history.rs b/components/script/dom/history.rs
index 05dff680c2c..c02cdb42472 100644
--- a/components/script/dom/history.rs
+++ b/components/script/dom/history.rs
@@ -11,7 +11,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString};
-use crate::dom::bindings::structuredclone::StructuredCloneData;
+use crate::dom::bindings::structuredclone;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
@@ -27,7 +27,7 @@ use msg::constellation_msg::{HistoryStateId, TraversalDirection};
use net_traits::{CoreResourceMsg, IpcSend};
use profile_traits::ipc;
use profile_traits::ipc::channel;
-use script_traits::ScriptMsg;
+use script_traits::{ScriptMsg, StructuredSerializedData};
use servo_url::ServoUrl;
use std::cell::Cell;
@@ -115,11 +115,16 @@ impl History {
};
match serialized_data {
- Some(serialized_data) => {
+ Some(data) => {
+ let data = StructuredSerializedData {
+ serialized: data,
+ ports: None,
+ };
let global_scope = self.window.upcast::<GlobalScope>();
rooted!(in(*global_scope.get_cx()) let mut state = UndefinedValue());
- StructuredCloneData::Vector(serialized_data)
- .read(&global_scope, state.handle_mut());
+ if let Err(_) = structuredclone::read(&global_scope, data, state.handle_mut()) {
+ warn!("Error reading structuredclone data");
+ }
self.state.set(state.get());
},
None => {
@@ -185,8 +190,7 @@ impl History {
// TODO: Step 4
// Step 5
- rooted!(in(cx) let transfer = UndefinedValue());
- let serialized_data = StructuredCloneData::write(*cx, data, transfer.handle())?.move_to_arraybuffer();
+ let serialized_data = structuredclone::write(cx, data, None)?;
let new_url: ServoUrl = match url {
// Step 6
@@ -255,7 +259,7 @@ impl History {
};
let _ = self.window.upcast::<GlobalScope>().resource_threads().send(
- CoreResourceMsg::SetHistoryState(state_id, serialized_data.clone()),
+ CoreResourceMsg::SetHistoryState(state_id, serialized_data.serialized.clone()),
);
// TODO: Step 9 Update current entry to represent a GET request
@@ -267,7 +271,9 @@ impl History {
// Step 11
let global_scope = self.window.upcast::<GlobalScope>();
rooted!(in(*cx) let mut state = UndefinedValue());
- StructuredCloneData::Vector(serialized_data).read(&global_scope, state.handle_mut());
+ if let Err(_) = structuredclone::read(&global_scope, serialized_data, state.handle_mut()) {
+ warn!("Error reading structuredclone data");
+ }
// Step 12
self.state.set(state.get());
diff --git a/components/script/dom/messagechannel.rs b/components/script/dom/messagechannel.rs
index e0d226d709a..efe883c6e6e 100644
--- a/components/script/dom/messagechannel.rs
+++ b/components/script/dom/messagechannel.rs
@@ -4,7 +4,7 @@
use crate::dom::bindings::codegen::Bindings::MessageChannelBinding::{MessageChannelMethods, Wrap};
use crate::dom::bindings::error::{Error, Fallible};
-use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
+use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
@@ -28,18 +28,24 @@ impl MessageChannel {
// Step 2
let port2 = MessagePort::new(&incumbent);
+ incumbent.track_message_port(&*port1, None);
+ incumbent.track_message_port(&*port2, None);
+
// Step 3
- port1.entangle(&port2);
+ incumbent.entangle_ports(
+ port1.message_port_id().clone(),
+ port2.message_port_id().clone(),
+ );
// Steps 4-6
- let channel = reflect_dom_object(Box::new(
- MessageChannel {
+ let channel = reflect_dom_object(
+ Box::new(MessageChannel {
reflector_: Reflector::new(),
port1: Dom::from_ref(&port1),
port2: Dom::from_ref(&port2),
}),
global,
- Wrap
+ Wrap,
);
// Step 7
diff --git a/components/script/dom/messageevent.rs b/components/script/dom/messageevent.rs
index 8d002923814..cb8ebaaebd1 100644
--- a/components/script/dom/messageevent.rs
+++ b/components/script/dom/messageevent.rs
@@ -11,6 +11,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::RootedTraceableBox;
+use crate::dom::bindings::utils::message_ports_to_frozen_array;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
@@ -18,10 +19,8 @@ use crate::dom::messageport::MessagePort;
use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
-use js::conversions::ToJSValConvertible;
-use js::jsapi::{Heap, JS_FreezeObject, JSContext, JSObject};
-use js::jsapi::HandleObject as RawHandleObject;
-use js::jsval::{JSVal, UndefinedValue};
+use js::jsapi::{Heap, JSObject};
+use js::jsval::JSVal;
use js::rust::HandleValue;
use servo_atoms::Atom;
use std::ptr::NonNull;
@@ -108,7 +107,7 @@ impl MessageEvent {
init.origin.clone(),
source.as_ref().map(|source| &**source),
init.lastEventId.clone(),
- init.ports.clone().unwrap_or(vec![])
+ init.ports.clone().unwrap_or(vec![]),
);
Ok(ev)
}
@@ -136,6 +135,26 @@ impl MessageEvent {
);
messageevent.upcast::<Event>().fire(target);
}
+
+ pub fn dispatch_error(target: &EventTarget, scope: &GlobalScope) {
+ let init = MessageEventBinding::MessageEventInit::empty();
+ let source = init
+ .source
+ .as_ref()
+ .and_then(|inner| inner.as_ref().map(|source| source.window_proxy()));
+ let messageevent = MessageEvent::new(
+ scope,
+ atom!("messageerror"),
+ init.parent.bubbles,
+ init.parent.cancelable,
+ init.data.handle(),
+ init.origin.clone(),
+ source.as_ref().map(|source| &**source),
+ init.lastEventId.clone(),
+ init.ports.clone().unwrap_or(vec![]),
+ );
+ messageevent.upcast::<Event>().fire(target);
+ }
}
impl MessageEventMethods for MessageEvent {
@@ -166,14 +185,8 @@ impl MessageEventMethods for MessageEvent {
self.event.IsTrusted()
}
- #[allow(unsafe_code)]
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-ports>
- unsafe fn Ports(&self, cx: *mut JSContext) -> JSVal {
- rooted!(in(cx) let mut ports = UndefinedValue());
- self.ports.to_jsval(cx, ports.handle_mut());
-
- rooted!(in(cx) let obj = ports.to_object());
- JS_FreezeObject(cx, RawHandleObject::from(obj.handle()));
- *ports
+ fn Ports(&self, cx: JSContext) -> JSVal {
+ message_ports_to_frozen_array(self.ports.as_slice(), cx)
}
}
diff --git a/components/script/dom/messageport.rs b/components/script/dom/messageport.rs
index 10489f6d2ad..67d470a10a2 100644
--- a/components/script/dom/messageport.rs
+++ b/components/script/dom/messageport.rs
@@ -3,393 +3,343 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
-use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{MessagePortMethods, Wrap};
-use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_object};
+use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{
+ MessagePortMethods, PostMessageOptions, Wrap,
+};
+use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::error::{Error, ErrorResult};
-use crate::dom::bindings::inheritance::{Castable, HasParent};
-use crate::dom::bindings::refcounted::Trusted;
-use crate::dom::bindings::reflector::{DomObject, reflect_dom_object};
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
-use crate::dom::bindings::structuredclone::StructuredCloneData;
-use crate::dom::bindings::trace::JSTraceable;
+use crate::dom::bindings::structuredclone::{self, StructuredDataHolder};
+use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::transferable::Transferable;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
-use crate::dom::messageevent::MessageEvent;
-use crate::task_source::TaskSource;
-use crate::task_source::port_message::PortMessageQueue;
-use js::jsapi::{JSContext, JSStructuredCloneReader, JSObject, JSTracer, MutableHandleObject};
-use js::jsval::UndefinedValue;
-use js::rust::{CustomAutoRooterGuard, HandleValue};
-use servo_remutex::ReentrantMutex;
+use crate::script_runtime::JSContext as SafeJSContext;
+use dom_struct::dom_struct;
+use js::jsapi::Heap;
+use js::jsapi::{JSObject, MutableHandleObject};
+use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
+use msg::constellation_msg::{MessagePortId, MessagePortIndex, PipelineNamespaceId};
+use script_traits::PortMessageTask;
use std::cell::{Cell, RefCell};
-use std::collections::VecDeque;
-use std::mem;
-use std::os::raw;
+use std::collections::HashMap;
+use std::convert::TryInto;
+use std::num::NonZeroU32;
use std::rc::Rc;
-use std::sync::Arc;
-// FIXME: This is wrong, we need to figure out a better way of collecting message port objects per transfer
-thread_local! {
- pub static TRANSFERRED_MESSAGE_PORTS: RefCell<Vec<DomRoot<MessagePort>>> = RefCell::new(Vec::new())
-}
-
-struct PortMessageTask {
- origin: String,
- data: Vec<u8>,
-}
-
-pub struct MessagePortInternal {
- dom_port: RefCell<Option<Trusted<MessagePort>>>,
- port_message_queue: RefCell<PortMessageQueue>,
- enabled: Cell<bool>,
- has_been_shipped: Cell<bool>,
- entangled_port: RefCell<Option<Arc<ReentrantMutex<MessagePortInternal>>>>,
- pending_port_messages: RefCell<VecDeque<PortMessageTask>>,
+#[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 MessagePortInternal {
- fn new(port_message_queue: PortMessageQueue) -> MessagePortInternal {
- MessagePortInternal {
- dom_port: RefCell::new(None),
- port_message_queue: RefCell::new(port_message_queue),
- enabled: Cell::new(false),
- has_been_shipped: Cell::new(false),
+impl MessagePort {
+ fn new_inherited(message_port_id: MessagePortId) -> MessagePort {
+ MessagePort {
+ eventtarget: EventTarget::new_inherited(),
entangled_port: RefCell::new(None),
- pending_port_messages: RefCell::new(VecDeque::new()),
+ detached: Cell::new(false),
+ message_port_id,
}
}
- /// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
- // Step 7 substeps
- #[allow(unrooted_must_root)]
- fn process_pending_port_messages(&self) {
- let PortMessageTask { origin, data } = match self.pending_port_messages.borrow_mut().pop_front() {
- Some(task) => task,
- None => return,
- };
+ /// <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)
+ }
- // Substep 1
- let final_target_port = self.dom_port.borrow().as_ref().unwrap().root();
+ /// 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,
+ )
+ }
- // Substep 2
- let target_global = final_target_port.global();
+ /// <https://html.spec.whatwg.org/multipage/#entangle>
+ pub fn entangle(&self, other_id: MessagePortId) {
+ *self.entangled_port.borrow_mut() = Some(other_id);
+ }
- // Substep 3-4
- rooted!(in(target_global.get_cx()) let mut message_clone = UndefinedValue());
- let deserialize_result = StructuredCloneData::Vector(data).read(
- &target_global,
- message_clone.handle_mut()
- );
- if !deserialize_result {
- return;
- }
+ pub fn message_port_id(&self) -> &MessagePortId {
+ &self.message_port_id
+ }
- // Substep 5
- let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| {
- mem::replace(&mut *list.borrow_mut(), vec![])
- });
-
- // Substep 6
- MessageEvent::dispatch_jsval(
- final_target_port.upcast(),
- &target_global,
- message_clone.handle(),
- Some(&origin),
- None,
- new_ports,
- );
+ pub fn detached(&self) -> bool {
+ self.detached.get()
}
-}
-#[derive(DenyPublicFields, DomObject, MallocSizeOf)]
-#[must_root]
-#[repr(C)]
-pub struct MessagePort {
- eventtarget: EventTarget,
- detached: Cell<bool>,
- #[ignore_malloc_size_of = "Defined in std"]
- message_port_internal: Arc<ReentrantMutex<MessagePortInternal>>,
-}
+ /// <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);
+ }
-#[allow(unsafe_code)]
-unsafe impl JSTraceable for MessagePort {
- unsafe fn trace(&self, trc: *mut JSTracer) {
- if !self.detached.get() {
- self.eventtarget.trace(trc);
+ /// <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(());
}
- // Otherwise, do nothing.
- }
-}
-impl HasParent for MessagePort {
- type Parent = EventTarget;
+ // Step 1 is the transfer argument.
- fn as_parent(&self) -> &EventTarget {
- &self.eventtarget
- }
-}
+ let target_port = self.entangled_port.borrow();
-impl MessagePort {
- fn new_inherited(global: &GlobalScope) -> MessagePort {
- MessagePort {
- eventtarget: EventTarget::new_inherited(),
- detached: Cell::new(false),
- message_port_internal: Arc::new(
- ReentrantMutex::new(
- MessagePortInternal::new(global.port_message_queue().clone())
- )
- ),
- }
- }
+ // Step 3
+ let mut doomed = false;
- fn new_transferred(message_port_internal: Arc<ReentrantMutex<MessagePortInternal>>) -> MessagePort {
- MessagePort {
- eventtarget: EventTarget::new_inherited(),
- detached: Cell::new(false),
- message_port_internal,
- }
- }
+ 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);
+ }
- /// <https://html.spec.whatwg.org/multipage/#create-a-new-messageport-object>
- pub fn new(owner: &GlobalScope) -> DomRoot<MessagePort> {
- let message_port = reflect_dom_object(Box::new(MessagePort::new_inherited(owner)), owner, Wrap);
- {
- let internal = message_port.message_port_internal.lock().unwrap();
- *internal.dom_port.borrow_mut() = Some(Trusted::new(&*message_port));
+ // Step 4
+ if let Some(target_id) = target_port.as_ref() {
+ if port.message_port_id() == target_id {
+ doomed = true;
+ }
+ }
}
- message_port
- }
- /// <https://html.spec.whatwg.org/multipage/#entangle>
- pub fn entangle(&self, other: &MessagePort) {
- {
- let internal = self.message_port_internal.lock().unwrap();
- *internal.entangled_port.borrow_mut() = Some(other.message_port_internal.clone());
+ // 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(());
}
- let internal = other.message_port_internal.lock().unwrap();
- *internal.entangled_port.borrow_mut() = Some(self.message_port_internal.clone());
- }
- /// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
- // Step 7 substeps
- fn process_pending_port_messages(&self) {
- if self.detached.get() { return; }
- let internal = self.message_port_internal.lock().unwrap();
- internal.process_pending_port_messages();
+ // Step 6, done in MessagePortImpl.
+
+ let incumbent = match GlobalScope::incumbent() {
+ None => unreachable!("postMessage called with no incumbent global"),
+ Some(incumbent) => incumbent,
+ };
+
+ // Step 7
+ let task = PortMessageTask {
+ origin: incumbent.origin().immutable().clone(),
+ data,
+ };
+
+ // Have the global proxy this call to the corresponding MessagePortImpl.
+ self.global()
+ .post_messageport_msg(self.message_port_id().clone(), task);
+ Ok(())
}
}
impl Transferable for MessagePort {
/// <https://html.spec.whatwg.org/multipage/#message-ports:transfer-steps>
- #[allow(unsafe_code)]
- fn transfer(
- &self,
- _closure: *mut raw::c_void,
- content: *mut *mut raw::c_void,
- extra_data: *mut u64
- ) -> bool {
- {
- let internal = self.message_port_internal.lock().unwrap();
- // Step 1
- internal.has_been_shipped.set(true);
-
- // Step 3
- if let Some(ref other_port) = *internal.entangled_port.borrow() {
- let entangled_internal = other_port.lock().unwrap();
- // Substep 1
- entangled_internal.has_been_shipped.set(true);
- }; // This line MUST contain a semicolon, due to the strict drop check rule
+ fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()> {
+ if self.detached.get() {
+ return Err(());
}
- unsafe {
- // Steps 2, 3.2 and 4
- *content = Arc::into_raw(self.message_port_internal.clone()) as *mut raw::c_void;
+ let port_impls = match sc_holder {
+ StructuredDataHolder::Write(port_impls) => port_impls,
+ _ => panic!("Unexpected variant of StructuredDataHolder"),
+ };
- *extra_data = 0;
+ self.detached.set(true);
+ let id = self.message_port_id();
+
+ // 1. Run local transfer logic, and return the object to be transferred.
+ let transferred_port = self.global().mark_port_as_transferred(id);
+
+ // 2. Store the transferred object at a given key.
+ if let Some(ports) = port_impls.as_mut() {
+ ports.insert(id.clone(), transferred_port);
+ } else {
+ let mut ports = HashMap::new();
+ ports.insert(id.clone(), transferred_port);
+ *port_impls = Some(ports);
}
- true
+ let PipelineNamespaceId(name_space) = id.clone().namespace_id;
+ let MessagePortIndex(index) = id.clone().index;
+ let index = index.get();
+
+ let mut big: [u8; 8] = [0; 8];
+ let name_space = name_space.to_ne_bytes();
+ let index = index.to_ne_bytes();
+
+ let (left, right) = big.split_at_mut(4);
+ left.copy_from_slice(&name_space);
+ right.copy_from_slice(&index);
+
+ // 3. Return a u64 representation of the key where the object is stored.
+ Ok(u64::from_ne_bytes(big))
}
/// https://html.spec.whatwg.org/multipage/#message-ports:transfer-receiving-steps
- #[allow(unrooted_must_root, unsafe_code)]
fn transfer_receive(
- cx: *mut JSContext,
- _r: *mut JSStructuredCloneReader,
- _closure: *mut raw::c_void,
- content: *mut raw::c_void,
- _extra_data: u64,
- return_object: MutableHandleObject
- ) -> bool {
- let internal = unsafe { Arc::from_raw(content as *const ReentrantMutex<MessagePortInternal>) };
- let value = MessagePort::new_transferred(internal);
-
- // Step 2
- let owner = unsafe { GlobalScope::from_context(cx) };
- let message_port = reflect_dom_object(Box::new(value), &*owner, Wrap);
-
- {
- let internal = message_port.message_port_internal.lock().unwrap();
-
- // Step 1
- internal.has_been_shipped.set(true);
-
- let dom_port = Trusted::new(&*message_port);
- internal.enabled.set(false);
- *internal.dom_port.borrow_mut() = Some(dom_port);
- *internal.port_message_queue.borrow_mut() = owner.port_message_queue().clone();
- }
- return_object.set(message_port.reflector().rootable().get());
- TRANSFERRED_MESSAGE_PORTS.with(|list| {
- list.borrow_mut().push(message_port);
- });
+ 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"),
+ };
- true
- }
+ // 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"),
+ );
- fn detached(&self) -> Option<bool> {
- Some(self.detached.get())
- }
+ let id = MessagePortId {
+ namespace_id,
+ index,
+ };
- fn set_detached(&self, value: bool) {
- self.detached.set(value);
- }
+ // 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());
- fn transferable(&self) -> bool {
- !self.detached.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 {
- #[allow(unsafe_code)]
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
- unsafe fn PostMessage(
+ fn PostMessage(
&self,
- cx: *mut JSContext,
+ cx: SafeJSContext,
message: HandleValue,
- transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
+ transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
- if self.detached.get() { return Ok(()); }
- let internal = self.message_port_internal.lock().unwrap();
- // Step 1
- let target_port = internal.entangled_port.borrow();
-
- // Step 3
- let mut doomed = false;
-
- rooted!(in(cx) let mut val = UndefinedValue());
- let transfer = match *transfer {
- Some(ref vec) => {
- let ports = vec.iter().filter_map(|&obj| root_from_object::<MessagePort>(obj).ok());
- for port in ports {
- // Step 2
- if Arc::ptr_eq(&port.message_port_internal, &self.message_port_internal) {
- return Err(Error::DataClone);
- }
-
- // Step 4
- if let Some(target) = target_port.as_ref() {
- if Arc::ptr_eq(&port.message_port_internal, target) {
- doomed = true;
- }
- }
- }
-
- vec.to_jsval(cx, val.handle_mut());
- val
- }
- None => {
- Vec::<*mut JSObject>::new().to_jsval(cx, val.handle_mut());
- val
- }
- };
-
- // Step 5
- let data = StructuredCloneData::write(cx, message, transfer.handle())?.move_to_arraybuffer();
-
- // Step 6
- if target_port.is_none() || doomed { return Ok(()); }
-
- // Step 7
- let task = PortMessageTask {
- origin: self.global().origin().immutable().ascii_serialization(),
- data,
- };
-
- {
- let target_port = target_port.as_ref().unwrap();
- let target_internal = target_port.lock().unwrap();
- target_internal.pending_port_messages.borrow_mut().push_back(task);
-
- if target_internal.enabled.get() {
- let target_port = target_port.clone();
- let _ = target_internal.port_message_queue.borrow().queue(
- task!(process_pending_port_messages: move || {
- let internal = target_port.lock().unwrap();
- internal.process_pending_port_messages();
- }),
- &self.global()
- );
- }
+ if self.detached.get() {
+ return Ok(());
}
+ self.post_message_impl(cx, message, transfer)
+ }
- Ok(())
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
+ fn PostMessage_(
+ &self,
+ cx: SafeJSContext,
+ message: HandleValue,
+ options: RootedTraceableBox<PostMessageOptions>,
+ ) -> ErrorResult {
+ if self.detached.get() {
+ return Ok(());
+ }
+ let mut rooted = CustomAutoRooter::new(
+ options
+ .transfer
+ .as_ref()
+ .unwrap_or(&Vec::with_capacity(0))
+ .iter()
+ .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
+ .collect(),
+ );
+ let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
+ self.post_message_impl(cx, message, guard)
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
fn Start(&self) {
- let len = {
- let internal = self.message_port_internal.lock().unwrap();
- if internal.enabled.get() {
- return;
- }
- internal.enabled.set(true);
- let messages = internal.pending_port_messages.borrow();
- messages.len()
- };
-
- let global = self.global();
- for _ in 0..len {
- let port = Trusted::new(self);
- let _ = global.port_message_queue().queue(
- task!(process_pending_port_messages: move || {
- let this = port.root();
- this.process_pending_port_messages();
- }),
- &global
- );
+ if self.detached.get() {
+ return;
}
+ self.global().start_message_port(self.message_port_id());
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
fn Close(&self) {
- // Step 1
- self.detached.set(true);
-
- // Step 2
- let maybe_port = {
- let internal = self.message_port_internal.lock().unwrap();
- let mut maybe_port = internal.entangled_port.borrow_mut();
- maybe_port.take()
- };
-
- if let Some(other) = maybe_port {
- let other_internal = other.lock().unwrap();
- *other_internal.entangled_port.borrow_mut() = None;
+ if self.detached.get() {
+ return;
}
+ self.detached.set(true);
+ self.global().close_message_port(self.message_port_id());
}
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
fn GetOnmessage(&self) -> Option<Rc<EventHandlerNonNull>> {
+ if self.detached.get() {
+ return None;
+ }
let eventtarget = self.upcast::<EventTarget>();
eventtarget.get_event_handler_common("message")
}
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
fn SetOnmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
- self.Start();
- let eventtarget = self.upcast::<EventTarget>();
- eventtarget.set_event_handler_common("message", listener)
+ if self.detached.get() {
+ return;
+ }
+ self.set_onmessage(listener);
+ // Note: we cannot use the event_handler macro, due to the need to start the port.
+ self.global().start_message_port(self.message_port_id());
}
+
+ /// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessageerror>
+ event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
}
diff --git a/components/script/dom/serviceworker.rs b/components/script/dom/serviceworker.rs
index 8d33337f1c5..ec64036d305 100644
--- a/components/script/dom/serviceworker.rs
+++ b/components/script/dom/serviceworker.rs
@@ -4,6 +4,7 @@
use crate::dom::abstractworker::SimpleWorkerErrorHandler;
use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
use crate::dom::bindings::codegen::Bindings::ServiceWorkerBinding::{
ServiceWorkerMethods, ServiceWorkerState, Wrap,
};
@@ -13,15 +14,15 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::USVString;
-use crate::dom::bindings::structuredclone::StructuredCloneData;
+use crate::dom::bindings::structuredclone;
+use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::JSContext;
use crate::task::TaskOnce;
use dom_struct::dom_struct;
-use js::jsapi::JSContext;
-use js::jsval::UndefinedValue;
-use js::rust::HandleValue;
+use js::jsapi::{Heap, JSObject};
+use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use script_traits::{DOMMessage, ScriptMsg};
use servo_url::ServoUrl;
use std::cell::Cell;
@@ -79,41 +80,76 @@ impl ServiceWorker {
pub fn get_script_url(&self) -> ServoUrl {
ServoUrl::parse(&self.script_url.borrow().clone()).unwrap()
}
-}
-
-impl ServiceWorkerMethods for ServiceWorker {
- // https://w3c.github.io/ServiceWorker/#service-worker-state-attribute
- fn State(&self) -> ServiceWorkerState {
- self.state.get()
- }
- // https://w3c.github.io/ServiceWorker/#service-worker-url-attribute
- fn ScriptURL(&self) -> USVString {
- USVString(self.script_url.borrow().clone())
- }
-
- // https://w3c.github.io/ServiceWorker/#service-worker-postmessage
- fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult {
+ /// 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
- rooted!(in(*cx) let transfer = UndefinedValue());
- let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
+ let data = structuredclone::write(cx, message, Some(transfer))?;
+ let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
let msg_vec = DOMMessage {
- origin: self.global().origin().immutable().ascii_serialization(),
- data: data.move_to_arraybuffer(),
+ origin: incumbent.origin().immutable().clone(),
+ data,
};
let _ = self
.global()
.script_to_constellation_chan()
.send(ScriptMsg::ForwardDOMMessage(
msg_vec,
- self.scope_url.clone()
+ self.scope_url.clone(),
));
Ok(())
}
+}
+
+impl ServiceWorkerMethods for ServiceWorker {
+ // https://w3c.github.io/ServiceWorker/#service-worker-state-attribute
+ fn State(&self) -> ServiceWorkerState {
+ self.state.get()
+ }
+
+ // https://w3c.github.io/ServiceWorker/#service-worker-url-attribute
+ fn ScriptURL(&self) -> USVString {
+ USVString(self.script_url.borrow().clone())
+ }
+
+ /// 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
event_handler!(error, GetOnerror, SetOnerror);
diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs
index 8daf3b6dc2f..657f43627ed 100644
--- a/components/script/dom/serviceworkerglobalscope.rs
+++ b/components/script/dom/serviceworkerglobalscope.rs
@@ -12,12 +12,14 @@ use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
use crate::dom::bindings::str::DOMString;
+use crate::dom::bindings::structuredclone;
use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::extendableevent::ExtendableEvent;
use crate::dom::extendablemessageevent::ExtendableMessageEvent;
use crate::dom::globalscope::GlobalScope;
+use crate::dom::messageevent::MessageEvent;
use crate::dom::worker::TrustedWorkerAddress;
use crate::dom::workerglobalscope::WorkerGlobalScope;
use crate::fetch::load_whole_resource;
@@ -414,8 +416,17 @@ impl ServiceWorkerGlobalScope {
let target = self.upcast();
let _ac = enter_realm(&*scope);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
- assert!(data.read(scope.upcast(), message.handle_mut()));
- ExtendableMessageEvent::dispatch_jsval(target, scope.upcast(), message.handle());
+ if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut())
+ {
+ ExtendableMessageEvent::dispatch_jsval(
+ target,
+ scope.upcast(),
+ message.handle(),
+ ports,
+ );
+ } else {
+ MessageEvent::dispatch_error(target, scope.upcast());
+ }
},
CommonWorker(WorkerScriptMsg::Common(msg)) => {
self.upcast::<WorkerGlobalScope>().process_event(msg);
diff --git a/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl b/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl
index 488cbe052dd..2df97dbacfe 100644
--- a/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl
+++ b/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl
@@ -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();
};
diff --git a/components/script/dom/webidls/DissimilarOriginWindow.webidl b/components/script/dom/webidls/DissimilarOriginWindow.webidl
index 7c0ca4a2c80..bbfcb75b69b 100644
--- a/components/script/dom/webidls/DissimilarOriginWindow.webidl
+++ b/components/script/dom/webidls/DissimilarOriginWindow.webidl
@@ -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();
diff --git a/components/script/dom/webidls/ExtendableMessageEvent.webidl b/components/script/dom/webidls/ExtendableMessageEvent.webidl
index d623d4cb898..1976fd77dd8 100644
--- a/components/script/dom/webidls/ExtendableMessageEvent.webidl
+++ b/components/script/dom/webidls/ExtendableMessageEvent.webidl
@@ -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 {
diff --git a/components/script/dom/webidls/MessagePort.webidl b/components/script/dom/webidls/MessagePort.webidl
index c00eba5291a..c2fd1dbd1f4 100644
--- a/components/script/dom/webidls/MessagePort.webidl
+++ b/components/script/dom/webidls/MessagePort.webidl
@@ -8,10 +8,16 @@
[Exposed=(Window,Worker)]
interface MessagePort : EventTarget {
- [Throws] void postMessage(any message, optional sequence<object> transfer /*= []*/);
+ [Throws] void postMessage(any message, sequence<object> transfer /*= []*/);
+ [Throws] void postMessage(any message, optional PostMessageOptions options = {});
void start();
void close();
// event handlers
attribute EventHandler onmessage;
+ attribute EventHandler onmessageerror;
+};
+
+dictionary PostMessageOptions {
+ sequence<object> transfer;
};
diff --git a/components/script/dom/webidls/ServiceWorker.webidl b/components/script/dom/webidls/ServiceWorker.webidl
index 24a700ab103..60bb6dfc4b9 100644
--- a/components/script/dom/webidls/ServiceWorker.webidl
+++ b/components/script/dom/webidls/ServiceWorker.webidl
@@ -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;
diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl
index a4af9bf692b..26edbca5c7a 100644
--- a/components/script/dom/webidls/Window.webidl
+++ b/components/script/dom/webidls/Window.webidl
@@ -65,6 +65,8 @@
[Throws]
void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/);
+ [Throws]
+ void postMessage(any message, optional WindowPostMessageOptions options = {});
// also has obsolete members
};
@@ -172,3 +174,8 @@ partial interface Window {
[Pref="css.animations.testing.enabled"]
readonly attribute unsigned long runningAnimationCount;
};
+
+dictionary WindowPostMessageOptions {
+ USVString targetOrigin = "/";
+ sequence<object> transfer;
+};
diff --git a/components/script/dom/webidls/Worker.webidl b/components/script/dom/webidls/Worker.webidl
index 4e55d6fbe00..93df577ec32 100644
--- a/components/script/dom/webidls/Worker.webidl
+++ b/components/script/dom/webidls/Worker.webidl
@@ -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;
};
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 7f3b6d1fb8d..37b5b5e3b2c 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -13,11 +13,10 @@ use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryLi
use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState;
use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
- self, FrameRequestCallback, WindowMethods,
+ self, FrameRequestCallback, WindowMethods, WindowPostMessageOptions,
};
use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString;
-use crate::dom::bindings::conversions::ToJSValConvertible;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
@@ -25,7 +24,7 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString};
-use crate::dom::bindings::structuredclone::StructuredCloneData;
+use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::utils::{GlobalStaticData, WindowProxyHandler};
use crate::dom::bindings::weakref::DOMTracker;
@@ -44,7 +43,6 @@ use crate::dom::location::Location;
use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
use crate::dom::mediaquerylistevent::MediaQueryListEvent;
use crate::dom::messageevent::MessageEvent;
-use crate::dom::messageport::TRANSFERRED_MESSAGE_PORTS;
use crate::dom::navigator::Navigator;
use crate::dom::node::{document_from_node, from_untrusted_node_address, Node, NodeDamage};
use crate::dom::performance::Performance;
@@ -81,6 +79,7 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
use ipc_channel::ipc::{channel, IpcSender};
use ipc_channel::router::ROUTER;
+use js::jsapi::Heap;
use js::jsapi::JSAutoRealm;
use js::jsapi::JSObject;
use js::jsapi::JSPROP_ENUMERATE;
@@ -88,7 +87,7 @@ use js::jsapi::{GCReason, JS_GC};
use js::jsval::UndefinedValue;
use js::jsval::{JSVal, NullValue};
use js::rust::wrappers::JS_DefineProperty;
-use js::rust::{CustomAutoRooterGuard, HandleValue};
+use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use media::WindowGLContext;
use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
@@ -107,7 +106,10 @@ use script_layout_interface::rpc::{
use script_layout_interface::{PendingImageState, TrustedNodeAddress};
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use script_traits::{ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData};
-use script_traits::{ScriptMsg, ScriptToConstellationChan, ScrollState, TimerEvent, TimerEventId};
+use script_traits::{
+ ScriptMsg, ScriptToConstellationChan, ScrollState, StructuredSerializedData, TimerEvent,
+ TimerEventId,
+};
use script_traits::{TimerSchedulerMsg, WindowSizeData, WindowSizeType};
use selectors::attr::CaseSensitivity;
use servo_geometry::{f32_rect_to_au_rect, MaxRect};
@@ -976,31 +978,53 @@ impl WindowMethods for Window {
&self,
cx: JSContext,
message: HandleValue,
- origin: USVString,
- transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
+ target_origin: USVString,
+ mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
) -> ErrorResult {
- let source_global = GlobalScope::incumbent().expect("no incumbent global??");
- let source = source_global.as_window();
+ let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
+ let source = incumbent.as_window();
+ let source_origin = source.Document().origin().immutable().clone();
+
+ 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 3-5.
- let origin = match &origin.0[..] {
- "*" => None,
- "/" => Some(source.Document().origin().immutable().clone()),
- url => match ServoUrl::parse(&url) {
- Ok(url) => Some(url.origin().clone()),
- Err(_) => return Err(Error::Syntax),
- },
- };
+ /// <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 1-2, 6-8.
- rooted!(in(*cx) let mut val = UndefinedValue());
- (*transfer).as_ref().unwrap_or(&Vec::new()).to_jsval(*cx, val.handle_mut());
+ let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
+ let source = incumbent.as_window();
- let data = StructuredCloneData::write(*cx, message, val.handle())?;
+ let source_origin = source.Document().origin().immutable().clone();
- // Step 9.
- self.post_message(origin, &*source.window_proxy(), data);
- Ok(())
+ self.post_message_impl(
+ &options.targetOrigin,
+ source_origin,
+ source,
+ cx,
+ message,
+ transfer,
+ )
}
// https://html.spec.whatwg.org/multipage/#dom-window-captureevents
@@ -1298,6 +1322,34 @@ impl WindowMethods for Window {
}
impl Window {
+ /// https://html.spec.whatwg.org/multipage/#window-post-message-steps
+ fn post_message_impl(
+ &self,
+ target_origin: &USVString,
+ source_origin: ImmutableOrigin,
+ source: &Window,
+ cx: JSContext,
+ message: HandleValue,
+ transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
+ ) -> ErrorResult {
+ // Step 1-2, 6-8.
+ let data = structuredclone::write(cx, message, transfer)?;
+
+ // Step 3-5.
+ let target_origin = match target_origin.0[..].as_ref() {
+ "*" => None,
+ "/" => Some(source_origin.clone()),
+ url => match ServoUrl::parse(&url) {
+ Ok(url) => Some(url.origin().clone()),
+ Err(_) => return Err(Error::Syntax),
+ },
+ };
+
+ // Step 9.
+ self.post_message(target_origin, source_origin, &*source.window_proxy(), data);
+ Ok(())
+ }
+
// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
pub fn paint_worklet(&self) -> DomRoot<Worklet> {
self.paint_worklet.or_init(|| self.new_paint_worklet())
@@ -1344,6 +1396,9 @@ impl Window {
// thread, informing it that it can safely free the memory.
self.Document().upcast::<Node>().teardown();
+ // Tell the constellation to drop the sender to our message-port router, if there is any.
+ self.upcast::<GlobalScope>().remove_message_ports_router();
+
// Clean up any active promises
// https://github.com/servo/servo/issues/15318
if let Some(custom_elements) = self.custom_element_registry.get() {
@@ -2345,8 +2400,9 @@ impl Window {
pub fn post_message(
&self,
target_origin: Option<ImmutableOrigin>,
+ source_origin: ImmutableOrigin,
source: &WindowProxy,
- serialize_with_transfer_result: StructuredCloneData,
+ data: StructuredSerializedData,
) {
let this = Trusted::new(self);
let source = Trusted::new(source);
@@ -2367,26 +2423,23 @@ impl Window {
let obj = this.reflector().get_jsobject();
let _ac = JSAutoRealm::new(*cx, obj.get());
rooted!(in(*cx) let mut message_clone = UndefinedValue());
- assert!(serialize_with_transfer_result.read(
- this.upcast(),
- message_clone.handle_mut(),
- ));
-
- // Step 7.6.
- let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| {
- mem::replace(&mut *list.borrow_mut(), vec![])
- });
-
- // Step 7.7.
- // TODO(#12719): Set the other attributes.
- MessageEvent::dispatch_jsval(
- this.upcast(),
- this.upcast(),
- message_clone.handle(),
- Some(&document.origin().immutable().ascii_serialization()),
- Some(&*source),
- new_ports,
- );
+ if let Ok(ports) = structuredclone::read(this.upcast(), data, message_clone.handle_mut()) {
+ // Step 7.6, 7.7
+ MessageEvent::dispatch_jsval(
+ this.upcast(),
+ this.upcast(),
+ message_clone.handle(),
+ Some(&source_origin.ascii_serialization()),
+ Some(&*source),
+ ports,
+ );
+ } else {
+ // Step 4, fire messageerror.
+ MessageEvent::dispatch_error(
+ this.upcast(),
+ this.upcast(),
+ );
+ }
});
// FIXME(nox): Why are errors silenced here?
// TODO(#12718): Use the "posted message task source".
diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs
index 1a8d9c292dc..3b9830f8ded 100644
--- a/components/script/dom/worker.rs
+++ b/components/script/dom/worker.rs
@@ -5,6 +5,7 @@
use crate::compartments::enter_realm;
use crate::dom::abstractworker::SimpleWorkerErrorHandler;
use crate::dom::abstractworker::WorkerScriptMsg;
+use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
use crate::dom::bindings::codegen::Bindings::WorkerBinding;
use crate::dom::bindings::codegen::Bindings::WorkerBinding::{WorkerMethods, WorkerOptions};
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
@@ -13,7 +14,8 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::USVString;
-use crate::dom::bindings::structuredclone::StructuredCloneData;
+use crate::dom::bindings::structuredclone;
+use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::dedicatedworkerglobalscope::{
DedicatedWorkerGlobalScope, DedicatedWorkerScriptMsg,
};
@@ -27,10 +29,10 @@ use crossbeam_channel::{unbounded, Sender};
use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg};
use dom_struct::dom_struct;
use ipc_channel::ipc;
-use js::jsapi::JS_RequestInterruptCallback;
+use js::jsapi::{Heap, JSObject, JS_RequestInterruptCallback};
use js::jsval::UndefinedValue;
-use js::rust::HandleValue;
-use script_traits::WorkerScriptLoadOrigin;
+use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
+use script_traits::{StructuredSerializedData, WorkerScriptLoadOrigin};
use std::cell::Cell;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
@@ -140,7 +142,7 @@ impl Worker {
pub fn handle_message(
address: TrustedWorkerAddress,
origin: String,
- data: StructuredCloneData,
+ data: StructuredSerializedData,
) {
let worker = address.root();
@@ -152,21 +154,34 @@ impl Worker {
let target = worker.upcast();
let _ac = enter_realm(target);
rooted!(in(*global.get_cx()) let mut message = UndefinedValue());
- assert!(data.read(&global, message.handle_mut()));
- MessageEvent::dispatch_jsval(target, &global, message.handle(), Some(&origin), None, vec![]);
+ if let Ok(ports) = structuredclone::read(&global, data, message.handle_mut()) {
+ MessageEvent::dispatch_jsval(
+ target,
+ &global,
+ message.handle(),
+ Some(&origin),
+ None,
+ ports,
+ );
+ } else {
+ // Step 4 of the "port post message steps" of the implicit messageport, fire messageerror.
+ MessageEvent::dispatch_error(target, &global);
+ }
}
pub fn dispatch_simple_error(address: TrustedWorkerAddress) {
let worker = address.root();
worker.upcast().fire_event(atom!("error"));
}
-}
-impl WorkerMethods for Worker {
- // https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
- fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult {
- rooted!(in(*cx) let transfer = UndefinedValue());
- let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
+ /// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
+ fn post_message_impl(
+ &self,
+ cx: JSContext,
+ message: HandleValue,
+ transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
+ ) -> ErrorResult {
+ let data = structuredclone::write(cx, message, Some(transfer))?;
let address = Trusted::new(self);
// NOTE: step 9 of https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage
@@ -174,12 +189,44 @@ impl WorkerMethods for Worker {
let _ = self.sender.send(DedicatedWorkerScriptMsg::CommonWorker(
address,
WorkerScriptMsg::DOMMessage {
- origin: self.global().origin().immutable().ascii_serialization(),
+ origin: self.global().origin().immutable().clone(),
data,
},
));
Ok(())
}
+}
+
+impl WorkerMethods for Worker {
+ /// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
+ fn PostMessage(
+ &self,
+ cx: JSContext,
+ message: HandleValue,
+ transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
+ ) -> ErrorResult {
+ self.post_message_impl(cx, message, transfer)
+ }
+
+ /// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
+ fn PostMessage_(
+ &self,
+ cx: JSContext,
+ message: HandleValue,
+ options: RootedTraceableBox<PostMessageOptions>,
+ ) -> ErrorResult {
+ let mut rooted = CustomAutoRooter::new(
+ options
+ .transfer
+ .as_ref()
+ .unwrap_or(&Vec::with_capacity(0))
+ .iter()
+ .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
+ .collect(),
+ );
+ let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
+ self.post_message_impl(cx, message, guard)
+ }
#[allow(unsafe_code)]
// https://html.spec.whatwg.org/multipage/#terminate-a-worker
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index fac11949d70..6a7a69651e7 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -38,7 +38,6 @@ use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::ThreadLocalStackRoots;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection};
use crate::dom::bindings::str::DOMString;
-use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::bindings::utils::WRAP_CALLBACKS;
use crate::dom::customelementregistry::{
@@ -133,6 +132,7 @@ use script_traits::CompositorEvent::{
CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent,
WheelEvent,
};
+use script_traits::StructuredSerializedData;
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{
DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement,
@@ -1589,6 +1589,11 @@ impl ScriptThread {
continue;
}
let window = document.window();
+
+ window
+ .upcast::<GlobalScope>()
+ .perform_a_message_port_garbage_collection_checkpoint();
+
let pending_reflows = window.get_pending_reflow_count();
if pending_reflows > 0 {
window.reflow(ReflowGoal::Full, ReflowReason::ImageLoaded);
@@ -1867,12 +1872,14 @@ impl ScriptThread {
source: source_pipeline_id,
source_browsing_context,
target_origin: origin,
+ source_origin,
data,
} => self.handle_post_message_msg(
target_pipeline_id,
source_pipeline_id,
source_browsing_context,
origin,
+ source_origin,
data,
),
ConstellationControlMsg::UpdatePipelineId(
@@ -2525,7 +2532,8 @@ impl ScriptThread {
source_pipeline_id: PipelineId,
source_browsing_context: TopLevelBrowsingContextId,
origin: Option<ImmutableOrigin>,
- data: Vec<u8>,
+ source_origin: ImmutableOrigin,
+ data: StructuredSerializedData,
) {
match { self.documents.borrow().find_window(pipeline_id) } {
None => return warn!("postMessage after target pipeline {} closed.", pipeline_id),
@@ -2547,7 +2555,7 @@ impl ScriptThread {
Some(source) => source,
};
// FIXME(#22512): enqueues a task; unnecessary delay.
- window.post_message(origin, &*source, StructuredCloneData::Vector(data))
+ window.post_message(origin, source_origin, &*source, data)
},
}
}
diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs
index 6e7884a891e..109628ae94e 100644
--- a/components/script/serviceworker_manager.rs
+++ b/components/script/serviceworker_manager.rs
@@ -8,7 +8,6 @@
//! active_workers map
use crate::dom::abstractworker::WorkerScriptMsg;
-use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::serviceworkerglobalscope::{ServiceWorkerGlobalScope, ServiceWorkerScriptMsg};
use crate::dom::serviceworkerregistration::longest_prefix_match;
use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
@@ -136,7 +135,6 @@ impl ServiceWorkerManager {
fn forward_message(&self, msg: DOMMessage, sender: &Sender<ServiceWorkerScriptMsg>) {
let DOMMessage { origin, data } = msg;
- let data = StructuredCloneData::Vector(data);
let _ = sender.send(ServiceWorkerScriptMsg::CommonWorker(
WorkerScriptMsg::DOMMessage { origin, data },
));
diff --git a/components/script/task_source/port_message.rs b/components/script/task_source/port_message.rs
index 8541f89bc64..e9d1766f521 100644
--- a/components/script/task_source/port_message.rs
+++ b/components/script/task_source/port_message.rs
@@ -9,7 +9,7 @@ use msg::constellation_msg::PipelineId;
use std::fmt;
#[derive(JSTraceable)]
-pub struct PortMessageQueue(pub Box<ScriptChan + Send + 'static>, pub PipelineId);
+pub struct PortMessageQueue(pub Box<dyn ScriptChan + Send + 'static>, pub PipelineId);
impl Clone for PortMessageQueue {
fn clone(&self) -> PortMessageQueue {
@@ -26,11 +26,7 @@ impl fmt::Debug for PortMessageQueue {
impl TaskSource for PortMessageQueue {
const NAME: TaskSourceName = TaskSourceName::PortMessage;
- fn queue_with_canceller<T>(
- &self,
- task: T,
- canceller: &TaskCanceller,
- ) -> Result<(), ()>
+ fn queue_with_canceller<T>(&self, task: T, canceller: &TaskCanceller) -> Result<(), ()>
where
T: TaskOnce + 'static,
{