continue messageport, transferable, postmessage options

This commit is contained in:
Gregory Terzian 2019-06-26 00:25:48 +08:00
parent c3b17c1201
commit 2f8932a6a1
100 changed files with 2456 additions and 1171 deletions

View file

@ -57,6 +57,7 @@ loadend
loadstart loadstart
message message
message message
messageerror
monospace monospace
month month
mousedown mousedown

View file

@ -130,7 +130,8 @@ use msg::constellation_msg::{
TopLevelBrowsingContextId, TopLevelBrowsingContextId,
}; };
use msg::constellation_msg::{ use msg::constellation_msg::{
PipelineNamespace, PipelineNamespaceId, PipelineNamespaceRequest, TraversalDirection, MessagePortId, MessagePortRouterId, PipelineNamespace, PipelineNamespaceId,
PipelineNamespaceRequest, TraversalDirection,
}; };
use net_traits::pub_domains::reg_host; use net_traits::pub_domains::reg_host;
use net_traits::request::RequestBuilder; use net_traits::request::RequestBuilder;
@ -153,6 +154,7 @@ use script_traits::{
IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg, IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg,
}; };
use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory}; use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
use script_traits::{MessagePortMsg, PortMessageTask, StructuredSerializedData};
use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg}; use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use servo_config::{opts, pref}; use servo_config::{opts, pref};
@ -175,6 +177,30 @@ use webvr_traits::{WebVREvent, WebVRMsg};
type PendingApprovalNavigations = HashMap<PipelineId, (LoadData, HistoryEntryReplacement)>; type PendingApprovalNavigations = HashMap<PipelineId, (LoadData, HistoryEntryReplacement)>;
#[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<PortMessageTask>),
/// 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<MessagePortId>,
}
/// Servo supports tabs (referred to as browsers), so `Constellation` needs to /// Servo supports tabs (referred to as browsers), so `Constellation` needs to
/// store browser specific data for bookkeeping. /// store browser specific data for bookkeeping.
struct Browser { struct Browser {
@ -340,6 +366,12 @@ pub struct Constellation<Message, LTF, STF> {
/// WebRender thread. /// WebRender thread.
webrender_api_sender: webrender_api::RenderApiSender, webrender_api_sender: webrender_api::RenderApiSender,
/// A map of message-port Id to info.
message_ports: HashMap<MessagePortId, MessagePortInfo>,
/// A map of router-id to ipc-sender, to route messages to ports.
message_port_routers: HashMap<MessagePortRouterId, IpcSender<MessagePortMsg>>,
/// The set of all the pipelines in the browser. (See the `pipeline` module /// The set of all the pipelines in the browser. (See the `pipeline` module
/// for more details.) /// for more details.)
pipelines: HashMap<PipelineId, Pipeline>, pipelines: HashMap<PipelineId, Pipeline>,
@ -751,6 +783,8 @@ where
swmanager_sender: sw_mgr_clone, swmanager_sender: sw_mgr_clone,
browsing_context_group_set: Default::default(), browsing_context_group_set: Default::default(),
browsing_context_group_next_id: Default::default(), browsing_context_group_next_id: Default::default(),
message_ports: HashMap::new(),
message_port_routers: HashMap::new(),
pipelines: HashMap::new(), pipelines: HashMap::new(),
browsing_contexts: HashMap::new(), browsing_contexts: HashMap::new(),
pending_changes: vec![], pending_changes: vec![],
@ -1487,6 +1521,27 @@ where
}; };
match content { 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) => { FromScriptMsg::ForwardToEmbedder(embedder_msg) => {
self.embedder_proxy self.embedder_proxy
.send((Some(source_top_ctx_id), embedder_msg)); .send((Some(source_top_ctx_id), embedder_msg));
@ -1563,9 +1618,16 @@ where
target: browsing_context_id, target: browsing_context_id,
source: source_pipeline_id, source: source_pipeline_id,
target_origin: origin, target_origin: origin,
source_origin,
data, 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 => { FromScriptMsg::Focus => {
self.handle_focus_msg(source_pipeline_id); 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<MessagePortMsg>,
) {
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) { fn handle_register_serviceworker(&self, scope_things: ScopeThings, scope: ServoUrl) {
if let Some(ref mgr) = self.swmanager_chan { if let Some(ref mgr) = self.swmanager_chan {
let _ = mgr.send(ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope)); let _ = mgr.send(ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope));
@ -3203,7 +3422,8 @@ where
browsing_context_id: BrowsingContextId, browsing_context_id: BrowsingContextId,
source_pipeline: PipelineId, source_pipeline: PipelineId,
origin: Option<ImmutableOrigin>, origin: Option<ImmutableOrigin>,
data: Vec<u8>, source_origin: ImmutableOrigin,
data: StructuredSerializedData,
) { ) {
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
None => { None => {
@ -3223,6 +3443,7 @@ where
source: source_pipeline, source: source_pipeline,
source_browsing_context: source_browsing_context, source_browsing_context: source_browsing_context,
target_origin: origin, target_origin: origin,
source_origin,
data, data,
}; };
let result = match self.pipelines.get(&pipeline_id) { let result = match self.pipelines.get(&pipeline_id) {

View file

@ -19,6 +19,7 @@ malloc_size_of = { path = "../malloc_size_of" }
malloc_size_of_derive = "0.1" malloc_size_of_derive = "0.1"
parking_lot = "0.9" parking_lot = "0.9"
serde = "1.0.60" serde = "1.0.60"
servo_url = {path = "../url"}
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
[dev-dependencies] [dev-dependencies]

View file

@ -156,6 +156,20 @@ impl PipelineNamespace {
index: HistoryStateIndex(self.next_index()), 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<Option<PipelineNamespace>> = Cell::new(None)); thread_local!(pub static PIPELINE_NAMESPACE: Cell<Option<PipelineNamespace>> = Cell::new(None));
@ -297,6 +311,68 @@ impl PartialEq<BrowsingContextId> 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)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct HistoryStateIndex(pub NonZeroU32); pub struct HistoryStateIndex(pub NonZeroU32);
malloc_size_of_is_0!(HistoryStateIndex); malloc_size_of_is_0!(HistoryStateIndex);

View file

@ -301,9 +301,9 @@ impl ResourceChannelManager {
.send(history_states.get(&history_state_id).cloned()) .send(history_states.get(&history_state_id).cloned())
.unwrap(); .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(); 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) => { CoreResourceMsg::RemoveHistoryStates(states_to_remove) => {
let mut history_states = http_state.history_states.write().unwrap(); let mut history_states = http_state.history_states.write().unwrap();

View file

@ -34,6 +34,7 @@ tinyfiledialogs = "3.0"
app_units = "0.7" app_units = "0.7"
backtrace = {version = "0.3", optional = true} backtrace = {version = "0.3", optional = true}
base64 = "0.10.1" base64 = "0.10.1"
bincode = "1"
bitflags = "1.0" bitflags = "1.0"
bluetooth_traits = {path = "../bluetooth_traits"} bluetooth_traits = {path = "../bluetooth_traits"}
canvas_traits = {path = "../canvas_traits"} canvas_traits = {path = "../canvas_traits"}
@ -97,7 +98,6 @@ servo_config = {path = "../config"}
servo_geometry = {path = "../geometry" } servo_geometry = {path = "../geometry" }
servo-media = {git = "https://github.com/servo/media"} servo-media = {git = "https://github.com/servo/media"}
servo_rand = {path = "../rand"} servo_rand = {path = "../rand"}
servo_remutex = {path = "../remutex"}
servo_url = {path = "../url"} servo_url = {path = "../url"}
sparkle = "0.1" sparkle = "0.1"
smallvec = { version = "0.6", features = ["std", "union"] } smallvec = { version = "0.6", features = ["std", "union"] }

View file

@ -4,8 +4,9 @@
use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::script_runtime::CommonScriptMsg; use crate::script_runtime::CommonScriptMsg;
use script_traits::StructuredSerializedData;
use servo_url::ImmutableOrigin;
/// Messages used to control the worker event loops /// Messages used to control the worker event loops
pub enum WorkerScriptMsg { pub enum WorkerScriptMsg {
@ -13,9 +14,9 @@ pub enum WorkerScriptMsg {
Common(CommonScriptMsg), Common(CommonScriptMsg),
/// Message sent through Worker.postMessage /// Message sent through Worker.postMessage
DOMMessage { DOMMessage {
origin: String, origin: ImmutableOrigin,
data: StructuredCloneData, data: StructuredSerializedData,
} },
} }
pub struct SimpleWorkerErrorHandler<T: DomObject> { pub struct SimpleWorkerErrorHandler<T: DomObject> {

View file

@ -155,4 +155,7 @@ pub fn run_worker_event_loop<T, TimerMsg, WorkerMsg, Event>(
.upcast::<GlobalScope>() .upcast::<GlobalScope>()
.perform_a_microtask_checkpoint(); .perform_a_microtask_checkpoint();
} }
worker_scope
.upcast::<GlobalScope>()
.perform_a_message_port_garbage_collection_checkpoint();
} }

View file

@ -30,6 +30,10 @@ DOMInterfaces = {
'weakReferenceable': True, 'weakReferenceable': True,
}, },
'MessagePort': {
'weakReferenceable': True,
},
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types #FIXME(jdm): This should be 'register': False, but then we don't generate enum types
'TestBinding': { 'TestBinding': {
'inCompartments': ['PromiseAttribute', 'PromiseNativeHandler'], 'inCompartments': ['PromiseAttribute', 'PromiseNativeHandler'],

View file

@ -6,7 +6,7 @@
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data). //! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).
use crate::compartments::enter_realm; 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::error::{Error, Fallible};
use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::DomRoot;
@ -14,6 +14,7 @@ use crate::dom::bindings::transferable::Transferable;
use crate::dom::blob::{Blob, BlobImpl}; use crate::dom::blob::{Blob, BlobImpl};
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort; use crate::dom::messageport::MessagePort;
use crate::script_runtime::JSContext as SafeJSContext;
use js::glue::CopyJSStructuredCloneData; use js::glue::CopyJSStructuredCloneData;
use js::glue::DeleteJSAutoStructuredCloneBuffer; use js::glue::DeleteJSAutoStructuredCloneBuffer;
use js::glue::GetLengthOfJSStructuredCloneData; use js::glue::GetLengthOfJSStructuredCloneData;
@ -30,12 +31,15 @@ use js::jsapi::{JSObject, JS_ClearPendingException};
use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter}; use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter};
use js::jsapi::{JS_ReadBytes, JS_WriteBytes}; use js::jsapi::{JS_ReadBytes, JS_WriteBytes};
use js::jsapi::{JS_ReadUint32Pair, JS_WriteUint32Pair}; use js::jsapi::{JS_ReadUint32Pair, JS_WriteUint32Pair};
use js::jsval::UndefinedValue;
use js::rust::wrappers::{JS_ReadStructuredClone, JS_WriteStructuredClone}; use js::rust::wrappers::{JS_ReadStructuredClone, JS_WriteStructuredClone};
use js::rust::{Handle, HandleValue, MutableHandleValue}; use js::rust::{CustomAutoRooterGuard, HandleValue, MutableHandleValue};
use libc::size_t; use msg::constellation_msg::MessagePortId;
use script_traits::transferable::MessagePortImpl;
use script_traits::StructuredSerializedData;
use std::collections::HashMap;
use std::os::raw; use std::os::raw;
use std::ptr; 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: 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. // TODO: Determine for sure which value Min and Max should have.
@ -129,19 +133,24 @@ impl StructuredCloneReader {
unsafe fn read_blob( unsafe fn read_blob(
cx: *mut JSContext, cx: *mut JSContext,
r: *mut JSStructuredCloneReader, r: *mut JSStructuredCloneReader,
sc_holder: &mut StructuredCloneHolder, sc_holder: &mut StructuredDataHolder,
) -> *mut JSObject { ) -> *mut JSObject {
let structured_reader = StructuredCloneReader { r: r }; let structured_reader = StructuredCloneReader { r: r };
let blob_buffer = structured_reader.read_bytes(); let blob_buffer = structured_reader.read_bytes();
let type_str = structured_reader.read_str(); let type_str = structured_reader.read_str();
let target_global = GlobalScope::from_context(cx); let target_global = GlobalScope::from_context(cx);
let blob = Blob::new( let read_blob = Blob::new(
&target_global, &target_global,
BlobImpl::new_from_bytes(blob_buffer), BlobImpl::new_from_bytes(blob_buffer),
type_str, type_str,
); );
let js_object = blob.reflector().get_jsobject().get(); let js_object = read_blob.reflector().get_jsobject().get();
sc_holder.blob = Some(blob); match sc_holder {
StructuredDataHolder::Read { blob, .. } => {
*blob = Some(read_blob);
},
_ => panic!("Unexpected variant of StructuredDataHolder"),
}
js_object js_object
} }
@ -174,7 +183,7 @@ unsafe extern "C" fn read_callback(
"tag should be higher than StructuredCloneTags::Min" "tag should be higher than StructuredCloneTags::Min"
); );
if tag == StructuredCloneTags::DomBlob as u32 { 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(); return ptr::null_mut();
} }
@ -185,7 +194,7 @@ unsafe extern "C" fn write_callback(
obj: RawHandleObject, obj: RawHandleObject,
_closure: *mut raw::c_void, _closure: *mut raw::c_void,
) -> bool { ) -> bool {
if let Ok(blob) = root_from_handleobject::<Blob>(Handle::from_raw(obj), cx) { if let Ok(blob) = root_from_object::<Blob>(*obj, cx) {
return write_blob(blob, w).is_ok(); return write_blob(blob, w).is_ok();
} }
return false; return false;
@ -193,39 +202,44 @@ unsafe extern "C" fn write_callback(
unsafe extern "C" fn read_transfer_callback( unsafe extern "C" fn read_transfer_callback(
cx: *mut JSContext, cx: *mut JSContext,
r: *mut JSStructuredCloneReader, _r: *mut JSStructuredCloneReader,
tag: u32, tag: u32,
content: *mut raw::c_void, _content: *mut raw::c_void,
extra_data: u64, extra_data: u64,
closure: *mut raw::c_void, closure: *mut raw::c_void,
return_object: RawMutableHandleObject, return_object: RawMutableHandleObject,
) -> bool { ) -> bool {
if tag == StructuredCloneTags::MessagePort as u32 { if tag == StructuredCloneTags::MessagePort as u32 {
<MessagePort as Transferable>::transfer_receive(cx, r, closure, content, extra_data, return_object) let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
} else { let owner = GlobalScope::from_context(cx);
false if let Ok(_) = <MessagePort as Transferable>::transfer_receive(
&owner,
&mut sc_holder,
extra_data,
return_object,
) {
return true;
}
} }
false
} }
/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer> /// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
unsafe extern "C" fn write_transfer_callback( unsafe extern "C" fn write_transfer_callback(
_cx: *mut JSContext, cx: *mut JSContext,
obj: RawHandleObject, obj: RawHandleObject,
closure: *mut raw::c_void, closure: *mut raw::c_void,
tag: *mut u32, tag: *mut u32,
ownership: *mut TransferableOwnership, ownership: *mut TransferableOwnership,
content: *mut *mut raw::c_void, _content: *mut *mut raw::c_void,
extra_data: *mut u64, extra_data: *mut u64,
) -> bool { ) -> bool {
if let Ok(port) = root_from_handleobject::<MessagePort>(Handle::from_raw(obj)) { if let Ok(port) = root_from_object::<MessagePort>(*obj, cx) {
if let Some(true) = port.detached() {
return false;
}
*tag = StructuredCloneTags::MessagePort as u32; *tag = StructuredCloneTags::MessagePort as u32;
*ownership = TransferableOwnership::SCTAG_TMO_CUSTOM; *ownership = TransferableOwnership::SCTAG_TMO_CUSTOM;
if port.transfer(closure, content, extra_data) { let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
port.set_detached(true); if let Ok(data) = port.transfer(&mut sc_holder) {
*extra_data = data;
return true; return true;
} }
} }
@ -242,10 +256,13 @@ unsafe extern "C" fn free_transfer_callback(
} }
unsafe extern "C" fn can_transfer_callback( unsafe extern "C" fn can_transfer_callback(
_cx: *mut JSContext, cx: *mut JSContext,
_obj: RawHandleObject, obj: RawHandleObject,
_closure: *mut raw::c_void, _closure: *mut raw::c_void,
) -> bool { ) -> bool {
if let Ok(_port) = root_from_object::<MessagePort>(*obj, cx) {
return true;
}
false false
} }
@ -261,123 +278,143 @@ static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredClon
canTransfer: Some(can_transfer_callback), canTransfer: Some(can_transfer_callback),
}; };
struct StructuredCloneHolder { /// A data holder for results from, and inputs to, structured-data read/write operations.
blob: Option<DomRoot<Blob>>, /// 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<DomRoot<Blob>>,
/// A vec of transfer-received DOM ports,
/// to be made available to script through a message event.
message_ports: Option<Vec<DomRoot<MessagePort>>>,
/// 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<HashMap<MessagePortId, MessagePortImpl>>,
},
/// A data holder into which transferred ports
/// can be written as part of their transfer steps.
Write(Option<HashMap<MessagePortId, MessagePortImpl>>),
} }
/// A buffer for a structured clone. /// Writes a structured clone. Returns a `DataClone` error if that fails.
pub enum StructuredCloneData { pub fn write(
/// A non-serializable (default) variant cx: SafeJSContext,
Struct(*mut u64, size_t), message: HandleValue,
/// A variant that can be serialized transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
Vector(Vec<u8>), ) -> Fallible<StructuredSerializedData> {
} unsafe {
rooted!(in(*cx) let mut val = UndefinedValue());
impl StructuredCloneData { if let Some(transfer) = transfer {
// TODO: should this be unsafe? transfer.to_jsval(*cx, val.handle_mut());
/// Writes a structured clone. Returns a `DataClone` error if that fails.
pub fn write(
cx: *mut JSContext,
message: HandleValue,
transfer: HandleValue,
) -> Fallible<StructuredCloneData> {
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(),
transfer,
);
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))
} }
}
/// Converts a StructuredCloneData to Vec<u8> for inter-thread sharing let mut sc_holder = StructuredDataHolder::Write(None);
pub fn move_to_arraybuffer(self) -> Vec<u8> {
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,
) -> bool {
let cx = global.get_cx();
let _ac = enter_realm(&*global);
let mut sc_holder = StructuredCloneHolder { blob: None };
let sc_holder_ptr = &mut sc_holder as *mut _; 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); let scbuf = NewJSAutoStructuredCloneBuffer(
StructuredCloneScope::DifferentProcess,
let result = JS_ReadStructuredClone( &STRUCTURED_CLONE_CALLBACKS,
*cx, );
scdata, let scdata = &mut ((*scbuf).data_);
JS_STRUCTURED_CLONE_VERSION, let policy = CloneDataPolicy {
StructuredCloneScope::DifferentProcess, // TODO: SAB?
rval, sharedArrayBuffer_: false,
&STRUCTURED_CLONE_CALLBACKS, };
sc_holder_ptr as *mut raw::c_void let result = JS_WriteStructuredClone(
); *cx,
message,
DeleteJSAutoStructuredCloneBuffer(scbuf); scdata,
StructuredCloneScope::DifferentProcess,
result 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. let nbytes = GetLengthOfJSStructuredCloneData(scdata);
pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) -> bool { let mut data = Vec::with_capacity(nbytes);
match self { CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
StructuredCloneData::Vector(mut vec_msg) => { data.set_len(nbytes);
let nbytes = vec_msg.len();
let data = vec_msg.as_mut_ptr() as *mut u64; DeleteJSAutoStructuredCloneBuffer(scbuf);
StructuredCloneData::read_clone(global, data, nbytes, rval)
} let mut port_impls = match sc_holder {
StructuredCloneData::Struct(data, nbytes) => { StructuredDataHolder::Write(port_impls) => port_impls,
StructuredCloneData::read_clone(global, data, nbytes, rval) _ => 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<Vec<DomRoot<MessagePort>>, ()> {
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(())
}
}

View file

@ -79,7 +79,8 @@ use media::WindowGLContext;
use metrics::{InteractiveMetrics, InteractiveWindow}; use metrics::{InteractiveMetrics, InteractiveWindow};
use mime::Mime; use mime::Mime;
use msg::constellation_msg::{ use msg::constellation_msg::{
BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId, BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId, PipelineId,
TopLevelBrowsingContextId,
}; };
use net_traits::filemanager_thread::RelativePos; use net_traits::filemanager_thread::RelativePos;
use net_traits::image::base::{Image, ImageMetadata}; 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 profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_layout_interface::rpc::LayoutRPC; use script_layout_interface::rpc::LayoutRPC;
use script_layout_interface::OpaqueStyleAndLayoutData; use script_layout_interface::OpaqueStyleAndLayoutData;
use script_traits::transferable::MessagePortImpl;
use script_traits::DrawAPaintImageResult; use script_traits::DrawAPaintImageResult;
use script_traits::{DocumentActivity, ScriptToConstellationChan, TimerEventId, TimerSource}; use script_traits::{DocumentActivity, ScriptToConstellationChan, TimerEventId, TimerSource};
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType}; use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
@ -154,6 +156,11 @@ pub unsafe trait JSTraceable {
unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>, Box<dyn EventLoopWaker>); unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>, Box<dyn EventLoopWaker>);
unsafe_no_jsmanaged_fields!(MessagePortImpl);
unsafe_no_jsmanaged_fields!(MessagePortId);
unsafe_no_jsmanaged_fields!(RefCell<Option<MessagePortId>>);
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
unsafe_no_jsmanaged_fields!(CSSError); unsafe_no_jsmanaged_fields!(CSSError);
unsafe_no_jsmanaged_fields!(&'static Encoding); unsafe_no_jsmanaged_fields!(&'static Encoding);

View file

@ -4,26 +4,19 @@
//! Trait representing the concept of [transferable objects] //! Trait representing the concept of [transferable objects]
//! (https://html.spec.whatwg.org/multipage/#transferable-objects). //! (https://html.spec.whatwg.org/multipage/#transferable-objects).
use crate::dom::bindings::reflector::DomObject;
use js::jsapi::{JSContext, JSStructuredCloneReader, MutableHandleObject};
use std::os::raw;
pub trait Transferable : DomObject { use crate::dom::bindings::reflector::DomObject;
fn transfer( use crate::dom::bindings::root::DomRoot;
&self, use crate::dom::bindings::structuredclone::StructuredDataHolder;
closure: *mut raw::c_void, use crate::dom::globalscope::GlobalScope;
content: *mut *mut raw::c_void, use js::jsapi::MutableHandleObject;
extra_data: *mut u64,
) -> bool; pub trait Transferable: DomObject {
fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()>;
fn transfer_receive( fn transfer_receive(
cx: *mut JSContext, owner: &DomRoot<GlobalScope>,
r: *mut JSStructuredCloneReader, sc_holder: &mut StructuredDataHolder,
closure: *mut raw::c_void,
content: *mut raw::c_void,
extra_data: u64, extra_data: u64,
return_object: MutableHandleObject, return_object: MutableHandleObject,
) -> bool; ) -> Result<(), ()>;
fn detached(&self) -> Option<bool> { None }
fn set_detached(&self, _value: bool) { }
fn transferable(&self) -> bool { false }
} }

View file

@ -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::conversions::{jsstring_to_str, private_from_proto_check};
use crate::dom::bindings::error::throw_invalid_this; use crate::dom::bindings::error::throw_invalid_this;
use crate::dom::bindings::inheritance::TopTypeId; use crate::dom::bindings::inheritance::TopTypeId;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::trace_object; use crate::dom::bindings::trace::trace_object;
use crate::dom::messageport::MessagePort;
use crate::dom::windowproxy; use crate::dom::windowproxy;
use crate::script_runtime::JSContext as SafeJSContext; use crate::script_runtime::JSContext as SafeJSContext;
use js::conversions::ToJSValConvertible;
use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper}; use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper};
use js::glue::{GetCrossCompartmentWrapper, JS_GetReservedSlot, WrapperNew}; use js::glue::{GetCrossCompartmentWrapper, JS_GetReservedSlot, WrapperNew};
use js::glue::{UnwrapObjectDynamic, RUST_JSID_TO_INT, RUST_JSID_TO_STRING}; 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::HandleObject as RawHandleObject;
use js::jsapi::MutableHandleObject as RawMutableHandleObject; use js::jsapi::MutableHandleObject as RawMutableHandleObject;
use js::jsapi::{AutoIdVector, CallArgs, DOMCallbacks, GetNonCCWObjectGlobal}; 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::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks};
use js::jsapi::{JS_EnumerateStandardClasses, JS_GetLatin1StringCharsAndLength}; use js::jsapi::{JS_EnumerateStandardClasses, JS_GetLatin1StringCharsAndLength};
use js::jsapi::{JS_IsExceptionPending, JS_IsGlobalObject}; use js::jsapi::{JS_IsExceptionPending, JS_IsGlobalObject};
@ -117,6 +120,19 @@ impl Clone for DOMJSClass {
} }
unsafe impl Sync 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<MessagePort>],
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. /// Returns the ProtoOrIfaceArray for the given global object.
/// Fails if `global` is not a DOM global object. /// Fails if `global` is not a DOM global object.
pub fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray { pub fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray {

View file

@ -10,13 +10,15 @@ use crate::dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThre
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding; use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods; 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::codegen::Bindings::WorkerBinding::WorkerType;
use crate::dom::bindings::error::{ErrorInfo, ErrorResult}; use crate::dom::bindings::error::{ErrorInfo, ErrorResult};
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
use crate::dom::bindings::str::DOMString; 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::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
@ -36,10 +38,10 @@ use devtools_traits::DevtoolScriptControlMsg;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use js::jsapi::JSContext;
use js::jsapi::JS_AddInterruptCallback; use js::jsapi::JS_AddInterruptCallback;
use js::jsapi::{Heap, JSContext, JSObject};
use js::jsval::UndefinedValue; use js::jsval::UndefinedValue;
use js::rust::HandleValue; use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId}; use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId};
use net_traits::image_cache::ImageCache; use net_traits::image_cache::ImageCache;
use net_traits::request::{CredentialsMode, Destination, ParserMetadata}; use net_traits::request::{CredentialsMode, Destination, ParserMetadata};
@ -467,15 +469,19 @@ impl DedicatedWorkerGlobalScope {
let target = self.upcast(); let target = self.upcast();
let _ac = enter_realm(self); let _ac = enter_realm(self);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue()); rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
assert!(data.read(scope.upcast(), message.handle_mut())); if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut())
MessageEvent::dispatch_jsval( {
target, MessageEvent::dispatch_jsval(
scope.upcast(), target,
message.handle(), scope.upcast(),
Some(&origin), message.handle(),
None, Some(&origin.ascii_serialization()),
vec![], None,
); ports,
);
} else {
MessageEvent::dispatch_error(target, scope.upcast());
}
}, },
WorkerScriptMsg::Common(msg) => { WorkerScriptMsg::Common(msg) => {
self.upcast::<WorkerGlobalScope>().process_event(msg); self.upcast::<WorkerGlobalScope>().process_event(msg);
@ -554,6 +560,32 @@ impl DedicatedWorkerGlobalScope {
)) ))
.unwrap(); .unwrap();
} }
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn post_message_impl(
&self,
cx: SafeJSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
let data = structuredclone::write(cx, message, Some(transfer))?;
let worker = self.worker.borrow().as_ref().unwrap().clone();
let global_scope = self.upcast::<GlobalScope>();
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)] #[allow(unsafe_code)]
@ -567,26 +599,34 @@ unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool {
} }
impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope { impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope {
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage /// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn PostMessage(&self, cx: SafeJSContext, message: HandleValue) -> ErrorResult { fn PostMessage(
rooted!(in(*cx) let transfer = UndefinedValue()); &self,
let data = StructuredCloneData::write(*cx, message, transfer.handle())?; cx: SafeJSContext,
let worker = self.worker.borrow().as_ref().unwrap().clone(); message: HandleValue,
let pipeline_id = self.global().pipeline_id(); transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
let origin = self.global().origin().immutable().ascii_serialization(); ) -> ErrorResult {
let task = Box::new(task!(post_worker_message: move || { self.post_message_impl(cx, message, transfer)
Worker::handle_message(worker, origin, data); }
}));
// TODO: Change this task source to a new `unshipped-port-message-queue` task source /// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
self.parent_sender fn PostMessage_(
.send(CommonScriptMsg::Task( &self,
WorkerEvent, cx: SafeJSContext,
task, message: HandleValue,
Some(pipeline_id), options: RootedTraceableBox<PostMessageOptions>,
TaskSourceName::DOMManipulation, ) -> ErrorResult {
)) let mut rooted = CustomAutoRooter::new(
.unwrap(); options
Ok(()) .transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
self.post_message_impl(cx, message, guard)
} }
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-close // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-close

View file

@ -4,21 +4,23 @@
use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding; use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding;
use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding::DissimilarOriginWindowMethods; 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::error::{Error, ErrorResult};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString; 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::dissimilaroriginlocation::DissimilarOriginLocation; use crate::dom::dissimilaroriginlocation::DissimilarOriginLocation;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::windowproxy::WindowProxy; use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::JSContext; use crate::script_runtime::JSContext;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use ipc_channel::ipc; use ipc_channel::ipc;
use js::jsapi::{Heap, JSObject};
use js::jsval::{JSVal, UndefinedValue}; use js::jsval::{JSVal, UndefinedValue};
use js::rust::HandleValue; use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use script_traits::ScriptMsg; use script_traits::{ScriptMsg, StructuredSerializedData};
use servo_url::ImmutableOrigin;
use servo_url::ServoUrl; use servo_url::ServoUrl;
/// Represents a dissimilar-origin `Window` that exists in another script thread. /// Represents a dissimilar-origin `Window` that exists in another script thread.
@ -133,29 +135,42 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
false false
} }
// https://html.spec.whatwg.org/multipage/#dom-window-postmessage /// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult { fn PostMessage(
// Step 3-5. &self,
let origin = match &origin[..] { cx: JSContext,
"*" => None, message: HandleValue,
"/" => { target_origin: USVString,
// TODO: Should be the origin of the incumbent settings object. mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
None ) -> ErrorResult {
}, if transfer.is_some() {
url => match ServoUrl::parse(&url) { let mut rooted = CustomAutoRooter::new(transfer.take().unwrap());
Ok(url) => Some(url.origin()), let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
Err(_) => return Err(Error::Syntax), self.post_message_impl(&target_origin, cx, message, transfer)
}, } else {
}; self.post_message_impl(&target_origin, cx, message, None)
}
}
// Step 1-2, 6-8. /// https://html.spec.whatwg.org/multipage/#dom-window-postmessage-options
// TODO(#12717): Should implement the `transfer` argument. fn PostMessage_(
rooted!(in(*cx) let transfer = UndefinedValue()); &self,
let data = StructuredCloneData::write(*cx, message, transfer.handle())?; cx: JSContext,
message: HandleValue,
options: RootedTraceableBox<WindowPostMessageOptions>,
) -> ErrorResult {
let mut rooted = CustomAutoRooter::new(
options
.transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
// Step 9. self.post_message_impl(&options.targetOrigin, cx, message, transfer)
self.post_message(origin, data);
Ok(())
} }
// https://html.spec.whatwg.org/multipage/#dom-opener // https://html.spec.whatwg.org/multipage/#dom-opener
@ -187,17 +202,54 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
} }
impl DissimilarOriginWindow { impl DissimilarOriginWindow {
pub fn post_message(&self, origin: Option<ImmutableOrigin>, 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<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
) -> 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() { 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, Some(incumbent) => incumbent,
}; };
let msg = ScriptMsg::PostMessage {
target: self.window_proxy.browsing_context_id(), let source_origin = incumbent.origin().immutable().clone();
source: incumbent.pipeline_id(),
target_origin: origin, // Step 3-5.
data: data.move_to_arraybuffer(), 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); let _ = incumbent.script_to_constellation_chan().send(msg);
Ok(())
} }
} }

