aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/messageport.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/messageport.rs')
-rw-r--r--components/script/dom/messageport.rs568
1 files changed, 259 insertions, 309 deletions
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);
}