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/atoms/static_atoms.txt b/components/atoms/static_atoms.txt index 43b1dbe169a..abbddbcfd3f 100644 --- a/components/atoms/static_atoms.txt +++ b/components/atoms/static_atoms.txt @@ -57,6 +57,7 @@ loadend loadstart message message +messageerror monospace month mousedown diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 9eef83f33a0..a9d5e33836c 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -130,7 +130,8 @@ use msg::constellation_msg::{ TopLevelBrowsingContextId, }; use msg::constellation_msg::{ - PipelineNamespace, PipelineNamespaceId, PipelineNamespaceRequest, TraversalDirection, + MessagePortId, MessagePortRouterId, PipelineNamespace, PipelineNamespaceId, + PipelineNamespaceRequest, TraversalDirection, }; use net_traits::pub_domains::reg_host; use net_traits::request::RequestBuilder; @@ -153,6 +154,7 @@ use script_traits::{ IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg, }; use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory}; +use script_traits::{MessagePortMsg, PortMessageTask, StructuredSerializedData}; use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg}; use serde::{Deserialize, Serialize}; use servo_config::{opts, pref}; @@ -175,6 +177,30 @@ use webvr_traits::{WebVREvent, WebVRMsg}; type PendingApprovalNavigations = HashMap; +#[derive(Debug)] +/// The state used by MessagePortInfo to represent the various states the port can be in. +enum TransferState { + /// The port is currently managed by a given global, + /// identified by its router id. + Managed(MessagePortRouterId), + /// The port is currently in-transfer, + /// and incoming tasks should be buffered until it becomes managed again. + TransferInProgress(VecDeque), + /// The entangled port has been removed while the port was in-transfer, + /// the current port should be removed as well once it is managed again. + EntangledRemoved, +} + +#[derive(Debug)] +/// Info related to a message-port tracked by the constellation. +struct MessagePortInfo { + /// The current state of the messageport. + state: TransferState, + + /// The id of the entangled port, if any. + entangled_with: Option, +} + /// Servo supports tabs (referred to as browsers), so `Constellation` needs to /// store browser specific data for bookkeeping. struct Browser { @@ -340,6 +366,12 @@ pub struct Constellation { /// WebRender thread. webrender_api_sender: webrender_api::RenderApiSender, + /// A map of message-port Id to info. + message_ports: HashMap, + + /// A map of router-id to ipc-sender, to route messages to ports. + message_port_routers: HashMap>, + /// The set of all the pipelines in the browser. (See the `pipeline` module /// for more details.) pipelines: HashMap, @@ -751,6 +783,8 @@ where swmanager_sender: sw_mgr_clone, browsing_context_group_set: Default::default(), browsing_context_group_next_id: Default::default(), + message_ports: HashMap::new(), + message_port_routers: HashMap::new(), pipelines: HashMap::new(), browsing_contexts: HashMap::new(), pending_changes: vec![], @@ -1487,6 +1521,27 @@ where }; match content { + FromScriptMsg::RerouteMessagePort(port_id, task) => { + self.handle_reroute_messageport(port_id, task); + }, + FromScriptMsg::MessagePortShipped(port_id) => { + self.handle_messageport_shipped(port_id); + }, + FromScriptMsg::NewMessagePortRouter(router_id, ipc_sender) => { + self.handle_new_messageport_router(router_id, ipc_sender); + }, + FromScriptMsg::RemoveMessagePortRouter(router_id) => { + self.handle_remove_messageport_router(router_id); + }, + FromScriptMsg::NewMessagePort(router_id, port_id) => { + self.handle_new_messageport(router_id, port_id); + }, + FromScriptMsg::RemoveMessagePort(port_id) => { + self.handle_remove_messageport(port_id); + }, + FromScriptMsg::EntanglePorts(port1, port2) => { + self.handle_entangle_messageports(port1, port2); + }, FromScriptMsg::ForwardToEmbedder(embedder_msg) => { self.embedder_proxy .send((Some(source_top_ctx_id), embedder_msg)); @@ -1563,9 +1618,16 @@ where target: browsing_context_id, source: source_pipeline_id, target_origin: origin, + source_origin, data, } => { - self.handle_post_message_msg(browsing_context_id, source_pipeline_id, origin, data); + self.handle_post_message_msg( + browsing_context_id, + source_pipeline_id, + origin, + source_origin, + data, + ); }, FromScriptMsg::Focus => { self.handle_focus_msg(source_pipeline_id); @@ -1685,6 +1747,163 @@ where } } + fn handle_reroute_messageport(&mut self, port_id: MessagePortId, task: PortMessageTask) { + let info = match self.message_ports.get_mut(&port_id) { + Some(info) => info, + None => { + return warn!( + "Constellation asked to re-route msg to unknown messageport {:?}", + port_id + ) + }, + }; + match &mut info.state { + TransferState::Managed(router_id) => { + if let Some(sender) = self.message_port_routers.get(&router_id) { + let _ = sender.send(MessagePortMsg::NewTask(port_id, task)); + } else { + warn!("No message-port sender for {:?}", router_id); + } + }, + TransferState::TransferInProgress(queue) => queue.push_back(task), + TransferState::EntangledRemoved => warn!( + "Messageport received a message, but entangled has alread been removed {:?}", + port_id + ), + } + } + + fn handle_messageport_shipped(&mut self, port_id: MessagePortId) { + if let Some(info) = self.message_ports.get_mut(&port_id) { + if let TransferState::Managed(_) = info.state { + info.state = TransferState::TransferInProgress(VecDeque::new()); + } + } else { + warn!( + "Constellation asked to mark unknown messageport as shipped {:?}", + port_id + ); + } + } + + fn handle_new_messageport_router( + &mut self, + router_id: MessagePortRouterId, + control_sender: IpcSender, + ) { + self.message_port_routers.insert(router_id, control_sender); + } + + fn handle_remove_messageport_router(&mut self, router_id: MessagePortRouterId) { + self.message_port_routers.remove(&router_id); + } + + fn handle_new_messageport(&mut self, router_id: MessagePortRouterId, port_id: MessagePortId) { + match self.message_ports.entry(port_id) { + // If we know about this port, it means it was transferred. + Entry::Occupied(mut entry) => { + if let TransferState::EntangledRemoved = entry.get().state { + // If the entangled port has been removed while this one was in-transfer, + // remove it now. + if let Some(sender) = self.message_port_routers.get(&router_id) { + let _ = sender.send(MessagePortMsg::RemoveMessagePort(port_id)); + } else { + warn!("No message-port sender for {:?}", router_id); + } + entry.remove_entry(); + return; + } + let new_info = MessagePortInfo { + state: TransferState::Managed(router_id), + entangled_with: entry.get().entangled_with.clone(), + }; + let old_info = entry.insert(new_info); + let buffer = match old_info.state { + TransferState::TransferInProgress(buffer) => buffer, + _ => { + return warn!("Completing transfer of a port that did not have a transfer in progress."); + }, + }; + // Forward the buffered message-queue. + if let Some(sender) = self.message_port_routers.get(&router_id) { + let _ = sender.send(MessagePortMsg::CompleteTransfer(port_id.clone(), buffer)); + } else { + warn!("No message-port sender for {:?}", router_id); + } + }, + Entry::Vacant(entry) => { + let info = MessagePortInfo { + state: TransferState::Managed(router_id), + entangled_with: None, + }; + entry.insert(info); + }, + } + } + + fn handle_remove_messageport(&mut self, port_id: MessagePortId) { + let entangled = match self.message_ports.remove(&port_id) { + Some(info) => info.entangled_with, + None => { + return warn!( + "Constellation asked to remove unknown messageport {:?}", + port_id + ); + }, + }; + let entangled_id = match entangled { + Some(id) => id, + None => return, + }; + let info = match self.message_ports.get_mut(&entangled_id) { + Some(info) => info, + None => { + return warn!( + "Constellation asked to remove unknown entangled messageport {:?}", + entangled_id + ) + }, + }; + let router_id = match info.state { + TransferState::EntangledRemoved => return warn!( + "Constellation asked to remove entangled messageport by a port that was already removed {:?}", + port_id + ), + TransferState::TransferInProgress(_) => { + // Note: since the port is in-transer, we don't have a router to send it a message + // to let it know that its entangled port has been removed. + // Hence we mark it so that it will be messaged and removed once the transfer completes. + info.state = TransferState::EntangledRemoved; + return; + }, + TransferState::Managed(router_id) => router_id, + }; + if let Some(sender) = self.message_port_routers.get(&router_id) { + let _ = sender.send(MessagePortMsg::RemoveMessagePort(entangled_id)); + } else { + warn!("No message-port sender for {:?}", router_id); + } + } + + fn handle_entangle_messageports(&mut self, port1: MessagePortId, port2: MessagePortId) { + if let Some(info) = self.message_ports.get_mut(&port1) { + info.entangled_with = Some(port2); + } else { + warn!( + "Constellation asked to entangle unknow messageport: {:?}", + port1 + ); + } + if let Some(info) = self.message_ports.get_mut(&port2) { + info.entangled_with = Some(port1); + } else { + warn!( + "Constellation asked to entangle unknow messageport: {:?}", + port2 + ); + } + } + fn handle_register_serviceworker(&self, scope_things: ScopeThings, scope: ServoUrl) { if let Some(ref mgr) = self.swmanager_chan { let _ = mgr.send(ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope)); @@ -3203,7 +3422,8 @@ where browsing_context_id: BrowsingContextId, source_pipeline: PipelineId, origin: Option, - data: Vec, + source_origin: ImmutableOrigin, + data: StructuredSerializedData, ) { let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { None => { @@ -3223,6 +3443,7 @@ where source: source_pipeline, source_browsing_context: source_browsing_context, target_origin: origin, + source_origin, data, }; let result = match self.pipelines.get(&pipeline_id) { diff --git a/components/msg/Cargo.toml b/components/msg/Cargo.toml index 9850ee5789a..8c098d9a903 100644 --- a/components/msg/Cargo.toml +++ b/components/msg/Cargo.toml @@ -19,6 +19,7 @@ malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = "0.1" parking_lot = "0.9" serde = "1.0.60" +servo_url = {path = "../url"} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} [dev-dependencies] diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 94370a56e2c..1214b68aa29 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -156,6 +156,20 @@ impl PipelineNamespace { index: HistoryStateIndex(self.next_index()), } } + + fn next_message_port_id(&mut self) -> MessagePortId { + MessagePortId { + namespace_id: self.id, + index: MessagePortIndex(self.next_index()), + } + } + + fn next_message_port_router_id(&mut self) -> MessagePortRouterId { + MessagePortRouterId { + namespace_id: self.id, + index: MessagePortRouterIndex(self.next_index()), + } + } } thread_local!(pub static PIPELINE_NAMESPACE: Cell> = Cell::new(None)); @@ -297,6 +311,68 @@ impl PartialEq for TopLevelBrowsingContextId { } } +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct MessagePortIndex(pub NonZeroU32); +malloc_size_of_is_0!(MessagePortIndex); + +#[derive( + Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize, +)] +pub struct MessagePortId { + pub namespace_id: PipelineNamespaceId, + pub index: MessagePortIndex, +} + +impl MessagePortId { + pub fn new() -> MessagePortId { + PIPELINE_NAMESPACE.with(|tls| { + let mut namespace = tls.get().expect("No namespace set for this thread!"); + let next_message_port_id = namespace.next_message_port_id(); + tls.set(Some(namespace)); + next_message_port_id + }) + } +} + +impl fmt::Display for MessagePortId { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let PipelineNamespaceId(namespace_id) = self.namespace_id; + let MessagePortIndex(index) = self.index; + write!(fmt, "({},{})", namespace_id, index.get()) + } +} + +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct MessagePortRouterIndex(pub NonZeroU32); +malloc_size_of_is_0!(MessagePortRouterIndex); + +#[derive( + Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize, +)] +pub struct MessagePortRouterId { + pub namespace_id: PipelineNamespaceId, + pub index: MessagePortRouterIndex, +} + +impl MessagePortRouterId { + pub fn new() -> MessagePortRouterId { + PIPELINE_NAMESPACE.with(|tls| { + let mut namespace = tls.get().expect("No namespace set for this thread!"); + let next_message_port_router_id = namespace.next_message_port_router_id(); + tls.set(Some(namespace)); + next_message_port_router_id + }) + } +} + +impl fmt::Display for MessagePortRouterId { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let PipelineNamespaceId(namespace_id) = self.namespace_id; + let MessagePortRouterIndex(index) = self.index; + write!(fmt, "({},{})", namespace_id, index.get()) + } +} + #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct HistoryStateIndex(pub NonZeroU32); malloc_size_of_is_0!(HistoryStateIndex); @@ -419,6 +495,7 @@ pub enum ScriptHangAnnotation { ExitFullscreen, WebVREvent, PerformanceTimelineTask, + PortMessage, } #[derive(Clone, Copy, Debug, Deserialize, Serialize)] diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index b93ed291dd2..e3cd2fb59aa 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -301,9 +301,9 @@ impl ResourceChannelManager { .send(history_states.get(&history_state_id).cloned()) .unwrap(); }, - CoreResourceMsg::SetHistoryState(history_state_id, history_state) => { + CoreResourceMsg::SetHistoryState(history_state_id, structured_data) => { let mut history_states = http_state.history_states.write().unwrap(); - history_states.insert(history_state_id, history_state); + history_states.insert(history_state_id, structured_data); }, CoreResourceMsg::RemoveHistoryStates(states_to_remove) => { let mut history_states = http_state.history_states.write().unwrap(); 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..cb30f437a11 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -34,6 +34,7 @@ tinyfiledialogs = "3.0" app_units = "0.7" backtrace = {version = "0.3", optional = true} base64 = "0.10.1" +bincode = "1" bitflags = "1.0" bluetooth_traits = {path = "../bluetooth_traits"} canvas_traits = {path = "../canvas_traits"} diff --git a/components/script/dom/abstractworker.rs b/components/script/dom/abstractworker.rs index 8754bb30d96..36ff18fe01b 100644 --- a/components/script/dom/abstractworker.rs +++ b/components/script/dom/abstractworker.rs @@ -4,15 +4,19 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomObject; -use crate::dom::bindings::structuredclone::StructuredCloneData; use crate::script_runtime::CommonScriptMsg; +use script_traits::StructuredSerializedData; +use servo_url::ImmutableOrigin; /// Messages used to control the worker event loops pub enum WorkerScriptMsg { /// Common variants associated with the script messages Common(CommonScriptMsg), /// Message sent through Worker.postMessage - DOMMessage(StructuredCloneData), + DOMMessage { + origin: ImmutableOrigin, + data: StructuredSerializedData, + }, } pub struct SimpleWorkerErrorHandler { diff --git a/components/script/dom/abstractworkerglobalscope.rs b/components/script/dom/abstractworkerglobalscope.rs index af7291c013c..e8e41375b70 100644 --- a/components/script/dom/abstractworkerglobalscope.rs +++ b/components/script/dom/abstractworkerglobalscope.rs @@ -75,7 +75,7 @@ impl ScriptPort for Receiver { }; match common_msg { WorkerScriptMsg::Common(script_msg) => Ok(script_msg), - WorkerScriptMsg::DOMMessage(_) => panic!("unexpected worker event message!"), + WorkerScriptMsg::DOMMessage { .. } => panic!("unexpected worker event message!"), } } } @@ -155,4 +155,7 @@ pub fn run_worker_event_loop( .upcast::() .perform_a_microtask_checkpoint(); } + worker_scope + .upcast::() + .perform_a_message_port_garbage_collection_checkpoint(); } diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf index e3de60005bc..3ff7a044b53 100644 --- a/components/script/dom/bindings/codegen/Bindings.conf +++ b/components/script/dom/bindings/codegen/Bindings.conf @@ -30,6 +30,10 @@ DOMInterfaces = { 'weakReferenceable': True, }, +'MessagePort': { + 'weakReferenceable': True, +}, + #FIXME(jdm): This should be 'register': False, but then we don't generate enum types 'TestBinding': { 'inCompartments': ['PromiseAttribute', 'PromiseNativeHandler'], 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..25c440365c8 100644 --- a/components/script/dom/bindings/structuredclone.rs +++ b/components/script/dom/bindings/structuredclone.rs @@ -6,12 +6,15 @@ //! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data). use crate::compartments::enter_realm; -use crate::dom::bindings::conversions::root_from_handleobject; +use crate::dom::bindings::conversions::{root_from_object, ToJSValConvertible}; 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 crate::script_runtime::JSContext as SafeJSContext; use js::glue::CopyJSStructuredCloneData; use js::glue::DeleteJSAutoStructuredCloneBuffer; use js::glue::GetLengthOfJSStructuredCloneData; @@ -28,12 +31,15 @@ use js::jsapi::{JSObject, JS_ClearPendingException}; use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter}; use js::jsapi::{JS_ReadBytes, JS_WriteBytes}; use js::jsapi::{JS_ReadUint32Pair, JS_WriteUint32Pair}; +use js::jsval::UndefinedValue; use js::rust::wrappers::{JS_ReadStructuredClone, JS_WriteStructuredClone}; -use js::rust::{Handle, HandleValue, MutableHandleValue}; -use libc::size_t; +use js::rust::{CustomAutoRooterGuard, HandleValue, MutableHandleValue}; +use msg::constellation_msg::MessagePortId; +use script_traits::transferable::MessagePortImpl; +use script_traits::StructuredSerializedData; +use std::collections::HashMap; use std::os::raw; use std::ptr; -use std::slice; // TODO: Should we add Min and Max const to https://github.com/servo/rust-mozjs/blob/master/src/consts.rs? // TODO: Determine for sure which value Min and Max should have. @@ -44,6 +50,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, } @@ -126,19 +133,24 @@ impl StructuredCloneReader { unsafe fn read_blob( cx: *mut JSContext, r: *mut JSStructuredCloneReader, - sc_holder: &mut StructuredCloneHolder, + sc_holder: &mut StructuredDataHolder, ) -> *mut JSObject { let structured_reader = StructuredCloneReader { r: r }; let blob_buffer = structured_reader.read_bytes(); let type_str = structured_reader.read_str(); let target_global = GlobalScope::from_context(cx); - let blob = Blob::new( + let read_blob = Blob::new( &target_global, BlobImpl::new_from_bytes(blob_buffer), type_str, ); - let js_object = blob.reflector().get_jsobject().get(); - sc_holder.blob = Some(blob); + let js_object = read_blob.reflector().get_jsobject().get(); + match sc_holder { + StructuredDataHolder::Read { blob, .. } => { + *blob = Some(read_blob); + }, + _ => panic!("Unexpected variant of StructuredDataHolder"), + } js_object } @@ -171,7 +183,7 @@ unsafe extern "C" fn read_callback( "tag should be higher than StructuredCloneTags::Min" ); if tag == StructuredCloneTags::DomBlob as u32 { - return read_blob(cx, r, &mut *(closure as *mut StructuredCloneHolder)); + return read_blob(cx, r, &mut *(closure as *mut StructuredDataHolder)); } return ptr::null_mut(); } @@ -182,33 +194,55 @@ unsafe extern "C" fn write_callback( obj: RawHandleObject, _closure: *mut raw::c_void, ) -> bool { - if let Ok(blob) = root_from_handleobject::(Handle::from_raw(obj), cx) { + if let Ok(blob) = root_from_object::(*obj, cx) { return write_blob(blob, w).is_ok(); } return false; } unsafe extern "C" fn read_transfer_callback( - _cx: *mut JSContext, + cx: *mut JSContext, _r: *mut JSStructuredCloneReader, - _tag: u32, + tag: u32, _content: *mut raw::c_void, - _extra_data: u64, - _closure: *mut raw::c_void, - _return_object: RawMutableHandleObject, + extra_data: u64, + closure: *mut raw::c_void, + return_object: RawMutableHandleObject, ) -> bool { + if tag == StructuredCloneTags::MessagePort as u32 { + let mut sc_holder = &mut *(closure as *mut StructuredDataHolder); + let owner = GlobalScope::from_context(cx); + if let Ok(_) = ::transfer_receive( + &owner, + &mut sc_holder, + extra_data, + return_object, + ) { + return true; + } + } false } +/// unsafe extern "C" fn write_transfer_callback( - _cx: *mut JSContext, - _obj: RawHandleObject, - _closure: *mut raw::c_void, - _tag: *mut u32, - _ownership: *mut TransferableOwnership, + 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, + extra_data: *mut u64, ) -> bool { + if let Ok(port) = root_from_object::(*obj, cx) { + *tag = StructuredCloneTags::MessagePort as u32; + *ownership = TransferableOwnership::SCTAG_TMO_CUSTOM; + let mut sc_holder = &mut *(closure as *mut StructuredDataHolder); + if let Ok(data) = port.transfer(&mut sc_holder) { + *extra_data = data; + return true; + } + } false } @@ -222,10 +256,13 @@ unsafe extern "C" fn free_transfer_callback( } unsafe extern "C" fn can_transfer_callback( - _cx: *mut JSContext, - _obj: RawHandleObject, + cx: *mut JSContext, + obj: RawHandleObject, _closure: *mut raw::c_void, ) -> bool { + if let Ok(_port) = root_from_object::(*obj, cx) { + return true; + } false } @@ -241,112 +278,143 @@ static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredClon canTransfer: Some(can_transfer_callback), }; -struct StructuredCloneHolder { - blob: Option>, +/// A data holder for results from, and inputs to, structured-data read/write operations. +/// https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data +pub enum StructuredDataHolder { + Read { + /// A deserialized blob, stored temporarily here to keep it rooted. + blob: Option>, + /// A vec of transfer-received DOM ports, + /// to be made available to script through a message event. + message_ports: Option>>, + /// A map of port implementations, + /// used as part of the "transfer-receiving" steps of ports, + /// to produce the DOM ports stored in `message_ports` above. + port_impls: Option>, + }, + /// A data holder into which transferred ports + /// can be written as part of their transfer steps. + Write(Option>), } -/// A buffer for a structured clone. -pub enum StructuredCloneData { - /// A non-serializable (default) variant - Struct(*mut u64, size_t), - /// A variant that can be serialized - Vector(Vec), -} - -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 { - unsafe { - let scbuf = NewJSAutoStructuredCloneBuffer( - StructuredCloneScope::DifferentProcess, - &STRUCTURED_CLONE_CALLBACKS, - ); - let scdata = &mut ((*scbuf).data_); - let policy = CloneDataPolicy { - // TODO: SAB? - sharedArrayBuffer_: false, - }; - let result = JS_WriteStructuredClone( - cx, - message, - scdata, - StructuredCloneScope::DifferentProcess, - policy, - &STRUCTURED_CLONE_CALLBACKS, - ptr::null_mut(), - HandleValue::undefined(), - ); - if !result { - JS_ClearPendingException(cx); - return Err(Error::DataClone); - } - - let nbytes = GetLengthOfJSStructuredCloneData(scdata); - let mut data = Vec::with_capacity(nbytes); - CopyJSStructuredCloneData(scdata, data.as_mut_ptr()); - data.set_len(nbytes); - - DeleteJSAutoStructuredCloneBuffer(scbuf); - - Ok(StructuredCloneData::Vector(data)) +/// Writes a structured clone. Returns a `DataClone` error if that fails. +pub fn write( + cx: SafeJSContext, + message: HandleValue, + transfer: Option>>, +) -> Fallible { + unsafe { + rooted!(in(*cx) let mut val = UndefinedValue()); + if let Some(transfer) = transfer { + transfer.to_jsval(*cx, val.handle_mut()); } - } - /// Converts a StructuredCloneData to Vec for inter-thread sharing - pub fn move_to_arraybuffer(self) -> Vec { - match self { - StructuredCloneData::Struct(data, nbytes) => unsafe { - slice::from_raw_parts(data as *mut u8, nbytes).to_vec() - }, - StructuredCloneData::Vector(msg) => msg, - } - } - - /// Reads a structured clone. - /// - /// Panics if `JS_ReadStructuredClone` fails. - fn read_clone(global: &GlobalScope, data: *mut u64, nbytes: size_t, rval: MutableHandleValue) { - let cx = global.get_cx(); - let _ac = enter_realm(&*global); - let mut sc_holder = StructuredCloneHolder { blob: None }; + let mut sc_holder = StructuredDataHolder::Write(None); let sc_holder_ptr = &mut sc_holder as *mut _; - unsafe { - let scbuf = NewJSAutoStructuredCloneBuffer( - StructuredCloneScope::DifferentProcess, - &STRUCTURED_CLONE_CALLBACKS, - ); - let scdata = &mut ((*scbuf).data_); - 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 - )); - - DeleteJSAutoStructuredCloneBuffer(scbuf); + let scbuf = NewJSAutoStructuredCloneBuffer( + StructuredCloneScope::DifferentProcess, + &STRUCTURED_CLONE_CALLBACKS, + ); + let scdata = &mut ((*scbuf).data_); + let policy = CloneDataPolicy { + // TODO: SAB? + sharedArrayBuffer_: false, + }; + let result = JS_WriteStructuredClone( + *cx, + message, + scdata, + StructuredCloneScope::DifferentProcess, + policy, + &STRUCTURED_CLONE_CALLBACKS, + sc_holder_ptr as *mut raw::c_void, + val.handle(), + ); + if !result { + JS_ClearPendingException(*cx); + return Err(Error::DataClone); } - } - /// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone. - pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) { - 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::Struct(data, nbytes) => { - StructuredCloneData::read_clone(global, data, nbytes, rval) - }, - } + let nbytes = GetLengthOfJSStructuredCloneData(scdata); + let mut data = Vec::with_capacity(nbytes); + CopyJSStructuredCloneData(scdata, data.as_mut_ptr()); + data.set_len(nbytes); + + DeleteJSAutoStructuredCloneBuffer(scbuf); + + let mut port_impls = match sc_holder { + StructuredDataHolder::Write(port_impls) => port_impls, + _ => panic!("Unexpected variant of StructuredDataHolder"), + }; + + let data = StructuredSerializedData { + serialized: data, + ports: port_impls.take(), + }; + + Ok(data) } } -unsafe impl Send for StructuredCloneData {} +/// Read structured serialized data, possibly containing transferred objects. +/// Returns a vec of rooted transfer-received ports, or an error. +pub fn read( + global: &GlobalScope, + mut data: StructuredSerializedData, + rval: MutableHandleValue, +) -> Result>, ()> { + let cx = global.get_cx(); + let _ac = enter_realm(&*global); + let mut sc_holder = StructuredDataHolder::Read { + blob: None, + message_ports: None, + port_impls: data.ports.take(), + }; + let sc_holder_ptr = &mut sc_holder as *mut _; + unsafe { + let scbuf = NewJSAutoStructuredCloneBuffer( + StructuredCloneScope::DifferentProcess, + &STRUCTURED_CLONE_CALLBACKS, + ); + let scdata = &mut ((*scbuf).data_); + + WriteBytesToJSStructuredCloneData( + data.serialized.as_mut_ptr() as *const u8, + data.serialized.len(), + scdata, + ); + + 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); + + if result { + let (mut message_ports, port_impls) = match sc_holder { + StructuredDataHolder::Read { + message_ports, + port_impls, + .. + } => (message_ports, port_impls), + _ => panic!("Unexpected variant of StructuredDataHolder"), + }; + + // Any transfer-received port-impls should have been taken out. + assert!(port_impls.is_none()); + + match message_ports.take() { + Some(ports) => return Ok(ports), + None => return Ok(Vec::with_capacity(0)), + } + } + Err(()) + } +} diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index a914b8161a6..4b79bc7085b 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -79,7 +79,8 @@ use media::WindowGLContext; use metrics::{InteractiveMetrics, InteractiveWindow}; use mime::Mime; use msg::constellation_msg::{ - BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId, + BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId, PipelineId, + TopLevelBrowsingContextId, }; use net_traits::filemanager_thread::RelativePos; use net_traits::image::base::{Image, ImageMetadata}; @@ -93,6 +94,7 @@ use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; use script_layout_interface::rpc::LayoutRPC; use script_layout_interface::OpaqueStyleAndLayoutData; +use script_traits::transferable::MessagePortImpl; use script_traits::DrawAPaintImageResult; use script_traits::{DocumentActivity, ScriptToConstellationChan, TimerEventId, TimerSource}; use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType}; @@ -154,6 +156,11 @@ pub unsafe trait JSTraceable { unsafe_no_jsmanaged_fields!(Box, Box); +unsafe_no_jsmanaged_fields!(MessagePortImpl); +unsafe_no_jsmanaged_fields!(MessagePortId); +unsafe_no_jsmanaged_fields!(RefCell>); +unsafe_no_jsmanaged_fields!(MessagePortRouterId); + unsafe_no_jsmanaged_fields!(CSSError); unsafe_no_jsmanaged_fields!(&'static Encoding); diff --git a/components/script/dom/bindings/transferable.rs b/components/script/dom/bindings/transferable.rs new file mode 100644 index 00000000000..fb2f4a991a2 --- /dev/null +++ b/components/script/dom/bindings/transferable.rs @@ -0,0 +1,22 @@ +/* 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 crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::structuredclone::StructuredDataHolder; +use crate::dom::globalscope::GlobalScope; +use js::jsapi::MutableHandleObject; + +pub trait Transferable: DomObject { + fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result; + fn transfer_receive( + owner: &DomRoot, + sc_holder: &mut StructuredDataHolder, + extra_data: u64, + return_object: MutableHandleObject, + ) -> Result<(), ()>; +} diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 7146e1533b7..ef3b270b130 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -10,10 +10,13 @@ use crate::dom::bindings::codegen::PrototypeList::{MAX_PROTO_CHAIN_LENGTH, PROTO use crate::dom::bindings::conversions::{jsstring_to_str, private_from_proto_check}; use crate::dom::bindings::error::throw_invalid_this; use crate::dom::bindings::inheritance::TopTypeId; +use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::trace_object; +use crate::dom::messageport::MessagePort; use crate::dom::windowproxy; use crate::script_runtime::JSContext as SafeJSContext; +use js::conversions::ToJSValConvertible; use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper}; use js::glue::{GetCrossCompartmentWrapper, JS_GetReservedSlot, WrapperNew}; use js::glue::{UnwrapObjectDynamic, RUST_JSID_TO_INT, RUST_JSID_TO_STRING}; @@ -22,7 +25,7 @@ use js::jsapi::HandleId as RawHandleId; use js::jsapi::HandleObject as RawHandleObject; use js::jsapi::MutableHandleObject as RawMutableHandleObject; use js::jsapi::{AutoIdVector, CallArgs, DOMCallbacks, GetNonCCWObjectGlobal}; -use js::jsapi::{Heap, JSAutoRealm, JSContext}; +use js::jsapi::{Heap, JSAutoRealm, JSContext, JS_FreezeObject}; use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks}; use js::jsapi::{JS_EnumerateStandardClasses, JS_GetLatin1StringCharsAndLength}; use js::jsapi::{JS_IsExceptionPending, JS_IsGlobalObject}; @@ -117,6 +120,19 @@ impl Clone for DOMJSClass { } unsafe impl Sync for DOMJSClass {} +/// Returns a JSVal representing a frozen array of ports +pub fn message_ports_to_frozen_array( + message_ports: &[DomRoot], + cx: SafeJSContext, +) -> JSVal { + rooted!(in(*cx) let mut ports = UndefinedValue()); + unsafe { message_ports.to_jsval(*cx, ports.handle_mut()) }; + + rooted!(in(*cx) let obj = ports.to_object()); + unsafe { JS_FreezeObject(*cx, RawHandleObject::from(obj.handle())) }; + *ports +} + /// Returns the ProtoOrIfaceArray for the given global object. /// Fails if `global` is not a DOM global object. pub fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray { diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index a3adde75498..3745ef9f1ac 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -10,13 +10,15 @@ use crate::dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThre use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding; use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods; +use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions; use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType; use crate::dom::bindings::error::{ErrorInfo, ErrorResult}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; use crate::dom::bindings::str::DOMString; -use crate::dom::bindings::structuredclone::StructuredCloneData; +use crate::dom::bindings::structuredclone; +use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::errorevent::ErrorEvent; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::eventtarget::EventTarget; @@ -36,10 +38,10 @@ use devtools_traits::DevtoolScriptControlMsg; use dom_struct::dom_struct; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; -use js::jsapi::JSContext; use js::jsapi::JS_AddInterruptCallback; +use js::jsapi::{Heap, JSContext, JSObject}; use js::jsval::UndefinedValue; -use js::rust::HandleValue; +use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue}; use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId}; use net_traits::image_cache::ImageCache; use net_traits::request::{CredentialsMode, Destination, ParserMetadata}; @@ -462,13 +464,24 @@ impl DedicatedWorkerGlobalScope { fn handle_script_event(&self, msg: WorkerScriptMsg) { match msg { - WorkerScriptMsg::DOMMessage(data) => { + WorkerScriptMsg::DOMMessage { origin, data } => { let scope = self.upcast::(); 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); + if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut()) + { + MessageEvent::dispatch_jsval( + target, + scope.upcast(), + message.handle(), + Some(&origin.ascii_serialization()), + None, + ports, + ); + } else { + MessageEvent::dispatch_error(target, scope.upcast()); + } }, WorkerScriptMsg::Common(msg) => { self.upcast::().process_event(msg); @@ -547,6 +560,32 @@ impl DedicatedWorkerGlobalScope { )) .unwrap(); } + + // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage + fn post_message_impl( + &self, + cx: SafeJSContext, + message: HandleValue, + transfer: CustomAutoRooterGuard>, + ) -> ErrorResult { + let data = structuredclone::write(cx, message, Some(transfer))?; + let worker = self.worker.borrow().as_ref().unwrap().clone(); + let global_scope = self.upcast::(); + let pipeline_id = global_scope.pipeline_id(); + let origin = global_scope.origin().immutable().ascii_serialization(); + let task = Box::new(task!(post_worker_message: move || { + Worker::handle_message(worker, origin, data); + })); + self.parent_sender + .send(CommonScriptMsg::Task( + WorkerEvent, + task, + Some(pipeline_id), + TaskSourceName::DOMManipulation, + )) + .unwrap(); + Ok(()) + } } #[allow(unsafe_code)] @@ -560,24 +599,34 @@ 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)?; - let worker = self.worker.borrow().as_ref().unwrap().clone(); - let pipeline_id = self.upcast::().pipeline_id(); - let task = Box::new(task!(post_worker_message: move || { - Worker::handle_message(worker, data); - })); - // TODO: Change this task source to a new `unshipped-port-message-queue` task source - self.parent_sender - .send(CommonScriptMsg::Task( - WorkerEvent, - task, - Some(pipeline_id), - TaskSourceName::DOMManipulation, - )) - .unwrap(); - Ok(()) + /// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage + fn PostMessage( + &self, + cx: SafeJSContext, + message: HandleValue, + transfer: CustomAutoRooterGuard>, + ) -> ErrorResult { + self.post_message_impl(cx, message, transfer) + } + + /// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage + fn PostMessage_( + &self, + cx: SafeJSContext, + message: HandleValue, + options: RootedTraceableBox, + ) -> ErrorResult { + let mut rooted = CustomAutoRooter::new( + options + .transfer + .as_ref() + .unwrap_or(&Vec::with_capacity(0)) + .iter() + .map(|js: &RootedTraceableBox>| js.get()) + .collect(), + ); + let guard = CustomAutoRooterGuard::new(*cx, &mut rooted); + self.post_message_impl(cx, message, guard) } // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-close diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs index 8f7eda58100..c628923604e 100644 --- a/components/script/dom/dissimilaroriginwindow.rs +++ b/components/script/dom/dissimilaroriginwindow.rs @@ -4,21 +4,23 @@ use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding; use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding::DissimilarOriginWindowMethods; +use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowPostMessageOptions; use crate::dom::bindings::error::{Error, ErrorResult}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; -use crate::dom::bindings::str::DOMString; -use crate::dom::bindings::structuredclone::StructuredCloneData; +use crate::dom::bindings::str::USVString; +use crate::dom::bindings::structuredclone; +use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::dissimilaroriginlocation::DissimilarOriginLocation; use crate::dom::globalscope::GlobalScope; use crate::dom::windowproxy::WindowProxy; use crate::script_runtime::JSContext; use dom_struct::dom_struct; use ipc_channel::ipc; +use js::jsapi::{Heap, JSObject}; use js::jsval::{JSVal, UndefinedValue}; -use js::rust::HandleValue; +use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue}; use msg::constellation_msg::PipelineId; -use script_traits::ScriptMsg; -use servo_url::ImmutableOrigin; +use script_traits::{ScriptMsg, StructuredSerializedData}; use servo_url::ServoUrl; /// Represents a dissimilar-origin `Window` that exists in another script thread. @@ -133,28 +135,42 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow { false } - // https://html.spec.whatwg.org/multipage/#dom-window-postmessage - fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult { - // Step 3-5. - let origin = match &origin[..] { - "*" => None, - "/" => { - // TODO: Should be the origin of the incumbent settings object. - None - }, - url => match ServoUrl::parse(&url) { - Ok(url) => Some(url.origin()), - Err(_) => return Err(Error::Syntax), - }, - }; + /// https://html.spec.whatwg.org/multipage/#dom-window-postmessage + fn PostMessage( + &self, + cx: JSContext, + message: HandleValue, + target_origin: USVString, + mut transfer: CustomAutoRooterGuard>>, + ) -> ErrorResult { + if transfer.is_some() { + let mut rooted = CustomAutoRooter::new(transfer.take().unwrap()); + let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted)); + self.post_message_impl(&target_origin, cx, message, transfer) + } else { + self.post_message_impl(&target_origin, cx, message, None) + } + } - // Step 1-2, 6-8. - // TODO(#12717): Should implement the `transfer` argument. - let data = StructuredCloneData::write(*cx, message)?; + /// https://html.spec.whatwg.org/multipage/#dom-window-postmessage-options + fn PostMessage_( + &self, + cx: JSContext, + message: HandleValue, + options: RootedTraceableBox, + ) -> ErrorResult { + let mut rooted = CustomAutoRooter::new( + options + .transfer + .as_ref() + .unwrap_or(&Vec::with_capacity(0)) + .iter() + .map(|js: &RootedTraceableBox>| js.get()) + .collect(), + ); + let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted)); - // Step 9. - self.post_message(origin, data); - Ok(()) + self.post_message_impl(&options.targetOrigin, cx, message, transfer) } // https://html.spec.whatwg.org/multipage/#dom-opener @@ -186,17 +202,54 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow { } impl DissimilarOriginWindow { - pub fn post_message(&self, origin: Option, data: StructuredCloneData) { + /// https://html.spec.whatwg.org/multipage/#window-post-message-steps + fn post_message_impl( + &self, + target_origin: &USVString, + cx: JSContext, + message: HandleValue, + transfer: Option>>, + ) -> ErrorResult { + // Step 6-7. + let data = structuredclone::write(cx, message, transfer)?; + + self.post_message(target_origin, data) + } + + /// https://html.spec.whatwg.org/multipage/#window-post-message-steps + pub fn post_message( + &self, + target_origin: &USVString, + data: StructuredSerializedData, + ) -> ErrorResult { + // Step 1. + let target = self.window_proxy.browsing_context_id(); + // Step 2. let incumbent = match GlobalScope::incumbent() { - None => return warn!("postMessage called with no incumbent global"), + None => panic!("postMessage called with no incumbent global"), Some(incumbent) => incumbent, }; - let msg = ScriptMsg::PostMessage { - target: self.window_proxy.browsing_context_id(), - source: incumbent.pipeline_id(), - target_origin: origin, - data: data.move_to_arraybuffer(), + + let source_origin = incumbent.origin().immutable().clone(); + + // Step 3-5. + let target_origin = match target_origin.0[..].as_ref() { + "*" => None, + "/" => Some(source_origin.clone()), + url => match ServoUrl::parse(&url) { + Ok(url) => Some(url.origin().clone()), + Err(_) => return Err(Error::Syntax), + }, }; + let msg = ScriptMsg::PostMessage { + target, + source: incumbent.pipeline_id(), + source_origin, + target_origin, + data: data, + }; + // Step 8 let _ = incumbent.script_to_constellation_chan().send(msg); + Ok(()) } } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 6581dbd54bb..ce88906deff 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -1917,10 +1917,11 @@ impl Document { } } } + + let global_scope = self.window.upcast::(); // Step 10, 14 + // https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps if !self.salvageable.get() { - // https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps - let global_scope = self.window.upcast::(); // Step 1 of clean-up steps. global_scope.close_event_sources(); let msg = ScriptMsg::DiscardDocument; diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs index 92eb2c2f414..bbda6c24396 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::with_capacity(0), ) }; // Step 7 diff --git a/components/script/dom/extendablemessageevent.rs b/components/script/dom/extendablemessageevent.rs index c2ede65745a..dab06b7d79d 100644 --- a/components/script/dom/extendablemessageevent.rs +++ b/components/script/dom/extendablemessageevent.rs @@ -10,10 +10,12 @@ use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::RootedTraceableBox; +use crate::dom::bindings::utils::message_ports_to_frozen_array; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::extendableevent::ExtendableEvent; use crate::dom::globalscope::GlobalScope; +use crate::dom::messageport::MessagePort; use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; use crate::script_runtime::JSContext; use dom_struct::dom_struct; @@ -29,6 +31,7 @@ pub struct ExtendableMessageEvent { data: Heap, origin: DOMString, lastEventId: DOMString, + ports: Vec>, } impl ExtendableMessageEvent { @@ -40,12 +43,14 @@ impl ExtendableMessageEvent { data: HandleValue, origin: DOMString, lastEventId: DOMString, + ports: Vec>, ) -> DomRoot { let ev = Box::new(ExtendableMessageEvent { event: ExtendableEvent::new_inherited(), data: Heap::default(), - origin: origin, - lastEventId: lastEventId, + origin, + lastEventId, + ports, }); let ev = reflect_dom_object(ev, global, ExtendableMessageEventBinding::Wrap); { @@ -71,13 +76,19 @@ impl ExtendableMessageEvent { init.data.handle(), init.origin.clone().unwrap(), init.lastEventId.clone().unwrap(), + vec![], ); Ok(ev) } } impl ExtendableMessageEvent { - pub fn dispatch_jsval(target: &EventTarget, scope: &GlobalScope, message: HandleValue) { + pub fn dispatch_jsval( + target: &EventTarget, + scope: &GlobalScope, + message: HandleValue, + ports: Vec>, + ) { let Extendablemessageevent = ExtendableMessageEvent::new( scope, atom!("message"), @@ -86,6 +97,7 @@ impl ExtendableMessageEvent { message, DOMString::new(), DOMString::new(), + ports, ); Extendablemessageevent.upcast::().fire(target); } @@ -111,4 +123,9 @@ impl ExtendableMessageEventMethods for ExtendableMessageEvent { fn IsTrusted(&self) -> bool { self.event.IsTrusted() } + + /// https://w3c.github.io/ServiceWorker/#extendablemessage-event-ports + fn Ports(&self, cx: JSContext) -> JSVal { + message_ports_to_frozen_array(self.ports.as_slice(), cx) + } } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index c7635e98463..7d07e67f9ec 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -9,17 +9,21 @@ use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlo use crate::dom::bindings::conversions::{root_from_object, root_from_object_static}; use crate::dom::bindings::error::{report_pending_exception, ErrorInfo}; use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript}; use crate::dom::bindings::str::DOMString; -use crate::dom::bindings::weakref::DOMTracker; +use crate::dom::bindings::structuredclone; +use crate::dom::bindings::weakref::{DOMTracker, WeakRef}; use crate::dom::crypto::Crypto; use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use crate::dom::errorevent::ErrorEvent; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::eventsource::EventSource; use crate::dom::eventtarget::EventTarget; +use crate::dom::messageevent::MessageEvent; +use crate::dom::messageport::MessagePort; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::performance::Performance; use crate::dom::window::Window; @@ -33,36 +37,43 @@ 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::TaskSource; use crate::task_source::TaskSourceName; use crate::timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle}; use crate::timers::{OneshotTimers, TimerCallback}; use content_security_policy::CspList; use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use dom_struct::dom_struct; -use ipc_channel::ipc::IpcSender; +use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::router::ROUTER; use js::glue::{IsWrapper, UnwrapObjectDynamic}; use js::jsapi::JSObject; use js::jsapi::{CurrentGlobalOrNull, GetNonCCWObjectGlobal}; use js::jsapi::{HandleObject, Heap}; use js::jsapi::{JSAutoRealm, JSContext}; +use js::jsval::UndefinedValue; use js::panic::maybe_resume_unwind; use js::rust::wrappers::EvaluateUtf8; use js::rust::{get_object_class, CompileOptionsWrapper, ParentRuntime, Runtime}; use js::rust::{HandleValue, MutableHandleValue}; use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; -use msg::constellation_msg::PipelineId; +use msg::constellation_msg::{MessagePortId, MessagePortRouterId, PipelineId}; use net_traits::image_cache::ImageCache; use net_traits::{CoreResourceThread, IpcSend, ResourceThreads}; use profile_traits::{mem as profile_mem, time as profile_time}; -use script_traits::{MsDuration, ScriptToConstellationChan, TimerEvent}; +use script_traits::transferable::MessagePortImpl; +use script_traits::{ + MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg, ScriptToConstellationChan, TimerEvent, +}; use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource}; use servo_url::{MutableOrigin, ServoUrl}; use std::borrow::Cow; use std::cell::Cell; use std::collections::hash_map::Entry; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::ffi::CString; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; @@ -84,6 +95,9 @@ pub struct GlobalScope { crypto: MutNullableDom, next_worker_id: Cell, + /// The message-port router id for this global, if it is managing ports. + message_port_state: DomRefCell, + /// Pipeline id associated with this global. pipeline_id: PipelineId, @@ -167,6 +181,77 @@ pub struct GlobalScope { user_agent: Cow<'static, str>, } +/// A wrapper for glue-code between the ipc router and the event-loop. +struct MessageListener { + canceller: TaskCanceller, + task_source: PortMessageQueue, + context: Trusted, +} + +/// Data representing a message-port managed by this global. +#[derive(JSTraceable, MallocSizeOf)] +pub enum ManagedMessagePort { + /// We keep ports pending when they are first transfer-received, + /// and only add them, and ask the constellation to complete the transfer, + /// in a subsequent task if the port hasn't been re-transfered. + Pending(MessagePortImpl, WeakRef), + /// A port who was transferred into, or initially created in, this realm, + /// and that hasn't been re-transferred in the same task it was noted. + Added(MessagePortImpl, WeakRef), +} + +/// State representing whether this global is currently managing messageports. +#[derive(JSTraceable, MallocSizeOf)] +pub enum MessagePortState { + /// The message-port router id for this global, and a map of managed ports. + Managed( + MessagePortRouterId, + HashMap, + ), + /// This global is not managing any ports at this time. + UnManaged, +} + +impl MessageListener { + /// A new message came in, handle it via a task enqueued on the event-loop. + /// A task is required, since we are using a trusted globalscope, + /// and we can only access the root from the event-loop. + fn notify(&self, msg: MessagePortMsg) { + match msg { + MessagePortMsg::CompleteTransfer(port_id, tasks) => { + let context = self.context.clone(); + let _ = self.task_source.queue_with_canceller( + task!(process_complete_transfer: move || { + let global = context.root(); + global.complete_port_transfer(port_id, tasks); + }), + &self.canceller, + ); + }, + MessagePortMsg::NewTask(port_id, task) => { + let context = self.context.clone(); + let _ = self.task_source.queue_with_canceller( + task!(process_new_task: move || { + let global = context.root(); + global.route_task_to_port(port_id, task); + }), + &self.canceller, + ); + }, + MessagePortMsg::RemoveMessagePort(port_id) => { + let context = self.context.clone(); + let _ = self.task_source.queue_with_canceller( + task!(process_remove_message_port: move || { + let global = context.root(); + global.remove_message_port(&port_id); + }), + &self.canceller, + ); + }, + } + } +} + impl GlobalScope { pub fn new_inherited( pipeline_id: PipelineId, @@ -183,6 +268,7 @@ impl GlobalScope { user_agent: Cow<'static, str>, ) -> Self { Self { + message_port_state: DomRefCell::new(MessagePortState::UnManaged), eventtarget: EventTarget::new_inherited(), crypto: Default::default(), next_worker_id: Cell::new(WorkerId(0)), @@ -208,6 +294,397 @@ impl GlobalScope { } } + /// Complete the transfer of a message-port. + fn complete_port_transfer(&self, port_id: MessagePortId, tasks: VecDeque) { + let should_start = if let MessagePortState::Managed(_id, message_ports) = + &mut *self.message_port_state.borrow_mut() + { + match message_ports.get_mut(&port_id) { + None => { + panic!("CompleteTransfer msg received in a global not managing the port."); + }, + Some(ManagedMessagePort::Pending(_, _)) => { + panic!("CompleteTransfer msg received for a pending port."); + }, + Some(ManagedMessagePort::Added(port_impl, _port)) => { + port_impl.complete_transfer(tasks); + port_impl.enabled() + }, + } + } else { + return warn!("CompleteTransfer msg received in a global not managing any ports."); + }; + if should_start { + self.start_message_port(&port_id); + } + } + + /// Update our state to un-managed, + /// and tell the constellation to drop the sender to our message-port router. + pub fn remove_message_ports_router(&self) { + if let MessagePortState::Managed(router_id, _message_ports) = + &*self.message_port_state.borrow() + { + let _ = self + .script_to_constellation_chan() + .send(ScriptMsg::RemoveMessagePortRouter(router_id.clone())); + } + *self.message_port_state.borrow_mut() = MessagePortState::UnManaged; + } + + /// + pub fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) { + if let MessagePortState::Managed(_id, message_ports) = + &mut *self.message_port_state.borrow_mut() + { + for (port_id, entangled_id) in &[(port1, port2), (port2, port1)] { + match message_ports.get_mut(&port_id) { + None => { + return warn!("entangled_ports called on a global not managing the port."); + }, + Some(ManagedMessagePort::Pending(port_impl, dom_port)) => { + dom_port + .root() + .expect("Port to be entangled to not have been GC'ed") + .entangle(entangled_id.clone()); + port_impl.entangle(entangled_id.clone()); + }, + Some(ManagedMessagePort::Added(port_impl, dom_port)) => { + dom_port + .root() + .expect("Port to be entangled to not have been GC'ed") + .entangle(entangled_id.clone()); + port_impl.entangle(entangled_id.clone()); + }, + } + } + } else { + panic!("entangled_ports called on a global not managing any ports."); + } + + let _ = self + .script_to_constellation_chan() + .send(ScriptMsg::EntanglePorts(port1, port2)); + } + + /// Remove all referrences to a port. + pub fn remove_message_port(&self, port_id: &MessagePortId) { + let is_empty = if let MessagePortState::Managed(_id, message_ports) = + &mut *self.message_port_state.borrow_mut() + { + match message_ports.remove(&port_id) { + None => panic!("remove_message_port called on a global not managing the port."), + Some(_) => message_ports.is_empty(), + } + } else { + return warn!("remove_message_port called on a global not managing any ports."); + }; + if is_empty { + // Remove our port router, + // it will be setup again if we start managing ports again. + self.remove_message_ports_router(); + } + } + + /// Handle the transfer of a port in the current task. + pub fn mark_port_as_transferred(&self, port_id: &MessagePortId) -> MessagePortImpl { + if let MessagePortState::Managed(_id, message_ports) = + &mut *self.message_port_state.borrow_mut() + { + let mut port = match message_ports.remove(&port_id) { + None => { + panic!("mark_port_as_transferred called on a global not managing the port.") + }, + Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl, + Some(ManagedMessagePort::Added(port_impl, _)) => port_impl, + }; + port.set_has_been_shipped(); + let _ = self + .script_to_constellation_chan() + .send(ScriptMsg::MessagePortShipped(port_id.clone())); + port + } else { + panic!("mark_port_as_transferred called on a global not managing any ports."); + } + } + + /// + pub fn start_message_port(&self, port_id: &MessagePortId) { + if let MessagePortState::Managed(_id, message_ports) = + &mut *self.message_port_state.borrow_mut() + { + let port = match message_ports.get_mut(&port_id) { + None => panic!("start_message_port called on a unknown port."), + Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl, + Some(ManagedMessagePort::Added(port_impl, _)) => port_impl, + }; + if let Some(message_buffer) = port.start() { + for task in message_buffer { + let port_id = port_id.clone(); + let this = Trusted::new(&*self); + let _ = self.port_message_queue().queue( + task!(process_pending_port_messages: move || { + let target_global = this.root(); + target_global.route_task_to_port(port_id, task); + }), + &self, + ); + } + } + } else { + return warn!("start_message_port called on a global not managing any ports."); + } + } + + /// + pub fn close_message_port(&self, port_id: &MessagePortId) { + if let MessagePortState::Managed(_id, message_ports) = + &mut *self.message_port_state.borrow_mut() + { + let port = match message_ports.get_mut(&port_id) { + None => panic!("close_message_port called on an unknown port."), + Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl, + Some(ManagedMessagePort::Added(port_impl, _)) => port_impl, + }; + port.close(); + } else { + return warn!("close_message_port called on a global not managing any ports."); + } + } + + /// + // Steps 6 and 7 + pub fn post_messageport_msg(&self, port_id: MessagePortId, task: PortMessageTask) { + if let MessagePortState::Managed(_id, message_ports) = + &mut *self.message_port_state.borrow_mut() + { + let port = match message_ports.get_mut(&port_id) { + None => panic!("post_messageport_msg called on an unknown port."), + Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl, + Some(ManagedMessagePort::Added(port_impl, _)) => port_impl, + }; + if let Some(entangled_id) = port.entangled_port_id() { + // Step 7 + let this = Trusted::new(&*self); + let _ = self.port_message_queue().queue( + task!(post_message: move || { + let global = this.root(); + // Note: we do this in a task, as this will ensure the global and constellation + // are aware of any transfer that might still take place in the current task. + global.route_task_to_port(entangled_id, task); + }), + self, + ); + } + } else { + return warn!("post_messageport_msg called on a global not managing any ports."); + } + } + + /// If we don't know about the port, + /// send the message to the constellation for routing. + fn re_route_port_task(&self, port_id: MessagePortId, task: PortMessageTask) { + let _ = self + .script_to_constellation_chan() + .send(ScriptMsg::RerouteMessagePort(port_id, task)); + } + + /// Route the task to be handled by the relevant port. + pub fn route_task_to_port(&self, port_id: MessagePortId, task: PortMessageTask) { + let should_dispatch = if let MessagePortState::Managed(_id, message_ports) = + &mut *self.message_port_state.borrow_mut() + { + if !message_ports.contains_key(&port_id) { + self.re_route_port_task(port_id, task); + return; + } + let (port_impl, dom_port) = match message_ports.get_mut(&port_id) { + None => panic!("route_task_to_port called for an unknown port."), + Some(ManagedMessagePort::Pending(port_impl, dom_port)) => (port_impl, dom_port), + Some(ManagedMessagePort::Added(port_impl, dom_port)) => (port_impl, dom_port), + }; + + // If the port is not enabled yet, or if is awaiting the completion of it's transfer, + // the task will be buffered and dispatched upon enablement or completion of the transfer. + if let Some(task_to_dispatch) = port_impl.handle_incoming(task) { + // Get a corresponding DOM message-port object. + let dom_port = match dom_port.root() { + Some(dom_port) => dom_port, + None => panic!("Messageport Gc'ed too early"), + }; + Some((dom_port, task_to_dispatch)) + } else { + None + } + } else { + self.re_route_port_task(port_id, task); + return; + }; + if let Some((dom_port, PortMessageTask { origin, data })) = should_dispatch { + // Substep 3-4 + rooted!(in(*self.get_cx()) let mut message_clone = UndefinedValue()); + if let Ok(ports) = structuredclone::read(self, data, message_clone.handle_mut()) { + // Substep 6 + // Dispatch the event, using the dom message-port. + MessageEvent::dispatch_jsval( + &dom_port.upcast(), + self, + message_clone.handle(), + Some(&origin.ascii_serialization()), + None, + ports, + ); + } else { + // Step 4, fire messageerror event. + MessageEvent::dispatch_error(&dom_port.upcast(), self); + } + } + } + + /// Check all ports that have been transfer-received in the previous task, + /// and complete their transfer if they haven't been re-transferred. + pub fn maybe_add_pending_ports(&self) { + if let MessagePortState::Managed(router_id, message_ports) = + &mut *self.message_port_state.borrow_mut() + { + let to_be_added: Vec = message_ports + .iter() + .filter_map(|(id, port_info)| match port_info { + ManagedMessagePort::Pending(_, _) => Some(id.clone()), + _ => None, + }) + .collect(); + for id in to_be_added { + let (id, port_info) = message_ports + .remove_entry(&id) + .expect("Collected port-id to match an entry"); + if let ManagedMessagePort::Pending(port_impl, dom_port) = port_info { + let _ = self + .script_to_constellation_chan() + .send(ScriptMsg::NewMessagePort( + router_id.clone(), + port_impl.message_port_id().clone(), + )); + let new_port_info = ManagedMessagePort::Added(port_impl, dom_port); + let present = message_ports.insert(id, new_port_info); + assert!(present.is_none()); + } + } + } else { + warn!("maybe_add_pending_ports called on a global not managing any ports."); + } + } + + /// https://html.spec.whatwg.org/multipage/#ports-and-garbage-collection + pub fn perform_a_message_port_garbage_collection_checkpoint(&self) { + let is_empty = if let MessagePortState::Managed(_id, message_ports) = + &mut *self.message_port_state.borrow_mut() + { + let to_be_removed: Vec = message_ports + .iter() + .filter_map(|(id, port_info)| { + if let ManagedMessagePort::Added(_port_impl, dom_port) = port_info { + if dom_port.root().is_none() { + // Let the constellation know to drop this port and the one it is entangled with, + // and to forward this message to the script-process where the entangled is found. + let _ = self + .script_to_constellation_chan() + .send(ScriptMsg::RemoveMessagePort(id.clone())); + return Some(id.clone()); + } + } + None + }) + .collect(); + for id in to_be_removed { + message_ports.remove(&id); + } + message_ports.is_empty() + } else { + false + }; + if is_empty { + self.remove_message_ports_router(); + } + } + + /// Start tracking a message-port + pub fn track_message_port(&self, dom_port: &MessagePort, port_impl: Option) { + let mut current_state = self.message_port_state.borrow_mut(); + + if let MessagePortState::UnManaged = &*current_state { + // Setup a route for IPC, for messages from the constellation to our ports. + let (port_control_sender, port_control_receiver) = + ipc::channel().expect("ipc channel failure"); + let context = Trusted::new(self); + let (task_source, canceller) = ( + self.port_message_queue(), + self.task_canceller(TaskSourceName::PortMessage), + ); + let listener = MessageListener { + canceller, + task_source, + context, + }; + ROUTER.add_route( + port_control_receiver.to_opaque(), + Box::new(move |message| { + let msg = message.to(); + match msg { + Ok(msg) => listener.notify(msg), + Err(err) => warn!("Error receiving a MessagePortMsg: {:?}", err), + } + }), + ); + let router_id = MessagePortRouterId::new(); + *current_state = MessagePortState::Managed(router_id.clone(), HashMap::new()); + let _ = self + .script_to_constellation_chan() + .send(ScriptMsg::NewMessagePortRouter( + router_id, + port_control_sender, + )); + } + + if let MessagePortState::Managed(router_id, message_ports) = &mut *current_state { + if let Some(port_impl) = port_impl { + // We keep transfer-received ports as "pending", + // and only ask the constellation to complete the transfer + // if they're not re-shipped in the current task. + message_ports.insert( + dom_port.message_port_id().clone(), + ManagedMessagePort::Pending(port_impl, WeakRef::new(dom_port)), + ); + + // Queue a task to complete the transfer, + // unless the port is re-transferred in the current task. + let this = Trusted::new(&*self); + let _ = self.port_message_queue().queue( + task!(process_pending_port_messages: move || { + let target_global = this.root(); + target_global.maybe_add_pending_ports(); + }), + &self, + ); + } else { + // If this is a newly-created port, let the constellation immediately know. + let port_impl = MessagePortImpl::new(dom_port.message_port_id().clone()); + message_ports.insert( + dom_port.message_port_id().clone(), + ManagedMessagePort::Added(port_impl, WeakRef::new(dom_port)), + ); + let _ = self + .script_to_constellation_chan() + .send(ScriptMsg::NewMessagePort( + router_id.clone(), + dom_port.message_port_id().clone(), + )); + }; + } else { + panic!("track_message_port should have first switched the state to managed."); + } + } + pub fn track_worker(&self, closing_worker: Arc) { self.list_auto_close_worker .borrow_mut() @@ -504,7 +981,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::() { @@ -516,7 +993,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::() { + return window.task_manager().port_message_queue(); + } + if let Some(worker) = self.downcast::() { + 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::() { @@ -528,7 +1017,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::() { diff --git a/components/script/dom/history.rs b/components/script/dom/history.rs index 92c27dfb82d..c02cdb42472 100644 --- a/components/script/dom/history.rs +++ b/components/script/dom/history.rs @@ -11,7 +11,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::{DOMString, USVString}; -use crate::dom::bindings::structuredclone::StructuredCloneData; +use crate::dom::bindings::structuredclone; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; @@ -27,7 +27,7 @@ use msg::constellation_msg::{HistoryStateId, TraversalDirection}; use net_traits::{CoreResourceMsg, IpcSend}; use profile_traits::ipc; use profile_traits::ipc::channel; -use script_traits::ScriptMsg; +use script_traits::{ScriptMsg, StructuredSerializedData}; use servo_url::ServoUrl; use std::cell::Cell; @@ -115,11 +115,16 @@ impl History { }; match serialized_data { - Some(serialized_data) => { + Some(data) => { + let data = StructuredSerializedData { + serialized: data, + ports: None, + }; let global_scope = self.window.upcast::(); rooted!(in(*global_scope.get_cx()) let mut state = UndefinedValue()); - StructuredCloneData::Vector(serialized_data) - .read(&global_scope, state.handle_mut()); + if let Err(_) = structuredclone::read(&global_scope, data, state.handle_mut()) { + warn!("Error reading structuredclone data"); + } self.state.set(state.get()); }, None => { @@ -185,7 +190,7 @@ impl History { // TODO: Step 4 // Step 5 - let serialized_data = StructuredCloneData::write(*cx, data)?.move_to_arraybuffer(); + let serialized_data = structuredclone::write(cx, data, None)?; let new_url: ServoUrl = match url { // Step 6 @@ -254,7 +259,7 @@ impl History { }; let _ = self.window.upcast::().resource_threads().send( - CoreResourceMsg::SetHistoryState(state_id, serialized_data.clone()), + CoreResourceMsg::SetHistoryState(state_id, serialized_data.serialized.clone()), ); // TODO: Step 9 Update current entry to represent a GET request @@ -266,7 +271,9 @@ impl History { // Step 11 let global_scope = self.window.upcast::(); rooted!(in(*cx) let mut state = UndefinedValue()); - StructuredCloneData::Vector(serialized_data).read(&global_scope, state.handle_mut()); + if let Err(_) = structuredclone::read(&global_scope, serialized_data, state.handle_mut()) { + warn!("Error reading structuredclone data"); + } // Step 12 self.state.set(state.get()); diff --git a/components/script/dom/messagechannel.rs b/components/script/dom/messagechannel.rs new file mode 100644 index 00000000000..efe883c6e6e --- /dev/null +++ b/components/script/dom/messagechannel.rs @@ -0,0 +1,66 @@ +/* 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::{reflect_dom_object, Reflector}; +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, + port2: Dom, +} + +impl MessageChannel { + /// + pub fn Constructor(global: &GlobalScope) -> Fallible> { + let incumbent = GlobalScope::incumbent().ok_or(Error::InvalidState)?; + + // Step 1 + let port1 = MessagePort::new(&incumbent); + + // Step 2 + let port2 = MessagePort::new(&incumbent); + + incumbent.track_message_port(&*port1, None); + incumbent.track_message_port(&*port2, None); + + // Step 3 + incumbent.entangle_ports( + port1.message_port_id().clone(), + port2.message_port_id().clone(), + ); + + // 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 { + /// + fn Port1(&self) -> DomRoot { + DomRoot::from_ref(&*self.port1) + } + + /// + fn Port2(&self) -> DomRoot { + DomRoot::from_ref(&*self.port2) + } +} diff --git a/components/script/dom/messageevent.rs b/components/script/dom/messageevent.rs index 52c0bd1acf1..cb8ebaaebd1 100644 --- a/components/script/dom/messageevent.rs +++ b/components/script/dom/messageevent.rs @@ -11,9 +11,11 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::RootedTraceableBox; +use crate::dom::bindings::utils::message_ports_to_frozen_array; 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; @@ -31,6 +33,7 @@ pub struct MessageEvent { origin: DOMString, source: Option>, lastEventId: DOMString, + ports: Vec>, } impl MessageEvent { @@ -41,6 +44,7 @@ impl MessageEvent { DOMString::new(), None, DOMString::new(), + vec![], ) } @@ -50,13 +54,15 @@ impl MessageEvent { origin: DOMString, source: Option<&WindowProxy>, lastEventId: DOMString, + ports: Vec>, ) -> DomRoot { 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 +79,9 @@ impl MessageEvent { origin: DOMString, source: Option<&WindowProxy>, lastEventId: DOMString, + ports: Vec>, ) -> DomRoot { - 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.init_event(type_, bubbles, cancelable); @@ -100,6 +107,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 +120,7 @@ impl MessageEvent { message: HandleValue, origin: Option<&str>, source: Option<&WindowProxy>, + ports: Vec>, ) { let messageevent = MessageEvent::new( scope, @@ -122,18 +131,39 @@ impl MessageEvent { DOMString::from(origin.unwrap_or("")), source, DOMString::new(), + ports, + ); + messageevent.upcast::().fire(target); + } + + pub fn dispatch_error(target: &EventTarget, scope: &GlobalScope) { + let init = MessageEventBinding::MessageEventInit::empty(); + let source = init + .source + .as_ref() + .and_then(|inner| inner.as_ref().map(|source| source.window_proxy())); + let messageevent = MessageEvent::new( + scope, + atom!("messageerror"), + init.parent.bubbles, + init.parent.cancelable, + init.data.handle(), + init.origin.clone(), + source.as_ref().map(|source| &**source), + init.lastEventId.clone(), + init.ports.clone().unwrap_or(vec![]), ); messageevent.upcast::().fire(target); } } impl MessageEventMethods for MessageEvent { - // 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 + /// fn Origin(&self) -> DOMString { self.origin.clone() } @@ -145,13 +175,18 @@ impl MessageEventMethods for MessageEvent { .and_then(|source| NonNull::new(source.reflector().get_jsobject().get())) } - // https://html.spec.whatwg.org/multipage/#dom-messageevent-lasteventid + /// fn LastEventId(&self) -> DOMString { self.lastEventId.clone() } - // https://dom.spec.whatwg.org/#dom-event-istrusted + /// fn IsTrusted(&self) -> bool { self.event.IsTrusted() } + + /// + fn Ports(&self, cx: JSContext) -> JSVal { + message_ports_to_frozen_array(self.ports.as_slice(), cx) + } } diff --git a/components/script/dom/messageport.rs b/components/script/dom/messageport.rs new file mode 100644 index 00000000000..67d470a10a2 --- /dev/null +++ b/components/script/dom/messageport.rs @@ -0,0 +1,345 @@ +/* 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, Wrap, +}; +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>, + detached: Cell, +} + +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, + } + } + + /// + pub fn new(owner: &GlobalScope) -> DomRoot { + let port_id = MessagePortId::new(); + reflect_dom_object(Box::new(MessagePort::new_inherited(port_id)), owner, Wrap) + } + + /// Create a new port for an incoming transfer-received one. + fn new_transferred( + owner: &GlobalScope, + transferred_port: MessagePortId, + entangled_port: Option, + ) -> DomRoot { + 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, + ) + } + + /// + 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() + } + + /// + fn set_onmessage(&self, listener: Option>) { + let eventtarget = self.upcast::(); + eventtarget.set_event_handler_common("message", listener); + } + + /// + fn post_message_impl( + &self, + cx: SafeJSContext, + message: HandleValue, + transfer: CustomAutoRooterGuard>, + ) -> 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::(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 { + /// + fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result { + if self.detached.get() { + return Err(()); + } + + let port_impls = match sc_holder { + StructuredDataHolder::Write(port_impls) => port_impls, + _ => 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: &DomRoot, + 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 { + /// + fn PostMessage( + &self, + cx: SafeJSContext, + message: HandleValue, + transfer: CustomAutoRooterGuard>, + ) -> ErrorResult { + if self.detached.get() { + return Ok(()); + } + self.post_message_impl(cx, message, transfer) + } + + /// + fn PostMessage_( + &self, + cx: SafeJSContext, + message: HandleValue, + options: RootedTraceableBox, + ) -> 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>| js.get()) + .collect(), + ); + let guard = CustomAutoRooterGuard::new(*cx, &mut rooted); + self.post_message_impl(cx, message, guard) + } + + /// + fn Start(&self) { + if self.detached.get() { + return; + } + self.global().start_message_port(self.message_port_id()); + } + + /// + fn Close(&self) { + if self.detached.get() { + return; + } + self.detached.set(true); + self.global().close_message_port(self.message_port_id()); + } + + /// + fn GetOnmessage(&self) -> Option> { + if self.detached.get() { + return None; + } + let eventtarget = self.upcast::(); + eventtarget.get_event_handler_common("message") + } + + /// + fn SetOnmessage(&self, listener: Option>) { + 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()); + } + + /// + event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror); +} 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..ec64036d305 100644 --- a/components/script/dom/serviceworker.rs +++ b/components/script/dom/serviceworker.rs @@ -4,6 +4,7 @@ use crate::dom::abstractworker::SimpleWorkerErrorHandler; use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions; use crate::dom::bindings::codegen::Bindings::ServiceWorkerBinding::{ ServiceWorkerMethods, ServiceWorkerState, Wrap, }; @@ -13,13 +14,15 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::USVString; -use crate::dom::bindings::structuredclone::StructuredCloneData; +use crate::dom::bindings::structuredclone; +use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::script_runtime::JSContext; use crate::task::TaskOnce; use dom_struct::dom_struct; -use js::rust::HandleValue; +use js::jsapi::{Heap, JSObject}; +use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue}; use script_traits::{DOMMessage, ScriptMsg}; use servo_url::ServoUrl; use std::cell::Cell; @@ -77,6 +80,34 @@ impl ServiceWorker { pub fn get_script_url(&self) -> ServoUrl { ServoUrl::parse(&self.script_url.borrow().clone()).unwrap() } + + /// https://w3c.github.io/ServiceWorker/#service-worker-postmessage + fn post_message_impl( + &self, + cx: JSContext, + message: HandleValue, + transfer: CustomAutoRooterGuard>, + ) -> ErrorResult { + // Step 1 + if let ServiceWorkerState::Redundant = self.state.get() { + return Err(Error::InvalidState); + } + // Step 7 + let data = structuredclone::write(cx, message, Some(transfer))?; + let incumbent = GlobalScope::incumbent().expect("no incumbent global?"); + let msg_vec = DOMMessage { + origin: incumbent.origin().immutable().clone(), + data, + }; + let _ = self + .global() + .script_to_constellation_chan() + .send(ScriptMsg::ForwardDOMMessage( + msg_vec, + self.scope_url.clone(), + )); + Ok(()) + } } impl ServiceWorkerMethods for ServiceWorker { @@ -90,23 +121,34 @@ impl ServiceWorkerMethods for ServiceWorker { USVString(self.script_url.borrow().clone()) } - // https://w3c.github.io/ServiceWorker/#service-worker-postmessage - fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult { - // Step 1 - if let ServiceWorkerState::Redundant = self.state.get() { - return Err(Error::InvalidState); - } - // Step 7 - let data = StructuredCloneData::write(*cx, message)?; - let msg_vec = DOMMessage(data.move_to_arraybuffer()); - let _ = self - .global() - .script_to_constellation_chan() - .send(ScriptMsg::ForwardDOMMessage( - msg_vec, - self.scope_url.clone(), - )); - Ok(()) + /// https://w3c.github.io/ServiceWorker/#service-worker-postmessage + fn PostMessage( + &self, + cx: JSContext, + message: HandleValue, + transfer: CustomAutoRooterGuard>, + ) -> ErrorResult { + self.post_message_impl(cx, message, transfer) + } + + /// https://w3c.github.io/ServiceWorker/#service-worker-postmessage + fn PostMessage_( + &self, + cx: JSContext, + message: HandleValue, + options: RootedTraceableBox, + ) -> ErrorResult { + let mut rooted = CustomAutoRooter::new( + options + .transfer + .as_ref() + .unwrap_or(&Vec::with_capacity(0)) + .iter() + .map(|js: &RootedTraceableBox>| js.get()) + .collect(), + ); + let guard = CustomAutoRooterGuard::new(*cx, &mut rooted); + self.post_message_impl(cx, message, guard) } // https://w3c.github.io/ServiceWorker/#service-worker-container-onerror-attribute diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index 5af7f07ab0f..657f43627ed 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -12,12 +12,14 @@ use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::structuredclone; use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::extendableevent::ExtendableEvent; use crate::dom::extendablemessageevent::ExtendableMessageEvent; use crate::dom::globalscope::GlobalScope; +use crate::dom::messageevent::MessageEvent; use crate::dom::worker::TrustedWorkerAddress; use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::fetch::load_whole_resource; @@ -409,13 +411,22 @@ impl ServiceWorkerGlobalScope { use self::ServiceWorkerScriptMsg::*; match msg { - CommonWorker(WorkerScriptMsg::DOMMessage(data)) => { + CommonWorker(WorkerScriptMsg::DOMMessage { data, .. }) => { let scope = self.upcast::(); 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()); - ExtendableMessageEvent::dispatch_jsval(target, scope.upcast(), message.handle()); + if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut()) + { + ExtendableMessageEvent::dispatch_jsval( + target, + scope.upcast(), + message.handle(), + ports, + ); + } else { + MessageEvent::dispatch_error(target, scope.upcast()); + } }, CommonWorker(WorkerScriptMsg::Common(msg)) => { self.upcast::().process_event(msg); diff --git a/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl b/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl index 488cbe052dd..2df97dbacfe 100644 --- a/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl +++ b/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl @@ -5,9 +5,9 @@ // https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope [Global=(Worker,DedicatedWorker), Exposed=DedicatedWorker] /*sealed*/ interface DedicatedWorkerGlobalScope : WorkerGlobalScope { - [Throws] - void postMessage(any message/*, optional sequence transfer*/); - attribute EventHandler onmessage; + [Throws] void postMessage(any message, sequence transfer); + [Throws] void postMessage(any message, optional PostMessageOptions options = {}); + attribute EventHandler onmessage; void close(); }; diff --git a/components/script/dom/webidls/DissimilarOriginWindow.webidl b/components/script/dom/webidls/DissimilarOriginWindow.webidl index 7c0ca4a2c80..bbfcb75b69b 100644 --- a/components/script/dom/webidls/DissimilarOriginWindow.webidl +++ b/components/script/dom/webidls/DissimilarOriginWindow.webidl @@ -25,7 +25,8 @@ interface DissimilarOriginWindow : GlobalScope { void close(); readonly attribute boolean closed; - [Throws] void postMessage(any message, DOMString targetOrigin); + [Throws] void postMessage(any message, USVString targetOrigin, optional sequence transfer /*= []*/); + [Throws] void postMessage(any message, optional WindowPostMessageOptions options = {}); attribute any opener; void blur(); void focus(); diff --git a/components/script/dom/webidls/ExtendableMessageEvent.webidl b/components/script/dom/webidls/ExtendableMessageEvent.webidl index d623d4cb898..1976fd77dd8 100644 --- a/components/script/dom/webidls/ExtendableMessageEvent.webidl +++ b/components/script/dom/webidls/ExtendableMessageEvent.webidl @@ -12,7 +12,7 @@ interface ExtendableMessageEvent : ExtendableEvent { readonly attribute DOMString origin; readonly attribute DOMString lastEventId; // [SameObject] readonly attribute (Client or ServiceWorker /*or MessagePort*/)? source; - // readonly attribute FrozenArray? ports; + readonly attribute /*FrozenArray*/any ports; }; dictionary ExtendableMessageEventInit : ExtendableEventInit { 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*/any ports; }; dictionary MessageEventInit : EventInit { @@ -22,5 +22,7 @@ dictionary MessageEventInit : EventInit { //DOMString channel; Window? source; //(WindowProxy or MessagePort)? source; - //sequence ports; + sequence 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..c2fd1dbd1f4 --- /dev/null +++ b/components/script/dom/webidls/MessagePort.webidl @@ -0,0 +1,23 @@ +/* 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, sequence transfer /*= []*/); + [Throws] void postMessage(any message, optional PostMessageOptions options = {}); + void start(); + void close(); + + // event handlers + attribute EventHandler onmessage; + attribute EventHandler onmessageerror; +}; + +dictionary PostMessageOptions { + sequence transfer; +}; diff --git a/components/script/dom/webidls/ServiceWorker.webidl b/components/script/dom/webidls/ServiceWorker.webidl index 24a700ab103..60bb6dfc4b9 100644 --- a/components/script/dom/webidls/ServiceWorker.webidl +++ b/components/script/dom/webidls/ServiceWorker.webidl @@ -7,7 +7,8 @@ interface ServiceWorker : EventTarget { readonly attribute USVString scriptURL; readonly attribute ServiceWorkerState state; - [Throws] void postMessage(any message/*, optional sequence transfer = []*/); + [Throws] void postMessage(any message, sequence transfer); + [Throws] void postMessage(any message, optional PostMessageOptions options = {}); // event attribute EventHandler onstatechange; diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index 65944aa3eb1..26edbca5c7a 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -63,9 +63,10 @@ unsigned long requestAnimationFrame(FrameRequestCallback callback); void cancelAnimationFrame(unsigned long handle); - //void postMessage(any message, DOMString targetOrigin, optional sequence transfer); [Throws] - void postMessage(any message, DOMString targetOrigin); + void postMessage(any message, USVString targetOrigin, optional sequence transfer /*= []*/); + [Throws] + void postMessage(any message, optional WindowPostMessageOptions options = {}); // also has obsolete members }; @@ -173,3 +174,8 @@ partial interface Window { [Pref="css.animations.testing.enabled"] readonly attribute unsigned long runningAnimationCount; }; + +dictionary WindowPostMessageOptions { + USVString targetOrigin = "/"; + sequence transfer; +}; diff --git a/components/script/dom/webidls/Worker.webidl b/components/script/dom/webidls/Worker.webidl index 4e55d6fbe00..93df577ec32 100644 --- a/components/script/dom/webidls/Worker.webidl +++ b/components/script/dom/webidls/Worker.webidl @@ -14,8 +14,8 @@ interface Worker : EventTarget { [Throws] constructor(USVString scriptURL, optional WorkerOptions options = {}); void terminate(); - [Throws] void postMessage(any message/*, sequence transfer*/); - // void postMessage(any message, optional PostMessageOptions options); + [Throws] void postMessage(any message, sequence transfer); + [Throws] void postMessage(any message, optional PostMessageOptions options = {}); attribute EventHandler onmessage; attribute EventHandler onmessageerror; }; 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..37b5b5e3b2c 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -13,7 +13,7 @@ use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryLi use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState; use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit; use crate::dom::bindings::codegen::Bindings::WindowBinding::{ - self, FrameRequestCallback, WindowMethods, + self, FrameRequestCallback, WindowMethods, WindowPostMessageOptions, }; use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions}; use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString; @@ -24,7 +24,7 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::str::{DOMString, USVString}; -use crate::dom::bindings::structuredclone::StructuredCloneData; +use crate::dom::bindings::structuredclone; use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::bindings::utils::{GlobalStaticData, WindowProxyHandler}; use crate::dom::bindings::weakref::DOMTracker; @@ -79,13 +79,15 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect}; use euclid::{Point2D, Rect, Scale, Size2D, Vector2D}; use ipc_channel::ipc::{channel, IpcSender}; use ipc_channel::router::ROUTER; +use js::jsapi::Heap; 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::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue}; use media::WindowGLContext; use msg::constellation_msg::PipelineId; use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse}; @@ -104,7 +106,10 @@ use script_layout_interface::rpc::{ use script_layout_interface::{PendingImageState, TrustedNodeAddress}; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::{ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData}; -use script_traits::{ScriptMsg, ScriptToConstellationChan, ScrollState, TimerEvent, TimerEventId}; +use script_traits::{ + ScriptMsg, ScriptToConstellationChan, ScrollState, StructuredSerializedData, TimerEvent, + TimerEventId, +}; use script_traits::{TimerSchedulerMsg, WindowSizeData, WindowSizeType}; use selectors::attr::CaseSensitivity; use servo_geometry::{f32_rect_to_au_rect, MaxRect}; @@ -969,27 +974,57 @@ impl WindowMethods for Window { } // https://html.spec.whatwg.org/multipage/#dom-window-postmessage - fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult { - let source_global = GlobalScope::incumbent().expect("no incumbent global??"); - let source = source_global.as_window(); + fn PostMessage( + &self, + cx: JSContext, + message: HandleValue, + target_origin: USVString, + mut transfer: CustomAutoRooterGuard>>, + ) -> ErrorResult { + let incumbent = GlobalScope::incumbent().expect("no incumbent global?"); + let source = incumbent.as_window(); + let source_origin = source.Document().origin().immutable().clone(); - // Step 3-5. - let origin = match &origin[..] { - "*" => None, - "/" => Some(source.Document().origin().immutable().clone()), - url => match ServoUrl::parse(&url) { - Ok(url) => Some(url.origin().clone()), - Err(_) => return Err(Error::Syntax), - }, - }; + if transfer.is_some() { + let mut rooted = CustomAutoRooter::new(transfer.take().unwrap()); + let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted)); + self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer) + } else { + self.post_message_impl(&target_origin, source_origin, source, cx, message, None) + } + } - // Step 1-2, 6-8. - // TODO(#12717): Should implement the `transfer` argument. - let data = StructuredCloneData::write(*cx, message)?; + /// + fn PostMessage_( + &self, + cx: JSContext, + message: HandleValue, + options: RootedTraceableBox, + ) -> ErrorResult { + let mut rooted = CustomAutoRooter::new( + options + .transfer + .as_ref() + .unwrap_or(&Vec::with_capacity(0)) + .iter() + .map(|js: &RootedTraceableBox>| js.get()) + .collect(), + ); + let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted)); - // Step 9. - self.post_message(origin, &*source.window_proxy(), data); - Ok(()) + let incumbent = GlobalScope::incumbent().expect("no incumbent global?"); + let source = incumbent.as_window(); + + let source_origin = source.Document().origin().immutable().clone(); + + self.post_message_impl( + &options.targetOrigin, + source_origin, + source, + cx, + message, + transfer, + ) } // https://html.spec.whatwg.org/multipage/#dom-window-captureevents @@ -1287,6 +1322,34 @@ impl WindowMethods for Window { } impl Window { + /// https://html.spec.whatwg.org/multipage/#window-post-message-steps + fn post_message_impl( + &self, + target_origin: &USVString, + source_origin: ImmutableOrigin, + source: &Window, + cx: JSContext, + message: HandleValue, + transfer: Option>>, + ) -> ErrorResult { + // Step 1-2, 6-8. + let data = structuredclone::write(cx, message, transfer)?; + + // Step 3-5. + let target_origin = match target_origin.0[..].as_ref() { + "*" => None, + "/" => Some(source_origin.clone()), + url => match ServoUrl::parse(&url) { + Ok(url) => Some(url.origin().clone()), + Err(_) => return Err(Error::Syntax), + }, + }; + + // Step 9. + self.post_message(target_origin, source_origin, &*source.window_proxy(), data); + Ok(()) + } + // https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet pub fn paint_worklet(&self) -> DomRoot { self.paint_worklet.or_init(|| self.new_paint_worklet()) @@ -1333,6 +1396,9 @@ impl Window { // thread, informing it that it can safely free the memory. self.Document().upcast::().teardown(); + // Tell the constellation to drop the sender to our message-port router, if there is any. + self.upcast::().remove_message_ports_router(); + // Clean up any active promises // https://github.com/servo/servo/issues/15318 if let Some(custom_elements) = self.custom_element_registry.get() { @@ -2334,18 +2400,20 @@ impl Window { pub fn post_message( &self, target_origin: Option, + source_origin: ImmutableOrigin, source: &WindowProxy, - serialize_with_transfer_result: StructuredCloneData, + data: StructuredSerializedData, ) { let this = Trusted::new(self); let source = Trusted::new(source); 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,23 +2423,23 @@ 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( - this.upcast(), - message_clone.handle_mut(), - ); - - // Step 7.6. - // TODO: MessagePort array. - - // Step 7.7. - // TODO(#12719): Set the other attributes. - MessageEvent::dispatch_jsval( - this.upcast(), - this.upcast(), - message_clone.handle(), - None, - Some(&*source), - ); + if let Ok(ports) = structuredclone::read(this.upcast(), data, message_clone.handle_mut()) { + // Step 7.6, 7.7 + MessageEvent::dispatch_jsval( + this.upcast(), + this.upcast(), + message_clone.handle(), + Some(&source_origin.ascii_serialization()), + Some(&*source), + ports, + ); + } else { + // Step 4, fire messageerror. + MessageEvent::dispatch_error( + this.upcast(), + this.upcast(), + ); + } }); // FIXME(nox): Why are errors silenced here? // TODO(#12718): Use the "posted message task source". diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index 1ae17571fbb..3b9830f8ded 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -5,6 +5,7 @@ use crate::compartments::enter_realm; use crate::dom::abstractworker::SimpleWorkerErrorHandler; use crate::dom::abstractworker::WorkerScriptMsg; +use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions; use crate::dom::bindings::codegen::Bindings::WorkerBinding; use crate::dom::bindings::codegen::Bindings::WorkerBinding::{WorkerMethods, WorkerOptions}; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; @@ -13,7 +14,8 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::USVString; -use crate::dom::bindings::structuredclone::StructuredCloneData; +use crate::dom::bindings::structuredclone; +use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::dedicatedworkerglobalscope::{ DedicatedWorkerGlobalScope, DedicatedWorkerScriptMsg, }; @@ -27,10 +29,10 @@ use crossbeam_channel::{unbounded, Sender}; use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg}; use dom_struct::dom_struct; use ipc_channel::ipc; -use js::jsapi::JS_RequestInterruptCallback; +use js::jsapi::{Heap, JSObject, JS_RequestInterruptCallback}; use js::jsval::UndefinedValue; -use js::rust::HandleValue; -use script_traits::WorkerScriptLoadOrigin; +use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue}; +use script_traits::{StructuredSerializedData, WorkerScriptLoadOrigin}; use std::cell::Cell; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -137,7 +139,11 @@ impl Worker { self.terminated.get() } - pub fn handle_message(address: TrustedWorkerAddress, data: StructuredCloneData) { + pub fn handle_message( + address: TrustedWorkerAddress, + origin: String, + data: StructuredSerializedData, + ) { let worker = address.root(); if worker.is_terminated() { @@ -148,30 +154,79 @@ 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); + if let Ok(ports) = structuredclone::read(&global, data, message.handle_mut()) { + MessageEvent::dispatch_jsval( + target, + &global, + message.handle(), + Some(&origin), + None, + ports, + ); + } else { + // Step 4 of the "port post message steps" of the implicit messageport, fire messageerror. + MessageEvent::dispatch_error(target, &global); + } } pub fn dispatch_simple_error(address: TrustedWorkerAddress) { let worker = address.root(); worker.upcast().fire_event(atom!("error")); } -} -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)?; + /// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage + fn post_message_impl( + &self, + cx: JSContext, + message: HandleValue, + transfer: CustomAutoRooterGuard>, + ) -> ErrorResult { + let data = structuredclone::write(cx, message, Some(transfer))?; 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().clone(), + data, + }, )); Ok(()) } +} + +impl WorkerMethods for Worker { + /// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage + fn PostMessage( + &self, + cx: JSContext, + message: HandleValue, + transfer: CustomAutoRooterGuard>, + ) -> ErrorResult { + self.post_message_impl(cx, message, transfer) + } + + /// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage + fn PostMessage_( + &self, + cx: JSContext, + message: HandleValue, + options: RootedTraceableBox, + ) -> ErrorResult { + let mut rooted = CustomAutoRooter::new( + options + .transfer + .as_ref() + .unwrap_or(&Vec::with_capacity(0)) + .iter() + .map(|js: &RootedTraceableBox>| js.get()) + .collect(), + ); + let guard = CustomAutoRooterGuard::new(*cx, &mut rooted); + self.post_message_impl(cx, message, guard) + } #[allow(unsafe_code)] // https://html.spec.whatwg.org/multipage/#terminate-a-worker 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..6a7a69651e7 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -38,7 +38,6 @@ use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::ThreadLocalStackRoots; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection}; use crate::dom::bindings::str::DOMString; -use crate::dom::bindings::structuredclone::StructuredCloneData; use crate::dom::bindings::trace::JSTraceable; use crate::dom::bindings::utils::WRAP_CALLBACKS; use crate::dom::customelementregistry::{ @@ -81,6 +80,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; @@ -132,6 +132,7 @@ use script_traits::CompositorEvent::{ CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent, WheelEvent, }; +use script_traits::StructuredSerializedData; use script_traits::{CompositorEvent, ConstellationControlMsg}; use script_traits::{ DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement, @@ -566,6 +567,8 @@ pub struct ScriptThread { performance_timeline_task_sender: Box, + port_message_sender: Box, + remote_event_task_sender: Box, /// 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(), @@ -1585,6 +1589,11 @@ impl ScriptThread { continue; } let window = document.window(); + + window + .upcast::() + .perform_a_message_port_garbage_collection_checkpoint(); + let pending_reflows = window.get_pending_reflow_count(); if pending_reflows > 0 { window.reflow(ReflowGoal::Full, ReflowReason::ImageLoaded); @@ -1656,6 +1665,7 @@ impl ScriptThread { ScriptThreadEventCategory::PerformanceTimelineTask => { ScriptHangAnnotation::PerformanceTimelineTask }, + ScriptThreadEventCategory::PortMessage => ScriptHangAnnotation::PortMessage, }; self.background_hang_monitor .notify_activity(HangAnnotation::Script(hang_annotation)); @@ -1756,6 +1766,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, @@ -1861,12 +1872,14 @@ impl ScriptThread { source: source_pipeline_id, source_browsing_context, target_origin: origin, + source_origin, data, } => self.handle_post_message_msg( target_pipeline_id, source_pipeline_id, source_browsing_context, origin, + source_origin, data, ), ConstellationControlMsg::UpdatePipelineId( @@ -2519,7 +2532,8 @@ impl ScriptThread { source_pipeline_id: PipelineId, source_browsing_context: TopLevelBrowsingContextId, origin: Option, - data: Vec, + source_origin: ImmutableOrigin, + data: StructuredSerializedData, ) { match { self.documents.borrow().find_window(pipeline_id) } { None => return warn!("postMessage after target pipeline {} closed.", pipeline_id), @@ -2541,7 +2555,7 @@ impl ScriptThread { Some(source) => source, }; // FIXME(#22512): enqueues a task; unnecessary delay. - window.post_message(origin, &*source, StructuredCloneData::Vector(data)) + window.post_message(origin, source_origin, &*source, data) }, } } @@ -2780,6 +2794,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 +3195,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..109628ae94e 100644 --- a/components/script/serviceworker_manager.rs +++ b/components/script/serviceworker_manager.rs @@ -8,7 +8,6 @@ //! active_workers map use crate::dom::abstractworker::WorkerScriptMsg; -use crate::dom::bindings::structuredclone::StructuredCloneData; use crate::dom::serviceworkerglobalscope::{ServiceWorkerGlobalScope, ServiceWorkerScriptMsg}; use crate::dom::serviceworkerregistration::longest_prefix_match; use crossbeam_channel::{unbounded, Receiver, RecvError, Sender}; @@ -135,10 +134,9 @@ impl ServiceWorkerManager { } fn forward_message(&self, msg: DOMMessage, sender: &Sender) { - let DOMMessage(data) = msg; - let data = StructuredCloneData::Vector(data); + let DOMMessage { origin, data } = msg; 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, @@ -136,6 +141,14 @@ impl TaskManager { PerformanceTimeline ); + task_source_functions!( + self, + port_message_queue_with_canceller, + port_message_queue, + PortMessageQueue, + PortMessage + ); + task_source_functions!( self, remote_event_task_source_with_canceller, 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..e9d1766f521 --- /dev/null +++ b/components/script/task_source/port_message.rs @@ -0,0 +1,41 @@ +/* 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, 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(&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/lib.rs b/components/script_traits/lib.rs index 1aa37e56533..4b89bca6fe1 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -17,8 +17,10 @@ extern crate malloc_size_of_derive; extern crate serde; mod script_msg; +pub mod transferable; pub mod webdriver_msg; +use crate::transferable::MessagePortImpl; use crate::webdriver_msg::{LoadStatus, WebDriverScriptCommand}; use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLPipeline; @@ -36,7 +38,7 @@ use keyboard_types::{CompositionEvent, KeyboardEvent}; use libc::c_void; use media::WindowGLContext; use msg::constellation_msg::BackgroundHangMonitorRegister; -use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, HistoryStateId, MessagePortId, PipelineId}; use msg::constellation_msg::{PipelineNamespaceId, TopLevelBrowsingContextId, TraversalDirection}; use net_traits::image::base::Image; use net_traits::image_cache::ImageCache; @@ -51,7 +53,7 @@ use servo_atoms::Atom; use servo_url::ImmutableOrigin; use servo_url::ServoUrl; use std::borrow::Cow; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::fmt; use std::sync::atomic::AtomicBool; use std::sync::Arc; @@ -324,8 +326,11 @@ pub enum ConstellationControlMsg { source_browsing_context: TopLevelBrowsingContextId, /// The expected origin of the target. target_origin: Option, + /// The source origin of the message. + /// https://html.spec.whatwg.org/multipage/#dom-messageevent-origin + source_origin: ImmutableOrigin, /// The data to be posted. - data: Vec, + data: StructuredSerializedData, }, /// Updates the current pipeline ID of a given iframe. /// First PipelineId is for the parent, second is the new PipelineId for the frame. @@ -1013,3 +1018,33 @@ impl ScriptToConstellationChan { self.sender.send((self.pipeline_id, msg)) } } + +/// A data-holder for serialized data and transferred objects. +/// +#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +pub struct StructuredSerializedData { + /// Data serialized by SpiderMonkey. + pub serialized: Vec, + /// Transferred objects. + pub ports: Option>, +} + +/// A task on the https://html.spec.whatwg.org/multipage/#port-message-queue +#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +pub struct PortMessageTask { + /// The origin of this task. + pub origin: ImmutableOrigin, + /// A data-holder for serialized data and transferred objects. + pub data: StructuredSerializedData, +} + +/// Messages for communication between the constellation and a global managing ports. +#[derive(Debug, Deserialize, Serialize)] +pub enum MessagePortMsg { + /// Enables a port to catch-up on messages that were sent while the transfer was ongoing. + CompleteTransfer(MessagePortId, VecDeque), + /// Remove a port, the entangled one doesn't exists anymore. + RemoveMessagePort(MessagePortId), + /// Handle a new port-message-task. + NewTask(MessagePortId, PortMessageTask), +} diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index c3ee8d75205..3a4d1618684 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -8,6 +8,9 @@ use crate::DocumentState; use crate::IFrameLoadInfoWithData; use crate::LayoutControlMsg; use crate::LoadData; +use crate::MessagePortMsg; +use crate::PortMessageTask; +use crate::StructuredSerializedData; use crate::WindowSizeType; use crate::WorkerGlobalScopeInit; use crate::WorkerScriptLoadOrigin; @@ -18,7 +21,9 @@ use euclid::default::Size2D as UntypedSize2D; use euclid::Size2D; use gfx_traits::Epoch; use ipc_channel::ipc::{IpcReceiver, IpcSender}; -use msg::constellation_msg::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId}; +use msg::constellation_msg::{ + BrowsingContextId, MessagePortId, MessagePortRouterId, PipelineId, TopLevelBrowsingContextId, +}; use msg::constellation_msg::{HistoryStateId, TraversalDirection}; use net_traits::request::RequestBuilder; use net_traits::storage_thread::StorageType; @@ -109,6 +114,20 @@ pub enum HistoryEntryReplacement { /// Messages from the script to the constellation. #[derive(Deserialize, Serialize)] pub enum ScriptMsg { + /// A new message-port was created or transferred, with corresponding control-sender. + NewMessagePort(MessagePortRouterId, MessagePortId), + /// A global has started managing message-ports + NewMessagePortRouter(MessagePortRouterId, IpcSender), + /// A global has stopped managing message-ports + RemoveMessagePortRouter(MessagePortRouterId), + /// A task requires re-routing to an already shipped message-port. + RerouteMessagePort(MessagePortId, PortMessageTask), + /// A message-port was shipped, let the entangled port know. + MessagePortShipped(MessagePortId), + /// A message-port has been discarded by script. + RemoveMessagePort(MessagePortId), + /// Entangle two message-ports. + EntanglePorts(MessagePortId, MessagePortId), /// Forward a message to the embedder. ForwardToEmbedder(EmbedderMsg), /// Requests are sent to constellation and fetches are checked manually @@ -166,8 +185,11 @@ pub enum ScriptMsg { source: PipelineId, /// The expected origin of the target. target_origin: Option, + /// The source origin of the message. + /// https://html.spec.whatwg.org/multipage/#dom-messageevent-origin + source_origin: ImmutableOrigin, /// The data to be posted. - data: Vec, + data: StructuredSerializedData, }, /// Inform the constellation that a fragment was navigated to and whether or not it was a replacement navigation. NavigatedToFragment(ServoUrl, HistoryEntryReplacement), @@ -226,6 +248,13 @@ impl fmt::Debug for ScriptMsg { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { use self::ScriptMsg::*; let variant = match *self { + NewMessagePortRouter(..) => "NewMessagePortRouter", + RemoveMessagePortRouter(..) => "RemoveMessagePortRouter", + NewMessagePort(..) => "NewMessagePort", + RerouteMessagePort(..) => "RerouteMessagePort", + RemoveMessagePort(..) => "RemoveMessagePort", + MessagePortShipped(..) => "MessagePortShipped", + EntanglePorts(..) => "EntanglePorts", ForwardToEmbedder(..) => "ForwardToEmbedder", InitiateNavigateRequest(..) => "InitiateNavigateRequest", BroadcastStorageEvent(..) => "BroadcastStorageEvent", @@ -283,8 +312,13 @@ pub struct ScopeThings { } /// Message that gets passed to service worker scope on postMessage -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct DOMMessage(pub Vec); +#[derive(Debug, Deserialize, Serialize)] +pub struct DOMMessage { + /// The origin of the message + pub origin: ImmutableOrigin, + /// The payload of the message + pub data: StructuredSerializedData, +} /// Channels to allow service worker manager to communicate with constellation and resource thread pub struct SWManagerSenders { diff --git a/components/script_traits/transferable.rs b/components/script_traits/transferable.rs new file mode 100644 index 00000000000..868654f2c2b --- /dev/null +++ b/components/script_traits/transferable.rs @@ -0,0 +1,160 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +//! This module contains implementations in script that are transferable. +//! The implementations are here instead of in script +//! so that the other modules involved in the transfer don't have +//! to depend on script. + +use crate::PortMessageTask; +use msg::constellation_msg::MessagePortId; +use std::collections::VecDeque; + +#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +enum MessagePortState { + /// + Detached, + /// + /// The message-queue of this port is enabled, + /// the boolean represents awaiting completion of a transfer. + Enabled(bool), + /// + /// The message-queue of this port is disabled, + /// the boolean represents awaiting completion of a transfer. + Disabled(bool), +} + +#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +/// The data and logic backing the DOM managed MessagePort. +pub struct MessagePortImpl { + /// The current state of the port. + state: MessagePortState, + + /// + entangled_port: Option, + + /// + message_buffer: Option>, + + /// The UUID of this port. + message_port_id: MessagePortId, +} + +impl MessagePortImpl { + /// Create a new messageport impl. + pub fn new(port_id: MessagePortId) -> MessagePortImpl { + MessagePortImpl { + state: MessagePortState::Disabled(false), + entangled_port: None, + message_buffer: None, + message_port_id: port_id, + } + } + + /// Get the Id. + pub fn message_port_id(&self) -> &MessagePortId { + &self.message_port_id + } + + /// Maybe get the Id of the entangled port. + pub fn entangled_port_id(&self) -> Option { + self.entangled_port.clone() + } + + /// Entanged this port with another. + pub fn entangle(&mut self, other_id: MessagePortId) { + self.entangled_port = Some(other_id); + } + + /// Is this port enabled? + pub fn enabled(&self) -> bool { + match self.state { + MessagePortState::Enabled(_) => true, + _ => false, + } + } + + /// Mark this port as having been shipped. + /// + pub fn set_has_been_shipped(&mut self) { + match self.state { + MessagePortState::Detached => { + panic!("Messageport set_has_been_shipped called in detached state") + }, + MessagePortState::Enabled(_) => self.state = MessagePortState::Enabled(true), + MessagePortState::Disabled(_) => self.state = MessagePortState::Disabled(true), + } + } + + /// Handle the completion of the transfer, + /// this is data received from the constellation. + pub fn complete_transfer(&mut self, mut tasks: VecDeque) { + match self.state { + MessagePortState::Detached => return, + MessagePortState::Enabled(_) => self.state = MessagePortState::Enabled(false), + MessagePortState::Disabled(_) => self.state = MessagePortState::Disabled(false), + } + + // Note: these are the tasks that were buffered while the transfer was ongoing, + // hence they need to execute first. + // The global will call `start` if we are enabled, + // which will add tasks on the event-loop to dispatch incoming messages. + match self.message_buffer { + Some(ref mut incoming_buffer) => { + while let Some(task) = tasks.pop_back() { + incoming_buffer.push_front(task); + } + }, + None => self.message_buffer = Some(tasks), + } + } + + /// A message was received from our entangled port, + /// returns an optional task to be dispatched. + pub fn handle_incoming(&mut self, task: PortMessageTask) -> Option { + let should_dispatch = match self.state { + MessagePortState::Detached => return None, + MessagePortState::Enabled(in_transfer) => !in_transfer, + MessagePortState::Disabled(_) => false, + }; + + if should_dispatch { + Some(task) + } else { + match self.message_buffer { + Some(ref mut buffer) => { + buffer.push_back(task); + }, + None => { + let mut queue = VecDeque::new(); + queue.push_back(task); + self.message_buffer = Some(queue); + }, + } + None + } + } + + /// + /// returns an optional queue of tasks that were buffered while the port was disabled. + pub fn start(&mut self) -> Option> { + match self.state { + MessagePortState::Detached => return None, + MessagePortState::Enabled(_) => {}, + MessagePortState::Disabled(in_transfer) => { + self.state = MessagePortState::Enabled(in_transfer); + }, + } + if let MessagePortState::Enabled(true) = self.state { + return None; + } + self.message_buffer.take() + } + + /// + pub fn close(&mut self) { + // Step 1 + self.state = MessagePortState::Detached; + } +} 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] diff --git a/tests/wpt/metadata/FileAPI/blob/Blob-constructor.html.ini b/tests/wpt/metadata/FileAPI/blob/Blob-constructor.html.ini index e4c22fcc206..bf69cc8679a 100644 --- a/tests/wpt/metadata/FileAPI/blob/Blob-constructor.html.ini +++ b/tests/wpt/metadata/FileAPI/blob/Blob-constructor.html.ini @@ -1,7 +1,4 @@ [Blob-constructor.html] - [Passing a FrozenArray as the blobParts array should work (FrozenArray).] - expected: FAIL - [options properties should be accessed in lexicographic order.] expected: FAIL diff --git a/tests/wpt/metadata/FileAPI/url/unicode-origin.sub.html.ini b/tests/wpt/metadata/FileAPI/url/unicode-origin.sub.html.ini deleted file mode 100644 index 05e435db569..00000000000 --- a/tests/wpt/metadata/FileAPI/url/unicode-origin.sub.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[unicode-origin.sub.html] - [Verify serialization of non-ascii origin in Blob URLs] - expected: FAIL - diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index 91a7c5e8f8f..79cb696d595 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -423442,6 +423442,49 @@ {} ] ], + "webmessaging/Channel_postMessage_transfer_xsite_incoming_messages.window.js": [ + [ + "webmessaging/Channel_postMessage_transfer_xsite_incoming_messages.window.html", + { + "script_metadata": [ + [ + "script", + "/common/get-host-info.sub.js" + ] + ] + } + ] + ], + "webmessaging/Channel_postMessage_with_transfer_entangled.any.js": [ + [ + "webmessaging/Channel_postMessage_with_transfer_entangled.any.html", + {} + ], + [ + "webmessaging/Channel_postMessage_with_transfer_entangled.any.worker.html", + {} + ] + ], + "webmessaging/Channel_postMessage_with_transfer_incoming_messages.any.js": [ + [ + "webmessaging/Channel_postMessage_with_transfer_incoming_messages.any.html", + {} + ], + [ + "webmessaging/Channel_postMessage_with_transfer_incoming_messages.any.worker.html", + {} + ] + ], + "webmessaging/Channel_postMessage_with_transfer_outgoing_messages.any.js": [ + [ + "webmessaging/Channel_postMessage_with_transfer_outgoing_messages.any.html", + {} + ], + [ + "webmessaging/Channel_postMessage_with_transfer_outgoing_messages.any.worker.html", + {} + ] + ], "webmessaging/MessageEvent-trusted.html": [ [ "webmessaging/MessageEvent-trusted.html", @@ -423646,6 +423689,19 @@ {} ] ], + "webmessaging/postMessage_MessagePorts_xsite.sub.window.js": [ + [ + "webmessaging/postMessage_MessagePorts_xsite.sub.window.html", + { + "script_metadata": [ + [ + "script", + "/common/get-host-info.sub.js" + ] + ] + } + ] + ], "webmessaging/postMessage_arrays.sub.htm": [ [ "webmessaging/postMessage_arrays.sub.htm", @@ -714533,6 +714589,22 @@ "4b00e68d49ef9fc85e92a5526ff76bd92259d4d9", "testharness" ], + "webmessaging/Channel_postMessage_transfer_xsite_incoming_messages.window.js": [ + "23237ae1555e67dafcb170ce8fccab4c966f7a2e", + "testharness" + ], + "webmessaging/Channel_postMessage_with_transfer_entangled.any.js": [ + "2226b278440346290066e622fe6abbaab2b93bd2", + "testharness" + ], + "webmessaging/Channel_postMessage_with_transfer_incoming_messages.any.js": [ + "fe2e96220d34d88127dc596eee70d183f7debd18", + "testharness" + ], + "webmessaging/Channel_postMessage_with_transfer_outgoing_messages.any.js": [ + "aa80b7589cffa58da6f1d790e7cdbeb95642c6e3", + "testharness" + ], "webmessaging/META.yml": [ "95d5071171b5a20cc14a414c97c9eae2f525f43f", "support" @@ -714701,6 +714773,10 @@ "cf2b8eb4c11b0b824a47677079c2d1b8837b3802", "testharness" ], + "webmessaging/postMessage_MessagePorts_xsite.sub.window.js": [ + "ca1e510edaa09f41350426e143923bb15e6df82b", + "testharness" + ], "webmessaging/postMessage_arrays.sub.htm": [ "41e4a75eda616196ab3546943a0913785cbe69be", "testharness" diff --git a/tests/wpt/metadata/WebIDL/ecmascript-binding/no-regexp-special-casing.any.js.ini b/tests/wpt/metadata/WebIDL/ecmascript-binding/no-regexp-special-casing.any.js.ini index f0c4f90e17b..684503e7d28 100644 --- a/tests/wpt/metadata/WebIDL/ecmascript-binding/no-regexp-special-casing.any.js.ini +++ b/tests/wpt/metadata/WebIDL/ecmascript-binding/no-regexp-special-casing.any.js.ini @@ -5,8 +5,6 @@ [no-regexp-special-casing] expected: FAIL - [Conversion to a sequence works] - expected: FAIL [no-regexp-special-casing.any.html] [Untitled] @@ -15,6 +13,3 @@ [no-regexp-special-casing] expected: FAIL - [Conversion to a sequence works] - expected: FAIL - diff --git a/tests/wpt/metadata/cors/remote-origin.htm.ini b/tests/wpt/metadata/cors/remote-origin.htm.ini index 71490850fc9..2ad7a6a8b60 100644 --- a/tests/wpt/metadata/cors/remote-origin.htm.ini +++ b/tests/wpt/metadata/cors/remote-origin.htm.ini @@ -1,138 +1,17 @@ [remote-origin.htm] bug: https://github.com/servo/servo/issues/21563 - expected: TIMEOUT - [Allow origin: *] - expected: TIMEOUT - [Allow origin: _*__] - expected: TIMEOUT - - [Allow origin: [tab\]*] - expected: TIMEOUT - - [Allow origin: http://www1.web-platform.test:8000] - expected: TIMEOUT - - [Allow origin: _http://www1.web-platform.test:8000] - expected: TIMEOUT + expected: FAIL [Allow origin: _http://www1.web-platform.test:8000___[tab\]_] - expected: TIMEOUT - - [Allow origin: [tab\]http://www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: http://web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: //www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: ://www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: ftp://www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: http:://www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: http:/www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: http:www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: http://www1.web-platform.test:8000?] - expected: TIMEOUT + expected: FAIL [Disallow origin: http://www1.web-platform.test:8000/] - expected: TIMEOUT - - [Disallow origin: http://www1.web-platform.test:8000_/] - expected: TIMEOUT + expected: FAIL [Disallow origin: http://www1.web-platform.test:8000#] - expected: TIMEOUT - - [Disallow origin: http://www1.web-platform.test:8000%23] - expected: TIMEOUT - - [Disallow origin: http://www1.web-platform.test:8000:80] - expected: TIMEOUT - - [Disallow origin: http://www1.web-platform.test:8000,_*] - expected: TIMEOUT - - [Disallow origin: http://www1.web-platform.test:8000\\0] - expected: TIMEOUT - - [Disallow origin: HTTP://WWW1.WEB-PLATFORM.TEST:8000] - expected: TIMEOUT + expected: FAIL [Disallow origin: HTTP://www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: http://WWW1.WEB-PLATFORM.TEST:8000] - expected: TIMEOUT - - [Disallow origin: -] - expected: TIMEOUT - - [Disallow origin: **] - expected: TIMEOUT - - [Disallow origin: \\0*] - expected: TIMEOUT - - [Disallow origin: *\\0] - expected: TIMEOUT - - [Disallow origin: '*'] - expected: TIMEOUT - - [Disallow origin: "*"] - expected: TIMEOUT - - [Disallow origin: *_*] - expected: TIMEOUT - - [Disallow origin: *http://*] - expected: TIMEOUT - - [Disallow origin: *http://www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: *_http://www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: *,_http://www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: \\0http://www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: null_http://www1.web-platform.test:8000] - expected: TIMEOUT - - [Disallow origin: http://example.net] - expected: TIMEOUT - - [Disallow origin: null] - expected: TIMEOUT - - [Disallow origin: ] - expected: TIMEOUT - - [Disallow origin: http://web-platform.test:8000/cors/remote-origin.htm] - expected: TIMEOUT - - [Disallow origin: http://web-platform.test:8000/cors/] - expected: TIMEOUT - - [Disallow origin: http://www1.web-platform.test:8000/cors/] - expected: TIMEOUT + expected: FAIL diff --git a/tests/wpt/metadata/css/geometry/structured-serialization.html.ini b/tests/wpt/metadata/css/geometry/structured-serialization.html.ini index 93196fea05c..899588de48f 100644 --- a/tests/wpt/metadata/css/geometry/structured-serialization.html.ini +++ b/tests/wpt/metadata/css/geometry/structured-serialization.html.ini @@ -89,6 +89,3 @@ [DOMMatrix clone: non-initial values (3d)] expected: FAIL - [DOMRectList clone] - expected: FAIL - diff --git a/tests/wpt/metadata/dom/events/EventListener-incumbent-global-1.sub.html.ini b/tests/wpt/metadata/dom/events/EventListener-incumbent-global-1.sub.html.ini index 6ccaa38b742..fc39772debe 100644 --- a/tests/wpt/metadata/dom/events/EventListener-incumbent-global-1.sub.html.ini +++ b/tests/wpt/metadata/dom/events/EventListener-incumbent-global-1.sub.html.ini @@ -1,6 +1,3 @@ [EventListener-incumbent-global-1.sub.html] type: testharness bug: https://github.com/servo/servo/issues/12715 - [Check the incumbent global EventListeners are called with] - expected: FAIL - diff --git a/tests/wpt/metadata/dom/events/EventListener-incumbent-global-2.sub.html.ini b/tests/wpt/metadata/dom/events/EventListener-incumbent-global-2.sub.html.ini deleted file mode 100644 index b5941dc2e24..00000000000 --- a/tests/wpt/metadata/dom/events/EventListener-incumbent-global-2.sub.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[EventListener-incumbent-global-2.sub.html] - type: testharness - bug: https://github.com/servo/servo/issues/12715 - [Check the incumbent global EventListeners are called with] - expected: FAIL - diff --git a/tests/wpt/metadata/fetch/security/embedded-credentials.tentative.sub.html.ini b/tests/wpt/metadata/fetch/security/embedded-credentials.tentative.sub.html.ini index 35196e2d082..f5fd1479f84 100644 --- a/tests/wpt/metadata/fetch/security/embedded-credentials.tentative.sub.html.ini +++ b/tests/wpt/metadata/fetch/security/embedded-credentials.tentative.sub.html.ini @@ -1,5 +1,6 @@ [embedded-credentials.tentative.sub.html] type: testharness + expected: TIMEOUT [Embedded credentials are treated as network errors.] expected: FAIL @@ -7,13 +8,13 @@ expected: FAIL [Embedded credentials are treated as network errors in new windows.] - expected: FAIL + expected: TIMEOUT [Embedded credentials matching the top-level are not treated as network errors for relative URLs.] - expected: FAIL + expected: TIMEOUT [Embedded credentials matching the top-level are not treated as network errors for same-origin URLs.] - expected: FAIL + expected: TIMEOUT [Embedded credentials matching the top-level are treated as network errors for cross-origin URLs.] expected: FAIL diff --git a/tests/wpt/metadata/html/browsers/origin/origin-of-data-document.html.ini b/tests/wpt/metadata/html/browsers/origin/origin-of-data-document.html.ini deleted file mode 100644 index 114eb9738ea..00000000000 --- a/tests/wpt/metadata/html/browsers/origin/origin-of-data-document.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[origin-of-data-document.html] - type: testharness - [The origin of a 'data:' document in a frame is opaque.] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html.ini b/tests/wpt/metadata/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html.ini index 12adf20e2af..8da94f60fc5 100644 --- a/tests/wpt/metadata/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html.ini +++ b/tests/wpt/metadata/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html.ini @@ -1,37 +1,38 @@ [document_domain_access_details.sub.html] + expected: TIMEOUT [Access allowed if same-origin with no 'document.domain' modification. (Sanity check)] - expected: FAIL + expected: TIMEOUT [Access is revoked to Window object when we stop being same effective script origin due to document.domain.] - expected: FAIL + expected: NOTRUN [Access allowed if different-origin but both set document.domain to parent domain.] - expected: FAIL + expected: NOTRUN [Access is not revoked to random object when we stop being same effective script origin due to document.domain.] - expected: FAIL + expected: NOTRUN [Access not allowed if different-origin with no 'document.domain' modification. (Sanity check)] - expected: FAIL + expected: NOTRUN [Access disallowed again if same-origin, both set document-domain to existing value, then one sets to parent.] - expected: FAIL + expected: NOTRUN [Access is revoked to Location object when we stop being same effective script origin due to document.domain.] - expected: FAIL + expected: NOTRUN [Access allowed if same-origin and both set document.domain to existing value.] - expected: FAIL + expected: NOTRUN [Access is not revoked to Document object when we stop being same effective script origin due to document.domain.] - expected: FAIL + expected: NOTRUN [Access disallowed if same-origin but only one sets document.domain.] - expected: FAIL + expected: NOTRUN [Access evolves correctly for cross-origin objects when we join up via document.domain and then diverge again.] - expected: FAIL + expected: NOTRUN [Access evolves correctly for non-cross-origin objects when we join up via document.domain and then diverge again.] - expected: FAIL + expected: NOTRUN diff --git a/tests/wpt/metadata/html/dom/idlharness.https.html.ini b/tests/wpt/metadata/html/dom/idlharness.https.html.ini index a9257cabc65..1117320ad5e 100644 --- a/tests/wpt/metadata/html/dom/idlharness.https.html.ini +++ b/tests/wpt/metadata/html/dom/idlharness.https.html.ini @@ -5,9 +5,6 @@ [ElementInternals interface: operation setValidity(ValidityStateFlags, DOMString, HTMLElement)] expected: FAIL - [MessageChannel interface: existence and properties of interface object] - expected: FAIL - [ValidityState interface: document.createElement("input").validity must inherit property "patternMismatch" with the proper type] expected: FAIL @@ -44,9 +41,6 @@ [ElementInternals interface: existence and properties of interface object] expected: FAIL - [MessagePort interface: attribute onmessage] - expected: FAIL - [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "imageSmoothingQuality" with the proper type] expected: FAIL @@ -161,9 +155,6 @@ [SVGElement interface: attribute onmousemove] expected: FAIL - [MessagePort interface object length] - expected: FAIL - [Navigator interface: window.navigator must inherit property "onLine" with the proper type] expected: FAIL @@ -269,9 +260,6 @@ [SharedWorker interface object name] expected: FAIL - [MessageChannel interface: attribute port1] - expected: FAIL - [SVGSVGElement interface: attribute onbeforeprint] expected: FAIL @@ -290,9 +278,6 @@ [SVGElement interface: attribute tabIndex] expected: FAIL - [MessageEvent interface: new MessageEvent("message", { data: 5 }) must inherit property "ports" with the proper type] - expected: FAIL - [Location interface: window.location must have own property "ancestorOrigins"] expected: FAIL @@ -311,9 +296,6 @@ [ApplicationCache interface: constant UNCACHED on interface object] expected: FAIL - [MessageEvent interface: attribute ports] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: attribute filter] expected: FAIL @@ -377,9 +359,6 @@ [SVGSVGElement interface: attribute onoffline] expected: FAIL - [MessagePort interface object name] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: existence and properties of interface prototype object's @@unscopables property] expected: FAIL @@ -410,9 +389,6 @@ [TextTrack interface: document.createElement("track").track must inherit property "inBandMetadataTrackDispatchType" with the proper type] expected: FAIL - [MessageChannel interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - [ValidityState interface: document.createElement("input").validity must inherit property "tooShort" with the proper type] expected: FAIL @@ -788,9 +764,6 @@ [ElementInternals interface: existence and properties of interface prototype object] expected: FAIL - [MessageChannel interface object length] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: operation commit()] expected: FAIL @@ -812,9 +785,6 @@ [Navigator interface: operation registerProtocolHandler(DOMString, USVString, DOMString)] expected: FAIL - [MessagePort interface: existence and properties of interface object] - expected: FAIL - [DataTransferItemList interface object name] expected: FAIL @@ -833,9 +803,6 @@ [SVGSVGElement interface: attribute onunhandledrejection] expected: FAIL - [MessageChannel interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - [SVGElement interface: attribute onloadedmetadata] expected: FAIL @@ -920,9 +887,6 @@ [CustomElementRegistry interface: operation whenDefined(DOMString)] expected: FAIL - [MessagePort interface: operation postMessage(any, PostMessageOptions)] - expected: FAIL - [DragEvent interface: existence and properties of interface object] expected: FAIL @@ -1010,9 +974,6 @@ [SVGElement interface: attribute ondragexit] expected: FAIL - [MessagePort interface: attribute onmessageerror] - expected: FAIL - [Navigator interface: window.navigator must inherit property "oscpu" with the proper type] expected: FAIL @@ -1040,12 +1001,6 @@ [SharedWorker interface: existence and properties of interface prototype object] expected: FAIL - [MessageChannel interface: existence and properties of interface prototype object] - expected: FAIL - - [MessagePort interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: operation createPattern(CanvasImageSource, DOMString)] expected: FAIL @@ -1205,9 +1160,6 @@ [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "drawFocusIfNeeded(Element)" with the proper type] expected: FAIL - [MessagePort interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: operation closePath()] expected: FAIL @@ -1241,12 +1193,6 @@ [External interface object length] expected: FAIL - [MessagePort interface: operation postMessage(any, [object Object\])] - expected: FAIL - - [MessageChannel interface: attribute port2] - expected: FAIL - [CanvasRenderingContext2D interface: operation getTransform()] expected: FAIL @@ -1259,9 +1205,6 @@ [DataTransfer interface: attribute effectAllowed] expected: FAIL - [MessagePort interface: operation close()] - expected: FAIL - [OffscreenCanvas interface: operation getContext(OffscreenRenderingContextId, any)] expected: FAIL @@ -1313,15 +1256,9 @@ [SVGAElement interface: attribute password] expected: FAIL - [MessagePort interface: existence and properties of interface prototype object] - expected: FAIL - [SharedWorker interface: existence and properties of interface prototype object's "constructor" property] expected: FAIL - [MessageChannel interface object name] - expected: FAIL - [DataTransfer interface object name] expected: FAIL @@ -1355,9 +1292,6 @@ [ImageBitmapRenderingContext interface: existence and properties of interface prototype object's @@unscopables property] expected: FAIL - [MessagePort interface: operation start()] - expected: FAIL - [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "isPointInStroke(unrestricted double, unrestricted double)" with the proper type] expected: FAIL @@ -1552,9 +1486,6 @@ [Window interface: window must inherit property "queueMicrotask(VoidFunction)" with the proper type] expected: FAIL - [Window interface: operation postMessage(any, WindowPostMessageOptions)] - expected: FAIL - [Document interface: calling execCommand(DOMString, boolean, DOMString) on new Document() with too few arguments must throw TypeError] expected: FAIL @@ -1780,9 +1711,6 @@ [Document interface: iframe.contentDocument must inherit property "queryCommandState(DOMString)" with the proper type] expected: FAIL - [Window interface: operation postMessage(any, USVString, [object Object\])] - expected: FAIL - [Window interface: operation queueMicrotask(VoidFunction)] expected: FAIL diff --git a/tests/wpt/metadata/html/dom/idlharness.worker.js.ini b/tests/wpt/metadata/html/dom/idlharness.worker.js.ini index eab12bd98ed..9fa9c825549 100644 --- a/tests/wpt/metadata/html/dom/idlharness.worker.js.ini +++ b/tests/wpt/metadata/html/dom/idlharness.worker.js.ini @@ -14,18 +14,12 @@ [DedicatedWorkerGlobalScope interface: self must inherit property "onmessageerror" with the proper type] expected: FAIL - [MessagePort interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: attribute filter] expected: FAIL [OffscreenCanvasRenderingContext2D interface: operation measureText(DOMString)] expected: FAIL - [MessagePort interface: attribute onmessage] - expected: FAIL - [ImageBitmapRenderingContext interface: existence and properties of interface object] expected: FAIL @@ -65,15 +59,9 @@ [WorkerGlobalScope interface: self must inherit property "onlanguagechange" with the proper type] expected: FAIL - [MessagePort interface: attribute onmessageerror] - expected: FAIL - [WorkerNavigator interface: attribute languages] expected: FAIL - [MessagePort interface object length] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: operation strokeText(DOMString, unrestricted double, unrestricted double, unrestricted double)] expected: FAIL @@ -89,9 +77,6 @@ [ImageBitmapRenderingContext interface: existence and properties of interface prototype object] expected: FAIL - [MessagePort interface: existence and properties of interface prototype object] - expected: FAIL - [Path2D interface: existence and properties of interface object] expected: FAIL @@ -104,9 +89,6 @@ [BroadcastChannel interface: operation close()] expected: FAIL - [MessageChannel interface: existence and properties of interface object] - expected: FAIL - [DedicatedWorkerGlobalScope interface: calling requestAnimationFrame(FrameRequestCallback) on self with too few arguments must throw TypeError] expected: FAIL @@ -116,12 +98,6 @@ [MessageEvent interface: calling initMessageEvent(DOMString, boolean, boolean, any, USVString, DOMString, MessageEventSource, [object Object\]) on new MessageEvent("message", { data: 5 }) with too few arguments must throw TypeError] expected: FAIL - [MessageChannel interface: attribute port1] - expected: FAIL - - [MessageChannel interface: attribute port2] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: attribute lineWidth] expected: FAIL @@ -161,9 +137,6 @@ [Path2D interface: operation moveTo(unrestricted double, unrestricted double)] expected: FAIL - [MessageChannel interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: operation bezierCurveTo(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double)] expected: FAIL @@ -179,9 +152,6 @@ [ImageBitmap interface: existence and properties of interface prototype object's @@unscopables property] expected: FAIL - [MessagePort interface: existence and properties of interface object] - expected: FAIL - [Path2D interface object name] expected: FAIL @@ -329,18 +299,12 @@ [OffscreenCanvasRenderingContext2D interface: attribute miterLimit] expected: FAIL - [MessageChannel interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: operation strokeRect(unrestricted double, unrestricted double, unrestricted double, unrestricted double)] expected: FAIL [OffscreenCanvasRenderingContext2D interface: existence and properties of interface object] expected: FAIL - [MessageChannel interface object length] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: operation commit()] expected: FAIL @@ -350,9 +314,6 @@ [WorkerGlobalScope interface: self must inherit property "onoffline" with the proper type] expected: FAIL - [MessageEvent interface: attribute ports] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: attribute lineDashOffset] expected: FAIL @@ -425,12 +386,6 @@ [Path2D interface: existence and properties of interface prototype object's @@unscopables property] expected: FAIL - [MessageChannel interface: existence and properties of interface prototype object] - expected: FAIL - - [MessagePort interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: operation createPattern(CanvasImageSource, DOMString)] expected: FAIL @@ -479,9 +434,6 @@ [Path2D interface: operation ellipse(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, boolean)] expected: FAIL - [MessageEvent interface: new MessageEvent("message", { data: 5 }) must inherit property "ports" with the proper type] - expected: FAIL - [WorkerGlobalScope interface: attribute ononline] expected: FAIL @@ -524,15 +476,9 @@ [BroadcastChannel interface: operation postMessage(any)] expected: FAIL - [MessageChannel interface object name] - expected: FAIL - [SharedWorker interface: attribute port] expected: FAIL - [MessagePort interface: operation postMessage(any, [object Object\])] - expected: FAIL - [WorkerNavigator interface: self.navigator must not have property "taintEnabled"] expected: FAIL @@ -548,9 +494,6 @@ [SharedWorker interface object name] expected: FAIL - [MessagePort interface object name] - expected: FAIL - [OffscreenCanvas interface: operation getContext(OffscreenRenderingContextId, any)] expected: FAIL @@ -596,15 +539,6 @@ [OffscreenCanvasRenderingContext2D interface: operation clip(Path2D, CanvasFillRule)] expected: FAIL - [MessagePort interface: operation postMessage(any, PostMessageOptions)] - expected: FAIL - - [MessagePort interface: operation start()] - expected: FAIL - - [MessagePort interface: operation close()] - expected: FAIL - [WebSocket interface: new WebSocket("ws://foo") must inherit property "extensions" with the proper type] expected: FAIL diff --git a/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-coop-coep.https.any.js.ini b/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-coop-coep.https.any.js.ini index dcef0b456e4..3b954375b4c 100644 --- a/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-coop-coep.https.any.js.ini +++ b/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-coop-coep.https.any.js.ini @@ -1,18 +1,9 @@ [no-coop-coep.https.any.worker.html] - [SharedArrayBuffer over MessageChannel without COOP+COEP] - expected: FAIL - [SharedArrayBuffer over BroadcastChannel without COOP+COEP] expected: FAIL [no-coop-coep.https.any.html] - [SharedArrayBuffer over MessageChannel without COOP+COEP] - expected: FAIL - - [SharedArrayBuffer over postMessage() without COOP+COEP] - expected: FAIL - [SharedArrayBuffer over BroadcastChannel without COOP+COEP] expected: FAIL diff --git a/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html.ini b/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html.ini deleted file mode 100644 index 043c0d4720d..00000000000 --- a/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html.ini +++ /dev/null @@ -1,10 +0,0 @@ -[no-transferring.https.html] - [Trying to transfer a SharedArrayBuffer to a worker throws] - expected: FAIL - - [Trying to transfer a SharedArrayBuffer through a MessagePort throws] - expected: FAIL - - [Trying to transfer a SharedArrayBuffer to this window throws] - expected: FAIL - diff --git a/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel-success.https.html.ini b/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel-success.https.html.ini index b9e9ec22ccf..1c16e180b86 100644 --- a/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel-success.https.html.ini +++ b/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel-success.https.html.ini @@ -1,5 +1,4 @@ [window-iframe-messagechannel-success.https.html] - expected: TIMEOUT [postMessaging to a same-origin iframe via MessageChannel allows them to see each others' modifications] - expected: TIMEOUT + expected: FAIL diff --git a/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/transfer-errors.window.js.ini b/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/transfer-errors.window.js.ini index 7e93619dd59..657f264899b 100644 --- a/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/transfer-errors.window.js.ini +++ b/tests/wpt/metadata/html/infrastructure/safe-passing-of-structured-data/transfer-errors.window.js.ini @@ -5,27 +5,12 @@ [transfer-errors] expected: FAIL - [Serialize should make the ArrayBuffer detached, so it cannot be transferred again] - expected: FAIL - [Serialize should throw before a detached ArrayBuffer is found] expected: FAIL - [Cannot transfer ArrayBuffer detached while the message was serialized] - expected: FAIL - - [Cannot transfer the same MessagePort twice] - expected: FAIL - - [Serialize should make the MessagePort detached, so it cannot be transferred again] - expected: FAIL - [Serialize should throw before a detached MessagePort is found] expected: FAIL - [Cannot transfer MessagePort detached while the message was serialized] - expected: FAIL - [Cannot transfer the same ImageBitmap twice] expected: FAIL diff --git a/tests/wpt/metadata/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.js.ini b/tests/wpt/metadata/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.js.ini index 9d210fefcfc..95d0f622ec2 100644 --- a/tests/wpt/metadata/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.js.ini +++ b/tests/wpt/metadata/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.js.ini @@ -1,11 +1,5 @@ [tasks.window.html] expected: TIMEOUT - [document.open() and tasks (MessagePort)] - expected: FAIL - - [tasks without document.open() (MessagePort)] - expected: FAIL - [document.open() and tasks (canvas.toBlob())] expected: FAIL diff --git a/tests/wpt/metadata/html/webappapis/scripting/events/messageevent-constructor.https.html.ini b/tests/wpt/metadata/html/webappapis/scripting/events/messageevent-constructor.https.html.ini index a6931ba3609..ab8ee35278d 100644 --- a/tests/wpt/metadata/html/webappapis/scripting/events/messageevent-constructor.https.html.ini +++ b/tests/wpt/metadata/html/webappapis/scripting/events/messageevent-constructor.https.html.ini @@ -3,12 +3,6 @@ [Default event values] expected: FAIL - [MessageEventInit dictionary] - expected: FAIL - - [Passing null for ports member] - expected: FAIL - [ports attribute should be a FrozenArray] expected: FAIL diff --git a/tests/wpt/metadata/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html.ini b/tests/wpt/metadata/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html.ini index 8c566a35035..59547f3ead4 100644 --- a/tests/wpt/metadata/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html.ini +++ b/tests/wpt/metadata/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html.ini @@ -1,20 +1,11 @@ [promise-rejection-events.dedicatedworker.html] type: testharness - [unhandledrejection: from a task-delayed rejection] - expected: FAIL - - [no unhandledrejection/rejectionhandled: all inside a queued task, a rejection handler attached synchronously to a promise created from returning a Promise.reject-created promise in a fulfillment handler] - expected: FAIL - [microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, all inside a postMessageTask] expected: FAIL [microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, all inside a postMessageTask] expected: FAIL - [delayed handling: a nested-task delay before attaching a handler causes unhandledrejection] - expected: FAIL - [delayed handling: a nested-postMessageTask after promise creation/rejection, plus promise microtasks, is too late to attach a rejection handler] expected: FAIL @@ -33,18 +24,3 @@ [no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from createImageBitmap] expected: FAIL - [microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, all inside a queueTask] - expected: FAIL - - [microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, all inside a queueTask] - expected: FAIL - - [delayed handling: a nested-queueTask after promise creation/rejection, plus promise microtasks, is too late to attach a rejection handler] - expected: FAIL - - [delayed handling: a nested-queueTask before promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler] - expected: FAIL - - [delayed handling: a nested-queueTask after promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler] - expected: FAIL - diff --git a/tests/wpt/metadata/js/builtins/Promise-incumbent-global.sub.html.ini b/tests/wpt/metadata/js/builtins/Promise-incumbent-global.sub.html.ini deleted file mode 100644 index 4168b4d620a..00000000000 --- a/tests/wpt/metadata/js/builtins/Promise-incumbent-global.sub.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[Promise-incumbent-global.sub.html] - type: testharness - [Check the incumbent global Promise callbacks are called with] - expected: FAIL - diff --git a/tests/wpt/metadata/wasm/serialization/module/no-transferring.html.ini b/tests/wpt/metadata/wasm/serialization/module/no-transferring.html.ini deleted file mode 100644 index af448eaecf2..00000000000 --- a/tests/wpt/metadata/wasm/serialization/module/no-transferring.html.ini +++ /dev/null @@ -1,10 +0,0 @@ -[no-transferring.html] - [Trying to transfer a WebAssembly.Module to a worker throws] - expected: FAIL - - [Trying to transfer a WebAssembly.Module through a MessagePort throws] - expected: FAIL - - [Trying to transfer a WebAssembly.Module to this window throws] - expected: FAIL - diff --git a/tests/wpt/metadata/webmessaging/MessageEvent-trusted.html.ini b/tests/wpt/metadata/webmessaging/MessageEvent-trusted.html.ini new file mode 100644 index 00000000000..d0bc235ec99 --- /dev/null +++ b/tests/wpt/metadata/webmessaging/MessageEvent-trusted.html.ini @@ -0,0 +1,4 @@ +[MessageEvent-trusted.html] + [With a BroadcastChannel] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/broadcastchannel/basics.html.ini b/tests/wpt/metadata/webmessaging/broadcastchannel/basics.html.ini new file mode 100644 index 00000000000..d3e0ec12be6 --- /dev/null +++ b/tests/wpt/metadata/webmessaging/broadcastchannel/basics.html.ini @@ -0,0 +1,16 @@ +[basics.html] + [messages are delivered in port creation order] + expected: FAIL + + [closing and creating channels during message delivery works correctly] + expected: FAIL + + [messages aren't delivered to a closed port] + expected: FAIL + + [Closing a channel in onmessage doesn't cancel already queued events] + expected: FAIL + + [postMessage results in correct event] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/broadcastchannel/blobs.html.ini b/tests/wpt/metadata/webmessaging/broadcastchannel/blobs.html.ini new file mode 100644 index 00000000000..3f8179adc2b --- /dev/null +++ b/tests/wpt/metadata/webmessaging/broadcastchannel/blobs.html.ini @@ -0,0 +1,7 @@ +[blobs.html] + [Blobs work with workers on BroadcastChannel] + expected: FAIL + + [Blobs work on BroadcastChannel] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/broadcastchannel/interface.html.ini b/tests/wpt/metadata/webmessaging/broadcastchannel/interface.html.ini new file mode 100644 index 00000000000..7893dea122f --- /dev/null +++ b/tests/wpt/metadata/webmessaging/broadcastchannel/interface.html.ini @@ -0,0 +1,40 @@ +[interface.html] + [Null name should not throw] + expected: FAIL + + [postMessage after close should throw] + expected: FAIL + + [Undefined name should not throw] + expected: FAIL + + [postMessage should throw with uncloneable data] + expected: FAIL + + [close should not throw when called multiple times] + expected: FAIL + + [close should not throw] + expected: FAIL + + [Non-empty name should not throw] + expected: FAIL + + [postMessage with null should not throw] + expected: FAIL + + [postMessage should throw InvalidStateError after close, even with uncloneable data] + expected: FAIL + + [BroadcastChannel should have an onmessage event] + expected: FAIL + + [Non-string name should not throw] + expected: FAIL + + [Should throw if no name is provided] + expected: FAIL + + [postMessage without parameters should throw] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/broadcastchannel/origin.window.js.ini b/tests/wpt/metadata/webmessaging/broadcastchannel/origin.window.js.ini new file mode 100644 index 00000000000..d9c41606860 --- /dev/null +++ b/tests/wpt/metadata/webmessaging/broadcastchannel/origin.window.js.ini @@ -0,0 +1,5 @@ +[origin.window.html] + expected: TIMEOUT + [Serialization of BroadcastChannel origin] + expected: TIMEOUT + diff --git a/tests/wpt/metadata/webmessaging/broadcastchannel/sandbox.html.ini b/tests/wpt/metadata/webmessaging/broadcastchannel/sandbox.html.ini new file mode 100644 index 00000000000..d8180a26ca9 --- /dev/null +++ b/tests/wpt/metadata/webmessaging/broadcastchannel/sandbox.html.ini @@ -0,0 +1,4 @@ +[sandbox.html] + [Creating BroadcastChannel in an opaque origin] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/broadcastchannel/workers.html.ini b/tests/wpt/metadata/webmessaging/broadcastchannel/workers.html.ini new file mode 100644 index 00000000000..6e9191be9a4 --- /dev/null +++ b/tests/wpt/metadata/webmessaging/broadcastchannel/workers.html.ini @@ -0,0 +1,17 @@ +[workers.html] + expected: TIMEOUT + [BroadcastChannel used after a worker self.close()] + expected: FAIL + + [BroadcastChannel works in shared workers] + expected: FAIL + + [BroadcastChannel works in workers] + expected: FAIL + + [BroadcastChannel created after a worker self.close()] + expected: TIMEOUT + + [Closing and re-opening a channel works.] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/event.source.xorigin.sub.htm.ini b/tests/wpt/metadata/webmessaging/event.source.xorigin.sub.htm.ini new file mode 100644 index 00000000000..c003ef1e2e6 --- /dev/null +++ b/tests/wpt/metadata/webmessaging/event.source.xorigin.sub.htm.ini @@ -0,0 +1,4 @@ +[event.source.xorigin.sub.htm] + [Test Description: Cross-origin: event.source returns the WindowProxy of the source window.] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/message-channels/user-activation.tentative.html.ini b/tests/wpt/metadata/webmessaging/message-channels/user-activation.tentative.html.ini new file mode 100644 index 00000000000..45501c1c2ba --- /dev/null +++ b/tests/wpt/metadata/webmessaging/message-channels/user-activation.tentative.html.ini @@ -0,0 +1,4 @@ +[user-activation.tentative.html] + [user activation messagechannel test] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/messageerror.html.ini b/tests/wpt/metadata/webmessaging/messageerror.html.ini new file mode 100644 index 00000000000..8086ac427ca --- /dev/null +++ b/tests/wpt/metadata/webmessaging/messageerror.html.ini @@ -0,0 +1,7 @@ +[messageerror.html] + [The onmessageerror content attribute must execute when an event is dispatched on the window] + expected: FAIL + + [The onmessageerror content attribute must be compiled into the onmessageerror property] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/with-ports/011.html.ini b/tests/wpt/metadata/webmessaging/with-ports/011.html.ini new file mode 100644 index 00000000000..77c7759c973 --- /dev/null +++ b/tests/wpt/metadata/webmessaging/with-ports/011.html.ini @@ -0,0 +1,4 @@ +[011.html] + [posting an imagedata (from a cloned canvas) in an array] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/without-ports/011.html.ini b/tests/wpt/metadata/webmessaging/without-ports/011.html.ini new file mode 100644 index 00000000000..77c7759c973 --- /dev/null +++ b/tests/wpt/metadata/webmessaging/without-ports/011.html.ini @@ -0,0 +1,4 @@ +[011.html] + [posting an imagedata (from a cloned canvas) in an array] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/without-ports/026.html.ini b/tests/wpt/metadata/webmessaging/without-ports/026.html.ini new file mode 100644 index 00000000000..1f298293898 --- /dev/null +++ b/tests/wpt/metadata/webmessaging/without-ports/026.html.ini @@ -0,0 +1,4 @@ +[026.html] + [Cloning objects with getter properties] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/without-ports/028.html.ini b/tests/wpt/metadata/webmessaging/without-ports/028.html.ini new file mode 100644 index 00000000000..1620c015c24 --- /dev/null +++ b/tests/wpt/metadata/webmessaging/without-ports/028.html.ini @@ -0,0 +1,4 @@ +[028.html] + [Cloning objects, preserving sharing #2] + expected: FAIL + diff --git a/tests/wpt/metadata/webmessaging/worker_postMessage_user_activation.tentative.html.ini b/tests/wpt/metadata/webmessaging/worker_postMessage_user_activation.tentative.html.ini new file mode 100644 index 00000000000..c19a1c61f1e --- /dev/null +++ b/tests/wpt/metadata/webmessaging/worker_postMessage_user_activation.tentative.html.ini @@ -0,0 +1,4 @@ +[worker_postMessage_user_activation.tentative.html] + [Post Message from a worker] + expected: FAIL + diff --git a/tests/wpt/metadata/workers/Worker-messageport.html.ini b/tests/wpt/metadata/workers/Worker-messageport.html.ini deleted file mode 100644 index dd918545e13..00000000000 --- a/tests/wpt/metadata/workers/Worker-messageport.html.ini +++ /dev/null @@ -1,13 +0,0 @@ -[Worker-messageport.html] - [Test sending message to a worker on a port.] - expected: FAIL - - [Test sending messages to workers with no port.] - expected: FAIL - - [Test getting messages from a worker on a port.] - expected: FAIL - - [Test sending many messages to workers using ports.] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/Worker-multi-port.html.ini b/tests/wpt/metadata/workers/Worker-multi-port.html.ini index 76af84f87ac..c2192f76f4a 100644 --- a/tests/wpt/metadata/workers/Worker-multi-port.html.ini +++ b/tests/wpt/metadata/workers/Worker-multi-port.html.ini @@ -5,18 +5,6 @@ [Test postMessage on channel with previous failed postMessage calls.] expected: FAIL - [Test postMessage with no port.] - expected: FAIL - - [Test postMessage with two ports.] - expected: FAIL - - [Test postMessage with no ports and empty array.] - expected: FAIL - - [Test postMessage with null ports throws exception.] - expected: FAIL - [Test postMessage with incorrect ports throws exception] expected: FAIL diff --git a/tests/wpt/metadata/workers/Worker-termination-with-port-messages.html.ini b/tests/wpt/metadata/workers/Worker-termination-with-port-messages.html.ini deleted file mode 100644 index 4d2f2755e59..00000000000 --- a/tests/wpt/metadata/workers/Worker-termination-with-port-messages.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[Worker-termination-with-port-messages.html] - [This test terminates a worker when there are many undelivered MessagePort messages still waiting to be dispatched into the Worker Context. This causes termination of JS execution and test should not try to dispatch the remaining messages. Test succeeds if it does not hang or crash (if worker thread is running in the separate process, that process could hang or crash).] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/constructors/Worker/expected-self-properties.worker.js.ini b/tests/wpt/metadata/workers/constructors/Worker/expected-self-properties.worker.js.ini index 4b41f263de2..671a32174ad 100644 --- a/tests/wpt/metadata/workers/constructors/Worker/expected-self-properties.worker.js.ini +++ b/tests/wpt/metadata/workers/constructors/Worker/expected-self-properties.worker.js.ini @@ -1,8 +1,5 @@ [expected-self-properties.worker.html] type: testharness - [existence of MessageChannel] - expected: FAIL - [existence of SharedWorker] expected: FAIL diff --git a/tests/wpt/metadata/workers/data-url-shared.html.ini b/tests/wpt/metadata/workers/data-url-shared.html.ini index 1241a6a6d52..7eaea7048ce 100644 --- a/tests/wpt/metadata/workers/data-url-shared.html.ini +++ b/tests/wpt/metadata/workers/data-url-shared.html.ini @@ -1,5 +1,6 @@ [data-url-shared.html] type: testharness + expected: TIMEOUT [data URL shared worker] expected: FAIL @@ -28,7 +29,7 @@ expected: FAIL [A data: URL shared worker should not be shared among origins.] - expected: FAIL + expected: TIMEOUT [indexedDB is present] expected: FAIL diff --git a/tests/wpt/metadata/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/event-ports-dedicated.html.ini b/tests/wpt/metadata/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/event-ports-dedicated.html.ini deleted file mode 100644 index 6dfbdc124e0..00000000000 --- a/tests/wpt/metadata/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/event-ports-dedicated.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[event-ports-dedicated.html] - type: testharness - [e.ports in dedicated worker] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-null-in-array.html.ini b/tests/wpt/metadata/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-null-in-array.html.ini deleted file mode 100644 index 33ab2492be0..00000000000 --- a/tests/wpt/metadata/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-null-in-array.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[second-argument-null-in-array.html] - type: testharness - [Using [null\] in postMessage's second argument] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/opaque-origin.html.ini b/tests/wpt/metadata/workers/opaque-origin.html.ini index 4988013bf01..b4ee662f87d 100644 --- a/tests/wpt/metadata/workers/opaque-origin.html.ini +++ b/tests/wpt/metadata/workers/opaque-origin.html.ini @@ -6,3 +6,9 @@ [opaque-origin] expected: FAIL + [Worker has an opaque origin.] + expected: FAIL + + [Worker can access BroadcastChannel] + expected: FAIL + diff --git a/tests/wpt/metadata/workers/postMessage_clone_port.htm.ini b/tests/wpt/metadata/workers/postMessage_clone_port.htm.ini deleted file mode 100644 index 8065dc35227..00000000000 --- a/tests/wpt/metadata/workers/postMessage_clone_port.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[postMessage_clone_port.htm] - type: testharness - [ postMessage(): clone a port ] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/postMessage_clone_port_error.htm.ini b/tests/wpt/metadata/workers/postMessage_clone_port_error.htm.ini deleted file mode 100644 index afc617adbfc..00000000000 --- a/tests/wpt/metadata/workers/postMessage_clone_port_error.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[postMessage_clone_port_error.htm] - type: testharness - [ postMessage(): cloning source port ] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/postMessage_ports_readonly_array.htm.ini b/tests/wpt/metadata/workers/postMessage_ports_readonly_array.htm.ini deleted file mode 100644 index aa4e1ca91c5..00000000000 --- a/tests/wpt/metadata/workers/postMessage_ports_readonly_array.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[postMessage_ports_readonly_array.htm] - type: testharness - [ postMessage(): read-only ports array ] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/postMessage_target_source.htm.ini b/tests/wpt/metadata/workers/postMessage_target_source.htm.ini deleted file mode 100644 index b86366d8577..00000000000 --- a/tests/wpt/metadata/workers/postMessage_target_source.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[postMessage_target_source.htm] - type: testharness - [ postMessage(): target port and source port ] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/semantics/interface-objects/001.worker.js.ini b/tests/wpt/metadata/workers/semantics/interface-objects/001.worker.js.ini index d62e99a8ea8..64196f66138 100644 --- a/tests/wpt/metadata/workers/semantics/interface-objects/001.worker.js.ini +++ b/tests/wpt/metadata/workers/semantics/interface-objects/001.worker.js.ini @@ -3,12 +3,6 @@ [The SharedWorker interface object should be exposed.] expected: FAIL - [The MessagePort interface object should be exposed.] - expected: FAIL - - [The MessageChannel interface object should be exposed.] - expected: FAIL - [The ImageBitmap interface object should be exposed.] expected: FAIL @@ -65,4 +59,3 @@ [The IDBTransaction interface object should be exposed.] expected: FAIL - diff --git a/tests/wpt/metadata/workers/semantics/multiple-workers/005.html.ini b/tests/wpt/metadata/workers/semantics/multiple-workers/005.html.ini index f584fce5df1..268949ced5c 100644 --- a/tests/wpt/metadata/workers/semantics/multiple-workers/005.html.ini +++ b/tests/wpt/metadata/workers/semantics/multiple-workers/005.html.ini @@ -1,5 +1,4 @@ [005.html] - expected: ERROR [dedicated worker in shared worker in dedicated worker] expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index cf90fd08a31..51457930957 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -18966,15 +18966,15 @@ "testharness" ], "mozilla/interfaces.html": [ - "1207eaa2d8ff55e0c2216086b5a2c90a41ff78cf", + "3578a6e01cc3bb6daf8b4942d672ecb7da1ff6b5", "testharness" ], "mozilla/interfaces.js": [ - "f1854a3ae00493554006ad2c483489f4be8450a6", + "786f6e12515ff71bc83f34e4ffa32e7e38b1cd26", "support" ], "mozilla/interfaces.worker.js": [ - "802fe64ebfc86480f3c5adc80718f550d09d330b", + "3f77783a6cc31e7803dd61873ba17c92ce12eccc", "testharness" ], "mozilla/invalid-this.html": [ diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index 1207eaa2d8f..3578a6e01cc 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -167,7 +167,9 @@ test_interfaces([ "MediaList", "MediaQueryList", "MediaQueryListEvent", + "MessageChannel", "MessageEvent", + "MessagePort", "MimeType", "MimeTypeArray", "MouseEvent", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.js b/tests/wpt/mozilla/tests/mozilla/interfaces.js index f1854a3ae00..786f6e12515 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.js @@ -24,6 +24,8 @@ function test_interfaces(interfaceNamesInGlobalScope) { "JSON", "Map", "Math", + "MessageChannel", + "MessagePort", "NaN", "Number", "Object", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js index 802fe64ebfc..3f77783a6cc 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js @@ -34,7 +34,9 @@ test_interfaces([ "Headers", "History", "ImageData", + "MessageChannel", "MessageEvent", + "MessagePort", "Performance", "PerformanceEntry", "PerformanceMark", diff --git a/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_transfer_xsite_incoming_messages.window.js b/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_transfer_xsite_incoming_messages.window.js new file mode 100644 index 00000000000..23237ae1555 --- /dev/null +++ b/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_transfer_xsite_incoming_messages.window.js @@ -0,0 +1,32 @@ +// META: script=/common/get-host-info.sub.js + +async_test(function(t) { + var channel1 = new MessageChannel(); + var host = get_host_info(); + let iframe = document.createElement('iframe'); + iframe.src = host.HTTP_NOTSAMESITE_ORIGIN + "/webmessaging/support/ChildWindowPostMessage.htm"; + document.body.appendChild(iframe); + var TARGET = document.querySelector("iframe").contentWindow; + iframe.onload = t.step_func(function() { + // Enable the port. + channel1.port1.onmessage = t.step_func(function (evt) { + assert_equals(Number(evt.data), 0); + + // Send a message, expecting it to be received in the iframe. + channel1.port2.postMessage(1) + + // Transfer the port. + TARGET.postMessage("ports", "*", [channel1.port1]); + }); + + // Send a message, expecting it to be received here. + channel1.port2.postMessage(0) + + channel1.port2.onmessage = t.step_func(function (evt) { + assert_equals(Number(evt.data), 1); + t.done(); + }); + }); +}, `Tasks enqueued on the port-message-queue of an enabled port, + are transferred along with the port, when the transfer happens in the same task + during which postMessage is called`); diff --git a/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_with_transfer_entangled.any.js b/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_with_transfer_entangled.any.js new file mode 100644 index 00000000000..2226b278440 --- /dev/null +++ b/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_with_transfer_entangled.any.js @@ -0,0 +1,36 @@ +async_test(function(t) { + var channel1 = new MessageChannel(); + var channel2 = new MessageChannel(); + + // One, send a message. + channel1.port1.postMessage(1); + + // Two, transfer both ports. + channel2.port1.postMessage("transfer", [channel1.port1]); + channel2.port1.postMessage("transfer", [channel1.port2]); + + var transfer_counter = 0; + var sender; + channel2.port2.onmessage = t.step_func(function (evt) { + if (transfer_counter == 0) { + sender = evt.ports[0]; + transfer_counter = 1; + } else { + sender.postMessage(2); + var counter = 0; + evt.ports[0].onmessage = t.step_func(function (evt) { + if (counter == 0) { + assert_equals(evt.data, 1); + counter = 1; + } else if (counter == 1) { + assert_equals(evt.data, 2); + counter = 2; + } else { + assert_equals(evt.data, 3); + t.done(); + } + }); + sender.postMessage(3); + } + }); +}, "An entangled port transferred to the same origin receives messages in order"); diff --git a/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_with_transfer_incoming_messages.any.js b/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_with_transfer_incoming_messages.any.js new file mode 100644 index 00000000000..fe2e96220d3 --- /dev/null +++ b/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_with_transfer_incoming_messages.any.js @@ -0,0 +1,32 @@ +async_test(function(t) { + var channel1 = new MessageChannel(); + var channel2 = new MessageChannel(); + var channel3 = new MessageChannel(); + channel2.port2.onmessage = t.step_func(function (evt) { + channel3.port1.onmessage = t.step_func(function (evt) { + var counter = 0; + evt.ports[0].onmessage = t.step_func(function (evt) { + if (counter == 0) { + assert_equals(evt.data, "First"); + counter = 1; + } else if (counter == 1) { + assert_equals(evt.data, "Second"); + counter = 2; + } else if (counter == 2) { + assert_equals(evt.data, "Third"); + counter = 3; + } else if (counter == 3) { + assert_equals(evt.data, "Fourth"); + t.done(); + } + }); + channel1.port2.postMessage("Fourth"); + }); + channel1.port2.postMessage("Second"); + channel1.port2.postMessage("Third"); + channel3.port2.postMessage("2", evt.ports); + }); + channel1.port2.postMessage("First"); + channel2.port1.postMessage("1", [channel1.port1]); +}, `When transferring a non-enabled port mutiple times, + incoming messages sent at various transfer steps are received in order upon enablement.`); diff --git a/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_with_transfer_outgoing_messages.any.js b/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_with_transfer_outgoing_messages.any.js new file mode 100644 index 00000000000..aa80b7589cf --- /dev/null +++ b/tests/wpt/web-platform-tests/webmessaging/Channel_postMessage_with_transfer_outgoing_messages.any.js @@ -0,0 +1,35 @@ + +async_test(function(t) { + var channel1 = new MessageChannel(); + var channel2 = new MessageChannel(); + var channel3 = new MessageChannel(); + channel2.port2.onmessage = t.step_func(function (evt) { + evt.ports[0].postMessage("Second"); + evt.ports[0].postMessage("Third"); + channel3.port1.onmessage = t.step_func(function (evt) { + evt.ports[0].postMessage("Fourth"); + }); + channel3.port2.postMessage("2", evt.ports); + }); + channel1.port1.postMessage("First"); + channel2.port1.postMessage("1", [channel1.port1]); + var counter = 0; + channel1.port2.onmessage = t.step_func(function (evt) { + if (counter == 0) { + assert_equals(evt.data, "First"); + counter = 1; + } else if (counter == 1) { + assert_equals(evt.data, "Second"); + counter = 2; + } + else if (counter == 2) { + assert_equals(evt.data, "Third"); + counter = 3; + } + else if (counter == 3) { + assert_equals(evt.data, "Fourth"); + t.done(); + } + }); +}, `When transferring a port, + outgoing messages sent at each transfer step are received in order by the entangled port.`); diff --git a/tests/wpt/web-platform-tests/webmessaging/postMessage_MessagePorts_xsite.sub.window.js b/tests/wpt/web-platform-tests/webmessaging/postMessage_MessagePorts_xsite.sub.window.js new file mode 100644 index 00000000000..ca1e510edaa --- /dev/null +++ b/tests/wpt/web-platform-tests/webmessaging/postMessage_MessagePorts_xsite.sub.window.js @@ -0,0 +1,66 @@ +// META: script=/common/get-host-info.sub.js + +async_test(function(t) { + var host = get_host_info(); + var noteSameSiteURL = host.HTTP_NOTSAMESITE_ORIGIN + "/webmessaging/support/ChildWindowPostMessage.htm"; + var TOTALPORTS = 100; + var LocalPorts = []; + var RemotePorts = []; + var PassedResult = 0; + var sum = 0; + let iframe = document.createElement('iframe'); + iframe.src = noteSameSiteURL; + document.body.appendChild(iframe); + var TARGET = document.querySelector("iframe").contentWindow; + iframe.onload = t.step_func(function() { + assert_own_property(window, "MessageChannel", "window"); + + var channels = []; + + for (var i=0; i