diff options
Diffstat (limited to 'components/script/dom/globalscope.rs')
-rw-r--r-- | components/script/dom/globalscope.rs | 379 |
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() } } |