aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/globalscope.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/globalscope.rs')
-rw-r--r--components/script/dom/globalscope.rs379
1 files changed, 314 insertions, 65 deletions
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index de3e83101e1..9e55f593f59 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -3,7 +3,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
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::PermissionStatusBinding::PermissionState;
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
@@ -19,6 +21,7 @@ use crate::dom::bindings::structuredclone;
use crate::dom::bindings::utils::to_frozen_array;
use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
use crate::dom::blob::Blob;
+use crate::dom::broadcastchannel::BroadcastChannel;
use crate::dom::crypto::Crypto;
use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
use crate::dom::errorevent::ErrorEvent;
@@ -58,20 +61,22 @@ use crate::timers::{OneshotTimers, TimerCallback};
use content_security_policy::CspList;
use devtools_traits::{PageError, ScriptToDevtoolsControlMsg};
use dom_struct::dom_struct;
+use embedder_traits::EmbedderMsg;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::glue::{IsWrapper, UnwrapObjectDynamic};
-use js::jsapi::JSContext;
-use js::jsapi::JSObject;
use js::jsapi::{CurrentGlobalOrNull, GetNonCCWObjectGlobal};
use js::jsapi::{HandleObject, Heap};
+use js::jsapi::{JSContext, JSObject, SourceText};
use js::jsval::{JSVal, UndefinedValue};
use js::panic::maybe_resume_unwind;
-use js::rust::wrappers::EvaluateUtf8;
+use js::rust::wrappers::Evaluate2;
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::{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::filemanager_thread::{
FileManagerResult, FileManagerThreadMsg, ReadFileProgress, RelativePos,
@@ -82,16 +87,17 @@ 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::transferable::MessagePortImpl;
use script_traits::{
- MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg, ScriptToConstellationChan, TimerEvent,
+ BroadcastMsg, MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg,
+ ScriptToConstellationChan, TimerEvent,
};
use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource};
use servo_url::{MutableOrigin, ServoUrl};
-use smallvec::SmallVec;
use std::borrow::Cow;
-use std::cell::{Cell, RefCell};
+use std::cell::{Cell, RefCell, RefMut};
use std::collections::hash_map::Entry;
use std::collections::{HashMap, VecDeque};
use std::ffi::CString;
+use std::marker::PhantomData;
use std::mem;
use std::ops::Index;
use std::rc::Rc;
@@ -99,13 +105,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use time::{get_time, Timespec};
use uuid::Uuid;
-use webgpu::wgpu::{
- id::{
- AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandEncoderId, ComputePipelineId,
- DeviceId, PipelineLayoutId, ShaderModuleId,
- },
- Backend,
-};
#[derive(JSTraceable)]
pub struct AutoCloseWorker(Arc<AtomicBool>);
@@ -124,6 +123,9 @@ pub struct GlobalScope {
/// The message-port router id for this global, if it is managing ports.
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.
blob_state: DomRefCell<BlobState>,
@@ -181,6 +183,9 @@ pub struct GlobalScope {
/// The origin of the globalscope
origin: MutableOrigin,
+ /// A map for storing the previous permission state read results.
+ permission_state_invocation_results: DomRefCell<HashMap<String, PermissionState>>,
+
/// The microtask queue associated with this global.
///
/// It is refcounted because windows in the same script thread share the
@@ -237,6 +242,13 @@ struct MessageListener {
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.
struct TimerListener {
canceller: TaskCanceller,
@@ -310,6 +322,23 @@ pub struct ManagedMessagePort {
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.
#[derive(JSTraceable, MallocSizeOf)]
#[unrooted_must_root_lint::must_root]
@@ -323,6 +352,29 @@ pub enum MessagePortState {
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 {
/// Handle a timer-event coming-in over IPC,
/// by queuing the appropriate task on the relevant event-loop.
@@ -501,6 +553,7 @@ impl GlobalScope {
) -> Self {
Self {
message_port_state: DomRefCell::new(MessagePortState::UnManaged),
+ broadcast_channel_state: DomRefCell::new(BroadcastChannelState::UnManaged),
blob_state: DomRefCell::new(BlobState::UnManaged),
eventtarget: EventTarget::new_inherited(),
crypto: Default::default(),
@@ -519,6 +572,7 @@ impl GlobalScope {
timers: OneshotTimers::new(scheduler_chan),
init_timers: Default::default(),
origin,
+ permission_state_invocation_results: Default::default(),
microtask_queue,
list_auto_close_worker: Default::default(),
event_source_tracker: DOMTracker::new(),
@@ -613,11 +667,18 @@ impl GlobalScope {
pub fn perform_a_dom_garbage_collection_checkpoint(&self) {
self.perform_a_message_port_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,
/// 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) =
&*self.message_port_state.borrow()
{
@@ -628,6 +689,22 @@ impl GlobalScope {
*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>
pub fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) {
if let MessagePortState::Managed(_id, message_ports) =
@@ -789,6 +866,115 @@ impl GlobalScope {
.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.
pub fn route_task_to_port(&self, port_id: MessagePortId, task: PortMessageTask) {
let should_dispatch = if let MessagePortState::Managed(_id, message_ports) =
@@ -905,6 +1091,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
pub fn track_message_port(&self, dom_port: &MessagePort, port_impl: Option<MessagePortImpl>) {
let mut current_state = self.message_port_state.borrow_mut();
@@ -1400,8 +1673,7 @@ impl GlobalScope {
let resource_threads = self.resource_threads();
let (chan, recv) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
let origin = get_blob_origin(&self.get_url());
- let check_url_validity = false;
- let msg = FileManagerThreadMsg::ReadFile(chan, id, check_url_validity, origin);
+ let msg = FileManagerThreadMsg::ReadFile(chan, id, origin);
let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
recv
}
@@ -1427,6 +1699,12 @@ impl GlobalScope {
}
}
+ pub fn permission_state_invocation_results(
+ &self,
+ ) -> &DomRefCell<HashMap<String, PermissionState>> {
+ &self.permission_state_invocation_results
+ }
+
pub fn track_worker(&self, closing_worker: Arc<AtomicBool>) {
self.list_auto_close_worker
.borrow_mut()
@@ -1632,6 +1910,14 @@ impl GlobalScope {
&self.script_to_constellation_chan
}
+ pub fn send_to_embedder(&self, msg: EmbedderMsg) {
+ self.send_to_constellation(ScriptMsg::ForwardToEmbedder(msg));
+ }
+
+ pub fn send_to_constellation(&self, msg: ScriptMsg) {
+ self.script_to_constellation_chan().send(msg).unwrap();
+ }
+
pub fn scheduler_chan(&self) -> &IpcSender<TimerSchedulerMsg> {
&self.scheduler_chan
}
@@ -1872,15 +2158,20 @@ impl GlobalScope {
let ar = enter_realm(&*self);
let _aes = AutoEntryScript::new(self);
- let options = CompileOptionsWrapper::new(*cx, filename.as_ptr(), line_number);
+ let options =
+ unsafe { CompileOptionsWrapper::new(*cx, filename.as_ptr(), line_number) };
debug!("evaluating Dom string");
let result = unsafe {
- EvaluateUtf8(
+ Evaluate2(
*cx,
options.ptr,
- code.as_ptr() as *const _,
- code.len() as libc::size_t,
+ &mut SourceText {
+ units_: code.as_ptr() as *const _,
+ length_: code.len() as u32,
+ ownsUnits_: false,
+ _phantom_0: PhantomData,
+ },
rval,
)
};
@@ -2155,50 +2446,8 @@ impl GlobalScope {
None
}
- pub fn wgpu_create_adapter_ids(&self) -> SmallVec<[AdapterId; 4]> {
- self.gpu_id_hub.borrow_mut().create_adapter_ids()
- }
-
- pub fn wgpu_create_bind_group_id(&self, backend: Backend) -> BindGroupId {
- self.gpu_id_hub.borrow_mut().create_bind_group_id(backend)
- }
-
- pub fn wgpu_create_bind_group_layout_id(&self, backend: Backend) -> BindGroupLayoutId {
- self.gpu_id_hub
- .borrow_mut()
- .create_bind_group_layout_id(backend)
- }
-
- pub fn wgpu_create_buffer_id(&self, backend: Backend) -> BufferId {
- self.gpu_id_hub.borrow_mut().create_buffer_id(backend)
- }
-
- pub fn wgpu_create_device_id(&self, backend: Backend) -> DeviceId {
- self.gpu_id_hub.borrow_mut().create_device_id(backend)
- }
-
- pub fn wgpu_create_pipeline_layout_id(&self, backend: Backend) -> PipelineLayoutId {
- self.gpu_id_hub
- .borrow_mut()
- .create_pipeline_layout_id(backend)
- }
-
- pub fn wgpu_create_shader_module_id(&self, backend: Backend) -> ShaderModuleId {
- self.gpu_id_hub
- .borrow_mut()
- .create_shader_module_id(backend)
- }
-
- pub fn wgpu_create_compute_pipeline_id(&self, backend: Backend) -> ComputePipelineId {
- self.gpu_id_hub
- .borrow_mut()
- .create_compute_pipeline_id(backend)
- }
-
- pub fn wgpu_create_command_encoder_id(&self, backend: Backend) -> CommandEncoderId {
- self.gpu_id_hub
- .borrow_mut()
- .create_command_encoder_id(backend)
+ pub fn wgpu_id_hub(&self) -> RefMut<Identities> {
+ self.gpu_id_hub.borrow_mut()
}
}