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
message
message
messageerror
monospace
month
mousedown

View file

@ -130,7 +130,8 @@ use msg::constellation_msg::{
TopLevelBrowsingContextId,
};
use msg::constellation_msg::{
PipelineNamespace, PipelineNamespaceId, PipelineNamespaceRequest, TraversalDirection,
MessagePortId, MessagePortRouterId, PipelineNamespace, PipelineNamespaceId,
PipelineNamespaceRequest, TraversalDirection,
};
use net_traits::pub_domains::reg_host;
use net_traits::request::RequestBuilder;
@ -153,6 +154,7 @@ use script_traits::{
IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg,
};
use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
use script_traits::{MessagePortMsg, PortMessageTask, StructuredSerializedData};
use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg};
use serde::{Deserialize, Serialize};
use servo_config::{opts, pref};
@ -175,6 +177,30 @@ use webvr_traits::{WebVREvent, WebVRMsg};
type PendingApprovalNavigations = HashMap<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
/// store browser specific data for bookkeeping.
struct Browser {
@ -340,6 +366,12 @@ pub struct Constellation<Message, LTF, STF> {
/// WebRender thread.
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
/// for more details.)
pipelines: HashMap<PipelineId, Pipeline>,
@ -751,6 +783,8 @@ where
swmanager_sender: sw_mgr_clone,
browsing_context_group_set: Default::default(),
browsing_context_group_next_id: Default::default(),
message_ports: HashMap::new(),
message_port_routers: HashMap::new(),
pipelines: HashMap::new(),
browsing_contexts: HashMap::new(),
pending_changes: vec![],
@ -1487,6 +1521,27 @@ where
};
match content {
FromScriptMsg::RerouteMessagePort(port_id, task) => {
self.handle_reroute_messageport(port_id, task);
},
FromScriptMsg::MessagePortShipped(port_id) => {
self.handle_messageport_shipped(port_id);
},
FromScriptMsg::NewMessagePortRouter(router_id, ipc_sender) => {
self.handle_new_messageport_router(router_id, ipc_sender);
},
FromScriptMsg::RemoveMessagePortRouter(router_id) => {
self.handle_remove_messageport_router(router_id);
},
FromScriptMsg::NewMessagePort(router_id, port_id) => {
self.handle_new_messageport(router_id, port_id);
},
FromScriptMsg::RemoveMessagePort(port_id) => {
self.handle_remove_messageport(port_id);
},
FromScriptMsg::EntanglePorts(port1, port2) => {
self.handle_entangle_messageports(port1, port2);
},
FromScriptMsg::ForwardToEmbedder(embedder_msg) => {
self.embedder_proxy
.send((Some(source_top_ctx_id), embedder_msg));
@ -1563,9 +1618,16 @@ where
target: browsing_context_id,
source: source_pipeline_id,
target_origin: origin,
source_origin,
data,
} => {
self.handle_post_message_msg(browsing_context_id, source_pipeline_id, origin, data);
self.handle_post_message_msg(
browsing_context_id,
source_pipeline_id,
origin,
source_origin,
data,
);
},
FromScriptMsg::Focus => {
self.handle_focus_msg(source_pipeline_id);
@ -1685,6 +1747,163 @@ where
}
}
fn handle_reroute_messageport(&mut self, port_id: MessagePortId, task: PortMessageTask) {
let info = match self.message_ports.get_mut(&port_id) {
Some(info) => info,
None => {
return warn!(
"Constellation asked to re-route msg to unknown messageport {:?}",
port_id
)
},
};
match &mut info.state {
TransferState::Managed(router_id) => {
if let Some(sender) = self.message_port_routers.get(&router_id) {
let _ = sender.send(MessagePortMsg::NewTask(port_id, task));
} else {
warn!("No message-port sender for {:?}", router_id);
}
},
TransferState::TransferInProgress(queue) => queue.push_back(task),
TransferState::EntangledRemoved => warn!(
"Messageport received a message, but entangled has alread been removed {:?}",
port_id
),
}
}
fn handle_messageport_shipped(&mut self, port_id: MessagePortId) {
if let Some(info) = self.message_ports.get_mut(&port_id) {
if let TransferState::Managed(_) = info.state {
info.state = TransferState::TransferInProgress(VecDeque::new());
}
} else {
warn!(
"Constellation asked to mark unknown messageport as shipped {:?}",
port_id
);
}
}
fn handle_new_messageport_router(
&mut self,
router_id: MessagePortRouterId,
control_sender: IpcSender<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) {
if let Some(ref mgr) = self.swmanager_chan {
let _ = mgr.send(ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope));
@ -3203,7 +3422,8 @@ where
browsing_context_id: BrowsingContextId,
source_pipeline: PipelineId,
origin: Option<ImmutableOrigin>,
data: Vec<u8>,
source_origin: ImmutableOrigin,
data: StructuredSerializedData,
) {
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
None => {
@ -3223,6 +3443,7 @@ where
source: source_pipeline,
source_browsing_context: source_browsing_context,
target_origin: origin,
source_origin,
data,
};
let result = match self.pipelines.get(&pipeline_id) {

View file

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

View file

@ -156,6 +156,20 @@ impl PipelineNamespace {
index: HistoryStateIndex(self.next_index()),
}
}
fn next_message_port_id(&mut self) -> MessagePortId {
MessagePortId {
namespace_id: self.id,
index: MessagePortIndex(self.next_index()),
}
}
fn next_message_port_router_id(&mut self) -> MessagePortRouterId {
MessagePortRouterId {
namespace_id: self.id,
index: MessagePortRouterIndex(self.next_index()),
}
}
}
thread_local!(pub static PIPELINE_NAMESPACE: Cell<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)]
pub struct HistoryStateIndex(pub NonZeroU32);
malloc_size_of_is_0!(HistoryStateIndex);

View file

