diff options
Diffstat (limited to 'components/script/dom/messageport.rs')
-rw-r--r-- | components/script/dom/messageport.rs | 568 |
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); } |