aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeith Yeung <kungfukeith11@gmail.com>2016-12-11 03:52:08 -0800
committerGregory Terzian <gterzian@users.noreply.github.com>2019-10-19 14:12:22 +0800
commitc3b17c1201441c9a24c4b272108aea0196fbf1b9 (patch)
tree0de5b1ba2b8579338ffcf5639e3e06797b9d95ff
parent605ddbecd4dfbbb67849cf63774b2d7635fc3601 (diff)
downloadservo-c3b17c1201441c9a24c4b272108aea0196fbf1b9.tar.gz
servo-c3b17c1201441c9a24c4b272108aea0196fbf1b9.zip
begin messageport, transferable objects, impl
Accept transfer argument for StructuredCloneData::write Allow structured clone reads to return a boolean Add Transferable trait Add basic skeletons to MessagePort Implement transfer and transfer-receiving steps on MessagePort Use transfer and transfer_receive in StructuredClone callbacks Implement MessageChannel Freeze the array object for the MessageEvent ports attribute Implement transfer argument on window.postMessage Use ReentrantMutex instead for MessagePortInternal Accept origin as a parameter in dispatch_jsval Fix BorrowMut crash with pending_port_message Detach port on closure and check for detached during transfer Enable webmessaging tests fix webidl fix
-rw-r--r--Cargo.lock2
-rw-r--r--components/msg/constellation_msg.rs1
-rw-r--r--components/profile/time.rs1
-rw-r--r--components/profile_traits/time.rs1
-rw-r--r--components/script/Cargo.toml1
-rw-r--r--components/script/dom/abstractworker.rs5
-rw-r--r--components/script/dom/abstractworkerglobalscope.rs2
-rw-r--r--components/script/dom/bindings/mod.rs1
-rw-r--r--components/script/dom/bindings/structuredclone.rs91
-rw-r--r--components/script/dom/bindings/transferable.rs29
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs21
-rw-r--r--components/script/dom/dissimilaroriginwindow.rs3
-rw-r--r--components/script/dom/eventsource.rs1
-rw-r--r--components/script/dom/globalscope.rs21
-rw-r--r--components/script/dom/history.rs3
-rw-r--r--components/script/dom/messagechannel.rs60
-rw-r--r--components/script/dom/messageevent.rs40
-rw-r--r--components/script/dom/messageport.rs395
-rw-r--r--components/script/dom/mod.rs2
-rw-r--r--components/script/dom/serviceworker.rs12
-rw-r--r--components/script/dom/serviceworkerglobalscope.rs4
-rw-r--r--components/script/dom/webidls/MessageChannel.webidl14
-rw-r--r--components/script/dom/webidls/MessageEvent.webidl6
-rw-r--r--components/script/dom/webidls/MessagePort.webidl17
-rw-r--r--components/script/dom/webidls/Window.webidl3
-rw-r--r--components/script/dom/websocket.rs1
-rw-r--r--components/script/dom/window.rs37
-rw-r--r--components/script/dom/worker.rs18
-rw-r--r--components/script/dom/workerglobalscope.rs5
-rw-r--r--components/script/script_runtime.rs1
-rw-r--r--components/script/script_thread.rs11
-rw-r--r--components/script/serviceworker_manager.rs4
-rw-r--r--components/script/task_manager.rs13
-rw-r--r--components/script/task_source/mod.rs2
-rw-r--r--components/script/task_source/port_message.rs45
-rw-r--r--components/script_traits/script_msg.rs7
-rw-r--r--tests/wpt/include.ini2
37 files changed, 801 insertions, 81 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 001d8c21963..6f791057f5c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3154,6 +3154,7 @@ dependencies = [
"malloc_size_of_derive",
"parking_lot",
"serde",
+ "servo_url",
"size_of_test",
"webrender_api",
]
@@ -4134,6 +4135,7 @@ dependencies = [
"app_units",
"backtrace",
"base64",
+ "bincode",
"bitflags",
"bluetooth_traits",
"canvas_traits",
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index 94370a56e2c..fdeba44b71d 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -419,6 +419,7 @@ pub enum ScriptHangAnnotation {
ExitFullscreen,
WebVREvent,
PerformanceTimelineTask,
+ PortMessage,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
diff --git a/components/profile/time.rs b/components/profile/time.rs
index 25933aea253..3dac5663799 100644
--- a/components/profile/time.rs
+++ b/components/profile/time.rs
@@ -136,6 +136,7 @@ impl Formattable for ProfilerCategory {
ProfilerCategory::ScriptParseHTML => "Script Parse HTML",
ProfilerCategory::ScriptParseXML => "Script Parse XML",
ProfilerCategory::ScriptPlannedNavigation => "Script Planned Navigation",
+ ProfilerCategory::ScriptPortMessage => "Script Port Message",
ProfilerCategory::ScriptResize => "Script Resize",
ProfilerCategory::ScriptEvent => "Script Event",
ProfilerCategory::ScriptUpdateReplacedElement => "Script Update Replaced Element",
diff --git a/components/profile_traits/time.rs b/components/profile_traits/time.rs
index f6ce63bc426..8881dcdef2b 100644
--- a/components/profile_traits/time.rs
+++ b/components/profile_traits/time.rs
@@ -108,6 +108,7 @@ pub enum ProfilerCategory {
ScriptWorkletEvent = 0x7a,
ScriptPerformanceEvent = 0x7b,
ScriptHistoryEvent = 0x7c,
+ ScriptPortMessage = 0x7d,
TimeToFirstPaint = 0x80,
TimeToFirstContentfulPaint = 0x81,
TimeToInteractive = 0x82,
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index e521235f1c6..efef253317c 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -97,6 +97,7 @@ 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 8754bb30d96..930f48cdcea 100644
--- a/components/script/dom/abstractworker.rs
+++ b/components/script/dom/abstractworker.rs
@@ -12,7 +12,10 @@ pub enum WorkerScriptMsg {
/// Common variants associated with the script messages
Common(CommonScriptMsg),
/// Message sent through Worker.postMessage
- DOMMessage(StructuredCloneData),
+ DOMMessage {
+ origin: String,
+ data: StructuredCloneData,
+ }
}
pub struct SimpleWorkerErrorHandler<T: DomObject> {
diff --git a/components/script/dom/abstractworkerglobalscope.rs b/components/script/dom/abstractworkerglobalscope.rs
index af7291c013c..6ffeba85d5e 100644
--- a/components/script/dom/abstractworkerglobalscope.rs
+++ b/components/script/dom/abstractworkerglobalscope.rs
@@ -75,7 +75,7 @@ impl ScriptPort for Receiver<DedicatedWorkerScriptMsg> {
};
match common_msg {
WorkerScriptMsg::Common(script_msg) => Ok(script_msg),
- WorkerScriptMsg::DOMMessage(_) => panic!("unexpected worker event message!"),
+ WorkerScriptMsg::DOMMessage { .. } => panic!("unexpected worker event message!"),
}
}
}
diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs
index 48ee7674c92..0bcdbf4fcfd 100644
--- a/components/script/dom/bindings/mod.rs
+++ b/components/script/dom/bindings/mod.rs
@@ -154,6 +154,7 @@ pub mod settings_stack;
pub mod str;
pub mod structuredclone;
pub mod trace;
+pub mod transferable;
pub mod utils;
pub mod weakref;
pub mod xmlname;
diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs
index f35a4af7210..2e33613b29d 100644
--- a/components/script/dom/bindings/structuredclone.rs
+++ b/components/script/dom/bindings/structuredclone.rs
@@ -10,8 +10,10 @@ use crate::dom::bindings::conversions::root_from_handleobject;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::transferable::Transferable;
use crate::dom::blob::{Blob, BlobImpl};
use crate::dom::globalscope::GlobalScope;
+use crate::dom::messageport::MessagePort;
use js::glue::CopyJSStructuredCloneData;
use js::glue::DeleteJSAutoStructuredCloneBuffer;
use js::glue::GetLengthOfJSStructuredCloneData;
@@ -44,6 +46,7 @@ enum StructuredCloneTags {
/// To support additional types, add new tags with values incremented from the last one before Max.
Min = 0xFFFF8000,
DomBlob = 0xFFFF8001,
+ MessagePort = 0xFFFF8002,
Max = 0xFFFFFFFF,
}
@@ -189,26 +192,43 @@ unsafe extern "C" fn write_callback(
}
unsafe extern "C" fn read_transfer_callback(
- _cx: *mut JSContext,
- _r: *mut JSStructuredCloneReader,
- _tag: u32,
- _content: *mut raw::c_void,
- _extra_data: u64,
- _closure: *mut raw::c_void,
- _return_object: RawMutableHandleObject,
+ cx: *mut JSContext,
+ r: *mut JSStructuredCloneReader,
+ tag: u32,
+ content: *mut raw::c_void,
+ extra_data: u64,
+ closure: *mut raw::c_void,
+ return_object: RawMutableHandleObject,
) -> bool {
- false
+ if tag == StructuredCloneTags::MessagePort as u32 {
+ <MessagePort as Transferable>::transfer_receive(cx, r, closure, content, extra_data, return_object)
+ } else {
+ false
+ }
}
+/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
unsafe extern "C" fn write_transfer_callback(
_cx: *mut JSContext,
- _obj: RawHandleObject,
- _closure: *mut raw::c_void,
- _tag: *mut u32,
- _ownership: *mut TransferableOwnership,
- _content: *mut *mut raw::c_void,
- _extra_data: *mut u64,
+ obj: RawHandleObject,
+ closure: *mut raw::c_void,
+ tag: *mut u32,
+ ownership: *mut TransferableOwnership,
+ 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;
+ }
+
+ *tag = StructuredCloneTags::MessagePort as u32;
+ *ownership = TransferableOwnership::SCTAG_TMO_CUSTOM;
+ if port.transfer(closure, content, extra_data) {
+ port.set_detached(true);
+ return true;
+ }
+ }
false
}
@@ -256,7 +276,11 @@ pub enum StructuredCloneData {
impl StructuredCloneData {
// TODO: should this be unsafe?
/// Writes a structured clone. Returns a `DataClone` error if that fails.
- pub fn write(cx: *mut JSContext, message: HandleValue) -> Fallible<StructuredCloneData> {
+ pub fn write(
+ cx: *mut JSContext,
+ message: HandleValue,
+ transfer: HandleValue,
+ ) -> Fallible<StructuredCloneData> {
unsafe {
let scbuf = NewJSAutoStructuredCloneBuffer(
StructuredCloneScope::DifferentProcess,
@@ -275,7 +299,7 @@ impl StructuredCloneData {
policy,
&STRUCTURED_CLONE_CALLBACKS,
ptr::null_mut(),
- HandleValue::undefined(),
+ transfer,
);
if !result {
JS_ClearPendingException(cx);
@@ -306,7 +330,12 @@ impl StructuredCloneData {
/// Reads a structured clone.
///
/// Panics if `JS_ReadStructuredClone` fails.
- fn read_clone(global: &GlobalScope, data: *mut u64, nbytes: size_t, rval: MutableHandleValue) {
+ 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 };
@@ -320,31 +349,33 @@ impl StructuredCloneData {
WriteBytesToJSStructuredCloneData(data as *const u8, nbytes, scdata);
- assert!(JS_ReadStructuredClone(
- *cx,
- scdata,
- JS_STRUCTURED_CLONE_VERSION,
- StructuredCloneScope::DifferentProcess,
- rval,
- &STRUCTURED_CLONE_CALLBACKS,
- sc_holder_ptr as *mut raw::c_void
- ));
+ 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
}
}
/// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone.
- pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) {
+ 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::read_clone(global, data, nbytes, rval)
+ }
StructuredCloneData::Struct(data, nbytes) => {
StructuredCloneData::read_clone(global, data, nbytes, rval)
- },
+ }
}
}
}
diff --git a/components/script/dom/bindings/transferable.rs b/components/script/dom/bindings/transferable.rs
new file mode 100644
index 00000000000..fcf3ad93ab8
--- /dev/null
+++ b/components/script/dom/bindings/transferable.rs
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Trait representing the concept of [transferable objects]
+//! (https://html.spec.whatwg.org/multipage/#transferable-objects).
+use crate::dom::bindings::reflector::DomObject;
+use js::jsapi::{JSContext, JSStructuredCloneReader, MutableHandleObject};
+use std::os::raw;
+
+pub trait Transferable : DomObject {
+ fn transfer(
+ &self,
+ closure: *mut raw::c_void,
+ content: *mut *mut raw::c_void,
+ extra_data: *mut u64,
+ ) -> bool;
+ 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;
+ fn detached(&self) -> Option<bool> { None }
+ fn set_detached(&self, _value: bool) { }
+ fn transferable(&self) -> bool { false }
+}
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
index a3adde75498..d0e8ba8258d 100644
--- a/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -462,13 +462,20 @@ impl DedicatedWorkerGlobalScope {
fn handle_script_event(&self, msg: WorkerScriptMsg) {
match msg {
- WorkerScriptMsg::DOMMessage(data) => {
+ WorkerScriptMsg::DOMMessage { origin, data } => {
let scope = self.upcast::<WorkerGlobalScope>();
let target = self.upcast();
let _ac = enter_realm(self);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
- data.read(scope.upcast(), message.handle_mut());
- MessageEvent::dispatch_jsval(target, scope.upcast(), message.handle(), None, None);
+ assert!(data.read(scope.upcast(), message.handle_mut()));
+ MessageEvent::dispatch_jsval(
+ target,
+ scope.upcast(),
+ message.handle(),
+ Some(&origin),
+ None,
+ vec![],
+ );
},
WorkerScriptMsg::Common(msg) => {
self.upcast::<WorkerGlobalScope>().process_event(msg);
@@ -562,11 +569,13 @@ unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool {
impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope {
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn PostMessage(&self, cx: SafeJSContext, message: HandleValue) -> ErrorResult {
- let data = StructuredCloneData::write(*cx, message)?;
+ rooted!(in(*cx) let transfer = UndefinedValue());
+ let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
let worker = self.worker.borrow().as_ref().unwrap().clone();
- let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
+ let pipeline_id = self.global().pipeline_id();
+ let origin = self.global().origin().immutable().ascii_serialization();
let task = Box::new(task!(post_worker_message: move || {
- Worker::handle_message(worker, data);
+ Worker::handle_message(worker, origin, data);
}));
// TODO: Change this task source to a new `unshipped-port-message-queue` task source
self.parent_sender
diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs
index 8f7eda58100..31bb13051f7 100644
--- a/components/script/dom/dissimilaroriginwindow.rs
+++ b/components/script/dom/dissimilaroriginwindow.rs
@@ -150,7 +150,8 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
// Step 1-2, 6-8.
// TODO(#12717): Should implement the `transfer` argument.
- let data = StructuredCloneData::write(*cx, message)?;
+ rooted!(in(*cx) let transfer = UndefinedValue());
+ let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
// Step 9.
self.post_message(origin, data);
diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs
index 92eb2c2f414..57d8eaf8ff2 100644
--- a/components/script/dom/eventsource.rs
+++ b/components/script/dom/eventsource.rs
@@ -236,6 +236,7 @@ impl EventSourceContext {
DOMString::from(self.origin.clone()),
None,
event_source.last_event_id.borrow().clone(),
+ vec![],
)
};
// Step 7
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index c7635e98463..f1b6c0000b9 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -33,6 +33,7 @@ use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
use crate::task_source::file_reading::FileReadingTaskSource;
use crate::task_source::networking::NetworkingTaskSource;
use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
+use crate::task_source::port_message::PortMessageQueue;
use crate::task_source::remote_event::RemoteEventTaskSource;
use crate::task_source::websocket::WebsocketTaskSource;
use crate::task_source::TaskSourceName;
@@ -504,7 +505,7 @@ impl GlobalScope {
unreachable!();
}
- /// `ScriptChan` to send messages to the networking task source of
+ /// `TaskSource` to send messages to the networking task source of
/// this global scope.
pub fn networking_task_source(&self) -> NetworkingTaskSource {
if let Some(window) = self.downcast::<Window>() {
@@ -516,7 +517,19 @@ impl GlobalScope {
unreachable!();
}
- /// `ScriptChan` to send messages to the remote-event task source of
+ /// `TaskSource` to send messages to the port message queue of
+ /// this global scope.
+ pub fn port_message_queue(&self) -> PortMessageQueue {
+ if let Some(window) = self.downcast::<Window>() {
+ return window.task_manager().port_message_queue();
+ }
+ if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
+ return worker.port_message_queue();
+ }
+ unreachable!();
+ }
+
+ /// `TaskSource` to send messages to the remote-event task source of
/// this global scope.
pub fn remote_event_task_source(&self) -> RemoteEventTaskSource {
if let Some(window) = self.downcast::<Window>() {
@@ -528,7 +541,7 @@ impl GlobalScope {
unreachable!();
}
- /// `ScriptChan` to send messages to the websocket task source of
+ /// `TaskSource` to send messages to the websocket task source of
/// this global scope.
pub fn websocket_task_source(&self) -> WebsocketTaskSource {
if let Some(window) = self.downcast::<Window>() {
@@ -537,7 +550,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 92c27dfb82d..05dff680c2c 100644
--- a/components/script/dom/history.rs
+++ b/components/script/dom/history.rs
@@ -185,7 +185,8 @@ impl History {
// TODO: Step 4
// Step 5
- let serialized_data = StructuredCloneData::write(*cx, data)?.move_to_arraybuffer();
+ rooted!(in(cx) let transfer = UndefinedValue());
+ let serialized_data = StructuredCloneData::write(*cx, data, transfer.handle())?.move_to_arraybuffer();
let new_url: ServoUrl = match url {
// Step 6
diff --git a/components/script/dom/messagechannel.rs b/components/script/dom/messagechannel.rs
new file mode 100644
index 00000000000..e0d226d709a
--- /dev/null
+++ b/components/script/dom/messagechannel.rs
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::dom::bindings::codegen::Bindings::MessageChannelBinding::{MessageChannelMethods, Wrap};
+use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::messageport::MessagePort;
+use dom_struct::dom_struct;
+
+#[dom_struct]
+pub struct MessageChannel {
+ reflector_: Reflector,
+ port1: Dom<MessagePort>,
+ port2: Dom<MessagePort>,
+}
+
+impl MessageChannel {
+ /// <https://html.spec.whatwg.org/multipage/#dom-messagechannel>
+ pub fn Constructor(global: &GlobalScope) -> Fallible<DomRoot<MessageChannel>> {
+ let incumbent = GlobalScope::incumbent().ok_or(Error::InvalidState)?;
+
+ // Step 1
+ let port1 = MessagePort::new(&incumbent);
+
+ // Step 2
+ let port2 = MessagePort::new(&incumbent);
+
+ // Step 3
+ port1.entangle(&port2);
+
+ // Steps 4-6
+ let channel = reflect_dom_object(Box::new(
+ MessageChannel {
+ reflector_: Reflector::new(),
+ port1: Dom::from_ref(&port1),
+ port2: Dom::from_ref(&port2),
+ }),
+ global,
+ Wrap
+ );
+
+ // Step 7
+ Ok(channel)
+ }
+}
+
+impl MessageChannelMethods for MessageChannel {
+ /// <https://html.spec.whatwg.org/multipage/#dom-messagechannel-port1>
+ fn Port1(&self) -> DomRoot<MessagePort> {
+ DomRoot::from_ref(&*self.port1)
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#dom-messagechannel-port2>
+ fn Port2(&self) -> DomRoot<MessagePort> {
+ DomRoot::from_ref(&*self.port2)
+ }
+}
diff --git a/components/script/dom/messageevent.rs b/components/script/dom/messageevent.rs
index 52c0bd1acf1..8d002923814 100644
--- a/components/script/dom/messageevent.rs
+++ b/components/script/dom/messageevent.rs
@@ -14,11 +14,14 @@ use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
+use crate::dom::messageport::MessagePort;
use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
-use js::jsapi::{Heap, JSObject};
-use js::jsval::JSVal;
+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::rust::HandleValue;
use servo_atoms::Atom;
use std::ptr::NonNull;
@@ -31,6 +34,7 @@ pub struct MessageEvent {
origin: DOMString,
source: Option<Dom<WindowProxy>>,
lastEventId: DOMString,
+ ports: Vec<DomRoot<MessagePort>>,
}
impl MessageEvent {
@@ -41,6 +45,7 @@ impl MessageEvent {
DOMString::new(),
None,
DOMString::new(),
+ vec![],
)
}
@@ -50,13 +55,15 @@ impl MessageEvent {
origin: DOMString,
source: Option<&WindowProxy>,
lastEventId: DOMString,
+ ports: Vec<DomRoot<MessagePort>>,
) -> DomRoot<MessageEvent> {
let ev = Box::new(MessageEvent {
event: Event::new_inherited(),
data: Heap::default(),
- origin: origin,
source: source.map(Dom::from_ref),
- lastEventId: lastEventId,
+ origin,
+ lastEventId,
+ ports,
});
let ev = reflect_dom_object(ev, global, MessageEventBinding::Wrap);
ev.data.set(data.get());
@@ -73,8 +80,9 @@ impl MessageEvent {
origin: DOMString,
source: Option<&WindowProxy>,
lastEventId: DOMString,
+ ports: Vec<DomRoot<MessagePort>>,
) -> DomRoot<MessageEvent> {
- let ev = MessageEvent::new_initialized(global, data, origin, source, lastEventId);
+ let ev = MessageEvent::new_initialized(global, data, origin, source, lastEventId, ports);
{
let event = ev.upcast::<Event>();
event.init_event(type_, bubbles, cancelable);
@@ -100,6 +108,7 @@ impl MessageEvent {
init.origin.clone(),
source.as_ref().map(|source| &**source),
init.lastEventId.clone(),
+ init.ports.clone().unwrap_or(vec![])
);
Ok(ev)
}
@@ -112,6 +121,7 @@ impl MessageEvent {
message: HandleValue,
origin: Option<&str>,
source: Option<&WindowProxy>,
+ ports: Vec<DomRoot<MessagePort>>,
) {
let messageevent = MessageEvent::new(
scope,
@@ -122,18 +132,19 @@ impl MessageEvent {
DOMString::from(origin.unwrap_or("")),
source,
DOMString::new(),
+ ports,
);
messageevent.upcast::<Event>().fire(target);
}
}
impl MessageEventMethods for MessageEvent {
- // https://html.spec.whatwg.org/multipage/#dom-messageevent-data
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-data>
fn Data(&self, _cx: JSContext) -> JSVal {
self.data.get()
}
- // https://html.spec.whatwg.org/multipage/#dom-messageevent-origin
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-origin>
fn Origin(&self) -> DOMString {
self.origin.clone()
}
@@ -145,13 +156,24 @@ impl MessageEventMethods for MessageEvent {
.and_then(|source| NonNull::new(source.reflector().get_jsobject().get()))
}
- // https://html.spec.whatwg.org/multipage/#dom-messageevent-lasteventid
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-lasteventid>
fn LastEventId(&self) -> DOMString {
self.lastEventId.clone()
}
- // https://dom.spec.whatwg.org/#dom-event-istrusted
+ /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
fn IsTrusted(&self) -> bool {
self.event.IsTrusted()
}
+
+ #[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
+ }
}
diff --git a/components/script/dom/messageport.rs b/components/script/dom/messageport.rs
new file mode 100644
index 00000000000..10489f6d2ad
--- /dev/null
+++ b/components/script/dom/messageport.rs
@@ -0,0 +1,395 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{MessagePortMethods, Wrap};
+use crate::dom::bindings::conversions::{ToJSValConvertible, 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::root::DomRoot;
+use crate::dom::bindings::structuredclone::StructuredCloneData;
+use crate::dom::bindings::trace::JSTraceable;
+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 std::cell::{Cell, RefCell};
+use std::collections::VecDeque;
+use std::mem;
+use std::os::raw;
+use std::rc::Rc;
+use std::sync::Arc;
+
+// FIXME: This is wrong, we need to figure out a better way of collecting message port objects per transfer
+thread_local! {
+ pub static TRANSFERRED_MESSAGE_PORTS: RefCell<Vec<DomRoot<MessagePort>>> = RefCell::new(Vec::new())
+}
+
+struct PortMessageTask {
+ origin: String,
+ data: Vec<u8>,
+}
+
+pub struct MessagePortInternal {
+ dom_port: RefCell<Option<Trusted<MessagePort>>>,
+ port_message_queue: RefCell<PortMessageQueue>,
+ enabled: Cell<bool>,
+ has_been_shipped: Cell<bool>,
+ entangled_port: RefCell<Option<Arc<ReentrantMutex<MessagePortInternal>>>>,
+ pending_port_messages: RefCell<VecDeque<PortMessageTask>>,
+}
+
+impl MessagePortInternal {
+ fn new(port_message_queue: PortMessageQueue) -> MessagePortInternal {
+ MessagePortInternal {
+ dom_port: RefCell::new(None),
+ port_message_queue: RefCell::new(port_message_queue),
+ enabled: Cell::new(false),
+ has_been_shipped: Cell::new(false),
+ entangled_port: RefCell::new(None),
+ pending_port_messages: RefCell::new(VecDeque::new()),
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
+ // Step 7 substeps
+ #[allow(unrooted_must_root)]
+ fn process_pending_port_messages(&self) {
+ let PortMessageTask { origin, data } = match self.pending_port_messages.borrow_mut().pop_front() {
+ Some(task) => task,
+ None => return,
+ };
+
+ // Substep 1
+ let final_target_port = self.dom_port.borrow().as_ref().unwrap().root();
+
+ // Substep 2
+ let target_global = final_target_port.global();
+
+ // Substep 3-4
+ rooted!(in(target_global.get_cx()) let mut message_clone = UndefinedValue());
+ let deserialize_result = StructuredCloneData::Vector(data).read(
+ &target_global,
+ message_clone.handle_mut()
+ );
+ if !deserialize_result {
+ return;
+ }
+
+ // Substep 5
+ let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| {
+ mem::replace(&mut *list.borrow_mut(), vec![])
+ });
+
+ // Substep 6
+ MessageEvent::dispatch_jsval(
+ final_target_port.upcast(),
+ &target_global,
+ message_clone.handle(),
+ Some(&origin),
+ None,
+ new_ports,
+ );
+ }
+}
+
+#[derive(DenyPublicFields, DomObject, MallocSizeOf)]
+#[must_root]
+#[repr(C)]
+pub struct MessagePort {
+ eventtarget: EventTarget,
+ detached: Cell<bool>,
+ #[ignore_malloc_size_of = "Defined in std"]
+ message_port_internal: Arc<ReentrantMutex<MessagePortInternal>>,
+}
+
+#[allow(unsafe_code)]
+unsafe impl JSTraceable for MessagePort {
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ if !self.detached.get() {
+ self.eventtarget.trace(trc);
+ }
+ // Otherwise, do nothing.
+ }
+}
+
+impl HasParent for MessagePort {
+ type Parent = EventTarget;
+
+ fn as_parent(&self) -> &EventTarget {
+ &self.eventtarget
+ }
+}
+
+impl MessagePort {
+ fn new_inherited(global: &GlobalScope) -> MessagePort {
+ MessagePort {
+ eventtarget: EventTarget::new_inherited(),
+ detached: Cell::new(false),
+ message_port_internal: Arc::new(
+ ReentrantMutex::new(
+ MessagePortInternal::new(global.port_message_queue().clone())
+ )
+ ),
+ }
+ }
+
+ fn new_transferred(message_port_internal: Arc<ReentrantMutex<MessagePortInternal>>) -> MessagePort {
+ MessagePort {
+ eventtarget: EventTarget::new_inherited(),
+ detached: Cell::new(false),
+ message_port_internal,
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#create-a-new-messageport-object>
+ pub fn new(owner: &GlobalScope) -> DomRoot<MessagePort> {
+ let message_port = reflect_dom_object(Box::new(MessagePort::new_inherited(owner)), owner, Wrap);
+ {
+ let internal = message_port.message_port_internal.lock().unwrap();
+ *internal.dom_port.borrow_mut() = Some(Trusted::new(&*message_port));
+ }
+ message_port
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#entangle>
+ pub fn entangle(&self, other: &MessagePort) {
+ {
+ let internal = self.message_port_internal.lock().unwrap();
+ *internal.entangled_port.borrow_mut() = Some(other.message_port_internal.clone());
+ }
+ let internal = other.message_port_internal.lock().unwrap();
+ *internal.entangled_port.borrow_mut() = Some(self.message_port_internal.clone());
+ }
+
+ /// <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();
+ }
+}
+
+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
+ }
+
+ unsafe {
+ // Steps 2, 3.2 and 4
+ *content = Arc::into_raw(self.message_port_internal.clone()) as *mut raw::c_void;
+
+ *extra_data = 0;
+ }
+
+ true
+ }
+
+ /// https://html.spec.whatwg.org/multipage/#message-ports:transfer-receiving-steps
+ #[allow(unrooted_must_root, unsafe_code)]
+ fn transfer_receive(
+ cx: *mut JSContext,
+ _r: *mut JSStructuredCloneReader,
+ _closure: *mut raw::c_void,
+ content: *mut raw::c_void,
+ _extra_data: u64,
+ return_object: MutableHandleObject
+ ) -> bool {
+ let internal = unsafe { Arc::from_raw(content as *const ReentrantMutex<MessagePortInternal>) };
+ let value = MessagePort::new_transferred(internal);
+
+ // Step 2
+ let owner = unsafe { GlobalScope::from_context(cx) };
+ let message_port = reflect_dom_object(Box::new(value), &*owner, Wrap);
+
+ {
+ let internal = message_port.message_port_internal.lock().unwrap();
+
+ // Step 1
+ internal.has_been_shipped.set(true);
+
+ let dom_port = Trusted::new(&*message_port);
+ internal.enabled.set(false);
+ *internal.dom_port.borrow_mut() = Some(dom_port);
+ *internal.port_message_queue.borrow_mut() = owner.port_message_queue().clone();
+ }
+ return_object.set(message_port.reflector().rootable().get());
+ TRANSFERRED_MESSAGE_PORTS.with(|list| {
+ list.borrow_mut().push(message_port);
+ });
+
+ true
+ }
+
+ fn detached(&self) -> Option<bool> {
+ Some(self.detached.get())
+ }
+
+ fn set_detached(&self, value: bool) {
+ self.detached.set(value);
+ }
+
+ fn transferable(&self) -> bool {
+ !self.detached.get()
+ }
+}
+
+impl MessagePortMethods for MessagePort {
+ #[allow(unsafe_code)]
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
+ unsafe fn PostMessage(
+ &self,
+ cx: *mut JSContext,
+ message: HandleValue,
+ transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
+ ) -> ErrorResult {
+ if self.detached.get() { return Ok(()); }
+ let internal = self.message_port_internal.lock().unwrap();
+ // Step 1
+ let target_port = internal.entangled_port.borrow();
+
+ // Step 3
+ let mut doomed = false;
+
+ rooted!(in(cx) let mut val = UndefinedValue());
+ let transfer = match *transfer {
+ Some(ref vec) => {
+ let ports = vec.iter().filter_map(|&obj| root_from_object::<MessagePort>(obj).ok());
+ for port in ports {
+ // Step 2
+ if Arc::ptr_eq(&port.message_port_internal, &self.message_port_internal) {
+ return Err(Error::DataClone);
+ }
+
+ // Step 4
+ if let Some(target) = target_port.as_ref() {
+ if Arc::ptr_eq(&port.message_port_internal, target) {
+ doomed = true;
+ }
+ }
+ }
+
+ vec.to_jsval(cx, val.handle_mut());
+ val
+ }
+ None => {
+ Vec::<*mut JSObject>::new().to_jsval(cx, val.handle_mut());
+ val
+ }
+ };
+
+ // 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()
+ );
+ }
+ }
+
+ Ok(())
+ }
+
+ /// <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
+ );
+ }
+ }
+
+ /// <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;
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
+ fn GetOnmessage(&self) -> Option<Rc<EventHandlerNonNull>> {
+ 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)
+ }
+}
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index aa7400db1b6..302ae5baad4 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -401,7 +401,9 @@ pub mod mediaquerylist;
pub mod mediaquerylistevent;
pub mod mediastream;
pub mod mediastreamtrack;
+pub mod messagechannel;
pub mod messageevent;
+pub mod messageport;
pub mod mimetype;
pub mod mimetypearray;
pub mod mouseevent;
diff --git a/components/script/dom/serviceworker.rs b/components/script/dom/serviceworker.rs
index dd968c34d94..8d33337f1c5 100644
--- a/components/script/dom/serviceworker.rs
+++ b/components/script/dom/serviceworker.rs
@@ -19,6 +19,8 @@ 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 script_traits::{DOMMessage, ScriptMsg};
use servo_url::ServoUrl;
@@ -97,14 +99,18 @@ impl ServiceWorkerMethods for ServiceWorker {
return Err(Error::InvalidState);
}
// Step 7
- let data = StructuredCloneData::write(*cx, message)?;
- let msg_vec = DOMMessage(data.move_to_arraybuffer());
+ rooted!(in(*cx) let transfer = UndefinedValue());
+ let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
+ let msg_vec = DOMMessage {
+ origin: self.global().origin().immutable().ascii_serialization(),
+ data: data.move_to_arraybuffer(),
+ };
let _ = self
.global()
.script_to_constellation_chan()
.send(ScriptMsg::ForwardDOMMessage(
msg_vec,
- self.scope_url.clone(),
+ self.scope_url.clone()
));
Ok(())
}
diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs
index 5af7f07ab0f..8daf3b6dc2f 100644
--- a/components/script/dom/serviceworkerglobalscope.rs
+++ b/components/script/dom/serviceworkerglobalscope.rs
@@ -409,12 +409,12 @@ impl ServiceWorkerGlobalScope {
use self::ServiceWorkerScriptMsg::*;
match msg {
- CommonWorker(WorkerScriptMsg::DOMMessage(data)) => {
+ CommonWorker(WorkerScriptMsg::DOMMessage { data, .. }) => {
let scope = self.upcast::<WorkerGlobalScope>();
let target = self.upcast();
let _ac = enter_realm(&*scope);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
- data.read(scope.upcast(), message.handle_mut());
+ assert!(data.read(scope.upcast(), message.handle_mut()));
ExtendableMessageEvent::dispatch_jsval(target, scope.upcast(), message.handle());
},
CommonWorker(WorkerScriptMsg::Common(msg)) => {
diff --git a/components/script/dom/webidls/MessageChannel.webidl b/components/script/dom/webidls/MessageChannel.webidl
new file mode 100644
index 00000000000..97baba289b8
--- /dev/null
+++ b/components/script/dom/webidls/MessageChannel.webidl
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * The origin of this IDL file is:
+ * https://html.spec.whatwg.org/multipage/#messagechannel
+ */
+
+[Exposed=(Window,Worker)]
+interface MessageChannel {
+ [Throws] constructor();
+ readonly attribute MessagePort port1;
+ readonly attribute MessagePort port2;
+};
diff --git a/components/script/dom/webidls/MessageEvent.webidl b/components/script/dom/webidls/MessageEvent.webidl
index e5b91a043f3..63dd9019cc9 100644
--- a/components/script/dom/webidls/MessageEvent.webidl
+++ b/components/script/dom/webidls/MessageEvent.webidl
@@ -12,7 +12,7 @@ interface MessageEvent : Event {
// FIXME(#22617): WindowProxy is not exposed in Worker globals
readonly attribute object? source;
//readonly attribute (WindowProxy or MessagePort)? source;
- //readonly attribute MessagePort[]? ports;
+ readonly attribute /*FrozenArray<MessagePort>*/any ports;
};
dictionary MessageEventInit : EventInit {
@@ -22,5 +22,7 @@ dictionary MessageEventInit : EventInit {
//DOMString channel;
Window? source;
//(WindowProxy or MessagePort)? source;
- //sequence<MessagePort> ports;
+ sequence<MessagePort> ports;
};
+
+typedef (/*WindowProxy or */MessagePort or ServiceWorker) MessageEventSource;
diff --git a/components/script/dom/webidls/MessagePort.webidl b/components/script/dom/webidls/MessagePort.webidl
new file mode 100644
index 00000000000..c00eba5291a
--- /dev/null
+++ b/components/script/dom/webidls/MessagePort.webidl
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * The origin of this IDL file is:
+ * https://html.spec.whatwg.org/multipage/#messageport
+ */
+
+[Exposed=(Window,Worker)]
+interface MessagePort : EventTarget {
+ [Throws] void postMessage(any message, optional sequence<object> transfer /*= []*/);
+ void start();
+ void close();
+
+ // event handlers
+ attribute EventHandler onmessage;
+};
diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl
index 65944aa3eb1..a4af9bf692b 100644
--- a/components/script/dom/webidls/Window.webidl
+++ b/components/script/dom/webidls/Window.webidl
@@ -63,9 +63,8 @@
unsigned long requestAnimationFrame(FrameRequestCallback callback);
void cancelAnimationFrame(unsigned long handle);
- //void postMessage(any message, DOMString targetOrigin, optional sequence<Transferable> transfer);
[Throws]
- void postMessage(any message, DOMString targetOrigin);
+ void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/);
// also has obsolete members
};
diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs
index 3051cc0ff33..fcdd63e2594 100644
--- a/components/script/dom/websocket.rs
+++ b/components/script/dom/websocket.rs
@@ -598,6 +598,7 @@ impl TaskOnce for MessageReceivedTask {
message.handle(),
Some(&ws.origin().ascii_serialization()),
None,
+ vec![],
);
}
}
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 565308ff36f..7f3b6d1fb8d 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -17,6 +17,7 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::{
};
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;
@@ -43,6 +44,7 @@ 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;
@@ -80,12 +82,13 @@ use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
use ipc_channel::ipc::{channel, IpcSender};
use ipc_channel::router::ROUTER;
use js::jsapi::JSAutoRealm;
+use js::jsapi::JSObject;
use js::jsapi::JSPROP_ENUMERATE;
use js::jsapi::{GCReason, JS_GC};
use js::jsval::UndefinedValue;
use js::jsval::{JSVal, NullValue};
use js::rust::wrappers::JS_DefineProperty;
-use js::rust::HandleValue;
+use js::rust::{CustomAutoRooterGuard, HandleValue};
use media::WindowGLContext;
use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
@@ -969,12 +972,18 @@ impl WindowMethods for Window {
}
// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
- fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult {
+ fn PostMessage(
+ &self,
+ cx: JSContext,
+ message: HandleValue,
+ origin: USVString,
+ transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
+ ) -> ErrorResult {
let source_global = GlobalScope::incumbent().expect("no incumbent global??");
let source = source_global.as_window();
// Step 3-5.
- let origin = match &origin[..] {
+ let origin = match &origin.0[..] {
"*" => None,
"/" => Some(source.Document().origin().immutable().clone()),
url => match ServoUrl::parse(&url) {
@@ -984,8 +993,10 @@ impl WindowMethods for Window {
};
// Step 1-2, 6-8.
- // TODO(#12717): Should implement the `transfer` argument.
- let data = StructuredCloneData::write(*cx, message)?;
+ rooted!(in(*cx) let mut val = UndefinedValue());
+ (*transfer).as_ref().unwrap_or(&Vec::new()).to_jsval(*cx, val.handle_mut());
+
+ let data = StructuredCloneData::write(*cx, message, val.handle())?;
// Step 9.
self.post_message(origin, &*source.window_proxy(), data);
@@ -2342,10 +2353,11 @@ impl Window {
let task = task!(post_serialised_message: move || {
let this = this.root();
let source = source.root();
+ let document = this.Document();
// Step 7.1.
- if let Some(target_origin) = target_origin {
- if !target_origin.same_origin(this.Document().origin()) {
+ if let Some(ref target_origin) = target_origin {
+ if !target_origin.same_origin(document.origin()) {
return;
}
}
@@ -2355,13 +2367,15 @@ impl Window {
let obj = this.reflector().get_jsobject();
let _ac = JSAutoRealm::new(*cx, obj.get());
rooted!(in(*cx) let mut message_clone = UndefinedValue());
- serialize_with_transfer_result.read(
+ assert!(serialize_with_transfer_result.read(
this.upcast(),
message_clone.handle_mut(),
- );
+ ));
// Step 7.6.
- // TODO: MessagePort array.
+ let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| {
+ mem::replace(&mut *list.borrow_mut(), vec![])
+ });
// Step 7.7.
// TODO(#12719): Set the other attributes.
@@ -2369,8 +2383,9 @@ impl Window {
this.upcast(),
this.upcast(),
message_clone.handle(),
- None,
+ Some(&document.origin().immutable().ascii_serialization()),
Some(&*source),
+ new_ports,
);
});
// FIXME(nox): Why are errors silenced here?
diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs
index 1ae17571fbb..1a8d9c292dc 100644
--- a/components/script/dom/worker.rs
+++ b/components/script/dom/worker.rs
@@ -137,7 +137,11 @@ impl Worker {
self.terminated.get()
}
- pub fn handle_message(address: TrustedWorkerAddress, data: StructuredCloneData) {
+ pub fn handle_message(
+ address: TrustedWorkerAddress,
+ origin: String,
+ data: StructuredCloneData,
+ ) {
let worker = address.root();
if worker.is_terminated() {
@@ -148,8 +152,8 @@ impl Worker {
let target = worker.upcast();
let _ac = enter_realm(target);
rooted!(in(*global.get_cx()) let mut message = UndefinedValue());
- data.read(&global, message.handle_mut());
- MessageEvent::dispatch_jsval(target, &global, message.handle(), None, None);
+ assert!(data.read(&global, message.handle_mut()));
+ MessageEvent::dispatch_jsval(target, &global, message.handle(), Some(&origin), None, vec![]);
}
pub fn dispatch_simple_error(address: TrustedWorkerAddress) {
@@ -161,14 +165,18 @@ impl Worker {
impl WorkerMethods for Worker {
// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult {
- let data = StructuredCloneData::write(*cx, message)?;
+ rooted!(in(*cx) let transfer = UndefinedValue());
+ let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
let address = Trusted::new(self);
// NOTE: step 9 of https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage
// indicates that a nonexistent communication channel should result in a silent error.
let _ = self.sender.send(DedicatedWorkerScriptMsg::CommonWorker(
address,
- WorkerScriptMsg::DOMMessage(data),
+ WorkerScriptMsg::DOMMessage {
+ origin: self.global().origin().immutable().ascii_serialization(),
+ data,
+ },
));
Ok(())
}
diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs
index 4ca2ad59d74..50cb8bb5aaf 100644
--- a/components/script/dom/workerglobalscope.rs
+++ b/components/script/dom/workerglobalscope.rs
@@ -33,6 +33,7 @@ use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
use crate::task_source::file_reading::FileReadingTaskSource;
use crate::task_source::networking::NetworkingTaskSource;
use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
+use crate::task_source::port_message::PortMessageQueue;
use crate::task_source::remote_event::RemoteEventTaskSource;
use crate::task_source::websocket::WebsocketTaskSource;
use crate::timers::{IsInterval, TimerCallback};
@@ -457,6 +458,10 @@ impl WorkerGlobalScope {
PerformanceTimelineTaskSource(self.script_chan(), self.pipeline_id())
}
+ pub fn port_message_queue(&self) -> PortMessageQueue {
+ PortMessageQueue(self.script_chan(), self.pipeline_id())
+ }
+
pub fn remote_event_task_source(&self) -> RemoteEventTaskSource {
RemoteEventTaskSource(self.script_chan(), self.pipeline_id())
}
diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs
index 305dd7d25b5..21cea763c00 100644
--- a/components/script/script_runtime.rs
+++ b/components/script/script_runtime.rs
@@ -115,6 +115,7 @@ pub enum ScriptThreadEventCategory {
ImageCacheMsg,
InputEvent,
NetworkEvent,
+ PortMessage,
Resize,
ScriptEvent,
SetScrollState,
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index b4363f5ada3..fac11949d70 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -81,6 +81,7 @@ use crate::task_source::history_traversal::HistoryTraversalTaskSource;
use crate::task_source::media_element::MediaElementTaskSource;
use crate::task_source::networking::NetworkingTaskSource;
use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
+use crate::task_source::port_message::PortMessageQueue;
use crate::task_source::remote_event::RemoteEventTaskSource;
use crate::task_source::user_interaction::UserInteractionTaskSource;
use crate::task_source::websocket::WebsocketTaskSource;
@@ -566,6 +567,8 @@ pub struct ScriptThread {
performance_timeline_task_sender: Box<dyn ScriptChan>,
+ port_message_sender: Box<dyn ScriptChan>,
+
remote_event_task_sender: Box<dyn ScriptChan>,
/// A channel to hand out to threads that need to respond to a message from the script thread.
@@ -1296,6 +1299,7 @@ impl ScriptThread {
media_element_task_sender: chan.clone(),
user_interaction_task_sender: chan.clone(),
networking_task_sender: boxed_script_sender.clone(),
+ port_message_sender: boxed_script_sender.clone(),
file_reading_task_sender: boxed_script_sender.clone(),
performance_timeline_task_sender: boxed_script_sender.clone(),
remote_event_task_sender: boxed_script_sender.clone(),
@@ -1656,6 +1660,7 @@ impl ScriptThread {
ScriptThreadEventCategory::PerformanceTimelineTask => {
ScriptHangAnnotation::PerformanceTimelineTask
},
+ ScriptThreadEventCategory::PortMessage => ScriptHangAnnotation::PortMessage,
};
self.background_hang_monitor
.notify_activity(HangAnnotation::Script(hang_annotation));
@@ -1756,6 +1761,7 @@ impl ScriptThread {
ScriptThreadEventCategory::ImageCacheMsg => ProfilerCategory::ScriptImageCacheMsg,
ScriptThreadEventCategory::InputEvent => ProfilerCategory::ScriptInputEvent,
ScriptThreadEventCategory::NetworkEvent => ProfilerCategory::ScriptNetworkEvent,
+ ScriptThreadEventCategory::PortMessage => ProfilerCategory::ScriptPortMessage,
ScriptThreadEventCategory::Resize => ProfilerCategory::ScriptResize,
ScriptThreadEventCategory::ScriptEvent => ProfilerCategory::ScriptEvent,
ScriptThreadEventCategory::SetScrollState => ProfilerCategory::ScriptSetScrollState,
@@ -2780,6 +2786,10 @@ impl ScriptThread {
NetworkingTaskSource(self.networking_task_sender.clone(), pipeline_id)
}
+ pub fn port_message_queue(&self, pipeline_id: PipelineId) -> PortMessageQueue {
+ PortMessageQueue(self.port_message_sender.clone(), pipeline_id)
+ }
+
pub fn file_reading_task_source(&self, pipeline_id: PipelineId) -> FileReadingTaskSource {
FileReadingTaskSource(self.file_reading_task_sender.clone(), pipeline_id)
}
@@ -3177,6 +3187,7 @@ impl ScriptThread {
self.networking_task_source(incomplete.pipeline_id),
self.performance_timeline_task_source(incomplete.pipeline_id)
.clone(),
+ self.port_message_queue(incomplete.pipeline_id),
self.user_interaction_task_source(incomplete.pipeline_id),
self.remote_event_task_source(incomplete.pipeline_id),
self.websocket_task_source(incomplete.pipeline_id),
diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs
index 2499c7a8494..6e7884a891e 100644
--- a/components/script/serviceworker_manager.rs
+++ b/components/script/serviceworker_manager.rs
@@ -135,10 +135,10 @@ impl ServiceWorkerManager {
}
fn forward_message(&self, msg: DOMMessage, sender: &Sender<ServiceWorkerScriptMsg>) {
- let DOMMessage(data) = msg;
+ let DOMMessage { origin, data } = msg;
let data = StructuredCloneData::Vector(data);
let _ = sender.send(ServiceWorkerScriptMsg::CommonWorker(
- WorkerScriptMsg::DOMMessage(data),
+ WorkerScriptMsg::DOMMessage { origin, data },
));
}
diff --git a/components/script/task_manager.rs b/components/script/task_manager.rs
index 40c713df5d1..a8908ea00a2 100644
--- a/components/script/task_manager.rs
+++ b/components/script/task_manager.rs
@@ -10,6 +10,7 @@ use crate::task_source::history_traversal::HistoryTraversalTaskSource;
use crate::task_source::media_element::MediaElementTaskSource;
use crate::task_source::networking::NetworkingTaskSource;
use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
+use crate::task_source::port_message::PortMessageQueue;
use crate::task_source::remote_event::RemoteEventTaskSource;
use crate::task_source::user_interaction::UserInteractionTaskSource;
use crate::task_source::websocket::WebsocketTaskSource;
@@ -47,6 +48,8 @@ pub struct TaskManager {
#[ignore_malloc_size_of = "task sources are hard"]
performance_timeline_task_source: PerformanceTimelineTaskSource,
#[ignore_malloc_size_of = "task sources are hard"]
+ port_message_queue: PortMessageQueue,
+ #[ignore_malloc_size_of = "task sources are hard"]
user_interaction_task_source: UserInteractionTaskSource,
#[ignore_malloc_size_of = "task sources are hard"]
remote_event_task_source: RemoteEventTaskSource,
@@ -62,6 +65,7 @@ impl TaskManager {
media_element_task_source: MediaElementTaskSource,
networking_task_source: NetworkingTaskSource,
performance_timeline_task_source: PerformanceTimelineTaskSource,
+ port_message_queue: PortMessageQueue,
user_interaction_task_source: UserInteractionTaskSource,
remote_event_task_source: RemoteEventTaskSource,
websocket_task_source: WebsocketTaskSource,
@@ -73,6 +77,7 @@ impl TaskManager {
media_element_task_source,
networking_task_source,
performance_timeline_task_source,
+ port_message_queue,
user_interaction_task_source,
remote_event_task_source,
websocket_task_source,
@@ -138,6 +143,14 @@ impl TaskManager {
task_source_functions!(
self,
+ port_message_queue_with_canceller,
+ port_message_queue,
+ PortMessageQueue,
+ PortMessage
+ );
+
+ task_source_functions!(
+ self,
remote_event_task_source_with_canceller,
remote_event_task_source,
RemoteEventTaskSource,
diff --git a/components/script/task_source/mod.rs b/components/script/task_source/mod.rs
index f716473f88a..d315b34fcae 100644
--- a/components/script/task_source/mod.rs
+++ b/components/script/task_source/mod.rs
@@ -8,6 +8,7 @@ pub mod history_traversal;
pub mod media_element;
pub mod networking;
pub mod performance_timeline;
+pub mod port_message;
pub mod remote_event;
pub mod user_interaction;
pub mod websocket;
@@ -28,6 +29,7 @@ pub enum TaskSourceName {
HistoryTraversal,
Networking,
PerformanceTimeline,
+ PortMessage,
UserInteraction,
RemoteEvent,
MediaElement,
diff --git a/components/script/task_source/port_message.rs b/components/script/task_source/port_message.rs
new file mode 100644
index 00000000000..8541f89bc64
--- /dev/null
+++ b/components/script/task_source/port_message.rs
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
+use crate::task::{TaskCanceller, TaskOnce};
+use crate::task_source::{TaskSource, TaskSourceName};
+use msg::constellation_msg::PipelineId;
+use std::fmt;
+
+#[derive(JSTraceable)]
+pub struct PortMessageQueue(pub Box<ScriptChan + Send + 'static>, pub PipelineId);
+
+impl Clone for PortMessageQueue {
+ fn clone(&self) -> PortMessageQueue {
+ PortMessageQueue(self.0.clone(), self.1.clone())
+ }
+}
+
+impl fmt::Debug for PortMessageQueue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "PortMessageQueue(...)")
+ }
+}
+
+impl TaskSource for PortMessageQueue {
+ const NAME: TaskSourceName = TaskSourceName::PortMessage;
+
+ fn queue_with_canceller<T>(
+ &self,
+ task: T,
+ canceller: &TaskCanceller,
+ ) -> Result<(), ()>
+ where
+ T: TaskOnce + 'static,
+ {
+ let msg = CommonScriptMsg::Task(
+ ScriptThreadEventCategory::PortMessage,
+ Box::new(canceller.wrap_task(task)),
+ Some(self.1),
+ Self::NAME,
+ );
+ self.0.send(msg).map_err(|_| ())
+ }
+}
diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs
index c3ee8d75205..bd841614212 100644
--- a/components/script_traits/script_msg.rs
+++ b/components/script_traits/script_msg.rs
@@ -284,7 +284,12 @@ pub struct ScopeThings {
/// Message that gets passed to service worker scope on postMessage
#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct DOMMessage(pub Vec<u8>);
+pub struct DOMMessage {
+ /// The origin of the message
+ pub origin: String,
+ /// The payload of the message
+ pub data: Vec<u8>,
+}
/// Channels to allow service worker manager to communicate with constellation and resource thread
pub struct SWManagerSenders {
diff --git a/tests/wpt/include.ini b/tests/wpt/include.ini
index 60b48c50879..6fded483450 100644
--- a/tests/wpt/include.ini
+++ b/tests/wpt/include.ini
@@ -167,6 +167,8 @@ skip: true
skip: false
[WebIDL]
skip: false
+[webmessaging]
+ skip: false
[websockets]
skip: false
[webstorage]