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.rs395
1 files changed, 395 insertions, 0 deletions
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)
+ }
+}