@ -301,9 +301,9 @@ impl ResourceChannelManager {
.send(history_states.get(&history_state_id).cloned())
.unwrap();
},
CoreResourceMsg::SetHistoryState(history_state_id, history_state) => {
CoreResourceMsg::SetHistoryState(history_state_id, structured_data) => {
let mut history_states = http_state.history_states.write().unwrap();
history_states.insert(history_state_id, history_state);
history_states.insert(history_state_id, structured_data);
},
CoreResourceMsg::RemoveHistoryStates(states_to_remove) => {
let mut history_states = http_state.history_states.write().unwrap();

View file

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

View file

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

View file

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

View file

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

View file

@ -6,7 +6,7 @@
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).
use crate::compartments::enter_realm;
use crate::dom::bindings::conversions::root_from_handleobject;
use crate::dom::bindings::conversions::{root_from_object, ToJSValConvertible};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
@ -14,6 +14,7 @@ use crate::dom::bindings::transferable::Transferable;
use crate::dom::blob::{Blob, BlobImpl};
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
use crate::script_runtime::JSContext as SafeJSContext;
use js::glue::CopyJSStructuredCloneData;
use js::glue::DeleteJSAutoStructuredCloneBuffer;
use js::glue::GetLengthOfJSStructuredCloneData;
@ -30,12 +31,15 @@ use js::jsapi::{JSObject, JS_ClearPendingException};
use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter};
use js::jsapi::{JS_ReadBytes, JS_WriteBytes};
use js::jsapi::{JS_ReadUint32Pair, JS_WriteUint32Pair};
use js::jsval::UndefinedValue;
use js::rust::wrappers::{JS_ReadStructuredClone, JS_WriteStructuredClone};
use js::rust::{Handle, HandleValue, MutableHandleValue};
use libc::size_t;
use js::rust::{CustomAutoRooterGuard, HandleValue, MutableHandleValue};
use msg::constellation_msg::MessagePortId;
use script_traits::transferable::MessagePortImpl;
use script_traits::StructuredSerializedData;
use std::collections::HashMap;
use std::os::raw;
use std::ptr;
use std::slice;
// TODO: Should we add Min and Max const to https://github.com/servo/rust-mozjs/blob/master/src/consts.rs?
// TODO: Determine for sure which value Min and Max should have.
@ -129,19 +133,24 @@ impl StructuredCloneReader {
unsafe fn read_blob(
cx: *mut JSContext,
r: *mut JSStructuredCloneReader,
sc_holder: &mut StructuredCloneHolder,
sc_holder: &mut StructuredDataHolder,
) -> *mut JSObject {
let structured_reader = StructuredCloneReader { r: r };
let blob_buffer = structured_reader.read_bytes();
let type_str = structured_reader.read_str();
let target_global = GlobalScope::from_context(cx);
let blob = Blob::new(
let read_blob = Blob::new(
&target_global,
BlobImpl::new_from_bytes(blob_buffer),
type_str,
);
let js_object = blob.reflector().get_jsobject().get();
sc_holder.blob = Some(blob);
let js_object = read_blob.reflector().get_jsobject().get();
match sc_holder {
StructuredDataHolder::Read { blob, .. } => {
*blob = Some(read_blob);
},
_ => panic!("Unexpected variant of StructuredDataHolder"),
}
js_object
}
@ -174,7 +183,7 @@ unsafe extern "C" fn read_callback(
"tag should be higher than StructuredCloneTags::Min"
);
if tag == StructuredCloneTags::DomBlob as u32 {
return read_blob(cx, r, &mut *(closure as *mut StructuredCloneHolder));
return read_blob(cx, r, &mut *(closure as *mut StructuredDataHolder));
}
return ptr::null_mut();
}
@ -185,7 +194,7 @@ unsafe extern "C" fn write_callback(
obj: RawHandleObject,
_closure: *mut raw::c_void,
) -> 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 false;
@ -193,39 +202,44 @@ unsafe extern "C" fn write_callback(
unsafe extern "C" fn read_transfer_callback(
cx: *mut JSContext,
r: *mut JSStructuredCloneReader,
_r: *mut JSStructuredCloneReader,
tag: u32,
content: *mut raw::c_void,
_content: *mut raw::c_void,
extra_data: u64,
closure: *mut raw::c_void,
return_object: RawMutableHandleObject,
) -> bool {
if tag == StructuredCloneTags::MessagePort as u32 {
<MessagePort as Transferable>::transfer_receive(cx, r, closure, content, extra_data, return_object)
} else {
false
let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
let owner = GlobalScope::from_context(cx);
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>
unsafe extern "C" fn write_transfer_callback(
_cx: *mut JSContext,
cx: *mut JSContext,
obj: RawHandleObject,
closure: *mut raw::c_void,
tag: *mut u32,
ownership: *mut TransferableOwnership,
content: *mut *mut raw::c_void,
_content: *mut *mut raw::c_void,
extra_data: *mut u64,
) -> bool {
if let Ok(port) = root_from_handleobject::<MessagePort>(Handle::from_raw(obj)) {
if let Some(true) = port.detached() {
return false;
}
if let Ok(port) = root_from_object::<MessagePort>(*obj, cx) {
*tag = StructuredCloneTags::MessagePort as u32;
*ownership = TransferableOwnership::SCTAG_TMO_CUSTOM;
if port.transfer(closure, content, extra_data) {
port.set_detached(true);
let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
if let Ok(data) = port.transfer(&mut sc_holder) {
*extra_data = data;
return true;
}
}
@ -242,10 +256,13 @@ unsafe extern "C" fn free_transfer_callback(
}
unsafe extern "C" fn can_transfer_callback(
_cx: *mut JSContext,
_obj: RawHandleObject,
cx: *mut JSContext,
obj: RawHandleObject,
_closure: *mut raw::c_void,
) -> bool {
if let Ok(_port) = root_from_object::<MessagePort>(*obj, cx) {
return true;
}
false
}
@ -261,27 +278,40 @@ static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredClon
canTransfer: Some(can_transfer_callback),
};
struct StructuredCloneHolder {
/// A data holder for results from, and inputs to, structured-data read/write operations.
/// https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data
pub enum StructuredDataHolder {
Read {
/// A deserialized blob, stored temporarily here to keep it rooted.
blob: Option<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.
pub enum StructuredCloneData {
/// A non-serializable (default) variant
Struct(*mut u64, size_t),
/// A variant that can be serialized
Vector(Vec<u8>),
}
impl StructuredCloneData {
// TODO: should this be unsafe?
/// Writes a structured clone. Returns a `DataClone` error if that fails.
pub fn write(
cx: *mut JSContext,
cx: SafeJSContext,
message: HandleValue,
transfer: HandleValue,
) -> Fallible<StructuredCloneData> {
transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
) -> Fallible<StructuredSerializedData> {
unsafe {
rooted!(in(*cx) let mut val = UndefinedValue());
if let Some(transfer) = transfer {
transfer.to_jsval(*cx, val.handle_mut());
}
let mut sc_holder = StructuredDataHolder::Write(None);
let sc_holder_ptr = &mut sc_holder as *mut _;
let scbuf = NewJSAutoStructuredCloneBuffer(
StructuredCloneScope::DifferentProcess,
&STRUCTURED_CLONE_CALLBACKS,
@ -292,17 +322,17 @@ impl StructuredCloneData {
sharedArrayBuffer_: false,
};
let result = JS_WriteStructuredClone(
cx,
*cx,
message,
scdata,
StructuredCloneScope::DifferentProcess,
policy,
&STRUCTURED_CLONE_CALLBACKS,
ptr::null_mut(),
transfer,
sc_holder_ptr as *mut raw::c_void,
val.handle(),
);
if !result {
JS_ClearPendingException(cx);
JS_ClearPendingException(*cx);
return Err(Error::DataClone);
}
@ -313,32 +343,34 @@ impl StructuredCloneData {
DeleteJSAutoStructuredCloneBuffer(scbuf);
Ok(StructuredCloneData::Vector(data))
let mut port_impls = match sc_holder {
StructuredDataHolder::Write(port_impls) => port_impls,
_ => panic!("Unexpected variant of StructuredDataHolder"),
};
let data = StructuredSerializedData {
serialized: data,
ports: port_impls.take(),
};
Ok(data)
}
}
/// Converts a StructuredCloneData to Vec<u8> for inter-thread sharing
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(
/// Read structured serialized data, possibly containing transferred objects.
/// Returns a vec of rooted transfer-received ports, or an error.
pub fn read(
global: &GlobalScope,
data: *mut u64,
nbytes: size_t,
mut data: StructuredSerializedData,
rval: MutableHandleValue,
) -> bool {
) -> Result<Vec<DomRoot<MessagePort>>, ()> {
let cx = global.get_cx();
let _ac = enter_realm(&*global);
let mut sc_holder = StructuredCloneHolder { blob: None };
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(
@ -347,7 +379,11 @@ impl StructuredCloneData {
);
let scdata = &mut ((*scbuf).data_);
WriteBytesToJSStructuredCloneData(data as *const u8, nbytes, scdata);
WriteBytesToJSStructuredCloneData(
data.serialized.as_mut_ptr() as *const u8,
data.serialized.len(),
scdata,
);
let result = JS_ReadStructuredClone(
*cx,
@ -356,28 +392,29 @@ impl StructuredCloneData {
StructuredCloneScope::DifferentProcess,
rval,
&STRUCTURED_CLONE_CALLBACKS,
sc_holder_ptr as *mut raw::c_void
sc_holder_ptr as *mut raw::c_void,
);
DeleteJSAutoStructuredCloneBuffer(scbuf);
result
}
}
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"),
};
/// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone.
pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) -> bool {
match self {
StructuredCloneData::Vector(mut vec_msg) => {
let nbytes = vec_msg.len();
let data = vec_msg.as_mut_ptr() as *mut u64;
StructuredCloneData::read_clone(global, data, nbytes, rval)
}
StructuredCloneData::Struct(data, nbytes) => {
StructuredCloneData::read_clone(global, data, nbytes, rval)
}
}
}
}
// Any transfer-received port-impls should have been taken out.
assert!(port_impls.is_none());
unsafe impl Send for StructuredCloneData {}
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 mime::Mime;
use msg::constellation_msg::{
BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId,
BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId, PipelineId,
TopLevelBrowsingContextId,
};
use net_traits::filemanager_thread::RelativePos;
use net_traits::image::base::{Image, ImageMetadata};
@ -93,6 +94,7 @@ use profile_traits::mem::ProfilerChan as MemProfilerChan;
use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_layout_interface::rpc::LayoutRPC;
use script_layout_interface::OpaqueStyleAndLayoutData;
use script_traits::transferable::MessagePortImpl;
use script_traits::DrawAPaintImageResult;
use script_traits::{DocumentActivity, ScriptToConstellationChan, TimerEventId, TimerSource};
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
@ -154,6 +156,11 @@ pub unsafe trait JSTraceable {
unsafe_no_jsmanaged_fields!(Box<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!(&'static Encoding);

View file

@ -4,26 +4,19 @@
//! Trait representing the concept of [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;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::structuredclone::StructuredDataHolder;
use crate::dom::globalscope::GlobalScope;
use js::jsapi::MutableHandleObject;
pub trait Transferable: DomObject {
fn transfer(
&self,
closure: *mut raw::c_void,
content: *mut *mut raw::c_void,
extra_data: *mut u64,
) -> bool;
fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()>;
fn transfer_receive(
cx: *mut JSContext,
r: *mut JSStructuredCloneReader,
closure: *mut raw::c_void,
content: *mut raw::c_void,
owner: &DomRoot<GlobalScope>,
sc_holder: &mut StructuredDataHolder,
extra_data: u64,
return_object: MutableHandleObject,
) -> bool;
fn detached(&self) -> Option<bool> { None }
fn set_detached(&self, _value: bool) { }
fn transferable(&self) -> bool { false }
) -> Result<(), ()>;
}

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::error::throw_invalid_this;
use crate::dom::bindings::inheritance::TopTypeId;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::trace_object;
use crate::dom::messageport::MessagePort;
use crate::dom::windowproxy;
use crate::script_runtime::JSContext as SafeJSContext;
use js::conversions::ToJSValConvertible;
use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper};
use js::glue::{GetCrossCompartmentWrapper, JS_GetReservedSlot, WrapperNew};
use js::glue::{UnwrapObjectDynamic, RUST_JSID_TO_INT, RUST_JSID_TO_STRING};
@ -22,7 +25,7 @@ use js::jsapi::HandleId as RawHandleId;
use js::jsapi::HandleObject as RawHandleObject;
use js::jsapi::MutableHandleObject as RawMutableHandleObject;
use js::jsapi::{AutoIdVector, CallArgs, DOMCallbacks, GetNonCCWObjectGlobal};
use js::jsapi::{Heap, JSAutoRealm, JSContext};
use js::jsapi::{Heap, JSAutoRealm, JSContext, JS_FreezeObject};
use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks};
use js::jsapi::{JS_EnumerateStandardClasses, JS_GetLatin1StringCharsAndLength};
use js::jsapi::{JS_IsExceptionPending, JS_IsGlobalObject};
@ -117,6 +120,19 @@ impl Clone for DOMJSClass {
}
unsafe impl Sync for DOMJSClass {}
/// Returns a JSVal representing a frozen array of ports
pub fn message_ports_to_frozen_array(
message_ports: &[DomRoot<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.
/// Fails if `global` is not a DOM global object.
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::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
use crate::dom::bindings::error::{ErrorInfo, ErrorResult};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventtarget::EventTarget;
@ -36,10 +38,10 @@ use devtools_traits::DevtoolScriptControlMsg;
use dom_struct::dom_struct;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use js::jsapi::JSContext;
use js::jsapi::JS_AddInterruptCallback;
use js::jsapi::{Heap, JSContext, JSObject};
use js::jsval::UndefinedValue;
use js::rust::HandleValue;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId};
use net_traits::image_cache::ImageCache;
use net_traits::request::{CredentialsMode, Destination, ParserMetadata};
@ -467,15 +469,19 @@ impl DedicatedWorkerGlobalScope {
let target = self.upcast();
let _ac = enter_realm(self);
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,
scope.upcast(),
message.handle(),
Some(&origin),
Some(&origin.ascii_serialization()),
None,
vec![],
ports,
);
} else {
MessageEvent::dispatch_error(target, scope.upcast());
}
},
WorkerScriptMsg::Common(msg) => {
self.upcast::<WorkerGlobalScope>().process_event(msg);
@ -554,6 +560,32 @@ impl DedicatedWorkerGlobalScope {
))
.unwrap();
}
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn post_message_impl(
&self,
cx: SafeJSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<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)]
@ -567,26 +599,34 @@ unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool {
}
impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope {
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn PostMessage(&self, cx: SafeJSContext, message: HandleValue) -> ErrorResult {
rooted!(in(*cx) let transfer = UndefinedValue());
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
let worker = self.worker.borrow().as_ref().unwrap().clone();
let pipeline_id = self.global().pipeline_id();
let origin = self.global().origin().immutable().ascii_serialization();
let task = Box::new(task!(post_worker_message: move || {
Worker::handle_message(worker, origin, data);
}));
// TODO: Change this task source to a new `unshipped-port-message-queue` task source
self.parent_sender
.send(CommonScriptMsg::Task(
WorkerEvent,
task,
Some(pipeline_id),
TaskSourceName::DOMManipulation,
))
.unwrap();
Ok(())
/// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn PostMessage(
&self,
cx: SafeJSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
self.post_message_impl(cx, message, transfer)
}
/// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn PostMessage_(
&self,
cx: SafeJSContext,
message: HandleValue,
options: RootedTraceableBox<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)
}
// 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::DissimilarOriginWindowMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowPostMessageOptions;
use crate::dom::bindings::error::{Error, ErrorResult};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::str::USVString;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::dissimilaroriginlocation::DissimilarOriginLocation;
use crate::dom::globalscope::GlobalScope;
use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
use ipc_channel::ipc;
use js::jsapi::{Heap, JSObject};
use js::jsval::{JSVal, UndefinedValue};
use js::rust::HandleValue;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use msg::constellation_msg::PipelineId;
use script_traits::ScriptMsg;
use servo_url::ImmutableOrigin;
use script_traits::{ScriptMsg, StructuredSerializedData};
use servo_url::ServoUrl;
/// Represents a dissimilar-origin `Window` that exists in another script thread.
@ -133,29 +135,42 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
false
}
// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult {
// Step 3-5.
let origin = match &origin[..] {
"*" => None,
"/" => {
// TODO: Should be the origin of the incumbent settings object.
None
},
url => match ServoUrl::parse(&url) {
Ok(url) => Some(url.origin()),
Err(_) => return Err(Error::Syntax),
},
};
/// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
fn PostMessage(
&self,
cx: JSContext,
message: HandleValue,
target_origin: USVString,
mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
) -> ErrorResult {
if transfer.is_some() {
let mut rooted = CustomAutoRooter::new(transfer.take().unwrap());
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
self.post_message_impl(&target_origin, cx, message, transfer)
} else {
self.post_message_impl(&target_origin, cx, message, None)
}
}
// Step 1-2, 6-8.
// TODO(#12717): Should implement the `transfer` argument.
rooted!(in(*cx) let transfer = UndefinedValue());
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
/// https://html.spec.whatwg.org/multipage/#dom-window-postmessage-options
fn PostMessage_(
&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));
// Step 9.
self.post_message(origin, data);
Ok(())
self.post_message_impl(&options.targetOrigin, cx, message, transfer)
}
// https://html.spec.whatwg.org/multipage/#dom-opener
@ -187,17 +202,54 @@ impl DissimilarOriginWindowMethods for 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() {
None => return warn!("postMessage called with no incumbent global"),
None => panic!("postMessage called with no incumbent global"),
Some(incumbent) => incumbent,
};
let msg = ScriptMsg::PostMessage {
target: self.window_proxy.browsing_context_id(),
source: incumbent.pipeline_id(),
target_origin: origin,
data: data.move_to_arraybuffer(),
let source_origin = incumbent.origin().immutable().clone();
// Step 3-5.
let target_origin = match target_origin.0[..].as_ref() {
"*" => None,
"/" => Some(source_origin.clone()),
url => match ServoUrl::parse(&url) {
Ok(url) => Some(url.origin().clone()),
Err(_) => return Err(Error::Syntax),
},
};
let msg = ScriptMsg::PostMessage {
target,
source: incumbent.pipeline_id(),
source_origin,
target_origin,
data: data,
};
// Step 8
let _ = incumbent.script_to_constellation_chan().send(msg);
Ok(())
}
}

View file

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

View file

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

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::error::{report_pending_exception, ErrorInfo};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::weakref::DOMTracker;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
use crate::dom::crypto::Crypto;
use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
use crate::dom::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventsource::EventSource;
use crate::dom::eventtarget::EventTarget;
use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::MessagePort;
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
use crate::dom::performance::Performance;
use crate::dom::window::Window;
@ -36,34 +40,40 @@ use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
use crate::task_source::port_message::PortMessageQueue;
use crate::task_source::remote_event::RemoteEventTaskSource;
use crate::task_source::websocket::WebsocketTaskSource;
use crate::task_source::TaskSource;
use crate::task_source::TaskSourceName;
use crate::timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle};
use crate::timers::{OneshotTimers, TimerCallback};
use content_security_policy::CspList;
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use dom_struct::dom_struct;
use ipc_channel::ipc::IpcSender;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::glue::{IsWrapper, UnwrapObjectDynamic};
use js::jsapi::JSObject;
use js::jsapi::{CurrentGlobalOrNull, GetNonCCWObjectGlobal};
use js::jsapi::{HandleObject, Heap};
use js::jsapi::{JSAutoRealm, JSContext};
use js::jsval::UndefinedValue;
use js::panic::maybe_resume_unwind;
use js::rust::wrappers::EvaluateUtf8;
use js::rust::{get_object_class, CompileOptionsWrapper, ParentRuntime, Runtime};
use js::rust::{HandleValue, MutableHandleValue};
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::{MessagePortId, MessagePortRouterId, PipelineId};
use net_traits::image_cache::ImageCache;
use net_traits::{CoreResourceThread, IpcSend, ResourceThreads};
use profile_traits::{mem as profile_mem, time as profile_time};
use script_traits::{MsDuration, ScriptToConstellationChan, TimerEvent};
use script_traits::transferable::MessagePortImpl;
use script_traits::{
MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg, ScriptToConstellationChan, TimerEvent,
};
use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource};
use servo_url::{MutableOrigin, ServoUrl};
use std::borrow::Cow;
use std::cell::Cell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::collections::{HashMap, VecDeque};
use std::ffi::CString;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
@ -85,6 +95,9 @@ pub struct GlobalScope {
crypto: MutNullableDom<Crypto>,
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: PipelineId,
@ -168,6 +181,77 @@ pub struct GlobalScope {
user_agent: Cow<'static, str>,
}
/// A wrapper for glue-code between the ipc router and the event-loop.
struct MessageListener {
canceller: TaskCanceller,
task_source: PortMessageQueue,
context: Trusted<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 {
pub fn new_inherited(
pipeline_id: PipelineId,
@ -184,6 +268,7 @@ impl GlobalScope {
user_agent: Cow<'static, str>,
) -> Self {
Self {
message_port_state: DomRefCell::new(MessagePortState::UnManaged),
eventtarget: EventTarget::new_inherited(),
crypto: Default::default(),
next_worker_id: Cell::new(WorkerId(0)),
@ -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>) {
self.list_auto_close_worker
.borrow_mut()
@ -550,7 +1026,7 @@ impl GlobalScope {
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
return worker.websocket_task_source();
}
unreachable!()
unreachable!();
}
/// 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::root::{Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::structuredclone;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
@ -27,7 +27,7 @@ use msg::constellation_msg::{HistoryStateId, TraversalDirection};
use net_traits::{CoreResourceMsg, IpcSend};
use profile_traits::ipc;
use profile_traits::ipc::channel;
use script_traits::ScriptMsg;
use script_traits::{ScriptMsg, StructuredSerializedData};
use servo_url::ServoUrl;
use std::cell::Cell;
@ -115,11 +115,16 @@ impl History {
};
match serialized_data {
Some(serialized_data) => {
Some(data) => {
let data = StructuredSerializedData {
serialized: data,
ports: None,
};
let global_scope = self.window.upcast::<GlobalScope>();
rooted!(in(*global_scope.get_cx()) let mut state = UndefinedValue());
StructuredCloneData::Vector(serialized_data)
.read(&global_scope, state.handle_mut());
if let Err(_) = structuredclone::read(&global_scope, data, state.handle_mut()) {
warn!("Error reading structuredclone data");
}
self.state.set(state.get());
},
None => {
@ -185,8 +190,7 @@ impl History {
// TODO: Step 4
// Step 5
rooted!(in(cx) let transfer = UndefinedValue());
let serialized_data = StructuredCloneData::write(*cx, data, transfer.handle())?.move_to_arraybuffer();
let serialized_data = structuredclone::write(cx, data, None)?;
let new_url: ServoUrl = match url {
// Step 6
@ -255,7 +259,7 @@ impl History {
};
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
@ -267,7 +271,9 @@ impl History {
// Step 11
let global_scope = self.window.upcast::<GlobalScope>();
rooted!(in(*cx) let mut state = UndefinedValue());
StructuredCloneData::Vector(serialized_data).read(&global_scope, state.handle_mut());
if let Err(_) = structuredclone::read(&global_scope, serialized_data, state.handle_mut()) {
warn!("Error reading structuredclone data");
}
// Step 12
self.state.set(state.get());

View file

@ -4,7 +4,7 @@
use crate::dom::bindings::codegen::Bindings::MessageChannelBinding::{MessageChannelMethods, Wrap};
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::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
@ -28,18 +28,24 @@ impl MessageChannel {
// Step 2
let port2 = MessagePort::new(&incumbent);
incumbent.track_message_port(&*port1, None);
incumbent.track_message_port(&*port2, None);
// Step 3
port1.entangle(&port2);
incumbent.entangle_ports(
port1.message_port_id().clone(),
port2.message_port_id().clone(),
);
// Steps 4-6
let channel = reflect_dom_object(Box::new(
MessageChannel {
let channel = reflect_dom_object(
Box::new(MessageChannel {
reflector_: Reflector::new(),
port1: Dom::from_ref(&port1),
port2: Dom::from_ref(&port2),
}),
global,
Wrap
Wrap,
);
// 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::str::DOMString;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::utils::message_ports_to_frozen_array;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
@ -18,10 +19,8 @@ use crate::dom::messageport::MessagePort;
use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
use js::jsapi::{Heap, JS_FreezeObject, JSContext, JSObject};
use js::jsapi::HandleObject as RawHandleObject;
use js::jsval::{JSVal, UndefinedValue};
use js::jsapi::{Heap, JSObject};
use js::jsval::JSVal;
use js::rust::HandleValue;
use servo_atoms::Atom;
use std::ptr::NonNull;
@ -108,7 +107,7 @@ impl MessageEvent {
init.origin.clone(),
source.as_ref().map(|source| &**source),
init.lastEventId.clone(),
init.ports.clone().unwrap_or(vec![])
init.ports.clone().unwrap_or(vec![]),
);
Ok(ev)
}
@ -136,6 +135,26 @@ impl MessageEvent {
);
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 {
@ -166,14 +185,8 @@ impl MessageEventMethods for MessageEvent {
self.event.IsTrusted()
}
#[allow(unsafe_code)]
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-ports>
unsafe fn Ports(&self, cx: *mut JSContext) -> JSVal {
rooted!(in(cx) let mut ports = UndefinedValue());
self.ports.to_jsval(cx, ports.handle_mut());
rooted!(in(cx) let obj = ports.to_object());
JS_FreezeObject(cx, RawHandleObject::from(obj.handle()));
*ports
fn Ports(&self, cx: JSContext) -> JSVal {
message_ports_to_frozen_array(self.ports.as_slice(), cx)
}
}

View file

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

View file

@ -4,6 +4,7 @@
use crate::dom::abstractworker::SimpleWorkerErrorHandler;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
use crate::dom::bindings::codegen::Bindings::ServiceWorkerBinding::{
ServiceWorkerMethods, ServiceWorkerState, Wrap,
};
@ -13,15 +14,15 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::USVString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::JSContext;
use crate::task::TaskOnce;
use dom_struct::dom_struct;
use js::jsapi::JSContext;
use js::jsval::UndefinedValue;
use js::rust::HandleValue;
use js::jsapi::{Heap, JSObject};
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use script_traits::{DOMMessage, ScriptMsg};
use servo_url::ServoUrl;
use std::cell::Cell;
@ -79,6 +80,34 @@ impl ServiceWorker {
pub fn get_script_url(&self) -> ServoUrl {
ServoUrl::parse(&self.script_url.borrow().clone()).unwrap()
}
/// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
fn post_message_impl(
&self,
cx: JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<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 {
@ -92,27 +121,34 @@ impl ServiceWorkerMethods for ServiceWorker {
USVString(self.script_url.borrow().clone())
}
// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult {
// Step 1
if let ServiceWorkerState::Redundant = self.state.get() {
return Err(Error::InvalidState);
/// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
fn PostMessage(
&self,
cx: JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
self.post_message_impl(cx, message, transfer)
}
// Step 7
rooted!(in(*cx) let transfer = UndefinedValue());
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
let msg_vec = DOMMessage {
origin: self.global().origin().immutable().ascii_serialization(),
data: data.move_to_arraybuffer(),
};
let _ = self
.global()
.script_to_constellation_chan()
.send(ScriptMsg::ForwardDOMMessage(
msg_vec,
self.scope_url.clone()
));
Ok(())
/// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
fn PostMessage_(
&self,
cx: JSContext,
message: HandleValue,
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)
}
// 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::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone;
use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::extendableevent::ExtendableEvent;
use crate::dom::extendablemessageevent::ExtendableMessageEvent;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageevent::MessageEvent;
use crate::dom::worker::TrustedWorkerAddress;
use crate::dom::workerglobalscope::WorkerGlobalScope;
use crate::fetch::load_whole_resource;
@ -414,8 +416,17 @@ impl ServiceWorkerGlobalScope {
let target = self.upcast();
let _ac = enter_realm(&*scope);
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
assert!(data.read(scope.upcast(), message.handle_mut()));
ExtendableMessageEvent::dispatch_jsval(target, scope.upcast(), message.handle());
if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut())
{
ExtendableMessageEvent::dispatch_jsval(
target,
scope.upcast(),
message.handle(),
ports,
);
} else {
MessageEvent::dispatch_error(target, scope.upcast());
}
},
CommonWorker(WorkerScriptMsg::Common(msg)) => {
self.upcast::<WorkerGlobalScope>().process_event(msg);

View file

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

View file

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

View file

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

View file

@ -8,10 +8,16 @@
[Exposed=(Window,Worker)]
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 close();
// event handlers
attribute EventHandler onmessage;
attribute EventHandler onmessageerror;
};
dictionary PostMessageOptions {
sequence<object> transfer;
};

View file

@ -7,7 +7,8 @@
interface ServiceWorker : EventTarget {
readonly attribute USVString scriptURL;
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
attribute EventHandler onstatechange;

View file

@ -65,6 +65,8 @@
[Throws]
void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/);
[Throws]
void postMessage(any message, optional WindowPostMessageOptions options = {});
// also has obsolete members
};
@ -172,3 +174,8 @@ partial interface Window {
[Pref="css.animations.testing.enabled"]
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 = {});
void terminate();
[Throws] void postMessage(any message/*, sequence<object> transfer*/);
// void postMessage(any message, optional PostMessageOptions options);
[Throws] void postMessage(any message, sequence<object> transfer);
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
attribute EventHandler onmessage;
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::RequestBinding::RequestInit;
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
self, FrameRequestCallback, WindowMethods,
self, FrameRequestCallback, WindowMethods, WindowPostMessageOptions,
};
use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString;
use crate::dom::bindings::conversions::ToJSValConvertible;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable;
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::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::utils::{GlobalStaticData, WindowProxyHandler};
use crate::dom::bindings::weakref::DOMTracker;
@ -44,7 +43,6 @@ use crate::dom::location::Location;
use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
use crate::dom::mediaquerylistevent::MediaQueryListEvent;
use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::TRANSFERRED_MESSAGE_PORTS;
use crate::dom::navigator::Navigator;
use crate::dom::node::{document_from_node, from_untrusted_node_address, Node, NodeDamage};
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 ipc_channel::ipc::{channel, IpcSender};
use ipc_channel::router::ROUTER;
use js::jsapi::Heap;
use js::jsapi::JSAutoRealm;
use js::jsapi::JSObject;
use js::jsapi::JSPROP_ENUMERATE;
@ -88,7 +87,7 @@ use js::jsapi::{GCReason, JS_GC};
use js::jsval::UndefinedValue;
use js::jsval::{JSVal, NullValue};
use js::rust::wrappers::JS_DefineProperty;
use js::rust::{CustomAutoRooterGuard, HandleValue};
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use media::WindowGLContext;
use msg::constellation_msg::PipelineId;
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_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use script_traits::{ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData};
use script_traits::{ScriptMsg, ScriptToConstellationChan, ScrollState, TimerEvent, TimerEventId};
use script_traits::{
ScriptMsg, ScriptToConstellationChan, ScrollState, StructuredSerializedData, TimerEvent,
TimerEventId,
};
use script_traits::{TimerSchedulerMsg, WindowSizeData, WindowSizeType};
use selectors::attr::CaseSensitivity;
use servo_geometry::{f32_rect_to_au_rect, MaxRect};
@ -976,31 +978,53 @@ impl WindowMethods for Window {
&self,
cx: JSContext,
message: HandleValue,
origin: USVString,
transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
target_origin: USVString,
mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
) -> ErrorResult {
let source_global = GlobalScope::incumbent().expect("no incumbent global??");
let source = source_global.as_window();
let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
let source = incumbent.as_window();
let source_origin = source.Document().origin().immutable().clone();
// Step 3-5.
let origin = match &origin.0[..] {
"*" => None,
"/" => Some(source.Document().origin().immutable().clone()),
url => match ServoUrl::parse(&url) {
Ok(url) => Some(url.origin().clone()),
Err(_) => return Err(Error::Syntax),
},
};
if transfer.is_some() {
let mut rooted = CustomAutoRooter::new(transfer.take().unwrap());
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
} else {
self.post_message_impl(&target_origin, source_origin, source, cx, message, None)
}
}
// Step 1-2, 6-8.
rooted!(in(*cx) let mut val = UndefinedValue());
(*transfer).as_ref().unwrap_or(&Vec::new()).to_jsval(*cx, val.handle_mut());
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
fn PostMessage_(
&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.
self.post_message(origin, &*source.window_proxy(), data);
Ok(())
let source_origin = source.Document().origin().immutable().clone();
self.post_message_impl(
&options.targetOrigin,
source_origin,
source,
cx,
message,
transfer,
)
}
// https://html.spec.whatwg.org/multipage/#dom-window-captureevents
@ -1298,6 +1322,34 @@ impl WindowMethods for Window {
}
impl Window {
/// https://html.spec.whatwg.org/multipage/#window-post-message-steps
fn post_message_impl(
&self,
target_origin: &USVString,
source_origin: ImmutableOrigin,
source: &Window,
cx: JSContext,
message: HandleValue,
transfer: Option<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
pub fn paint_worklet(&self) -> DomRoot<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.
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
// https://github.com/servo/servo/issues/15318
if let Some(custom_elements) = self.custom_element_registry.get() {
@ -2345,8 +2400,9 @@ impl Window {
pub fn post_message(
&self,
target_origin: Option<ImmutableOrigin>,
source_origin: ImmutableOrigin,
source: &WindowProxy,
serialize_with_transfer_result: StructuredCloneData,
data: StructuredSerializedData,
) {
let this = Trusted::new(self);
let source = Trusted::new(source);
@ -2367,26 +2423,23 @@ impl Window {
let obj = this.reflector().get_jsobject();
let _ac = JSAutoRealm::new(*cx, obj.get());
rooted!(in(*cx) let mut message_clone = UndefinedValue());
assert!(serialize_with_transfer_result.read(
this.upcast(),
message_clone.handle_mut(),
));
// Step 7.6.
let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| {
mem::replace(&mut *list.borrow_mut(), vec![])
});
// Step 7.7.
// TODO(#12719): Set the other attributes.
if let Ok(ports) = structuredclone::read(this.upcast(), data, message_clone.handle_mut()) {
// Step 7.6, 7.7
MessageEvent::dispatch_jsval(
this.upcast(),
this.upcast(),
message_clone.handle(),
Some(&document.origin().immutable().ascii_serialization()),
Some(&source_origin.ascii_serialization()),
Some(&*source),
new_ports,
ports,
);
} else {
// Step 4, fire messageerror.
MessageEvent::dispatch_error(
this.upcast(),
this.upcast(),
);
}
});
// FIXME(nox): Why are errors silenced here?
// TODO(#12718): Use the "posted message task source".

View file

@ -5,6 +5,7 @@
use crate::compartments::enter_realm;
use crate::dom::abstractworker::SimpleWorkerErrorHandler;
use crate::dom::abstractworker::WorkerScriptMsg;
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
use crate::dom::bindings::codegen::Bindings::WorkerBinding;
use crate::dom::bindings::codegen::Bindings::WorkerBinding::{WorkerMethods, WorkerOptions};
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
@ -13,7 +14,8 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::USVString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::dedicatedworkerglobalscope::{
DedicatedWorkerGlobalScope, DedicatedWorkerScriptMsg,
};
@ -27,10 +29,10 @@ use crossbeam_channel::{unbounded, Sender};
use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg};
use dom_struct::dom_struct;
use ipc_channel::ipc;
use js::jsapi::JS_RequestInterruptCallback;
use js::jsapi::{Heap, JSObject, JS_RequestInterruptCallback};
use js::jsval::UndefinedValue;
use js::rust::HandleValue;
use script_traits::WorkerScriptLoadOrigin;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use script_traits::{StructuredSerializedData, WorkerScriptLoadOrigin};
use std::cell::Cell;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
@ -140,7 +142,7 @@ impl Worker {
pub fn handle_message(
address: TrustedWorkerAddress,
origin: String,
data: StructuredCloneData,
data: StructuredSerializedData,
) {
let worker = address.root();
@ -152,21 +154,34 @@ impl Worker {
let target = worker.upcast();
let _ac = enter_realm(target);
rooted!(in(*global.get_cx()) let mut message = UndefinedValue());
assert!(data.read(&global, message.handle_mut()));
MessageEvent::dispatch_jsval(target, &global, message.handle(), Some(&origin), None, vec![]);
if let Ok(ports) = structuredclone::read(&global, data, message.handle_mut()) {
MessageEvent::dispatch_jsval(
target,
&global,
message.handle(),
Some(&origin),
None,
ports,
);
} else {
// Step 4 of the "port post message steps" of the implicit messageport, fire messageerror.
MessageEvent::dispatch_error(target, &global);
}
}
pub fn dispatch_simple_error(address: TrustedWorkerAddress) {
let worker = address.root();
worker.upcast().fire_event(atom!("error"));
}
}
impl WorkerMethods for Worker {
// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult {
rooted!(in(*cx) let transfer = UndefinedValue());
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
/// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
fn post_message_impl(
&self,
cx: JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
let data = structuredclone::write(cx, message, Some(transfer))?;
let address = Trusted::new(self);
// NOTE: step 9 of https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage
@ -174,12 +189,44 @@ impl WorkerMethods for Worker {
let _ = self.sender.send(DedicatedWorkerScriptMsg::CommonWorker(
address,
WorkerScriptMsg::DOMMessage {
origin: self.global().origin().immutable().ascii_serialization(),
origin: self.global().origin().immutable().clone(),
data,
},
));
Ok(())
}
}
impl WorkerMethods for Worker {
/// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
fn PostMessage(
&self,
cx: JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<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)]
// 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::{Dom, DomRoot, MutNullableDom, RootCollection};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone::StructuredCloneData;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::bindings::utils::WRAP_CALLBACKS;
use crate::dom::customelementregistry::{
@ -133,6 +132,7 @@ use script_traits::CompositorEvent::{
CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent,
WheelEvent,
};
use script_traits::StructuredSerializedData;
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{
DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement,
@ -1589,6 +1589,11 @@ impl ScriptThread {
continue;
}
let window = document.window();
window
.upcast::<GlobalScope>()
.perform_a_message_port_garbage_collection_checkpoint();
let pending_reflows = window.get_pending_reflow_count();
if pending_reflows > 0 {
window.reflow(ReflowGoal::Full, ReflowReason::ImageLoaded);
@ -1867,12 +1872,14 @@ impl ScriptThread {
source: source_pipeline_id,
source_browsing_context,
target_origin: origin,
source_origin,
data,
} => self.handle_post_message_msg(
target_pipeline_id,
source_pipeline_id,
source_browsing_context,
origin,
source_origin,
data,
),
ConstellationControlMsg::UpdatePipelineId(
@ -2525,7 +2532,8 @@ impl ScriptThread {
source_pipeline_id: PipelineId,
source_browsing_context: TopLevelBrowsingContextId,
origin: Option<ImmutableOrigin>,
data: Vec<u8>,
source_origin: ImmutableOrigin,
data: StructuredSerializedData,
) {
match { self.documents.borrow().find_window(pipeline_id) } {
None => return warn!("postMessage after target pipeline {} closed.", pipeline_id),
@ -2547,7 +2555,7 @@ impl ScriptThread {
Some(source) => source,
};
// FIXME(#22512): enqueues a task; unnecessary delay.
window.post_message(origin, &*source, StructuredCloneData::Vector(data))
window.post_message(origin, source_origin, &*source, data)
},
}
}

View file

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

View file

@ -9,7 +9,7 @@ use msg::constellation_msg::PipelineId;
use std::fmt;
#[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 {
fn clone(&self) -> PortMessageQueue {
@ -26,11 +26,7 @@ impl fmt::Debug for PortMessageQueue {
impl TaskSource for PortMessageQueue {
const NAME: TaskSourceName = TaskSourceName::PortMessage;
fn queue_with_canceller<T>(
&self,
task: T,
canceller: &TaskCanceller,
) -> Result<(), ()>
fn queue_with_canceller<T>(&self, task: T, canceller: &TaskCanceller) -> Result<(), ()>
where
T: TaskOnce + 'static,
{

View file

@ -17,8 +17,10 @@ extern crate malloc_size_of_derive;
extern crate serde;
mod script_msg;
pub mod transferable;
pub mod webdriver_msg;
use crate::transferable::MessagePortImpl;
use crate::webdriver_msg::{LoadStatus, WebDriverScriptCommand};
use bluetooth_traits::BluetoothRequest;
use canvas_traits::webgl::WebGLPipeline;
@ -36,7 +38,7 @@ use keyboard_types::{CompositionEvent, KeyboardEvent};
use libc::c_void;
use media::WindowGLContext;
use msg::constellation_msg::BackgroundHangMonitorRegister;
use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId};
use msg::constellation_msg::{BrowsingContextId, HistoryStateId, MessagePortId, PipelineId};
use msg::constellation_msg::{PipelineNamespaceId, TopLevelBrowsingContextId, TraversalDirection};
use net_traits::image::base::Image;
use net_traits::image_cache::ImageCache;
@ -51,7 +53,7 @@ use servo_atoms::Atom;
use servo_url::ImmutableOrigin;
use servo_url::ServoUrl;
use std::borrow::Cow;
use std::collections::HashMap;
use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
@ -324,8 +326,11 @@ pub enum ConstellationControlMsg {
source_browsing_context: TopLevelBrowsingContextId,
/// The expected origin of the target.
target_origin: Option<ImmutableOrigin>,
/// The source origin of the message.
/// https://html.spec.whatwg.org/multipage/#dom-messageevent-origin
source_origin: ImmutableOrigin,
/// The data to be posted.
data: Vec<u8>,
data: StructuredSerializedData,
},
/// Updates the current pipeline ID of a given iframe.
/// First PipelineId is for the parent, second is the new PipelineId for the frame.
@ -1013,3 +1018,33 @@ impl ScriptToConstellationChan {
self.sender.send((self.pipeline_id, msg))
}
}
/// A data-holder for serialized data and transferred objects.
/// <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::LayoutControlMsg;
use crate::LoadData;
use crate::MessagePortMsg;
use crate::PortMessageTask;
use crate::StructuredSerializedData;
use crate::WindowSizeType;
use crate::WorkerGlobalScopeInit;
use crate::WorkerScriptLoadOrigin;
@ -18,7 +21,9 @@ use euclid::default::Size2D as UntypedSize2D;
use euclid::Size2D;
use gfx_traits::Epoch;
use ipc_channel::ipc::{IpcReceiver, IpcSender};
use msg::constellation_msg::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId};
use msg::constellation_msg::{
BrowsingContextId, MessagePortId, MessagePortRouterId, PipelineId, TopLevelBrowsingContextId,
};
use msg::constellation_msg::{HistoryStateId, TraversalDirection};
use net_traits::request::RequestBuilder;
use net_traits::storage_thread::StorageType;
@ -109,6 +114,20 @@ pub enum HistoryEntryReplacement {
/// Messages from the script to the constellation.
#[derive(Deserialize, Serialize)]
pub enum ScriptMsg {
/// A new message-port was created or transferred, with corresponding control-sender.
NewMessagePort(MessagePortRouterId, MessagePortId),
/// A global has started managing message-ports
NewMessagePortRouter(MessagePortRouterId, IpcSender<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.
ForwardToEmbedder(EmbedderMsg),
/// Requests are sent to constellation and fetches are checked manually
@ -166,8 +185,11 @@ pub enum ScriptMsg {
source: PipelineId,
/// The expected origin of the target.
target_origin: Option<ImmutableOrigin>,
/// The source origin of the message.
/// https://html.spec.whatwg.org/multipage/#dom-messageevent-origin
source_origin: ImmutableOrigin,
/// The data to be posted.
data: Vec<u8>,
data: StructuredSerializedData,
},
/// Inform the constellation that a fragment was navigated to and whether or not it was a replacement navigation.
NavigatedToFragment(ServoUrl, HistoryEntryReplacement),
@ -226,6 +248,13 @@ impl fmt::Debug for ScriptMsg {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
use self::ScriptMsg::*;
let variant = match *self {
NewMessagePortRouter(..) => "NewMessagePortRouter",
RemoveMessagePortRouter(..) => "RemoveMessagePortRouter",
NewMessagePort(..) => "NewMessagePort",
RerouteMessagePort(..) => "RerouteMessagePort",
RemoveMessagePort(..) => "RemoveMessagePort",
MessagePortShipped(..) => "MessagePortShipped",
EntanglePorts(..) => "EntanglePorts",
ForwardToEmbedder(..) => "ForwardToEmbedder",
InitiateNavigateRequest(..) => "InitiateNavigateRequest",
BroadcastStorageEvent(..) => "BroadcastStorageEvent",
@ -283,12 +312,12 @@ pub struct ScopeThings {
}
/// Message that gets passed to service worker scope on postMessage
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize)]
pub struct DOMMessage {
/// The origin of the message
pub origin: String,
pub origin: ImmutableOrigin,
/// 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

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]
[Passing a FrozenArray as the blobParts array should work (FrozenArray<MessagePort>).]
expected: FAIL
[options properties should be accessed in lexicographic order.]
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",
@ -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",
@ -712918,6 +712974,22 @@
"4b00e68d49ef9fc85e92a5526ff76bd92259d4d9",
"testharness"
],
"webmessaging/Channel_postMessage_transfer_xsite_incoming_messages.window.js": [
"23237ae1555e67dafcb170ce8fccab4c966f7a2e",
"testharness"
],
"webmessaging/Channel_postMessage_with_transfer_entangled.any.js": [
"2226b278440346290066e622fe6abbaab2b93bd2",
"testharness"
],
"webmessaging/Channel_postMessage_with_transfer_incoming_messages.any.js": [
"fe2e96220d34d88127dc596eee70d183f7debd18",
"testharness"
],
"webmessaging/Channel_postMessage_with_transfer_outgoing_messages.any.js": [
"aa80b7589cffa58da6f1d790e7cdbeb95642c6e3",
"testharness"
],
"webmessaging/META.yml": [
"95d5071171b5a20cc14a414c97c9eae2f525f43f",
"support"
@ -713086,6 +713158,10 @@
"cf2b8eb4c11b0b824a47677079c2d1b8837b3802",
"testharness"
],
"webmessaging/postMessage_MessagePorts_xsite.sub.window.js": [
"ca1e510edaa09f41350426e143923bb15e6df82b",
"testharness"
],
"webmessaging/postMessage_arrays.sub.htm": [
"41e4a75eda616196ab3546943a0913785cbe69be",
"testharness"

View file

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

View file

@ -1,138 +1,17 @@
[remote-origin.htm]
bug: https://github.com/servo/servo/issues/21563
expected: TIMEOUT
[Allow origin: *]
expected: TIMEOUT
[Allow origin: _*__]
expected: TIMEOUT
[Allow origin: [tab\]*]
expected: TIMEOUT
[Allow origin: http://www1.web-platform.test:8000]
expected: TIMEOUT
[Allow origin: _http://www1.web-platform.test:8000]
expected: TIMEOUT
expected: FAIL
[Allow origin: _http://www1.web-platform.test:8000___[tab\]_]
expected: TIMEOUT
[Allow origin: [tab\]http://www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: http://web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: //www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: ://www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: ftp://www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: http:://www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: http:/www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: http:www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: http://www1.web-platform.test:8000?]
expected: TIMEOUT
expected: FAIL
[Disallow origin: http://www1.web-platform.test:8000/]
expected: TIMEOUT
[Disallow origin: http://www1.web-platform.test:8000_/]
expected: TIMEOUT
expected: FAIL
[Disallow origin: http://www1.web-platform.test:8000#]
expected: TIMEOUT
[Disallow origin: http://www1.web-platform.test:8000%23]
expected: TIMEOUT
[Disallow origin: http://www1.web-platform.test:8000:80]
expected: TIMEOUT
[Disallow origin: http://www1.web-platform.test:8000,_*]
expected: TIMEOUT
[Disallow origin: http://www1.web-platform.test:8000\\0]
expected: TIMEOUT
[Disallow origin: HTTP://WWW1.WEB-PLATFORM.TEST:8000]
expected: TIMEOUT
expected: FAIL
[Disallow origin: HTTP://www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: http://WWW1.WEB-PLATFORM.TEST:8000]
expected: TIMEOUT
[Disallow origin: -]
expected: TIMEOUT
[Disallow origin: **]
expected: TIMEOUT
[Disallow origin: \\0*]
expected: TIMEOUT
[Disallow origin: *\\0]
expected: TIMEOUT
[Disallow origin: '*']
expected: TIMEOUT
[Disallow origin: "*"]
expected: TIMEOUT
[Disallow origin: *_*]
expected: TIMEOUT
[Disallow origin: *http://*]
expected: TIMEOUT
[Disallow origin: *http://www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: *_http://www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: *,_http://www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: \\0http://www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: null_http://www1.web-platform.test:8000]
expected: TIMEOUT
[Disallow origin: http://example.net]
expected: TIMEOUT
[Disallow origin: null]
expected: TIMEOUT
[Disallow origin: ]
expected: TIMEOUT
[Disallow origin: http://web-platform.test:8000/cors/remote-origin.htm]
expected: TIMEOUT
[Disallow origin: http://web-platform.test:8000/cors/]
expected: TIMEOUT
[Disallow origin: http://www1.web-platform.test:8000/cors/]
expected: TIMEOUT
expected: FAIL

View file

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

View file

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

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

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]
expected: TIMEOUT
[Access allowed if same-origin with no 'document.domain' modification. (Sanity check)]
expected: FAIL
expected: TIMEOUT
[Access is revoked to Window object when we stop being same effective script origin due to document.domain.]
expected: FAIL
expected: NOTRUN
[Access allowed if different-origin but both set document.domain to parent domain.]
expected: FAIL
expected: NOTRUN
[Access is not revoked to random object when we stop being same effective script origin due to document.domain.]
expected: FAIL
expected: NOTRUN
[Access not allowed if different-origin with no 'document.domain' modification. (Sanity check)]
expected: FAIL
expected: NOTRUN
[Access disallowed again if same-origin, both set document-domain to existing value, then one sets to parent.]
expected: FAIL
expected: NOTRUN
[Access is revoked to Location object when we stop being same effective script origin due to document.domain.]
expected: FAIL
expected: NOTRUN
[Access allowed if same-origin and both set document.domain to existing value.]
expected: FAIL
expected: NOTRUN
[Access is not revoked to Document object when we stop being same effective script origin due to document.domain.]
expected: FAIL
expected: NOTRUN
[Access disallowed if same-origin but only one sets document.domain.]
expected: FAIL
expected: NOTRUN
[Access evolves correctly for cross-origin objects when we join up via document.domain and then diverge again.]
expected: FAIL
expected: NOTRUN
[Access evolves correctly for non-cross-origin objects when we join up via document.domain and then diverge again.]
expected: FAIL
expected: NOTRUN

View file

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

View file

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

View file

@ -1,18 +1,9 @@
[no-coop-coep.https.any.worker.html]
[SharedArrayBuffer over MessageChannel without COOP+COEP]
expected: FAIL
[SharedArrayBuffer over BroadcastChannel without COOP+COEP]
expected: FAIL
[no-coop-coep.https.any.html]
[SharedArrayBuffer over MessageChannel without COOP+COEP]
expected: FAIL
[SharedArrayBuffer over postMessage() without COOP+COEP]
expected: FAIL
[SharedArrayBuffer over BroadcastChannel without COOP+COEP]
expected: FAIL

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]
expected: TIMEOUT
[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]
expected: FAIL
[Serialize should make the ArrayBuffer detached, so it cannot be transferred again]
expected: FAIL
[Serialize should throw before a detached ArrayBuffer is found]
expected: FAIL
[Cannot transfer ArrayBuffer detached while the message was serialized]
expected: FAIL
[Cannot transfer the same MessagePort twice]
expected: FAIL
[Serialize should make the MessagePort detached, so it cannot be transferred again]
expected: FAIL
[Serialize should throw before a detached MessagePort is found]
expected: FAIL
[Cannot transfer MessagePort detached while the message was serialized]
expected: FAIL
[Cannot transfer the same ImageBitmap twice]
expected: FAIL

View file

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

View file

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

View file

@ -1,20 +1,11 @@
[promise-rejection-events.dedicatedworker.html]
type: testharness
[unhandledrejection: from a task-delayed rejection]
expected: FAIL
[no unhandledrejection/rejectionhandled: all inside a queued task, a rejection handler attached synchronously to a promise created from returning a Promise.reject-created promise in a fulfillment handler]
expected: FAIL
[microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, all inside a postMessageTask]
expected: FAIL
[microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, all inside a postMessageTask]
expected: FAIL
[delayed handling: a nested-task delay before attaching a handler causes unhandledrejection]
expected: FAIL
[delayed handling: a nested-postMessageTask after promise creation/rejection, plus promise microtasks, is too late to attach a rejection handler]
expected: FAIL
@ -33,18 +24,3 @@
[no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from createImageBitmap]
expected: FAIL
[microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, all inside a queueTask]
expected: FAIL
[microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, all inside a queueTask]
expected: FAIL
[delayed handling: a nested-queueTask after promise creation/rejection, plus promise microtasks, is too late to attach a rejection handler]
expected: FAIL
[delayed handling: a nested-queueTask before promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler]
expected: FAIL
[delayed handling: a nested-queueTask after promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler]
expected: FAIL

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.]
expected: FAIL
[Test postMessage with no port.]
expected: FAIL
[Test postMessage with two ports.]
expected: FAIL
[Test postMessage with no ports and empty array.]
expected: FAIL
[Test postMessage with null ports throws exception.]
expected: FAIL
[Test postMessage with incorrect ports throws exception]
expected: FAIL

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]
type: testharness
[existence of MessageChannel]
expected: FAIL
[existence of SharedWorker]
expected: FAIL

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

@ -34,7 +34,9 @@ test_interfaces([
"Headers",
"History",
"ImageData",
"MessageChannel",
"MessageEvent",
"MessagePort",
"Performance",
"PerformanceEntry",
"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.");