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