mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
implement broadcastchannel
This commit is contained in:
parent
145c89a2d4
commit
eb21d5f738
32 changed files with 763 additions and 216 deletions
|
@ -126,12 +126,12 @@ use log::{Level, LevelFilter, Log, Metadata, Record};
|
||||||
use media::{GLPlayerThreads, WindowGLContext};
|
use media::{GLPlayerThreads, WindowGLContext};
|
||||||
use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg};
|
use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg};
|
||||||
use msg::constellation_msg::{
|
use msg::constellation_msg::{
|
||||||
BrowsingContextGroupId, BrowsingContextId, HistoryStateId, PipelineId,
|
BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineNamespace,
|
||||||
TopLevelBrowsingContextId,
|
PipelineNamespaceId, PipelineNamespaceRequest, TraversalDirection,
|
||||||
};
|
};
|
||||||
use msg::constellation_msg::{
|
use msg::constellation_msg::{
|
||||||
MessagePortId, MessagePortRouterId, PipelineNamespace, PipelineNamespaceId,
|
BrowsingContextGroupId, BrowsingContextId, HistoryStateId, PipelineId,
|
||||||
PipelineNamespaceRequest, TraversalDirection,
|
TopLevelBrowsingContextId,
|
||||||
};
|
};
|
||||||
use net_traits::pub_domains::reg_host;
|
use net_traits::pub_domains::reg_host;
|
||||||
use net_traits::request::RequestBuilder;
|
use net_traits::request::RequestBuilder;
|
||||||
|
@ -142,7 +142,8 @@ use profile_traits::time;
|
||||||
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent};
|
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent};
|
||||||
use script_traits::{webdriver_msg, LogEntry, ScriptToConstellationChan, ServiceWorkerMsg};
|
use script_traits::{webdriver_msg, LogEntry, ScriptToConstellationChan, ServiceWorkerMsg};
|
||||||
use script_traits::{
|
use script_traits::{
|
||||||
AnimationState, AnimationTickType, AuxiliaryBrowsingContextLoadInfo, CompositorEvent,
|
AnimationState, AnimationTickType, AuxiliaryBrowsingContextLoadInfo, BroadcastMsg,
|
||||||
|
CompositorEvent,
|
||||||
};
|
};
|
||||||
use script_traits::{ConstellationControlMsg, DiscardBrowsingContext};
|
use script_traits::{ConstellationControlMsg, DiscardBrowsingContext};
|
||||||
use script_traits::{DocumentActivity, DocumentState, LayoutControlMsg, LoadData, LoadOrigin};
|
use script_traits::{DocumentActivity, DocumentState, LayoutControlMsg, LoadData, LoadOrigin};
|
||||||
|
@ -399,6 +400,12 @@ pub struct Constellation<Message, LTF, STF> {
|
||||||
/// A map of router-id to ipc-sender, to route messages to ports.
|
/// A map of router-id to ipc-sender, to route messages to ports.
|
||||||
message_port_routers: HashMap<MessagePortRouterId, IpcSender<MessagePortMsg>>,
|
message_port_routers: HashMap<MessagePortRouterId, IpcSender<MessagePortMsg>>,
|
||||||
|
|
||||||
|
/// A map of broadcast routers to their IPC sender.
|
||||||
|
broadcast_routers: HashMap<BroadcastChannelRouterId, IpcSender<BroadcastMsg>>,
|
||||||
|
|
||||||
|
/// A map of origin to a map of channel-name to a list of relevant routers.
|
||||||
|
broadcast_channels: HashMap<ImmutableOrigin, HashMap<String, Vec<BroadcastChannelRouterId>>>,
|
||||||
|
|
||||||
/// The set of all the pipelines in the browser. (See the `pipeline` module
|
/// The set of all the pipelines in the browser. (See the `pipeline` module
|
||||||
/// for more details.)
|
/// for more details.)
|
||||||
pipelines: HashMap<PipelineId, Pipeline>,
|
pipelines: HashMap<PipelineId, Pipeline>,
|
||||||
|
@ -961,6 +968,8 @@ where
|
||||||
browsing_context_group_next_id: Default::default(),
|
browsing_context_group_next_id: Default::default(),
|
||||||
message_ports: HashMap::new(),
|
message_ports: HashMap::new(),
|
||||||
message_port_routers: HashMap::new(),
|
message_port_routers: HashMap::new(),
|
||||||
|
broadcast_routers: HashMap::new(),
|
||||||
|
broadcast_channels: HashMap::new(),
|
||||||
pipelines: HashMap::new(),
|
pipelines: HashMap::new(),
|
||||||
browsing_contexts: HashMap::new(),
|
browsing_contexts: HashMap::new(),
|
||||||
pending_changes: vec![],
|
pending_changes: vec![],
|
||||||
|
@ -1760,6 +1769,36 @@ where
|
||||||
FromScriptMsg::EntanglePorts(port1, port2) => {
|
FromScriptMsg::EntanglePorts(port1, port2) => {
|
||||||
self.handle_entangle_messageports(port1, port2);
|
self.handle_entangle_messageports(port1, port2);
|
||||||
},
|
},
|
||||||
|
FromScriptMsg::NewBroadcastChannelRouter(router_id, ipc_sender, origin) => {
|
||||||
|
self.handle_new_broadcast_channel_router(
|
||||||
|
source_pipeline_id,
|
||||||
|
router_id,
|
||||||
|
ipc_sender,
|
||||||
|
origin,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
FromScriptMsg::NewBroadcastChannelNameInRouter(router_id, channel_name, origin) => {
|
||||||
|
self.handle_new_broadcast_channel_name_in_router(
|
||||||
|
source_pipeline_id,
|
||||||
|
router_id,
|
||||||
|
channel_name,
|
||||||
|
origin,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
FromScriptMsg::RemoveBroadcastChannelNameInRouter(router_id, channel_name, origin) => {
|
||||||
|
self.handle_remove_broadcast_channel_name_in_router(
|
||||||
|
source_pipeline_id,
|
||||||
|
router_id,
|
||||||
|
channel_name,
|
||||||
|
origin,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
FromScriptMsg::RemoveBroadcastChannelRouter(router_id, origin) => {
|
||||||
|
self.handle_remove_broadcast_channel_router(source_pipeline_id, router_id, origin);
|
||||||
|
},
|
||||||
|
FromScriptMsg::ScheduleBroadcast(router_id, message) => {
|
||||||
|
self.handle_schedule_broadcast(source_pipeline_id, router_id, message);
|
||||||
|
},
|
||||||
FromScriptMsg::ForwardToEmbedder(embedder_msg) => {
|
FromScriptMsg::ForwardToEmbedder(embedder_msg) => {
|
||||||
self.embedder_proxy
|
self.embedder_proxy
|
||||||
.send((Some(source_top_ctx_id), embedder_msg));
|
.send((Some(source_top_ctx_id), embedder_msg));
|
||||||
|
@ -1976,6 +2015,170 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check the origin of a message against that of the pipeline it came from.
|
||||||
|
/// Note: this is still limited as a security check,
|
||||||
|
/// see https://github.com/servo/servo/issues/11722
|
||||||
|
fn check_origin_against_pipeline(
|
||||||
|
&self,
|
||||||
|
pipeline_id: &PipelineId,
|
||||||
|
origin: &ImmutableOrigin,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
let pipeline_origin = match self.pipelines.get(&pipeline_id) {
|
||||||
|
Some(pipeline) => pipeline.load_data.url.origin(),
|
||||||
|
None => {
|
||||||
|
warn!("Received message from closed or unknown pipeline.");
|
||||||
|
return Err(());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if &pipeline_origin == origin {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Broadcast a message via routers in various event-loops.
|
||||||
|
fn handle_schedule_broadcast(
|
||||||
|
&self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
router_id: BroadcastChannelRouterId,
|
||||||
|
message: BroadcastMsg,
|
||||||
|
) {
|
||||||
|
if self
|
||||||
|
.check_origin_against_pipeline(&pipeline_id, &message.origin)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return warn!(
|
||||||
|
"Attempt to schedule broadcast from an origin not matching the origin of the msg."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(channels) = self.broadcast_channels.get(&message.origin) {
|
||||||
|
let routers = match channels.get(&message.channel_name) {
|
||||||
|
Some(routers) => routers,
|
||||||
|
None => return warn!("Broadcast to channel name without active routers."),
|
||||||
|
};
|
||||||
|
for router in routers {
|
||||||
|
// Exclude the sender of the broadcast.
|
||||||
|
// Broadcasting locally is done at the point of sending.
|
||||||
|
if router == &router_id {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sender) = self.broadcast_routers.get(&router) {
|
||||||
|
if sender.send(message.clone()).is_err() {
|
||||||
|
warn!("Failed to broadcast message to router: {:?}", router);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("No sender for broadcast router: {:?}", router);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Attempt to schedule a broadcast for an origin without routers {:?}",
|
||||||
|
message.origin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a channel-name for a given broadcast router.
|
||||||
|
fn handle_remove_broadcast_channel_name_in_router(
|
||||||
|
&mut self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
router_id: BroadcastChannelRouterId,
|
||||||
|
channel_name: String,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
|
) {
|
||||||
|
if self
|
||||||
|
.check_origin_against_pipeline(&pipeline_id, &origin)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return warn!("Attempt to remove channel name from an unexpected origin.");
|
||||||
|
}
|
||||||
|
if let Some(channels) = self.broadcast_channels.get_mut(&origin) {
|
||||||
|
let is_empty = if let Some(routers) = channels.get_mut(&channel_name) {
|
||||||
|
routers.retain(|router| router != &router_id);
|
||||||
|
routers.is_empty()
|
||||||
|
} else {
|
||||||
|
return warn!(
|
||||||
|
"Multiple attemps to remove name for broadcast-channel {:?} at {:?}",
|
||||||
|
channel_name, origin
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if is_empty {
|
||||||
|
channels.remove(&channel_name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Attempt to remove a channel-name for an origin without channels {:?}",
|
||||||
|
origin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Note a new channel-name relevant to a given broadcast router.
|
||||||
|
fn handle_new_broadcast_channel_name_in_router(
|
||||||
|
&mut self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
router_id: BroadcastChannelRouterId,
|
||||||
|
channel_name: String,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
|
) {
|
||||||
|
if self
|
||||||
|
.check_origin_against_pipeline(&pipeline_id, &origin)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return warn!("Attempt to add channel name from an unexpected origin.");
|
||||||
|
}
|
||||||
|
let channels = self
|
||||||
|
.broadcast_channels
|
||||||
|
.entry(origin)
|
||||||
|
.or_insert_with(HashMap::new);
|
||||||
|
|
||||||
|
let routers = channels.entry(channel_name).or_insert_with(Vec::new);
|
||||||
|
|
||||||
|
routers.push(router_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a broadcast router.
|
||||||
|
fn handle_remove_broadcast_channel_router(
|
||||||
|
&mut self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
router_id: BroadcastChannelRouterId,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
|
) {
|
||||||
|
if self
|
||||||
|
.check_origin_against_pipeline(&pipeline_id, &origin)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return warn!("Attempt to remove broadcast router from an unexpected origin.");
|
||||||
|
}
|
||||||
|
if self.broadcast_routers.remove(&router_id).is_none() {
|
||||||
|
warn!("Attempt to remove unknown broadcast-channel router.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new broadcast router.
|
||||||
|
fn handle_new_broadcast_channel_router(
|
||||||
|
&mut self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
router_id: BroadcastChannelRouterId,
|
||||||
|
ipc_sender: IpcSender<BroadcastMsg>,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
|
) {
|
||||||
|
if self
|
||||||
|
.check_origin_against_pipeline(&pipeline_id, &origin)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return warn!("Attempt to add broadcast router from an unexpected origin.");
|
||||||
|
}
|
||||||
|
if self
|
||||||
|
.broadcast_routers
|
||||||
|
.insert(router_id, ipc_sender)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
warn!("Multple attempt to add broadcast-channel router.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_request_wgpu_adapter(
|
fn handle_request_wgpu_adapter(
|
||||||
&mut self,
|
&mut self,
|
||||||
source_pipeline_id: PipelineId,
|
source_pipeline_id: PipelineId,
|
||||||
|
|
|
@ -171,6 +171,13 @@ impl PipelineNamespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_broadcast_channel_router_id(&mut self) -> BroadcastChannelRouterId {
|
||||||
|
BroadcastChannelRouterId {
|
||||||
|
namespace_id: self.id,
|
||||||
|
index: BroadcastChannelRouterIndex(self.next_index()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn next_blob_id(&mut self) -> BlobId {
|
fn next_blob_id(&mut self) -> BlobId {
|
||||||
BlobId {
|
BlobId {
|
||||||
namespace_id: self.id,
|
namespace_id: self.id,
|
||||||
|
@ -380,6 +387,42 @@ impl fmt::Display for MessagePortRouterId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
pub struct BroadcastChannelRouterIndex(pub NonZeroU32);
|
||||||
|
malloc_size_of_is_0!(BroadcastChannelRouterIndex);
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
|
||||||
|
)]
|
||||||
|
pub struct BroadcastChannelRouterId {
|
||||||
|
pub namespace_id: PipelineNamespaceId,
|
||||||
|
pub index: BroadcastChannelRouterIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BroadcastChannelRouterId {
|
||||||
|
pub fn new() -> BroadcastChannelRouterId {
|
||||||
|
PIPELINE_NAMESPACE.with(|tls| {
|
||||||
|
let mut namespace = tls.get().expect("No namespace set for this thread!");
|
||||||
|
let next_broadcast_channel_router_id = namespace.next_broadcast_channel_router_id();
|
||||||
|
tls.set(Some(namespace));
|
||||||
|
next_broadcast_channel_router_id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BroadcastChannelRouterId {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let PipelineNamespaceId(namespace_id) = self.namespace_id;
|
||||||
|
let BroadcastChannelRouterIndex(index) = self.index;
|
||||||
|
write!(
|
||||||
|
fmt,
|
||||||
|
"(BroadcastChannelRouterId{},{})",
|
||||||
|
namespace_id,
|
||||||
|
index.get()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
pub struct BlobIndex(pub NonZeroU32);
|
pub struct BlobIndex(pub NonZeroU32);
|
||||||
malloc_size_of_is_0!(BlobIndex);
|
malloc_size_of_is_0!(BlobIndex);
|
||||||
|
|
|
@ -83,8 +83,8 @@ use media::WindowGLContext;
|
||||||
use metrics::{InteractiveMetrics, InteractiveWindow};
|
use metrics::{InteractiveMetrics, InteractiveWindow};
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use msg::constellation_msg::{
|
use msg::constellation_msg::{
|
||||||
BlobId, BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId, PipelineId,
|
BlobId, BroadcastChannelRouterId, BrowsingContextId, HistoryStateId, MessagePortId,
|
||||||
TopLevelBrowsingContextId,
|
MessagePortRouterId, PipelineId, TopLevelBrowsingContextId,
|
||||||
};
|
};
|
||||||
use net_traits::filemanager_thread::RelativePos;
|
use net_traits::filemanager_thread::RelativePos;
|
||||||
use net_traits::image::base::{Image, ImageMetadata};
|
use net_traits::image::base::{Image, ImageMetadata};
|
||||||
|
@ -175,6 +175,8 @@ unsafe_no_jsmanaged_fields!(MessagePortId);
|
||||||
unsafe_no_jsmanaged_fields!(RefCell<Option<MessagePortId>>);
|
unsafe_no_jsmanaged_fields!(RefCell<Option<MessagePortId>>);
|
||||||
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
|
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
|
||||||
|
|
||||||
|
unsafe_no_jsmanaged_fields!(BroadcastChannelRouterId);
|
||||||
|
|
||||||
unsafe_no_jsmanaged_fields!(BlobId);
|
unsafe_no_jsmanaged_fields!(BlobId);
|
||||||
unsafe_no_jsmanaged_fields!(BlobImpl);
|
unsafe_no_jsmanaged_fields!(BlobImpl);
|
||||||
|
|
||||||
|
|
106
components/script/dom/broadcastchannel.rs
Normal file
106
components/script/dom/broadcastchannel.rs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use crate::dom::bindings::codegen::Bindings::BroadcastChannelBinding::{
|
||||||
|
BroadcastChannelMethods, Wrap,
|
||||||
|
};
|
||||||
|
use crate::dom::bindings::error::{Error, ErrorResult};
|
||||||
|
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
|
||||||
|
use crate::dom::bindings::root::DomRoot;
|
||||||
|
use crate::dom::bindings::str::DOMString;
|
||||||
|
use crate::dom::bindings::structuredclone;
|
||||||
|
use crate::dom::eventtarget::EventTarget;
|
||||||
|
use crate::dom::globalscope::GlobalScope;
|
||||||
|
use crate::script_runtime::JSContext as SafeJSContext;
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use js::rust::HandleValue;
|
||||||
|
use script_traits::BroadcastMsg;
|
||||||
|
use std::cell::Cell;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct BroadcastChannel {
|
||||||
|
eventtarget: EventTarget,
|
||||||
|
name: DOMString,
|
||||||
|
closed: Cell<bool>,
|
||||||
|
id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BroadcastChannel {
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#broadcastchannel>
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn Constructor(global: &GlobalScope, name: DOMString) -> DomRoot<BroadcastChannel> {
|
||||||
|
BroadcastChannel::new(global, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(global: &GlobalScope, name: DOMString) -> DomRoot<BroadcastChannel> {
|
||||||
|
let channel = reflect_dom_object(
|
||||||
|
Box::new(BroadcastChannel::new_inherited(name)),
|
||||||
|
global,
|
||||||
|
Wrap,
|
||||||
|
);
|
||||||
|
global.track_broadcast_channel(&*channel);
|
||||||
|
channel
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_inherited(name: DOMString) -> BroadcastChannel {
|
||||||
|
BroadcastChannel {
|
||||||
|
eventtarget: EventTarget::new_inherited(),
|
||||||
|
name,
|
||||||
|
closed: Default::default(),
|
||||||
|
id: Uuid::new_v4(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The unique Id of this channel.
|
||||||
|
/// Used for filtering out the sender from the local broadcast.
|
||||||
|
pub fn id(&self) -> &Uuid {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is this channel closed?
|
||||||
|
pub fn closed(&self) -> bool {
|
||||||
|
self.closed.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BroadcastChannelMethods for BroadcastChannel {
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
|
||||||
|
fn PostMessage(&self, cx: SafeJSContext, message: HandleValue) -> ErrorResult {
|
||||||
|
// Step 3, if closed.
|
||||||
|
if self.closed.get() {
|
||||||
|
return Err(Error::InvalidState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6, StructuredSerialize(message).
|
||||||
|
let data = structuredclone::write(cx, message, None)?;
|
||||||
|
|
||||||
|
let global = self.global();
|
||||||
|
|
||||||
|
let msg = BroadcastMsg {
|
||||||
|
origin: global.origin().immutable().clone(),
|
||||||
|
channel_name: self.Name().to_string(),
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
|
||||||
|
global.schedule_broadcast(msg, &self.id);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-name>
|
||||||
|
fn Name(&self) -> DOMString {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-close>
|
||||||
|
fn Close(&self) {
|
||||||
|
self.closed.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#handler-broadcastchannel-onmessageerror>
|
||||||
|
event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#handler-broadcastchannel-onmessage>
|
||||||
|
event_handler!(message, GetOnmessage, SetOnmessage);
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use crate::dom::bindings::cell::DomRefCell;
|
use crate::dom::bindings::cell::DomRefCell;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::BroadcastChannelBinding::BroadcastChannelMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::EventSourceBinding::EventSourceBinding::EventSourceMethods;
|
use crate::dom::bindings::codegen::Bindings::EventSourceBinding::EventSourceBinding::EventSourceMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
|
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
|
||||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
|
@ -19,6 +20,7 @@ use crate::dom::bindings::structuredclone;
|
||||||
use crate::dom::bindings::utils::to_frozen_array;
|
use crate::dom::bindings::utils::to_frozen_array;
|
||||||
use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
|
use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
|
||||||
use crate::dom::blob::Blob;
|
use crate::dom::blob::Blob;
|
||||||
|
use crate::dom::broadcastchannel::BroadcastChannel;
|
||||||
use crate::dom::crypto::Crypto;
|
use crate::dom::crypto::Crypto;
|
||||||
use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
|
use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
|
||||||
use crate::dom::errorevent::ErrorEvent;
|
use crate::dom::errorevent::ErrorEvent;
|
||||||
|
@ -71,7 +73,9 @@ use js::rust::wrappers::EvaluateUtf8;
|
||||||
use js::rust::{get_object_class, CompileOptionsWrapper, ParentRuntime, Runtime};
|
use js::rust::{get_object_class, CompileOptionsWrapper, ParentRuntime, Runtime};
|
||||||
use js::rust::{HandleValue, MutableHandleValue};
|
use js::rust::{HandleValue, MutableHandleValue};
|
||||||
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
|
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
|
||||||
use msg::constellation_msg::{BlobId, MessagePortId, MessagePortRouterId, PipelineId};
|
use msg::constellation_msg::{
|
||||||
|
BlobId, BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineId,
|
||||||
|
};
|
||||||
use net_traits::blob_url_store::{get_blob_origin, BlobBuf};
|
use net_traits::blob_url_store::{get_blob_origin, BlobBuf};
|
||||||
use net_traits::filemanager_thread::{
|
use net_traits::filemanager_thread::{
|
||||||
FileManagerResult, FileManagerThreadMsg, ReadFileProgress, RelativePos,
|
FileManagerResult, FileManagerThreadMsg, ReadFileProgress, RelativePos,
|
||||||
|
@ -82,7 +86,8 @@ use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_tim
|
||||||
use script_traits::serializable::{BlobData, BlobImpl, FileBlob};
|
use script_traits::serializable::{BlobData, BlobImpl, FileBlob};
|
||||||
use script_traits::transferable::MessagePortImpl;
|
use script_traits::transferable::MessagePortImpl;
|
||||||
use script_traits::{
|
use script_traits::{
|
||||||
MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg, ScriptToConstellationChan, TimerEvent,
|
BroadcastMsg, MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg,
|
||||||
|
ScriptToConstellationChan, TimerEvent,
|
||||||
};
|
};
|
||||||
use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource};
|
use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource};
|
||||||
use servo_url::{MutableOrigin, ServoUrl};
|
use servo_url::{MutableOrigin, ServoUrl};
|
||||||
|
@ -124,6 +129,9 @@ pub struct GlobalScope {
|
||||||
/// The message-port router id for this global, if it is managing ports.
|
/// The message-port router id for this global, if it is managing ports.
|
||||||
message_port_state: DomRefCell<MessagePortState>,
|
message_port_state: DomRefCell<MessagePortState>,
|
||||||
|
|
||||||
|
/// The broadcast channels state this global, if it is managing any.
|
||||||
|
broadcast_channel_state: DomRefCell<BroadcastChannelState>,
|
||||||
|
|
||||||
/// The blobs managed by this global, if any.
|
/// The blobs managed by this global, if any.
|
||||||
blob_state: DomRefCell<BlobState>,
|
blob_state: DomRefCell<BlobState>,
|
||||||
|
|
||||||
|
@ -237,6 +245,13 @@ struct MessageListener {
|
||||||
context: Trusted<GlobalScope>,
|
context: Trusted<GlobalScope>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A wrapper for broadcasts coming in over IPC, and the event-loop.
|
||||||
|
struct BroadcastListener {
|
||||||
|
canceller: TaskCanceller,
|
||||||
|
task_source: DOMManipulationTaskSource,
|
||||||
|
context: Trusted<GlobalScope>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A wrapper between timer events coming in over IPC, and the event-loop.
|
/// A wrapper between timer events coming in over IPC, and the event-loop.
|
||||||
struct TimerListener {
|
struct TimerListener {
|
||||||
canceller: TaskCanceller,
|
canceller: TaskCanceller,
|
||||||
|
@ -310,6 +325,23 @@ pub struct ManagedMessagePort {
|
||||||
closed: bool,
|
closed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// State representing whether this global is currently managing broadcast channels.
|
||||||
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
|
#[unrooted_must_root_lint::must_root]
|
||||||
|
pub enum BroadcastChannelState {
|
||||||
|
/// The broadcast-channel router id for this global, and a queue of managed channels.
|
||||||
|
/// Step 9, "sort destinations"
|
||||||
|
/// of https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage
|
||||||
|
/// requires keeping track of creation order, hence the queue.
|
||||||
|
Managed(
|
||||||
|
BroadcastChannelRouterId,
|
||||||
|
/// The map of channel-name to queue of channels, in order of creation.
|
||||||
|
HashMap<DOMString, VecDeque<Dom<BroadcastChannel>>>,
|
||||||
|
),
|
||||||
|
/// This global is not managing any broadcast channels at this time.
|
||||||
|
UnManaged,
|
||||||
|
}
|
||||||
|
|
||||||
/// State representing whether this global is currently managing messageports.
|
/// State representing whether this global is currently managing messageports.
|
||||||
#[derive(JSTraceable, MallocSizeOf)]
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
#[unrooted_must_root_lint::must_root]
|
#[unrooted_must_root_lint::must_root]
|
||||||
|
@ -323,6 +355,29 @@ pub enum MessagePortState {
|
||||||
UnManaged,
|
UnManaged,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BroadcastListener {
|
||||||
|
/// Handle a broadcast coming in over IPC,
|
||||||
|
/// by queueing the appropriate task on the relevant event-loop.
|
||||||
|
fn handle(&self, event: BroadcastMsg) {
|
||||||
|
let context = self.context.clone();
|
||||||
|
|
||||||
|
// Note: strictly speaking we should just queue the message event tasks,
|
||||||
|
// not queue a task that then queues more tasks.
|
||||||
|
// This however seems to be hard to avoid in the light of the IPC.
|
||||||
|
// One can imagine queueing tasks directly,
|
||||||
|
// for channels that would be in the same script-thread.
|
||||||
|
let _ = self.task_source.queue_with_canceller(
|
||||||
|
task!(broadcast_message_event: move || {
|
||||||
|
let global = context.root();
|
||||||
|
// Step 10 of https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage,
|
||||||
|
// For each BroadcastChannel object destination in destinations, queue a task.
|
||||||
|
global.broadcast_message_event(event, None);
|
||||||
|
}),
|
||||||
|
&self.canceller,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TimerListener {
|
impl TimerListener {
|
||||||
/// Handle a timer-event coming-in over IPC,
|
/// Handle a timer-event coming-in over IPC,
|
||||||
/// by queuing the appropriate task on the relevant event-loop.
|
/// by queuing the appropriate task on the relevant event-loop.
|
||||||
|
@ -501,6 +556,7 @@ impl GlobalScope {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
message_port_state: DomRefCell::new(MessagePortState::UnManaged),
|
message_port_state: DomRefCell::new(MessagePortState::UnManaged),
|
||||||
|
broadcast_channel_state: DomRefCell::new(BroadcastChannelState::UnManaged),
|
||||||
blob_state: DomRefCell::new(BlobState::UnManaged),
|
blob_state: DomRefCell::new(BlobState::UnManaged),
|
||||||
eventtarget: EventTarget::new_inherited(),
|
eventtarget: EventTarget::new_inherited(),
|
||||||
crypto: Default::default(),
|
crypto: Default::default(),
|
||||||
|
@ -613,11 +669,18 @@ impl GlobalScope {
|
||||||
pub fn perform_a_dom_garbage_collection_checkpoint(&self) {
|
pub fn perform_a_dom_garbage_collection_checkpoint(&self) {
|
||||||
self.perform_a_message_port_garbage_collection_checkpoint();
|
self.perform_a_message_port_garbage_collection_checkpoint();
|
||||||
self.perform_a_blob_garbage_collection_checkpoint();
|
self.perform_a_blob_garbage_collection_checkpoint();
|
||||||
|
self.perform_a_broadcast_channel_garbage_collection_checkpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove the routers for ports and broadcast-channels.
|
||||||
|
pub fn remove_web_messaging_infra(&self) {
|
||||||
|
self.remove_message_ports_router();
|
||||||
|
self.remove_broadcast_channel_router();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update our state to un-managed,
|
/// Update our state to un-managed,
|
||||||
/// and tell the constellation to drop the sender to our message-port router.
|
/// and tell the constellation to drop the sender to our message-port router.
|
||||||
pub fn remove_message_ports_router(&self) {
|
fn remove_message_ports_router(&self) {
|
||||||
if let MessagePortState::Managed(router_id, _message_ports) =
|
if let MessagePortState::Managed(router_id, _message_ports) =
|
||||||
&*self.message_port_state.borrow()
|
&*self.message_port_state.borrow()
|
||||||
{
|
{
|
||||||
|
@ -628,6 +691,22 @@ impl GlobalScope {
|
||||||
*self.message_port_state.borrow_mut() = MessagePortState::UnManaged;
|
*self.message_port_state.borrow_mut() = MessagePortState::UnManaged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update our state to un-managed,
|
||||||
|
/// and tell the constellation to drop the sender to our broadcast router.
|
||||||
|
fn remove_broadcast_channel_router(&self) {
|
||||||
|
if let BroadcastChannelState::Managed(router_id, _channels) =
|
||||||
|
&*self.broadcast_channel_state.borrow()
|
||||||
|
{
|
||||||
|
let _ =
|
||||||
|
self.script_to_constellation_chan()
|
||||||
|
.send(ScriptMsg::RemoveBroadcastChannelRouter(
|
||||||
|
router_id.clone(),
|
||||||
|
self.origin().immutable().clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
*self.broadcast_channel_state.borrow_mut() = BroadcastChannelState::UnManaged;
|
||||||
|
}
|
||||||
|
|
||||||
/// <https://html.spec.whatwg.org/multipage/#entangle>
|
/// <https://html.spec.whatwg.org/multipage/#entangle>
|
||||||
pub fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) {
|
pub fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) {
|
||||||
if let MessagePortState::Managed(_id, message_ports) =
|
if let MessagePortState::Managed(_id, message_ports) =
|
||||||
|
@ -789,6 +868,115 @@ impl GlobalScope {
|
||||||
.send(ScriptMsg::RerouteMessagePort(port_id, task));
|
.send(ScriptMsg::RerouteMessagePort(port_id, task));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage>
|
||||||
|
/// Step 7 and following steps.
|
||||||
|
pub fn schedule_broadcast(&self, msg: BroadcastMsg, channel_id: &Uuid) {
|
||||||
|
// First, broadcast locally.
|
||||||
|
self.broadcast_message_event(msg.clone(), Some(channel_id));
|
||||||
|
|
||||||
|
if let BroadcastChannelState::Managed(router_id, _) =
|
||||||
|
&*self.broadcast_channel_state.borrow()
|
||||||
|
{
|
||||||
|
// Second, broadcast to other globals via the constellation.
|
||||||
|
//
|
||||||
|
// Note: for globals in the same script-thread,
|
||||||
|
// we could skip the hop to the constellation.
|
||||||
|
let _ = self
|
||||||
|
.script_to_constellation_chan()
|
||||||
|
.send(ScriptMsg::ScheduleBroadcast(router_id.clone(), msg));
|
||||||
|
} else {
|
||||||
|
panic!("Attemps to broadcast a message via global not managing any channels.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage>
|
||||||
|
/// Step 7 and following steps.
|
||||||
|
pub fn broadcast_message_event(&self, event: BroadcastMsg, channel_id: Option<&Uuid>) {
|
||||||
|
if let BroadcastChannelState::Managed(_, channels) = &*self.broadcast_channel_state.borrow()
|
||||||
|
{
|
||||||
|
let BroadcastMsg {
|
||||||
|
data,
|
||||||
|
origin,
|
||||||
|
channel_name,
|
||||||
|
} = event;
|
||||||
|
|
||||||
|
// Step 7, a few preliminary steps.
|
||||||
|
|
||||||
|
// - Check the worker is not closing.
|
||||||
|
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
||||||
|
if worker.is_closing() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Check the associated document is fully-active.
|
||||||
|
if let Some(window) = self.downcast::<Window>() {
|
||||||
|
if !window.Document().is_fully_active() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Check for a case-sensitive match for the name of the channel.
|
||||||
|
let channel_name = DOMString::from_string(channel_name);
|
||||||
|
|
||||||
|
if let Some(channels) = channels.get(&channel_name) {
|
||||||
|
channels
|
||||||
|
.iter()
|
||||||
|
.filter(|ref channel| {
|
||||||
|
// Step 8.
|
||||||
|
// Filter out the sender.
|
||||||
|
if let Some(id) = channel_id {
|
||||||
|
channel.id() != id
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|channel| DomRoot::from_ref(&**channel))
|
||||||
|
// Step 9, sort by creation order,
|
||||||
|
// done by using a queue to store channels in creation order.
|
||||||
|
.for_each(|channel| {
|
||||||
|
let data = data.clone_for_broadcast();
|
||||||
|
let origin = origin.clone();
|
||||||
|
|
||||||
|
// Step 10: Queue a task on the DOM manipulation task-source,
|
||||||
|
// to fire the message event
|
||||||
|
let channel = Trusted::new(&*channel);
|
||||||
|
let global = Trusted::new(&*self);
|
||||||
|
let _ = self.dom_manipulation_task_source().queue(
|
||||||
|
task!(process_pending_port_messages: move || {
|
||||||
|
let destination = channel.root();
|
||||||
|
let global = global.root();
|
||||||
|
|
||||||
|
// 10.1 Check for closed flag.
|
||||||
|
if destination.closed() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rooted!(in(*global.get_cx()) let mut message = UndefinedValue());
|
||||||
|
|
||||||
|
// Step 10.3 StructuredDeserialize(serialized, targetRealm).
|
||||||
|
if let Ok(ports) = structuredclone::read(&global, data, message.handle_mut()) {
|
||||||
|
// Step 10.4, Fire an event named message at destination.
|
||||||
|
MessageEvent::dispatch_jsval(
|
||||||
|
&*destination.upcast(),
|
||||||
|
&global,
|
||||||
|
message.handle(),
|
||||||
|
Some(&origin.ascii_serialization()),
|
||||||
|
None,
|
||||||
|
ports,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Step 10.3, fire an event named messageerror at destination.
|
||||||
|
MessageEvent::dispatch_error(&*destination.upcast(), &global);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
&self,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Route the task to be handled by the relevant port.
|
/// Route the task to be handled by the relevant port.
|
||||||
pub fn route_task_to_port(&self, port_id: MessagePortId, task: PortMessageTask) {
|
pub fn route_task_to_port(&self, port_id: MessagePortId, task: PortMessageTask) {
|
||||||
let should_dispatch = if let MessagePortState::Managed(_id, message_ports) =
|
let should_dispatch = if let MessagePortState::Managed(_id, message_ports) =
|
||||||
|
@ -905,6 +1093,93 @@ impl GlobalScope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove broadcast-channels that are closed.
|
||||||
|
/// TODO: Also remove them if they do not have an event-listener.
|
||||||
|
/// see https://github.com/servo/servo/issues/25772
|
||||||
|
pub fn perform_a_broadcast_channel_garbage_collection_checkpoint(&self) {
|
||||||
|
let is_empty = if let BroadcastChannelState::Managed(router_id, ref mut channels) =
|
||||||
|
&mut *self.broadcast_channel_state.borrow_mut()
|
||||||
|
{
|
||||||
|
channels.retain(|name, ref mut channels| {
|
||||||
|
channels.retain(|ref chan| !chan.closed());
|
||||||
|
if channels.is_empty() {
|
||||||
|
let _ = self.script_to_constellation_chan().send(
|
||||||
|
ScriptMsg::RemoveBroadcastChannelNameInRouter(
|
||||||
|
router_id.clone(),
|
||||||
|
name.to_string(),
|
||||||
|
self.origin().immutable().clone(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
channels.is_empty()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if is_empty {
|
||||||
|
self.remove_broadcast_channel_router();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start tracking a broadcast-channel.
|
||||||
|
pub fn track_broadcast_channel(&self, dom_channel: &BroadcastChannel) {
|
||||||
|
let mut current_state = self.broadcast_channel_state.borrow_mut();
|
||||||
|
|
||||||
|
if let BroadcastChannelState::UnManaged = &*current_state {
|
||||||
|
// Setup a route for IPC, for broadcasts from the constellation to our channels.
|
||||||
|
let (broadcast_control_sender, broadcast_control_receiver) =
|
||||||
|
ipc::channel().expect("ipc channel failure");
|
||||||
|
let context = Trusted::new(self);
|
||||||
|
let (task_source, canceller) = (
|
||||||
|
self.dom_manipulation_task_source(),
|
||||||
|
self.task_canceller(TaskSourceName::DOMManipulation),
|
||||||
|
);
|
||||||
|
let listener = BroadcastListener {
|
||||||
|
canceller,
|
||||||
|
task_source,
|
||||||
|
context,
|
||||||
|
};
|
||||||
|
ROUTER.add_route(
|
||||||
|
broadcast_control_receiver.to_opaque(),
|
||||||
|
Box::new(move |message| {
|
||||||
|
let msg = message.to();
|
||||||
|
match msg {
|
||||||
|
Ok(msg) => listener.handle(msg),
|
||||||
|
Err(err) => warn!("Error receiving a BroadcastMsg: {:?}", err),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
let router_id = BroadcastChannelRouterId::new();
|
||||||
|
*current_state = BroadcastChannelState::Managed(router_id.clone(), HashMap::new());
|
||||||
|
let _ = self
|
||||||
|
.script_to_constellation_chan()
|
||||||
|
.send(ScriptMsg::NewBroadcastChannelRouter(
|
||||||
|
router_id,
|
||||||
|
broadcast_control_sender,
|
||||||
|
self.origin().immutable().clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let BroadcastChannelState::Managed(router_id, channels) = &mut *current_state {
|
||||||
|
let entry = channels.entry(dom_channel.Name()).or_insert_with(|| {
|
||||||
|
let _ = self.script_to_constellation_chan().send(
|
||||||
|
ScriptMsg::NewBroadcastChannelNameInRouter(
|
||||||
|
router_id.clone(),
|
||||||
|
dom_channel.Name().to_string(),
|
||||||
|
self.origin().immutable().clone(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
VecDeque::new()
|
||||||
|
});
|
||||||
|
entry.push_back(Dom::from_ref(dom_channel));
|
||||||
|
} else {
|
||||||
|
panic!("track_broadcast_channel should have first switched the state to managed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Start tracking a message-port
|
/// Start tracking a message-port
|
||||||
pub fn track_message_port(&self, dom_port: &MessagePort, port_impl: Option<MessagePortImpl>) {
|
pub fn track_message_port(&self, dom_port: &MessagePort, port_impl: Option<MessagePortImpl>) {
|
||||||
let mut current_state = self.message_port_state.borrow_mut();
|
let mut current_state = self.message_port_state.borrow_mut();
|
||||||
|
|
|
@ -239,6 +239,7 @@ pub mod bluetoothremotegattdescriptor;
|
||||||
pub mod bluetoothremotegattserver;
|
pub mod bluetoothremotegattserver;
|
||||||
pub mod bluetoothremotegattservice;
|
pub mod bluetoothremotegattservice;
|
||||||
pub mod bluetoothuuid;
|
pub mod bluetoothuuid;
|
||||||
|
pub mod broadcastchannel;
|
||||||
pub mod canvasgradient;
|
pub mod canvasgradient;
|
||||||
pub mod canvaspattern;
|
pub mod canvaspattern;
|
||||||
pub mod canvasrenderingcontext2d;
|
pub mod canvasrenderingcontext2d;
|
||||||
|
|
18
components/script/dom/webidls/BroadcastChannel.webidl
Normal file
18
components/script/dom/webidls/BroadcastChannel.webidl
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
/*
|
||||||
|
* The origin of this IDL file is:
|
||||||
|
* https://html.spec.whatwg.org/multipage/#broadcastchannel
|
||||||
|
*/
|
||||||
|
|
||||||
|
[Exposed=(Window,Worker)]
|
||||||
|
interface BroadcastChannel : EventTarget {
|
||||||
|
constructor(DOMString name);
|
||||||
|
|
||||||
|
readonly attribute DOMString name;
|
||||||
|
[Throws] void postMessage(any message);
|
||||||
|
void close();
|
||||||
|
attribute EventHandler onmessage;
|
||||||
|
attribute EventHandler onmessageerror;
|
||||||
|
};
|
|
@ -1407,8 +1407,8 @@ impl Window {
|
||||||
.upcast::<Node>()
|
.upcast::<Node>()
|
||||||
.teardown(self.layout_chan());
|
.teardown(self.layout_chan());
|
||||||
|
|
||||||
// Tell the constellation to drop the sender to our message-port router, if there is any.
|
// Remove the infra for managing messageports and broadcast channels.
|
||||||
self.upcast::<GlobalScope>().remove_message_ports_router();
|
self.upcast::<GlobalScope>().remove_web_messaging_infra();
|
||||||
|
|
||||||
// Clean up any active promises
|
// Clean up any active promises
|
||||||
// https://github.com/servo/servo/issues/15318
|
// https://github.com/servo/servo/issues/15318
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub mod serializable;
|
||||||
pub mod transferable;
|
pub mod transferable;
|
||||||
pub mod webdriver_msg;
|
pub mod webdriver_msg;
|
||||||
|
|
||||||
use crate::serializable::BlobImpl;
|
use crate::serializable::{BlobData, BlobImpl};
|
||||||
use crate::transferable::MessagePortImpl;
|
use crate::transferable::MessagePortImpl;
|
||||||
use crate::webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
use crate::webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
||||||
use bluetooth_traits::BluetoothRequest;
|
use bluetooth_traits::BluetoothRequest;
|
||||||
|
@ -955,6 +955,48 @@ pub struct StructuredSerializedData {
|
||||||
pub ports: Option<HashMap<MessagePortId, MessagePortImpl>>,
|
pub ports: Option<HashMap<MessagePortId, MessagePortImpl>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StructuredSerializedData {
|
||||||
|
/// Clone the serialized data for use with broadcast-channels.
|
||||||
|
pub fn clone_for_broadcast(&self) -> StructuredSerializedData {
|
||||||
|
let serialized = self.serialized.clone();
|
||||||
|
|
||||||
|
let blobs = if let Some(blobs) = self.blobs.as_ref() {
|
||||||
|
let mut blob_clones = HashMap::with_capacity(blobs.len());
|
||||||
|
|
||||||
|
for (original_id, blob) in blobs.iter() {
|
||||||
|
let type_string = blob.type_string();
|
||||||
|
|
||||||
|
if let BlobData::Memory(ref bytes) = blob.blob_data() {
|
||||||
|
let blob_clone = BlobImpl::new_from_bytes(bytes.clone(), type_string);
|
||||||
|
|
||||||
|
// Note: we insert the blob at the original id,
|
||||||
|
// otherwise this will not match the storage key as serialized by SM in `serialized`.
|
||||||
|
// The clone has it's own new Id however.
|
||||||
|
blob_clones.insert(original_id.clone(), blob_clone);
|
||||||
|
} else {
|
||||||
|
// Not panicking only because this is called from the constellation.
|
||||||
|
warn!("Serialized blob not in memory format(should never happen).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(blob_clones)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.ports.is_some() {
|
||||||
|
// Not panicking only because this is called from the constellation.
|
||||||
|
warn!("Attempt to broadcast structured serialized data including ports(should never happen).");
|
||||||
|
}
|
||||||
|
|
||||||
|
StructuredSerializedData {
|
||||||
|
serialized,
|
||||||
|
blobs,
|
||||||
|
// Ports cannot be broadcast.
|
||||||
|
ports: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A task on the https://html.spec.whatwg.org/multipage/#port-message-queue
|
/// A task on the https://html.spec.whatwg.org/multipage/#port-message-queue
|
||||||
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
|
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||||
pub struct PortMessageTask {
|
pub struct PortMessageTask {
|
||||||
|
@ -979,6 +1021,27 @@ pub enum MessagePortMsg {
|
||||||
NewTask(MessagePortId, PortMessageTask),
|
NewTask(MessagePortId, PortMessageTask),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Message for communication between the constellation and a global managing broadcast channels.
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct BroadcastMsg {
|
||||||
|
/// The origin of this message.
|
||||||
|
pub origin: ImmutableOrigin,
|
||||||
|
/// The name of the channel.
|
||||||
|
pub channel_name: String,
|
||||||
|
/// A data-holder for serialized data.
|
||||||
|
pub data: StructuredSerializedData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for BroadcastMsg {
|
||||||
|
fn clone(&self) -> BroadcastMsg {
|
||||||
|
BroadcastMsg {
|
||||||
|
data: self.data.clone_for_broadcast(),
|
||||||
|
origin: self.origin.clone(),
|
||||||
|
channel_name: self.channel_name.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The type of MediaSession action.
|
/// The type of MediaSession action.
|
||||||
/// https://w3c.github.io/mediasession/#enumdef-mediasessionaction
|
/// https://w3c.github.io/mediasession/#enumdef-mediasessionaction
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use crate::AnimationState;
|
use crate::AnimationState;
|
||||||
use crate::AuxiliaryBrowsingContextLoadInfo;
|
use crate::AuxiliaryBrowsingContextLoadInfo;
|
||||||
|
use crate::BroadcastMsg;
|
||||||
use crate::DocumentState;
|
use crate::DocumentState;
|
||||||
use crate::IFrameLoadInfoWithData;
|
use crate::IFrameLoadInfoWithData;
|
||||||
use crate::LayoutControlMsg;
|
use crate::LayoutControlMsg;
|
||||||
|
@ -22,7 +23,8 @@ use euclid::Size2D;
|
||||||
use gfx_traits::Epoch;
|
use gfx_traits::Epoch;
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||||
use msg::constellation_msg::{
|
use msg::constellation_msg::{
|
||||||
BrowsingContextId, MessagePortId, MessagePortRouterId, PipelineId, TopLevelBrowsingContextId,
|
BroadcastChannelRouterId, BrowsingContextId, MessagePortId, MessagePortRouterId, PipelineId,
|
||||||
|
TopLevelBrowsingContextId,
|
||||||
};
|
};
|
||||||
use msg::constellation_msg::{HistoryStateId, TraversalDirection};
|
use msg::constellation_msg::{HistoryStateId, TraversalDirection};
|
||||||
use net_traits::request::RequestBuilder;
|
use net_traits::request::RequestBuilder;
|
||||||
|
@ -142,6 +144,21 @@ pub enum ScriptMsg {
|
||||||
RemoveMessagePort(MessagePortId),
|
RemoveMessagePort(MessagePortId),
|
||||||
/// Entangle two message-ports.
|
/// Entangle two message-ports.
|
||||||
EntanglePorts(MessagePortId, MessagePortId),
|
EntanglePorts(MessagePortId, MessagePortId),
|
||||||
|
/// A global has started managing broadcast-channels.
|
||||||
|
NewBroadcastChannelRouter(
|
||||||
|
BroadcastChannelRouterId,
|
||||||
|
IpcSender<BroadcastMsg>,
|
||||||
|
ImmutableOrigin,
|
||||||
|
),
|
||||||
|
/// A global has stopped managing broadcast-channels.
|
||||||
|
RemoveBroadcastChannelRouter(BroadcastChannelRouterId, ImmutableOrigin),
|
||||||
|
/// A global started managing broadcast channels for a given channel-name.
|
||||||
|
NewBroadcastChannelNameInRouter(BroadcastChannelRouterId, String, ImmutableOrigin),
|
||||||
|
/// A global stopped managing broadcast channels for a given channel-name.
|
||||||
|
RemoveBroadcastChannelNameInRouter(BroadcastChannelRouterId, String, ImmutableOrigin),
|
||||||
|
/// Broadcast a message to all same-origin broadcast channels,
|
||||||
|
/// excluding the source of the broadcast.
|
||||||
|
ScheduleBroadcast(BroadcastChannelRouterId, BroadcastMsg),
|
||||||
/// Forward a message to the embedder.
|
/// Forward a message to the embedder.
|
||||||
ForwardToEmbedder(EmbedderMsg),
|
ForwardToEmbedder(EmbedderMsg),
|
||||||
/// Requests are sent to constellation and fetches are checked manually
|
/// Requests are sent to constellation and fetches are checked manually
|
||||||
|
@ -280,6 +297,11 @@ impl fmt::Debug for ScriptMsg {
|
||||||
RemoveMessagePort(..) => "RemoveMessagePort",
|
RemoveMessagePort(..) => "RemoveMessagePort",
|
||||||
MessagePortShipped(..) => "MessagePortShipped",
|
MessagePortShipped(..) => "MessagePortShipped",
|
||||||
EntanglePorts(..) => "EntanglePorts",
|
EntanglePorts(..) => "EntanglePorts",
|
||||||
|
NewBroadcastChannelRouter(..) => "NewBroadcastChannelRouter",
|
||||||
|
RemoveBroadcastChannelRouter(..) => "RemoveBroadcastChannelRouter",
|
||||||
|
RemoveBroadcastChannelNameInRouter(..) => "RemoveBroadcastChannelNameInRouter",
|
||||||
|
NewBroadcastChannelNameInRouter(..) => "NewBroadcastChannelNameInRouter",
|
||||||
|
ScheduleBroadcast(..) => "ScheduleBroadcast",
|
||||||
ForwardToEmbedder(..) => "ForwardToEmbedder",
|
ForwardToEmbedder(..) => "ForwardToEmbedder",
|
||||||
InitiateNavigateRequest(..) => "InitiateNavigateRequest",
|
InitiateNavigateRequest(..) => "InitiateNavigateRequest",
|
||||||
BroadcastStorageEvent(..) => "BroadcastStorageEvent",
|
BroadcastStorageEvent(..) => "BroadcastStorageEvent",
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
[noopener-noreferrer-BarProp.window.html]
|
[noopener-noreferrer-BarProp.window.html]
|
||||||
|
expected: TIMEOUT
|
||||||
[window.open() with noopener should have all bars visible]
|
[window.open() with noopener should have all bars visible]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[All bars visible]
|
[All bars visible]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[window.open() with noreferrer should have all bars visible]
|
[window.open() with noreferrer should have all bars visible]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
[noopener-noreferrer-sizing.window.html]
|
|
||||||
[window.open() with noreferrer should have equal viewport width and height]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[window.open() with noopener should have equal viewport width and height]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
|
|
||||||
|
|
||||||
[window-open-noopener.html?indexed]
|
[window-open-noopener.html?indexed]
|
||||||
expected: ERROR
|
|
||||||
[window.open() with 'noopener' should not reuse existing target]
|
[window.open() with 'noopener' should not reuse existing target]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -49,30 +48,6 @@
|
||||||
[noopener needs to be present as a token on its own yet again]
|
[noopener needs to be present as a token on its own yet again]
|
||||||
expected: NOTRUN
|
expected: NOTRUN
|
||||||
|
|
||||||
[Trailing noopener should work]
|
|
||||||
expected: NOTRUN
|
|
||||||
|
|
||||||
[Leading noopener should work]
|
|
||||||
expected: NOTRUN
|
|
||||||
|
|
||||||
[Interior noopener should work]
|
|
||||||
expected: NOTRUN
|
|
||||||
|
|
||||||
[noopener=1 means the same as noopener]
|
|
||||||
expected: NOTRUN
|
|
||||||
|
|
||||||
[noopener=0 means lack of noopener]
|
|
||||||
expected: NOTRUN
|
|
||||||
|
|
||||||
[noopener separated only by spaces should work]
|
|
||||||
expected: NOTRUN
|
|
||||||
|
|
||||||
[window.open() with 'noopener' should reuse existing target]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[noreferrer should also suppress opener when reusing existing target]
|
|
||||||
expected: NOTRUN
|
|
||||||
|
|
||||||
|
|
||||||
[window-open-noopener.html?_self]
|
[window-open-noopener.html?_self]
|
||||||
[noopener window.open targeting _self]
|
[noopener window.open targeting _self]
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[window-open-noreferrer.html]
|
|
||||||
[window.open() with "noreferrer" tests]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -332,18 +332,12 @@
|
||||||
[CanvasRenderingContext2D interface: operation scrollPathIntoView(Path2D)]
|
[CanvasRenderingContext2D interface: operation scrollPathIntoView(Path2D)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface: attribute name]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ApplicationCache interface object name]
|
[ApplicationCache interface object name]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[DOMStringList interface: calling item(unsigned long) on location.ancestorOrigins with too few arguments must throw TypeError]
|
[DOMStringList interface: calling item(unsigned long) on location.ancestorOrigins with too few arguments must throw TypeError]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface: existence and properties of interface prototype object's "constructor" property]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[BarProp interface object length]
|
[BarProp interface object length]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -704,9 +698,6 @@
|
||||||
[OffscreenCanvasRenderingContext2D interface object name]
|
[OffscreenCanvasRenderingContext2D interface object name]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface: attribute onmessage]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[ElementInternals interface object name]
|
[ElementInternals interface object name]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -800,9 +791,6 @@
|
||||||
[ApplicationCache interface: attribute onerror]
|
[ApplicationCache interface: attribute onerror]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface: existence and properties of interface object]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGElement interface: attribute onsubmit]
|
[SVGElement interface: attribute onsubmit]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -968,9 +956,6 @@
|
||||||
[SVGElement interface: attribute onkeydown]
|
[SVGElement interface: attribute onkeydown]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface: existence and properties of interface prototype object's @@unscopables property]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Path2D interface: existence and properties of interface prototype object's "constructor" property]
|
[Path2D interface: existence and properties of interface prototype object's "constructor" property]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1067,9 +1052,6 @@
|
||||||
[OffscreenCanvas interface object name]
|
[OffscreenCanvas interface object name]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface: operation close()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[SVGElement interface: attribute onresize]
|
[SVGElement interface: attribute onresize]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1166,9 +1148,6 @@
|
||||||
[ImageBitmap interface: attribute width]
|
[ImageBitmap interface: attribute width]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface: operation postMessage(any)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[DataTransfer interface: attribute types]
|
[DataTransfer interface: attribute types]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1214,18 +1193,12 @@
|
||||||
[SVGElement interface: attribute onvolumechange]
|
[SVGElement interface: attribute onvolumechange]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface object name]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CanvasRenderingContext2D interface: attribute textBaseline]
|
[CanvasRenderingContext2D interface: attribute textBaseline]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[ImageBitmapRenderingContext interface object length]
|
[ImageBitmapRenderingContext interface object length]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface: existence and properties of interface prototype object]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Path2D interface: operation addPath(Path2D, DOMMatrix2DInit)]
|
[Path2D interface: operation addPath(Path2D, DOMMatrix2DInit)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1307,9 +1280,6 @@
|
||||||
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "scrollPathIntoView()" with the proper type]
|
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "scrollPathIntoView()" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface: attribute onmessageerror]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[OffscreenCanvas interface: operation convertToBlob(ImageEncodeOptions)]
|
[OffscreenCanvas interface: operation convertToBlob(ImageEncodeOptions)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -1331,9 +1301,6 @@
|
||||||
[ValidityState interface: document.createElement("input").validity must inherit property "valid" with the proper type]
|
[ValidityState interface: document.createElement("input").validity must inherit property "valid" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[BroadcastChannel interface object length]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Location interface: stringifier]
|
[Location interface: stringifier]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -4442,4 +4409,3 @@
|
||||||
|
|
||||||
[HTMLImageElement interface: document.createElement("img") must inherit property "loading" with the proper type]
|
[HTMLImageElement interface: document.createElement("img") must inherit property "loading" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
[no-coop-coep.https.any.worker.html]
|
[no-coop-coep.https.any.worker.html]
|
||||||
[SharedArrayBuffer over BroadcastChannel without COOP+COEP]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Bonus: self.crossOriginIsolated]
|
[Bonus: self.crossOriginIsolated]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
||||||
[no-coop-coep.https.any.html]
|
[no-coop-coep.https.any.html]
|
||||||
[SharedArrayBuffer over BroadcastChannel without COOP+COEP]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Bonus: self.crossOriginIsolated]
|
[Bonus: self.crossOriginIsolated]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
[rel-base-target.html]
|
[rel-base-target.html]
|
||||||
|
expected: TIMEOUT
|
||||||
[<form rel="noreferrer opener"> with <base target>]
|
[<form rel="noreferrer opener"> with <base target>]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
[htmlanchorelement_noopener.html]
|
[htmlanchorelement_noopener.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
expected: ERROR
|
|
||||||
[Check that targeting of rel=noopener with a given name ignores an existing window with that name]
|
[Check that targeting of rel=noopener with a given name ignores an existing window with that name]
|
||||||
expected: NOTRUN
|
expected: NOTRUN
|
||||||
|
|
||||||
|
@ -11,7 +10,7 @@
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Check that targeting of rel=noopener with a given name reuses an existing window with that name]
|
[Check that targeting of rel=noopener with a given name reuses an existing window with that name]
|
||||||
expected: NOTRUN
|
expected: FAIL
|
||||||
|
|
||||||
[Check that rel=noopener with target=_self does a normal load]
|
[Check that rel=noopener with target=_self does a normal load]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,40 +1,20 @@
|
||||||
[target_blank_implicit_noopener.html]
|
[target_blank_implicit_noopener.html]
|
||||||
[Anchor element with target=_blank with rel=opener+noopener]
|
expected: TIMEOUT
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Area element with target=_blank with rel=opener+noopener]
|
[Area element with target=_blank with rel=opener+noopener]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[Anchor element with target=_blank with rel=noopener+opener+noreferrer]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Anchor element with target=_blank with rel=opener]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Anchor element with target=_blank with rel=noopener+opener]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Area element with target=_blank with rel=noopener]
|
[Area element with target=_blank with rel=noopener]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[Area element with target=_blank with rel=opener]
|
[Area element with target=_blank with rel=opener]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[Anchor element with target=_blank with implicit rel=noopener]
|
[Anchor element with target=_blank with implicit rel=noopener]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Anchor element with target=_blank with rel=opener+noreferrer]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Area element with target=_blank with implicit rel=noopener]
|
[Area element with target=_blank with implicit rel=noopener]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[Anchor element with target=_blank with rel=noreferrer]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Area element with target=_blank with rel=noopener+opener]
|
[Area element with target=_blank with rel=noopener+opener]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[Anchor element with target=_blank with rel=noopener]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
[target_blank_implicit_noopener_base.html]
|
[target_blank_implicit_noopener_base.html]
|
||||||
|
expected: TIMEOUT
|
||||||
[Anchor element with base target=_blank with implicit rel=noopener]
|
[Anchor element with base target=_blank with implicit rel=noopener]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[MessageEvent-trusted.html]
|
|
||||||
[With a BroadcastChannel]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
[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
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
[blobs.html]
|
|
||||||
[Blobs work with workers on BroadcastChannel]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Blobs work on BroadcastChannel]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
[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
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[origin.window.html]
|
|
||||||
expected: TIMEOUT
|
|
||||||
[Serialization of BroadcastChannel origin]
|
|
||||||
expected: TIMEOUT
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[sandbox.html]
|
|
||||||
[Creating BroadcastChannel in an opaque origin]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,17 +1,4 @@
|
||||||
[workers.html]
|
[workers.html]
|
||||||
expected: TIMEOUT
|
|
||||||
[BroadcastChannel used after a worker self.close()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[BroadcastChannel works in shared workers]
|
[BroadcastChannel works in shared workers]
|
||||||
expected: FAIL
|
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
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,3 @@
|
||||||
|
|
||||||
[Worker has an opaque origin.]
|
[Worker has an opaque origin.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Worker can access BroadcastChannel]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,6 @@
|
||||||
[The Path2D interface object should be exposed.]
|
[The Path2D interface object should be exposed.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[The BroadcastChannel interface object should be exposed.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[The ReadableStream interface object should be exposed.]
|
[The ReadableStream interface object should be exposed.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -59,3 +56,4 @@
|
||||||
|
|
||||||
[The IDBTransaction interface object should be exposed.]
|
[The IDBTransaction interface object should be exposed.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ test_interfaces([
|
||||||
"BeforeUnloadEvent",
|
"BeforeUnloadEvent",
|
||||||
"BiquadFilterNode",
|
"BiquadFilterNode",
|
||||||
"Blob",
|
"Blob",
|
||||||
|
"BroadcastChannel",
|
||||||
"CanvasGradient",
|
"CanvasGradient",
|
||||||
"CanvasRenderingContext2D",
|
"CanvasRenderingContext2D",
|
||||||
"CanvasPattern",
|
"CanvasPattern",
|
||||||
|
|
|
@ -7,6 +7,7 @@ function test_interfaces(interfaceNamesInGlobalScope) {
|
||||||
"ArrayBuffer",
|
"ArrayBuffer",
|
||||||
"Atomics",
|
"Atomics",
|
||||||
"Boolean",
|
"Boolean",
|
||||||
|
"BroadcastChannel",
|
||||||
"Crypto",
|
"Crypto",
|
||||||
"DataView",
|
"DataView",
|
||||||
"Date",
|
"Date",
|
||||||
|
|
|
@ -8,6 +8,7 @@ importScripts("interfaces.js");
|
||||||
// IMPORTANT: Do not change the list below without review from a DOM peer!
|
// IMPORTANT: Do not change the list below without review from a DOM peer!
|
||||||
test_interfaces([
|
test_interfaces([
|
||||||
"Blob",
|
"Blob",
|
||||||
|
"BroadcastChannel",
|
||||||
"CanvasGradient",
|
"CanvasGradient",
|
||||||
"CanvasPattern",
|
"CanvasPattern",
|
||||||
"CloseEvent",
|
"CloseEvent",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue