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.rs342
1 files changed, 342 insertions, 0 deletions
diff --git a/components/script/dom/messageport.rs b/components/script/dom/messageport.rs
new file mode 100644
index 00000000000..fee7c75a9eb
--- /dev/null
+++ b/components/script/dom/messageport.rs
@@ -0,0 +1,342 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{
+ MessagePortMethods, PostMessageOptions,
+};
+use crate::dom::bindings::conversions::root_from_object;
+use crate::dom::bindings::error::{Error, ErrorResult};
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::structuredclone::{self, StructuredDataHolder};
+use crate::dom::bindings::trace::RootedTraceableBox;
+use crate::dom::bindings::transferable::Transferable;
+use crate::dom::eventtarget::EventTarget;
+use crate::dom::globalscope::GlobalScope;
+use crate::script_runtime::JSContext as SafeJSContext;
+use dom_struct::dom_struct;
+use js::jsapi::Heap;
+use js::jsapi::{JSObject, MutableHandleObject};
+use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
+use msg::constellation_msg::{MessagePortId, MessagePortIndex, PipelineNamespaceId};
+use script_traits::PortMessageTask;
+use std::cell::{Cell, RefCell};
+use std::collections::HashMap;
+use std::convert::TryInto;
+use std::num::NonZeroU32;
+use std::rc::Rc;
+
+#[dom_struct]
+/// The MessagePort used in the DOM.
+pub struct MessagePort {
+ eventtarget: EventTarget,
+ message_port_id: MessagePortId,
+ entangled_port: RefCell<Option<MessagePortId>>,
+ detached: Cell<bool>,
+}
+
+impl MessagePort {
+ fn new_inherited(message_port_id: MessagePortId) -> MessagePort {
+ MessagePort {
+ eventtarget: EventTarget::new_inherited(),
+ entangled_port: RefCell::new(None),
+ detached: Cell::new(false),
+ message_port_id,
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#create-a-new-messageport-object>
+ pub fn new(owner: &GlobalScope) -> DomRoot<MessagePort> {
+ let port_id = MessagePortId::new();
+ reflect_dom_object(Box::new(MessagePort::new_inherited(port_id)), owner)
+ }
+
+ /// 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,
+ )
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#entangle>
+ pub fn entangle(&self, other_id: MessagePortId) {
+ *self.entangled_port.borrow_mut() = Some(other_id);
+ }
+
+ pub fn message_port_id(&self) -> &MessagePortId {
+ &self.message_port_id
+ }
+
+ pub fn detached(&self) -> bool {
+ self.detached.get()
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
+ fn set_onmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
+ let eventtarget = self.upcast::<EventTarget>();
+ eventtarget.set_event_handler_common("message", listener);
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#message-port-post-message-steps>
+ fn post_message_impl(
+ &self,
+ cx: SafeJSContext,
+ message: HandleValue,
+ transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
+ ) -> ErrorResult {
+ if self.detached.get() {
+ return Ok(());
+ }
+
+ // Step 1 is the transfer argument.
+
+ let target_port = self.entangled_port.borrow();
+
+ // Step 3
+ let mut doomed = false;
+
+ let ports = transfer
+ .iter()
+ .filter_map(|&obj| root_from_object::<MessagePort>(obj, *cx).ok());
+ for port in ports {
+ // Step 2
+ if port.message_port_id() == self.message_port_id() {
+ return Err(Error::DataClone);
+ }
+
+ // Step 4
+ if let Some(target_id) = target_port.as_ref() {
+ if port.message_port_id() == target_id {
+ doomed = true;
+ }
+ }
+ }
+
+ // Step 5
+ let data = structuredclone::write(cx, message, Some(transfer))?;
+
+ if doomed {
+ // TODO: The spec says to optionally report such a case to a dev console.
+ return Ok(());
+ }
+
+ // Step 6, done in MessagePortImpl.
+
+ let incumbent = match GlobalScope::incumbent() {
+ None => unreachable!("postMessage called with no incumbent global"),
+ Some(incumbent) => incumbent,
+ };
+
+ // Step 7
+ let task = PortMessageTask {
+ origin: incumbent.origin().immutable().clone(),
+ data,
+ };
+
+ // Have the global proxy this call to the corresponding MessagePortImpl.
+ self.global()
+ .post_messageport_msg(self.message_port_id().clone(), task);
+ Ok(())
+ }
+}
+
+impl Transferable for MessagePort {
+ /// <https://html.spec.whatwg.org/multipage/#message-ports:transfer-steps>
+ fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()> {
+ if self.detached.get() {
+ return Err(());
+ }
+
+ let port_impls = match sc_holder {
+ StructuredDataHolder::Write { ports, .. } => ports,
+ _ => panic!("Unexpected variant of StructuredDataHolder"),
+ };
+
+ self.detached.set(true);
+ let id = self.message_port_id();
+
+ // 1. Run local transfer logic, and return the object to be transferred.
+ let transferred_port = self.global().mark_port_as_transferred(id);
+
+ // 2. Store the transferred object at a given key.
+ if let Some(ports) = port_impls.as_mut() {
+ ports.insert(id.clone(), transferred_port);
+ } else {
+ let mut ports = HashMap::new();
+ ports.insert(id.clone(), transferred_port);
+ *port_impls = Some(ports);
+ }
+
+ let PipelineNamespaceId(name_space) = id.clone().namespace_id;
+ let MessagePortIndex(index) = id.clone().index;
+ let index = index.get();
+
+ let mut big: [u8; 8] = [0; 8];
+ let name_space = name_space.to_ne_bytes();
+ let index = index.to_ne_bytes();
+
+ let (left, right) = big.split_at_mut(4);
+ left.copy_from_slice(&name_space);
+ right.copy_from_slice(&index);
+
+ // 3. Return a u64 representation of the key where the object is stored.
+ Ok(u64::from_ne_bytes(big))
+ }
+
+ /// https://html.spec.whatwg.org/multipage/#message-ports:transfer-receiving-steps
+ fn transfer_receive(
+ owner: &GlobalScope,
+ sc_holder: &mut StructuredDataHolder,
+ extra_data: u64,
+ return_object: MutableHandleObject,
+ ) -> Result<(), ()> {
+ let (message_ports, port_impls) = match sc_holder {
+ StructuredDataHolder::Read {
+ message_ports,
+ port_impls,
+ ..
+ } => (message_ports, port_impls),
+ _ => panic!("Unexpected variant of StructuredDataHolder"),
+ };
+
+ // 1. Re-build the key for the storage location
+ // of the transferred object.
+ let big: [u8; 8] = extra_data.to_ne_bytes();
+ let (name_space, index) = big.split_at(4);
+
+ let namespace_id = PipelineNamespaceId(u32::from_ne_bytes(
+ name_space
+ .try_into()
+ .expect("name_space to be a slice of four."),
+ ));
+ let index = MessagePortIndex(
+ NonZeroU32::new(u32::from_ne_bytes(
+ index.try_into().expect("index to be a slice of four."),
+ ))
+ .expect("Index to be non-zero"),
+ );
+
+ let id = MessagePortId {
+ namespace_id,
+ index,
+ };
+
+ // 2. Get the transferred object from its storage, using the key.
+ // Assign the transfer-received port-impl, and total number of transferred ports.
+ let (ports_len, port_impl) = if let Some(ports) = port_impls.as_mut() {
+ let ports_len = ports.len();
+ let port_impl = ports.remove(&id).expect("Transferred port to be stored");
+ if ports.is_empty() {
+ *port_impls = None;
+ }
+ (ports_len, port_impl)
+ } else {
+ panic!("A messageport was transfer-received, yet the SC holder does not have any port impls");
+ };
+
+ let transferred_port =
+ MessagePort::new_transferred(&*owner, id.clone(), port_impl.entangled_port_id());
+ owner.track_message_port(&transferred_port, Some(port_impl));
+
+ return_object.set(transferred_port.reflector().rootable().get());
+
+ // Store the DOM port where it will be passed along to script in the message-event.
+ if let Some(ports) = message_ports.as_mut() {
+ ports.push(transferred_port);
+ } else {
+ let mut ports = Vec::with_capacity(ports_len);
+ ports.push(transferred_port);
+ *message_ports = Some(ports);
+ }
+
+ Ok(())
+ }
+}
+
+impl MessagePortMethods for MessagePort {
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
+ fn PostMessage(
+ &self,
+ cx: SafeJSContext,
+ message: HandleValue,
+ transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
+ ) -> ErrorResult {
+ if self.detached.get() {
+ return Ok(());
+ }
+ self.post_message_impl(cx, message, transfer)
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
+ fn PostMessage_(
+ &self,
+ cx: SafeJSContext,
+ message: HandleValue,
+ options: RootedTraceableBox<PostMessageOptions>,
+ ) -> ErrorResult {
+ if self.detached.get() {
+ return Ok(());
+ }
+ let mut rooted = CustomAutoRooter::new(
+ options
+ .transfer
+ .iter()
+ .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
+ .collect(),
+ );
+ let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
+ self.post_message_impl(cx, message, guard)
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
+ fn Start(&self) {
+ if self.detached.get() {
+ return;
+ }
+ self.global().start_message_port(self.message_port_id());
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
+ fn Close(&self) {
+ if self.detached.get() {
+ return;
+ }
+ self.detached.set(true);
+ self.global().close_message_port(self.message_port_id());
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
+ fn GetOnmessage(&self) -> Option<Rc<EventHandlerNonNull>> {
+ if self.detached.get() {
+ return None;
+ }
+ let eventtarget = self.upcast::<EventTarget>();
+ eventtarget.get_event_handler_common("message")
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
+ fn SetOnmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
+ if self.detached.get() {
+ return;
+ }
+ self.set_onmessage(listener);
+ // Note: we cannot use the event_handler macro, due to the need to start the port.
+ self.global().start_message_port(self.message_port_id());
+ }
+
+ // <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessageerror>
+ event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
+}