View file

@ -1917,10 +1917,11 @@ impl Document {
} }
} }
} }
let global_scope = self.window.upcast::<GlobalScope>();
// Step 10, 14 // Step 10, 14
// https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps
if !self.salvageable.get() { if !self.salvageable.get() {
// https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps
let global_scope = self.window.upcast::<GlobalScope>();
// Step 1 of clean-up steps. // Step 1 of clean-up steps.
global_scope.close_event_sources(); global_scope.close_event_sources();
let msg = ScriptMsg::DiscardDocument; let msg = ScriptMsg::DiscardDocument;

View file

@ -236,7 +236,7 @@ impl EventSourceContext {
DOMString::from(self.origin.clone()), DOMString::from(self.origin.clone()),
None, None,
event_source.last_event_id.borrow().clone(), event_source.last_event_id.borrow().clone(),
vec![], Vec::with_capacity(0),
) )
}; };
// Step 7 // Step 7

View file

@ -10,10 +10,12 @@ use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::utils::message_ports_to_frozen_array;
use crate::dom::event::Event; use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::extendableevent::ExtendableEvent; use crate::dom::extendableevent::ExtendableEvent;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
use crate::script_runtime::JSContext; use crate::script_runtime::JSContext;
use dom_struct::dom_struct; use dom_struct::dom_struct;
@ -29,6 +31,7 @@ pub struct ExtendableMessageEvent {
data: Heap<JSVal>, data: Heap<JSVal>,
origin: DOMString, origin: DOMString,
lastEventId: DOMString, lastEventId: DOMString,
ports: Vec<DomRoot<MessagePort>>,
} }
impl ExtendableMessageEvent { impl ExtendableMessageEvent {
@ -40,12 +43,14 @@ impl ExtendableMessageEvent {
data: HandleValue, data: HandleValue,
origin: DOMString, origin: DOMString,
lastEventId: DOMString, lastEventId: DOMString,
ports: Vec<DomRoot<MessagePort>>,
) -> DomRoot<ExtendableMessageEvent> { ) -> DomRoot<ExtendableMessageEvent> {
let ev = Box::new(ExtendableMessageEvent { let ev = Box::new(ExtendableMessageEvent {
event: ExtendableEvent::new_inherited(), event: ExtendableEvent::new_inherited(),
data: Heap::default(), data: Heap::default(),
origin: origin, origin,
lastEventId: lastEventId, lastEventId,
ports,
}); });
let ev = reflect_dom_object(ev, global, ExtendableMessageEventBinding::Wrap); let ev = reflect_dom_object(ev, global, ExtendableMessageEventBinding::Wrap);
{ {
@ -71,13 +76,19 @@ impl ExtendableMessageEvent {
init.data.handle(), init.data.handle(),
init.origin.clone().unwrap(), init.origin.clone().unwrap(),
init.lastEventId.clone().unwrap(), init.lastEventId.clone().unwrap(),
vec![],
); );
Ok(ev) Ok(ev)
} }
} }
impl ExtendableMessageEvent { impl ExtendableMessageEvent {
pub fn dispatch_jsval(target: &EventTarget, scope: &GlobalScope, message: HandleValue) { pub fn dispatch_jsval(
target: &EventTarget,
scope: &GlobalScope,
message: HandleValue,
ports: Vec<DomRoot<MessagePort>>,
) {
let Extendablemessageevent = ExtendableMessageEvent::new( let Extendablemessageevent = ExtendableMessageEvent::new(
scope, scope,
atom!("message"), atom!("message"),
@ -86,6 +97,7 @@ impl ExtendableMessageEvent {
message, message,
DOMString::new(), DOMString::new(),
DOMString::new(), DOMString::new(),
ports,
); );
Extendablemessageevent.upcast::<Event>().fire(target); Extendablemessageevent.upcast::<Event>().fire(target);
} }
@ -111,4 +123,9 @@ impl ExtendableMessageEventMethods for ExtendableMessageEvent {
fn IsTrusted(&self) -> bool { fn IsTrusted(&self) -> bool {
self.event.IsTrusted() 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)
}
} }

View file

@ -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::conversions::{root_from_object, root_from_object_static};
use crate::dom::bindings::error::{report_pending_exception, ErrorInfo}; use crate::dom::bindings::error::{report_pending_exception, ErrorInfo};
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript}; use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript};
use crate::dom::bindings::str::DOMString; 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::crypto::Crypto;
use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
use crate::dom::errorevent::ErrorEvent; use crate::dom::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventsource::EventSource; use crate::dom::eventsource::EventSource;
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::MessagePort;
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
use crate::dom::performance::Performance; use crate::dom::performance::Performance;
use crate::dom::window::Window; use crate::dom::window::Window;
@ -36,34 +40,40 @@ use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
use crate::task_source::port_message::PortMessageQueue; use crate::task_source::port_message::PortMessageQueue;
use crate::task_source::remote_event::RemoteEventTaskSource; use crate::task_source::remote_event::RemoteEventTaskSource;
use crate::task_source::websocket::WebsocketTaskSource; use crate::task_source::websocket::WebsocketTaskSource;
use crate::task_source::TaskSource;
use crate::task_source::TaskSourceName; use crate::task_source::TaskSourceName;
use crate::timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle}; use crate::timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle};
use crate::timers::{OneshotTimers, TimerCallback}; use crate::timers::{OneshotTimers, TimerCallback};
use content_security_policy::CspList; use content_security_policy::CspList;
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use dom_struct::dom_struct; 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::glue::{IsWrapper, UnwrapObjectDynamic};
use js::jsapi::JSObject; use js::jsapi::JSObject;
use js::jsapi::{CurrentGlobalOrNull, GetNonCCWObjectGlobal}; use js::jsapi::{CurrentGlobalOrNull, GetNonCCWObjectGlobal};
use js::jsapi::{HandleObject, Heap}; use js::jsapi::{HandleObject, Heap};
use js::jsapi::{JSAutoRealm, JSContext}; use js::jsapi::{JSAutoRealm, JSContext};
use js::jsval::UndefinedValue;
use js::panic::maybe_resume_unwind; use js::panic::maybe_resume_unwind;
use js::rust::wrappers::EvaluateUtf8; use js::rust::wrappers::EvaluateUtf8;
use js::rust::{get_object_class, CompileOptionsWrapper, ParentRuntime, Runtime}; use js::rust::{get_object_class, CompileOptionsWrapper, ParentRuntime, Runtime};
use js::rust::{HandleValue, MutableHandleValue}; use js::rust::{HandleValue, MutableHandleValue};
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; 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::image_cache::ImageCache;
use net_traits::{CoreResourceThread, IpcSend, ResourceThreads}; use net_traits::{CoreResourceThread, IpcSend, ResourceThreads};
use profile_traits::{mem as profile_mem, time as profile_time}; 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 script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource};
use servo_url::{MutableOrigin, ServoUrl}; use servo_url::{MutableOrigin, ServoUrl};
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::Cell; use std::cell::Cell;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::collections::HashMap; use std::collections::{HashMap, VecDeque};
use std::ffi::CString; use std::ffi::CString;
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@ -85,6 +95,9 @@ pub struct GlobalScope {
crypto: MutNullableDom<Crypto>, crypto: MutNullableDom<Crypto>,
next_worker_id: Cell<WorkerId>, next_worker_id: Cell<WorkerId>,
/// The message-port router id for this global, if it is managing ports.
message_port_state: DomRefCell<MessagePortState>,
/// Pipeline id associated with this global. /// Pipeline id associated with this global.
pipeline_id: PipelineId, pipeline_id: PipelineId,
@ -168,6 +181,77 @@ pub struct GlobalScope {
user_agent: Cow<'static, str>, 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<GlobalScope>,
}
/// 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<MessagePort>),
/// 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<MessagePort>),
}
/// 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<MessagePortId, ManagedMessagePort>,
),
/// 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 { impl GlobalScope {
pub fn new_inherited( pub fn new_inherited(
pipeline_id: PipelineId, pipeline_id: PipelineId,
@ -184,6 +268,7 @@ impl GlobalScope {
user_agent: Cow<'static, str>, user_agent: Cow<'static, str>,
) -> Self { ) -> Self {
Self { Self {
message_port_state: DomRefCell::new(MessagePortState::UnManaged),
eventtarget: EventTarget::new_inherited(), eventtarget: EventTarget::new_inherited(),
crypto: Default::default(), crypto: Default::default(),
next_worker_id: Cell::new(WorkerId(0)), next_worker_id: Cell::new(WorkerId(0)),
@ -209,6 +294,397 @@ impl GlobalScope {
} }
} }
/// Complete the transfer of a message-port.
fn complete_port_transfer(&self, port_id: MessagePortId, tasks: VecDeque<PortMessageTask>) {
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;
}
/// <https://html.spec.whatwg.org/multipage/#entangle>
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.");
}
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
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.");
}
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
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.");
}
}
/// <https://html.spec.whatwg.org/multipage/#message-port-post-message-steps>
// 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<MessagePortId> = 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<MessagePortId> = 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<MessagePortImpl>) {
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<AtomicBool>) { pub fn track_worker(&self, closing_worker: Arc<AtomicBool>) {
self.list_auto_close_worker self.list_auto_close_worker
.borrow_mut() .borrow_mut()
@ -550,7 +1026,7 @@ impl GlobalScope {
if let Some(worker) = self.downcast::<WorkerGlobalScope>() { if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
return worker.websocket_task_source(); return worker.websocket_task_source();
} }
unreachable!() unreachable!();
} }
/// Evaluate JS code on this global scope. /// Evaluate JS code on this global scope.

