1use std::borrow::ToOwned;
88use std::cell::{Cell, OnceCell, RefCell};
89use std::collections::hash_map::Entry;
90use std::collections::{HashMap, HashSet, VecDeque};
91use std::marker::PhantomData;
92use std::mem::replace;
93use std::rc::{Rc, Weak};
94use std::sync::Arc;
95use std::thread::JoinHandle;
96use std::{process, thread};
97
98use accesskit::{ActionRequest, TreeId};
99use background_hang_monitor_api::{
100 BackgroundHangMonitorControlMsg, BackgroundHangMonitorRegister, HangAlert,
101};
102use content_security_policy::sandboxing_directive::SandboxingFlagSet;
103use crossbeam_channel::{Receiver, Select, Sender, unbounded};
104#[cfg(feature = "devtools")]
105use devtools_traits::{
106 ChromeToDevtoolsControlMsg, DevtoolsControlMsg, DevtoolsPageInfo, NavigationState,
107 ScriptToDevtoolsControlMsg,
108};
109use embedder_traits::resources::{self, Resource};
110use embedder_traits::user_contents::{UserContentManagerId, UserContents};
111use embedder_traits::{
112 AnimationState, EmbedderControlId, EmbedderControlResponse, EmbedderProxy, FocusSequenceNumber,
113 GenericEmbedderProxy, InputEvent, InputEventAndId, InputEventOutcome, JSValue,
114 JavaScriptEvaluationError, JavaScriptEvaluationId, KeyboardEvent, MediaSessionActionType,
115 MediaSessionEvent, MediaSessionPlaybackState, MouseButtonAction, MouseButtonEvent,
116 NewWebViewDetails, PaintHitTestResult, Theme, ViewportDetails, WakeLockDelegate, WakeLockType,
117 WebDriverCommandMsg, WebDriverLoadStatus, WebDriverScriptCommand,
118};
119use euclid::default::Size2D as UntypedSize2D;
120use fonts::SystemFontServiceProxy;
121use ipc_channel::router::ROUTER;
122use keyboard_types::{Key, KeyState, Modifiers, NamedKey};
123use layout_api::{LayoutFactory, ScriptThreadFactory};
124use log::{debug, error, info, trace, warn};
125use media::WindowGLContext;
126use net::image_cache::ImageCacheFactoryImpl;
127use net_traits::pub_domains::registered_domain_name;
128use net_traits::{self, AsyncRuntime, FetchThread, ResourceThreads};
129use paint_api::{
130 PaintMessage, PaintProxy, PinchZoomInfos, PipelineExitSource, SendableFrameTree,
131 WebRenderExternalImageIdManager,
132};
133use profile_traits::mem::ProfilerMsg;
134use profile_traits::{mem, time};
135use rand::rngs::SmallRng;
136use rand::seq::IndexedRandom;
137use rand::{RngExt, SeedableRng, make_rng};
138use rustc_hash::{FxHashMap, FxHashSet};
139use script_traits::{
140 ConstellationInputEvent, DiscardBrowsingContext, DocumentActivity, MouseButtons,
141 NewPipelineInfo, ScriptThreadMessage, UpdatePipelineIdReason,
142};
143use servo_background_hang_monitor::HangMonitorRegister;
144use servo_base::generic_channel;
145use servo_base::generic_channel::{
146 GenericCallback, GenericSend, GenericSender, RoutedReceiver, SendError,
147};
148use servo_base::id::{
149 BrowsingContextId, CONSTELLATION_PIPELINE_NAMESPACE_ID, FIRST_CONTENT_PIPELINE_NAMESPACE_ID,
150 HistoryStateId, MessagePortId, MessagePortRouterId, PainterId, PipelineId, PipelineNamespace,
151 PipelineNamespaceId, PipelineNamespaceRequest, ScriptEventLoopId, WebViewId, WorkerId,
152};
153use servo_base::threadboost::{BoostAffinity, ThreadPriority};
154#[cfg(feature = "bluetooth")]
155use servo_bluetooth_traits::BluetoothRequest;
156use servo_canvas::canvas_paint_thread::CanvasPaintThread;
157use servo_canvas_traits::ConstellationCanvasMsg;
158use servo_canvas_traits::canvas::{CanvasId, CanvasMsg};
159use servo_config::{opts, pref};
160use servo_constellation_traits::{
161 AuxiliaryWebViewCreationRequest, AuxiliaryWebViewCreationResponse, ConstellationInterest,
162 EmbedderToConstellationMessage, HistoryTraversalSource, IFrameLoadInfo, IFrameLoadInfoWithData,
163 IFrameSizeMsg, LoadData, LogEntry, MessagePortMsg, NavigationHistoryBehavior, PaintMetricEvent,
164 PortMessageTask, PortTransferInfo, RemoteFocusOperation, SWManagerSenders,
165 ScreenshotReadinessResponse, ScriptToConstellationMessage, ScrollStateUpdate,
166 ServiceWorkerAlgorithm, ServiceWorkerManagerFactory, ServiceWorkerManagerMsg, ServiceWorkerMsg,
167 SessionHistoryTraversalRequest, StructuredSerializedData, TargetSnapshotParams,
168 TraversalDirection, UserContentManagerAction, WindowSizeType, WorkerAnimationFrameTick,
169};
170use servo_url::{Host, ImmutableOrigin, ServoUrl};
171use storage_traits::StorageThreads;
172use storage_traits::cache_storage::CacheStorageThreadMessage;
173use storage_traits::client_storage::ClientStorageThreadMessage;
174use storage_traits::indexeddb::{IndexedDBThreadMsg, SyncOperation};
175use storage_traits::webstorage_thread::{WebStorageThreadMsg, WebStorageType};
176#[cfg(feature = "webgpu")]
177use webgpu::canvas_context::WebGpuExternalImageMap;
178#[cfg(feature = "webgpu")]
179use webgpu_traits::{WebGPU, WebGPURequest};
180
181use super::embedder::ConstellationToEmbedderMsg;
182use crate::broadcastchannel::BroadcastChannels;
183use crate::browsingcontext::{
184 AllBrowsingContextsIterator, BrowsingContext, FullyActiveBrowsingContextsIterator,
185 NewBrowsingContextInfo,
186};
187use crate::constellation_webview::{ConstellationWebView, OngoingHistoryTraversalRequest};
188use crate::event_loop::EventLoop;
189use crate::pipeline::Pipeline;
190use crate::serviceworker::ServiceWorkerUnprivilegedContent;
191use crate::session_history::{NeedsToReload, SessionHistoryChange, SessionHistoryDiff};
192
193struct PendingApprovalNavigation {
194 load_data: LoadData,
195 history_behaviour: NavigationHistoryBehavior,
196 target_snapshot_params: TargetSnapshotParams,
197}
198
199type PendingApprovalNavigations = FxHashMap<PipelineId, PendingApprovalNavigation>;
200
201#[derive(Debug)]
202enum TransferState {
204 Managed(MessagePortRouterId),
207 TransferInProgress(VecDeque<PortMessageTask>),
210 CompletionInProgress(MessagePortRouterId),
213 CompletionFailed(VecDeque<PortMessageTask>),
219 CompletionRequested(MessagePortRouterId, VecDeque<PortMessageTask>),
222}
223
224#[derive(Debug)]
225struct MessagePortInfo {
227 state: TransferState,
229
230 entangled_with: Option<MessagePortId>,
232}
233
234#[cfg(feature = "webgpu")]
235struct WebRenderWGPU {
237 webrender_external_image_id_manager: WebRenderExternalImageIdManager,
239
240 wgpu_image_map: WebGpuExternalImageMap,
242}
243
244#[derive(Default)]
248pub(crate) struct BrowsingContextGroup {
249 event_loops: HashMap<Host, Weak<EventLoop>>,
257
258 #[cfg(feature = "webgpu")]
260 webgpus: HashMap<Host, WebGPU>,
261}
262
263impl Drop for BrowsingContextGroup {
264 fn drop(&mut self) {
265 #[cfg(feature = "webgpu")]
266 for webgpu in self.webgpus.values() {
267 if let Some((sender, _)) = generic_channel::oneshot() &&
272 let Err(error) = webgpu.exit(sender)
273 {
274 warn!("Failed to request WebGPU exit: {error}.");
275 }
276 }
277 }
278}
279
280struct WorkerAnimationFrameProvider {
281 webview_id: WebViewId,
282 pipeline_id: PipelineId,
283 sender: GenericSender<WorkerAnimationFrameTick>,
284 tick_pending: bool,
285}
286
287pub struct Constellation<STF, SWF> {
301 namespace_receiver: RoutedReceiver<PipelineNamespaceRequest>,
305 pub(crate) namespace_ipc_sender: GenericSender<PipelineNamespaceRequest>,
306
307 event_loops: Vec<Weak<EventLoop>>,
311
312 pub(crate) script_sender: GenericSender<(WebViewId, PipelineId, ScriptToConstellationMessage)>,
315
316 script_receiver:
319 Receiver<Result<(WebViewId, PipelineId, ScriptToConstellationMessage), SendError>>,
320
321 pub(crate) background_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
324
325 background_monitor_register_join_handle: Option<JoinHandle<()>>,
327
328 background_monitor_control_sender: Option<GenericSender<BackgroundHangMonitorControlMsg>>,
331
332 #[cfg_attr(not(feature = "multiprocess"), expect(unused))]
335 pub(crate) background_hang_monitor_sender: GenericSender<HangAlert>,
336
337 background_hang_monitor_receiver: RoutedReceiver<HangAlert>,
340
341 sw_manager_receiver: RoutedReceiver<ServiceWorkerManagerMsg>,
343
344 sw_manager_sender: GenericSender<ServiceWorkerManagerMsg>,
348
349 pub(crate) layout_factory: Arc<dyn LayoutFactory>,
353
354 embedder_to_constellation_receiver: Receiver<EmbedderToConstellationMessage>,
356
357 pub(crate) embedder_proxy: EmbedderProxy,
361
362 pub(crate) constellation_to_embedder_proxy: GenericEmbedderProxy<ConstellationToEmbedderMsg>,
364
365 pub(crate) paint_proxy: PaintProxy,
368
369 webviews: FxHashMap<WebViewId, ConstellationWebView>,
371
372 pub(crate) public_resource_threads: ResourceThreads,
376
377 pub(crate) private_resource_threads: ResourceThreads,
382
383 pub(crate) public_storage_threads: StorageThreads,
387
388 pub(crate) private_storage_threads: StorageThreads,
393
394 pub(crate) system_font_service: Arc<SystemFontServiceProxy>,
397
398 #[cfg(feature = "devtools")]
401 pub(crate) devtools_sender: Option<Sender<DevtoolsControlMsg>>,
402
403 #[cfg(feature = "devtools")]
406 pub script_to_devtools_callback: OnceCell<Option<GenericCallback<ScriptToDevtoolsControlMsg>>>,
407
408 #[cfg(feature = "bluetooth")]
411 pub(crate) bluetooth_ipc_sender: GenericSender<BluetoothRequest>,
412
413 sw_managers: HashMap<ImmutableOrigin, GenericSender<ServiceWorkerMsg>>,
415
416 pub(crate) time_profiler_chan: time::ProfilerChan,
419
420 pub(crate) mem_profiler_chan: mem::ProfilerChan,
423
424 #[cfg(feature = "webgpu")]
426 webrender_wgpu: WebRenderWGPU,
427
428 message_ports: FxHashMap<MessagePortId, MessagePortInfo>,
430
431 message_port_routers: FxHashMap<MessagePortRouterId, GenericCallback<MessagePortMsg>>,
433
434 broadcast_channels: BroadcastChannels,
436
437 pipeline_interests: FxHashMap<ConstellationInterest, FxHashSet<PipelineId>>,
439
440 pipelines: FxHashMap<PipelineId, Pipeline>,
443
444 worker_animation_frame_providers: FxHashMap<WorkerId, WorkerAnimationFrameProvider>,
445
446 browsing_contexts: FxHashMap<BrowsingContextId, BrowsingContext>,
448
449 next_pipeline_namespace_id: Cell<PipelineNamespaceId>,
452
453 webdriver_load_status_sender: Option<(GenericSender<WebDriverLoadStatus>, PipelineId)>,
455
456 shutting_down: bool,
458
459 handled_warnings: VecDeque<(Option<String>, String)>,
462
463 random_pipeline_closure: Option<(SmallRng, f32)>,
466
467 phantom: PhantomData<(STF, SWF)>,
469
470 #[cfg(feature = "webgl")]
472 pub(crate) webgl_threads: Option<servo_canvas_traits::webgl::WebGLThreads>,
473
474 pub(crate) webxr_registry: Option<webxr_api::Registry>,
476
477 canvas: OnceCell<(Sender<ConstellationCanvasMsg>, GenericSender<CanvasMsg>)>,
479
480 pending_approval_navigations: PendingApprovalNavigations,
482
483 pressed_mouse_buttons: MouseButtons,
486
487 active_keyboard_modifiers: Modifiers,
489
490 hard_fail: bool,
492
493 active_media_session: Option<PipelineId>,
495
496 screen_wake_lock_count: u32,
499
500 wake_lock_provider: Box<dyn WakeLockDelegate>,
502
503 #[cfg(feature = "multiprocess")]
507 pub(crate) broken_image_icon_data: Vec<u8>,
508
509 #[cfg(feature = "multiprocess")]
511 pub(crate) process_manager: crate::process_manager::ProcessManager,
512
513 async_runtime: Box<dyn AsyncRuntime>,
515
516 event_loop_join_handles: Vec<JoinHandle<()>>,
519
520 pub(crate) privileged_urls: Vec<ServoUrl>,
522
523 pub(crate) image_cache_factory: Arc<ImageCacheFactoryImpl>,
527
528 pub(crate) user_contents_for_manager_id: FxHashMap<UserContentManagerId, UserContents>,
533
534 online: bool,
536}
537
538pub struct InitialConstellationState {
540 pub embedder_proxy: EmbedderProxy,
544
545 pub constellation_to_embedder_proxy: GenericEmbedderProxy<ConstellationToEmbedderMsg>,
547
548 pub paint_proxy: PaintProxy,
550
551 #[cfg(feature = "devtools")]
553 pub devtools_sender: Option<Sender<DevtoolsControlMsg>>,
554
555 #[cfg(feature = "bluetooth")]
557 pub bluetooth_thread: GenericSender<BluetoothRequest>,
558
559 pub system_font_service: Arc<SystemFontServiceProxy>,
561
562 pub public_resource_threads: ResourceThreads,
564
565 pub private_resource_threads: ResourceThreads,
567
568 pub public_storage_threads: StorageThreads,
570
571 pub private_storage_threads: StorageThreads,
573
574 pub time_profiler_chan: time::ProfilerChan,
576
577 pub mem_profiler_chan: mem::ProfilerChan,
579
580 pub webrender_external_image_id_manager: WebRenderExternalImageIdManager,
582
583 #[cfg(feature = "webgl")]
585 pub webgl_threads: Option<servo_canvas_traits::webgl::WebGLThreads>,
586
587 pub webxr_registry: Option<webxr_api::Registry>,
589
590 #[cfg(feature = "webgpu")]
591 pub wgpu_image_map: WebGpuExternalImageMap,
592
593 pub privileged_urls: Vec<ServoUrl>,
595
596 pub async_runtime: Box<dyn AsyncRuntime>,
598
599 pub wake_lock_provider: Box<dyn WakeLockDelegate>,
601}
602
603#[derive(Clone, Copy, Debug)]
608enum ExitPipelineMode {
609 Normal,
610 Force,
611}
612
613const WARNINGS_BUFFER_SIZE: usize = 32;
615
616impl<STF, SWF> Constellation<STF, SWF>
617where
618 STF: ScriptThreadFactory,
619 SWF: ServiceWorkerManagerFactory,
620{
621 #[servo_tracing::instrument(skip(state, layout_factory))]
623 pub fn start(
624 embedder_to_constellation_receiver: Receiver<EmbedderToConstellationMessage>,
625 state: InitialConstellationState,
626 layout_factory: Arc<dyn LayoutFactory>,
627 random_pipeline_closure_probability: Option<f32>,
628 random_pipeline_closure_seed: Option<usize>,
629 hard_fail: bool,
630 ) {
631 thread::Builder::new()
632 .name("Constellation".to_owned())
633 .spawn(move || {
634 servo_base::threadboost::boost_thread(ThreadPriority::Elevated, BoostAffinity::Boost);
635 let (script_ipc_sender, script_ipc_receiver) =
636 generic_channel::channel().expect("ipc channel failure");
637 let script_receiver = script_ipc_receiver.route_preserving_errors();
638
639 let (namespace_ipc_sender, namespace_ipc_receiver) =
640 generic_channel::channel().expect("ipc channel failure");
641 let namespace_receiver = namespace_ipc_receiver.route_preserving_errors();
642
643 let (background_hang_monitor_ipc_sender, background_hang_monitor_ipc_receiver) =
644 generic_channel::channel().expect("ipc channel failure");
645 let background_hang_monitor_receiver =
646 background_hang_monitor_ipc_receiver.route_preserving_errors();
647
648 let (sw_manager_sender, sw_manager_ipc_receiver) =
649 generic_channel::channel().expect("ipc channel failure");
650 let sw_manager_receiver = sw_manager_ipc_receiver.route_preserving_errors();
651
652 let (
657 background_monitor_register,
658 background_monitor_register_join_handle,
659 background_monitor_control_sender
660 ) = if opts::get().multiprocess {
661 (None, None, None)
662 } else {
663 let (
664 background_hang_monitor_control_ipc_sender,
665 background_hang_monitor_control_ipc_receiver,
666 ) = generic_channel::channel().expect("ipc channel failure");
667 let (register, join_handle) = HangMonitorRegister::init(
668 background_hang_monitor_ipc_sender.clone(),
669 background_hang_monitor_control_ipc_receiver,
670 opts::get().background_hang_monitor,
671 );
672 (
673 Some(register),
674 Some(join_handle),
675 Some(background_hang_monitor_control_ipc_sender),
676 )
677 };
678
679 PipelineNamespace::install(CONSTELLATION_PIPELINE_NAMESPACE_ID);
680
681 #[cfg(feature = "webgpu")]
682 let webrender_wgpu = WebRenderWGPU {
683 webrender_external_image_id_manager: state.webrender_external_image_id_manager,
684 wgpu_image_map: state.wgpu_image_map,
685 };
686
687 let broken_image_icon_data = resources::read_bytes(Resource::BrokenImageIcon);
688
689 let mut constellation: Constellation<STF, SWF> = Constellation {
690 event_loops: Default::default(),
691 sw_manager_sender,
692 sw_manager_receiver,
693 namespace_receiver,
694 namespace_ipc_sender,
695 script_sender: script_ipc_sender,
696 background_hang_monitor_sender: background_hang_monitor_ipc_sender,
697 background_hang_monitor_receiver,
698 background_monitor_register,
699 background_monitor_register_join_handle,
700 background_monitor_control_sender,
701 script_receiver,
702 embedder_to_constellation_receiver,
703 layout_factory,
704 embedder_proxy: state.embedder_proxy,
705 constellation_to_embedder_proxy: state.constellation_to_embedder_proxy,
706 paint_proxy: state.paint_proxy,
707 webviews: Default::default(),
708 #[cfg(feature = "devtools")]
709 devtools_sender: state.devtools_sender,
710 #[cfg(feature = "devtools")]
711 script_to_devtools_callback: Default::default(),
712 #[cfg(feature = "bluetooth")]
713 bluetooth_ipc_sender: state.bluetooth_thread,
714 public_resource_threads: state.public_resource_threads,
715 private_resource_threads: state.private_resource_threads,
716 public_storage_threads: state.public_storage_threads,
717 private_storage_threads: state.private_storage_threads,
718 system_font_service: state.system_font_service,
719 sw_managers: Default::default(),
720 message_ports: Default::default(),
721 message_port_routers: Default::default(),
722 broadcast_channels: Default::default(),
723 pipeline_interests: Default::default(),
724 pipelines: Default::default(),
725 worker_animation_frame_providers: Default::default(),
726 browsing_contexts: Default::default(),
727 next_pipeline_namespace_id: Cell::new(FIRST_CONTENT_PIPELINE_NAMESPACE_ID),
728 time_profiler_chan: state.time_profiler_chan,
729 mem_profiler_chan: state.mem_profiler_chan.clone(),
730 phantom: PhantomData,
731 webdriver_load_status_sender: None,
732 #[cfg(feature = "webgpu")]
733 webrender_wgpu,
734 shutting_down: false,
735 handled_warnings: VecDeque::new(),
736 random_pipeline_closure: random_pipeline_closure_probability.map(|probability| {
737 let rng = random_pipeline_closure_seed
738 .map(|seed| SmallRng::seed_from_u64(seed as u64))
739 .unwrap_or_else(make_rng);
740 warn!("Randomly closing pipelines using seed {random_pipeline_closure_seed:?}.");
741 (rng, probability)
742 }),
743 #[cfg(feature = "webgl")]
744 webgl_threads: state.webgl_threads,
745 webxr_registry: state.webxr_registry,
746 canvas: OnceCell::new(),
747 pending_approval_navigations: Default::default(),
748 pressed_mouse_buttons: MouseButtons::empty(),
749 active_keyboard_modifiers: Modifiers::empty(),
750 hard_fail,
751 active_media_session: None,
752 screen_wake_lock_count: 0,
753 wake_lock_provider: state.wake_lock_provider,
754 #[cfg(feature = "multiprocess")]
755 broken_image_icon_data: broken_image_icon_data.clone(),
756 #[cfg(feature = "multiprocess")]
757 process_manager: crate::process_manager::ProcessManager::new(state.mem_profiler_chan),
758 async_runtime: state.async_runtime,
759 event_loop_join_handles: Default::default(),
760 privileged_urls: state.privileged_urls,
761 image_cache_factory: Arc::new(ImageCacheFactoryImpl::new(
762 broken_image_icon_data,
763 )),
764 user_contents_for_manager_id: Default::default(),
765 online: true,
767 };
768
769 constellation.run();
770 })
771 .expect("Thread spawning failed");
772 }
773
774 pub(crate) fn online(&self) -> bool {
776 self.online
777 }
778
779 fn event_loops(&self) -> Vec<Rc<EventLoop>> {
780 self.event_loops
781 .iter()
782 .filter_map(|weak_event_loop| weak_event_loop.upgrade())
783 .collect()
784 }
785
786 pub(crate) fn add_event_loop(&mut self, event_loop: &Rc<EventLoop>) {
787 self.event_loops.push(Rc::downgrade(event_loop));
788 }
789
790 pub(crate) fn add_event_loop_join_handle(&mut self, join_handle: JoinHandle<()>) {
791 self.event_loop_join_handles.push(join_handle);
792 }
793
794 fn clean_up_finished_script_event_loops(&mut self) {
795 self.event_loop_join_handles
796 .retain(|join_handle| !join_handle.is_finished());
797 self.event_loops
798 .retain(|event_loop| event_loop.upgrade().is_some());
799 }
800
801 fn run(&mut self) {
803 while !self.shutting_down || !self.pipelines.is_empty() {
804 self.maybe_close_random_pipeline();
807 self.handle_request();
808 self.finish_completed_session_history_traversal_requests();
809 self.clean_up_finished_script_event_loops();
810 }
811 self.handle_shutdown();
812
813 if !opts::get().multiprocess {
814 style::global_style_data::StyleThreadPool::shutdown();
815 }
816
817 FetchThread::exit();
819
820 debug!("Asking embedding layer to complete shutdown.");
824 self.constellation_to_embedder_proxy
825 .send(ConstellationToEmbedderMsg::ShutdownComplete);
826 }
827
828 fn send_message_to_pipeline(
831 &mut self,
832 pipeline_id: PipelineId,
833 message: ScriptThreadMessage,
834 failure_message: &str,
835 ) -> bool {
836 let result = match self.pipelines.get(&pipeline_id) {
837 Some(pipeline) => pipeline.event_loop.send(message),
838 None => {
839 warn!("{pipeline_id}: {failure_message}");
840 return false;
841 },
842 };
843 if let Err(err) = result {
844 self.handle_send_error(pipeline_id, err);
845 }
846 true
847 }
848
849 pub(crate) fn next_pipeline_namespace_id(&self) -> PipelineNamespaceId {
851 let pipeline_namespace_id = self.next_pipeline_namespace_id.get();
852 self.next_pipeline_namespace_id
853 .set(PipelineNamespaceId(pipeline_namespace_id.0 + 1));
854 pipeline_namespace_id
855 }
856
857 fn browsing_context_group_for_webview_id(
858 &self,
859 webview_id: &WebViewId,
860 ) -> Option<Rc<RefCell<BrowsingContextGroup>>> {
861 self.webviews
862 .get(webview_id)
863 .map(ConstellationWebView::browsing_context_group)
864 }
865
866 fn get_event_loop(
867 &self,
868 host: &Host,
869 webview_id: &WebViewId,
870 ) -> Result<Weak<EventLoop>, &'static str> {
871 let Some(browsing_context_group) = self.browsing_context_group_for_webview_id(webview_id)
872 else {
873 return Err("Trying to get an event-loop for an unknown WebView");
874 };
875 browsing_context_group
876 .borrow()
877 .event_loops
878 .get(host)
879 .ok_or("Trying to get an event-loop from an unknown browsing context group")
880 .cloned()
881 }
882
883 fn set_event_loop(&mut self, event_loop: &Rc<EventLoop>, host: Host, webview_id: &WebViewId) {
884 let Some(browsing_context_group) = self.browsing_context_group_for_webview_id(webview_id)
885 else {
886 return warn!("Trying to add an event loop to an unknown WebView ({webview_id})");
887 };
888
889 if browsing_context_group
890 .borrow_mut()
891 .event_loops
892 .insert(host.clone(), Rc::downgrade(event_loop))
893 .is_some_and(|old_event_loop| old_event_loop.strong_count() != 0)
894 {
895 warn!("Double-setting an event loop for {host:?} in {webview_id:?}");
896 }
897 }
898
899 fn get_event_loop_for_new_pipeline(
900 &self,
901 load_data: &LoadData,
902 webview_id: WebViewId,
903 parent_pipeline_id: Option<PipelineId>,
904 registered_domain_name: &Option<Host>,
905 ) -> Option<Rc<EventLoop>> {
906 if load_data
908 .creation_sandboxing_flag_set
909 .contains(SandboxingFlagSet::SANDBOXED_ORIGIN_BROWSING_CONTEXT_FLAG)
910 {
911 return None;
912 }
913
914 if load_data.url.as_str() == "about:blank" || load_data.url.as_str() == "about:srcdoc" {
918 if let Some(parent) =
919 parent_pipeline_id.and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
920 {
921 return Some(parent.event_loop.clone());
922 }
923
924 if let Some(creator) = load_data
925 .creator_pipeline_id
926 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
927 {
928 return Some(creator.event_loop.clone());
929 }
930
931 return None;
934 }
935
936 let Some(registered_domain_name) = registered_domain_name else {
937 return None;
938 };
939
940 self.get_event_loop(registered_domain_name, &webview_id)
941 .ok()?
942 .upgrade()
943 }
944
945 fn get_or_create_event_loop_for_new_pipeline(
946 &mut self,
947 webview_id: WebViewId,
948 parent_pipeline_id: Option<PipelineId>,
949 load_data: &LoadData,
950 is_private: bool,
951 ) -> Result<Rc<EventLoop>, SendError> {
952 let registered_domain_name = if load_data
953 .creation_sandboxing_flag_set
954 .contains(SandboxingFlagSet::SANDBOXED_ORIGIN_BROWSING_CONTEXT_FLAG)
955 {
956 None
957 } else {
958 registered_domain_name(&load_data.url)
959 };
960
961 if let Some(event_loop) = self.get_event_loop_for_new_pipeline(
962 load_data,
963 webview_id,
964 parent_pipeline_id,
965 ®istered_domain_name,
966 ) {
967 return Ok(event_loop);
968 }
969
970 let event_loop = EventLoop::spawn(self, is_private)?;
971 if let Some(registered_domain_name) = registered_domain_name {
972 self.set_event_loop(&event_loop, registered_domain_name, &webview_id);
973 }
974 Ok(event_loop)
975 }
976
977 #[expect(clippy::too_many_arguments)]
979 fn new_pipeline(
980 &mut self,
981 new_pipeline_id: PipelineId,
982 browsing_context_id: BrowsingContextId,
983 webview_id: WebViewId,
984 parent_pipeline_id: Option<PipelineId>,
985 opener: Option<BrowsingContextId>,
986 initial_viewport_details: ViewportDetails,
987 load_data: LoadData,
992 is_private: bool,
993 target_snapshot_params: TargetSnapshotParams,
994 name: Option<String>,
995 ) {
996 if self.shutting_down {
997 return;
998 }
999
1000 debug!("Creating new pipeline ({new_pipeline_id:?}) in {browsing_context_id}");
1001 let (webview_hidden, webview_state) = {
1002 let Some(webview) = self.webviews.get(&webview_id) else {
1003 warn!("Tried to create Pipeline for unknown WebViewId: {webview_id:?}");
1004 return;
1005 };
1006 (webview.hidden(), webview.state())
1007 };
1008
1009 let event_loop = match self.get_or_create_event_loop_for_new_pipeline(
1010 webview_id,
1011 parent_pipeline_id,
1012 &load_data,
1013 is_private,
1014 ) {
1015 Ok(event_loop) => event_loop,
1016 Err(error) => return self.handle_send_error(new_pipeline_id, error),
1017 };
1018
1019 let user_content_manager_id = self
1020 .webviews
1021 .get(&webview_id)
1022 .and_then(|webview| webview.user_content_manager_id);
1023
1024 let new_pipeline_info = NewPipelineInfo {
1025 webview_state,
1026 parent_info: parent_pipeline_id,
1027 new_pipeline_id,
1028 browsing_context_id,
1029 opener,
1030 load_data,
1031 viewport_details: initial_viewport_details,
1032 user_content_manager_id,
1033 target_snapshot_params,
1034 frame_name: name,
1035 };
1036 let pipeline = match Pipeline::spawn(new_pipeline_info, event_loop, self, webview_hidden) {
1037 Ok(pipeline) => pipeline,
1038 Err(error) => return self.handle_send_error(new_pipeline_id, error),
1039 };
1040
1041 assert!(!self.pipelines.contains_key(&new_pipeline_id));
1042 self.pipelines.insert(new_pipeline_id, pipeline);
1043 }
1044
1045 fn fully_active_descendant_browsing_contexts_iter(
1047 &self,
1048 browsing_context_id: BrowsingContextId,
1049 ) -> FullyActiveBrowsingContextsIterator<'_> {
1050 FullyActiveBrowsingContextsIterator::new(
1051 browsing_context_id,
1052 &self.browsing_contexts,
1053 &self.pipelines,
1054 )
1055 }
1056
1057 fn fully_active_browsing_contexts_iter(
1059 &self,
1060 webview_id: WebViewId,
1061 ) -> FullyActiveBrowsingContextsIterator<'_> {
1062 self.fully_active_descendant_browsing_contexts_iter(BrowsingContextId::from(webview_id))
1063 }
1064
1065 fn all_descendant_browsing_contexts_iter(
1067 &self,
1068 browsing_context_id: BrowsingContextId,
1069 ) -> AllBrowsingContextsIterator<'_> {
1070 AllBrowsingContextsIterator {
1071 stack: vec![browsing_context_id],
1072 pipelines: &self.pipelines,
1073 browsing_contexts: &self.browsing_contexts,
1074 }
1075 }
1076
1077 fn ancestor_pipelines_of_browsing_context_iter(
1080 &self,
1081 browsing_context_id: BrowsingContextId,
1082 ) -> impl Iterator<Item = &Pipeline> + '_ {
1083 let mut state: Option<PipelineId> = self
1084 .browsing_contexts
1085 .get(&browsing_context_id)
1086 .and_then(|browsing_context| browsing_context.parent_pipeline_id);
1087 std::iter::from_fn(move || {
1088 if let Some(pipeline_id) = state {
1089 let pipeline = self.pipelines.get(&pipeline_id)?;
1090 let browsing_context = self.browsing_contexts.get(&pipeline.browsing_context_id)?;
1091 state = browsing_context.parent_pipeline_id;
1092 Some(pipeline)
1093 } else {
1094 None
1095 }
1096 })
1097 }
1098
1099 fn ancestor_or_self_pipelines_of_browsing_context_iter(
1102 &self,
1103 browsing_context_id: BrowsingContextId,
1104 ) -> impl Iterator<Item = &Pipeline> + '_ {
1105 let this_pipeline = self
1106 .browsing_contexts
1107 .get(&browsing_context_id)
1108 .map(|browsing_context| browsing_context.pipeline_id)
1109 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id));
1110 this_pipeline
1111 .into_iter()
1112 .chain(self.ancestor_pipelines_of_browsing_context_iter(browsing_context_id))
1113 }
1114
1115 #[expect(clippy::too_many_arguments)]
1117 fn new_browsing_context(
1118 &mut self,
1119 browsing_context_id: BrowsingContextId,
1120 webview_id: WebViewId,
1121 pipeline_id: PipelineId,
1122 parent_pipeline_id: Option<PipelineId>,
1123 viewport_details: ViewportDetails,
1124 is_private: bool,
1125 inherited_secure_context: Option<bool>,
1126 ) {
1127 debug!("{browsing_context_id}: Creating new browsing context");
1128 let Some(webview) = self.webviews.get_mut(&webview_id) else {
1129 println!("Adding BrowsingContext for unknown WebView: {webview_id:?}");
1130 return;
1131 };
1132
1133 let viewport_details = webview
1135 .take_pending_viewport_details(&browsing_context_id)
1136 .unwrap_or(viewport_details);
1137 let browsing_context = BrowsingContext::new(
1138 browsing_context_id,
1139 webview_id,
1140 pipeline_id,
1141 parent_pipeline_id,
1142 viewport_details,
1143 is_private,
1144 inherited_secure_context,
1145 );
1146 self.browsing_contexts
1147 .insert(browsing_context_id, browsing_context);
1148
1149 if let Some(parent_pipeline_id) = parent_pipeline_id &&
1151 let Some(parent) = self.pipelines.get_mut(&parent_pipeline_id)
1152 {
1153 parent.add_child(browsing_context_id);
1154 }
1155 }
1156
1157 #[servo_tracing::instrument(skip_all)]
1159 fn handle_request(&mut self) {
1160 #[expect(clippy::large_enum_variant)]
1161 #[derive(Debug)]
1162 enum Request {
1163 ServiceWorkerManager(ServiceWorkerManagerMsg),
1164 PipelineNamespace(PipelineNamespaceRequest),
1165 Script((WebViewId, PipelineId, ScriptToConstellationMessage)),
1166 BackgroundHangMonitor(HangAlert),
1167 Embedder(EmbedderToConstellationMessage),
1168 #[cfg_attr(not(feature = "multiprocess"), expect(unused))]
1169 RemoveProcess(usize),
1170 }
1171 let mut sel = Select::new();
1183 sel.recv(&self.sw_manager_receiver);
1184 sel.recv(&self.namespace_receiver);
1185 sel.recv(&self.script_receiver);
1186 sel.recv(&self.background_hang_monitor_receiver);
1187 sel.recv(&self.embedder_to_constellation_receiver);
1188
1189 #[cfg(feature = "multiprocess")]
1190 self.process_manager.register(&mut sel);
1191
1192 let request = {
1193 let oper = {
1194 let _span = profile_traits::trace_span!("handle_request::select").entered();
1195 sel.select()
1196 };
1197 let index = oper.index();
1198
1199 match index {
1200 0 => oper
1201 .recv(&self.sw_manager_receiver)
1202 .expect("Unexpected service worker manager channel panic in constellation")
1203 .map(Request::ServiceWorkerManager),
1204 1 => oper
1205 .recv(&self.namespace_receiver)
1206 .expect("Unexpected script channel panic in constellation")
1207 .map(Request::PipelineNamespace),
1208 2 => oper
1209 .recv(&self.script_receiver)
1210 .expect("Unexpected script channel panic in constellation")
1211 .map(Request::Script),
1212 3 => oper
1213 .recv(&self.background_hang_monitor_receiver)
1214 .expect("Unexpected BHM channel panic in constellation")
1215 .map(Request::BackgroundHangMonitor),
1216 4 => Ok(Request::Embedder(
1217 oper.recv(&self.embedder_to_constellation_receiver)
1218 .expect("Unexpected embedder channel panic in constellation"),
1219 )),
1220 _ => {
1221 let process_index = index - 5;
1223 #[cfg(feature = "multiprocess")]
1224 let _ = oper.recv(self.process_manager.receiver_at(process_index));
1225 Ok(Request::RemoveProcess(process_index))
1226 },
1227 }
1228 };
1229
1230 let request = match request {
1231 Ok(request) => request,
1232 Err(err) => return error!("Deserialization failed ({}).", err),
1233 };
1234
1235 match request {
1236 Request::ServiceWorkerManager(message) => {
1237 self.handle_request_from_service_worker_manager(message);
1238 },
1239 Request::PipelineNamespace(message) => {
1240 self.handle_request_for_pipeline_namespace(message)
1241 },
1242 Request::Embedder(message) => self.handle_request_from_embedder(message),
1243 Request::Script(message) => {
1244 self.handle_request_from_script(message);
1245 },
1246 Request::BackgroundHangMonitor(message) => {
1247 self.handle_request_from_background_hang_monitor(message);
1248 },
1249 #[cfg(feature = "multiprocess")]
1250 Request::RemoveProcess(index) => self.process_manager.remove(index),
1251 #[cfg(not(feature = "multiprocess"))]
1252 Request::RemoveProcess(_) => {},
1253 }
1254 }
1255
1256 #[servo_tracing::instrument(skip_all)]
1257 fn handle_request_for_pipeline_namespace(&mut self, request: PipelineNamespaceRequest) {
1258 let PipelineNamespaceRequest(sender) = request;
1259 let _ = sender.send(self.next_pipeline_namespace_id());
1260 }
1261
1262 #[servo_tracing::instrument(skip_all)]
1263 fn handle_request_from_background_hang_monitor(&self, message: HangAlert) {
1264 warn!("Component hang alert: {:?}", message);
1267 }
1268
1269 fn handle_request_from_service_worker_manager(&mut self, message: ServiceWorkerManagerMsg) {
1270 match message {
1271 ServiceWorkerManagerMsg::UpdateWorkerState {
1272 worker_id,
1273 state,
1274 origin,
1275 } => {
1276 self.pipelines
1279 .values()
1280 .filter(|pipeline| pipeline.url.origin() == origin)
1281 .for_each(|pipeline| {
1282 if pipeline
1283 .event_loop
1284 .send(ScriptThreadMessage::UpdateServiceWorkerState {
1285 pipeline_id: pipeline.id,
1286 worker_id,
1287 state: state.clone(),
1288 })
1289 .is_err()
1290 {
1291 error!(
1292 "Failed to send UpdateServiceWorkerState to pipeline {}",
1293 pipeline.id
1294 );
1295 }
1296 });
1297 },
1298 }
1299 }
1300
1301 #[servo_tracing::instrument(skip_all)]
1302 fn handle_request_from_embedder(&mut self, message: EmbedderToConstellationMessage) {
1303 trace_msg_from_embedder!(message, "{message:?}");
1304 match message {
1305 EmbedderToConstellationMessage::Exit => {
1306 self.handle_exit();
1307 },
1308 EmbedderToConstellationMessage::AllowNavigationResponse(pipeline_id, allowed) => {
1309 self.handle_allow_navigation_response(pipeline_id, allowed);
1310 },
1311 EmbedderToConstellationMessage::LoadUrl(webview_id, url_request) => {
1315 let mut load_data = LoadData::new_for_new_unrelated_webview(url_request.url);
1316
1317 if !url_request.headers.is_empty() {
1318 load_data.headers.extend(url_request.headers);
1319 }
1320
1321 let ctx_id = BrowsingContextId::from(webview_id);
1322 let pipeline_id = match self.browsing_contexts.get(&ctx_id) {
1323 Some(ctx) => ctx.pipeline_id,
1324 None => {
1325 return warn!("{}: LoadUrl for unknown browsing context", webview_id);
1326 },
1327 };
1328 self.load_url(
1331 webview_id,
1332 pipeline_id,
1333 load_data,
1334 NavigationHistoryBehavior::Push,
1335 TargetSnapshotParams::default(),
1336 );
1337 },
1338 EmbedderToConstellationMessage::NewWebView(url, new_webview_details) => {
1341 self.handle_new_top_level_browsing_context(url, new_webview_details);
1342 },
1343 EmbedderToConstellationMessage::CloseWebView(webview_id) => {
1345 self.handle_close_top_level_browsing_context(webview_id);
1346 },
1347 EmbedderToConstellationMessage::SetWebViewHasSystemFocus(
1348 webview_id,
1349 has_system_focus,
1350 ) => {
1351 self.handle_set_has_system_focus(webview_id, has_system_focus);
1352 },
1353 EmbedderToConstellationMessage::TraverseHistory(request) => {
1355 self.handle_traverse_session_history_msg(request);
1356 },
1357 EmbedderToConstellationMessage::ChangeViewportDetails(
1358 webview_id,
1359 new_viewport_details,
1360 size_type,
1361 ) => {
1362 self.handle_change_viewport_details_msg(
1363 webview_id,
1364 new_viewport_details,
1365 size_type,
1366 );
1367 },
1368 EmbedderToConstellationMessage::ThemeChange(webview_id, theme) => {
1369 self.handle_theme_change(webview_id, theme);
1370 },
1371 EmbedderToConstellationMessage::TickAnimation(webview_ids) => {
1372 self.handle_tick_animation(webview_ids)
1373 },
1374 EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
1375 pipeline_ids,
1376 ) => self.handle_no_longer_waiting_on_asynchronous_image_updates(pipeline_ids),
1377 EmbedderToConstellationMessage::WebDriverCommand(command) => {
1378 self.handle_webdriver_msg(command);
1379 },
1380 EmbedderToConstellationMessage::Reload(webview_id) => {
1381 self.handle_reload_msg(webview_id);
1382 },
1383 EmbedderToConstellationMessage::LogEntry(event_loop_id, thread_name, entry) => {
1384 self.handle_log_entry(event_loop_id, thread_name, entry);
1385 },
1386 EmbedderToConstellationMessage::ForwardInputEvent(webview_id, event, hit_test) => {
1387 self.forward_input_event(webview_id, event, hit_test);
1388 },
1389 EmbedderToConstellationMessage::RefreshCursor(pipeline_id) => {
1390 self.handle_refresh_cursor(pipeline_id)
1391 },
1392 EmbedderToConstellationMessage::ExitFullScreen(webview_id) => {
1393 self.handle_exit_fullscreen_msg(webview_id);
1394 },
1395 EmbedderToConstellationMessage::MediaSessionAction(action) => {
1396 self.handle_media_session_action_msg(action);
1397 },
1398 EmbedderToConstellationMessage::SetWebViewHidden(webview_id, hidden) => {
1399 self.set_webview_hidden(webview_id, hidden);
1400 },
1401 EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_states) => {
1402 self.handle_set_scroll_states(pipeline_id, scroll_states)
1403 },
1404 EmbedderToConstellationMessage::PaintMetric(pipeline_id, paint_metric_event) => {
1405 self.handle_paint_metric(pipeline_id, paint_metric_event);
1406 },
1407 EmbedderToConstellationMessage::EvaluateJavaScript(
1408 webview_id,
1409 evaluation_id,
1410 script,
1411 ) => {
1412 self.handle_evaluate_javascript(webview_id, evaluation_id, script);
1413 },
1414 EmbedderToConstellationMessage::CreateMemoryReport(sender) => {
1415 self.mem_profiler_chan.send(ProfilerMsg::Report(sender));
1416 },
1417 EmbedderToConstellationMessage::SendImageKeysForPipeline(pipeline_id, image_keys) => {
1418 if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
1419 if pipeline
1420 .event_loop
1421 .send(ScriptThreadMessage::SendImageKeysBatch(
1422 pipeline_id,
1423 image_keys,
1424 ))
1425 .is_err()
1426 {
1427 warn!("Could not send image keys to pipeline {:?}", pipeline_id);
1428 }
1429 } else {
1430 warn!(
1431 "Keys were generated for a pipeline ({:?}) that was
1432 closed before the request could be fulfilled.",
1433 pipeline_id
1434 )
1435 }
1436 },
1437 EmbedderToConstellationMessage::PreferencesUpdated(updates) => {
1438 let event_loops = self
1439 .pipelines
1440 .values()
1441 .map(|pipeline| pipeline.event_loop.clone());
1442 for event_loop in event_loops {
1443 let _ = event_loop.send(ScriptThreadMessage::PreferencesUpdated(
1444 updates
1445 .iter()
1446 .map(|(name, value)| (String::from(*name), value.clone()))
1447 .collect(),
1448 ));
1449 }
1450 },
1451 EmbedderToConstellationMessage::RequestScreenshotReadiness(webview_id) => {
1452 if let Some(webview) = self.webviews.get_mut(&webview_id) {
1453 webview.handle_screenshot_readiness_request(
1454 &self.browsing_contexts,
1455 &self.pipelines,
1456 );
1457 }
1458 },
1459 EmbedderToConstellationMessage::EmbedderControlResponse(id, response) => {
1460 self.handle_embedder_control_response(id, response);
1461 },
1462 EmbedderToConstellationMessage::UserContentManagerAction(
1463 user_content_manager_id,
1464 action,
1465 ) => {
1466 self.handle_user_content_manager_action(user_content_manager_id, action);
1467 },
1468 EmbedderToConstellationMessage::UpdatePinchZoomInfos(pipeline_id, pinch_zoom) => {
1469 self.handle_update_pinch_zoom_infos(pipeline_id, pinch_zoom);
1470 },
1471 EmbedderToConstellationMessage::SetAccessibilityActive(webview_id, active) => {
1472 self.set_accessibility_active(webview_id, active);
1473 },
1474 EmbedderToConstellationMessage::ForwardAccessibilityAction(action_request) => {
1475 self.forward_accessibility_action(action_request);
1476 },
1477 EmbedderToConstellationMessage::ClearSessionHistory(webview_id) => {
1478 self.handle_clear_session_history(webview_id);
1479 },
1480 EmbedderToConstellationMessage::SetNetworkOnlineState(online) => {
1481 self.handle_set_network_online_state(online);
1482 },
1483 }
1484 }
1485
1486 fn mutate_user_contents_for_manager_id_and_notify_script_threads(
1487 &mut self,
1488 user_content_manager_id: UserContentManagerId,
1489 callback: impl FnOnce(&mut UserContents),
1490 ) {
1491 let event_loops = self.event_loops();
1492 let user_contents = self
1493 .user_contents_for_manager_id
1494 .entry(user_content_manager_id)
1495 .or_default();
1496
1497 callback(user_contents);
1498
1499 for event_loop in event_loops {
1500 let _ = event_loop.send(ScriptThreadMessage::SetUserContents(
1501 user_content_manager_id,
1502 user_contents.clone(),
1503 ));
1504 }
1505 }
1506
1507 fn handle_user_content_manager_action(
1508 &mut self,
1509 user_content_manager_id: UserContentManagerId,
1510 action: UserContentManagerAction,
1511 ) {
1512 match action {
1513 UserContentManagerAction::AddUserScript(user_script) => {
1514 self.mutate_user_contents_for_manager_id_and_notify_script_threads(
1515 user_content_manager_id,
1516 |user_contents| {
1517 user_contents.scripts.push(user_script);
1518 },
1519 );
1520 },
1521 UserContentManagerAction::RemoveUserScript(user_script_id) => {
1522 self.mutate_user_contents_for_manager_id_and_notify_script_threads(
1523 user_content_manager_id,
1524 |user_contents| {
1525 user_contents
1526 .scripts
1527 .retain(|user_script| user_script.id() != user_script_id);
1528 },
1529 );
1530 },
1531 UserContentManagerAction::AddUserStyleSheet(user_stylesheet) => {
1532 self.mutate_user_contents_for_manager_id_and_notify_script_threads(
1533 user_content_manager_id,
1534 |user_contents| {
1535 user_contents.stylesheets.push(user_stylesheet);
1536 },
1537 );
1538 },
1539 UserContentManagerAction::RemoveUserStyleSheet(user_stylesheet_id) => {
1540 self.mutate_user_contents_for_manager_id_and_notify_script_threads(
1541 user_content_manager_id,
1542 |user_contents| {
1543 user_contents
1544 .stylesheets
1545 .retain(|user_stylesheet| user_stylesheet.id() != user_stylesheet_id);
1546 },
1547 );
1548 },
1549 UserContentManagerAction::DestroyUserContentManager => {
1550 self.user_contents_for_manager_id
1551 .remove(&user_content_manager_id);
1552
1553 for event_loop in self.event_loops() {
1554 let _ = event_loop.send(ScriptThreadMessage::DestroyUserContentManager(
1555 user_content_manager_id,
1556 ));
1557 }
1558 },
1559 }
1560 }
1561
1562 fn send_message_to_all_background_hang_monitors(
1563 &self,
1564 message: BackgroundHangMonitorControlMsg,
1565 ) {
1566 if let Some(background_monitor_control_sender) = &self.background_monitor_control_sender &&
1567 let Err(error) = background_monitor_control_sender.send(message.clone())
1568 {
1569 error!("Could not send message ({message:?}) to BHM: {error}");
1570 }
1571 for event_loop in self.event_loops() {
1572 event_loop.send_message_to_background_hang_monitor(&message);
1573 }
1574 }
1575
1576 #[servo_tracing::instrument(skip_all)]
1577 fn handle_evaluate_javascript(
1578 &mut self,
1579 webview_id: WebViewId,
1580 evaluation_id: JavaScriptEvaluationId,
1581 script: String,
1582 ) {
1583 let browsing_context_id = BrowsingContextId::from(webview_id);
1584 let Some(pipeline) = self
1585 .browsing_contexts
1586 .get(&browsing_context_id)
1587 .and_then(|browsing_context| self.pipelines.get(&browsing_context.pipeline_id))
1588 else {
1589 self.handle_finish_javascript_evaluation(
1590 evaluation_id,
1591 Err(JavaScriptEvaluationError::InternalError),
1592 );
1593 return;
1594 };
1595
1596 if pipeline
1597 .event_loop
1598 .send(ScriptThreadMessage::EvaluateJavaScript(
1599 webview_id,
1600 pipeline.id,
1601 evaluation_id,
1602 script,
1603 ))
1604 .is_err()
1605 {
1606 self.handle_finish_javascript_evaluation(
1607 evaluation_id,
1608 Err(JavaScriptEvaluationError::InternalError),
1609 );
1610 }
1611 }
1612
1613 #[servo_tracing::instrument(skip_all)]
1614 fn handle_request_from_script(
1615 &mut self,
1616 message: (WebViewId, PipelineId, ScriptToConstellationMessage),
1617 ) {
1618 let (webview_id, source_pipeline_id, content) = message;
1619 trace_script_msg!(content, "{source_pipeline_id}: {content:?}");
1620
1621 match content {
1622 ScriptToConstellationMessage::CompleteMessagePortTransfer(router_id, ports) => {
1623 self.handle_complete_message_port_transfer(router_id, ports);
1624 },
1625 ScriptToConstellationMessage::MessagePortTransferResult(
1626 router_id,
1627 succeeded,
1628 failed,
1629 ) => {
1630 self.handle_message_port_transfer_completed(router_id, succeeded);
1631 self.handle_message_port_transfer_failed(failed);
1632 },
1633 ScriptToConstellationMessage::RerouteMessagePort(port_id, task) => {
1634 self.handle_reroute_messageport(port_id, task);
1635 },
1636 ScriptToConstellationMessage::MessagePortShipped(port_id) => {
1637 self.handle_messageport_shipped(port_id);
1638 },
1639 ScriptToConstellationMessage::NewMessagePortRouter(router_id, callback) => {
1640 self.handle_new_messageport_router(router_id, callback);
1641 },
1642 ScriptToConstellationMessage::RemoveMessagePortRouter(router_id) => {
1643 self.handle_remove_messageport_router(router_id);
1644 },
1645 ScriptToConstellationMessage::NewMessagePort(router_id, port_id) => {
1646 self.handle_new_messageport(router_id, port_id);
1647 },
1648 ScriptToConstellationMessage::EntanglePorts(port1, port2) => {
1649 self.handle_entangle_messageports(port1, port2);
1650 },
1651 ScriptToConstellationMessage::DisentanglePorts(port1, port2) => {
1652 self.handle_disentangle_messageports(port1, port2);
1653 },
1654 ScriptToConstellationMessage::NewBroadcastChannelRouter(
1655 router_id,
1656 response_sender,
1657 origin,
1658 ) => {
1659 if self
1660 .check_origin_against_pipeline(&source_pipeline_id, &origin)
1661 .is_err()
1662 {
1663 return warn!("Attempt to add broadcast router from an unexpected origin.");
1664 }
1665 self.broadcast_channels
1666 .new_broadcast_channel_router(router_id, response_sender);
1667 },
1668 ScriptToConstellationMessage::NewBroadcastChannelNameInRouter(
1669 router_id,
1670 channel_name,
1671 origin,
1672 ) => {
1673 if self
1674 .check_origin_against_pipeline(&source_pipeline_id, &origin)
1675 .is_err()
1676 {
1677 return warn!("Attempt to add channel name from an unexpected origin.");
1678 }
1679 self.broadcast_channels
1680 .new_broadcast_channel_name_in_router(router_id, channel_name, origin);
1681 },
1682 ScriptToConstellationMessage::RemoveBroadcastChannelNameInRouter(
1683 router_id,
1684 channel_name,
1685 origin,
1686 ) => {
1687 if self
1688 .check_origin_against_pipeline(&source_pipeline_id, &origin)
1689 .is_err()
1690 {
1691 return warn!("Attempt to remove channel name from an unexpected origin.");
1692 }
1693 self.broadcast_channels
1694 .remove_broadcast_channel_name_in_router(router_id, channel_name, origin);
1695 },
1696 ScriptToConstellationMessage::RemoveBroadcastChannelRouter(router_id, origin) => {
1697 if self
1698 .check_origin_against_pipeline(&source_pipeline_id, &origin)
1699 .is_err()
1700 {
1701 return warn!("Attempt to remove broadcast router from an unexpected origin.");
1702 }
1703 self.broadcast_channels
1704 .remove_broadcast_channel_router(router_id);
1705 },
1706 ScriptToConstellationMessage::ScheduleBroadcast(router_id, message) => {
1707 if self
1708 .check_origin_against_pipeline(&source_pipeline_id, &message.origin)
1709 .is_err()
1710 {
1711 return warn!(
1712 "Attempt to schedule broadcast from an origin not matching the origin of the msg."
1713 );
1714 }
1715 self.broadcast_channels
1716 .schedule_broadcast(router_id, message);
1717 },
1718 ScriptToConstellationMessage::PipelineExited => {
1719 self.handle_pipeline_exited(source_pipeline_id, PipelineExitSource::Constellation);
1720 },
1721 ScriptToConstellationMessage::DiscardDocument => {
1722 self.handle_discard_document(webview_id, source_pipeline_id);
1723 },
1724 ScriptToConstellationMessage::DiscardTopLevelBrowsingContext => {
1725 self.handle_close_top_level_browsing_context(webview_id);
1726 },
1727 ScriptToConstellationMessage::ScriptLoadedURLInIFrame(load_info) => {
1728 self.handle_script_loaded_url_in_iframe_msg(load_info);
1729 },
1730 ScriptToConstellationMessage::ScriptNewIFrame(load_info) => {
1731 self.handle_script_new_iframe(load_info);
1732 },
1733 ScriptToConstellationMessage::CreateAuxiliaryWebView(load_info) => {
1734 self.handle_script_new_auxiliary(load_info);
1735 },
1736 ScriptToConstellationMessage::ChangeRunningAnimationsState(animation_state) => {
1737 self.handle_change_running_animations_state(source_pipeline_id, animation_state)
1738 },
1739 ScriptToConstellationMessage::RegisterWorkerAnimationFrameProvider(
1740 worker_id,
1741 sender,
1742 ) => self.handle_register_worker_animation_frame_provider(
1743 webview_id,
1744 source_pipeline_id,
1745 worker_id,
1746 sender,
1747 ),
1748 ScriptToConstellationMessage::UnregisterWorkerAnimationFrameProvider(worker_id) => {
1749 self.handle_unregister_worker_animation_frame_provider(worker_id)
1750 },
1751 ScriptToConstellationMessage::ChangeWorkerAnimationFrameProviderState(
1752 worker_id,
1753 active,
1754 ) => self.handle_change_worker_animation_frame_provider_state(
1755 source_pipeline_id,
1756 worker_id,
1757 active,
1758 ),
1759 ScriptToConstellationMessage::LoadUrl(
1761 load_data,
1762 history_handling,
1763 target_snapshot_params,
1764 ) => {
1765 self.schedule_navigation(
1766 webview_id,
1767 source_pipeline_id,
1768 load_data,
1769 history_handling,
1770 target_snapshot_params,
1771 );
1772 },
1773 ScriptToConstellationMessage::AbortLoadUrl => {
1774 self.handle_abort_load_url_msg(webview_id, source_pipeline_id);
1775 },
1776 ScriptToConstellationMessage::LoadFinished { aborted } => {
1777 self.handle_load_finished_message(webview_id, source_pipeline_id, aborted)
1778 },
1779 ScriptToConstellationMessage::NavigatedToFragment(new_url, replacement_enabled) => {
1781 self.handle_navigated_to_fragment(source_pipeline_id, new_url, replacement_enabled);
1782 },
1783 ScriptToConstellationMessage::TraverseHistory(request) => {
1785 self.handle_traverse_session_history_msg(request);
1786 },
1787 ScriptToConstellationMessage::PushHistoryState(history_state_id, url) => {
1789 self.handle_push_history_state_msg(source_pipeline_id, history_state_id, url);
1790 },
1791 ScriptToConstellationMessage::ReplaceHistoryState(history_state_id, url) => {
1792 self.handle_replace_history_state_msg(source_pipeline_id, history_state_id, url);
1793 },
1794 ScriptToConstellationMessage::JointSessionHistoryLength(response_sender) => {
1796 self.handle_joint_session_history_length(webview_id, response_sender);
1797 },
1798 ScriptToConstellationMessage::ActivateDocument => {
1800 self.handle_activate_document_msg(webview_id, source_pipeline_id);
1801 },
1802 ScriptToConstellationMessage::SetFinalUrl(final_url) => {
1804 if let Some(ref mut pipeline) = self.pipelines.get_mut(&source_pipeline_id) {
1806 pipeline.url = final_url;
1807 } else {
1808 warn!("constellation got set final url message for dead pipeline");
1809 }
1810 },
1811 ScriptToConstellationMessage::PostMessage {
1812 target: browsing_context_id,
1813 source: source_pipeline_id,
1814 target_origin: origin,
1815 source_origin,
1816 data,
1817 } => {
1818 self.handle_post_message_msg(
1819 browsing_context_id,
1820 source_pipeline_id,
1821 origin,
1822 source_origin,
1823 data,
1824 );
1825 },
1826 ScriptToConstellationMessage::FocusAncestorBrowsingContextsForFocusingSteps(
1827 focused_child_browsing_context_id,
1828 sequence,
1829 ) => {
1830 self.handle_focus_ancestor_browsing_contexts_for_focusing_steps(
1831 source_pipeline_id,
1832 focused_child_browsing_context_id,
1833 sequence,
1834 );
1835 },
1836 ScriptToConstellationMessage::FocusRemoteBrowsingContext(
1837 focused_browsing_context_id,
1838 remote_focus_operation,
1839 ) => {
1840 self.handle_focus_remote_browsing_context(
1841 focused_browsing_context_id,
1842 remote_focus_operation,
1843 );
1844 },
1845 ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, response_sender) => {
1846 let removed_pipeline_ids =
1847 self.handle_remove_iframe_msg(webview_id, browsing_context_id);
1848 if let Err(e) = response_sender.send(removed_pipeline_ids) {
1849 warn!("Error replying to remove iframe ({})", e);
1850 }
1851 },
1852 ScriptToConstellationMessage::CreateCanvasPaintThread(size, response_sender) => {
1853 self.handle_create_canvas_paint_thread_msg(size, response_sender)
1854 },
1855 ScriptToConstellationMessage::LogEntry(event_loop_id, thread_name, entry) => {
1856 self.handle_log_entry(event_loop_id, thread_name, entry);
1857 },
1858 ScriptToConstellationMessage::GetBrowsingContextInfo(pipeline_id, response_sender) => {
1859 let result = self
1860 .pipelines
1861 .get(&pipeline_id)
1862 .and_then(|pipeline| self.browsing_contexts.get(&pipeline.browsing_context_id))
1863 .map(|ctx| (ctx.id, ctx.parent_pipeline_id));
1864 if let Err(e) = response_sender.send(result) {
1865 warn!(
1866 "Sending reply to get browsing context info failed ({:?}).",
1867 e
1868 );
1869 }
1870 },
1871 ScriptToConstellationMessage::GetTopForBrowsingContext(
1872 browsing_context_id,
1873 response_sender,
1874 ) => {
1875 let result = self
1876 .browsing_contexts
1877 .get(&browsing_context_id)
1878 .map(|bc| bc.webview_id);
1879 if let Err(e) = response_sender.send(result) {
1880 warn!(
1881 "Sending reply to get top for browsing context info failed ({:?}).",
1882 e
1883 );
1884 }
1885 },
1886 ScriptToConstellationMessage::GetChildBrowsingContextCount(
1887 browsing_context_id,
1888 response_sender,
1889 ) => {
1890 let count = self
1891 .browsing_contexts
1892 .get(&browsing_context_id)
1893 .and_then(|browsing_context| self.pipelines.get(&browsing_context.pipeline_id))
1894 .map(|pipeline| pipeline.children.len())
1895 .unwrap_or_default();
1896 if let Err(error) = response_sender.send(count) {
1897 warn!("Sending reply to get child browsing context count failed ({error:?}).",);
1898 }
1899 },
1900
1901 ScriptToConstellationMessage::GetChildBrowsingContextId(
1902 browsing_context_id,
1903 index,
1904 response_sender,
1905 ) => {
1906 let result = self
1907 .browsing_contexts
1908 .get(&browsing_context_id)
1909 .and_then(|bc| self.pipelines.get(&bc.pipeline_id))
1910 .and_then(|pipeline| pipeline.children.get(index))
1911 .copied();
1912 if let Err(error) = response_sender.send(result) {
1913 warn!("Sending reply to get child browsing context ID failed ({error:?}).",);
1914 }
1915 },
1916 ScriptToConstellationMessage::IsCurrentlyFullyActive(pipeline_id, response_sender) => {
1917 if let Err(error) = response_sender
1918 .send(self.get_activity(pipeline_id) == DocumentActivity::FullyActive)
1919 {
1920 warn!("Sending reply to get document activity failed ({error:?}).");
1921 }
1922 },
1923 ScriptToConstellationMessage::GetDocumentOrigin(pipeline_id, response_sender) => {
1924 self.send_message_to_pipeline(
1925 pipeline_id,
1926 ScriptThreadMessage::GetDocumentOrigin(pipeline_id, response_sender),
1927 "Document origin retrieval after closure",
1928 );
1929 },
1930 ScriptToConstellationMessage::GetDocumentOriginDetails(
1931 pipeline_id,
1932 response_sender,
1933 ) => {
1934 self.send_message_to_pipeline(
1935 pipeline_id,
1936 ScriptThreadMessage::GetDocumentOriginDetails(pipeline_id, response_sender),
1937 "Document ancestor origin details retrieval after closure",
1938 );
1939 },
1940 ScriptToConstellationMessage::ServiceWorkerAlgorithm(algorithm) => {
1941 self.handle_serviceworker_algorithm(source_pipeline_id, algorithm);
1942 },
1943 ScriptToConstellationMessage::ForwardDOMMessage(msg_vec, scope_url) => {
1944 if let Some(mgr) = self.sw_managers.get(&scope_url.origin()) {
1945 let _ = mgr.send(ServiceWorkerMsg::ForwardDOMMessage(msg_vec, scope_url));
1946 } else {
1947 warn!("Unable to forward DOMMessage for postMessage call");
1948 }
1949 },
1950 ScriptToConstellationMessage::RegisterInterest(interest) => {
1951 self.pipeline_interests
1952 .entry(interest)
1953 .or_default()
1954 .insert(source_pipeline_id);
1955 },
1956 ScriptToConstellationMessage::UnregisterInterest(interest) => {
1957 if let Some(set) = self.pipeline_interests.get_mut(&interest) {
1958 set.remove(&source_pipeline_id);
1959 if set.is_empty() {
1960 self.pipeline_interests.remove(&interest);
1961 }
1962 }
1963 },
1964 ScriptToConstellationMessage::BroadcastStorageEvent(
1965 storage,
1966 url,
1967 key,
1968 old_value,
1969 new_value,
1970 ) => {
1971 self.handle_broadcast_storage_event(
1972 source_pipeline_id,
1973 storage,
1974 url,
1975 key,
1976 old_value,
1977 new_value,
1978 );
1979 },
1980 ScriptToConstellationMessage::MediaSessionEvent(pipeline_id, event) => {
1981 if self.active_media_session.is_some() &&
1988 let MediaSessionEvent::PlaybackStateChange(ref state) = event &&
1989 !matches!(
1990 state,
1991 MediaSessionPlaybackState::Playing | MediaSessionPlaybackState::Paused
1992 )
1993 {
1994 return;
1995 };
1996 self.active_media_session = Some(pipeline_id);
1997 self.constellation_to_embedder_proxy.send(
1998 ConstellationToEmbedderMsg::MediaSessionEvent(webview_id, event),
1999 );
2000 },
2001 #[cfg(feature = "webgpu")]
2002 ScriptToConstellationMessage::RequestAdapter(response_sender, options, ids) => self
2003 .handle_wgpu_request(
2004 webview_id,
2005 source_pipeline_id,
2006 ScriptToConstellationMessage::RequestAdapter(response_sender, options, ids),
2007 ),
2008 #[cfg(feature = "webgpu")]
2009 ScriptToConstellationMessage::GetWebGPUChan(response_sender) => self
2010 .handle_wgpu_request(
2011 webview_id,
2012 source_pipeline_id,
2013 ScriptToConstellationMessage::GetWebGPUChan(response_sender),
2014 ),
2015 ScriptToConstellationMessage::TitleChanged(pipeline, title) => {
2016 if let Some(pipeline) = self.pipelines.get_mut(&pipeline) {
2017 pipeline.title = title;
2018 }
2019 },
2020 ScriptToConstellationMessage::IFrameSizes(iframe_sizes) => {
2021 self.handle_iframe_size_msg(webview_id, iframe_sizes)
2022 },
2023 ScriptToConstellationMessage::ReportMemory(sender) => {
2024 self.mem_profiler_chan
2026 .send(mem::ProfilerMsg::Report(sender));
2027 },
2028 ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result) => {
2029 self.handle_finish_javascript_evaluation(evaluation_id, result)
2030 },
2031 ScriptToConstellationMessage::ForwardKeyboardScroll(pipeline_id, scroll) => {
2032 if let Some(pipeline) = self.pipelines.get(&pipeline_id) &&
2033 let Err(error) =
2034 pipeline
2035 .event_loop
2036 .send(ScriptThreadMessage::ForwardKeyboardScroll(
2037 pipeline_id,
2038 scroll,
2039 ))
2040 {
2041 warn!("Could not forward {scroll:?} to {pipeline_id}: {error:?}");
2042 }
2043 },
2044 ScriptToConstellationMessage::RespondToScreenshotReadinessRequest(response) => {
2045 if let Some(webview) = self.webviews.get_mut(&webview_id) {
2046 webview.handle_screenshot_readiness_response(
2047 source_pipeline_id,
2048 response,
2049 &self.paint_proxy,
2050 );
2051 }
2052 },
2053 ScriptToConstellationMessage::TriggerGarbageCollection => {
2054 for event_loop in self.event_loops() {
2055 let _ = event_loop.send(ScriptThreadMessage::TriggerGarbageCollection);
2056 }
2057 },
2058 ScriptToConstellationMessage::AcquireWakeLock(type_) => match type_ {
2059 WakeLockType::Screen => {
2060 self.screen_wake_lock_count += 1;
2061 if self.screen_wake_lock_count == 1 &&
2062 let Err(e) = self.wake_lock_provider.acquire(type_)
2063 {
2064 warn!("Failed to acquire screen wake lock: {e}");
2065 }
2066 },
2067 },
2068 ScriptToConstellationMessage::ReleaseWakeLock(type_) => match type_ {
2069 WakeLockType::Screen => {
2070 self.screen_wake_lock_count = self.screen_wake_lock_count.saturating_sub(1);
2071 if self.screen_wake_lock_count == 0 &&
2072 let Err(e) = self.wake_lock_provider.release(type_)
2073 {
2074 warn!("Failed to release screen wake lock: {e}");
2075 }
2076 },
2077 },
2078 }
2079 }
2080
2081 fn check_origin_against_pipeline(
2085 &self,
2086 pipeline_id: &PipelineId,
2087 origin: &ImmutableOrigin,
2088 ) -> Result<(), ()> {
2089 let pipeline_origin = match self.pipelines.get(pipeline_id) {
2090 Some(pipeline) => pipeline.load_data.url.origin(),
2091 None => {
2092 warn!("Received message from closed or unknown pipeline.");
2093 return Err(());
2094 },
2095 };
2096 if &pipeline_origin == origin {
2097 return Ok(());
2098 }
2099 Err(())
2100 }
2101
2102 #[cfg(feature = "webgpu")]
2104 fn webgpu_chan_for_pipeline(
2105 &self,
2106 webview_id: WebViewId,
2107 source_pipeline_id: PipelineId,
2108 ) -> Option<WebGPU> {
2109 use webgpu::start_webgpu_thread;
2110
2111 let Some(browsing_context_group) = self.browsing_context_group_for_webview_id(&webview_id)
2112 else {
2113 warn!("WebView ({webview_id:?}) not found when handling WebGPU request");
2114 return None;
2115 };
2116 let Some(source_pipeline) = self.pipelines.get(&source_pipeline_id) else {
2117 warn!("{source_pipeline_id}: ScriptMsg from closed pipeline");
2118 return None;
2119 };
2120 let Some(host) = registered_domain_name(&source_pipeline.url) else {
2121 warn!("Invalid host url");
2122 return None;
2123 };
2124
2125 let mut browsing_context_group = browsing_context_group.borrow_mut();
2126 match browsing_context_group.webgpus.entry(host) {
2127 Entry::Vacant(v) => start_webgpu_thread(
2128 self.paint_proxy.cross_process_paint_api.clone(),
2129 self.webrender_wgpu
2130 .webrender_external_image_id_manager
2131 .clone(),
2132 self.webrender_wgpu.wgpu_image_map.clone(),
2133 )
2134 .map(|webgpu| {
2135 let msg = ScriptThreadMessage::SetWebGPUPort(webgpu.1);
2136 if let Err(e) = source_pipeline.event_loop.send(msg) {
2137 warn!(
2138 "{}: Failed to send SetWebGPUPort to pipeline ({:?})",
2139 source_pipeline_id, e
2140 );
2141 }
2142 v.insert(webgpu.0).clone()
2143 }),
2144 Entry::Occupied(o) => Some(o.get().clone()),
2145 }
2146 }
2147
2148 #[servo_tracing::instrument(skip_all)]
2149 #[cfg(feature = "webgpu")]
2150 fn handle_wgpu_request(
2151 &mut self,
2152 webview_id: WebViewId,
2153 source_pipeline_id: PipelineId,
2154 request: ScriptToConstellationMessage,
2155 ) {
2156 let webgpu_chan = self.webgpu_chan_for_pipeline(webview_id, source_pipeline_id);
2157 match request {
2158 ScriptToConstellationMessage::RequestAdapter(response_sender, options, adapter_id) => {
2159 match webgpu_chan {
2160 None => {
2161 if let Err(e) = response_sender.send(None) {
2162 warn!("Failed to send request adapter message: {}", e)
2163 }
2164 },
2165 Some(webgpu_chan) => {
2166 let adapter_request = WebGPURequest::RequestAdapter {
2167 sender: response_sender,
2168 options,
2169 adapter_id,
2170 };
2171 if webgpu_chan.0.send(adapter_request).is_err() {
2172 warn!("Failed to send request adapter message on WebGPU channel");
2173 }
2174 },
2175 }
2176 },
2177 ScriptToConstellationMessage::GetWebGPUChan(response_sender) => {
2178 if response_sender.send(webgpu_chan).is_err() {
2179 warn!(
2180 "{}: Failed to send WebGPU channel to pipeline",
2181 source_pipeline_id
2182 )
2183 }
2184 },
2185 _ => warn!("Wrong message type in handle_wgpu_request"),
2186 }
2187 }
2188
2189 #[servo_tracing::instrument(skip_all)]
2190 fn handle_message_port_transfer_completed(
2191 &mut self,
2192 router_id: Option<MessagePortRouterId>,
2193 ports: Vec<MessagePortId>,
2194 ) {
2195 let Some(router_id) = router_id else {
2196 if !ports.is_empty() {
2197 warn!(
2198 "Constellation unable to process port transfer successes, since no router id was received"
2199 );
2200 }
2201 return;
2202 };
2203 for port_id in ports.into_iter() {
2204 let mut entry = match self.message_ports.entry(port_id) {
2205 Entry::Vacant(_) => {
2206 warn!(
2207 "Constellation received a port transfer completed msg for unknown messageport {port_id:?}",
2208 );
2209 continue;
2210 },
2211 Entry::Occupied(entry) => entry,
2212 };
2213 match entry.get().state {
2214 TransferState::CompletionInProgress(expected_router_id) => {
2215 if expected_router_id != router_id {
2218 return warn!(
2219 "Transfer completed by an unexpected router: {:?}",
2220 router_id
2221 );
2222 }
2223 let new_info = MessagePortInfo {
2225 state: TransferState::Managed(router_id),
2226 entangled_with: entry.get().entangled_with,
2227 };
2228 entry.insert(new_info);
2229 },
2230 _ => warn!("Constellation received unexpected port transfer completed message"),
2231 }
2232 }
2233 }
2234
2235 fn handle_message_port_transfer_failed(
2236 &mut self,
2237 ports: FxHashMap<MessagePortId, PortTransferInfo>,
2238 ) {
2239 for (port_id, mut transfer_info) in ports.into_iter() {
2240 let Some(entry) = self.message_ports.remove(&port_id) else {
2241 warn!(
2242 "Constellation received a port transfer completed msg for unknown messageport {port_id:?}",
2243 );
2244 continue;
2245 };
2246 let new_info = match entry.state {
2247 TransferState::CompletionFailed(mut current_buffer) => {
2248 while let Some(task) = transfer_info.port_message_queue.pop_back() {
2255 current_buffer.push_front(task);
2256 }
2257 MessagePortInfo {
2259 state: TransferState::TransferInProgress(current_buffer),
2260 entangled_with: entry.entangled_with,
2261 }
2262 },
2263 TransferState::CompletionRequested(target_router_id, mut current_buffer) => {
2264 while let Some(task) = transfer_info.port_message_queue.pop_back() {
2274 current_buffer.push_front(task);
2275 }
2276 if let Some(ipc_sender) = self.message_port_routers.get(&target_router_id) {
2278 if ipc_sender
2279 .send(MessagePortMsg::CompletePendingTransfer(
2280 port_id,
2281 PortTransferInfo {
2282 port_message_queue: current_buffer,
2283 disentangled: entry.entangled_with.is_none(),
2284 },
2285 ))
2286 .is_err()
2287 {
2288 warn!("Constellation failed to send complete port transfer response.");
2289 }
2290 } else {
2291 warn!("No message-port sender for {:?}", target_router_id);
2292 }
2293
2294 MessagePortInfo {
2296 state: TransferState::CompletionInProgress(target_router_id),
2297 entangled_with: entry.entangled_with,
2298 }
2299 },
2300 _ => {
2301 warn!("Unexpected port transfer failed message received");
2302 continue;
2303 },
2304 };
2305 self.message_ports.insert(port_id, new_info);
2306 }
2307 }
2308
2309 #[servo_tracing::instrument(skip_all)]
2310 fn handle_complete_message_port_transfer(
2311 &mut self,
2312 router_id: MessagePortRouterId,
2313 ports: Vec<MessagePortId>,
2314 ) {
2315 let mut response = FxHashMap::default();
2316 for port_id in ports.into_iter() {
2317 let Some(entry) = self.message_ports.remove(&port_id) else {
2318 warn!(
2319 "Constellation asked to complete transfer for unknown messageport {port_id:?}",
2320 );
2321 continue;
2322 };
2323 let new_info = match entry.state {
2324 TransferState::TransferInProgress(buffer) => {
2325 response.insert(
2326 port_id,
2327 PortTransferInfo {
2328 port_message_queue: buffer,
2329 disentangled: entry.entangled_with.is_none(),
2330 },
2331 );
2332
2333 MessagePortInfo {
2336 state: TransferState::CompletionInProgress(router_id),
2337 entangled_with: entry.entangled_with,
2338 }
2339 },
2340 TransferState::CompletionFailed(buffer) |
2341 TransferState::CompletionRequested(_, buffer) => {
2342 MessagePortInfo {
2355 state: TransferState::CompletionRequested(router_id, buffer),
2356 entangled_with: entry.entangled_with,
2357 }
2358 },
2359 _ => {
2360 warn!("Unexpected complete port transfer message received");
2361 continue;
2362 },
2363 };
2364 self.message_ports.insert(port_id, new_info);
2365 }
2366
2367 if !response.is_empty() {
2368 if let Some(ipc_sender) = self.message_port_routers.get(&router_id) {
2370 if ipc_sender
2371 .send(MessagePortMsg::CompleteTransfer(response))
2372 .is_err()
2373 {
2374 warn!("Constellation failed to send complete port transfer response.");
2375 }
2376 } else {
2377 warn!("No message-port sender for {:?}", router_id);
2378 }
2379 }
2380 }
2381
2382 #[servo_tracing::instrument(skip_all)]
2383 fn handle_reroute_messageport(&mut self, port_id: MessagePortId, task: PortMessageTask) {
2384 let Some(info) = self.message_ports.get_mut(&port_id) else {
2385 return warn!(
2386 "Constellation asked to re-route msg to unknown messageport {:?}",
2387 port_id
2388 );
2389 };
2390 match &mut info.state {
2391 TransferState::Managed(router_id) | TransferState::CompletionInProgress(router_id) => {
2392 if let Some(ipc_sender) = self.message_port_routers.get(router_id) {
2396 let _ = ipc_sender.send(MessagePortMsg::NewTask(port_id, task));
2397 } else {
2398 warn!("No message-port sender for {:?}", router_id);
2399 }
2400 },
2401 TransferState::TransferInProgress(queue) => queue.push_back(task),
2402 TransferState::CompletionFailed(queue) => queue.push_back(task),
2403 TransferState::CompletionRequested(_, queue) => queue.push_back(task),
2404 }
2405 }
2406
2407 #[servo_tracing::instrument(skip_all)]
2408 fn handle_messageport_shipped(&mut self, port_id: MessagePortId) {
2409 if let Some(info) = self.message_ports.get_mut(&port_id) {
2410 match info.state {
2411 TransferState::Managed(_) => {
2412 info.state = TransferState::TransferInProgress(VecDeque::new());
2414 },
2415 TransferState::CompletionInProgress(_) => {
2416 info.state = TransferState::CompletionFailed(VecDeque::new());
2421 },
2422 _ => warn!("Unexpected messageport shipped received"),
2423 }
2424 } else {
2425 warn!(
2426 "Constellation asked to mark unknown messageport as shipped {:?}",
2427 port_id
2428 );
2429 }
2430 }
2431
2432 fn handle_new_messageport_router(
2433 &mut self,
2434 router_id: MessagePortRouterId,
2435 message_port_callbacks: GenericCallback<MessagePortMsg>,
2436 ) {
2437 self.message_port_routers
2438 .insert(router_id, message_port_callbacks);
2439 }
2440
2441 fn handle_remove_messageport_router(&mut self, router_id: MessagePortRouterId) {
2442 self.message_port_routers.remove(&router_id);
2443 }
2444
2445 fn handle_new_messageport(&mut self, router_id: MessagePortRouterId, port_id: MessagePortId) {
2446 match self.message_ports.entry(port_id) {
2447 Entry::Occupied(_) => warn!(
2449 "Constellation asked to start tracking an existing messageport {:?}",
2450 port_id
2451 ),
2452 Entry::Vacant(entry) => {
2453 let info = MessagePortInfo {
2454 state: TransferState::Managed(router_id),
2455 entangled_with: None,
2456 };
2457 entry.insert(info);
2458 },
2459 }
2460 }
2461
2462 #[servo_tracing::instrument(skip_all)]
2463 fn handle_entangle_messageports(&mut self, port1: MessagePortId, port2: MessagePortId) {
2464 if let Some(info) = self.message_ports.get_mut(&port1) {
2465 info.entangled_with = Some(port2);
2466 } else {
2467 warn!(
2468 "Constellation asked to entangle unknown messageport: {:?}",
2469 port1
2470 );
2471 }
2472 if let Some(info) = self.message_ports.get_mut(&port2) {
2473 info.entangled_with = Some(port1);
2474 } else {
2475 warn!(
2476 "Constellation asked to entangle unknown messageport: {:?}",
2477 port2
2478 );
2479 }
2480 }
2481
2482 #[servo_tracing::instrument(skip_all)]
2483 fn handle_disentangle_messageports(
2485 &mut self,
2486 port1: MessagePortId,
2487 port2: Option<MessagePortId>,
2488 ) {
2489 let _ = self.message_ports.remove(&port1);
2495
2496 let Some(port2) = port2 else {
2499 return;
2500 };
2501
2502 if let Some(info) = self.message_ports.get_mut(&port2) {
2504 info.entangled_with = None;
2505 match &mut info.state {
2506 TransferState::Managed(router_id) |
2507 TransferState::CompletionInProgress(router_id) => {
2508 if let Some(ipc_sender) = self.message_port_routers.get(router_id) {
2513 let _ = ipc_sender.send(MessagePortMsg::CompleteDisentanglement(port2));
2514 } else {
2515 warn!("No message-port sender for {:?}", router_id);
2516 }
2517 },
2518 _ => {
2519 },
2521 }
2522 } else {
2523 warn!(
2524 "Constellation asked to disentangle unknown messageport: {:?}",
2525 port2
2526 );
2527 }
2528 }
2529
2530 #[servo_tracing::instrument(skip_all)]
2534 fn handle_serviceworker_algorithm(
2535 &mut self,
2536 pipeline_id: PipelineId,
2537 algorithm: ServiceWorkerAlgorithm,
2538 ) {
2539 let origin = match &algorithm {
2540 ServiceWorkerAlgorithm::StartRegister(job) => job.storage_key.clone(),
2541 ServiceWorkerAlgorithm::Unregister(job) => job.storage_key.clone(),
2542 ServiceWorkerAlgorithm::MatchServiceWorkerRegistration { storage_key, .. } => {
2543 storage_key.clone()
2544 },
2545 };
2546
2547 if self
2548 .check_origin_against_pipeline(&pipeline_id, &origin)
2549 .is_err()
2550 {
2551 return warn!(
2552 "Attempt to schedule a serviceworker job from an origin not matching the origin of the job."
2553 );
2554 }
2555
2556 let sw_manager = match self.sw_managers.entry(origin.clone()) {
2558 Entry::Occupied(entry) => entry.into_mut(),
2559 Entry::Vacant(entry) => {
2560 let (own_sender, receiver) =
2561 generic_channel::channel().expect("Failed to create IPC channel!");
2562
2563 let sw_senders = SWManagerSenders {
2564 constellation_sender: self.sw_manager_sender.clone(),
2565 resource_threads: self.public_resource_threads.clone(),
2566 own_sender: own_sender.clone(),
2567 receiver,
2568 paint_api: self.paint_proxy.cross_process_paint_api.clone(),
2569 system_font_service_sender: self.system_font_service.to_sender(),
2570 };
2571
2572 #[cfg(feature = "multiprocess")]
2573 if opts::get().multiprocess {
2574 let (sender, receiver) = generic_channel::channel()
2575 .expect("Failed to create lifeline channel for sw");
2576 let content =
2577 ServiceWorkerUnprivilegedContent::new(sw_senders, origin, Some(sender));
2578
2579 if let Ok(process) = content.spawn_multiprocess() {
2580 let crossbeam_receiver = receiver.route_preserving_errors();
2581 self.process_manager.add(crossbeam_receiver, process);
2582 } else {
2583 return warn!("Failed to spawn process for SW manager.");
2584 }
2585 } else {
2586 let content = ServiceWorkerUnprivilegedContent::new(sw_senders, origin, None);
2587 content.start::<SWF>();
2588 }
2589 #[cfg(not(feature = "multiprocess"))]
2590 {
2591 let content = ServiceWorkerUnprivilegedContent::new(sw_senders, origin, None);
2592 content.start::<SWF>();
2593 }
2594
2595 entry.insert(own_sender)
2596 },
2597 };
2598 if let Err(err) = sw_manager.send(ServiceWorkerMsg::HandleAlgorithm(algorithm)) {
2599 warn!("Failed to send algorithm to SW manager: {:?}", err);
2600 }
2601 }
2602
2603 #[servo_tracing::instrument(skip_all)]
2604 fn handle_broadcast_storage_event(
2605 &self,
2606 pipeline_id: PipelineId,
2607 storage: WebStorageType,
2608 url: ServoUrl,
2609 key: Option<String>,
2610 old_value: Option<String>,
2611 new_value: Option<String>,
2612 ) {
2613 let origin = url.origin();
2614 let Some(source_pipeline) = self.pipelines.get(&pipeline_id) else {
2615 warn!("Received storage event broadcast request from closed pipeline.");
2616 return;
2617 };
2618
2619 if source_pipeline.url.origin() != origin {
2620 return warn!(
2621 "Attempt to broadcast storage event from an origin not matching the source pipeline origin."
2622 );
2623 }
2624
2625 let interested = match self
2626 .pipeline_interests
2627 .get(&ConstellationInterest::StorageEvent)
2628 {
2629 Some(set) => set,
2630 None => return,
2631 }
2632 .iter()
2633 .filter_map(|interested_id| self.pipelines.get(interested_id));
2634
2635 for pipeline in interested {
2636 if pipeline.id == pipeline_id || pipeline.url.origin() != origin {
2637 continue;
2638 }
2639
2640 if storage == WebStorageType::Session &&
2648 pipeline.webview_id != source_pipeline.webview_id
2649 {
2650 continue;
2651 }
2652
2653 let msg = ScriptThreadMessage::DispatchStorageEvent(
2654 pipeline.id,
2655 storage,
2656 url.clone(),
2657 key.clone(),
2658 old_value.clone(),
2659 new_value.clone(),
2660 );
2661 if let Err(err) = pipeline.event_loop.send(msg) {
2662 warn!(
2663 "{}: Failed to broadcast storage event to pipeline ({:?}).",
2664 pipeline.id, err
2665 );
2666 }
2667 }
2668 }
2669
2670 #[servo_tracing::instrument(skip_all)]
2671 fn handle_exit(&mut self) {
2672 debug!("Handling exit.");
2673
2674 if self.shutting_down {
2676 return;
2677 }
2678 self.shutting_down = true;
2679
2680 self.mem_profiler_chan.send(mem::ProfilerMsg::Exit);
2681
2682 self.send_message_to_all_background_hang_monitors(BackgroundHangMonitorControlMsg::Exit);
2686
2687 let browsing_context_ids: Vec<_> = self
2689 .browsing_contexts
2690 .values()
2691 .filter(|browsing_context| browsing_context.is_top_level())
2692 .map(|browsing_context| (browsing_context.webview_id, browsing_context.id))
2693 .collect();
2694 for (webview_id, browsing_context_id) in browsing_context_ids {
2695 debug!(
2696 "{}: Removing top-level browsing context",
2697 browsing_context_id
2698 );
2699 self.close_browsing_context(webview_id, browsing_context_id, ExitPipelineMode::Normal);
2700 }
2701
2702 let pending_changes: Vec<_> = self
2704 .webviews
2705 .values_mut()
2706 .flat_map(|webview| webview.pending_changes.drain(..))
2707 .collect();
2708 for pending in pending_changes {
2709 debug!(
2710 "{}: Removing pending browsing context",
2711 pending.browsing_context_id
2712 );
2713 self.close_browsing_context(
2714 pending.webview_id,
2715 pending.browsing_context_id,
2716 ExitPipelineMode::Normal,
2717 );
2718 debug!("{}: Removing pending pipeline", pending.new_pipeline_id);
2719 self.close_pipeline(
2720 pending.new_pipeline_id,
2721 DiscardBrowsingContext::Yes,
2722 ExitPipelineMode::Normal,
2723 );
2724 }
2725
2726 let browsing_context_ids: Vec<_> = self
2728 .browsing_contexts
2729 .iter()
2730 .map(|(browsing_context_id, browsing_context)| {
2731 (*browsing_context_id, browsing_context.webview_id)
2732 })
2733 .collect();
2734
2735 for (browsing_context_id, webview_id) in browsing_context_ids {
2736 debug!(
2737 "{}: Removing detached browsing context",
2738 browsing_context_id
2739 );
2740 self.close_browsing_context(webview_id, browsing_context_id, ExitPipelineMode::Normal);
2741 }
2742
2743 let pipeline_ids: Vec<PipelineId> = self.pipelines.keys().cloned().collect();
2745 for pipeline_id in pipeline_ids {
2746 debug!("{}: Removing detached pipeline", pipeline_id);
2747 self.close_pipeline(
2748 pipeline_id,
2749 DiscardBrowsingContext::Yes,
2750 ExitPipelineMode::Normal,
2751 );
2752 }
2753 }
2754
2755 #[servo_tracing::instrument(skip_all)]
2756 fn handle_shutdown(&mut self) {
2757 debug!("Handling shutdown.");
2758
2759 for join_handle in self.event_loop_join_handles.drain(..) {
2760 if join_handle.join().is_err() {
2761 error!("Failed to join on a script-thread.");
2762 }
2763 }
2764
2765 drop(self.background_monitor_register.take());
2767 if let Some(join_handle) = self.background_monitor_register_join_handle.take() &&
2768 join_handle.join().is_err()
2769 {
2770 error!("Failed to join on the bhm background thread.");
2771 }
2772
2773 let (core_ipc_sender, core_ipc_receiver) =
2777 generic_channel::oneshot().expect("Failed to create IPC channel!");
2778 let (public_client_storage_generic_sender, public_client_storage_generic_receiver) =
2779 generic_channel::channel().expect("Failed to create generic channel!");
2780 let (private_client_storage_generic_sender, private_client_storage_generic_receiver) =
2781 generic_channel::channel().expect("Failed to create generic channel!");
2782 let (private_cache_storage_generic_sender, private_cache_storage_generic_receiver) =
2783 generic_channel::channel().expect("Failed to create generic channel!");
2784 let (public_cache_storage_generic_sender, public_cache_storage_generic_receiver) =
2785 generic_channel::channel().expect("Failed to create generic channel!");
2786 let (public_indexeddb_ipc_sender, public_indexeddb_ipc_receiver) =
2787 generic_channel::channel().expect("Failed to create generic channel!");
2788 let (private_indexeddb_ipc_sender, private_indexeddb_ipc_receiver) =
2789 generic_channel::channel().expect("Failed to create generic channel!");
2790 let (public_web_storage_generic_sender, public_web_storage_generic_receiver) =
2791 generic_channel::channel().expect("Failed to create generic channel!");
2792 let (private_web_storage_generic_sender, private_web_storage_generic_receiver) =
2793 generic_channel::channel().expect("Failed to create generic channel!");
2794
2795 debug!("Exiting core resource threads.");
2796 if let Err(e) = self
2797 .public_resource_threads
2798 .send(net_traits::CoreResourceMsg::Exit(core_ipc_sender))
2799 {
2800 warn!("Exit resource thread failed ({})", e);
2801 }
2802
2803 #[cfg(feature = "devtools")]
2804 if let Some(ref chan) = self.devtools_sender {
2805 debug!("Exiting devtools.");
2806 let msg = DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::ServerExitMsg);
2807 if let Err(e) = chan.send(msg) {
2808 warn!("Exit devtools failed ({:?})", e);
2809 }
2810 }
2811
2812 debug!("Exiting public client storage thread.");
2813 if let Err(e) = generic_channel::GenericSend::send(
2814 &self.public_storage_threads,
2815 ClientStorageThreadMessage::Exit(public_client_storage_generic_sender),
2816 ) {
2817 warn!("Exit public client storage thread failed ({})", e);
2818 }
2819 debug!("Exiting private client storage thread.");
2820 if let Err(e) = generic_channel::GenericSend::send(
2821 &self.private_storage_threads,
2822 ClientStorageThreadMessage::Exit(private_client_storage_generic_sender),
2823 ) {
2824 warn!("Exit private client storage thread failed ({})", e);
2825 }
2826
2827 debug!("Exiting public cache storage thread.");
2828 if let Err(e) = generic_channel::GenericSend::send(
2829 &self.public_storage_threads,
2830 CacheStorageThreadMessage::Exit(public_cache_storage_generic_sender),
2831 ) {
2832 warn!("Exit public cache storage thread failed ({})", e);
2833 }
2834 debug!("Exiting private cache storage thread.");
2835 if let Err(e) = generic_channel::GenericSend::send(
2836 &self.private_storage_threads,
2837 CacheStorageThreadMessage::Exit(private_cache_storage_generic_sender),
2838 ) {
2839 warn!("Exit private cache storage thread failed ({})", e);
2840 }
2841
2842 debug!("Exiting public indexeddb resource threads.");
2843 if let Err(e) =
2844 self.public_storage_threads
2845 .send(IndexedDBThreadMsg::Sync(SyncOperation::Exit(
2846 public_indexeddb_ipc_sender,
2847 )))
2848 {
2849 warn!("Exit public indexeddb thread failed ({})", e);
2850 }
2851
2852 debug!("Exiting private indexeddb resource threads.");
2853 if let Err(e) =
2854 self.private_storage_threads
2855 .send(IndexedDBThreadMsg::Sync(SyncOperation::Exit(
2856 private_indexeddb_ipc_sender,
2857 )))
2858 {
2859 warn!("Exit private indexeddb thread failed ({})", e);
2860 }
2861
2862 debug!("Exiting public web storage thread.");
2863 if let Err(e) = generic_channel::GenericSend::send(
2864 &self.public_storage_threads,
2865 WebStorageThreadMsg::Exit(public_web_storage_generic_sender),
2866 ) {
2867 warn!("Exit public web storage thread failed ({})", e);
2868 }
2869
2870 debug!("Exiting private web storage thread.");
2871 if let Err(e) = generic_channel::GenericSend::send(
2872 &self.private_storage_threads,
2873 WebStorageThreadMsg::Exit(private_web_storage_generic_sender),
2874 ) {
2875 warn!("Exit private web storage thread failed ({})", e);
2876 }
2877
2878 #[cfg(feature = "bluetooth")]
2879 {
2880 debug!("Exiting bluetooth thread.");
2881 if let Err(e) = self.bluetooth_ipc_sender.send(BluetoothRequest::Exit) {
2882 warn!("Exit bluetooth thread failed ({})", e);
2883 }
2884 }
2885
2886 debug!("Exiting service worker manager thread.");
2887 for (_, mgr) in self.sw_managers.drain() {
2888 if let Err(e) = mgr.send(ServiceWorkerMsg::Exit) {
2889 warn!("Exit service worker manager failed ({})", e);
2890 }
2891 }
2892
2893 let canvas_exit_receiver = if let Some((canvas_sender, _)) = self.canvas.get() {
2894 debug!("Exiting Canvas Paint thread.");
2895 let (canvas_exit_sender, canvas_exit_receiver) = unbounded();
2896 if let Err(e) = canvas_sender.send(ConstellationCanvasMsg::Exit(canvas_exit_sender)) {
2897 warn!("Exit Canvas Paint thread failed ({})", e);
2898 }
2899 Some(canvas_exit_receiver)
2900 } else {
2901 None
2902 };
2903
2904 debug!("Exiting WebGPU threads.");
2905 #[cfg(feature = "webgpu")]
2906 {
2907 let receivers: Vec<_> = self
2908 .webviews
2909 .values()
2910 .flat_map(|webview| {
2911 std::mem::take(&mut webview.browsing_context_group().borrow_mut().webgpus)
2915 })
2916 .filter_map(|(_, webgpu)| {
2917 let (sender, receiver) =
2918 generic_channel::oneshot().expect("Failed to create IPC channel!");
2919 webgpu.exit(sender).is_ok().then_some(receiver)
2920 })
2921 .collect();
2922
2923 for receiver in receivers {
2924 if let Err(error) = receiver.recv() {
2925 warn!("Failed to receive exit response from WebGPU: {error:?}.");
2926 }
2927 }
2928 }
2929
2930 debug!("Exiting GLPlayer thread.");
2931 WindowGLContext::get().exit();
2932
2933 if let Some(canvas_exit_receiver) = canvas_exit_receiver {
2936 let _ = canvas_exit_receiver.recv();
2937 }
2938
2939 debug!("Exiting the system font service thread.");
2940 self.system_font_service.exit();
2941
2942 if let Err(e) = core_ipc_receiver.recv() {
2944 warn!("Exit resource thread failed ({:?})", e);
2945 }
2946 if let Err(e) = public_client_storage_generic_receiver.recv() {
2947 warn!("Exit public client storage thread failed ({:?})", e);
2948 }
2949 if let Err(e) = private_client_storage_generic_receiver.recv() {
2950 warn!("Exit private client storage thread failed ({:?})", e);
2951 }
2952 if let Err(e) = private_cache_storage_generic_receiver.recv() {
2953 warn!("Exit private cache storage thread failed ({:?})", e);
2954 }
2955 if let Err(e) = public_cache_storage_generic_receiver.recv() {
2956 warn!("Exit public cache storage thread failed ({:?})", e);
2957 }
2958 if let Err(e) = public_indexeddb_ipc_receiver.recv() {
2959 warn!("Exit public indexeddb thread failed ({:?})", e);
2960 }
2961 if let Err(e) = private_indexeddb_ipc_receiver.recv() {
2962 warn!("Exit private indexeddb thread failed ({:?})", e);
2963 }
2964 if let Err(e) = public_web_storage_generic_receiver.recv() {
2965 warn!("Exit public web storage thread failed ({:?})", e);
2966 }
2967 if let Err(e) = private_web_storage_generic_receiver.recv() {
2968 warn!("Exit private web storage thread failed ({:?})", e);
2969 }
2970
2971 debug!("Shutting-down IPC router thread in constellation.");
2972 ROUTER.shutdown();
2973
2974 debug!("Shutting-down the async runtime in constellation.");
2975 self.async_runtime.shutdown();
2976 }
2977
2978 fn handle_pipeline_exited(&mut self, pipeline_id: PipelineId, exit_source: PipelineExitSource) {
2979 debug!("{}: Exited", pipeline_id);
2980 self.remove_worker_animation_frame_providers_for_pipeline(pipeline_id);
2981
2982 let Some(pipeline) = self.pipelines.remove(&pipeline_id) else {
2983 return;
2984 };
2985
2986 self.constellation_to_embedder_proxy
2988 .send(ConstellationToEmbedderMsg::PipelineExited(pipeline_id));
2989
2990 self.pipeline_interests.retain(|_, set| {
2992 set.remove(&pipeline_id);
2993 !set.is_empty()
2994 });
2995
2996 self.paint_proxy.send(PaintMessage::PipelineExited(
3000 pipeline.webview_id,
3001 pipeline.id,
3002 exit_source,
3003 ));
3004 }
3005
3006 #[servo_tracing::instrument(skip_all)]
3007 fn handle_send_error(&mut self, pipeline_id: PipelineId, error: SendError) {
3008 error!("Error sending message to {pipeline_id:?}: {error}",);
3009
3010 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
3012 return;
3013 };
3014
3015 self.handle_panic_in_webview(
3017 pipeline.webview_id,
3018 &format!("Send failed ({error})"),
3019 &None,
3020 );
3021 }
3022
3023 #[servo_tracing::instrument(skip_all)]
3024 fn handle_panic(
3025 &mut self,
3026 event_loop_id: Option<ScriptEventLoopId>,
3027 reason: String,
3028 backtrace: Option<String>,
3029 ) {
3030 if self.hard_fail {
3031 error!("Pipeline failed in hard-fail mode. Crashing!");
3034 process::exit(1);
3035 }
3036
3037 let Some(event_loop_id) = event_loop_id else {
3038 return;
3039 };
3040 debug!("Panic handler for {event_loop_id:?}: {reason:?}",);
3041
3042 let mut webview_ids = HashSet::new();
3043 let mut crashed_pipelines = Vec::new();
3044 for pipeline in self.pipelines.values() {
3045 if pipeline.event_loop.id() == event_loop_id {
3046 crashed_pipelines.push(pipeline.id);
3047 webview_ids.insert(pipeline.webview_id);
3048 }
3049 }
3050
3051 for webview_id in webview_ids {
3052 self.handle_panic_in_webview(webview_id, &reason, &backtrace);
3053 }
3054
3055 for pipeline_id in crashed_pipelines {
3056 self.close_pipeline(
3057 pipeline_id,
3058 DiscardBrowsingContext::No,
3059 ExitPipelineMode::Force,
3060 );
3061 self.handle_pipeline_exited(pipeline_id, PipelineExitSource::all());
3066 }
3067 }
3068
3069 fn handle_panic_in_webview(
3070 &mut self,
3071 webview_id: WebViewId,
3072 reason: &String,
3073 backtrace: &Option<String>,
3074 ) {
3075 self.constellation_to_embedder_proxy
3076 .send(ConstellationToEmbedderMsg::Panic(
3077 webview_id,
3078 reason.clone(),
3079 backtrace.clone(),
3080 ));
3081
3082 let browsing_context_id = BrowsingContextId::from(webview_id);
3083 let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) else {
3084 return warn!("failed browsing context is missing");
3085 };
3086 let viewport_details = browsing_context.viewport_details;
3087 let pipeline_id = browsing_context.pipeline_id;
3088
3089 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
3090 return warn!("failed pipeline is missing");
3091 };
3092 let opener = pipeline.opener;
3093
3094 let old_pipeline_id = pipeline_id;
3095 let Some(old_load_data) = self.refresh_load_data(pipeline_id) else {
3096 return warn!("failed pipeline is missing");
3097 };
3098
3099 self.close_browsing_context_children(
3100 webview_id,
3101 browsing_context_id,
3102 DiscardBrowsingContext::No,
3103 ExitPipelineMode::Force,
3104 );
3105
3106 if old_load_data.crash.is_some() {
3107 return error!("crash page crashed");
3108 }
3109
3110 warn!("creating replacement pipeline for crash page");
3111
3112 let new_pipeline_id = PipelineId::new();
3113 let new_load_data = LoadData {
3114 crash: Some(
3115 backtrace
3116 .clone()
3117 .map(|backtrace| format!("{reason}\n{backtrace}"))
3118 .unwrap_or_else(|| reason.clone()),
3119 ),
3120 creation_sandboxing_flag_set: SandboxingFlagSet::all(),
3121 ..old_load_data.clone()
3122 };
3123
3124 let is_private = false;
3125 self.new_pipeline(
3126 new_pipeline_id,
3127 browsing_context_id,
3128 webview_id,
3129 None,
3130 opener,
3131 viewport_details,
3132 new_load_data,
3133 is_private,
3134 TargetSnapshotParams::default(),
3135 None,
3136 );
3137
3138 let Some(webview) = self.webviews.get_mut(&webview_id) else {
3139 return warn!("Adding pending change to unknown WebView: {webview_id:?}");
3140 };
3141 webview.add_pending_change(SessionHistoryChange {
3142 webview_id,
3143 browsing_context_id,
3144 new_pipeline_id,
3145 replace: Some(NeedsToReload::Yes(old_pipeline_id, old_load_data)),
3148 new_browsing_context_info: None,
3149 viewport_details,
3150 });
3151 }
3152
3153 #[servo_tracing::instrument(skip_all)]
3154 fn handle_set_has_system_focus(&mut self, webview_id: WebViewId, has_system_focus: bool) {
3155 let Some(webview) = self.webviews.get_mut(&webview_id) else {
3156 return warn!("Tried to focus a nonexistent WebView: {webview_id:?}");
3157 };
3158 if !webview.set_has_system_focus(has_system_focus) {
3159 return;
3160 }
3161
3162 let state = webview.state();
3163 for event_loop in self.event_loops() {
3164 if let Err(error) =
3165 event_loop.send(ScriptThreadMessage::UpdateWebViewState(state.clone()))
3166 {
3167 warn!(
3168 "Sending to closed event loop ({:?}): {error}",
3169 event_loop.id()
3170 );
3171 }
3172 }
3173 }
3174
3175 #[servo_tracing::instrument(skip_all)]
3176 fn handle_log_entry(
3177 &mut self,
3178 event_loop_id: Option<ScriptEventLoopId>,
3179 thread_name: Option<String>,
3180 entry: LogEntry,
3181 ) {
3182 if let LogEntry::Panic(ref reason, ref backtrace) = entry {
3183 self.handle_panic(event_loop_id, reason.clone(), Some(backtrace.clone()));
3184 }
3185
3186 match entry {
3187 LogEntry::Panic(reason, _) | LogEntry::Error(reason) | LogEntry::Warn(reason) => {
3188 if WARNINGS_BUFFER_SIZE <= self.handled_warnings.len() {
3190 self.handled_warnings.pop_front();
3191 }
3192 self.handled_warnings.push_back((thread_name, reason));
3193 },
3194 }
3195 }
3196
3197 fn update_pressed_mouse_buttons(&mut self, event: &MouseButtonEvent) {
3198 let Ok(buttons) = MouseButtons::try_from(event.button) else {
3199 return;
3200 };
3201 match event.action {
3202 MouseButtonAction::Down => {
3203 self.pressed_mouse_buttons |= buttons;
3204 },
3205 MouseButtonAction::Up => {
3206 self.pressed_mouse_buttons &= !(buttons);
3207 },
3208 }
3209 }
3210
3211 #[expect(deprecated)]
3212 fn update_active_keybord_modifiers(&mut self, event: &KeyboardEvent) {
3213 self.active_keyboard_modifiers = event.event.modifiers;
3214
3215 let Key::Named(named_key) = event.event.key else {
3220 return;
3221 };
3222
3223 let modified_modifier = match named_key {
3224 NamedKey::Alt => Modifiers::ALT,
3225 NamedKey::AltGraph => Modifiers::ALT_GRAPH,
3226 NamedKey::CapsLock => Modifiers::CAPS_LOCK,
3227 NamedKey::Control => Modifiers::CONTROL,
3228 NamedKey::Fn => Modifiers::FN,
3229 NamedKey::FnLock => Modifiers::FN_LOCK,
3230 NamedKey::Meta => Modifiers::META,
3231 NamedKey::NumLock => Modifiers::NUM_LOCK,
3232 NamedKey::ScrollLock => Modifiers::SCROLL_LOCK,
3233 NamedKey::Shift => Modifiers::SHIFT,
3234 NamedKey::Symbol => Modifiers::SYMBOL,
3235 NamedKey::SymbolLock => Modifiers::SYMBOL_LOCK,
3236 NamedKey::Hyper => Modifiers::HYPER,
3237 NamedKey::Super => Modifiers::META,
3240 _ => return,
3241 };
3242 match event.event.state {
3243 KeyState::Down => self.active_keyboard_modifiers.insert(modified_modifier),
3244 KeyState::Up => self.active_keyboard_modifiers.remove(modified_modifier),
3245 }
3246 }
3247
3248 fn set_accessibility_active(&mut self, webview_id: WebViewId, active: bool) {
3249 if !(pref!(accessibility_enabled)) {
3250 return;
3251 }
3252
3253 let Some(webview) = self.webviews.get_mut(&webview_id) else {
3254 return;
3255 };
3256
3257 webview.accessibility_active = active;
3258 let Some(pipeline_id) = webview.active_top_level_pipeline_id else {
3259 return;
3260 };
3261 let epoch = webview.active_top_level_pipeline_epoch;
3262 self.send_message_to_pipeline(
3267 pipeline_id,
3268 ScriptThreadMessage::SetAccessibilityActive(pipeline_id, active, epoch),
3269 "Set accessibility active after closure",
3270 );
3271 }
3272
3273 fn forward_accessibility_action(&mut self, action_request: ActionRequest) {
3274 let Some(pipeline_id) = self
3275 .pipelines
3276 .keys()
3277 .copied()
3278 .find(|&pipeline_id| TreeId::from(pipeline_id) == action_request.target_tree)
3279 else {
3280 warn!(
3281 "Could not complete accessibility action {action_request:?}: no matching pipeline."
3282 );
3283 return;
3284 };
3285
3286 self.send_message_to_pipeline(
3287 pipeline_id,
3288 ScriptThreadMessage::ForwardAccessibilityAction(pipeline_id, action_request),
3289 "Action request failed: sending message to pipeline failed.",
3290 );
3291 }
3292
3293 fn handle_clear_session_history(&mut self, webview_id: WebViewId) {
3294 let Some(webview) = self.webviews.get_mut(&webview_id) else {
3295 return;
3296 };
3297 webview.session_history.future.clear();
3298 webview.session_history.past.clear();
3299 self.notify_history_changed(webview_id);
3300 }
3301
3302 fn forward_input_event(
3303 &mut self,
3304 webview_id: WebViewId,
3305 event: InputEventAndId,
3306 hit_test_result: Option<PaintHitTestResult>,
3307 ) {
3308 if let InputEvent::MouseButton(event) = &event.event {
3309 self.update_pressed_mouse_buttons(event);
3310 }
3311
3312 if let InputEvent::Keyboard(event) = &event.event {
3313 self.update_active_keybord_modifiers(event);
3314 }
3315
3316 let pressed_mouse_buttons = self.pressed_mouse_buttons;
3319 let active_keyboard_modifiers = self.active_keyboard_modifiers;
3320
3321 let event_id = event.id;
3322 let Some(webview) = self.webviews.get_mut(&webview_id) else {
3323 warn!("Got input event for unknown WebViewId: {webview_id:?}");
3324 self.constellation_to_embedder_proxy.send(
3325 ConstellationToEmbedderMsg::InputEventsHandled(
3326 webview_id,
3327 vec![InputEventOutcome {
3328 id: event_id,
3329 result: Default::default(),
3330 }],
3331 ),
3332 );
3333 return;
3334 };
3335
3336 let event = ConstellationInputEvent {
3337 hit_test_result,
3338 pressed_mouse_buttons,
3339 active_keyboard_modifiers,
3340 event,
3341 };
3342
3343 if !webview.forward_input_event(event, &self.pipelines, &self.browsing_contexts) {
3344 self.constellation_to_embedder_proxy.send(
3345 ConstellationToEmbedderMsg::InputEventsHandled(
3346 webview_id,
3347 vec![InputEventOutcome {
3348 id: event_id,
3349 result: Default::default(),
3350 }],
3351 ),
3352 );
3353 }
3354 }
3355
3356 #[servo_tracing::instrument(skip_all)]
3357 fn handle_new_top_level_browsing_context(
3358 &mut self,
3359 url: ServoUrl,
3360 NewWebViewDetails {
3361 webview_id,
3362 viewport_details,
3363 user_content_manager_id,
3364 }: NewWebViewDetails,
3365 ) {
3366 let pipeline_id = PipelineId::new();
3367 let browsing_context_id = BrowsingContextId::from(webview_id);
3368 let load_data = LoadData::new_for_new_unrelated_webview(url);
3369 let is_private = false;
3370
3371 let mut new_webview = ConstellationWebView::new(
3374 webview_id,
3375 Rc::new(RefCell::new(BrowsingContextGroup::default())),
3377 browsing_context_id,
3378 user_content_manager_id,
3379 );
3380 new_webview.add_pending_change(SessionHistoryChange {
3381 webview_id,
3382 browsing_context_id,
3383 new_pipeline_id: pipeline_id,
3384 replace: None,
3385 new_browsing_context_info: Some(NewBrowsingContextInfo {
3386 parent_pipeline_id: None,
3387 is_private,
3388 inherited_secure_context: None,
3389 }),
3390 viewport_details,
3391 });
3392 self.webviews.insert(webview_id, new_webview);
3393
3394 self.new_pipeline(
3395 pipeline_id,
3396 browsing_context_id,
3397 webview_id,
3398 None,
3399 None,
3400 viewport_details,
3401 load_data,
3402 is_private,
3403 TargetSnapshotParams::default(),
3404 None,
3405 );
3406
3407 self.system_font_service
3408 .prefetch_font_keys_for_painter(PainterId::from(webview_id));
3409 }
3410
3411 #[servo_tracing::instrument(skip_all)]
3412 fn handle_close_top_level_browsing_context(&mut self, webview_id: WebViewId) {
3414 debug!("{webview_id}: Closing");
3415
3416 let browsing_context_id = BrowsingContextId::from(webview_id);
3418 self.close_browsing_context(webview_id, browsing_context_id, ExitPipelineMode::Normal);
3425
3426 self.constellation_to_embedder_proxy
3429 .send(ConstellationToEmbedderMsg::WebViewClosed(webview_id));
3430
3431 if let Some(mut webview) = self.webviews.remove(&webview_id) {
3433 if let Some(ongoing_request) = webview.ongoing_history_traversal_request {
3436 self.notify_embedder_of_completed_session_history_traversal_request(
3437 &ongoing_request.traversal_request,
3438 );
3439 }
3440 for request in webview.session_history_traversal_request_queue.drain(..) {
3441 self.notify_embedder_of_completed_session_history_traversal_request(&request);
3442 }
3443 }
3444
3445 debug!("{webview_id}: Closed");
3446 }
3447
3448 #[servo_tracing::instrument(skip_all)]
3449 fn handle_iframe_size_msg(&mut self, webview_id: WebViewId, iframe_sizes: Vec<IFrameSizeMsg>) {
3450 for IFrameSizeMsg {
3451 browsing_context_id,
3452 size,
3453 type_,
3454 } in iframe_sizes
3455 {
3456 self.resize_browsing_context(webview_id, size, type_, browsing_context_id);
3457 }
3458 }
3459
3460 #[servo_tracing::instrument(skip_all)]
3461 fn handle_finish_javascript_evaluation(
3462 &mut self,
3463 evaluation_id: JavaScriptEvaluationId,
3464 result: Result<JSValue, JavaScriptEvaluationError>,
3465 ) {
3466 self.constellation_to_embedder_proxy.send(
3467 ConstellationToEmbedderMsg::FinishJavaScriptEvaluation(evaluation_id, result),
3468 );
3469 }
3470
3471 #[servo_tracing::instrument(skip_all)]
3472 fn handle_subframe_load_finished(&mut self, pipeline_id: PipelineId, aborted: bool) {
3473 let browsing_context_id = match self.pipelines.get(&pipeline_id) {
3474 Some(pipeline) => pipeline.browsing_context_id,
3475 None => return warn!("Subframe ({pipeline_id:?}) load finished after closure"),
3476 };
3477 let parent_pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
3478 Some(browsing_context) => browsing_context.parent_pipeline_id,
3479 None => {
3480 return warn!(
3481 "Subframe ({pipeline_id:?}) load finished in closed {browsing_context_id:?}",
3482 );
3483 },
3484 };
3485 let Some(parent_pipeline_id) = parent_pipeline_id else {
3486 return warn!("Subframe ({pipeline_id:?}) has no parent");
3487 };
3488
3489 let msg = ScriptThreadMessage::IFrameLoadFinished {
3490 target: browsing_context_id,
3491 parent: parent_pipeline_id,
3492 child: pipeline_id,
3493 aborted,
3494 };
3495
3496 let result = match self.pipelines.get(&parent_pipeline_id) {
3497 Some(parent) => parent.event_loop.send(msg),
3498 None => {
3499 return warn!(
3500 "Parent pipeline ({parent_pipeline_id:?}) browsing context loaded after closure",
3501 );
3502 },
3503 };
3504
3505 if let Err(e) = result {
3506 self.handle_send_error(parent_pipeline_id, e);
3507 }
3508 }
3509
3510 #[servo_tracing::instrument(skip_all)]
3515 fn handle_script_loaded_url_in_iframe_msg(&mut self, load_info: IFrameLoadInfoWithData) {
3516 let IFrameLoadInfo {
3517 parent_pipeline_id,
3518 browsing_context_id,
3519 webview_id,
3520 new_pipeline_id,
3521 is_private,
3522 mut history_handling,
3523 target_snapshot_params,
3524 name,
3525 ..
3526 } = load_info.info;
3527
3528 let old_pipeline = load_info
3530 .old_pipeline_id
3531 .and_then(|id| self.pipelines.get(&id));
3532
3533 if let Some(old_pipeline) = old_pipeline {
3536 if !old_pipeline.completely_loaded {
3537 history_handling = NavigationHistoryBehavior::Replace;
3538 }
3539 debug!(
3540 "{:?}: Old pipeline is {}completely loaded",
3541 load_info.old_pipeline_id,
3542 if old_pipeline.completely_loaded {
3543 ""
3544 } else {
3545 "not "
3546 }
3547 );
3548 }
3549
3550 let is_parent_private = {
3551 let parent_browsing_context_id = match self.pipelines.get(&parent_pipeline_id) {
3552 Some(pipeline) => pipeline.browsing_context_id,
3553 None => {
3554 return warn!(
3555 "{parent_pipeline_id}: Script loaded url in iframe \
3556 {browsing_context_id} in closed parent pipeline",
3557 );
3558 },
3559 };
3560
3561 let Some(ctx) = self.browsing_contexts.get(&parent_browsing_context_id) else {
3562 return warn!(
3563 "{parent_browsing_context_id}: Script loaded url in \
3564 iframe {browsing_context_id} in closed parent browsing context",
3565 );
3566 };
3567 ctx.is_private
3568 };
3569 let is_private = is_private || is_parent_private;
3570
3571 let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) else {
3572 return warn!(
3573 "{browsing_context_id}: Script loaded url in iframe with closed browsing context",
3574 );
3575 };
3576
3577 let replace = if history_handling == NavigationHistoryBehavior::Replace {
3578 Some(NeedsToReload::No(browsing_context.pipeline_id))
3579 } else {
3580 None
3581 };
3582
3583 let browsing_context_size = browsing_context.viewport_details;
3584 debug_assert_eq!(browsing_context_size, load_info.viewport_details);
3585
3586 self.new_pipeline(
3588 new_pipeline_id,
3589 browsing_context_id,
3590 webview_id,
3591 Some(parent_pipeline_id),
3592 None,
3593 browsing_context_size,
3594 load_info.load_data,
3595 is_private,
3596 target_snapshot_params,
3597 name,
3598 );
3599
3600 let Some(webview) = self.webviews.get_mut(&webview_id) else {
3601 warn!("Could not find WebView for script-loaded iframe: ({webview_id:?})");
3602 return;
3603 };
3604
3605 let superseded_pending_changes =
3610 webview.remove_pending_changes_for_browsing_context_id(browsing_context_id);
3611 webview.add_pending_change(SessionHistoryChange {
3612 webview_id,
3613 browsing_context_id,
3614 new_pipeline_id,
3615 replace,
3616 new_browsing_context_info: None,
3618 viewport_details: load_info.viewport_details,
3619 });
3620 for change in superseded_pending_changes {
3621 self.close_pipeline(
3622 change.new_pipeline_id,
3623 DiscardBrowsingContext::No,
3624 ExitPipelineMode::Normal,
3625 );
3626 }
3627 }
3628
3629 #[servo_tracing::instrument(skip_all)]
3630 fn handle_script_new_iframe(&mut self, load_info: IFrameLoadInfoWithData) {
3631 let IFrameLoadInfo {
3632 parent_pipeline_id,
3633 new_pipeline_id,
3634 browsing_context_id,
3635 webview_id,
3636 is_private,
3637 ..
3638 } = load_info.info;
3639
3640 let (script_sender, parent_browsing_context_id) =
3641 match self.pipelines.get(&parent_pipeline_id) {
3642 Some(pipeline) => (pipeline.event_loop.clone(), pipeline.browsing_context_id),
3643 None => {
3644 return warn!(
3645 "{}: Script loaded url in closed iframe pipeline",
3646 parent_pipeline_id
3647 );
3648 },
3649 };
3650 let (is_parent_private, is_parent_secure) =
3651 match self.browsing_contexts.get(&parent_browsing_context_id) {
3652 Some(ctx) => (ctx.is_private, ctx.inherited_secure_context),
3653 None => {
3654 return warn!(
3655 "{}: New iframe {} loaded in closed parent browsing context",
3656 parent_browsing_context_id, browsing_context_id,
3657 );
3658 },
3659 };
3660
3661 let webview_hidden = self
3662 .webviews
3663 .get(&webview_id)
3664 .is_none_or(|webview| webview.hidden());
3665 let is_private = is_private || is_parent_private;
3666 let pipeline = Pipeline::new_already_spawned(
3667 new_pipeline_id,
3668 browsing_context_id,
3669 webview_id,
3670 None,
3671 script_sender,
3672 self.paint_proxy.clone(),
3673 webview_hidden,
3674 load_info.load_data,
3675 );
3676
3677 assert!(!self.pipelines.contains_key(&new_pipeline_id));
3678 self.pipelines.insert(new_pipeline_id, pipeline);
3679
3680 if let Some(webview) = self.webviews.get_mut(&webview_id) {
3681 webview.add_pending_change(SessionHistoryChange {
3682 webview_id,
3683 browsing_context_id,
3684 new_pipeline_id,
3685 replace: None,
3686 new_browsing_context_info: Some(NewBrowsingContextInfo {
3688 parent_pipeline_id: Some(parent_pipeline_id),
3689 is_private,
3690 inherited_secure_context: is_parent_secure,
3691 }),
3692 viewport_details: load_info.viewport_details,
3693 });
3694 } else {
3695 warn!("Could not find WebView for new iframe: ({webview_id:?})");
3696 }
3697 }
3698
3699 #[servo_tracing::instrument(skip_all)]
3700 fn handle_script_new_auxiliary(&mut self, load_info: AuxiliaryWebViewCreationRequest) {
3701 let AuxiliaryWebViewCreationRequest {
3702 load_data,
3703 opener_webview_id,
3704 opener_pipeline_id,
3705 response_sender,
3706 } = load_info;
3707
3708 let Some(browsing_context_group) =
3709 self.browsing_context_group_for_webview_id(&opener_webview_id)
3710 else {
3711 warn!("Opener WebView ({opener_webview_id:?}) not found for new auxiliary WebView");
3712 let _ = response_sender.send(None);
3713 return;
3714 };
3715
3716 let Some((webview_id_sender, webview_id_receiver)) = generic_channel::channel() else {
3717 warn!("Failed to create channel");
3718 let _ = response_sender.send(None);
3719 return;
3720 };
3721 self.constellation_to_embedder_proxy
3722 .send(ConstellationToEmbedderMsg::AllowOpeningWebView(
3723 opener_webview_id,
3724 webview_id_sender,
3725 ));
3726 let NewWebViewDetails {
3727 webview_id: new_webview_id,
3728 viewport_details,
3729 user_content_manager_id,
3730 } = match webview_id_receiver.recv() {
3731 Ok(Some(new_webview_details)) => new_webview_details,
3732 Ok(None) | Err(_) => {
3733 let _ = response_sender.send(None);
3734 return;
3735 },
3736 };
3737 let new_browsing_context_id = BrowsingContextId::from(new_webview_id);
3738
3739 let (script_sender, opener_browsing_context_id) =
3740 match self.pipelines.get(&opener_pipeline_id) {
3741 Some(pipeline) => (pipeline.event_loop.clone(), pipeline.browsing_context_id),
3742 None => {
3743 return warn!(
3744 "{}: Auxiliary loaded url in closed iframe pipeline",
3745 opener_pipeline_id
3746 );
3747 },
3748 };
3749 let (is_opener_private, is_opener_secure) =
3750 match self.browsing_contexts.get(&opener_browsing_context_id) {
3751 Some(ctx) => (ctx.is_private, ctx.inherited_secure_context),
3752 None => {
3753 return warn!(
3754 "{}: New auxiliary {} loaded in closed opener browsing context",
3755 opener_browsing_context_id, new_browsing_context_id,
3756 );
3757 },
3758 };
3759 let new_pipeline_id = PipelineId::new();
3760 let pipeline = Pipeline::new_already_spawned(
3761 new_pipeline_id,
3762 new_browsing_context_id,
3763 new_webview_id,
3764 Some(opener_browsing_context_id),
3765 script_sender,
3766 self.paint_proxy.clone(),
3767 false, load_data,
3772 );
3773 let _ = response_sender.send(Some(AuxiliaryWebViewCreationResponse {
3774 new_webview_id,
3775 new_pipeline_id,
3776 user_content_manager_id,
3777 }));
3778
3779 assert!(!self.pipelines.contains_key(&new_pipeline_id));
3780 self.pipelines.insert(new_pipeline_id, pipeline);
3781
3782 let mut new_webview = ConstellationWebView::new(
3783 new_webview_id,
3784 browsing_context_group,
3785 new_browsing_context_id,
3786 user_content_manager_id,
3787 );
3788
3789 let opener_theme = self
3792 .webviews
3793 .get(&opener_webview_id)
3794 .map_or(Theme::Light, |webview| webview.theme());
3795 new_webview.set_theme(opener_theme);
3796
3797 new_webview.add_pending_change(SessionHistoryChange {
3798 webview_id: new_webview_id,
3799 browsing_context_id: new_browsing_context_id,
3800 new_pipeline_id,
3801 replace: None,
3802 new_browsing_context_info: Some(NewBrowsingContextInfo {
3803 parent_pipeline_id: None,
3805 is_private: is_opener_private,
3806 inherited_secure_context: is_opener_secure,
3807 }),
3808 viewport_details,
3809 });
3810 self.webviews.insert(new_webview_id, new_webview);
3811 }
3812
3813 #[servo_tracing::instrument(skip_all)]
3814 fn handle_refresh_cursor(&self, pipeline_id: PipelineId) {
3815 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
3816 return;
3817 };
3818
3819 if let Err(error) = pipeline
3820 .event_loop
3821 .send(ScriptThreadMessage::RefreshCursor(pipeline_id))
3822 {
3823 warn!("Could not send RefreshCursor message to pipeline: {error:?}");
3824 }
3825 }
3826
3827 #[servo_tracing::instrument(skip_all)]
3828 fn handle_change_running_animations_state(
3829 &mut self,
3830 pipeline_id: PipelineId,
3831 animation_state: AnimationState,
3832 ) {
3833 match animation_state {
3834 AnimationState::AnimationCallbacksPresent => {
3835 self.handle_change_document_animation_frame_provider_state(pipeline_id, true);
3836 },
3837 AnimationState::AnimationCallbacksAbsent => {
3838 self.handle_change_document_animation_frame_provider_state(pipeline_id, false);
3839 },
3840 AnimationState::AnimationsPresent | AnimationState::NoAnimationsPresent => {
3841 if let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) &&
3842 pipeline.animation_state != animation_state
3843 {
3844 pipeline.animation_state = animation_state;
3845 self.paint_proxy
3846 .send(PaintMessage::ChangeRunningAnimationsState(
3847 pipeline.webview_id,
3848 pipeline_id,
3849 animation_state,
3850 ))
3851 }
3852 },
3853 }
3854 }
3855
3856 fn send_animation_frame_callbacks_state_if_changed(&mut self, pipeline_id: PipelineId) {
3857 let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) else {
3858 return;
3859 };
3860
3861 let callbacks_active =
3863 pipeline.document_callbacks_active || !pipeline.worker_callbacks_active.is_empty();
3864 if pipeline.last_callbacks_active_sent_to_paint == callbacks_active {
3865 return;
3866 }
3867
3868 pipeline.last_callbacks_active_sent_to_paint = callbacks_active;
3869 let animation_state = if callbacks_active {
3870 AnimationState::AnimationCallbacksPresent
3871 } else {
3872 AnimationState::AnimationCallbacksAbsent
3873 };
3874
3875 self.paint_proxy
3876 .send(PaintMessage::ChangeRunningAnimationsState(
3877 pipeline.webview_id,
3878 pipeline_id,
3879 animation_state,
3880 ));
3881 }
3882
3883 fn handle_change_document_animation_frame_provider_state(
3884 &mut self,
3885 pipeline_id: PipelineId,
3886 active: bool,
3887 ) {
3888 let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) else {
3889 return;
3890 };
3891
3892 if pipeline.document_callbacks_active == active {
3893 return;
3894 }
3895
3896 pipeline.document_callbacks_active = active;
3897 self.send_animation_frame_callbacks_state_if_changed(pipeline_id);
3898 }
3899
3900 fn handle_register_worker_animation_frame_provider(
3901 &mut self,
3902 webview_id: WebViewId,
3903 pipeline_id: PipelineId,
3904 worker_id: WorkerId,
3905 sender: GenericSender<WorkerAnimationFrameTick>,
3906 ) {
3907 if !self.pipelines.contains_key(&pipeline_id) {
3908 debug!(
3909 "Ignoring worker animation frame provider for closed pipeline: worker={worker_id:?}, pipeline={pipeline_id:?} ---->",
3910 );
3911 return;
3912 }
3913
3914 debug!(
3915 "Registering worker animation frame provider: worker={worker_id:?}, pipeline={pipeline_id:?}---->",
3916 );
3917 self.worker_animation_frame_providers.insert(
3918 worker_id,
3919 WorkerAnimationFrameProvider {
3920 webview_id,
3921 pipeline_id,
3922 sender,
3923 tick_pending: false,
3924 },
3925 );
3926 }
3927
3928 fn handle_unregister_worker_animation_frame_provider(&mut self, worker_id: WorkerId) {
3929 let Some(provider) = self.worker_animation_frame_providers.remove(&worker_id) else {
3930 return;
3931 };
3932
3933 debug!(
3934 "Unregistering worker animation frame provider: worker={worker_id:?}, pipeline={:?}",
3935 provider.pipeline_id,
3936 );
3937 if let Some(pipeline) = self.pipelines.get_mut(&provider.pipeline_id) {
3938 pipeline.worker_callbacks_active.remove(&worker_id);
3939 }
3940 self.send_animation_frame_callbacks_state_if_changed(provider.pipeline_id);
3941 }
3942
3943 fn remove_worker_animation_frame_providers_for_pipeline(&mut self, pipeline_id: PipelineId) {
3944 let worker_ids = self
3945 .worker_animation_frame_providers
3946 .iter()
3947 .filter_map(|(worker_id, provider)| {
3948 (provider.pipeline_id == pipeline_id).then_some(*worker_id)
3949 })
3950 .collect::<Vec<_>>();
3951
3952 if worker_ids.is_empty() {
3953 return;
3954 }
3955
3956 for worker_id in &worker_ids {
3957 self.worker_animation_frame_providers.remove(worker_id);
3958 }
3959
3960 if let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) {
3961 let mut changed = false;
3962 for worker_id in &worker_ids {
3963 changed |= pipeline.worker_callbacks_active.remove(worker_id);
3964 }
3965 if changed {
3966 self.send_animation_frame_callbacks_state_if_changed(pipeline_id);
3967 }
3968 }
3969 }
3970
3971 fn handle_change_worker_animation_frame_provider_state(
3972 &mut self,
3973 pipeline_id: PipelineId,
3974 worker_id: WorkerId,
3975 active: bool,
3976 ) {
3977 let Some(provider) = self.worker_animation_frame_providers.get_mut(&worker_id) else {
3978 return;
3979 };
3980 let provider_pipeline_id = provider.pipeline_id;
3981 let provider_webview_id = provider.webview_id;
3982 if provider_pipeline_id != pipeline_id {
3983 warn!("Worker animation frame state arrived for an unexpected pipeline --->");
3984 return;
3985 }
3986 let tick_was_pending = std::mem::take(&mut provider.tick_pending);
3987
3988 if !self.pipelines.contains_key(&pipeline_id) {
3989 debug!(
3990 "Removing worker animation frame provider for closed pipeline: worker={worker_id:?}, pipeline={pipeline_id:?}",
3991 );
3992 self.worker_animation_frame_providers.remove(&worker_id);
3993 return;
3994 }
3995
3996 let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) else {
3997 return;
3998 };
3999
4000 let changed = if active {
4001 pipeline.worker_callbacks_active.insert(worker_id)
4002 } else {
4003 pipeline.worker_callbacks_active.remove(&worker_id)
4004 };
4005 debug!(
4006 "Worker animation frame provider state: worker={worker_id:?}, pipeline={pipeline_id:?}, active={active}, changed={changed}",
4007 );
4008 if changed {
4009 self.send_animation_frame_callbacks_state_if_changed(pipeline_id);
4010 } else if active && tick_was_pending {
4011 self.paint_proxy
4014 .send(PaintMessage::GenerateFrame(vec![PainterId::from(
4015 provider_webview_id,
4016 )]));
4017 }
4018 }
4019
4020 fn deliver_rendering_opportunity_to_worker(&mut self, worker_id: WorkerId) {
4021 let mut send_failed = false;
4022 {
4023 let Some(provider) = self.worker_animation_frame_providers.get_mut(&worker_id) else {
4024 return;
4025 };
4026 if provider.tick_pending {
4027 debug!("Skipping pending rendering opportunity: worker={worker_id:?}");
4028 return;
4029 }
4030 if provider.sender.send(WorkerAnimationFrameTick).is_err() {
4031 send_failed = true;
4032 } else {
4033 debug!("Delivered rendering opportunity: worker={worker_id:?}");
4034 provider.tick_pending = true;
4035 }
4036 }
4037
4038 if send_failed {
4039 self.handle_unregister_worker_animation_frame_provider(worker_id);
4040 }
4041 }
4042
4043 #[servo_tracing::instrument(skip_all)]
4045 fn handle_tick_animation(&mut self, webview_ids: Vec<WebViewId>) {
4046 let mut animating_event_loops = HashSet::new();
4047 let mut animating_workers = FxHashSet::default();
4048
4049 for webview_id in webview_ids.iter() {
4050 for browsing_context in self.fully_active_browsing_contexts_iter(*webview_id) {
4051 let Some(pipeline) = self.pipelines.get(&browsing_context.pipeline_id) else {
4052 continue;
4053 };
4054
4055 animating_workers.extend(pipeline.worker_callbacks_active.iter().copied());
4056
4057 let event_loop = &pipeline.event_loop;
4059 if !animating_event_loops.contains(&event_loop.id()) {
4060 let _ = event_loop
4065 .send(ScriptThreadMessage::TickAllAnimations(webview_ids.clone()));
4066 animating_event_loops.insert(event_loop.id());
4067 }
4068 }
4069 }
4070
4071 for worker_id in animating_workers {
4074 let Some(provider) = self.worker_animation_frame_providers.get(&worker_id) else {
4075 error!(
4076 "No animation frame provider found for active worker: worker={worker_id:?}, webviews={webview_ids:?}",
4077 );
4078 continue;
4079 };
4080 if webview_ids.contains(&provider.webview_id) {
4081 self.deliver_rendering_opportunity_to_worker(worker_id);
4082 } else {
4083 error!(
4084 "Worker animation frame provider's WebView is not part of the current refresh tick: worker={worker_id:?}, provider_webview={:?}, webviews={webview_ids:?}",
4085 provider.webview_id,
4086 );
4087 }
4088 }
4089 }
4090
4091 #[servo_tracing::instrument(skip_all)]
4092 fn handle_no_longer_waiting_on_asynchronous_image_updates(
4093 &mut self,
4094 pipeline_ids: Vec<PipelineId>,
4095 ) {
4096 for pipeline_id in pipeline_ids.into_iter() {
4097 if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
4098 let _ = pipeline.event_loop.send(
4099 ScriptThreadMessage::NoLongerWaitingOnAsychronousImageUpdates(pipeline_id),
4100 );
4101 }
4102 }
4103 }
4104
4105 #[servo_tracing::instrument(skip_all)]
4109 fn schedule_navigation(
4110 &mut self,
4111 webview_id: WebViewId,
4112 source_id: PipelineId,
4113 load_data: LoadData,
4114 history_handling: NavigationHistoryBehavior,
4115 target_snapshot_params: TargetSnapshotParams,
4116 ) {
4117 match self.pending_approval_navigations.entry(source_id) {
4118 Entry::Occupied(_) => {
4119 return warn!(
4120 "{}: Tried to schedule a navigation while one is already pending",
4121 source_id
4122 );
4123 },
4124 Entry::Vacant(entry) => {
4125 let _ = entry.insert(PendingApprovalNavigation {
4126 load_data: load_data.clone(),
4127 history_behaviour: history_handling,
4128 target_snapshot_params,
4129 });
4130 },
4131 };
4132
4133 if load_data.is_initial_about_blank {
4134 assert_eq!(load_data.url.as_str(), "about:blank");
4135 self.handle_allow_navigation_response(source_id, true);
4138 } else {
4139 self.constellation_to_embedder_proxy.send(
4141 ConstellationToEmbedderMsg::AllowNavigationRequest(
4142 webview_id,
4143 source_id,
4144 load_data.url,
4145 ),
4146 );
4147 }
4148 }
4149
4150 fn handle_allow_navigation_response(&mut self, pipeline_id: PipelineId, allowed: bool) {
4154 let pending = self.pending_approval_navigations.remove(&pipeline_id);
4155
4156 let webview_id = match self.pipelines.get(&pipeline_id) {
4157 Some(pipeline) => pipeline.webview_id,
4158 None => return warn!("{}: Attempted to navigate after closure", pipeline_id),
4159 };
4160
4161 match pending {
4162 Some(pending) => {
4163 if allowed {
4164 self.load_url(
4165 webview_id,
4166 pipeline_id,
4167 pending.load_data,
4168 pending.history_behaviour,
4169 pending.target_snapshot_params,
4170 );
4171 } else {
4172 if let Some((sender, id)) = &self.webdriver_load_status_sender &&
4173 pipeline_id == *id
4174 {
4175 let _ = sender.send(WebDriverLoadStatus::NavigationStop);
4176 }
4177
4178 let pipeline_is_top_level_pipeline = self
4179 .browsing_contexts
4180 .get(&BrowsingContextId::from(webview_id))
4181 .is_some_and(|ctx| ctx.pipeline_id == pipeline_id);
4182 if !pipeline_is_top_level_pipeline {
4186 self.send_message_to_pipeline(
4187 pipeline_id,
4188 ScriptThreadMessage::StopDelayingLoadEventsMode(pipeline_id),
4189 "Attempted to navigate after closure",
4190 );
4191 }
4192 }
4193 },
4194 None => {
4195 warn!(
4196 "{}: AllowNavigationResponse for unknown request",
4197 pipeline_id
4198 )
4199 },
4200 }
4201 }
4202
4203 #[servo_tracing::instrument(skip_all)]
4204 fn load_url(
4205 &mut self,
4206 webview_id: WebViewId,
4207 source_id: PipelineId,
4208 load_data: LoadData,
4209 history_handling: NavigationHistoryBehavior,
4210 target_snapshot_params: TargetSnapshotParams,
4211 ) -> Option<PipelineId> {
4212 debug!(
4213 "{}: Loading ({}replacing): {}",
4214 source_id,
4215 match history_handling {
4216 NavigationHistoryBehavior::Push => "not ",
4217 NavigationHistoryBehavior::Replace => "",
4218 NavigationHistoryBehavior::Auto => "unsure if ",
4219 },
4220 load_data.url,
4221 );
4222 let (browsing_context_id, opener) = match self.pipelines.get(&source_id) {
4229 Some(pipeline) => (pipeline.browsing_context_id, pipeline.opener),
4230 None => {
4231 warn!("{}: Loaded after closure", source_id);
4232 return None;
4233 },
4234 };
4235 let (viewport_details, pipeline_id, parent_pipeline_id, is_private) =
4236 match self.browsing_contexts.get(&browsing_context_id) {
4237 Some(ctx) => (
4238 ctx.viewport_details,
4239 ctx.pipeline_id,
4240 ctx.parent_pipeline_id,
4241 ctx.is_private,
4242 ),
4243 None => {
4244 warn!(
4249 "{}: Loaded url in closed {}",
4250 source_id, browsing_context_id,
4251 );
4252 return None;
4253 },
4254 };
4255
4256 #[cfg(feature = "devtools")]
4257 if let Some(ref chan) = self.devtools_sender {
4258 let state = NavigationState::Start(load_data.url.clone());
4259 let _ = chan.send(DevtoolsControlMsg::FromScript(
4260 ScriptToDevtoolsControlMsg::Navigate(browsing_context_id, state),
4261 ));
4262 }
4263
4264 match parent_pipeline_id {
4265 Some(parent_pipeline_id) => {
4266 let msg = ScriptThreadMessage::NavigateIframe(
4269 parent_pipeline_id,
4270 browsing_context_id,
4271 load_data,
4272 history_handling,
4273 target_snapshot_params,
4274 );
4275 let result = match self.pipelines.get(&parent_pipeline_id) {
4276 Some(parent_pipeline) => parent_pipeline.event_loop.send(msg),
4277 None => {
4278 warn!("{}: Child loaded after closure", parent_pipeline_id);
4279 return None;
4280 },
4281 };
4282 if let Err(e) = result {
4283 self.handle_send_error(parent_pipeline_id, e);
4284 } else if let Some((sender, id)) = &self.webdriver_load_status_sender &&
4285 source_id == *id
4286 {
4287 let _ = sender.send(WebDriverLoadStatus::NavigationStop);
4288 }
4289
4290 None
4291 },
4292 None => {
4293 if let Some(webview) = self.webviews.get(&webview_id) &&
4295 webview.pending_changes.iter().any(|pending_change| {
4296 pending_change.browsing_context_id == browsing_context_id
4297 })
4298 {
4299 return None;
4301 }
4302
4303 if self.get_activity(source_id) == DocumentActivity::Inactive {
4304 return None;
4309 }
4310
4311 let replace = if history_handling == NavigationHistoryBehavior::Replace {
4317 Some(NeedsToReload::No(pipeline_id))
4318 } else {
4319 None
4320 };
4321
4322 let new_pipeline_id = PipelineId::new();
4323 self.new_pipeline(
4324 new_pipeline_id,
4325 browsing_context_id,
4326 webview_id,
4327 None,
4328 opener,
4329 viewport_details,
4330 load_data,
4331 is_private,
4332 target_snapshot_params,
4333 None,
4334 );
4335
4336 if let Some(webview) = self.webviews.get_mut(&webview_id) {
4337 webview.add_pending_change(SessionHistoryChange {
4338 webview_id,
4339 browsing_context_id,
4340 new_pipeline_id,
4341 replace,
4342 new_browsing_context_info: None,
4344 viewport_details,
4345 });
4346 } else {
4347 warn!("Could not find WebView for URL load: ({webview_id:?})");
4348 }
4349
4350 Some(new_pipeline_id)
4351 },
4352 }
4353 }
4354
4355 #[servo_tracing::instrument(skip_all)]
4356 fn handle_abort_load_url_msg(&mut self, webview_id: WebViewId, new_pipeline_id: PipelineId) {
4357 if self
4358 .webviews
4359 .get_mut(&webview_id)
4360 .and_then(|webview| webview.remove_pending_change_for_pipeline(new_pipeline_id))
4361 .is_none()
4362 {
4363 return;
4364 }
4365
4366 self.close_pipeline(
4367 new_pipeline_id,
4368 DiscardBrowsingContext::No,
4369 ExitPipelineMode::Normal,
4370 );
4371
4372 if let Some(webview) = self.webviews.get_mut(&webview_id) {
4373 webview.send_screenshot_readiness_requests_to_pipelines(
4374 &self.browsing_contexts,
4375 &self.pipelines,
4376 );
4377 };
4378 }
4379
4380 #[servo_tracing::instrument(skip_all)]
4381 fn handle_load_finished_message(
4382 &mut self,
4383 webview_id: WebViewId,
4384 pipeline_id: PipelineId,
4385 aborted: bool,
4386 ) {
4387 debug!("Load finished in {pipeline_id:?} aborted={aborted}");
4388
4389 if !aborted && let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) {
4390 pipeline.completely_loaded = true;
4391 }
4392
4393 let pipeline_is_top_level_pipeline = self
4396 .browsing_contexts
4397 .get(&BrowsingContextId::from(webview_id))
4398 .is_some_and(|ctx| ctx.pipeline_id == pipeline_id);
4399 if !pipeline_is_top_level_pipeline {
4400 self.handle_subframe_load_finished(pipeline_id, aborted);
4401 }
4402 }
4403
4404 #[servo_tracing::instrument(skip_all)]
4405 fn handle_navigated_to_fragment(
4406 &mut self,
4407 pipeline_id: PipelineId,
4408 new_url: ServoUrl,
4409 history_handling: NavigationHistoryBehavior,
4410 ) {
4411 let (webview_id, old_url) = match self.pipelines.get_mut(&pipeline_id) {
4412 Some(pipeline) => {
4413 let old_url = replace(&mut pipeline.url, new_url.clone());
4414 (pipeline.webview_id, old_url)
4415 },
4416 None => {
4417 return warn!("{}: Navigated to fragment after closure", pipeline_id);
4418 },
4419 };
4420
4421 let Some(webview) = self.webviews.get_mut(&webview_id) else {
4422 return warn!("Ignoring navigation in non-existent WebView ({webview_id:?}).");
4423 };
4424
4425 match history_handling {
4426 NavigationHistoryBehavior::Replace => {},
4427 _ => {
4428 let diff = SessionHistoryDiff::Hash {
4429 pipeline_reloader: NeedsToReload::No(pipeline_id),
4430 new_url,
4431 old_url,
4432 };
4433
4434 webview.session_history.push_diff(diff);
4435 self.notify_history_changed(webview_id);
4436 },
4437 }
4438 }
4439
4440 #[servo_tracing::instrument(skip_all)]
4447 fn handle_traverse_session_history_msg(
4448 &mut self,
4449 traversal_request: SessionHistoryTraversalRequest,
4450 ) {
4451 let webview_id = traversal_request.webview_id;
4452 let Some(webview) = self.webviews.get_mut(&webview_id) else {
4453 self.notify_embedder_of_completed_session_history_traversal_request(&traversal_request);
4454 return warn!("Ignoring history traversal in non-existent WebView ({webview_id:?}).");
4455 };
4456
4457 webview
4458 .session_history_traversal_request_queue
4459 .push_back(traversal_request);
4460 self.apply_queued_session_history_traversal_requests(webview_id);
4461 }
4462
4463 fn apply_session_history_traversal_request(
4467 &mut self,
4468 traversal_request: SessionHistoryTraversalRequest,
4469 ) -> Option<SessionHistoryTraversalRequest> {
4470 let mut browsing_context_changes = FxHashMap::<BrowsingContextId, NeedsToReload>::default();
4471 let mut pipeline_changes =
4472 FxHashMap::<PipelineId, (Option<HistoryStateId>, ServoUrl)>::default();
4473 let mut url_to_load = FxHashMap::<PipelineId, ServoUrl>::default();
4474 let webview_id = traversal_request.webview_id;
4475
4476 {
4477 let Some(webview) = self.webviews.get_mut(&webview_id) else {
4478 warn!("Ignoring history traversal in non-existent WebView ({webview_id:?}).");
4479 return Some(traversal_request);
4480 };
4481
4482 match traversal_request.direction {
4483 TraversalDirection::Forward(forward) => {
4484 let future_length = webview.session_history.future.len();
4485 if future_length < forward {
4486 warn!("Cannot traverse that far into the future.");
4487 return Some(traversal_request);
4488 }
4489
4490 for diff in webview
4491 .session_history
4492 .future
4493 .drain(future_length - forward..)
4494 .rev()
4495 {
4496 match diff {
4497 SessionHistoryDiff::BrowsingContext {
4498 browsing_context_id,
4499 ref new_reloader,
4500 ..
4501 } => {
4502 browsing_context_changes
4503 .insert(browsing_context_id, new_reloader.clone());
4504 },
4505 SessionHistoryDiff::Pipeline {
4506 ref pipeline_reloader,
4507 new_history_state_id,
4508 ref new_url,
4509 ..
4510 } => match *pipeline_reloader {
4511 NeedsToReload::No(pipeline_id) => {
4512 pipeline_changes.insert(
4513 pipeline_id,
4514 (Some(new_history_state_id), new_url.clone()),
4515 );
4516 },
4517 NeedsToReload::Yes(pipeline_id, ..) => {
4518 url_to_load.insert(pipeline_id, new_url.clone());
4519 },
4520 },
4521 SessionHistoryDiff::Hash {
4522 ref pipeline_reloader,
4523 ref new_url,
4524 ..
4525 } => match *pipeline_reloader {
4526 NeedsToReload::No(pipeline_id) => {
4527 let state = pipeline_changes
4528 .get(&pipeline_id)
4529 .and_then(|change| change.0);
4530 pipeline_changes.insert(pipeline_id, (state, new_url.clone()));
4531 },
4532 NeedsToReload::Yes(pipeline_id, ..) => {
4533 url_to_load.insert(pipeline_id, new_url.clone());
4534 },
4535 },
4536 }
4537 webview.session_history.past.push(diff);
4538 }
4539 },
4540 TraversalDirection::Back(back) => {
4541 let past_length = webview.session_history.past.len();
4542 if past_length < back {
4543 warn!("Cannot traverse that far into the past.");
4544 return Some(traversal_request);
4545 }
4546
4547 for diff in webview
4548 .session_history
4549 .past
4550 .drain(past_length - back..)
4551 .rev()
4552 {
4553 match diff {
4554 SessionHistoryDiff::BrowsingContext {
4555 browsing_context_id,
4556 ref old_reloader,
4557 ..
4558 } => {
4559 browsing_context_changes
4560 .insert(browsing_context_id, old_reloader.clone());
4561 },
4562 SessionHistoryDiff::Pipeline {
4563 ref pipeline_reloader,
4564 old_history_state_id,
4565 ref old_url,
4566 ..
4567 } => match *pipeline_reloader {
4568 NeedsToReload::No(pipeline_id) => {
4569 pipeline_changes.insert(
4570 pipeline_id,
4571 (old_history_state_id, old_url.clone()),
4572 );
4573 },
4574 NeedsToReload::Yes(pipeline_id, ..) => {
4575 url_to_load.insert(pipeline_id, old_url.clone());
4576 },
4577 },
4578 SessionHistoryDiff::Hash {
4579 ref pipeline_reloader,
4580 ref old_url,
4581 ..
4582 } => match *pipeline_reloader {
4583 NeedsToReload::No(pipeline_id) => {
4584 let state = pipeline_changes
4585 .get(&pipeline_id)
4586 .and_then(|change| change.0);
4587 pipeline_changes.insert(pipeline_id, (state, old_url.clone()));
4588 },
4589 NeedsToReload::Yes(pipeline_id, ..) => {
4590 url_to_load.insert(pipeline_id, old_url.clone());
4591 },
4592 },
4593 }
4594 webview.session_history.future.push(diff);
4595 }
4596 },
4597 }
4598 }
4599
4600 let pipelines_awaiting_activation: FxHashSet<_> = browsing_context_changes
4601 .drain()
4602 .filter_map(|(browsing_context_id, mut pipeline_reloader)| {
4603 if let NeedsToReload::Yes(pipeline_id, ref mut load_data) = pipeline_reloader &&
4604 let Some(url) = url_to_load.get(&pipeline_id)
4605 {
4606 load_data.url = url.clone();
4607 }
4608 self.activate_history_entry_in_browsing_context(
4609 browsing_context_id,
4610 pipeline_reloader,
4611 )
4612 })
4613 .collect();
4614
4615 for (pipeline_id, (history_state_id, url)) in pipeline_changes.drain() {
4616 self.update_pipeline_history_state(pipeline_id, history_state_id, url);
4617 }
4618
4619 self.notify_history_changed(webview_id);
4620 self.trim_history(webview_id);
4621 self.set_frame_tree_for_webview(webview_id);
4622
4623 if pipelines_awaiting_activation.is_empty() {
4627 return Some(traversal_request);
4628 }
4629
4630 let Some(webview) = self.webviews.get_mut(&webview_id) else {
4631 return Some(traversal_request);
4632 };
4633 webview.ongoing_history_traversal_request = Some(OngoingHistoryTraversalRequest {
4634 traversal_request,
4635 pipelines_awaiting_activation,
4636 });
4637 None
4638 }
4639
4640 fn finish_completed_session_history_traversal_requests(&mut self) {
4645 while let Some(traversal_request) = self
4646 .webviews
4647 .values_mut()
4648 .find_map(|webview| webview.maybe_finish_ongoing_session_history_traversal_request())
4649 {
4650 self.notify_embedder_of_completed_session_history_traversal_request(&traversal_request);
4651 self.apply_queued_session_history_traversal_requests(traversal_request.webview_id);
4652 }
4653 }
4654
4655 fn notify_embedder_of_completed_session_history_traversal_request(
4658 &self,
4659 traversal_request: &SessionHistoryTraversalRequest,
4660 ) {
4661 if traversal_request.source == HistoryTraversalSource::Embedder {
4662 self.constellation_to_embedder_proxy.send(
4663 ConstellationToEmbedderMsg::HistoryTraversalComplete(
4664 traversal_request.webview_id,
4665 traversal_request.id.clone(),
4666 ),
4667 );
4668 }
4669 }
4670
4671 fn apply_next_queued_session_history_traversal_request(
4674 &mut self,
4675 webview_id: WebViewId,
4676 ) -> Option<SessionHistoryTraversalRequest> {
4677 let webview = self.webviews.get_mut(&webview_id)?;
4678 if webview.ongoing_history_traversal_request.is_some() {
4679 return None;
4680 }
4681
4682 let traversal_request = webview
4683 .session_history_traversal_request_queue
4684 .pop_front()?;
4685 self.apply_session_history_traversal_request(traversal_request)
4686 }
4687
4688 fn apply_queued_session_history_traversal_requests(&mut self, webview_id: WebViewId) {
4692 while let Some(traversal_request) =
4693 self.apply_next_queued_session_history_traversal_request(webview_id)
4694 {
4695 self.notify_embedder_of_completed_session_history_traversal_request(&traversal_request);
4696 }
4697 }
4698
4699 #[servo_tracing::instrument(skip_all)]
4705 fn activate_history_entry_in_browsing_context(
4706 &mut self,
4707 browsing_context_id: BrowsingContextId,
4708 new_reloader: NeedsToReload,
4709 ) -> Option<PipelineId> {
4710 let new_pipeline_id = match new_reloader {
4711 NeedsToReload::No(pipeline_id) => pipeline_id,
4712 NeedsToReload::Yes(pipeline_id, mut load_data) => {
4713 debug!(
4714 "{}: Reloading document {}",
4715 browsing_context_id, pipeline_id,
4716 );
4717
4718 load_data.history_navigation = true;
4722 load_data.reload_navigation = false;
4723
4724 let (webview_id, old_pipeline_id, parent_pipeline_id, viewport_details, is_private) =
4725 match self.browsing_contexts.get(&browsing_context_id) {
4726 Some(ctx) => (
4727 ctx.webview_id,
4728 ctx.pipeline_id,
4729 ctx.parent_pipeline_id,
4730 ctx.viewport_details,
4731 ctx.is_private,
4732 ),
4733 None => {
4734 warn!("No browsing context to traverse!");
4735 return None;
4736 },
4737 };
4738 let opener = match self.pipelines.get(&old_pipeline_id) {
4739 Some(pipeline) => pipeline.opener,
4740 None => None,
4741 };
4742 let new_pipeline_id = PipelineId::new();
4743 self.new_pipeline(
4744 new_pipeline_id,
4745 browsing_context_id,
4746 webview_id,
4747 parent_pipeline_id,
4748 opener,
4749 viewport_details,
4750 load_data.clone(),
4751 is_private,
4752 TargetSnapshotParams::default(),
4756 None,
4757 );
4758
4759 let webview = self
4760 .webviews
4761 .get_mut(&webview_id)
4762 .expect("WebView for history traversal should always exist at this point");
4763 webview.add_pending_change(SessionHistoryChange {
4764 webview_id,
4765 browsing_context_id,
4766 new_pipeline_id,
4767 replace: Some(NeedsToReload::Yes(pipeline_id, load_data)),
4768 new_browsing_context_info: None,
4770 viewport_details,
4771 });
4772 return Some(new_pipeline_id);
4773 },
4774 };
4775
4776 let (old_pipeline_id, parent_pipeline_id, webview_id) =
4777 match self.browsing_contexts.get_mut(&browsing_context_id) {
4778 Some(browsing_context) => {
4779 let old_pipeline_id = browsing_context.pipeline_id;
4780 browsing_context.update_current_entry(new_pipeline_id);
4781 (
4782 old_pipeline_id,
4783 browsing_context.parent_pipeline_id,
4784 browsing_context.webview_id,
4785 )
4786 },
4787 None => {
4788 warn!("{browsing_context_id}: Closed during traversal");
4789 return None;
4790 },
4791 };
4792
4793 self.unload_document(old_pipeline_id);
4794
4795 if let Some(new_pipeline) = self.pipelines.get_mut(&new_pipeline_id) {
4796 #[cfg(feature = "devtools")]
4797 if let Some(ref chan) = self.devtools_sender {
4798 let state = NavigationState::Start(new_pipeline.url.clone());
4799 let _ = chan.send(DevtoolsControlMsg::FromScript(
4800 ScriptToDevtoolsControlMsg::Navigate(browsing_context_id, state),
4801 ));
4802 let page_info = DevtoolsPageInfo {
4803 title: new_pipeline.title.clone(),
4804 url: new_pipeline.url.clone(),
4805 is_top_level_global: webview_id == browsing_context_id,
4806 is_service_worker: false,
4807 };
4808 let state = NavigationState::Stop(new_pipeline.id, page_info);
4809 let _ = chan.send(DevtoolsControlMsg::FromScript(
4810 ScriptToDevtoolsControlMsg::Navigate(browsing_context_id, state),
4811 ));
4812 }
4813
4814 new_pipeline.set_has_active_document(true);
4815
4816 let webview_hidden = self
4819 .webviews
4820 .get(&webview_id)
4821 .is_none_or(|webview| webview.hidden());
4822 new_pipeline.send_throttle_messages(webview_hidden);
4823
4824 self.notify_focus_state(new_pipeline_id);
4825 }
4826
4827 self.update_activity(old_pipeline_id);
4828 self.update_activity(new_pipeline_id);
4829
4830 if let Some(parent_pipeline_id) = parent_pipeline_id {
4831 let msg = ScriptThreadMessage::UpdatePipelineId(
4832 parent_pipeline_id,
4833 browsing_context_id,
4834 webview_id,
4835 new_pipeline_id,
4836 UpdatePipelineIdReason::Traversal,
4837 );
4838 self.send_message_to_pipeline(parent_pipeline_id, msg, "Child traversed after closure");
4839 }
4840
4841 None
4842 }
4843
4844 #[servo_tracing::instrument(skip_all)]
4845 fn update_pipeline_history_state(
4846 &mut self,
4847 pipeline_id: PipelineId,
4848 history_state_id: Option<HistoryStateId>,
4849 url: ServoUrl,
4850 ) {
4851 if let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) {
4852 pipeline.history_state_id = history_state_id;
4853 pipeline.url = url.clone();
4854 }
4855 let msg = ScriptThreadMessage::UpdateHistoryState(pipeline_id, history_state_id, url);
4856 self.send_message_to_pipeline(pipeline_id, msg, "History state updated after closure");
4857 }
4858
4859 #[servo_tracing::instrument(skip_all)]
4860 fn handle_joint_session_history_length(
4861 &self,
4862 webview_id: WebViewId,
4863 response_sender: GenericSender<u32>,
4864 ) {
4865 let length = self
4866 .webviews
4867 .get(&webview_id)
4868 .map(|webview| webview.session_history.history_length())
4869 .unwrap_or(1);
4870 let _ = response_sender.send(length as u32);
4871 }
4872
4873 #[servo_tracing::instrument(skip_all)]
4874 fn handle_push_history_state_msg(
4875 &mut self,
4876 pipeline_id: PipelineId,
4877 history_state_id: HistoryStateId,
4878 url: ServoUrl,
4879 ) {
4880 let (webview_id, old_state_id, old_url) = match self.pipelines.get_mut(&pipeline_id) {
4881 Some(pipeline) => {
4882 let old_history_state_id = pipeline.history_state_id;
4883 let old_url = replace(&mut pipeline.url, url.clone());
4884 pipeline.history_state_id = Some(history_state_id);
4885 pipeline.history_states.insert(history_state_id);
4886 (pipeline.webview_id, old_history_state_id, old_url)
4887 },
4888 None => {
4889 return warn!(
4890 "{}: Push history state {} for closed pipeline",
4891 pipeline_id, history_state_id,
4892 );
4893 },
4894 };
4895
4896 let Some(webview) = self.webviews.get_mut(&webview_id) else {
4897 return warn!("Ignoring history change in non-existent WebView ({webview_id:?}).");
4898 };
4899
4900 let diff = SessionHistoryDiff::Pipeline {
4901 pipeline_reloader: NeedsToReload::No(pipeline_id),
4902 new_history_state_id: history_state_id,
4903 new_url: url,
4904 old_history_state_id: old_state_id,
4905 old_url,
4906 };
4907 webview.session_history.push_diff(diff);
4908 self.notify_history_changed(webview_id);
4909 }
4910
4911 #[servo_tracing::instrument(skip_all)]
4912 fn handle_replace_history_state_msg(
4913 &mut self,
4914 pipeline_id: PipelineId,
4915 history_state_id: HistoryStateId,
4916 url: ServoUrl,
4917 ) {
4918 let webview_id = match self.pipelines.get_mut(&pipeline_id) {
4919 Some(pipeline) => {
4920 pipeline.history_state_id = Some(history_state_id);
4921 pipeline.url = url.clone();
4922 pipeline.webview_id
4923 },
4924 None => {
4925 return warn!(
4926 "{}: Replace history state {} for closed pipeline",
4927 history_state_id, pipeline_id
4928 );
4929 },
4930 };
4931
4932 let Some(webview) = self.webviews.get_mut(&webview_id) else {
4933 return warn!("Ignoring history change in non-existent WebView ({webview_id:?}).");
4934 };
4935
4936 webview
4937 .session_history
4938 .replace_history_state(pipeline_id, history_state_id, url);
4939 self.notify_history_changed(webview_id);
4940 }
4941
4942 #[servo_tracing::instrument(skip_all)]
4943 fn handle_reload_msg(&mut self, webview_id: WebViewId) {
4944 let browsing_context_id = BrowsingContextId::from(webview_id);
4945 let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
4946 Some(browsing_context) => browsing_context.pipeline_id,
4947 None => {
4948 return warn!("{}: Got reload event after closure", browsing_context_id);
4949 },
4950 };
4951 self.send_message_to_pipeline(
4952 pipeline_id,
4953 ScriptThreadMessage::Reload(pipeline_id),
4954 "Got reload event after closure",
4955 );
4956 }
4957
4958 #[servo_tracing::instrument(skip_all)]
4960 fn handle_post_message_msg(
4961 &mut self,
4962 browsing_context_id: BrowsingContextId,
4963 source_pipeline: PipelineId,
4964 origin: Option<ImmutableOrigin>,
4965 source_origin: ImmutableOrigin,
4966 data: StructuredSerializedData,
4967 ) {
4968 let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
4969 None => {
4970 return warn!(
4971 "{}: PostMessage to closed browsing context",
4972 browsing_context_id
4973 );
4974 },
4975 Some(browsing_context) => browsing_context.pipeline_id,
4976 };
4977 let source_webview = match self.pipelines.get(&source_pipeline) {
4978 Some(pipeline) => pipeline.webview_id,
4979 None => return warn!("{}: PostMessage from closed pipeline", source_pipeline),
4980 };
4981
4982 let browsing_context_for_pipeline = |pipeline_id| {
4983 self.pipelines
4984 .get(&pipeline_id)
4985 .and_then(|pipeline| self.browsing_contexts.get(&pipeline.browsing_context_id))
4986 };
4987 let mut maybe_browsing_context = browsing_context_for_pipeline(source_pipeline);
4988 if maybe_browsing_context.is_none() {
4989 return warn!("{source_pipeline}: PostMessage from pipeline with closed parent");
4990 }
4991
4992 let mut source_with_ancestry = vec![];
4998 while let Some(browsing_context) = maybe_browsing_context {
4999 source_with_ancestry.push(browsing_context.id);
5000 maybe_browsing_context = browsing_context
5001 .parent_pipeline_id
5002 .and_then(browsing_context_for_pipeline);
5003 }
5004 let msg = ScriptThreadMessage::PostMessage {
5005 target: pipeline_id,
5006 source_webview,
5007 source_with_ancestry,
5008 target_origin: origin,
5009 source_origin,
5010 data: Box::new(data),
5011 };
5012 self.send_message_to_pipeline(pipeline_id, msg, "PostMessage to closed pipeline");
5013 }
5014
5015 #[servo_tracing::instrument(skip_all)]
5016 fn handle_focus_ancestor_browsing_contexts_for_focusing_steps(
5017 &mut self,
5018 pipeline_id: PipelineId,
5019 focused_child_browsing_context_id: Option<BrowsingContextId>,
5020 sequence: FocusSequenceNumber,
5021 ) {
5022 let browsing_context_id = match self.pipelines.get_mut(&pipeline_id) {
5023 Some(pipeline) => {
5024 pipeline.focus_sequence = sequence;
5025 pipeline.browsing_context_id
5026 },
5027 None => return warn!("{}: Focus parent after closure", pipeline_id),
5028 };
5029
5030 if self.get_activity(pipeline_id) != DocumentActivity::FullyActive {
5032 debug!(
5033 "Ignoring the focus request because pipeline {} is not \
5034 fully active",
5035 pipeline_id
5036 );
5037 return;
5038 }
5039
5040 let focused_browsing_context_id =
5044 focused_child_browsing_context_id.unwrap_or(browsing_context_id);
5045
5046 self.focus_browsing_context(Some(pipeline_id), focused_browsing_context_id);
5050 }
5051
5052 fn handle_focus_remote_browsing_context(
5053 &mut self,
5054 target: BrowsingContextId,
5055 operation: RemoteFocusOperation,
5056 ) {
5057 let Some(browsing_context) = self.browsing_contexts.get(&target) else {
5058 return warn!("{target:?} not found for focus message");
5059 };
5060 let pipeline_id = browsing_context.pipeline_id;
5061 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5062 return warn!("{pipeline_id:?} not found for focus message");
5063 };
5064 if let Err(error) = pipeline
5065 .event_loop
5066 .send(ScriptThreadMessage::FocusDocument(pipeline_id, operation))
5067 {
5068 self.handle_send_error(pipeline_id, error);
5069 }
5070 }
5071
5072 #[servo_tracing::instrument(skip_all)]
5083 fn focus_browsing_context(
5084 &mut self,
5085 initiator_pipeline_id: Option<PipelineId>,
5086 focused_browsing_context_id: BrowsingContextId,
5087 ) {
5088 let webview_id = match self.browsing_contexts.get(&focused_browsing_context_id) {
5089 Some(browsing_context) => browsing_context.webview_id,
5090 None => return warn!("Browsing context {} not found", focused_browsing_context_id),
5091 };
5092
5093 let old_focused_browsing_context_id = match self.webviews.get_mut(&webview_id) {
5095 Some(browser) => replace(
5096 &mut browser.focused_browsing_context_id,
5097 focused_browsing_context_id,
5098 ),
5099 None => {
5100 return warn!(
5101 "{}: Browsing context for focus msg does not exist",
5102 webview_id
5103 );
5104 },
5105 };
5106
5107 let mut old_focus_chain_pipelines: Vec<&Pipeline> = self
5114 .ancestor_or_self_pipelines_of_browsing_context_iter(old_focused_browsing_context_id)
5115 .collect();
5116 let mut new_focus_chain_pipelines: Vec<&Pipeline> = self
5117 .ancestor_or_self_pipelines_of_browsing_context_iter(focused_browsing_context_id)
5118 .collect();
5119
5120 debug!(
5121 "old_focus_chain_pipelines = {:?}",
5122 old_focus_chain_pipelines
5123 .iter()
5124 .map(|p| p.id.to_string())
5125 .collect::<Vec<_>>()
5126 );
5127 debug!(
5128 "new_focus_chain_pipelines = {:?}",
5129 new_focus_chain_pipelines
5130 .iter()
5131 .map(|p| p.id.to_string())
5132 .collect::<Vec<_>>()
5133 );
5134
5135 match (
5139 &old_focus_chain_pipelines[..],
5140 &new_focus_chain_pipelines[..],
5141 ) {
5142 ([.., p1], [.., p2]) if p1.id == p2.id => {},
5143 _ => {
5144 warn!("Aborting the focus operation - focus chain sanity check failed");
5145 return;
5146 },
5147 }
5148
5149 let mut first_common_pipeline_in_chain = None;
5153 while let ([.., p1], [.., p2]) = (
5154 &old_focus_chain_pipelines[..],
5155 &new_focus_chain_pipelines[..],
5156 ) {
5157 if p1.id != p2.id {
5158 break;
5159 }
5160 old_focus_chain_pipelines.pop();
5161 first_common_pipeline_in_chain = new_focus_chain_pipelines.pop();
5162 }
5163
5164 let mut send_errors = Vec::new();
5165
5166 for &pipeline in old_focus_chain_pipelines.iter() {
5169 if Some(pipeline.id) != initiator_pipeline_id {
5170 let msg = ScriptThreadMessage::UnfocusDocumentAsPartOfFocusingSteps(
5171 pipeline.id,
5172 pipeline.focus_sequence,
5173 );
5174 trace!("Sending {:?} to {}", msg, pipeline.id);
5175 if let Err(e) = pipeline.event_loop.send(msg) {
5176 send_errors.push((pipeline.id, e));
5177 }
5178 } else {
5179 trace!(
5180 "Not notifying {} - it's the initiator of this focus operation",
5181 pipeline.id
5182 );
5183 }
5184 }
5185
5186 let mut child_browsing_context_id = None;
5189 for &pipeline in new_focus_chain_pipelines.iter().rev() {
5190 if Some(pipeline.id) != initiator_pipeline_id &&
5193 let Err(error) = pipeline.event_loop.send(
5194 ScriptThreadMessage::FocusDocumentAsPartOfFocusingSteps(
5195 pipeline.id,
5196 pipeline.focus_sequence,
5197 child_browsing_context_id,
5198 ),
5199 )
5200 {
5201 send_errors.push((pipeline.id, error));
5202 }
5203 child_browsing_context_id = Some(pipeline.browsing_context_id);
5204 }
5205
5206 if let Some(pipeline) = first_common_pipeline_in_chain &&
5207 Some(pipeline.id) != initiator_pipeline_id &&
5208 let Err(error) = pipeline.event_loop.send(
5209 ScriptThreadMessage::FocusDocumentAsPartOfFocusingSteps(
5210 pipeline.id,
5211 pipeline.focus_sequence,
5212 child_browsing_context_id,
5213 ),
5214 )
5215 {
5216 send_errors.push((pipeline.id, error));
5217 }
5218
5219 for (pipeline_id, error) in send_errors {
5220 self.handle_send_error(pipeline_id, error);
5221 }
5222 }
5223
5224 #[servo_tracing::instrument(skip_all)]
5225 fn handle_remove_iframe_msg(
5226 &mut self,
5227 webview_id: WebViewId,
5228 browsing_context_id: BrowsingContextId,
5229 ) -> Vec<PipelineId> {
5230 let result = self
5231 .all_descendant_browsing_contexts_iter(browsing_context_id)
5232 .flat_map(|browsing_context| browsing_context.pipelines.iter().cloned())
5233 .collect();
5234 self.close_browsing_context(webview_id, browsing_context_id, ExitPipelineMode::Normal);
5235 result
5236 }
5237
5238 #[servo_tracing::instrument(skip_all)]
5239 fn handle_create_canvas_paint_thread_msg(
5240 &mut self,
5241 size: UntypedSize2D<u64>,
5242 response_sender: GenericSender<Option<(GenericSender<CanvasMsg>, CanvasId)>>,
5243 ) {
5244 let (canvas_data_sender, canvas_data_receiver) = unbounded();
5245 let (canvas_sender, canvas_ipc_sender) = self
5246 .canvas
5247 .get_or_init(|| self.create_canvas_paint_thread());
5248
5249 let response = if let Err(e) = canvas_sender.send(ConstellationCanvasMsg::Create {
5250 sender: canvas_data_sender,
5251 size,
5252 }) {
5253 warn!("Create canvas paint thread failed ({})", e);
5254 None
5255 } else {
5256 match canvas_data_receiver.recv() {
5257 Ok(Some(canvas_id)) => Some((canvas_ipc_sender.clone(), canvas_id)),
5258 Ok(None) => None,
5259 Err(e) => {
5260 warn!("Create canvas paint thread id response failed ({})", e);
5261 None
5262 },
5263 }
5264 };
5265 if let Err(e) = response_sender.send(response) {
5266 warn!("Create canvas paint thread response failed ({})", e);
5267 }
5268 }
5269
5270 #[servo_tracing::instrument(skip_all)]
5271 fn handle_webdriver_msg(&mut self, msg: WebDriverCommandMsg) {
5272 match msg {
5275 WebDriverCommandMsg::IsBrowsingContextOpen(browsing_context_id, response_sender) => {
5276 let is_open = self.browsing_contexts.contains_key(&browsing_context_id);
5277 let _ = response_sender.send(is_open);
5278 },
5279 WebDriverCommandMsg::FocusBrowsingContext(browsing_context_id) => {
5280 self.handle_focus_remote_browsing_context(
5281 browsing_context_id,
5282 RemoteFocusOperation::Viewport,
5283 );
5284 },
5285 WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd) => {
5287 let pipeline_id = if let Some(browsing_context) =
5288 self.browsing_contexts.get(&browsing_context_id)
5289 {
5290 browsing_context.pipeline_id
5291 } else {
5292 return warn!("{}: Browsing context is not ready", browsing_context_id);
5293 };
5294
5295 match &cmd {
5296 WebDriverScriptCommand::AddLoadStatusSender(_, sender) => {
5297 self.webdriver_load_status_sender = Some((sender.clone(), pipeline_id));
5298 },
5299 WebDriverScriptCommand::RemoveLoadStatusSender(_) => {
5300 self.webdriver_load_status_sender = None;
5301 },
5302 _ => {},
5303 };
5304
5305 let control_msg = ScriptThreadMessage::WebDriverScriptCommand(pipeline_id, cmd);
5306 self.send_message_to_pipeline(
5307 pipeline_id,
5308 control_msg,
5309 "ScriptCommand after closure",
5310 );
5311 },
5312 WebDriverCommandMsg::CloseWebView(..) |
5313 WebDriverCommandMsg::NewWindow(..) |
5314 WebDriverCommandMsg::SelectWebViewForInteraction(..) |
5315 WebDriverCommandMsg::IsWebViewOpen(..) |
5316 WebDriverCommandMsg::GetWindowRect(..) |
5317 WebDriverCommandMsg::GetViewportSize(..) |
5318 WebDriverCommandMsg::SetWindowRect(..) |
5319 WebDriverCommandMsg::MaximizeWebView(..) |
5320 WebDriverCommandMsg::LoadUrl(..) |
5321 WebDriverCommandMsg::Refresh(..) |
5322 WebDriverCommandMsg::InputEvent(..) |
5323 WebDriverCommandMsg::TakeScreenshot(..) => {
5324 unreachable!("This command should be send directly to the embedder.");
5325 },
5326 _ => {
5327 warn!("Unhandled WebDriver command: {:?}", msg);
5328 },
5329 }
5330 }
5331
5332 #[servo_tracing::instrument(skip_all)]
5333 fn set_webview_hidden(&mut self, webview_id: WebViewId, hidden: bool) {
5334 if self
5335 .webviews
5336 .get_mut(&webview_id)
5337 .is_none_or(|webview| !webview.set_hidden(hidden))
5338 {
5339 return;
5340 }
5341
5342 for pipeline in self.pipelines.values() {
5343 if pipeline.webview_id == webview_id {
5344 pipeline.send_throttle_messages(hidden);
5345 }
5346 }
5347 }
5348
5349 #[servo_tracing::instrument(skip_all)]
5350 fn notify_history_changed(&self, webview_id: WebViewId) {
5351 let session_history = match self.webviews.get(&webview_id) {
5358 Some(webview) => &webview.session_history,
5359 None => {
5360 return warn!(
5361 "{}: Session history does not exist for browsing context",
5362 webview_id
5363 );
5364 },
5365 };
5366
5367 let browsing_context_id = BrowsingContextId::from(webview_id);
5368 let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) else {
5369 return warn!("notify_history_changed error after top-level browsing context closed.");
5370 };
5371
5372 let current_url = match self.pipelines.get(&browsing_context.pipeline_id) {
5373 Some(pipeline) => pipeline.url.clone(),
5374 None => {
5375 return warn!("{}: Refresh after closure", browsing_context.pipeline_id);
5376 },
5377 };
5378
5379 let resolve_url_future =
5382 |previous_url: &mut ServoUrl, diff: &SessionHistoryDiff| match *diff {
5383 SessionHistoryDiff::BrowsingContext {
5384 browsing_context_id,
5385 ref new_reloader,
5386 ..
5387 } => {
5388 if browsing_context_id == webview_id {
5389 let url = match *new_reloader {
5390 NeedsToReload::No(pipeline_id) => {
5391 match self.pipelines.get(&pipeline_id) {
5392 Some(pipeline) => pipeline.url.clone(),
5393 None => previous_url.clone(),
5394 }
5395 },
5396 NeedsToReload::Yes(_, ref load_data) => load_data.url.clone(),
5397 };
5398 *previous_url = url.clone();
5399 Some(url)
5400 } else {
5401 Some(previous_url.clone())
5402 }
5403 },
5404 SessionHistoryDiff::Hash { ref new_url, .. } => {
5405 *previous_url = new_url.clone();
5406 Some(new_url.clone())
5407 },
5408 _ => Some(previous_url.clone()),
5409 };
5410
5411 let resolve_url_past = |previous_url: &mut ServoUrl, diff: &SessionHistoryDiff| match *diff
5412 {
5413 SessionHistoryDiff::BrowsingContext {
5414 browsing_context_id,
5415 ref old_reloader,
5416 ..
5417 } => {
5418 if browsing_context_id == webview_id {
5419 let url = match *old_reloader {
5420 NeedsToReload::No(pipeline_id) => match self.pipelines.get(&pipeline_id) {
5421 Some(pipeline) => pipeline.url.clone(),
5422 None => previous_url.clone(),
5423 },
5424 NeedsToReload::Yes(_, ref load_data) => load_data.url.clone(),
5425 };
5426 *previous_url = url.clone();
5427 Some(url)
5428 } else {
5429 Some(previous_url.clone())
5430 }
5431 },
5432 SessionHistoryDiff::Hash { ref old_url, .. } => {
5433 *previous_url = old_url.clone();
5434 Some(old_url.clone())
5435 },
5436 _ => Some(previous_url.clone()),
5437 };
5438
5439 let mut entries: Vec<ServoUrl> = session_history
5440 .past
5441 .iter()
5442 .rev()
5443 .scan(current_url.clone(), &resolve_url_past)
5444 .collect();
5445
5446 entries.reverse();
5447
5448 let current_index = entries.len();
5449
5450 entries.push(current_url.clone());
5451
5452 entries.extend(
5453 session_history
5454 .future
5455 .iter()
5456 .rev()
5457 .scan(current_url, &resolve_url_future),
5458 );
5459 self.constellation_to_embedder_proxy
5460 .send(ConstellationToEmbedderMsg::HistoryChanged(
5461 webview_id,
5462 entries,
5463 current_index,
5464 ));
5465 }
5466
5467 #[servo_tracing::instrument(skip_all)]
5468 fn change_session_history(&mut self, change: SessionHistoryChange) {
5469 debug!(
5470 "{}: Setting to {}",
5471 change.browsing_context_id, change.new_pipeline_id
5472 );
5473
5474 if self.focused_browsing_context_is_descendant_of(&change) &&
5478 let Some(webview) = self.webviews.get_mut(&change.webview_id)
5479 {
5480 webview.focused_browsing_context_id = change.browsing_context_id;
5481 }
5482
5483 let (old_pipeline_id, webview_id) =
5484 match self.browsing_contexts.get_mut(&change.browsing_context_id) {
5485 Some(browsing_context) => {
5486 debug!("Adding pipeline to existing browsing context.");
5487 let old_pipeline_id = browsing_context.pipeline_id;
5488 browsing_context.pipelines.insert(change.new_pipeline_id);
5489 browsing_context.update_current_entry(change.new_pipeline_id);
5490 (Some(old_pipeline_id), Some(browsing_context.webview_id))
5491 },
5492 None => {
5493 debug!("Adding pipeline to new browsing context.");
5494 (None, None)
5495 },
5496 };
5497
5498 if let Some(old_pipeline_id) = old_pipeline_id {
5499 self.unload_document(old_pipeline_id);
5500 }
5501
5502 let Some(webview) = self.webviews.get_mut(&change.webview_id) else {
5503 return warn!("Ignoring history change in non-existent WebView ({webview_id:?}).");
5504 };
5505
5506 match old_pipeline_id {
5507 None => {
5508 let Some(new_context_info) = change.new_browsing_context_info else {
5509 return warn!(
5510 "{}: No NewBrowsingContextInfo for browsing context",
5511 change.browsing_context_id,
5512 );
5513 };
5514 self.new_browsing_context(
5515 change.browsing_context_id,
5516 change.webview_id,
5517 change.new_pipeline_id,
5518 new_context_info.parent_pipeline_id,
5519 change.viewport_details,
5520 new_context_info.is_private,
5521 new_context_info.inherited_secure_context,
5522 );
5523 self.update_activity(change.new_pipeline_id);
5524 },
5525 Some(old_pipeline_id) => {
5526 let (pipelines_to_close, states_to_close) = if let Some(replace_reloader) =
5528 change.replace
5529 {
5530 webview.session_history.replace_reloader(
5531 replace_reloader.clone(),
5532 NeedsToReload::No(change.new_pipeline_id),
5533 );
5534
5535 match replace_reloader {
5536 NeedsToReload::No(pipeline_id) => (Some(vec![pipeline_id]), None),
5537 NeedsToReload::Yes(..) => (None, None),
5538 }
5539 } else {
5540 let diff = SessionHistoryDiff::BrowsingContext {
5541 browsing_context_id: change.browsing_context_id,
5542 new_reloader: NeedsToReload::No(change.new_pipeline_id),
5543 old_reloader: NeedsToReload::No(old_pipeline_id),
5544 };
5545
5546 let mut pipelines_to_close = vec![];
5547 let mut states_to_close = FxHashMap::default();
5548
5549 let diffs_to_close = webview.session_history.push_diff(diff);
5550 for diff in diffs_to_close {
5551 match diff {
5552 SessionHistoryDiff::BrowsingContext { new_reloader, .. } => {
5553 if let Some(pipeline_id) = new_reloader.alive_pipeline_id() {
5554 pipelines_to_close.push(pipeline_id);
5555 }
5556 },
5557 SessionHistoryDiff::Pipeline {
5558 pipeline_reloader,
5559 new_history_state_id,
5560 ..
5561 } => {
5562 if let Some(pipeline_id) = pipeline_reloader.alive_pipeline_id() {
5563 let states =
5564 states_to_close.entry(pipeline_id).or_insert(Vec::new());
5565 states.push(new_history_state_id);
5566 }
5567 },
5568 _ => {},
5569 }
5570 }
5571
5572 (Some(pipelines_to_close), Some(states_to_close))
5573 };
5574
5575 self.update_activity(old_pipeline_id);
5576 self.update_activity(change.new_pipeline_id);
5577
5578 if let Some(states_to_close) = states_to_close {
5579 for (pipeline_id, states) in states_to_close {
5580 let msg = ScriptThreadMessage::RemoveHistoryStates(pipeline_id, states);
5581 if !self.send_message_to_pipeline(
5582 pipeline_id,
5583 msg,
5584 "Removed history states after closure",
5585 ) {
5586 return;
5587 }
5588 }
5589 }
5590
5591 if let Some(pipelines_to_close) = pipelines_to_close {
5592 for pipeline_id in pipelines_to_close {
5593 self.close_pipeline(
5594 pipeline_id,
5595 DiscardBrowsingContext::No,
5596 ExitPipelineMode::Normal,
5597 );
5598 }
5599 }
5600 },
5601 }
5602
5603 if let Some(webview_id) = webview_id {
5604 self.trim_history(webview_id);
5605 }
5606
5607 self.notify_focus_state(change.new_pipeline_id);
5608
5609 self.notify_history_changed(change.webview_id);
5610 self.set_frame_tree_for_webview(change.webview_id);
5611 }
5612
5613 fn notify_focus_state(&mut self, pipeline_id: PipelineId) {
5617 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5618 return warn!("Pipeline {pipeline_id} is closed");
5619 };
5620
5621 let is_focused = match self.webviews.get(&pipeline.webview_id) {
5622 Some(webview) => webview.focused_browsing_context_id == pipeline.browsing_context_id,
5623 None => {
5624 return warn!(
5625 "Pipeline {pipeline_id}'s top-level browsing context {} is closed",
5626 pipeline.webview_id
5627 );
5628 },
5629 };
5630
5631 let msg = if is_focused {
5633 ScriptThreadMessage::FocusDocumentAsPartOfFocusingSteps(
5634 pipeline_id,
5635 pipeline.focus_sequence,
5636 None,
5637 )
5638 } else {
5639 ScriptThreadMessage::UnfocusDocumentAsPartOfFocusingSteps(
5640 pipeline_id,
5641 pipeline.focus_sequence,
5642 )
5643 };
5644 if let Err(e) = pipeline.event_loop.send(msg) {
5645 self.handle_send_error(pipeline_id, e);
5646 }
5647 }
5648
5649 #[servo_tracing::instrument(skip_all)]
5650 fn focused_browsing_context_is_descendant_of(&self, change: &SessionHistoryChange) -> bool {
5651 let focused_browsing_context_id = self
5652 .webviews
5653 .get(&change.webview_id)
5654 .map(|webview| webview.focused_browsing_context_id);
5655 focused_browsing_context_id.is_some_and(|focused_browsing_context_id| {
5656 focused_browsing_context_id == change.browsing_context_id ||
5657 self.fully_active_descendant_browsing_contexts_iter(change.browsing_context_id)
5658 .any(|nested_ctx| nested_ctx.id == focused_browsing_context_id)
5659 })
5660 }
5661
5662 #[servo_tracing::instrument(skip_all)]
5663 fn trim_history(&mut self, webview_id: WebViewId) {
5664 let pipelines_to_evict = {
5665 let Some(webview) = self.webviews.get_mut(&webview_id) else {
5666 return warn!("Not trimming history for non-existent WebView ({webview_id:}");
5667 };
5668 let history_length = pref!(session_history_max_length) as usize;
5669
5670 let past_trim = webview
5674 .session_history
5675 .past
5676 .iter()
5677 .rev()
5678 .map(|diff| diff.alive_old_pipeline())
5679 .skip(history_length)
5680 .flatten();
5681
5682 let future_trim = webview
5685 .session_history
5686 .future
5687 .iter()
5688 .rev()
5689 .map(|diff| diff.alive_new_pipeline())
5690 .skip(history_length)
5691 .flatten();
5692
5693 past_trim.chain(future_trim).collect::<Vec<_>>()
5694 };
5695
5696 let mut dead_pipelines = vec![];
5697 for evicted_id in pipelines_to_evict {
5698 let Some(load_data) = self.refresh_load_data(evicted_id) else {
5699 continue;
5700 };
5701
5702 dead_pipelines.push((evicted_id, NeedsToReload::Yes(evicted_id, load_data)));
5703 self.close_pipeline(
5704 evicted_id,
5705 DiscardBrowsingContext::No,
5706 ExitPipelineMode::Normal,
5707 );
5708 }
5709
5710 if let Some(webview) = self.webviews.get_mut(&webview_id) {
5711 for (alive_id, dead) in dead_pipelines {
5712 webview
5713 .session_history
5714 .replace_reloader(NeedsToReload::No(alive_id), dead);
5715 }
5716 };
5717 }
5718
5719 #[servo_tracing::instrument(skip_all)]
5720 fn handle_activate_document_msg(&mut self, webview_id: WebViewId, pipeline_id: PipelineId) {
5721 debug!("{}: Document ready to activate", pipeline_id);
5722
5723 let Some(change) = self
5726 .webviews
5727 .get_mut(&webview_id)
5728 .and_then(|webview| webview.remove_pending_change_for_pipeline(pipeline_id))
5729 else {
5730 return;
5731 };
5732
5733 let parent_pipeline_id = match change.new_browsing_context_info {
5735 Some(ref info) => info.parent_pipeline_id,
5737 None => match self.browsing_contexts.get(&change.browsing_context_id) {
5739 Some(ctx) => ctx.parent_pipeline_id,
5740 None => {
5741 return warn!(
5742 "{}: Activated document after closure of {}",
5743 change.new_pipeline_id, change.browsing_context_id,
5744 );
5745 },
5746 },
5747 };
5748 if let Some(parent_pipeline_id) = parent_pipeline_id &&
5749 let Some(parent_pipeline) = self.pipelines.get(&parent_pipeline_id)
5750 {
5751 let msg = ScriptThreadMessage::UpdatePipelineId(
5752 parent_pipeline_id,
5753 change.browsing_context_id,
5754 webview_id,
5755 pipeline_id,
5756 UpdatePipelineIdReason::Navigation,
5757 );
5758 let _ = parent_pipeline.event_loop.send(msg);
5759 }
5760
5761 self.change_session_history(change);
5762
5763 if let Some(webview) = self.webviews.get_mut(&webview_id) {
5764 webview.send_screenshot_readiness_requests_to_pipelines(
5765 &self.browsing_contexts,
5766 &self.pipelines,
5767 );
5768 }
5769 }
5770
5771 #[servo_tracing::instrument(skip_all)]
5773 fn handle_change_viewport_details_msg(
5774 &mut self,
5775 webview_id: WebViewId,
5776 new_viewport_details: ViewportDetails,
5777 size_type: WindowSizeType,
5778 ) {
5779 debug!(
5780 "handle_change_viewport_details_msg: {:?}",
5781 new_viewport_details
5782 );
5783
5784 let browsing_context_id = BrowsingContextId::from(webview_id);
5785 self.resize_browsing_context(
5786 webview_id,
5787 new_viewport_details,
5788 size_type,
5789 browsing_context_id,
5790 );
5791 }
5792
5793 #[servo_tracing::instrument(skip_all)]
5795 fn handle_exit_fullscreen_msg(&mut self, webview_id: WebViewId) {
5796 let browsing_context_id = BrowsingContextId::from(webview_id);
5797 self.switch_fullscreen_mode(browsing_context_id);
5798 }
5799
5800 #[servo_tracing::instrument(skip_all)]
5802 fn get_activity(&self, pipeline_id: PipelineId) -> DocumentActivity {
5803 let mut ancestor_id = pipeline_id;
5804 loop {
5805 if let Some(ancestor) = self.pipelines.get(&ancestor_id) &&
5806 let Some(browsing_context) =
5807 self.browsing_contexts.get(&ancestor.browsing_context_id) &&
5808 browsing_context.pipeline_id == ancestor_id
5809 {
5810 if let Some(parent_pipeline_id) = browsing_context.parent_pipeline_id {
5811 ancestor_id = parent_pipeline_id;
5812 continue;
5813 } else {
5814 return DocumentActivity::FullyActive;
5815 }
5816 }
5817 if pipeline_id == ancestor_id {
5818 return DocumentActivity::Inactive;
5819 } else {
5820 return DocumentActivity::Active;
5821 }
5822 }
5823 }
5824
5825 #[servo_tracing::instrument(skip_all)]
5827 fn set_activity(&self, pipeline_id: PipelineId, activity: DocumentActivity) {
5828 debug!("{}: Setting activity to {:?}", pipeline_id, activity);
5829 if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
5830 pipeline.set_activity(activity);
5831 let child_activity = if activity == DocumentActivity::Inactive {
5832 DocumentActivity::Active
5833 } else {
5834 activity
5835 };
5836 for child_id in &pipeline.children {
5837 if let Some(child) = self.browsing_contexts.get(child_id) {
5838 self.set_activity(child.pipeline_id, child_activity);
5839 }
5840 }
5841 }
5842 }
5843
5844 #[servo_tracing::instrument(skip_all)]
5846 fn update_activity(&self, pipeline_id: PipelineId) {
5847 self.set_activity(pipeline_id, self.get_activity(pipeline_id));
5848 }
5849
5850 #[servo_tracing::instrument(skip_all)]
5853 fn resize_browsing_context(
5854 &mut self,
5855 webview_id: WebViewId,
5856 new_viewport_details: ViewportDetails,
5857 size_type: WindowSizeType,
5858 browsing_context_id: BrowsingContextId,
5859 ) {
5860 let Some(webview) = self.webviews.get_mut(&webview_id) else {
5861 warn!("Resizing browsing context for unkown WebView: {webview_id:?}");
5862 return;
5863 };
5864
5865 if let Some(browsing_context) = self.browsing_contexts.get_mut(&browsing_context_id) {
5866 browsing_context.viewport_details = new_viewport_details;
5867 let pipeline_id = browsing_context.pipeline_id;
5869 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5870 return warn!("{}: Resized after closing", pipeline_id);
5871 };
5872 let _ = pipeline.event_loop.send(ScriptThreadMessage::Resize(
5873 pipeline.id,
5874 new_viewport_details,
5875 size_type,
5876 ));
5877 let pipeline_ids = browsing_context
5878 .pipelines
5879 .iter()
5880 .filter(|pipeline_id| **pipeline_id != pipeline.id);
5881 for id in pipeline_ids {
5882 if let Some(pipeline) = self.pipelines.get(id) {
5883 let _ = pipeline
5884 .event_loop
5885 .send(ScriptThreadMessage::ResizeInactive(
5886 pipeline.id,
5887 new_viewport_details,
5888 ));
5889 }
5890 }
5891 } else {
5892 webview.add_viewport_details(browsing_context_id, new_viewport_details);
5893 }
5894
5895 for change in &webview.pending_changes {
5897 let pipeline_id = change.new_pipeline_id;
5898 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5899 warn!("Pending pipeline is closed: {pipeline_id}");
5900 continue;
5901 };
5902 if pipeline.browsing_context_id == browsing_context_id {
5903 let _ = pipeline.event_loop.send(ScriptThreadMessage::Resize(
5904 pipeline.id,
5905 new_viewport_details,
5906 size_type,
5907 ));
5908 }
5909 }
5910 }
5911
5912 #[servo_tracing::instrument(skip_all)]
5914 fn handle_theme_change(&mut self, webview_id: WebViewId, theme: Theme) {
5915 let Some(webview) = self.webviews.get_mut(&webview_id) else {
5916 warn!("Received theme change request for unknown WebViewId: {webview_id:?}");
5917 return;
5918 };
5919 if !webview.set_theme(theme) {
5920 return;
5921 }
5922 let state = webview.state();
5923 for event_loop in self.event_loops() {
5924 if let Err(error) =
5925 event_loop.send(ScriptThreadMessage::UpdateWebViewState(state.clone()))
5926 {
5927 warn!(
5928 "Sending to closed event loop ({:?}): {error}",
5929 event_loop.id()
5930 );
5931 }
5932 }
5933 }
5934
5935 #[servo_tracing::instrument(skip_all)]
5937 fn switch_fullscreen_mode(&mut self, browsing_context_id: BrowsingContextId) {
5938 if let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) {
5939 let pipeline_id = browsing_context.pipeline_id;
5940 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5941 return warn!("{pipeline_id}: Switched from fullscreen mode after closing",);
5942 };
5943 let _ = pipeline
5944 .event_loop
5945 .send(ScriptThreadMessage::ExitFullScreen(pipeline.id));
5946 }
5947 }
5948
5949 #[servo_tracing::instrument(skip_all)]
5951 fn close_browsing_context(
5952 &mut self,
5953 webview_id: WebViewId,
5954 browsing_context_id: BrowsingContextId,
5955 exit_mode: ExitPipelineMode,
5956 ) -> Option<BrowsingContext> {
5957 debug!("{}: Closing", browsing_context_id);
5958
5959 self.close_browsing_context_children(
5960 webview_id,
5961 browsing_context_id,
5962 DiscardBrowsingContext::Yes,
5963 exit_mode,
5964 );
5965
5966 let Some(webview) = self.webviews.get_mut(&webview_id) else {
5967 warn!("Closing BrowsingContext in unknown WebView: {webview_id:?}");
5968 return self.browsing_contexts.remove(&browsing_context_id);
5969 };
5970
5971 webview.close_browsing_context(browsing_context_id);
5972
5973 let Some(browsing_context) = self.browsing_contexts.remove(&browsing_context_id) else {
5974 warn!("fn close_browsing_context: {browsing_context_id}: Closing twice");
5975 return None;
5976 };
5977
5978 if let Some(parent_pipeline_id) = browsing_context.parent_pipeline_id {
5979 match self.pipelines.get_mut(&parent_pipeline_id) {
5980 None => {
5981 warn!("{parent_pipeline_id}: Child closed after parent");
5982 },
5983 Some(parent_pipeline) => {
5984 parent_pipeline.remove_child(browsing_context_id);
5985
5986 if webview.focused_browsing_context_id == browsing_context_id {
5988 trace!(
5989 "About-to-be-closed browsing context {} is currently focused, so \
5990 focusing its parent {}",
5991 browsing_context_id, parent_pipeline.browsing_context_id
5992 );
5993 webview.focused_browsing_context_id = parent_pipeline.browsing_context_id;
5994 }
5995 },
5996 };
5997 }
5998
5999 debug!("{}: Closed", browsing_context_id);
6000 Some(browsing_context)
6001 }
6002
6003 #[servo_tracing::instrument(skip_all)]
6005 fn close_browsing_context_children(
6006 &mut self,
6007 webview_id: WebViewId,
6008 browsing_context_id: BrowsingContextId,
6009 dbc: DiscardBrowsingContext,
6010 exit_mode: ExitPipelineMode,
6011 ) {
6012 debug!("{}: Closing browsing context children", browsing_context_id);
6013
6014 let mut pipelines_to_close: Vec<_> = {
6019 let Some(webview) = self.webviews.get(&webview_id) else {
6020 return warn!(
6021 "Couldnot find WebView for closing browsing context id: {webview_id:?}"
6022 );
6023 };
6024 webview
6025 .pending_changes
6026 .iter()
6027 .filter(|change| change.browsing_context_id == browsing_context_id)
6028 .map(|change| change.new_pipeline_id)
6029 .collect()
6030 };
6031
6032 if let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) {
6033 pipelines_to_close.extend(&browsing_context.pipelines)
6034 }
6035
6036 for pipeline_id in pipelines_to_close {
6037 self.close_pipeline(pipeline_id, dbc, exit_mode);
6038 }
6039
6040 debug!("{}: Closed browsing context children", browsing_context_id);
6041 }
6042
6043 fn refresh_load_data(&self, pipeline_id: PipelineId) -> Option<LoadData> {
6046 self.pipelines.get(&pipeline_id).map(|pipeline| {
6047 let mut load_data = pipeline.load_data.clone();
6048 load_data.url = pipeline.url.clone();
6049 load_data
6050 })
6051 }
6052
6053 #[servo_tracing::instrument(skip_all)]
6055 fn handle_discard_document(&mut self, webview_id: WebViewId, pipeline_id: PipelineId) {
6056 let Some(load_data) = self.refresh_load_data(pipeline_id) else {
6057 return warn!("{}: Discarding closed pipeline", pipeline_id);
6058 };
6059 match self.webviews.get_mut(&webview_id) {
6060 Some(webview) => {
6061 webview.session_history.replace_reloader(
6062 NeedsToReload::No(pipeline_id),
6063 NeedsToReload::Yes(pipeline_id, load_data),
6064 );
6065 },
6066 None => {
6067 return warn!("{pipeline_id}: Discarding after closure of {webview_id}",);
6068 },
6069 };
6070 self.close_pipeline(
6071 pipeline_id,
6072 DiscardBrowsingContext::No,
6073 ExitPipelineMode::Normal,
6074 );
6075 }
6076
6077 #[servo_tracing::instrument(skip_all)]
6079 fn unload_document(&mut self, pipeline_id: PipelineId) {
6080 if let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) {
6081 pipeline.set_has_active_document(false);
6082 let _ = pipeline
6083 .event_loop
6084 .send(ScriptThreadMessage::UnloadDocument(pipeline_id));
6085 }
6086 }
6087
6088 #[servo_tracing::instrument(skip_all)]
6090 fn close_pipeline(
6091 &mut self,
6092 pipeline_id: PipelineId,
6093 dbc: DiscardBrowsingContext,
6094 exit_mode: ExitPipelineMode,
6095 ) {
6096 debug!("{}: Closing", pipeline_id);
6097
6098 let Some((webview_id, browsing_context_id)) = self
6100 .pipelines
6101 .get(&pipeline_id)
6102 .map(|pipeline| (pipeline.webview_id, pipeline.browsing_context_id))
6103 else {
6104 return warn!("Tried closing unknown pipeline: {pipeline_id:?}");
6105 };
6106
6107 if let Some(browsing_context) = self.browsing_contexts.get_mut(&browsing_context_id) {
6108 browsing_context.pipelines.remove(&pipeline_id);
6109 }
6110
6111 let browsing_contexts_to_close = {
6116 let mut browsing_contexts_to_close = vec![];
6117
6118 if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
6119 browsing_contexts_to_close.extend_from_slice(&pipeline.children);
6120 }
6121
6122 browsing_contexts_to_close
6123 };
6124
6125 for child_browsing_context in &browsing_contexts_to_close {
6127 self.close_browsing_context(webview_id, *child_browsing_context, exit_mode);
6128 }
6129
6130 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
6133 return warn!("fn close_pipeline: {pipeline_id}: Closing twice");
6134 };
6135
6136 if !pipeline.send_exit_message_to_script(dbc) {
6138 self.handle_pipeline_exited(pipeline_id, PipelineExitSource::all());
6143 }
6144
6145 if let Some(webview) = self.webviews.get_mut(&webview_id) {
6147 webview.remove_pending_change_for_pipeline(pipeline_id);
6148 webview.send_screenshot_readiness_requests_to_pipelines(
6149 &self.browsing_contexts,
6150 &self.pipelines,
6151 );
6152 webview.handle_screenshot_readiness_response(
6153 pipeline_id,
6154 ScreenshotReadinessResponse::NoLongerActive,
6155 &self.paint_proxy,
6156 );
6157 }
6158
6159 debug!("{}: Closed", pipeline_id);
6160 }
6161
6162 fn maybe_close_random_pipeline(&mut self) {
6164 match self.random_pipeline_closure {
6165 Some((ref mut rng, probability)) => {
6166 if probability <= rng.random::<f32>() {
6167 return;
6168 }
6169 },
6170 _ => return,
6171 };
6172 let mut pipeline_ids: Vec<&PipelineId> = self.pipelines.keys().collect();
6174 pipeline_ids.sort_unstable();
6175 if let Some((ref mut rng, probability)) = self.random_pipeline_closure &&
6176 let Some(pipeline_id) = pipeline_ids.choose(rng) &&
6177 let Some(pipeline) = self.pipelines.get(pipeline_id)
6178 {
6179 if self
6180 .webviews
6181 .values()
6182 .any(|webview| webview.pipeline_is_pending(pipeline.id)) &&
6183 probability <= rng.random::<f32>()
6184 {
6185 info!("{}: Not closing pending pipeline", pipeline_id);
6190 } else {
6191 warn!("{}: Randomly closing pipeline", pipeline_id);
6194 pipeline.send_exit_message_to_script(DiscardBrowsingContext::No);
6195 }
6196 }
6197 }
6198
6199 #[servo_tracing::instrument(skip_all)]
6201 fn browsing_context_to_sendable(
6202 &self,
6203 browsing_context_id: BrowsingContextId,
6204 ) -> Option<SendableFrameTree> {
6205 self.browsing_contexts
6206 .get(&browsing_context_id)
6207 .and_then(|browsing_context| {
6208 self.pipelines
6209 .get(&browsing_context.pipeline_id)
6210 .map(|pipeline| {
6211 let mut frame_tree = SendableFrameTree {
6212 pipeline: pipeline.to_sendable(),
6213 children: vec![],
6214 };
6215
6216 for child_browsing_context_id in &pipeline.children {
6217 if let Some(child) =
6218 self.browsing_context_to_sendable(*child_browsing_context_id)
6219 {
6220 frame_tree.children.push(child);
6221 }
6222 }
6223
6224 frame_tree
6225 })
6226 })
6227 }
6228
6229 #[servo_tracing::instrument(skip_all)]
6231 fn set_frame_tree_for_webview(&mut self, webview_id: WebViewId) {
6232 let browsing_context_id = BrowsingContextId::from(webview_id);
6236 let Some(frame_tree) = self.browsing_context_to_sendable(browsing_context_id) else {
6237 return;
6238 };
6239
6240 let new_pipeline_id = frame_tree.pipeline.id;
6241
6242 debug!("{}: Sending frame tree", browsing_context_id);
6243 self.paint_proxy
6244 .send(PaintMessage::SetFrameTreeForWebView(webview_id, frame_tree));
6245
6246 let Some(webview) = self.webviews.get_mut(&webview_id) else {
6247 return;
6248 };
6249 if webview.active_top_level_pipeline_id == Some(new_pipeline_id) {
6250 return;
6251 }
6252
6253 let old_pipeline_id = webview.active_top_level_pipeline_id;
6254 let old_epoch = webview.active_top_level_pipeline_epoch;
6255 let new_epoch = old_epoch.next();
6256
6257 let accessibility_active = webview.accessibility_active;
6258
6259 webview.active_top_level_pipeline_id = Some(new_pipeline_id);
6260 webview.active_top_level_pipeline_epoch = new_epoch;
6261
6262 if let Some(old_pipeline_id) = old_pipeline_id {
6267 self.send_message_to_pipeline(
6268 old_pipeline_id,
6269 ScriptThreadMessage::SetAccessibilityActive(old_pipeline_id, false, old_epoch),
6270 "Set accessibility active after closure",
6271 );
6272 }
6273
6274 self.send_message_to_pipeline(
6277 new_pipeline_id,
6278 ScriptThreadMessage::SetAccessibilityActive(
6279 new_pipeline_id,
6280 accessibility_active,
6281 new_epoch,
6282 ),
6283 "Set accessibility active after closure",
6284 );
6285 }
6286
6287 #[servo_tracing::instrument(skip_all)]
6288 fn handle_media_session_action_msg(&mut self, action: MediaSessionActionType) {
6289 if let Some(media_session_pipeline_id) = self.active_media_session {
6290 self.send_message_to_pipeline(
6291 media_session_pipeline_id,
6292 ScriptThreadMessage::MediaSessionAction(media_session_pipeline_id, action),
6293 "Got media session action request after closure",
6294 );
6295 } else {
6296 error!("Got a media session action but no active media session is registered");
6297 }
6298 }
6299
6300 #[servo_tracing::instrument(skip_all)]
6301 fn handle_set_scroll_states(&self, pipeline_id: PipelineId, scroll_states: ScrollStateUpdate) {
6302 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
6303 warn!("Discarding scroll offset update for unknown pipeline");
6304 return;
6305 };
6306 if let Err(error) = pipeline
6307 .event_loop
6308 .send(ScriptThreadMessage::SetScrollStates(
6309 pipeline_id,
6310 scroll_states,
6311 ))
6312 {
6313 warn!("Could not send scroll offsets to pipeline: {pipeline_id:?}: {error:?}");
6314 }
6315 }
6316
6317 #[servo_tracing::instrument(skip_all)]
6318 fn handle_paint_metric(&mut self, pipeline_id: PipelineId, event: PaintMetricEvent) {
6319 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
6320 warn!("Discarding paint metric event for unknown pipeline");
6321 return;
6322 };
6323 if let Err(error) = pipeline
6324 .event_loop
6325 .send(ScriptThreadMessage::PaintMetric(pipeline_id, event))
6326 {
6327 warn!("Could not send paint metric event to pipeline: {pipeline_id:?}: {error:?}");
6328 }
6329 }
6330
6331 fn create_canvas_paint_thread(
6332 &self,
6333 ) -> (Sender<ConstellationCanvasMsg>, GenericSender<CanvasMsg>) {
6334 CanvasPaintThread::start(
6335 self.paint_proxy.cross_process_paint_api.clone(),
6336 self.mem_profiler_chan.clone(),
6337 )
6338 }
6339
6340 fn handle_embedder_control_response(
6341 &self,
6342 id: EmbedderControlId,
6343 response: EmbedderControlResponse,
6344 ) {
6345 let pipeline_id = id.pipeline_id;
6346 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
6347 warn!("Not sending embedder control response for unknown pipeline {pipeline_id:?}");
6348 return;
6349 };
6350
6351 if let Err(error) = pipeline
6352 .event_loop
6353 .send(ScriptThreadMessage::EmbedderControlResponse(id, response))
6354 {
6355 warn!(
6356 "Could not send embedder control response to pipeline {pipeline_id:?}: {error:?}"
6357 );
6358 }
6359 }
6360
6361 fn handle_update_pinch_zoom_infos(
6362 &self,
6363 pipeline_id: PipelineId,
6364 pinch_zoom_infos: PinchZoomInfos,
6365 ) {
6366 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
6367 warn!("Discarding pinch zoom update for unknown pipeline");
6368 return;
6369 };
6370 if let Err(error) = pipeline
6371 .event_loop
6372 .send(ScriptThreadMessage::UpdatePinchZoomInfos(
6373 pipeline_id,
6374 pinch_zoom_infos,
6375 ))
6376 {
6377 warn!("Could not send pinch zoom update to pipeline: {pipeline_id:?}: {error:?}");
6378 }
6379 }
6380
6381 #[cfg(feature = "devtools")]
6382 pub(crate) fn script_to_devtools_callback(
6383 &self,
6384 ) -> Option<GenericCallback<ScriptToDevtoolsControlMsg>> {
6385 self.script_to_devtools_callback
6386 .get_or_init(|| {
6387 self.devtools_sender.as_ref().and_then(|devtools_sender| {
6388 let devtools_sender = devtools_sender.clone();
6389 let callback = GenericCallback::new(move |message| match message {
6390 Err(error) => {
6391 error!("Cast to ScriptToDevtoolsControlMsg failed ({error}).")
6392 },
6393 Ok(message) => {
6394 if let Err(error) =
6395 devtools_sender.send(DevtoolsControlMsg::FromScript(message))
6396 {
6397 warn!("Sending to devtools failed ({error:?})")
6398 }
6399 },
6400 });
6401 match callback {
6402 Ok(callback) => Some(callback),
6403 Err(error) => {
6404 error!("Could not create Devtools communication channel: {error}");
6405 None
6406 },
6407 }
6408 })
6409 })
6410 .clone()
6411 }
6412
6413 fn handle_set_network_online_state(&mut self, online: bool) {
6414 let previously_online = std::mem::replace(&mut self.online, online);
6415 if previously_online == online {
6416 return;
6417 }
6418
6419 for event_loop in self.event_loops() {
6420 if let Err(error) = event_loop.send(ScriptThreadMessage::SetNetworkOnlineState(online))
6421 {
6422 error!("Could not send network online state to event loop: {error}");
6423 }
6424 }
6425
6426 for service_worker_manager in self.sw_managers.values() {
6427 if let Err(error) =
6428 service_worker_manager.send(ServiceWorkerMsg::SetNetworkOnlineState(online))
6429 {
6430 error!("Could not send network online state to service worker: {error}");
6431 }
6432 }
6433 }
6434}