View file

@ -11,7 +11,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString}; 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::event::Event;
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
@ -27,7 +27,7 @@ use msg::constellation_msg::{HistoryStateId, TraversalDirection};
use net_traits::{CoreResourceMsg, IpcSend}; use net_traits::{CoreResourceMsg, IpcSend};
use profile_traits::ipc; use profile_traits::ipc;
use profile_traits::ipc::channel; use profile_traits::ipc::channel;
use script_traits::ScriptMsg; use script_traits::{ScriptMsg, StructuredSerializedData};
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::cell::Cell; use std::cell::Cell;
@ -115,11 +115,16 @@ impl History {
}; };
match serialized_data { match serialized_data {
Some(serialized_data) => { Some(data) => {
let data = StructuredSerializedData {
serialized: data,
ports: None,
};
let global_scope = self.window.upcast::<GlobalScope>(); let global_scope = self.window.upcast::<GlobalScope>();
rooted!(in(*global_scope.get_cx()) let mut state = UndefinedValue()); rooted!(in(*global_scope.get_cx()) let mut state = UndefinedValue());
StructuredCloneData::Vector(serialized_data) if let Err(_) = structuredclone::read(&global_scope, data, state.handle_mut()) {
.read(&global_scope, state.handle_mut()); warn!("Error reading structuredclone data");
}
self.state.set(state.get()); self.state.set(state.get());
}, },
None => { None => {
@ -185,8 +190,7 @@ impl History {
// TODO: Step 4 // TODO: Step 4
// Step 5 // Step 5
rooted!(in(cx) let transfer = UndefinedValue()); let serialized_data = structuredclone::write(cx, data, None)?;
let serialized_data = StructuredCloneData::write(*cx, data, transfer.handle())?.move_to_arraybuffer();
let new_url: ServoUrl = match url { let new_url: ServoUrl = match url {
// Step 6 // Step 6
@ -255,7 +259,7 @@ impl History {
}; };
let _ = self.window.upcast::<GlobalScope>().resource_threads().send( let _ = self.window.upcast::<GlobalScope>().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 // TODO: Step 9 Update current entry to represent a GET request
@ -267,7 +271,9 @@ impl History {
// Step 11 // Step 11
let global_scope = self.window.upcast::<GlobalScope>(); let global_scope = self.window.upcast::<GlobalScope>();
rooted!(in(*cx) let mut state = UndefinedValue()); 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 // Step 12
self.state.set(state.get()); self.state.set(state.get());

View file

@ -4,7 +4,7 @@
use crate::dom::bindings::codegen::Bindings::MessageChannelBinding::{MessageChannelMethods, Wrap}; use crate::dom::bindings::codegen::Bindings::MessageChannelBinding::{MessageChannelMethods, Wrap};
use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort; use crate::dom::messageport::MessagePort;
@ -28,18 +28,24 @@ impl MessageChannel {
// Step 2 // Step 2
let port2 = MessagePort::new(&incumbent); let port2 = MessagePort::new(&incumbent);
incumbent.track_message_port(&*port1, None);
incumbent.track_message_port(&*port2, None);
// Step 3 // Step 3
port1.entangle(&port2); incumbent.entangle_ports(
port1.message_port_id().clone(),
port2.message_port_id().clone(),
);
// Steps 4-6 // Steps 4-6
let channel = reflect_dom_object(Box::new( let channel = reflect_dom_object(
MessageChannel { Box::new(MessageChannel {
reflector_: Reflector::new(), reflector_: Reflector::new(),
port1: Dom::from_ref(&port1), port1: Dom::from_ref(&port1),
port2: Dom::from_ref(&port2), port2: Dom::from_ref(&port2),
}), }),
global, global,
Wrap Wrap,
); );
// Step 7 // Step 7

View file

@ -11,6 +11,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::utils::message_ports_to_frozen_array;
use crate::dom::event::Event; use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
@ -18,10 +19,8 @@ use crate::dom::messageport::MessagePort;
use crate::dom::windowproxy::WindowProxy; use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::JSContext; use crate::script_runtime::JSContext;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible; use js::jsapi::{Heap, JSObject};
use js::jsapi::{Heap, JS_FreezeObject, JSContext, JSObject}; use js::jsval::JSVal;
use js::jsapi::HandleObject as RawHandleObject;
use js::jsval::{JSVal, UndefinedValue};
use js::rust::HandleValue; use js::rust::HandleValue;
use servo_atoms::Atom; use servo_atoms::Atom;
use std::ptr::NonNull; use std::ptr::NonNull;
@ -108,7 +107,7 @@ impl MessageEvent {
init.origin.clone(), init.origin.clone(),
source.as_ref().map(|source| &**source), source.as_ref().map(|source| &**source),
init.lastEventId.clone(), init.lastEventId.clone(),
init.ports.clone().unwrap_or(vec![]) init.ports.clone().unwrap_or(vec![]),
); );
Ok(ev) Ok(ev)
} }
@ -136,6 +135,26 @@ impl MessageEvent {
); );
messageevent.upcast::<Event>().fire(target); messageevent.upcast::<Event>().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::<Event>().fire(target);
}
} }
impl MessageEventMethods for MessageEvent { impl MessageEventMethods for MessageEvent {
@ -166,14 +185,8 @@ impl MessageEventMethods for MessageEvent {
self.event.IsTrusted() self.event.IsTrusted()
} }
#[allow(unsafe_code)]
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-ports> /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-ports>
unsafe fn Ports(&self, cx: *mut JSContext) -> JSVal { fn Ports(&self, cx: JSContext) -> JSVal {
rooted!(in(cx) let mut ports = UndefinedValue()); message_ports_to_frozen_array(self.ports.as_slice(), cx)
self.ports.to_jsval(cx, ports.handle_mut());
rooted!(in(cx) let obj = ports.to_object());
JS_FreezeObject(cx, RawHandleObject::from(obj.handle()));
*ports
} }
} }

View file

@ -3,393 +3,343 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * 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::EventHandlerBinding::EventHandlerNonNull;
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{MessagePortMethods, Wrap}; use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{
use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_object}; MessagePortMethods, PostMessageOptions, Wrap,
};
use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::error::{Error, ErrorResult}; use crate::dom::bindings::error::{Error, ErrorResult};
use crate::dom::bindings::inheritance::{Castable, HasParent}; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::reflector::{DomObject, reflect_dom_object};
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::structuredclone::StructuredCloneData; use crate::dom::bindings::structuredclone::{self, StructuredDataHolder};
use crate::dom::bindings::trace::JSTraceable; use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::transferable::Transferable; use crate::dom::bindings::transferable::Transferable;
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::messageevent::MessageEvent; use crate::script_runtime::JSContext as SafeJSContext;
use crate::task_source::TaskSource; use dom_struct::dom_struct;
use crate::task_source::port_message::PortMessageQueue; use js::jsapi::Heap;
use js::jsapi::{JSContext, JSStructuredCloneReader, JSObject, JSTracer, MutableHandleObject}; use js::jsapi::{JSObject, MutableHandleObject};
use js::jsval::UndefinedValue; use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use js::rust::{CustomAutoRooterGuard, HandleValue}; use msg::constellation_msg::{MessagePortId, MessagePortIndex, PipelineNamespaceId};
use servo_remutex::ReentrantMutex; use script_traits::PortMessageTask;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::VecDeque; use std::collections::HashMap;
use std::mem; use std::convert::TryInto;
use std::os::raw; use std::num::NonZeroU32;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
// FIXME: This is wrong, we need to figure out a better way of collecting message port objects per transfer #[dom_struct]
thread_local! { /// The MessagePort used in the DOM.
pub static TRANSFERRED_MESSAGE_PORTS: RefCell<Vec<DomRoot<MessagePort>>> = RefCell::new(Vec::new())
}
struct PortMessageTask {
origin: String,
data: Vec<u8>,
}
pub struct MessagePortInternal {
dom_port: RefCell<Option<Trusted<MessagePort>>>,
port_message_queue: RefCell<PortMessageQueue>,
enabled: Cell<bool>,
has_been_shipped: Cell<bool>,
entangled_port: RefCell<Option<Arc<ReentrantMutex<MessagePortInternal>>>>,
pending_port_messages: RefCell<VecDeque<PortMessageTask>>,
}
impl MessagePortInternal {
fn new(port_message_queue: PortMessageQueue) -> MessagePortInternal {
MessagePortInternal {
dom_port: RefCell::new(None),
port_message_queue: RefCell::new(port_message_queue),
enabled: Cell::new(false),
has_been_shipped: Cell::new(false),
entangled_port: RefCell::new(None),
pending_port_messages: RefCell::new(VecDeque::new()),
}
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
// Step 7 substeps
#[allow(unrooted_must_root)]
fn process_pending_port_messages(&self) {
let PortMessageTask { origin, data } = match self.pending_port_messages.borrow_mut().pop_front() {
Some(task) => task,
None => return,
};
// Substep 1
let final_target_port = self.dom_port.borrow().as_ref().unwrap().root();
// Substep 2
let target_global = final_target_port.global();
// Substep 3-4
rooted!(in(target_global.get_cx()) let mut message_clone = UndefinedValue());
let deserialize_result = StructuredCloneData::Vector(data).read(
&target_global,
message_clone.handle_mut()
);
if !deserialize_result {
return;
}
// Substep 5
let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| {
mem::replace(&mut *list.borrow_mut(), vec![])
});
// Substep 6
MessageEvent::dispatch_jsval(
final_target_port.upcast(),
&target_global,
message_clone.handle(),
Some(&origin),
None,
new_ports,
);
}
}
#[derive(DenyPublicFields, DomObject, MallocSizeOf)]
#[must_root]
#[repr(C)]
pub struct MessagePort { pub struct MessagePort {
eventtarget: EventTarget, eventtarget: EventTarget,
message_port_id: MessagePortId,
entangled_port: RefCell<Option<MessagePortId>>,
detached: Cell<bool>, detached: Cell<bool>,
#[ignore_malloc_size_of = "Defined in std"]
message_port_internal: Arc<ReentrantMutex<MessagePortInternal>>,
}
#[allow(unsafe_code)]
unsafe impl JSTraceable for MessagePort {
unsafe fn trace(&self, trc: *mut JSTracer) {
if !self.detached.get() {
self.eventtarget.trace(trc);
}
// Otherwise, do nothing.
}
}
impl HasParent for MessagePort {
type Parent = EventTarget;
fn as_parent(&self) -> &EventTarget {
&self.eventtarget
}
} }
impl MessagePort { impl MessagePort {
fn new_inherited(global: &GlobalScope) -> MessagePort { fn new_inherited(message_port_id: MessagePortId) -> MessagePort {
MessagePort { MessagePort {
eventtarget: EventTarget::new_inherited(), eventtarget: EventTarget::new_inherited(),
entangled_port: RefCell::new(None),
detached: Cell::new(false), detached: Cell::new(false),
message_port_internal: Arc::new( message_port_id,
ReentrantMutex::new(
MessagePortInternal::new(global.port_message_queue().clone())
)
),
}
}
fn new_transferred(message_port_internal: Arc<ReentrantMutex<MessagePortInternal>>) -> MessagePort {
MessagePort {
eventtarget: EventTarget::new_inherited(),
detached: Cell::new(false),
message_port_internal,
} }
} }
/// <https://html.spec.whatwg.org/multipage/#create-a-new-messageport-object> /// <https://html.spec.whatwg.org/multipage/#create-a-new-messageport-object>
pub fn new(owner: &GlobalScope) -> DomRoot<MessagePort> { pub fn new(owner: &GlobalScope) -> DomRoot<MessagePort> {
let message_port = reflect_dom_object(Box::new(MessagePort::new_inherited(owner)), owner, Wrap); let port_id = MessagePortId::new();
{ reflect_dom_object(Box::new(MessagePort::new_inherited(port_id)), owner, Wrap)
let internal = message_port.message_port_internal.lock().unwrap(); }
*internal.dom_port.borrow_mut() = Some(Trusted::new(&*message_port));
} /// Create a new port for an incoming transfer-received one.
message_port fn new_transferred(
owner: &GlobalScope,
transferred_port: MessagePortId,
entangled_port: Option<MessagePortId>,
) -> DomRoot<MessagePort> {
reflect_dom_object(
Box::new(MessagePort {
message_port_id: transferred_port,
eventtarget: EventTarget::new_inherited(),
detached: Cell::new(false),
entangled_port: RefCell::new(entangled_port),
}),
owner,
Wrap,
)
} }
/// <https://html.spec.whatwg.org/multipage/#entangle> /// <https://html.spec.whatwg.org/multipage/#entangle>
pub fn entangle(&self, other: &MessagePort) { pub fn entangle(&self, other_id: MessagePortId) {
{ *self.entangled_port.borrow_mut() = Some(other_id);
let internal = self.message_port_internal.lock().unwrap();
*internal.entangled_port.borrow_mut() = Some(other.message_port_internal.clone());
}
let internal = other.message_port_internal.lock().unwrap();
*internal.entangled_port.borrow_mut() = Some(self.message_port_internal.clone());
} }
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage> pub fn message_port_id(&self) -> &MessagePortId {
// Step 7 substeps &self.message_port_id
fn process_pending_port_messages(&self) { }
if self.detached.get() { return; }
let internal = self.message_port_internal.lock().unwrap(); pub fn detached(&self) -> bool {
internal.process_pending_port_messages(); self.detached.get()
}
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
fn set_onmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
let eventtarget = self.upcast::<EventTarget>();
eventtarget.set_event_handler_common("message", listener);
}
/// <https://html.spec.whatwg.org/multipage/#message-port-post-message-steps>
fn post_message_impl(
&self,
cx: SafeJSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
if self.detached.get() {
return Ok(());
}
// Step 1 is the transfer argument.
let target_port = self.entangled_port.borrow();
// Step 3
let mut doomed = false;
let ports = transfer
.iter()
.filter_map(|&obj| root_from_object::<MessagePort>(obj, *cx).ok());
for port in ports {
// Step 2
if port.message_port_id() == self.message_port_id() {
return Err(Error::DataClone);
}
// Step 4
if let Some(target_id) = target_port.as_ref() {
if port.message_port_id() == target_id {
doomed = true;
}
}
}
// Step 5
let data = structuredclone::write(cx, message, Some(transfer))?;
if doomed {
// TODO: The spec says to optionally report such a case to a dev console.
return Ok(());
}
// Step 6, done in MessagePortImpl.
let incumbent = match GlobalScope::incumbent() {
None => unreachable!("postMessage called with no incumbent global"),
Some(incumbent) => incumbent,
};
// Step 7
let task = PortMessageTask {
origin: incumbent.origin().immutable().clone(),
data,
};
// Have the global proxy this call to the corresponding MessagePortImpl.
self.global()
.post_messageport_msg(self.message_port_id().clone(), task);
Ok(())
} }
} }
impl Transferable for MessagePort { impl Transferable for MessagePort {
/// <https://html.spec.whatwg.org/multipage/#message-ports:transfer-steps> /// <https://html.spec.whatwg.org/multipage/#message-ports:transfer-steps>
#[allow(unsafe_code)] fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()> {
fn transfer( if self.detached.get() {
&self, return Err(());
_closure: *mut raw::c_void,
content: *mut *mut raw::c_void,
extra_data: *mut u64
) -> bool {
{
let internal = self.message_port_internal.lock().unwrap();
// Step 1
internal.has_been_shipped.set(true);
// Step 3
if let Some(ref other_port) = *internal.entangled_port.borrow() {
let entangled_internal = other_port.lock().unwrap();
// Substep 1
entangled_internal.has_been_shipped.set(true);
}; // This line MUST contain a semicolon, due to the strict drop check rule
} }
unsafe { let port_impls = match sc_holder {
// Steps 2, 3.2 and 4 StructuredDataHolder::Write(port_impls) => port_impls,
*content = Arc::into_raw(self.message_port_internal.clone()) as *mut raw::c_void; _ => panic!("Unexpected variant of StructuredDataHolder"),
};
*extra_data = 0; self.detached.set(true);
let id = self.message_port_id();
// 1. Run local transfer logic, and return the object to be transferred.
let transferred_port = self.global().mark_port_as_transferred(id);
// 2. Store the transferred object at a given key.
if let Some(ports) = port_impls.as_mut() {
ports.insert(id.clone(), transferred_port);
} else {
let mut ports = HashMap::new();
ports.insert(id.clone(), transferred_port);
*port_impls = Some(ports);
} }
true let PipelineNamespaceId(name_space) = id.clone().namespace_id;
let MessagePortIndex(index) = id.clone().index;
let index = index.get();
let mut big: [u8; 8] = [0; 8];
let name_space = name_space.to_ne_bytes();
let index = index.to_ne_bytes();
let (left, right) = big.split_at_mut(4);
left.copy_from_slice(&name_space);
right.copy_from_slice(&index);
// 3. Return a u64 representation of the key where the object is stored.
Ok(u64::from_ne_bytes(big))
} }
/// https://html.spec.whatwg.org/multipage/#message-ports:transfer-receiving-steps /// https://html.spec.whatwg.org/multipage/#message-ports:transfer-receiving-steps
#[allow(unrooted_must_root, unsafe_code)]
fn transfer_receive( fn transfer_receive(
cx: *mut JSContext, owner: &DomRoot<GlobalScope>,
_r: *mut JSStructuredCloneReader, sc_holder: &mut StructuredDataHolder,
_closure: *mut raw::c_void, extra_data: u64,
content: *mut raw::c_void, return_object: MutableHandleObject,
_extra_data: u64, ) -> Result<(), ()> {
return_object: MutableHandleObject let (message_ports, port_impls) = match sc_holder {
) -> bool { StructuredDataHolder::Read {
let internal = unsafe { Arc::from_raw(content as *const ReentrantMutex<MessagePortInternal>) }; message_ports,
let value = MessagePort::new_transferred(internal); port_impls,
..
// Step 2 } => (message_ports, port_impls),
let owner = unsafe { GlobalScope::from_context(cx) }; _ => panic!("Unexpected variant of StructuredDataHolder"),
let message_port = reflect_dom_object(Box::new(value), &*owner, Wrap);
{
let internal = message_port.message_port_internal.lock().unwrap();
// Step 1
internal.has_been_shipped.set(true);
let dom_port = Trusted::new(&*message_port);
internal.enabled.set(false);
*internal.dom_port.borrow_mut() = Some(dom_port);
*internal.port_message_queue.borrow_mut() = owner.port_message_queue().clone();
}
return_object.set(message_port.reflector().rootable().get());
TRANSFERRED_MESSAGE_PORTS.with(|list| {
list.borrow_mut().push(message_port);
});
true
}
fn detached(&self) -> Option<bool> {
Some(self.detached.get())
}
fn set_detached(&self, value: bool) {
self.detached.set(value);
}
fn transferable(&self) -> bool {
!self.detached.get()
}
}
impl MessagePortMethods for MessagePort {
#[allow(unsafe_code)]
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
unsafe fn PostMessage(
&self,
cx: *mut JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
) -> ErrorResult {
if self.detached.get() { return Ok(()); }
let internal = self.message_port_internal.lock().unwrap();
// Step 1
let target_port = internal.entangled_port.borrow();
// Step 3
let mut doomed = false;
rooted!(in(cx) let mut val = UndefinedValue());
let transfer = match *transfer {
Some(ref vec) => {
let ports = vec.iter().filter_map(|&obj| root_from_object::<MessagePort>(obj).ok());
for port in ports {
// Step 2
if Arc::ptr_eq(&port.message_port_internal, &self.message_port_internal) {
return Err(Error::DataClone);
}
// Step 4
if let Some(target) = target_port.as_ref() {
if Arc::ptr_eq(&port.message_port_internal, target) {
doomed = true;
}
}
}
vec.to_jsval(cx, val.handle_mut());
val
}
None => {
Vec::<*mut JSObject>::new().to_jsval(cx, val.handle_mut());
val
}
}; };
// Step 5 // 1. Re-build the key for the storage location
let data = StructuredCloneData::write(cx, message, transfer.handle())?.move_to_arraybuffer(); // of the transferred object.
let big: [u8; 8] = extra_data.to_ne_bytes();
let (name_space, index) = big.split_at(4);
// Step 6 let namespace_id = PipelineNamespaceId(u32::from_ne_bytes(
if target_port.is_none() || doomed { return Ok(()); } 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"),
);
// Step 7 let id = MessagePortId {
let task = PortMessageTask { namespace_id,
origin: self.global().origin().immutable().ascii_serialization(), index,
data,
}; };
{ // 2. Get the transferred object from its storage, using the key.
let target_port = target_port.as_ref().unwrap(); // Assign the transfer-received port-impl, and total number of transferred ports.
let target_internal = target_port.lock().unwrap(); let (ports_len, port_impl) = if let Some(ports) = port_impls.as_mut() {
target_internal.pending_port_messages.borrow_mut().push_back(task); let ports_len = ports.len();
let port_impl = ports.remove(&id).expect("Transferred port to be stored");
if target_internal.enabled.get() { if ports.is_empty() {
let target_port = target_port.clone(); *port_impls = None;
let _ = target_internal.port_message_queue.borrow().queue(
task!(process_pending_port_messages: move || {
let internal = target_port.lock().unwrap();
internal.process_pending_port_messages();
}),
&self.global()
);
} }
(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(()) Ok(())
} }
}
impl MessagePortMethods for MessagePort {
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
fn PostMessage(
&self,
cx: SafeJSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
if self.detached.get() {
return Ok(());
}
self.post_message_impl(cx, message, transfer)
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
fn PostMessage_(
&self,
cx: SafeJSContext,
message: HandleValue,
options: RootedTraceableBox<PostMessageOptions>,
) -> ErrorResult {
if self.detached.get() {
return Ok(());
}
let mut rooted = CustomAutoRooter::new(
options
.transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
self.post_message_impl(cx, message, guard)
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start> /// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
fn Start(&self) { fn Start(&self) {
let len = { if self.detached.get() {
let internal = self.message_port_internal.lock().unwrap(); return;
if internal.enabled.get() {
return;
}
internal.enabled.set(true);
let messages = internal.pending_port_messages.borrow();
messages.len()
};
let global = self.global();
for _ in 0..len {
let port = Trusted::new(self);
let _ = global.port_message_queue().queue(
task!(process_pending_port_messages: move || {
let this = port.root();
this.process_pending_port_messages();
}),
&global
);
} }
self.global().start_message_port(self.message_port_id());
} }
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close> /// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
fn Close(&self) { fn Close(&self) {
// Step 1 if self.detached.get() {
self.detached.set(true); return;
// Step 2
let maybe_port = {
let internal = self.message_port_internal.lock().unwrap();
let mut maybe_port = internal.entangled_port.borrow_mut();
maybe_port.take()
};
if let Some(other) = maybe_port {
let other_internal = other.lock().unwrap();
*other_internal.entangled_port.borrow_mut() = None;
} }
self.detached.set(true);
self.global().close_message_port(self.message_port_id());
} }
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage> /// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
fn GetOnmessage(&self) -> Option<Rc<EventHandlerNonNull>> { fn GetOnmessage(&self) -> Option<Rc<EventHandlerNonNull>> {
if self.detached.get() {
return None;
}
let eventtarget = self.upcast::<EventTarget>(); let eventtarget = self.upcast::<EventTarget>();
eventtarget.get_event_handler_common("message") eventtarget.get_event_handler_common("message")
} }
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage> /// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
fn SetOnmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) { fn SetOnmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
self.Start(); if self.detached.get() {
let eventtarget = self.upcast::<EventTarget>(); return;
eventtarget.set_event_handler_common("message", listener) }
self.set_onmessage(listener);
// Note: we cannot use the event_handler macro, due to the need to start the port.
self.global().start_message_port(self.message_port_id());
} }
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessageerror>
event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
} }

View file

@ -4,6 +4,7 @@
use crate::dom::abstractworker::SimpleWorkerErrorHandler; use crate::dom::abstractworker::SimpleWorkerErrorHandler;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
use crate::dom::bindings::codegen::Bindings::ServiceWorkerBinding::{ use crate::dom::bindings::codegen::Bindings::ServiceWorkerBinding::{
ServiceWorkerMethods, ServiceWorkerState, Wrap, ServiceWorkerMethods, ServiceWorkerState, Wrap,
}; };
@ -13,15 +14,15 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::USVString; 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::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::JSContext; use crate::script_runtime::JSContext;
use crate::task::TaskOnce; use crate::task::TaskOnce;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::jsapi::JSContext; use js::jsapi::{Heap, JSObject};
use js::jsval::UndefinedValue; use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use js::rust::HandleValue;
use script_traits::{DOMMessage, ScriptMsg}; use script_traits::{DOMMessage, ScriptMsg};
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::cell::Cell; use std::cell::Cell;
@ -79,6 +80,34 @@ impl ServiceWorker {
pub fn get_script_url(&self) -> ServoUrl { pub fn get_script_url(&self) -> ServoUrl {
ServoUrl::parse(&self.script_url.borrow().clone()).unwrap() 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<Vec<*mut JSObject>>,
) -> 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 { impl ServiceWorkerMethods for ServiceWorker {
@ -92,27 +121,34 @@ impl ServiceWorkerMethods for ServiceWorker {
USVString(self.script_url.borrow().clone()) USVString(self.script_url.borrow().clone())
} }
// https://w3c.github.io/ServiceWorker/#service-worker-postmessage /// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult { fn PostMessage(
// Step 1 &self,
if let ServiceWorkerState::Redundant = self.state.get() { cx: JSContext,
return Err(Error::InvalidState); message: HandleValue,
} transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
// Step 7 ) -> ErrorResult {
rooted!(in(*cx) let transfer = UndefinedValue()); self.post_message_impl(cx, message, transfer)
let data = StructuredCloneData::write(*cx, message, transfer.handle())?; }
let msg_vec = DOMMessage {
origin: self.global().origin().immutable().ascii_serialization(), /// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
data: data.move_to_arraybuffer(), fn PostMessage_(
}; &self,
let _ = self cx: JSContext,
.global() message: HandleValue,
.script_to_constellation_chan() options: RootedTraceableBox<PostMessageOptions>,
.send(ScriptMsg::ForwardDOMMessage( ) -> ErrorResult {
msg_vec, let mut rooted = CustomAutoRooter::new(
self.scope_url.clone() options
)); .transfer
Ok(()) .as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
self.post_message_impl(cx, message, guard)
} }
// https://w3c.github.io/ServiceWorker/#service-worker-container-onerror-attribute // https://w3c.github.io/ServiceWorker/#service-worker-container-onerror-attribute

View file

@ -12,12 +12,14 @@ use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone;
use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset; use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset;
use crate::dom::event::Event; use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::extendableevent::ExtendableEvent; use crate::dom::extendableevent::ExtendableEvent;
use crate::dom::extendablemessageevent::ExtendableMessageEvent; use crate::dom::extendablemessageevent::ExtendableMessageEvent;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::messageevent::MessageEvent;
use crate::dom::worker::TrustedWorkerAddress; use crate::dom::worker::TrustedWorkerAddress;
use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::dom::workerglobalscope::WorkerGlobalScope;
use crate::fetch::load_whole_resource; use crate::fetch::load_whole_resource;
@ -414,8 +416,17 @@ impl ServiceWorkerGlobalScope {
let target = self.upcast(); let target = self.upcast();
let _ac = enter_realm(&*scope); let _ac = enter_realm(&*scope);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue()); rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
assert!(data.read(scope.upcast(), message.handle_mut())); if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut())
ExtendableMessageEvent::dispatch_jsval(target, scope.upcast(), message.handle()); {
ExtendableMessageEvent::dispatch_jsval(
target,
scope.upcast(),
message.handle(),
ports,
);
} else {
MessageEvent::dispatch_error(target, scope.upcast());
}
}, },
CommonWorker(WorkerScriptMsg::Common(msg)) => { CommonWorker(WorkerScriptMsg::Common(msg)) => {
self.upcast::<WorkerGlobalScope>().process_event(msg); self.upcast::<WorkerGlobalScope>().process_event(msg);

View file

@ -5,9 +5,9 @@
// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope // https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
[Global=(Worker,DedicatedWorker), Exposed=DedicatedWorker] [Global=(Worker,DedicatedWorker), Exposed=DedicatedWorker]
/*sealed*/ interface DedicatedWorkerGlobalScope : WorkerGlobalScope { /*sealed*/ interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
[Throws] [Throws] void postMessage(any message, sequence<object> transfer);
void postMessage(any message/*, optional sequence<Transferable> transfer*/); [Throws] void postMessage(any message, optional PostMessageOptions options = {});
attribute EventHandler onmessage; attribute EventHandler onmessage;
void close(); void close();
}; };

View file

@ -25,7 +25,8 @@ interface DissimilarOriginWindow : GlobalScope {
void close(); void close();
readonly attribute boolean closed; readonly attribute boolean closed;
[Throws] void postMessage(any message, DOMString targetOrigin); [Throws] void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/);
[Throws] void postMessage(any message, optional WindowPostMessageOptions options = {});
attribute any opener; attribute any opener;
void blur(); void blur();
void focus(); void focus();

View file

@ -12,7 +12,7 @@ interface ExtendableMessageEvent : ExtendableEvent {
readonly attribute DOMString origin; readonly attribute DOMString origin;
readonly attribute DOMString lastEventId; readonly attribute DOMString lastEventId;
// [SameObject] readonly attribute (Client or ServiceWorker /*or MessagePort*/)? source; // [SameObject] readonly attribute (Client or ServiceWorker /*or MessagePort*/)? source;
// readonly attribute FrozenArray<MessagePort>? ports; readonly attribute /*FrozenArray<MessagePort>*/any ports;
}; };
dictionary ExtendableMessageEventInit : ExtendableEventInit { dictionary ExtendableMessageEventInit : ExtendableEventInit {

View file

@ -8,10 +8,16 @@
[Exposed=(Window,Worker)] [Exposed=(Window,Worker)]
interface MessagePort : EventTarget { interface MessagePort : EventTarget {
[Throws] void postMessage(any message, optional sequence<object> transfer /*= []*/); [Throws] void postMessage(any message, sequence<object> transfer /*= []*/);
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
void start(); void start();
void close(); void close();
// event handlers // event handlers
attribute EventHandler onmessage; attribute EventHandler onmessage;
attribute EventHandler onmessageerror;
};
dictionary PostMessageOptions {
sequence<object> transfer;
}; };

View file

@ -7,7 +7,8 @@
interface ServiceWorker : EventTarget { interface ServiceWorker : EventTarget {
readonly attribute USVString scriptURL; readonly attribute USVString scriptURL;
readonly attribute ServiceWorkerState state; readonly attribute ServiceWorkerState state;
[Throws] void postMessage(any message/*, optional sequence<object> transfer = []*/); [Throws] void postMessage(any message, sequence<object> transfer);
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
// event // event
attribute EventHandler onstatechange; attribute EventHandler onstatechange;

View file

@ -65,6 +65,8 @@
[Throws] [Throws]
void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/); void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/);
[Throws]
void postMessage(any message, optional WindowPostMessageOptions options = {});
// also has obsolete members // also has obsolete members
}; };
@ -172,3 +174,8 @@ partial interface Window {
[Pref="css.animations.testing.enabled"] [Pref="css.animations.testing.enabled"]
readonly attribute unsigned long runningAnimationCount; readonly attribute unsigned long runningAnimationCount;
}; };
dictionary WindowPostMessageOptions {
USVString targetOrigin = "/";
sequence<object> transfer;
};

View file

@ -14,8 +14,8 @@ interface Worker : EventTarget {
[Throws] constructor(USVString scriptURL, optional WorkerOptions options = {}); [Throws] constructor(USVString scriptURL, optional WorkerOptions options = {});
void terminate(); void terminate();
[Throws] void postMessage(any message/*, sequence<object> transfer*/); [Throws] void postMessage(any message, sequence<object> transfer);
// void postMessage(any message, optional PostMessageOptions options); [Throws] void postMessage(any message, optional PostMessageOptions options = {});
attribute EventHandler onmessage; attribute EventHandler onmessage;
attribute EventHandler onmessageerror; attribute EventHandler onmessageerror;
}; };

View file

@ -13,11 +13,10 @@ use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryLi
use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState; use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState;
use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit; use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
use crate::dom::bindings::codegen::Bindings::WindowBinding::{ 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::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString; use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString;
use crate::dom::bindings::conversions::ToJSValConvertible;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite; use crate::dom::bindings::num::Finite;
@ -25,7 +24,7 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString}; 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::trace::RootedTraceableBox;
use crate::dom::bindings::utils::{GlobalStaticData, WindowProxyHandler}; use crate::dom::bindings::utils::{GlobalStaticData, WindowProxyHandler};
use crate::dom::bindings::weakref::DOMTracker; use crate::dom::bindings::weakref::DOMTracker;
@ -44,7 +43,6 @@ use crate::dom::location::Location;
use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState}; use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
use crate::dom::mediaquerylistevent::MediaQueryListEvent; use crate::dom::mediaquerylistevent::MediaQueryListEvent;
use crate::dom::messageevent::MessageEvent; use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::TRANSFERRED_MESSAGE_PORTS;
use crate::dom::navigator::Navigator; use crate::dom::navigator::Navigator;
use crate::dom::node::{document_from_node, from_untrusted_node_address, Node, NodeDamage}; use crate::dom::node::{document_from_node, from_untrusted_node_address, Node, NodeDamage};
use crate::dom::performance::Performance; use crate::dom::performance::Performance;
@ -81,6 +79,7 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D}; use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
use ipc_channel::ipc::{channel, IpcSender}; use ipc_channel::ipc::{channel, IpcSender};
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use js::jsapi::Heap;
use js::jsapi::JSAutoRealm; use js::jsapi::JSAutoRealm;
use js::jsapi::JSObject; use js::jsapi::JSObject;
use js::jsapi::JSPROP_ENUMERATE; use js::jsapi::JSPROP_ENUMERATE;
@ -88,7 +87,7 @@ use js::jsapi::{GCReason, JS_GC};
use js::jsval::UndefinedValue; use js::jsval::UndefinedValue;
use js::jsval::{JSVal, NullValue}; use js::jsval::{JSVal, NullValue};
use js::rust::wrappers::JS_DefineProperty; use js::rust::wrappers::JS_DefineProperty;
use js::rust::{CustomAutoRooterGuard, HandleValue}; use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use media::WindowGLContext; use media::WindowGLContext;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse}; use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
@ -107,7 +106,10 @@ use script_layout_interface::rpc::{
use script_layout_interface::{PendingImageState, TrustedNodeAddress}; use script_layout_interface::{PendingImageState, TrustedNodeAddress};
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use script_traits::{ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData}; 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 script_traits::{TimerSchedulerMsg, WindowSizeData, WindowSizeType};
use selectors::attr::CaseSensitivity; use selectors::attr::CaseSensitivity;
use servo_geometry::{f32_rect_to_au_rect, MaxRect}; use servo_geometry::{f32_rect_to_au_rect, MaxRect};
@ -976,31 +978,53 @@ impl WindowMethods for Window {
&self, &self,
cx: JSContext, cx: JSContext,
message: HandleValue, message: HandleValue,
origin: USVString, target_origin: USVString,
transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>, mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
) -> ErrorResult { ) -> ErrorResult {
let source_global = GlobalScope::incumbent().expect("no incumbent global??"); let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
let source = source_global.as_window(); let source = incumbent.as_window();
let source_origin = source.Document().origin().immutable().clone();
// Step 3-5. if transfer.is_some() {
let origin = match &origin.0[..] { let mut rooted = CustomAutoRooter::new(transfer.take().unwrap());
"*" => None, let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
"/" => Some(source.Document().origin().immutable().clone()), self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
url => match ServoUrl::parse(&url) { } else {
Ok(url) => Some(url.origin().clone()), self.post_message_impl(&target_origin, source_origin, source, cx, message, None)
Err(_) => return Err(Error::Syntax), }
}, }
};
// Step 1-2, 6-8. /// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
rooted!(in(*cx) let mut val = UndefinedValue()); fn PostMessage_(
(*transfer).as_ref().unwrap_or(&Vec::new()).to_jsval(*cx, val.handle_mut()); &self,
cx: JSContext,
message: HandleValue,
options: RootedTraceableBox<WindowPostMessageOptions>,
) -> ErrorResult {
let mut rooted = CustomAutoRooter::new(
options
.transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
let data = StructuredCloneData::write(*cx, message, val.handle())?; let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
let source = incumbent.as_window();
// Step 9. let source_origin = source.Document().origin().immutable().clone();
self.post_message(origin, &*source.window_proxy(), data);
Ok(()) self.post_message_impl(
&options.targetOrigin,
source_origin,
source,
cx,
message,
transfer,
)
} }
// https://html.spec.whatwg.org/multipage/#dom-window-captureevents // https://html.spec.whatwg.org/multipage/#dom-window-captureevents
@ -1298,6 +1322,34 @@ impl WindowMethods for Window {
} }
impl 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<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
) -> 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 // https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
pub fn paint_worklet(&self) -> DomRoot<Worklet> { pub fn paint_worklet(&self) -> DomRoot<Worklet> {
self.paint_worklet.or_init(|| self.new_paint_worklet()) self.paint_worklet.or_init(|| self.new_paint_worklet())
@ -1344,6 +1396,9 @@ impl Window {
// thread, informing it that it can safely free the memory. // thread, informing it that it can safely free the memory.
self.Document().upcast::<Node>().teardown(); self.Document().upcast::<Node>().teardown();
// Tell the constellation to drop the sender to our message-port router, if there is any.
self.upcast::<GlobalScope>().remove_message_ports_router();
// Clean up any active promises // Clean up any active promises
// https://github.com/servo/servo/issues/15318 // https://github.com/servo/servo/issues/15318
if let Some(custom_elements) = self.custom_element_registry.get() { if let Some(custom_elements) = self.custom_element_registry.get() {
@ -2345,8 +2400,9 @@ impl Window {
pub fn post_message( pub fn post_message(
&self, &self,
target_origin: Option<ImmutableOrigin>, target_origin: Option<ImmutableOrigin>,
source_origin: ImmutableOrigin,
source: &WindowProxy, source: &WindowProxy,
serialize_with_transfer_result: StructuredCloneData, data: StructuredSerializedData,
) { ) {
let this = Trusted::new(self); let this = Trusted::new(self);
let source = Trusted::new(source); let source = Trusted::new(source);
@ -2367,26 +2423,23 @@ impl Window {
let obj = this.reflector().get_jsobject(); let obj = this.reflector().get_jsobject();
let _ac = JSAutoRealm::new(*cx, obj.get()); let _ac = JSAutoRealm::new(*cx, obj.get());
rooted!(in(*cx) let mut message_clone = UndefinedValue()); rooted!(in(*cx) let mut message_clone = UndefinedValue());
assert!(serialize_with_transfer_result.read( if let Ok(ports) = structuredclone::read(this.upcast(), data, message_clone.handle_mut()) {
this.upcast(), // Step 7.6, 7.7
message_clone.handle_mut(), MessageEvent::dispatch_jsval(
)); this.upcast(),
this.upcast(),
// Step 7.6. message_clone.handle(),
let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| { Some(&source_origin.ascii_serialization()),
mem::replace(&mut *list.borrow_mut(), vec![]) Some(&*source),
}); ports,
);
// Step 7.7. } else {
// TODO(#12719): Set the other attributes. // Step 4, fire messageerror.
MessageEvent::dispatch_jsval( MessageEvent::dispatch_error(
this.upcast(), this.upcast(),
this.upcast(), this.upcast(),
message_clone.handle(), );
Some(&document.origin().immutable().ascii_serialization()), }
Some(&*source),
new_ports,
);
}); });
// FIXME(nox): Why are errors silenced here? // FIXME(nox): Why are errors silenced here?
// TODO(#12718): Use the "posted message task source". // TODO(#12718): Use the "posted message task source".

View file

@ -5,6 +5,7 @@
use crate::compartments::enter_realm; use crate::compartments::enter_realm;
use crate::dom::abstractworker::SimpleWorkerErrorHandler; use crate::dom::abstractworker::SimpleWorkerErrorHandler;
use crate::dom::abstractworker::WorkerScriptMsg; 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;
use crate::dom::bindings::codegen::Bindings::WorkerBinding::{WorkerMethods, WorkerOptions}; use crate::dom::bindings::codegen::Bindings::WorkerBinding::{WorkerMethods, WorkerOptions};
use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; 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::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::USVString; 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::{ use crate::dom::dedicatedworkerglobalscope::{
DedicatedWorkerGlobalScope, DedicatedWorkerScriptMsg, DedicatedWorkerGlobalScope, DedicatedWorkerScriptMsg,
}; };
@ -27,10 +29,10 @@ use crossbeam_channel::{unbounded, Sender};
use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg}; use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use ipc_channel::ipc; use ipc_channel::ipc;
use js::jsapi::JS_RequestInterruptCallback; use js::jsapi::{Heap, JSObject, JS_RequestInterruptCallback};
use js::jsval::UndefinedValue; use js::jsval::UndefinedValue;
use js::rust::HandleValue; use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use script_traits::WorkerScriptLoadOrigin; use script_traits::{StructuredSerializedData, WorkerScriptLoadOrigin};
use std::cell::Cell; use std::cell::Cell;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
@ -140,7 +142,7 @@ impl Worker {
pub fn handle_message( pub fn handle_message(
address: TrustedWorkerAddress, address: TrustedWorkerAddress,
origin: String, origin: String,
data: StructuredCloneData, data: StructuredSerializedData,
) { ) {
let worker = address.root(); let worker = address.root();
@ -152,21 +154,34 @@ impl Worker {
let target = worker.upcast(); let target = worker.upcast();
let _ac = enter_realm(target); let _ac = enter_realm(target);
rooted!(in(*global.get_cx()) let mut message = UndefinedValue()); rooted!(in(*global.get_cx()) let mut message = UndefinedValue());
assert!(data.read(&global, message.handle_mut())); if let Ok(ports) = structuredclone::read(&global, data, message.handle_mut()) {
MessageEvent::dispatch_jsval(target, &global, message.handle(), Some(&origin), None, vec![]); 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) { pub fn dispatch_simple_error(address: TrustedWorkerAddress) {
let worker = address.root(); let worker = address.root();
worker.upcast().fire_event(atom!("error")); worker.upcast().fire_event(atom!("error"));
} }
}
impl WorkerMethods for Worker { /// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage fn post_message_impl(
fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult { &self,
rooted!(in(*cx) let transfer = UndefinedValue()); cx: JSContext,
let data = StructuredCloneData::write(*cx, message, transfer.handle())?; message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
let data = structuredclone::write(cx, message, Some(transfer))?;
let address = Trusted::new(self); let address = Trusted::new(self);
// NOTE: step 9 of https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage // NOTE: step 9 of https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage
@ -174,12 +189,44 @@ impl WorkerMethods for Worker {
let _ = self.sender.send(DedicatedWorkerScriptMsg::CommonWorker( let _ = self.sender.send(DedicatedWorkerScriptMsg::CommonWorker(
address, address,
WorkerScriptMsg::DOMMessage { WorkerScriptMsg::DOMMessage {
origin: self.global().origin().immutable().ascii_serialization(), origin: self.global().origin().immutable().clone(),
data, data,
}, },
)); ));
Ok(()) Ok(())
} }
}
impl WorkerMethods for Worker {
/// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
fn PostMessage(
&self,
cx: JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> 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<PostMessageOptions>,
) -> ErrorResult {
let mut rooted = CustomAutoRooter::new(
options
.transfer
.as_ref()
.unwrap_or(&Vec::with_capacity(0))
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
self.post_message_impl(cx, message, guard)
}
#[allow(unsafe_code)] #[allow(unsafe_code)]
// https://html.spec.whatwg.org/multipage/#terminate-a-worker // https://html.spec.whatwg.org/multipage/#terminate-a-worker

View file

@ -38,7 +38,6 @@ use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::ThreadLocalStackRoots; use crate::dom::bindings::root::ThreadLocalStackRoots;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection};
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::trace::JSTraceable; use crate::dom::bindings::trace::JSTraceable;
use crate::dom::bindings::utils::WRAP_CALLBACKS; use crate::dom::bindings::utils::WRAP_CALLBACKS;
use crate::dom::customelementregistry::{ use crate::dom::customelementregistry::{
@ -133,6 +132,7 @@ use script_traits::CompositorEvent::{
CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent, CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent,
WheelEvent, WheelEvent,
}; };
use script_traits::StructuredSerializedData;
use script_traits::{CompositorEvent, ConstellationControlMsg}; use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{ use script_traits::{
DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement, DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement,
@ -1589,6 +1589,11 @@ impl ScriptThread {
continue; continue;
} }
let window = document.window(); let window = document.window();
window
.upcast::<GlobalScope>()
.perform_a_message_port_garbage_collection_checkpoint();
let pending_reflows = window.get_pending_reflow_count(); let pending_reflows = window.get_pending_reflow_count();
if pending_reflows > 0 { if pending_reflows > 0 {
window.reflow(ReflowGoal::Full, ReflowReason::ImageLoaded); window.reflow(ReflowGoal::Full, ReflowReason::ImageLoaded);
@ -1867,12 +1872,14 @@ impl ScriptThread {
source: source_pipeline_id, source: source_pipeline_id,
source_browsing_context, source_browsing_context,
target_origin: origin, target_origin: origin,
source_origin,
data, data,
} => self.handle_post_message_msg( } => self.handle_post_message_msg(
target_pipeline_id, target_pipeline_id,
source_pipeline_id, source_pipeline_id,
source_browsing_context, source_browsing_context,
origin, origin,
source_origin,
data, data,
), ),
ConstellationControlMsg::UpdatePipelineId( ConstellationControlMsg::UpdatePipelineId(
@ -2525,7 +2532,8 @@ impl ScriptThread {
source_pipeline_id: PipelineId, source_pipeline_id: PipelineId,
source_browsing_context: TopLevelBrowsingContextId, source_browsing_context: TopLevelBrowsingContextId,
origin: Option<ImmutableOrigin>, origin: Option<ImmutableOrigin>,
data: Vec<u8>, source_origin: ImmutableOrigin,
data: StructuredSerializedData,
) { ) {
match { self.documents.borrow().find_window(pipeline_id) } { match { self.documents.borrow().find_window(pipeline_id) } {
None => return warn!("postMessage after target pipeline {} closed.", pipeline_id), None => return warn!("postMessage after target pipeline {} closed.", pipeline_id),
@ -2547,7 +2555,7 @@ impl ScriptThread {
Some(source) => source, Some(source) => source,
}; };
// FIXME(#22512): enqueues a task; unnecessary delay. // FIXME(#22512): enqueues a task; unnecessary delay.
window.post_message(origin, &*source, StructuredCloneData::Vector(data)) window.post_message(origin, source_origin, &*source, data)
}, },
} }
} }

View file

@ -8,7 +8,6 @@
//! active_workers map //! active_workers map
use crate::dom::abstractworker::WorkerScriptMsg; use crate::dom::abstractworker::WorkerScriptMsg;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::serviceworkerglobalscope::{ServiceWorkerGlobalScope, ServiceWorkerScriptMsg}; use crate::dom::serviceworkerglobalscope::{ServiceWorkerGlobalScope, ServiceWorkerScriptMsg};
use crate::dom::serviceworkerregistration::longest_prefix_match; use crate::dom::serviceworkerregistration::longest_prefix_match;
use crossbeam_channel::{unbounded, Receiver, RecvError, Sender}; use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
@ -136,7 +135,6 @@ impl ServiceWorkerManager {
fn forward_message(&self, msg: DOMMessage, sender: &Sender<ServiceWorkerScriptMsg>) { fn forward_message(&self, msg: DOMMessage, sender: &Sender<ServiceWorkerScriptMsg>) {
let DOMMessage { origin, data } = msg; let DOMMessage { origin, data } = msg;
let data = StructuredCloneData::Vector(data);
let _ = sender.send(ServiceWorkerScriptMsg::CommonWorker( let _ = sender.send(ServiceWorkerScriptMsg::CommonWorker(
WorkerScriptMsg::DOMMessage { origin, data }, WorkerScriptMsg::DOMMessage { origin, data },
)); ));

View file

@ -9,7 +9,7 @@ use msg::constellation_msg::PipelineId;
use std::fmt; use std::fmt;
#[derive(JSTraceable)] #[derive(JSTraceable)]
pub struct PortMessageQueue(pub Box<ScriptChan + Send + 'static>, pub PipelineId); pub struct PortMessageQueue(pub Box<dyn ScriptChan + Send + 'static>, pub PipelineId);
impl Clone for PortMessageQueue { impl Clone for PortMessageQueue {
fn clone(&self) -> PortMessageQueue { fn clone(&self) -> PortMessageQueue {
@ -26,11 +26,7 @@ impl fmt::Debug for PortMessageQueue {
impl TaskSource for PortMessageQueue { impl TaskSource for PortMessageQueue {
const NAME: TaskSourceName = TaskSourceName::PortMessage; const NAME: TaskSourceName = TaskSourceName::PortMessage;
fn queue_with_canceller<T>( fn queue_with_canceller<T>(&self, task: T, canceller: &TaskCanceller) -> Result<(), ()>
&self,
task: T,
canceller: &TaskCanceller,
) -> Result<(), ()>
where where
T: TaskOnce + 'static, T: TaskOnce + 'static,
{ {

View file

@ -17,8 +17,10 @@ extern crate malloc_size_of_derive;
extern crate serde; extern crate serde;
mod script_msg; mod script_msg;
pub mod transferable;
pub mod webdriver_msg; pub mod webdriver_msg;
use crate::transferable::MessagePortImpl;
use crate::webdriver_msg::{LoadStatus, WebDriverScriptCommand}; use crate::webdriver_msg::{LoadStatus, WebDriverScriptCommand};
use bluetooth_traits::BluetoothRequest; use bluetooth_traits::BluetoothRequest;
use canvas_traits::webgl::WebGLPipeline; use canvas_traits::webgl::WebGLPipeline;
@ -36,7 +38,7 @@ use keyboard_types::{CompositionEvent, KeyboardEvent};
use libc::c_void; use libc::c_void;
use media::WindowGLContext; use media::WindowGLContext;
use msg::constellation_msg::BackgroundHangMonitorRegister; 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 msg::constellation_msg::{PipelineNamespaceId, TopLevelBrowsingContextId, TraversalDirection};
use net_traits::image::base::Image; use net_traits::image::base::Image;
use net_traits::image_cache::ImageCache; use net_traits::image_cache::ImageCache;
@ -51,7 +53,7 @@ use servo_atoms::Atom;
use servo_url::ImmutableOrigin; use servo_url::ImmutableOrigin;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::{HashMap, VecDeque};
use std::fmt; use std::fmt;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::Arc; use std::sync::Arc;
@ -324,8 +326,11 @@ pub enum ConstellationControlMsg {
source_browsing_context: TopLevelBrowsingContextId, source_browsing_context: TopLevelBrowsingContextId,
/// The expected origin of the target. /// The expected origin of the target.
target_origin: Option<ImmutableOrigin>, target_origin: Option<ImmutableOrigin>,
/// The source origin of the message.
/// https://html.spec.whatwg.org/multipage/#dom-messageevent-origin
source_origin: ImmutableOrigin,
/// The data to be posted. /// The data to be posted.
data: Vec<u8>, data: StructuredSerializedData,
}, },
/// Updates the current pipeline ID of a given iframe. /// Updates the current pipeline ID of a given iframe.
/// First PipelineId is for the parent, second is the new PipelineId for the frame. /// 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)) self.sender.send((self.pipeline_id, msg))
} }
} }
/// A data-holder for serialized data and transferred objects.
/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct StructuredSerializedData {
/// Data serialized by SpiderMonkey.
pub serialized: Vec<u8>,
/// Transferred objects.
pub ports: Option<HashMap<MessagePortId, MessagePortImpl>>,
}
/// 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<PortMessageTask>),
/// Remove a port, the entangled one doesn't exists anymore.
RemoveMessagePort(MessagePortId),
/// Handle a new port-message-task.
NewTask(MessagePortId, PortMessageTask),
}

View file

@ -8,6 +8,9 @@ use crate::DocumentState;
use crate::IFrameLoadInfoWithData; use crate::IFrameLoadInfoWithData;
use crate::LayoutControlMsg; use crate::LayoutControlMsg;
use crate::LoadData; use crate::LoadData;
use crate::MessagePortMsg;
use crate::PortMessageTask;
use crate::StructuredSerializedData;
use crate::WindowSizeType; use crate::WindowSizeType;
use crate::WorkerGlobalScopeInit; use crate::WorkerGlobalScopeInit;
use crate::WorkerScriptLoadOrigin; use crate::WorkerScriptLoadOrigin;
@ -18,7 +21,9 @@ use euclid::default::Size2D as UntypedSize2D;
use euclid::Size2D; use euclid::Size2D;
use gfx_traits::Epoch; use gfx_traits::Epoch;
use ipc_channel::ipc::{IpcReceiver, IpcSender}; 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 msg::constellation_msg::{HistoryStateId, TraversalDirection};
use net_traits::request::RequestBuilder; use net_traits::request::RequestBuilder;
use net_traits::storage_thread::StorageType; use net_traits::storage_thread::StorageType;
@ -109,6 +114,20 @@ pub enum HistoryEntryReplacement {
/// Messages from the script to the constellation. /// Messages from the script to the constellation.
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub enum ScriptMsg { 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<MessagePortMsg>),
/// 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. /// Forward a message to the embedder.
ForwardToEmbedder(EmbedderMsg), ForwardToEmbedder(EmbedderMsg),
/// Requests are sent to constellation and fetches are checked manually /// Requests are sent to constellation and fetches are checked manually
@ -166,8 +185,11 @@ pub enum ScriptMsg {
source: PipelineId, source: PipelineId,
/// The expected origin of the target. /// The expected origin of the target.
target_origin: Option<ImmutableOrigin>, target_origin: Option<ImmutableOrigin>,
/// The source origin of the message.
/// https://html.spec.whatwg.org/multipage/#dom-messageevent-origin
source_origin: ImmutableOrigin,
/// The data to be posted. /// The data to be posted.
data: Vec<u8>, data: StructuredSerializedData,
}, },
/// Inform the constellation that a fragment was navigated to and whether or not it was a replacement navigation. /// Inform the constellation that a fragment was navigated to and whether or not it was a replacement navigation.
NavigatedToFragment(ServoUrl, HistoryEntryReplacement), NavigatedToFragment(ServoUrl, HistoryEntryReplacement),
@ -226,6 +248,13 @@ impl fmt::Debug for ScriptMsg {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
use self::ScriptMsg::*; use self::ScriptMsg::*;
let variant = match *self { let variant = match *self {
NewMessagePortRouter(..) => "NewMessagePortRouter",
RemoveMessagePortRouter(..) => "RemoveMessagePortRouter",
NewMessagePort(..) => "NewMessagePort",
RerouteMessagePort(..) => "RerouteMessagePort",
RemoveMessagePort(..) => "RemoveMessagePort",
MessagePortShipped(..) => "MessagePortShipped",
EntanglePorts(..) => "EntanglePorts",
ForwardToEmbedder(..) => "ForwardToEmbedder", ForwardToEmbedder(..) => "ForwardToEmbedder",
InitiateNavigateRequest(..) => "InitiateNavigateRequest", InitiateNavigateRequest(..) => "InitiateNavigateRequest",
BroadcastStorageEvent(..) => "BroadcastStorageEvent", BroadcastStorageEvent(..) => "BroadcastStorageEvent",
@ -283,12 +312,12 @@ pub struct ScopeThings {
} }
/// Message that gets passed to service worker scope on postMessage /// Message that gets passed to service worker scope on postMessage
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct DOMMessage { pub struct DOMMessage {
/// The origin of the message /// The origin of the message
pub origin: String, pub origin: ImmutableOrigin,
/// The payload of the message /// The payload of the message
pub data: Vec<u8>, pub data: StructuredSerializedData,
} }
/// Channels to allow service worker manager to communicate with constellation and resource thread /// Channels to allow service worker manager to communicate with constellation and resource thread

View file

@ -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 {
/// <https://html.spec.whatwg.org/multipage/#detached>
Detached,
/// <https://html.spec.whatwg.org/multipage/#port-message-queue>
/// The message-queue of this port is enabled,
/// the boolean represents awaiting completion of a transfer.
Enabled(bool),
/// <https://html.spec.whatwg.org/multipage/#port-message-queue>
/// 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,
/// <https://html.spec.whatwg.org/multipage/#entangle>
entangled_port: Option<MessagePortId>,
/// <https://html.spec.whatwg.org/multipage/#port-message-queue>
message_buffer: Option<VecDeque<PortMessageTask>>,
/// 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<MessagePortId> {
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.
/// <https://html.spec.whatwg.org/multipage/#has-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<PortMessageTask>) {
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<PortMessageTask> {
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
}
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
/// returns an optional queue of tasks that were buffered while the port was disabled.
pub fn start(&mut self) -> Option<VecDeque<PortMessageTask>> {
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()
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
pub fn close(&mut self) {
// Step 1
self.state = MessagePortState::Detached;
}
}

View file

@ -1,7 +1,4 @@
[Blob-constructor.html] [Blob-constructor.html]
[Passing a FrozenArray as the blobParts array should work (FrozenArray<MessagePort>).]
expected: FAIL
[options properties should be accessed in lexicographic order.] [options properties should be accessed in lexicographic order.]
expected: FAIL expected: FAIL

View file

@ -1,4 +0,0 @@
[unicode-origin.sub.html]
[Verify serialization of non-ascii origin in Blob URLs]
expected: FAIL

View file

@ -422337,6 +422337,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": [
[ [
"webmessaging/MessageEvent-trusted.html", "webmessaging/MessageEvent-trusted.html",
@ -422541,6 +422584,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": [
[ [
"webmessaging/postMessage_arrays.sub.htm", "webmessaging/postMessage_arrays.sub.htm",
@ -712918,6 +712974,22 @@
"4b00e68d49ef9fc85e92a5526ff76bd92259d4d9", "4b00e68d49ef9fc85e92a5526ff76bd92259d4d9",
"testharness" "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": [ "webmessaging/META.yml": [
"95d5071171b5a20cc14a414c97c9eae2f525f43f", "95d5071171b5a20cc14a414c97c9eae2f525f43f",
"support" "support"
@ -713086,6 +713158,10 @@
"cf2b8eb4c11b0b824a47677079c2d1b8837b3802", "cf2b8eb4c11b0b824a47677079c2d1b8837b3802",
"testharness" "testharness"
], ],
"webmessaging/postMessage_MessagePorts_xsite.sub.window.js": [
"ca1e510edaa09f41350426e143923bb15e6df82b",
"testharness"
],
"webmessaging/postMessage_arrays.sub.htm": [ "webmessaging/postMessage_arrays.sub.htm": [
"41e4a75eda616196ab3546943a0913785cbe69be", "41e4a75eda616196ab3546943a0913785cbe69be",
"testharness" "testharness"

View file

@ -5,8 +5,6 @@
[no-regexp-special-casing] [no-regexp-special-casing]
expected: FAIL expected: FAIL
[Conversion to a sequence works]
expected: FAIL
[no-regexp-special-casing.any.html] [no-regexp-special-casing.any.html]
[Untitled] [Untitled]
@ -15,6 +13,3 @@
[no-regexp-special-casing] [no-regexp-special-casing]
expected: FAIL expected: FAIL
[Conversion to a sequence works]
expected: FAIL

View file

@ -1,138 +1,17 @@
[remote-origin.htm] [remote-origin.htm]
bug: https://github.com/servo/servo/issues/21563 bug: https://github.com/servo/servo/issues/21563
expected: TIMEOUT
[Allow origin: *]
expected: TIMEOUT
[Allow origin: _*__] [Allow origin: _*__]
expected: TIMEOUT expected: FAIL
[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
[Allow origin: _http://www1.web-platform.test:8000___[tab\]_] [Allow origin: _http://www1.web-platform.test:8000___[tab\]_]
expected: TIMEOUT expected: FAIL
[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
[Disallow origin: http://www1.web-platform.test:8000/] [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#] [Disallow origin: http://www1.web-platform.test:8000#]
expected: TIMEOUT expected: FAIL
[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
[Disallow origin: HTTP://www1.web-platform.test:8000] [Disallow origin: HTTP://www1.web-platform.test:8000]
expected: TIMEOUT expected: FAIL
[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

View file

@ -89,6 +89,3 @@
[DOMMatrix clone: non-initial values (3d)] [DOMMatrix clone: non-initial values (3d)]
expected: FAIL expected: FAIL
[DOMRectList clone]
expected: FAIL

View file

@ -1,6 +1,3 @@
[EventListener-incumbent-global-1.sub.html] [EventListener-incumbent-global-1.sub.html]
type: testharness type: testharness
bug: https://github.com/servo/servo/issues/12715 bug: https://github.com/servo/servo/issues/12715
[Check the incumbent global EventListeners are called with]
expected: FAIL

View file

@ -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

View file

@ -1,5 +1,6 @@
[embedded-credentials.tentative.sub.html] [embedded-credentials.tentative.sub.html]
type: testharness type: testharness
expected: TIMEOUT
[Embedded credentials are treated as network errors.] [Embedded credentials are treated as network errors.]
expected: FAIL expected: FAIL
@ -7,13 +8,13 @@
expected: FAIL expected: FAIL
[Embedded credentials are treated as network errors in new windows.] [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.] [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.] [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.] [Embedded credentials matching the top-level are treated as network errors for cross-origin URLs.]
expected: FAIL expected: FAIL

View file

@ -1,5 +0,0 @@
[origin-of-data-document.html]
type: testharness
[The origin of a 'data:' document in a frame is opaque.]
expected: FAIL

View file

@ -1,37 +1,38 @@
[document_domain_access_details.sub.html] [document_domain_access_details.sub.html]
expected: TIMEOUT
[Access allowed if same-origin with no 'document.domain' modification. (Sanity check)] [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.] [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.] [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.] [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)] [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.] [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.] [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.] [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.] [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.] [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.] [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.] [Access evolves correctly for non-cross-origin objects when we join up via document.domain and then diverge again.]
expected: FAIL expected: NOTRUN

View file

@ -5,9 +5,6 @@
[ElementInternals interface: operation setValidity(ValidityStateFlags, DOMString, HTMLElement)] [ElementInternals interface: operation setValidity(ValidityStateFlags, DOMString, HTMLElement)]
expected: FAIL 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] [ValidityState interface: document.createElement("input").validity must inherit property "patternMismatch" with the proper type]
expected: FAIL expected: FAIL
@ -44,9 +41,6 @@
[ElementInternals interface: existence and properties of interface object] [ElementInternals interface: existence and properties of interface object]
expected: FAIL expected: FAIL
[MessagePort interface: attribute onmessage]
expected: FAIL
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "imageSmoothingQuality" with the proper type] [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "imageSmoothingQuality" with the proper type]
expected: FAIL expected: FAIL
@ -161,9 +155,6 @@
[SVGElement interface: attribute onmousemove] [SVGElement interface: attribute onmousemove]
expected: FAIL expected: FAIL
[MessagePort interface object length]
expected: FAIL
[Navigator interface: window.navigator must inherit property "onLine" with the proper type] [Navigator interface: window.navigator must inherit property "onLine" with the proper type]
expected: FAIL expected: FAIL
@ -269,9 +260,6 @@
[SharedWorker interface object name] [SharedWorker interface object name]
expected: FAIL expected: FAIL
[MessageChannel interface: attribute port1]
expected: FAIL
[SVGSVGElement interface: attribute onbeforeprint] [SVGSVGElement interface: attribute onbeforeprint]
expected: FAIL expected: FAIL
@ -290,9 +278,6 @@
[SVGElement interface: attribute tabIndex] [SVGElement interface: attribute tabIndex]
expected: FAIL 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"] [Location interface: window.location must have own property "ancestorOrigins"]
expected: FAIL expected: FAIL
@ -311,9 +296,6 @@
[ApplicationCache interface: constant UNCACHED on interface object] [ApplicationCache interface: constant UNCACHED on interface object]
expected: FAIL expected: FAIL
[MessageEvent interface: attribute ports]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: attribute filter] [OffscreenCanvasRenderingContext2D interface: attribute filter]
expected: FAIL expected: FAIL
@ -377,9 +359,6 @@
[SVGSVGElement interface: attribute onoffline] [SVGSVGElement interface: attribute onoffline]
expected: FAIL expected: FAIL
[MessagePort interface object name]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: existence and properties of interface prototype object's @@unscopables property] [OffscreenCanvasRenderingContext2D interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL expected: FAIL
@ -410,9 +389,6 @@
[TextTrack interface: document.createElement("track").track must inherit property "inBandMetadataTrackDispatchType" with the proper type] [TextTrack interface: document.createElement("track").track must inherit property "inBandMetadataTrackDispatchType" with the proper type]
expected: FAIL 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] [ValidityState interface: document.createElement("input").validity must inherit property "tooShort" with the proper type]
expected: FAIL expected: FAIL
@ -788,9 +764,6 @@
[ElementInternals interface: existence and properties of interface prototype object] [ElementInternals interface: existence and properties of interface prototype object]
expected: FAIL expected: FAIL
[MessageChannel interface object length]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: operation commit()] [OffscreenCanvasRenderingContext2D interface: operation commit()]
expected: FAIL expected: FAIL
@ -812,9 +785,6 @@
[Navigator interface: operation registerProtocolHandler(DOMString, USVString, DOMString)] [Navigator interface: operation registerProtocolHandler(DOMString, USVString, DOMString)]
expected: FAIL expected: FAIL
[MessagePort interface: existence and properties of interface object]
expected: FAIL
[DataTransferItemList interface object name] [DataTransferItemList interface object name]
expected: FAIL expected: FAIL
@ -833,9 +803,6 @@
[SVGSVGElement interface: attribute onunhandledrejection] [SVGSVGElement interface: attribute onunhandledrejection]
expected: FAIL expected: FAIL
[MessageChannel interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL
[SVGElement interface: attribute onloadedmetadata] [SVGElement interface: attribute onloadedmetadata]
expected: FAIL expected: FAIL
@ -920,9 +887,6 @@
[CustomElementRegistry interface: operation whenDefined(DOMString)] [CustomElementRegistry interface: operation whenDefined(DOMString)]
expected: FAIL expected: FAIL
[MessagePort interface: operation postMessage(any, PostMessageOptions)]
expected: FAIL
[DragEvent interface: existence and properties of interface object] [DragEvent interface: existence and properties of interface object]
expected: FAIL expected: FAIL
@ -1010,9 +974,6 @@
[SVGElement interface: attribute ondragexit] [SVGElement interface: attribute ondragexit]
expected: FAIL expected: FAIL
[MessagePort interface: attribute onmessageerror]
expected: FAIL
[Navigator interface: window.navigator must inherit property "oscpu" with the proper type] [Navigator interface: window.navigator must inherit property "oscpu" with the proper type]
expected: FAIL expected: FAIL
@ -1040,12 +1001,6 @@
[SharedWorker interface: existence and properties of interface prototype object] [SharedWorker interface: existence and properties of interface prototype object]
expected: FAIL 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)] [OffscreenCanvasRenderingContext2D interface: operation createPattern(CanvasImageSource, DOMString)]
expected: FAIL expected: FAIL
@ -1205,9 +1160,6 @@
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "drawFocusIfNeeded(Element)" with the proper type] [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "drawFocusIfNeeded(Element)" with the proper type]
expected: FAIL expected: FAIL
[MessagePort interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: operation closePath()] [OffscreenCanvasRenderingContext2D interface: operation closePath()]
expected: FAIL expected: FAIL
@ -1241,12 +1193,6 @@
[External interface object length] [External interface object length]
expected: FAIL expected: FAIL
[MessagePort interface: operation postMessage(any, [object Object\])]
expected: FAIL
[MessageChannel interface: attribute port2]
expected: FAIL
[CanvasRenderingContext2D interface: operation getTransform()] [CanvasRenderingContext2D interface: operation getTransform()]
expected: FAIL expected: FAIL
@ -1259,9 +1205,6 @@
[DataTransfer interface: attribute effectAllowed] [DataTransfer interface: attribute effectAllowed]
expected: FAIL expected: FAIL
[MessagePort interface: operation close()]
expected: FAIL
[OffscreenCanvas interface: operation getContext(OffscreenRenderingContextId, any)] [OffscreenCanvas interface: operation getContext(OffscreenRenderingContextId, any)]
expected: FAIL expected: FAIL
@ -1313,15 +1256,9 @@
[SVGAElement interface: attribute password] [SVGAElement interface: attribute password]
expected: FAIL 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] [SharedWorker interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL expected: FAIL
[MessageChannel interface object name]
expected: FAIL
[DataTransfer interface object name] [DataTransfer interface object name]
expected: FAIL expected: FAIL
@ -1355,9 +1292,6 @@
[ImageBitmapRenderingContext interface: existence and properties of interface prototype object's @@unscopables property] [ImageBitmapRenderingContext interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL 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] [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "isPointInStroke(unrestricted double, unrestricted double)" with the proper type]
expected: FAIL expected: FAIL
@ -1552,9 +1486,6 @@
[Window interface: window must inherit property "queueMicrotask(VoidFunction)" with the proper type] [Window interface: window must inherit property "queueMicrotask(VoidFunction)" with the proper type]
expected: FAIL 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] [Document interface: calling execCommand(DOMString, boolean, DOMString) on new Document() with too few arguments must throw TypeError]
expected: FAIL expected: FAIL
@ -1780,9 +1711,6 @@
[Document interface: iframe.contentDocument must inherit property "queryCommandState(DOMString)" with the proper type] [Document interface: iframe.contentDocument must inherit property "queryCommandState(DOMString)" with the proper type]
expected: FAIL expected: FAIL
[Window interface: operation postMessage(any, USVString, [object Object\])]
expected: FAIL
[Window interface: operation queueMicrotask(VoidFunction)] [Window interface: operation queueMicrotask(VoidFunction)]
expected: FAIL expected: FAIL

View file

@ -14,18 +14,12 @@
[DedicatedWorkerGlobalScope interface: self must inherit property "onmessageerror" with the proper type] [DedicatedWorkerGlobalScope interface: self must inherit property "onmessageerror" with the proper type]
expected: FAIL expected: FAIL
[MessagePort interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: attribute filter] [OffscreenCanvasRenderingContext2D interface: attribute filter]
expected: FAIL expected: FAIL
[OffscreenCanvasRenderingContext2D interface: operation measureText(DOMString)] [OffscreenCanvasRenderingContext2D interface: operation measureText(DOMString)]
expected: FAIL expected: FAIL
[MessagePort interface: attribute onmessage]
expected: FAIL
[ImageBitmapRenderingContext interface: existence and properties of interface object] [ImageBitmapRenderingContext interface: existence and properties of interface object]
expected: FAIL expected: FAIL
@ -65,15 +59,9 @@
[WorkerGlobalScope interface: self must inherit property "onlanguagechange" with the proper type] [WorkerGlobalScope interface: self must inherit property "onlanguagechange" with the proper type]
expected: FAIL expected: FAIL
[MessagePort interface: attribute onmessageerror]
expected: FAIL
[WorkerNavigator interface: attribute languages] [WorkerNavigator interface: attribute languages]
expected: FAIL expected: FAIL
[MessagePort interface object length]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: operation strokeText(DOMString, unrestricted double, unrestricted double, unrestricted double)] [OffscreenCanvasRenderingContext2D interface: operation strokeText(DOMString, unrestricted double, unrestricted double, unrestricted double)]
expected: FAIL expected: FAIL
@ -89,9 +77,6 @@
[ImageBitmapRenderingContext interface: existence and properties of interface prototype object] [ImageBitmapRenderingContext interface: existence and properties of interface prototype object]
expected: FAIL expected: FAIL
[MessagePort interface: existence and properties of interface prototype object]
expected: FAIL
[Path2D interface: existence and properties of interface object] [Path2D interface: existence and properties of interface object]
expected: FAIL expected: FAIL
@ -104,9 +89,6 @@
[BroadcastChannel interface: operation close()] [BroadcastChannel interface: operation close()]
expected: FAIL 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] [DedicatedWorkerGlobalScope interface: calling requestAnimationFrame(FrameRequestCallback) on self with too few arguments must throw TypeError]
expected: FAIL 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] [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 expected: FAIL
[MessageChannel interface: attribute port1]
expected: FAIL
[MessageChannel interface: attribute port2]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: attribute lineWidth] [OffscreenCanvasRenderingContext2D interface: attribute lineWidth]
expected: FAIL expected: FAIL
@ -161,9 +137,6 @@
[Path2D interface: operation moveTo(unrestricted double, unrestricted double)] [Path2D interface: operation moveTo(unrestricted double, unrestricted double)]
expected: FAIL 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)] [OffscreenCanvasRenderingContext2D interface: operation bezierCurveTo(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double)]
expected: FAIL expected: FAIL
@ -179,9 +152,6 @@
[ImageBitmap interface: existence and properties of interface prototype object's @@unscopables property] [ImageBitmap interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL expected: FAIL
[MessagePort interface: existence and properties of interface object]
expected: FAIL
[Path2D interface object name] [Path2D interface object name]
expected: FAIL expected: FAIL
@ -329,18 +299,12 @@
[OffscreenCanvasRenderingContext2D interface: attribute miterLimit] [OffscreenCanvasRenderingContext2D interface: attribute miterLimit]
expected: FAIL 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)] [OffscreenCanvasRenderingContext2D interface: operation strokeRect(unrestricted double, unrestricted double, unrestricted double, unrestricted double)]
expected: FAIL expected: FAIL
[OffscreenCanvasRenderingContext2D interface: existence and properties of interface object] [OffscreenCanvasRenderingContext2D interface: existence and properties of interface object]
expected: FAIL expected: FAIL
[MessageChannel interface object length]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: operation commit()] [OffscreenCanvasRenderingContext2D interface: operation commit()]
expected: FAIL expected: FAIL
@ -350,9 +314,6 @@
[WorkerGlobalScope interface: self must inherit property "onoffline" with the proper type] [WorkerGlobalScope interface: self must inherit property "onoffline" with the proper type]
expected: FAIL expected: FAIL
[MessageEvent interface: attribute ports]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: attribute lineDashOffset] [OffscreenCanvasRenderingContext2D interface: attribute lineDashOffset]
expected: FAIL expected: FAIL
@ -425,12 +386,6 @@
[Path2D interface: existence and properties of interface prototype object's @@unscopables property] [Path2D interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL 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)] [OffscreenCanvasRenderingContext2D interface: operation createPattern(CanvasImageSource, DOMString)]
expected: FAIL 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)] [Path2D interface: operation ellipse(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, boolean)]
expected: FAIL expected: FAIL
[MessageEvent interface: new MessageEvent("message", { data: 5 }) must inherit property "ports" with the proper type]
expected: FAIL
[WorkerGlobalScope interface: attribute ononline] [WorkerGlobalScope interface: attribute ononline]
expected: FAIL expected: FAIL
@ -524,15 +476,9 @@
[BroadcastChannel interface: operation postMessage(any)] [BroadcastChannel interface: operation postMessage(any)]
expected: FAIL expected: FAIL
[MessageChannel interface object name]
expected: FAIL
[SharedWorker interface: attribute port] [SharedWorker interface: attribute port]
expected: FAIL expected: FAIL
[MessagePort interface: operation postMessage(any, [object Object\])]
expected: FAIL
[WorkerNavigator interface: self.navigator must not have property "taintEnabled"] [WorkerNavigator interface: self.navigator must not have property "taintEnabled"]
expected: FAIL expected: FAIL
@ -548,9 +494,6 @@
[SharedWorker interface object name] [SharedWorker interface object name]
expected: FAIL expected: FAIL
[MessagePort interface object name]
expected: FAIL
[OffscreenCanvas interface: operation getContext(OffscreenRenderingContextId, any)] [OffscreenCanvas interface: operation getContext(OffscreenRenderingContextId, any)]
expected: FAIL expected: FAIL
@ -596,15 +539,6 @@
[OffscreenCanvasRenderingContext2D interface: operation clip(Path2D, CanvasFillRule)] [OffscreenCanvasRenderingContext2D interface: operation clip(Path2D, CanvasFillRule)]
expected: FAIL 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] [WebSocket interface: new WebSocket("ws://foo") must inherit property "extensions" with the proper type]
expected: FAIL expected: FAIL

View file

@ -1,18 +1,9 @@
[no-coop-coep.https.any.worker.html] [no-coop-coep.https.any.worker.html]
[SharedArrayBuffer over MessageChannel without COOP+COEP]
expected: FAIL
[SharedArrayBuffer over BroadcastChannel without COOP+COEP] [SharedArrayBuffer over BroadcastChannel without COOP+COEP]
expected: FAIL expected: FAIL
[no-coop-coep.https.any.html] [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] [SharedArrayBuffer over BroadcastChannel without COOP+COEP]
expected: FAIL expected: FAIL

View file

@ -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

View file

@ -1,5 +1,4 @@
[window-iframe-messagechannel-success.https.html] [window-iframe-messagechannel-success.https.html]
expected: TIMEOUT
[postMessaging to a same-origin iframe via MessageChannel allows them to see each others' modifications] [postMessaging to a same-origin iframe via MessageChannel allows them to see each others' modifications]
expected: TIMEOUT expected: FAIL

View file

@ -5,27 +5,12 @@
[transfer-errors] [transfer-errors]
expected: FAIL 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] [Serialize should throw before a detached ArrayBuffer is found]
expected: FAIL 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] [Serialize should throw before a detached MessagePort is found]
expected: FAIL expected: FAIL
[Cannot transfer MessagePort detached while the message was serialized]
expected: FAIL
[Cannot transfer the same ImageBitmap twice] [Cannot transfer the same ImageBitmap twice]
expected: FAIL expected: FAIL

View file

@ -1,11 +1,5 @@
[tasks.window.html] [tasks.window.html]
expected: TIMEOUT expected: TIMEOUT
[document.open() and tasks (MessagePort)]
expected: FAIL
[tasks without document.open() (MessagePort)]
expected: FAIL
[document.open() and tasks (canvas.toBlob())] [document.open() and tasks (canvas.toBlob())]
expected: FAIL expected: FAIL

View file

@ -3,12 +3,6 @@
[Default event values] [Default event values]
expected: FAIL expected: FAIL
[MessageEventInit dictionary]
expected: FAIL
[Passing null for ports member]
expected: FAIL
[ports attribute should be a FrozenArray] [ports attribute should be a FrozenArray]
expected: FAIL expected: FAIL

View file

@ -1,20 +1,11 @@
[promise-rejection-events.dedicatedworker.html] [promise-rejection-events.dedicatedworker.html]
type: testharness 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] [microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, all inside a postMessageTask]
expected: FAIL expected: FAIL
[microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, all inside a postMessageTask] [microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, all inside a postMessageTask]
expected: FAIL 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] [delayed handling: a nested-postMessageTask after promise creation/rejection, plus promise microtasks, is too late to attach a rejection handler]
expected: FAIL expected: FAIL
@ -33,18 +24,3 @@
[no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from createImageBitmap] [no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from createImageBitmap]
expected: FAIL 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

View file

@ -1,5 +0,0 @@
[Promise-incumbent-global.sub.html]
type: testharness
[Check the incumbent global Promise callbacks are called with]
expected: FAIL

View file

@ -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

View file

@ -0,0 +1,4 @@
[MessageEvent-trusted.html]
[With a BroadcastChannel]
expected: FAIL

View file

@ -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

View file

@ -0,0 +1,7 @@
[blobs.html]
[Blobs work with workers on BroadcastChannel]
expected: FAIL
[Blobs work on BroadcastChannel]
expected: FAIL

View file

@ -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

View file

@ -0,0 +1,5 @@
[origin.window.html]
expected: TIMEOUT
[Serialization of BroadcastChannel origin]
expected: TIMEOUT

View file

@ -0,0 +1,4 @@
[sandbox.html]
[Creating BroadcastChannel in an opaque origin]
expected: FAIL

View file

@ -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

View file

@ -0,0 +1,4 @@
[event.source.xorigin.sub.htm]
[Test Description: Cross-origin: event.source returns the WindowProxy of the source window.]
expected: FAIL

View file

@ -0,0 +1,4 @@
[user-activation.tentative.html]
[user activation messagechannel test]
expected: FAIL

View file

@ -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

View file

@ -0,0 +1,4 @@
[011.html]
[posting an imagedata (from a cloned canvas) in an array]
expected: FAIL

View file

@ -0,0 +1,4 @@
[011.html]
[posting an imagedata (from a cloned canvas) in an array]
expected: FAIL

View file

@ -0,0 +1,4 @@
[026.html]
[Cloning objects with getter properties]
expected: FAIL

View file

@ -0,0 +1,4 @@
[028.html]
[Cloning objects, preserving sharing #2]
expected: FAIL

View file

@ -0,0 +1,4 @@
[worker_postMessage_user_activation.tentative.html]
[Post Message from a worker]
expected: FAIL

View file

@ -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

View file

@ -5,18 +5,6 @@
[Test postMessage on channel with previous failed postMessage calls.] [Test postMessage on channel with previous failed postMessage calls.]
expected: FAIL 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] [Test postMessage with incorrect ports throws exception]
expected: FAIL expected: FAIL

View file

@ -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

View file

@ -1,8 +1,5 @@
[expected-self-properties.worker.html] [expected-self-properties.worker.html]
type: testharness type: testharness
[existence of MessageChannel]
expected: FAIL
[existence of SharedWorker] [existence of SharedWorker]
expected: FAIL expected: FAIL

View file

@ -1,5 +1,6 @@
[data-url-shared.html] [data-url-shared.html]
type: testharness type: testharness
expected: TIMEOUT
[data URL shared worker] [data URL shared worker]
expected: FAIL expected: FAIL
@ -28,7 +29,7 @@
expected: FAIL expected: FAIL
[A data: URL shared worker should not be shared among origins.] [A data: URL shared worker should not be shared among origins.]
expected: FAIL expected: TIMEOUT
[indexedDB is present] [indexedDB is present]
expected: FAIL expected: FAIL

View file

@ -1,5 +0,0 @@
[event-ports-dedicated.html]
type: testharness
[e.ports in dedicated worker]
expected: FAIL

View file

@ -1,5 +0,0 @@
[second-argument-null-in-array.html]
type: testharness
[Using [null\] in postMessage's second argument]
expected: FAIL

View file

@ -6,3 +6,9 @@
[opaque-origin] [opaque-origin]
expected: FAIL expected: FAIL
[Worker has an opaque origin.]
expected: FAIL
[Worker can access BroadcastChannel]
expected: FAIL

View file

@ -1,5 +0,0 @@
[postMessage_clone_port.htm]
type: testharness
[ postMessage(): clone a port ]
expected: FAIL

View file

@ -1,5 +0,0 @@
[postMessage_clone_port_error.htm]
type: testharness
[ postMessage(): cloning source port ]
expected: FAIL

View file

@ -1,5 +0,0 @@
[postMessage_ports_readonly_array.htm]
type: testharness
[ postMessage(): read-only ports array ]
expected: FAIL

View file

@ -1,5 +0,0 @@
[postMessage_target_source.htm]
type: testharness
[ postMessage(): target port and source port ]
expected: FAIL

View file

@ -3,12 +3,6 @@
[The SharedWorker interface object should be exposed.] [The SharedWorker interface object should be exposed.]
expected: FAIL 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.] [The ImageBitmap interface object should be exposed.]
expected: FAIL expected: FAIL
@ -65,4 +59,3 @@
[The IDBTransaction interface object should be exposed.] [The IDBTransaction interface object should be exposed.]
expected: FAIL expected: FAIL

View file

@ -1,5 +1,4 @@
[005.html] [005.html]
expected: ERROR
[dedicated worker in shared worker in dedicated worker] [dedicated worker in shared worker in dedicated worker]
expected: FAIL expected: FAIL

View file

@ -18966,15 +18966,15 @@
"testharness" "testharness"
], ],
"mozilla/interfaces.html": [ "mozilla/interfaces.html": [
"1207eaa2d8ff55e0c2216086b5a2c90a41ff78cf", "3578a6e01cc3bb6daf8b4942d672ecb7da1ff6b5",
"testharness" "testharness"
], ],
"mozilla/interfaces.js": [ "mozilla/interfaces.js": [
"f1854a3ae00493554006ad2c483489f4be8450a6", "786f6e12515ff71bc83f34e4ffa32e7e38b1cd26",
"support" "support"
], ],
"mozilla/interfaces.worker.js": [ "mozilla/interfaces.worker.js": [
"802fe64ebfc86480f3c5adc80718f550d09d330b", "3f77783a6cc31e7803dd61873ba17c92ce12eccc",
"testharness" "testharness"
], ],
"mozilla/invalid-this.html": [ "mozilla/invalid-this.html": [

View file

@ -167,7 +167,9 @@ test_interfaces([
"MediaList", "MediaList",
"MediaQueryList", "MediaQueryList",
"MediaQueryListEvent", "MediaQueryListEvent",
"MessageChannel",
"MessageEvent", "MessageEvent",
"MessagePort",
"MimeType", "MimeType",
"MimeTypeArray", "MimeTypeArray",
"MouseEvent", "MouseEvent",

View file

@ -24,6 +24,8 @@ function test_interfaces(interfaceNamesInGlobalScope) {
"JSON", "JSON",
"Map", "Map",
"Math", "Math",
"MessageChannel",
"MessagePort",
"NaN", "NaN",
"Number", "Number",
"Object", "Object",

View file

@ -34,7 +34,9 @@ test_interfaces([
"Headers", "Headers",
"History", "History",
"ImageData", "ImageData",
"MessageChannel",
"MessageEvent", "MessageEvent",
"MessagePort",
"Performance", "Performance",
"PerformanceEntry", "PerformanceEntry",
"PerformanceMark", "PerformanceMark",

View file

@ -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`);

View file

@ -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");

View file

@ -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.`);

View file

@ -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.`);

View file

@ -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<TOTALPORTS; i++)
{
channels[i] = new MessageChannel();
LocalPorts[i] = channels[i].port1;
LocalPorts[i].foo = i;
RemotePorts[i] = channels[i].port2;
LocalPorts[i].onmessage = t.step_func(function(e)
{
assert_equals(e.target.foo, e.data);
PassedResult++;
sum += e.data;
if (PassedResult == TOTALPORTS)
{
assert_equals(sum, 4950);
t.done();
}
});
}
// Sending in two batches, to test the two postMessage variants.
var firstBatch = RemotePorts.slice(0, 50);
var secondBatch = RemotePorts.slice(50, 100);
TARGET.postMessage("ports", "*", firstBatch);
TARGET.postMessage("ports", {targetOrigin: '*', transfer: secondBatch});
});
var counter = 0;
window.onmessage = function(e)
{
if (e.data === "ports")
{
if (counter == 0) {
for (var i=0; i<51; i++)
{
LocalPorts[i].postMessage(LocalPorts[i].foo);
}
counter = 1;
} else {
for (var i=51; i<100; i++)
{
LocalPorts[i].postMessage(LocalPorts[i].foo);
}
}
}
}
}, "Test Description: postMessage to cross-site iframe with MessagePort array containing 100 ports.");