1use std::borrow::ToOwned;
88use std::cell::{Cell, OnceCell, RefCell};
89use std::collections::hash_map::Entry;
90use std::collections::{HashMap, HashSet, VecDeque};
91use std::marker::PhantomData;
92use std::mem::replace;
93use std::rc::{Rc, Weak};
94use std::sync::{Arc, Mutex};
95use std::thread::JoinHandle;
96use std::{process, thread};
97
98use background_hang_monitor::HangMonitorRegister;
99use background_hang_monitor_api::{
100 BackgroundHangMonitorControlMsg, BackgroundHangMonitorRegister, HangMonitorAlert,
101};
102use base::generic_channel::{GenericSender, RoutedReceiver};
103use base::id::{
104 BrowsingContextGroupId, BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId,
105 PainterId, PipelineId, PipelineNamespace, PipelineNamespaceId, PipelineNamespaceRequest,
106 WebViewId,
107};
108use base::{Epoch, IpcSend, generic_channel};
109#[cfg(feature = "bluetooth")]
110use bluetooth_traits::BluetoothRequest;
111use canvas::canvas_paint_thread::CanvasPaintThread;
112use canvas_traits::ConstellationCanvasMsg;
113use canvas_traits::canvas::{CanvasId, CanvasMsg};
114use canvas_traits::webgl::WebGLThreads;
115use compositing_traits::{
116 CompositorMsg, CompositorProxy, PipelineExitSource, SendableFrameTree,
117 WebRenderExternalImageRegistry,
118};
119use constellation_traits::{
120 AuxiliaryWebViewCreationRequest, AuxiliaryWebViewCreationResponse, DocumentState,
121 EmbedderToConstellationMessage, IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSizeMsg, Job,
122 LoadData, LoadOrigin, LogEntry, MessagePortMsg, NavigationHistoryBehavior, PaintMetricEvent,
123 PortMessageTask, PortTransferInfo, SWManagerMsg, SWManagerSenders, ScreenshotReadinessResponse,
124 ScriptToConstellationChan, ScriptToConstellationMessage, ServiceWorkerManagerFactory,
125 ServiceWorkerMsg, StructuredSerializedData, TraversalDirection, WindowSizeType,
126};
127use content_security_policy::sandboxing_directive::SandboxingFlagSet;
128use crossbeam_channel::{Receiver, Select, Sender, unbounded};
129use devtools_traits::{
130 ChromeToDevtoolsControlMsg, DevtoolsControlMsg, DevtoolsPageInfo, NavigationState,
131 ScriptToDevtoolsControlMsg,
132};
133use embedder_traits::resources::{self, Resource};
134use embedder_traits::user_content_manager::UserContentManager;
135use embedder_traits::{
136 AnimationState, CompositorHitTestResult, EmbedderControlId, EmbedderControlResponse,
137 EmbedderMsg, EmbedderProxy, FocusSequenceNumber, InputEvent, InputEventAndId, JSValue,
138 JavaScriptEvaluationError, JavaScriptEvaluationId, KeyboardEvent, MediaSessionActionType,
139 MediaSessionEvent, MediaSessionPlaybackState, MouseButton, MouseButtonAction, MouseButtonEvent,
140 ScriptToEmbedderChan, Theme, ViewportDetails, WebDriverCommandMsg, WebDriverLoadStatus,
141 WebDriverScriptCommand,
142};
143use euclid::Size2D;
144use euclid::default::Size2D as UntypedSize2D;
145use fonts::SystemFontServiceProxy;
146use ipc_channel::Error as IpcError;
147use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
148use ipc_channel::router::ROUTER;
149use keyboard_types::{Key, KeyState, Modifiers, NamedKey};
150use layout_api::{LayoutFactory, ScriptThreadFactory};
151use log::{debug, error, info, trace, warn};
152use media::WindowGLContext;
153use net::image_cache::ImageCacheFactoryImpl;
154use net_traits::pub_domains::reg_host;
155use net_traits::request::Referrer;
156use net_traits::{
157 self, AsyncRuntime, ReferrerPolicy, ResourceThreads, exit_fetch_thread, start_fetch_thread,
158};
159use profile_traits::mem::ProfilerMsg;
160use profile_traits::{mem, time};
161use rand::rngs::SmallRng;
162use rand::seq::IndexedRandom;
163use rand::{Rng, SeedableRng};
164use rustc_hash::{FxHashMap, FxHashSet};
165use script_traits::{
166 ConstellationInputEvent, DiscardBrowsingContext, DocumentActivity, ProgressiveWebMetricType,
167 ScriptThreadMessage, UpdatePipelineIdReason,
168};
169use serde::{Deserialize, Serialize};
170use servo_config::{opts, pref};
171use servo_url::{Host, ImmutableOrigin, ServoUrl};
172use storage_traits::StorageThreads;
173use storage_traits::webstorage_thread::{StorageType, WebStorageThreadMsg};
174use style::global_style_data::StyleThreadPool;
175#[cfg(feature = "webgpu")]
176use webgpu::canvas_context::WGPUImageMap;
177#[cfg(feature = "webgpu")]
178use webgpu_traits::{WebGPU, WebGPURequest};
179use webrender_api::ExternalScrollId;
180use webrender_api::units::LayoutVector2D;
181
182use crate::broadcastchannel::BroadcastChannels;
183use crate::browsingcontext::{
184 AllBrowsingContextsIterator, BrowsingContext, FullyActiveBrowsingContextsIterator,
185 NewBrowsingContextInfo,
186};
187use crate::constellation_webview::ConstellationWebView;
188use crate::event_loop::EventLoop;
189use crate::pipeline::{InitialPipelineState, Pipeline};
190use crate::process_manager::ProcessManager;
191use crate::serviceworker::ServiceWorkerUnprivilegedContent;
192use crate::session_history::{
193 JointSessionHistory, NeedsToReload, SessionHistoryChange, SessionHistoryDiff,
194};
195use crate::webview_manager::WebViewManager;
196
197type PendingApprovalNavigations = FxHashMap<PipelineId, (LoadData, NavigationHistoryBehavior)>;
198
199#[derive(Debug)]
200enum TransferState {
202 Managed(MessagePortRouterId),
205 TransferInProgress(VecDeque<PortMessageTask>),
208 CompletionInProgress(MessagePortRouterId),
211 CompletionFailed(VecDeque<PortMessageTask>),
217 CompletionRequested(MessagePortRouterId, VecDeque<PortMessageTask>),
220}
221
222#[derive(Debug)]
223struct MessagePortInfo {
225 state: TransferState,
227
228 entangled_with: Option<MessagePortId>,
230}
231
232#[cfg(feature = "webgpu")]
233struct WebRenderWGPU {
235 webrender_external_images: Arc<Mutex<WebRenderExternalImageRegistry>>,
237
238 wgpu_image_map: WGPUImageMap,
240}
241
242#[derive(Clone, Default)]
246struct BrowsingContextGroup {
247 top_level_browsing_context_set: FxHashSet<WebViewId>,
249
250 event_loops: HashMap<Host, Weak<EventLoop>>,
258
259 #[cfg(feature = "webgpu")]
261 webgpus: HashMap<Host, WebGPU>,
262}
263
264pub struct Constellation<STF, SWF> {
278 namespace_receiver: RoutedReceiver<PipelineNamespaceRequest>,
282 namespace_ipc_sender: GenericSender<PipelineNamespaceRequest>,
283
284 script_sender: GenericSender<(WebViewId, PipelineId, ScriptToConstellationMessage)>,
287
288 script_receiver:
291 Receiver<Result<(WebViewId, PipelineId, ScriptToConstellationMessage), IpcError>>,
292
293 background_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
296
297 background_monitor_register_join_handle: Option<JoinHandle<()>>,
299
300 background_monitor_control_senders: Vec<GenericSender<BackgroundHangMonitorControlMsg>>,
304
305 background_hang_monitor_sender: GenericSender<HangMonitorAlert>,
308
309 background_hang_monitor_receiver: RoutedReceiver<HangMonitorAlert>,
312
313 layout_factory: Arc<dyn LayoutFactory>,
317
318 embedder_to_constellation_receiver: Receiver<EmbedderToConstellationMessage>,
320
321 embedder_proxy: EmbedderProxy,
323
324 compositor_proxy: CompositorProxy,
327
328 webviews: WebViewManager<ConstellationWebView>,
330
331 public_resource_threads: ResourceThreads,
335
336 private_resource_threads: ResourceThreads,
341
342 public_storage_threads: StorageThreads,
346
347 private_storage_threads: StorageThreads,
352
353 system_font_service: Arc<SystemFontServiceProxy>,
356
357 devtools_sender: Option<Sender<DevtoolsControlMsg>>,
360
361 #[cfg(feature = "bluetooth")]
364 bluetooth_ipc_sender: IpcSender<BluetoothRequest>,
365
366 sw_managers: HashMap<ImmutableOrigin, GenericSender<ServiceWorkerMsg>>,
368
369 swmanager_ipc_sender: GenericSender<SWManagerMsg>,
373
374 swmanager_receiver: RoutedReceiver<SWManagerMsg>,
378
379 time_profiler_chan: time::ProfilerChan,
382
383 mem_profiler_chan: mem::ProfilerChan,
386
387 #[cfg(feature = "webgpu")]
389 webrender_wgpu: WebRenderWGPU,
390
391 message_ports: FxHashMap<MessagePortId, MessagePortInfo>,
393
394 message_port_routers: FxHashMap<MessagePortRouterId, IpcSender<MessagePortMsg>>,
396
397 broadcast_channels: BroadcastChannels,
399
400 pipelines: FxHashMap<PipelineId, Pipeline>,
403
404 browsing_contexts: FxHashMap<BrowsingContextId, BrowsingContext>,
406
407 browsing_context_group_set: FxHashMap<BrowsingContextGroupId, BrowsingContextGroup>,
411
412 browsing_context_group_next_id: u32,
414
415 pending_changes: Vec<SessionHistoryChange>,
421
422 next_pipeline_namespace_id: PipelineNamespaceId,
425
426 webdriver_load_status_sender: Option<(GenericSender<WebDriverLoadStatus>, PipelineId)>,
428
429 document_states: FxHashMap<PipelineId, DocumentState>,
431
432 shutting_down: bool,
434
435 handled_warnings: VecDeque<(Option<String>, String)>,
438
439 random_pipeline_closure: Option<(SmallRng, f32)>,
442
443 phantom: PhantomData<(STF, SWF)>,
445
446 webgl_threads: Option<WebGLThreads>,
448
449 webxr_registry: Option<webxr_api::Registry>,
451
452 canvas: OnceCell<(Sender<ConstellationCanvasMsg>, GenericSender<CanvasMsg>)>,
454
455 pending_approval_navigations: PendingApprovalNavigations,
457
458 pressed_mouse_buttons: u16,
461
462 active_keyboard_modifiers: Modifiers,
464
465 hard_fail: bool,
467
468 active_media_session: Option<PipelineId>,
470
471 broken_image_icon_data: Vec<u8>,
475
476 user_content_manager: UserContentManager,
478
479 process_manager: ProcessManager,
481
482 async_runtime: Box<dyn AsyncRuntime>,
484
485 script_join_handles: FxHashMap<WebViewId, JoinHandle<()>>,
487
488 privileged_urls: Vec<ServoUrl>,
490
491 image_cache_factory: Arc<ImageCacheFactoryImpl>,
495
496 pending_viewport_changes: HashMap<BrowsingContextId, ViewportDetails>,
499
500 screenshot_readiness_requests: Vec<ScreenshotReadinessRequest>,
504}
505
506pub struct InitialConstellationState {
508 pub embedder_proxy: EmbedderProxy,
510
511 pub compositor_proxy: CompositorProxy,
513
514 pub devtools_sender: Option<Sender<DevtoolsControlMsg>>,
516
517 #[cfg(feature = "bluetooth")]
519 pub bluetooth_thread: IpcSender<BluetoothRequest>,
520
521 pub system_font_service: Arc<SystemFontServiceProxy>,
523
524 pub public_resource_threads: ResourceThreads,
526
527 pub private_resource_threads: ResourceThreads,
529
530 pub public_storage_threads: StorageThreads,
532
533 pub private_storage_threads: StorageThreads,
535
536 pub time_profiler_chan: time::ProfilerChan,
538
539 pub mem_profiler_chan: mem::ProfilerChan,
541
542 pub webrender_external_images: Arc<Mutex<WebRenderExternalImageRegistry>>,
544
545 pub webgl_threads: Option<WebGLThreads>,
547
548 pub webxr_registry: Option<webxr_api::Registry>,
550
551 #[cfg(feature = "webgpu")]
552 pub wgpu_image_map: WGPUImageMap,
553
554 pub user_content_manager: UserContentManager,
556
557 pub privileged_urls: Vec<ServoUrl>,
559
560 pub async_runtime: Box<dyn AsyncRuntime>,
562}
563
564#[derive(Clone, Copy, Debug)]
569enum ExitPipelineMode {
570 Normal,
571 Force,
572}
573
574const WARNINGS_BUFFER_SIZE: usize = 32;
576
577fn route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors<T>(
579 ipc_receiver: IpcReceiver<T>,
580) -> Receiver<Result<T, IpcError>>
581where
582 T: for<'de> Deserialize<'de> + Serialize + Send + 'static,
583{
584 let (crossbeam_sender, crossbeam_receiver) = unbounded();
585 ROUTER.add_typed_route(
586 ipc_receiver,
587 Box::new(move |message| {
588 let _ = crossbeam_sender.send(message);
589 }),
590 );
591 crossbeam_receiver
592}
593
594impl<STF, SWF> Constellation<STF, SWF>
595where
596 STF: ScriptThreadFactory,
597 SWF: ServiceWorkerManagerFactory,
598{
599 #[servo_tracing::instrument(skip(state, layout_factory))]
601 pub fn start(
602 embedder_to_constellation_receiver: Receiver<EmbedderToConstellationMessage>,
603 state: InitialConstellationState,
604 layout_factory: Arc<dyn LayoutFactory>,
605 random_pipeline_closure_probability: Option<f32>,
606 random_pipeline_closure_seed: Option<usize>,
607 hard_fail: bool,
608 ) {
609 let (swmanager_ipc_sender, swmanager_ipc_receiver) =
611 generic_channel::channel().expect("ipc channel failure");
612
613 thread::Builder::new()
614 .name("Constellation".to_owned())
615 .spawn(move || {
616 let (script_ipc_sender, script_ipc_receiver) =
617 generic_channel::channel().expect("ipc channel failure");
618 let script_receiver = script_ipc_receiver.route_preserving_errors();
619
620 let (namespace_ipc_sender, namespace_ipc_receiver) =
621 generic_channel::channel().expect("ipc channel failure");
622 let namespace_receiver = namespace_ipc_receiver.route_preserving_errors();
623
624 let (background_hang_monitor_ipc_sender, background_hang_monitor_ipc_receiver) =
625 generic_channel::channel().expect("ipc channel failure");
626 let background_hang_monitor_receiver =
627 background_hang_monitor_ipc_receiver.route_preserving_errors();
628
629 let (
633 background_monitor_register,
634 background_monitor_register_join_handle,
635 background_hang_monitor_control_ipc_senders,
636 ) = if opts::get().multiprocess {
637 (None, None, vec![])
638 } else {
639 let (
640 background_hang_monitor_control_ipc_sender,
641 background_hang_monitor_control_ipc_receiver,
642 ) = generic_channel::channel().expect("ipc channel failure");
643 let (register, join_handle) = HangMonitorRegister::init(
644 background_hang_monitor_ipc_sender.clone(),
645 background_hang_monitor_control_ipc_receiver,
646 opts::get().background_hang_monitor,
647 );
648 (
649 Some(register),
650 Some(join_handle),
651 vec![background_hang_monitor_control_ipc_sender],
652 )
653 };
654
655 let swmanager_receiver = swmanager_ipc_receiver.route_preserving_errors();
656
657 PipelineNamespace::install(PipelineNamespaceId(1));
659
660 #[cfg(feature = "webgpu")]
661 let webrender_wgpu = WebRenderWGPU {
662 webrender_external_images: state.webrender_external_images,
663 wgpu_image_map: state.wgpu_image_map,
664 };
665
666 let broken_image_icon_data = resources::read_bytes(Resource::BrokenImageIcon);
667
668 let mut constellation: Constellation<STF, SWF> = Constellation {
669 namespace_receiver,
670 namespace_ipc_sender,
671 script_sender: script_ipc_sender,
672 background_hang_monitor_sender: background_hang_monitor_ipc_sender,
673 background_hang_monitor_receiver,
674 background_monitor_register,
675 background_monitor_register_join_handle,
676 background_monitor_control_senders: background_hang_monitor_control_ipc_senders,
677 script_receiver,
678 embedder_to_constellation_receiver,
679 layout_factory,
680 embedder_proxy: state.embedder_proxy,
681 compositor_proxy: state.compositor_proxy.clone(),
682 webviews: WebViewManager::default(),
683 devtools_sender: state.devtools_sender,
684 #[cfg(feature = "bluetooth")]
685 bluetooth_ipc_sender: state.bluetooth_thread,
686 public_resource_threads: state.public_resource_threads,
687 private_resource_threads: state.private_resource_threads,
688 public_storage_threads: state.public_storage_threads,
689 private_storage_threads: state.private_storage_threads,
690 system_font_service: state.system_font_service,
691 sw_managers: Default::default(),
692 swmanager_receiver,
693 swmanager_ipc_sender,
694 browsing_context_group_set: Default::default(),
695 browsing_context_group_next_id: Default::default(),
696 message_ports: Default::default(),
697 message_port_routers: Default::default(),
698 broadcast_channels: Default::default(),
699 pipelines: Default::default(),
700 browsing_contexts: Default::default(),
701 pending_changes: vec![],
702 next_pipeline_namespace_id: PipelineNamespaceId(2),
705 time_profiler_chan: state.time_profiler_chan,
706 mem_profiler_chan: state.mem_profiler_chan.clone(),
707 phantom: PhantomData,
708 webdriver_load_status_sender: None,
709 document_states: Default::default(),
710 #[cfg(feature = "webgpu")]
711 webrender_wgpu,
712 shutting_down: false,
713 handled_warnings: VecDeque::new(),
714 random_pipeline_closure: random_pipeline_closure_probability.map(|probability| {
715 let rng = random_pipeline_closure_seed
716 .map(|seed| SmallRng::seed_from_u64(seed as u64))
717 .unwrap_or_else(SmallRng::from_os_rng);
718 warn!("Randomly closing pipelines using seed {random_pipeline_closure_seed:?}.");
719 (rng, probability)
720 }),
721 webgl_threads: state.webgl_threads,
722 webxr_registry: state.webxr_registry,
723 canvas: OnceCell::new(),
724 pending_approval_navigations: Default::default(),
725 pressed_mouse_buttons: 0,
726 active_keyboard_modifiers: Modifiers::empty(),
727 hard_fail,
728 active_media_session: None,
729 broken_image_icon_data: broken_image_icon_data.clone(),
730 user_content_manager: state.user_content_manager,
731 process_manager: ProcessManager::new(state.mem_profiler_chan),
732 async_runtime: state.async_runtime,
733 script_join_handles: Default::default(),
734 privileged_urls: state.privileged_urls,
735 image_cache_factory: Arc::new(ImageCacheFactoryImpl::new(
736 broken_image_icon_data,
737 )),
738 pending_viewport_changes: Default::default(),
739 screenshot_readiness_requests: Vec::new(),
740 };
741
742 constellation.run();
743 })
744 .expect("Thread spawning failed");
745 }
746
747 fn run(&mut self) {
749 let join_handle = start_fetch_thread();
753
754 while !self.shutting_down || !self.pipelines.is_empty() {
755 self.maybe_close_random_pipeline();
758 self.handle_request();
759 }
760 self.handle_shutdown();
761
762 if !opts::get().multiprocess {
763 StyleThreadPool::shutdown();
764 }
765
766 exit_fetch_thread();
768 join_handle
769 .join()
770 .expect("Failed to join on the fetch thread in the constellation");
771
772 debug!("Asking embedding layer to complete shutdown.");
776 self.embedder_proxy.send(EmbedderMsg::ShutdownComplete);
777 }
778
779 fn send_message_to_pipeline(
782 &mut self,
783 pipeline_id: PipelineId,
784 message: ScriptThreadMessage,
785 failure_message: &str,
786 ) -> bool {
787 let result = match self.pipelines.get(&pipeline_id) {
788 Some(pipeline) => pipeline.event_loop.send(message),
789 None => {
790 warn!("{pipeline_id}: {failure_message}");
791 return false;
792 },
793 };
794 if let Err(err) = result {
795 self.handle_send_error(pipeline_id, err);
796 }
797 true
798 }
799
800 fn next_pipeline_namespace_id(&mut self) -> PipelineNamespaceId {
802 let namespace_id = self.next_pipeline_namespace_id;
803 let PipelineNamespaceId(ref mut i) = self.next_pipeline_namespace_id;
804 *i += 1;
805 namespace_id
806 }
807
808 fn next_browsing_context_group_id(&mut self) -> BrowsingContextGroupId {
809 let id = self.browsing_context_group_next_id;
810 self.browsing_context_group_next_id += 1;
811 BrowsingContextGroupId(id)
812 }
813
814 fn get_event_loop(
815 &mut self,
816 host: &Host,
817 webview_id: &WebViewId,
818 opener: &Option<BrowsingContextId>,
819 ) -> Result<Weak<EventLoop>, &'static str> {
820 let bc_group = match opener {
821 Some(browsing_context_id) => {
822 let opener = self
823 .browsing_contexts
824 .get(browsing_context_id)
825 .ok_or("Opener was closed before the openee started")?;
826 self.browsing_context_group_set
827 .get(&opener.bc_group_id)
828 .ok_or("Opener belongs to an unknown browsing context group")?
829 },
830 None => self
831 .browsing_context_group_set
832 .iter()
833 .filter_map(|(_, bc_group)| {
834 if bc_group
835 .top_level_browsing_context_set
836 .contains(webview_id)
837 {
838 Some(bc_group)
839 } else {
840 None
841 }
842 })
843 .last()
844 .ok_or(
845 "Trying to get an event-loop for a top-level belonging to an unknown browsing context group",
846 )?,
847 };
848 bc_group
849 .event_loops
850 .get(host)
851 .ok_or("Trying to get an event-loop from an unknown browsing context group")
852 .cloned()
853 }
854
855 fn set_event_loop(
856 &mut self,
857 event_loop: Weak<EventLoop>,
858 host: Host,
859 webview_id: WebViewId,
860 opener: Option<BrowsingContextId>,
861 ) {
862 let relevant_top_level = if let Some(opener) = opener {
863 match self.browsing_contexts.get(&opener) {
864 Some(opener) => opener.webview_id,
865 None => {
866 warn!("Setting event-loop for an unknown auxiliary");
867 return;
868 },
869 }
870 } else {
871 webview_id
872 };
873 let maybe_bc_group_id = self
874 .browsing_context_group_set
875 .iter()
876 .filter_map(|(id, bc_group)| {
877 if bc_group
878 .top_level_browsing_context_set
879 .contains(&webview_id)
880 {
881 Some(*id)
882 } else {
883 None
884 }
885 })
886 .last();
887 let Some(bc_group_id) = maybe_bc_group_id else {
888 return warn!("Trying to add an event-loop to an unknown browsing context group");
889 };
890 if let Some(bc_group) = self.browsing_context_group_set.get_mut(&bc_group_id) {
891 if bc_group
892 .event_loops
893 .insert(host.clone(), event_loop)
894 .is_some()
895 {
896 warn!(
897 "Double-setting an event-loop for {:?} at {:?}",
898 host, relevant_top_level
899 );
900 }
901 }
902 }
903
904 #[allow(clippy::too_many_arguments)]
906 fn new_pipeline(
907 &mut self,
908 pipeline_id: PipelineId,
909 browsing_context_id: BrowsingContextId,
910 webview_id: WebViewId,
911 parent_pipeline_id: Option<PipelineId>,
912 opener: Option<BrowsingContextId>,
913 initial_viewport_details: ViewportDetails,
914 load_data: LoadData,
919 is_private: bool,
920 throttled: bool,
921 ) {
922 if self.shutting_down {
923 return;
924 }
925
926 let Some(theme) = self
927 .webviews
928 .get(webview_id)
929 .map(ConstellationWebView::theme)
930 else {
931 warn!("Tried to create Pipeline for uknown WebViewId: {webview_id:?}");
932 return;
933 };
934
935 debug!(
936 "{}: Creating new pipeline in {}",
937 pipeline_id, browsing_context_id
938 );
939
940 let (event_loop, host) = if !load_data
941 .creation_sandboxing_flag_set
942 .contains(SandboxingFlagSet::SANDBOXED_ORIGIN_BROWSING_CONTEXT_FLAG)
943 {
944 if load_data.url.as_str() != "about:blank" && load_data.url.as_str() != "about:srcdoc" {
948 match reg_host(&load_data.url) {
949 None => (None, None),
950 Some(host) => match self.get_event_loop(&host, &webview_id, &opener) {
951 Err(err) => {
952 warn!("{}", err);
953 (None, Some(host))
954 },
955 Ok(event_loop) => {
956 if let Some(event_loop) = event_loop.upgrade() {
957 (Some(event_loop), None)
958 } else {
959 (None, Some(host))
960 }
961 },
962 },
963 }
964 } else if let Some(parent) =
965 parent_pipeline_id.and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
966 {
967 (Some(parent.event_loop.clone()), None)
968 } else if let Some(creator) = load_data
969 .creator_pipeline_id
970 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
971 {
972 (Some(creator.event_loop.clone()), None)
973 } else {
974 (None, None)
975 }
976 } else {
977 (None, None)
978 };
979
980 let resource_threads = if is_private {
981 self.private_resource_threads.clone()
982 } else {
983 self.public_resource_threads.clone()
984 };
985 let storage_threads = if is_private {
986 self.private_storage_threads.clone()
987 } else {
988 self.public_storage_threads.clone()
989 };
990
991 let embedder_chan = self.embedder_proxy.sender.clone();
992 let eventloop_waker = self.embedder_proxy.event_loop_waker.clone();
993 let script_to_embedder_chan = ScriptToEmbedderChan::new(embedder_chan, eventloop_waker);
994
995 let result = Pipeline::spawn::<STF>(InitialPipelineState {
996 id: pipeline_id,
997 browsing_context_id,
998 webview_id,
999 parent_pipeline_id,
1000 opener,
1001 script_to_constellation_chan: ScriptToConstellationChan {
1002 sender: self.script_sender.clone(),
1003 webview_id,
1004 pipeline_id,
1005 },
1006 script_to_embedder_chan,
1007 namespace_request_sender: self.namespace_ipc_sender.clone(),
1008 pipeline_namespace_id: self.next_pipeline_namespace_id(),
1009 background_monitor_register: self.background_monitor_register.clone(),
1010 background_hang_monitor_to_constellation_chan: self
1011 .background_hang_monitor_sender
1012 .clone(),
1013 layout_factory: self.layout_factory.clone(),
1014 compositor_proxy: self.compositor_proxy.clone(),
1015 devtools_sender: self.devtools_sender.clone(),
1016 #[cfg(feature = "bluetooth")]
1017 bluetooth_thread: self.bluetooth_ipc_sender.clone(),
1018 system_font_service: self.system_font_service.clone(),
1019 resource_threads,
1020 storage_threads,
1021 time_profiler_chan: self.time_profiler_chan.clone(),
1022 mem_profiler_chan: self.mem_profiler_chan.clone(),
1023 viewport_details: initial_viewport_details,
1024 theme,
1025 event_loop,
1026 load_data,
1027 prev_throttled: throttled,
1028 webgl_chan: self
1029 .webgl_threads
1030 .as_ref()
1031 .map(|threads| threads.pipeline()),
1032 webxr_registry: self.webxr_registry.clone(),
1033 player_context: WindowGLContext::get(),
1034 broken_image_icon_data: self.broken_image_icon_data.clone(),
1035 user_content_manager: self.user_content_manager.clone(),
1036 privileged_urls: self.privileged_urls.clone(),
1037 image_cache_factory: self.image_cache_factory.clone(),
1038 });
1039
1040 let pipeline = match result {
1041 Ok(result) => result,
1042 Err(e) => return self.handle_send_error(pipeline_id, e),
1043 };
1044
1045 if let Some(chan) = pipeline.bhm_control_chan {
1046 self.background_monitor_control_senders.push(chan);
1047 }
1048
1049 if let Some(join_handle) = pipeline.join_handle {
1050 self.script_join_handles.insert(webview_id, join_handle);
1051 }
1052
1053 if let Some(host) = host {
1054 debug!("{}: Adding new host entry {}", webview_id, host,);
1055 self.set_event_loop(
1056 Rc::downgrade(&pipeline.pipeline.event_loop),
1057 host,
1058 webview_id,
1059 opener,
1060 );
1061 }
1062
1063 if let Some((lifeline_receiver, process)) = pipeline.lifeline {
1064 let crossbeam_receiver =
1065 route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors(lifeline_receiver);
1066 self.process_manager.add(crossbeam_receiver, process);
1067 }
1068
1069 assert!(!self.pipelines.contains_key(&pipeline_id));
1070 self.pipelines.insert(pipeline_id, pipeline.pipeline);
1071 }
1072
1073 fn fully_active_descendant_browsing_contexts_iter(
1075 &self,
1076 browsing_context_id: BrowsingContextId,
1077 ) -> FullyActiveBrowsingContextsIterator<'_> {
1078 FullyActiveBrowsingContextsIterator {
1079 stack: vec![browsing_context_id],
1080 pipelines: &self.pipelines,
1081 browsing_contexts: &self.browsing_contexts,
1082 }
1083 }
1084
1085 fn fully_active_browsing_contexts_iter(
1087 &self,
1088 webview_id: WebViewId,
1089 ) -> FullyActiveBrowsingContextsIterator<'_> {
1090 self.fully_active_descendant_browsing_contexts_iter(BrowsingContextId::from(webview_id))
1091 }
1092
1093 fn all_descendant_browsing_contexts_iter(
1095 &self,
1096 browsing_context_id: BrowsingContextId,
1097 ) -> AllBrowsingContextsIterator<'_> {
1098 AllBrowsingContextsIterator {
1099 stack: vec![browsing_context_id],
1100 pipelines: &self.pipelines,
1101 browsing_contexts: &self.browsing_contexts,
1102 }
1103 }
1104
1105 fn ancestor_pipelines_of_browsing_context_iter(
1108 &self,
1109 browsing_context_id: BrowsingContextId,
1110 ) -> impl Iterator<Item = &Pipeline> + '_ {
1111 let mut state: Option<PipelineId> = self
1112 .browsing_contexts
1113 .get(&browsing_context_id)
1114 .and_then(|browsing_context| browsing_context.parent_pipeline_id);
1115 std::iter::from_fn(move || {
1116 if let Some(pipeline_id) = state {
1117 let pipeline = self.pipelines.get(&pipeline_id)?;
1118 let browsing_context = self.browsing_contexts.get(&pipeline.browsing_context_id)?;
1119 state = browsing_context.parent_pipeline_id;
1120 Some(pipeline)
1121 } else {
1122 None
1123 }
1124 })
1125 }
1126
1127 fn ancestor_or_self_pipelines_of_browsing_context_iter(
1130 &self,
1131 browsing_context_id: BrowsingContextId,
1132 ) -> impl Iterator<Item = &Pipeline> + '_ {
1133 let this_pipeline = self
1134 .browsing_contexts
1135 .get(&browsing_context_id)
1136 .map(|browsing_context| browsing_context.pipeline_id)
1137 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id));
1138 this_pipeline
1139 .into_iter()
1140 .chain(self.ancestor_pipelines_of_browsing_context_iter(browsing_context_id))
1141 }
1142
1143 #[allow(clippy::too_many_arguments)]
1145 fn new_browsing_context(
1146 &mut self,
1147 browsing_context_id: BrowsingContextId,
1148 webview_id: WebViewId,
1149 pipeline_id: PipelineId,
1150 parent_pipeline_id: Option<PipelineId>,
1151 viewport_details: ViewportDetails,
1152 is_private: bool,
1153 inherited_secure_context: Option<bool>,
1154 throttled: bool,
1155 ) {
1156 debug!("{}: Creating new browsing context", browsing_context_id);
1157 let bc_group_id = match self
1158 .browsing_context_group_set
1159 .iter_mut()
1160 .filter_map(|(id, bc_group)| {
1161 if bc_group
1162 .top_level_browsing_context_set
1163 .contains(&webview_id)
1164 {
1165 Some(id)
1166 } else {
1167 None
1168 }
1169 })
1170 .last()
1171 {
1172 Some(id) => *id,
1173 None => {
1174 warn!("Top-level was unexpectedly removed from its top_level_browsing_context_set");
1175 return;
1176 },
1177 };
1178
1179 let viewport_details = self
1181 .pending_viewport_changes
1182 .remove(&browsing_context_id)
1183 .unwrap_or(viewport_details);
1184 let browsing_context = BrowsingContext::new(
1185 bc_group_id,
1186 browsing_context_id,
1187 webview_id,
1188 pipeline_id,
1189 parent_pipeline_id,
1190 viewport_details,
1191 is_private,
1192 inherited_secure_context,
1193 throttled,
1194 );
1195 self.browsing_contexts
1196 .insert(browsing_context_id, browsing_context);
1197
1198 if let Some(parent_pipeline_id) = parent_pipeline_id {
1200 if let Some(parent) = self.pipelines.get_mut(&parent_pipeline_id) {
1201 parent.add_child(browsing_context_id);
1202 }
1203 }
1204 }
1205
1206 fn add_pending_change(&mut self, change: SessionHistoryChange) {
1207 debug!(
1208 "adding pending session history change with {}",
1209 if change.replace.is_some() {
1210 "replacement"
1211 } else {
1212 "no replacement"
1213 },
1214 );
1215 self.pending_changes.push(change);
1216 }
1217
1218 #[servo_tracing::instrument(skip_all)]
1220 fn handle_request(&mut self) {
1221 #[allow(clippy::large_enum_variant)]
1222 #[derive(Debug)]
1223 enum Request {
1224 PipelineNamespace(PipelineNamespaceRequest),
1225 Script((WebViewId, PipelineId, ScriptToConstellationMessage)),
1226 BackgroundHangMonitor(HangMonitorAlert),
1227 Compositor(EmbedderToConstellationMessage),
1228 FromSWManager(SWManagerMsg),
1229 RemoveProcess(usize),
1230 }
1231 let mut sel = Select::new();
1243 sel.recv(&self.namespace_receiver);
1244 sel.recv(&self.script_receiver);
1245 sel.recv(&self.background_hang_monitor_receiver);
1246 sel.recv(&self.embedder_to_constellation_receiver);
1247 sel.recv(&self.swmanager_receiver);
1248
1249 self.process_manager.register(&mut sel);
1250
1251 let request = {
1252 let oper = sel.select();
1253 let index = oper.index();
1254
1255 #[cfg(feature = "tracing")]
1256 let _span =
1257 tracing::trace_span!("handle_request::select", servo_profiling = true).entered();
1258 match index {
1259 0 => oper
1260 .recv(&self.namespace_receiver)
1261 .expect("Unexpected script channel panic in constellation")
1262 .map(Request::PipelineNamespace),
1263 1 => oper
1264 .recv(&self.script_receiver)
1265 .expect("Unexpected script channel panic in constellation")
1266 .map(Request::Script),
1267 2 => oper
1268 .recv(&self.background_hang_monitor_receiver)
1269 .expect("Unexpected BHM channel panic in constellation")
1270 .map(Request::BackgroundHangMonitor),
1271 3 => Ok(Request::Compositor(
1272 oper.recv(&self.embedder_to_constellation_receiver)
1273 .expect("Unexpected compositor channel panic in constellation"),
1274 )),
1275 4 => oper
1276 .recv(&self.swmanager_receiver)
1277 .expect("Unexpected SW channel panic in constellation")
1278 .map(Request::FromSWManager),
1279 _ => {
1280 let process_index = index - 5;
1282 let _ = oper.recv(self.process_manager.receiver_at(process_index));
1283 Ok(Request::RemoveProcess(process_index))
1284 },
1285 }
1286 };
1287
1288 let request = match request {
1289 Ok(request) => request,
1290 Err(err) => return error!("Deserialization failed ({}).", err),
1291 };
1292
1293 match request {
1294 Request::PipelineNamespace(message) => {
1295 self.handle_request_for_pipeline_namespace(message)
1296 },
1297 Request::Compositor(message) => self.handle_request_from_compositor(message),
1298 Request::Script(message) => {
1299 self.handle_request_from_script(message);
1300 },
1301 Request::BackgroundHangMonitor(message) => {
1302 self.handle_request_from_background_hang_monitor(message);
1303 },
1304 Request::FromSWManager(message) => {
1305 self.handle_request_from_swmanager(message);
1306 },
1307 Request::RemoveProcess(index) => self.process_manager.remove(index),
1308 }
1309 }
1310
1311 #[servo_tracing::instrument(skip_all)]
1312 fn handle_request_for_pipeline_namespace(&mut self, request: PipelineNamespaceRequest) {
1313 let PipelineNamespaceRequest(sender) = request;
1314 let _ = sender.send(self.next_pipeline_namespace_id());
1315 }
1316
1317 #[servo_tracing::instrument(skip_all)]
1318 fn handle_request_from_background_hang_monitor(&self, message: HangMonitorAlert) {
1319 match message {
1320 HangMonitorAlert::Profile(bytes) => {
1321 self.embedder_proxy.send(EmbedderMsg::ReportProfile(bytes))
1322 },
1323 HangMonitorAlert::Hang(hang) => {
1324 warn!("Component hang alert: {:?}", hang);
1327 },
1328 }
1329 }
1330
1331 fn handle_request_from_swmanager(&mut self, message: SWManagerMsg) {
1332 match message {
1333 SWManagerMsg::PostMessageToClient => {
1334 },
1337 }
1338 }
1339
1340 #[servo_tracing::instrument(skip_all)]
1341 fn handle_request_from_compositor(&mut self, message: EmbedderToConstellationMessage) {
1342 trace_msg_from_compositor!(message, "{message:?}");
1343 match message {
1344 EmbedderToConstellationMessage::Exit => {
1345 self.handle_exit();
1346 },
1347 EmbedderToConstellationMessage::AllowNavigationResponse(pipeline_id, allowed) => {
1351 let pending = self.pending_approval_navigations.remove(&pipeline_id);
1352
1353 let webview_id = match self.pipelines.get(&pipeline_id) {
1354 Some(pipeline) => pipeline.webview_id,
1355 None => return warn!("{}: Attempted to navigate after closure", pipeline_id),
1356 };
1357
1358 match pending {
1359 Some((load_data, history_handling)) => {
1360 if allowed {
1361 self.load_url(webview_id, pipeline_id, load_data, history_handling);
1362 } else {
1363 if let Some((sender, id)) = &self.webdriver_load_status_sender {
1364 if pipeline_id == *id {
1365 let _ = sender.send(WebDriverLoadStatus::NavigationStop);
1366 }
1367 }
1368
1369 let pipeline_is_top_level_pipeline = self
1370 .browsing_contexts
1371 .get(&BrowsingContextId::from(webview_id))
1372 .is_some_and(|ctx| ctx.pipeline_id == pipeline_id);
1373 if !pipeline_is_top_level_pipeline {
1377 self.send_message_to_pipeline(
1378 pipeline_id,
1379 ScriptThreadMessage::StopDelayingLoadEventsMode(pipeline_id),
1380 "Attempted to navigate after closure",
1381 );
1382 }
1383 }
1384 },
1385 None => {
1386 warn!(
1387 "{}: AllowNavigationResponse for unknown request",
1388 pipeline_id
1389 )
1390 },
1391 }
1392 },
1393 EmbedderToConstellationMessage::ClearCache => {
1394 self.public_resource_threads.clear_cache();
1395 self.private_resource_threads.clear_cache();
1396 },
1397 EmbedderToConstellationMessage::LoadUrl(webview_id, url) => {
1401 let load_data = LoadData::new(
1402 LoadOrigin::Constellation,
1403 url,
1404 None,
1405 Referrer::NoReferrer,
1406 ReferrerPolicy::EmptyString,
1407 None,
1408 None,
1409 false,
1410 SandboxingFlagSet::empty(),
1411 );
1412 let ctx_id = BrowsingContextId::from(webview_id);
1413 let pipeline_id = match self.browsing_contexts.get(&ctx_id) {
1414 Some(ctx) => ctx.pipeline_id,
1415 None => {
1416 return warn!("{}: LoadUrl for unknown browsing context", webview_id);
1417 },
1418 };
1419 self.load_url(
1422 webview_id,
1423 pipeline_id,
1424 load_data,
1425 NavigationHistoryBehavior::Push,
1426 );
1427 },
1428 EmbedderToConstellationMessage::NewWebView(url, webview_id, viewport_details) => {
1431 self.handle_new_top_level_browsing_context(url, webview_id, viewport_details);
1432 },
1433 EmbedderToConstellationMessage::CloseWebView(webview_id) => {
1435 self.handle_close_top_level_browsing_context(webview_id);
1436 },
1437 EmbedderToConstellationMessage::SendError(webview_id, error) => {
1439 debug!("constellation got SendError message");
1440 if webview_id.is_none() {
1441 warn!("constellation got a SendError message without top level id");
1442 }
1443 self.handle_panic(webview_id, error, None);
1444 },
1445 EmbedderToConstellationMessage::FocusWebView(webview_id) => {
1446 self.handle_focus_web_view(webview_id);
1447 },
1448 EmbedderToConstellationMessage::BlurWebView => {
1449 self.webviews.unfocus();
1450 self.embedder_proxy.send(EmbedderMsg::WebViewBlurred);
1451 },
1452 EmbedderToConstellationMessage::TraverseHistory(
1454 webview_id,
1455 direction,
1456 traversal_id,
1457 ) => {
1458 self.handle_traverse_history_msg(webview_id, direction);
1459 self.embedder_proxy
1460 .send(EmbedderMsg::HistoryTraversalComplete(
1461 webview_id,
1462 traversal_id,
1463 ));
1464 },
1465 EmbedderToConstellationMessage::ChangeViewportDetails(
1466 webview_id,
1467 new_viewport_details,
1468 size_type,
1469 ) => {
1470 self.handle_change_viewport_details_msg(
1471 webview_id,
1472 new_viewport_details,
1473 size_type,
1474 );
1475 },
1476 EmbedderToConstellationMessage::ThemeChange(webview_id, theme) => {
1477 self.handle_theme_change(webview_id, theme);
1478 },
1479 EmbedderToConstellationMessage::TickAnimation(webview_ids) => {
1480 self.handle_tick_animation(webview_ids)
1481 },
1482 EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
1483 pipeline_ids,
1484 ) => self.handle_no_longer_waiting_on_asynchronous_image_updates(pipeline_ids),
1485 EmbedderToConstellationMessage::WebDriverCommand(command) => {
1486 self.handle_webdriver_msg(command);
1487 },
1488 EmbedderToConstellationMessage::Reload(webview_id) => {
1489 self.handle_reload_msg(webview_id);
1490 },
1491 EmbedderToConstellationMessage::LogEntry(webview_id, thread_name, entry) => {
1492 self.handle_log_entry(webview_id, thread_name, entry);
1493 },
1494 EmbedderToConstellationMessage::ForwardInputEvent(webview_id, event, hit_test) => {
1495 self.forward_input_event(webview_id, event, hit_test);
1496 },
1497 EmbedderToConstellationMessage::RefreshCursor(pipeline_id) => {
1498 self.handle_refresh_cursor(pipeline_id)
1499 },
1500 EmbedderToConstellationMessage::ToggleProfiler(rate, max_duration) => {
1501 for background_monitor_control_sender in &self.background_monitor_control_senders {
1502 if let Err(e) = background_monitor_control_sender.send(
1503 BackgroundHangMonitorControlMsg::ToggleSampler(rate, max_duration),
1504 ) {
1505 warn!("error communicating with sampling profiler: {}", e);
1506 }
1507 }
1508 },
1509 EmbedderToConstellationMessage::ExitFullScreen(webview_id) => {
1510 self.handle_exit_fullscreen_msg(webview_id);
1511 },
1512 EmbedderToConstellationMessage::MediaSessionAction(action) => {
1513 self.handle_media_session_action_msg(action);
1514 },
1515 EmbedderToConstellationMessage::SetWebViewThrottled(webview_id, throttled) => {
1516 self.set_webview_throttled(webview_id, throttled);
1517 },
1518 EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_states) => {
1519 self.handle_set_scroll_states(pipeline_id, scroll_states)
1520 },
1521 EmbedderToConstellationMessage::PaintMetric(pipeline_id, paint_metric_event) => {
1522 self.handle_paint_metric(pipeline_id, paint_metric_event);
1523 },
1524 EmbedderToConstellationMessage::EvaluateJavaScript(
1525 webview_id,
1526 evaluation_id,
1527 script,
1528 ) => {
1529 self.handle_evaluate_javascript(webview_id, evaluation_id, script);
1530 },
1531 EmbedderToConstellationMessage::CreateMemoryReport(sender) => {
1532 self.mem_profiler_chan.send(ProfilerMsg::Report(sender));
1533 },
1534 EmbedderToConstellationMessage::SendImageKeysForPipeline(pipeline_id, image_keys) => {
1535 if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
1536 if pipeline
1537 .event_loop
1538 .send(ScriptThreadMessage::SendImageKeysBatch(
1539 pipeline_id,
1540 image_keys,
1541 ))
1542 .is_err()
1543 {
1544 warn!("Could not send image keys to pipeline {:?}", pipeline_id);
1545 }
1546 } else {
1547 warn!(
1548 "Keys were generated for a pipeline ({:?}) that was
1549 closed before the request could be fulfilled.",
1550 pipeline_id
1551 )
1552 }
1553 },
1554 EmbedderToConstellationMessage::PreferencesUpdated(updates) => {
1555 let event_loops = self
1556 .pipelines
1557 .values()
1558 .map(|pipeline| pipeline.event_loop.clone());
1559 for event_loop in event_loops {
1560 let _ = event_loop.send(ScriptThreadMessage::PreferencesUpdated(
1561 updates
1562 .iter()
1563 .map(|(name, value)| (String::from(*name), value.clone()))
1564 .collect(),
1565 ));
1566 }
1567 },
1568 EmbedderToConstellationMessage::RequestScreenshotReadiness(webview_id) => {
1569 self.handle_request_screenshot_readiness(webview_id)
1570 },
1571 EmbedderToConstellationMessage::EmbedderControlResponse(id, response) => {
1572 self.handle_embedder_control_response(id, response);
1573 },
1574 }
1575 }
1576
1577 #[servo_tracing::instrument(skip_all)]
1578 fn handle_evaluate_javascript(
1579 &mut self,
1580 webview_id: WebViewId,
1581 evaluation_id: JavaScriptEvaluationId,
1582 script: String,
1583 ) {
1584 let browsing_context_id = BrowsingContextId::from(webview_id);
1585 let Some(pipeline) = self
1586 .browsing_contexts
1587 .get(&browsing_context_id)
1588 .and_then(|browsing_context| self.pipelines.get(&browsing_context.pipeline_id))
1589 else {
1590 self.handle_finish_javascript_evaluation(
1591 evaluation_id,
1592 Err(JavaScriptEvaluationError::InternalError),
1593 );
1594 return;
1595 };
1596
1597 if pipeline
1598 .event_loop
1599 .send(ScriptThreadMessage::EvaluateJavaScript(
1600 webview_id,
1601 pipeline.id,
1602 evaluation_id,
1603 script,
1604 ))
1605 .is_err()
1606 {
1607 self.handle_finish_javascript_evaluation(
1608 evaluation_id,
1609 Err(JavaScriptEvaluationError::InternalError),
1610 );
1611 }
1612 }
1613
1614 #[servo_tracing::instrument(skip_all)]
1615 fn handle_request_from_script(
1616 &mut self,
1617 message: (WebViewId, PipelineId, ScriptToConstellationMessage),
1618 ) {
1619 let (webview_id, source_pipeline_id, content) = message;
1620 trace_script_msg!(content, "{source_pipeline_id}: {content:?}");
1621
1622 match content {
1623 ScriptToConstellationMessage::CompleteMessagePortTransfer(router_id, ports) => {
1624 self.handle_complete_message_port_transfer(router_id, ports);
1625 },
1626 ScriptToConstellationMessage::MessagePortTransferResult(
1627 router_id,
1628 succeeded,
1629 failed,
1630 ) => {
1631 self.handle_message_port_transfer_completed(router_id, succeeded);
1632 self.handle_message_port_transfer_failed(failed);
1633 },
1634 ScriptToConstellationMessage::RerouteMessagePort(port_id, task) => {
1635 self.handle_reroute_messageport(port_id, task);
1636 },
1637 ScriptToConstellationMessage::MessagePortShipped(port_id) => {
1638 self.handle_messageport_shipped(port_id);
1639 },
1640 ScriptToConstellationMessage::NewMessagePortRouter(router_id, ipc_sender) => {
1641 self.handle_new_messageport_router(router_id, ipc_sender);
1642 },
1643 ScriptToConstellationMessage::RemoveMessagePortRouter(router_id) => {
1644 self.handle_remove_messageport_router(router_id);
1645 },
1646 ScriptToConstellationMessage::NewMessagePort(router_id, port_id) => {
1647 self.handle_new_messageport(router_id, port_id);
1648 },
1649 ScriptToConstellationMessage::EntanglePorts(port1, port2) => {
1650 self.handle_entangle_messageports(port1, port2);
1651 },
1652 ScriptToConstellationMessage::DisentanglePorts(port1, port2) => {
1653 self.handle_disentangle_messageports(port1, port2);
1654 },
1655 ScriptToConstellationMessage::NewBroadcastChannelRouter(
1656 router_id,
1657 response_sender,
1658 origin,
1659 ) => {
1660 if self
1661 .check_origin_against_pipeline(&source_pipeline_id, &origin)
1662 .is_err()
1663 {
1664 return warn!("Attempt to add broadcast router from an unexpected origin.");
1665 }
1666 self.broadcast_channels
1667 .new_broadcast_channel_router(router_id, response_sender);
1668 },
1669 ScriptToConstellationMessage::NewBroadcastChannelNameInRouter(
1670 router_id,
1671 channel_name,
1672 origin,
1673 ) => {
1674 if self
1675 .check_origin_against_pipeline(&source_pipeline_id, &origin)
1676 .is_err()
1677 {
1678 return warn!("Attempt to add channel name from an unexpected origin.");
1679 }
1680 self.broadcast_channels
1681 .new_broadcast_channel_name_in_router(router_id, channel_name, origin);
1682 },
1683 ScriptToConstellationMessage::RemoveBroadcastChannelNameInRouter(
1684 router_id,
1685 channel_name,
1686 origin,
1687 ) => {
1688 if self
1689 .check_origin_against_pipeline(&source_pipeline_id, &origin)
1690 .is_err()
1691 {
1692 return warn!("Attempt to remove channel name from an unexpected origin.");
1693 }
1694 self.broadcast_channels
1695 .remove_broadcast_channel_name_in_router(router_id, channel_name, origin);
1696 },
1697 ScriptToConstellationMessage::RemoveBroadcastChannelRouter(router_id, origin) => {
1698 if self
1699 .check_origin_against_pipeline(&source_pipeline_id, &origin)
1700 .is_err()
1701 {
1702 return warn!("Attempt to remove broadcast router from an unexpected origin.");
1703 }
1704 self.broadcast_channels
1705 .remove_broadcast_channel_router(router_id);
1706 },
1707 ScriptToConstellationMessage::ScheduleBroadcast(router_id, message) => {
1708 if self
1709 .check_origin_against_pipeline(&source_pipeline_id, &message.origin)
1710 .is_err()
1711 {
1712 return warn!(
1713 "Attempt to schedule broadcast from an origin not matching the origin of the msg."
1714 );
1715 }
1716 self.broadcast_channels
1717 .schedule_broadcast(router_id, message);
1718 },
1719 ScriptToConstellationMessage::PipelineExited => {
1720 self.handle_pipeline_exited(source_pipeline_id);
1721 },
1722 ScriptToConstellationMessage::DiscardDocument => {
1723 self.handle_discard_document(webview_id, source_pipeline_id);
1724 },
1725 ScriptToConstellationMessage::DiscardTopLevelBrowsingContext => {
1726 self.handle_close_top_level_browsing_context(webview_id);
1727 },
1728 ScriptToConstellationMessage::ScriptLoadedURLInIFrame(load_info) => {
1729 self.handle_script_loaded_url_in_iframe_msg(load_info);
1730 },
1731 ScriptToConstellationMessage::ScriptNewIFrame(load_info) => {
1732 self.handle_script_new_iframe(load_info);
1733 },
1734 ScriptToConstellationMessage::CreateAuxiliaryWebView(load_info) => {
1735 self.handle_script_new_auxiliary(load_info);
1736 },
1737 ScriptToConstellationMessage::ChangeRunningAnimationsState(animation_state) => {
1738 self.handle_change_running_animations_state(source_pipeline_id, animation_state)
1739 },
1740 ScriptToConstellationMessage::LoadUrl(load_data, history_handling) => {
1742 self.schedule_navigation(
1743 webview_id,
1744 source_pipeline_id,
1745 load_data,
1746 history_handling,
1747 );
1748 },
1749 ScriptToConstellationMessage::AbortLoadUrl => {
1750 self.handle_abort_load_url_msg(source_pipeline_id);
1751 },
1752 ScriptToConstellationMessage::LoadComplete => {
1754 self.handle_load_complete_msg(webview_id, source_pipeline_id)
1755 },
1756 ScriptToConstellationMessage::NavigatedToFragment(new_url, replacement_enabled) => {
1758 self.handle_navigated_to_fragment(source_pipeline_id, new_url, replacement_enabled);
1759 },
1760 ScriptToConstellationMessage::TraverseHistory(direction) => {
1762 self.handle_traverse_history_msg(webview_id, direction);
1763 },
1764 ScriptToConstellationMessage::PushHistoryState(history_state_id, url) => {
1766 self.handle_push_history_state_msg(source_pipeline_id, history_state_id, url);
1767 },
1768 ScriptToConstellationMessage::ReplaceHistoryState(history_state_id, url) => {
1769 self.handle_replace_history_state_msg(source_pipeline_id, history_state_id, url);
1770 },
1771 ScriptToConstellationMessage::JointSessionHistoryLength(response_sender) => {
1773 self.handle_joint_session_history_length(webview_id, response_sender);
1774 },
1775 ScriptToConstellationMessage::ActivateDocument => {
1777 self.handle_activate_document_msg(source_pipeline_id);
1778 },
1779 ScriptToConstellationMessage::SetFinalUrl(final_url) => {
1781 if let Some(ref mut pipeline) = self.pipelines.get_mut(&source_pipeline_id) {
1783 pipeline.url = final_url;
1784 } else {
1785 warn!("constellation got set final url message for dead pipeline");
1786 }
1787 },
1788 ScriptToConstellationMessage::PostMessage {
1789 target: browsing_context_id,
1790 source: source_pipeline_id,
1791 target_origin: origin,
1792 source_origin,
1793 data,
1794 } => {
1795 self.handle_post_message_msg(
1796 browsing_context_id,
1797 source_pipeline_id,
1798 origin,
1799 source_origin,
1800 data,
1801 );
1802 },
1803 ScriptToConstellationMessage::Focus(focused_child_browsing_context_id, sequence) => {
1804 self.handle_focus_msg(
1805 source_pipeline_id,
1806 focused_child_browsing_context_id,
1807 sequence,
1808 );
1809 },
1810 ScriptToConstellationMessage::FocusRemoteDocument(focused_browsing_context_id) => {
1811 self.handle_focus_remote_document_msg(focused_browsing_context_id);
1812 },
1813 ScriptToConstellationMessage::SetThrottledComplete(throttled) => {
1814 self.handle_set_throttled_complete(source_pipeline_id, throttled);
1815 },
1816 ScriptToConstellationMessage::RemoveIFrame(browsing_context_id, response_sender) => {
1817 let removed_pipeline_ids = self.handle_remove_iframe_msg(browsing_context_id);
1818 if let Err(e) = response_sender.send(removed_pipeline_ids) {
1819 warn!("Error replying to remove iframe ({})", e);
1820 }
1821 },
1822 ScriptToConstellationMessage::CreateCanvasPaintThread(size, response_sender) => {
1823 self.handle_create_canvas_paint_thread_msg(size, response_sender)
1824 },
1825 ScriptToConstellationMessage::SetDocumentState(state) => {
1826 self.document_states.insert(source_pipeline_id, state);
1827 },
1828 ScriptToConstellationMessage::LogEntry(thread_name, entry) => {
1829 self.handle_log_entry(Some(webview_id), thread_name, entry);
1830 },
1831 ScriptToConstellationMessage::GetBrowsingContextInfo(pipeline_id, response_sender) => {
1832 let result = self
1833 .pipelines
1834 .get(&pipeline_id)
1835 .and_then(|pipeline| self.browsing_contexts.get(&pipeline.browsing_context_id))
1836 .map(|ctx| (ctx.id, ctx.parent_pipeline_id));
1837 if let Err(e) = response_sender.send(result) {
1838 warn!(
1839 "Sending reply to get browsing context info failed ({:?}).",
1840 e
1841 );
1842 }
1843 },
1844 ScriptToConstellationMessage::GetTopForBrowsingContext(
1845 browsing_context_id,
1846 response_sender,
1847 ) => {
1848 let result = self
1849 .browsing_contexts
1850 .get(&browsing_context_id)
1851 .map(|bc| bc.webview_id);
1852 if let Err(e) = response_sender.send(result) {
1853 warn!(
1854 "Sending reply to get top for browsing context info failed ({:?}).",
1855 e
1856 );
1857 }
1858 },
1859 ScriptToConstellationMessage::GetChildBrowsingContextId(
1860 browsing_context_id,
1861 index,
1862 response_sender,
1863 ) => {
1864 let result = self
1865 .browsing_contexts
1866 .get(&browsing_context_id)
1867 .and_then(|bc| self.pipelines.get(&bc.pipeline_id))
1868 .and_then(|pipeline| pipeline.children.get(index))
1869 .copied();
1870 if let Err(e) = response_sender.send(result) {
1871 warn!(
1872 "Sending reply to get child browsing context ID failed ({:?}).",
1873 e
1874 );
1875 }
1876 },
1877 ScriptToConstellationMessage::ScheduleJob(job) => {
1878 self.handle_schedule_serviceworker_job(source_pipeline_id, job);
1879 },
1880 ScriptToConstellationMessage::ForwardDOMMessage(msg_vec, scope_url) => {
1881 if let Some(mgr) = self.sw_managers.get(&scope_url.origin()) {
1882 let _ = mgr.send(ServiceWorkerMsg::ForwardDOMMessage(msg_vec, scope_url));
1883 } else {
1884 warn!("Unable to forward DOMMessage for postMessage call");
1885 }
1886 },
1887 ScriptToConstellationMessage::BroadcastStorageEvent(
1888 storage,
1889 url,
1890 key,
1891 old_value,
1892 new_value,
1893 ) => {
1894 self.handle_broadcast_storage_event(
1895 source_pipeline_id,
1896 storage,
1897 url,
1898 key,
1899 old_value,
1900 new_value,
1901 );
1902 },
1903 ScriptToConstellationMessage::MediaSessionEvent(pipeline_id, event) => {
1904 if self.active_media_session.is_some() {
1911 if let MediaSessionEvent::PlaybackStateChange(ref state) = event {
1912 if !matches!(
1913 state,
1914 MediaSessionPlaybackState::Playing | MediaSessionPlaybackState::Paused
1915 ) {
1916 return;
1917 }
1918 };
1919 }
1920 self.active_media_session = Some(pipeline_id);
1921 self.embedder_proxy
1922 .send(EmbedderMsg::MediaSessionEvent(webview_id, event));
1923 },
1924 #[cfg(feature = "webgpu")]
1925 ScriptToConstellationMessage::RequestAdapter(response_sender, options, ids) => self
1926 .handle_wgpu_request(
1927 source_pipeline_id,
1928 BrowsingContextId::from(webview_id),
1929 ScriptToConstellationMessage::RequestAdapter(response_sender, options, ids),
1930 ),
1931 #[cfg(feature = "webgpu")]
1932 ScriptToConstellationMessage::GetWebGPUChan(response_sender) => self
1933 .handle_wgpu_request(
1934 source_pipeline_id,
1935 BrowsingContextId::from(webview_id),
1936 ScriptToConstellationMessage::GetWebGPUChan(response_sender),
1937 ),
1938 ScriptToConstellationMessage::TitleChanged(pipeline, title) => {
1939 if let Some(pipeline) = self.pipelines.get_mut(&pipeline) {
1940 pipeline.title = title;
1941 }
1942 },
1943 ScriptToConstellationMessage::IFrameSizes(iframe_sizes) => {
1944 self.handle_iframe_size_msg(iframe_sizes)
1945 },
1946 ScriptToConstellationMessage::ReportMemory(sender) => {
1947 self.mem_profiler_chan
1949 .send(mem::ProfilerMsg::Report(sender));
1950 },
1951 ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result) => {
1952 self.handle_finish_javascript_evaluation(evaluation_id, result)
1953 },
1954 ScriptToConstellationMessage::ForwardKeyboardScroll(pipeline_id, scroll) => {
1955 if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
1956 if let Err(error) =
1957 pipeline
1958 .event_loop
1959 .send(ScriptThreadMessage::ForwardKeyboardScroll(
1960 pipeline_id,
1961 scroll,
1962 ))
1963 {
1964 warn!("Could not forward {scroll:?} to {pipeline_id}: {error:?}");
1965 }
1966 }
1967 },
1968 ScriptToConstellationMessage::RespondToScreenshotReadinessRequest(response) => {
1969 self.handle_screenshot_readiness_response(source_pipeline_id, response);
1970 },
1971 }
1972 }
1973
1974 fn check_origin_against_pipeline(
1978 &self,
1979 pipeline_id: &PipelineId,
1980 origin: &ImmutableOrigin,
1981 ) -> Result<(), ()> {
1982 let pipeline_origin = match self.pipelines.get(pipeline_id) {
1983 Some(pipeline) => pipeline.load_data.url.origin(),
1984 None => {
1985 warn!("Received message from closed or unknown pipeline.");
1986 return Err(());
1987 },
1988 };
1989 if &pipeline_origin == origin {
1990 return Ok(());
1991 }
1992 Err(())
1993 }
1994
1995 #[servo_tracing::instrument(skip_all)]
1996 #[cfg(feature = "webgpu")]
1997 fn handle_wgpu_request(
1998 &mut self,
1999 source_pipeline_id: PipelineId,
2000 browsing_context_id: BrowsingContextId,
2001 request: ScriptToConstellationMessage,
2002 ) {
2003 use webgpu::start_webgpu_thread;
2004
2005 let browsing_context_group_id = match self.browsing_contexts.get(&browsing_context_id) {
2006 Some(bc) => &bc.bc_group_id,
2007 None => return warn!("Browsing context not found"),
2008 };
2009 let Some(source_pipeline) = self.pipelines.get(&source_pipeline_id) else {
2010 return warn!("{source_pipeline_id}: ScriptMsg from closed pipeline");
2011 };
2012 let Some(host) = reg_host(&source_pipeline.url) else {
2013 return warn!("Invalid host url");
2014 };
2015 let browsing_context_group = if let Some(bcg) = self
2016 .browsing_context_group_set
2017 .get_mut(browsing_context_group_id)
2018 {
2019 bcg
2020 } else {
2021 return warn!("Browsing context group not found");
2022 };
2023 let webgpu_chan = match browsing_context_group.webgpus.entry(host) {
2024 Entry::Vacant(v) => start_webgpu_thread(
2025 self.compositor_proxy.cross_process_compositor_api.clone(),
2026 self.webrender_wgpu.webrender_external_images.clone(),
2027 self.webrender_wgpu.wgpu_image_map.clone(),
2028 )
2029 .map(|webgpu| {
2030 let msg = ScriptThreadMessage::SetWebGPUPort(webgpu.1);
2031 if let Err(e) = source_pipeline.event_loop.send(msg) {
2032 warn!(
2033 "{}: Failed to send SetWebGPUPort to pipeline ({:?})",
2034 source_pipeline_id, e
2035 );
2036 }
2037 v.insert(webgpu.0).clone()
2038 }),
2039 Entry::Occupied(o) => Some(o.get().clone()),
2040 };
2041 match request {
2042 ScriptToConstellationMessage::RequestAdapter(response_sender, options, adapter_id) => {
2043 match webgpu_chan {
2044 None => {
2045 if let Err(e) = response_sender.send(None) {
2046 warn!("Failed to send request adapter message: {}", e)
2047 }
2048 },
2049 Some(webgpu_chan) => {
2050 let adapter_request = WebGPURequest::RequestAdapter {
2051 sender: response_sender,
2052 options,
2053 adapter_id,
2054 };
2055 if webgpu_chan.0.send(adapter_request).is_err() {
2056 warn!("Failed to send request adapter message on WebGPU channel");
2057 }
2058 },
2059 }
2060 },
2061 ScriptToConstellationMessage::GetWebGPUChan(response_sender) => {
2062 if response_sender.send(webgpu_chan).is_err() {
2063 warn!(
2064 "{}: Failed to send WebGPU channel to pipeline",
2065 source_pipeline_id
2066 )
2067 }
2068 },
2069 _ => warn!("Wrong message type in handle_wgpu_request"),
2070 }
2071 }
2072
2073 #[servo_tracing::instrument(skip_all)]
2074 fn handle_message_port_transfer_completed(
2075 &mut self,
2076 router_id: Option<MessagePortRouterId>,
2077 ports: Vec<MessagePortId>,
2078 ) {
2079 let Some(router_id) = router_id else {
2080 if !ports.is_empty() {
2081 warn!(
2082 "Constellation unable to process port transfer successes, since no router id was received"
2083 );
2084 }
2085 return;
2086 };
2087 for port_id in ports.into_iter() {
2088 let mut entry = match self.message_ports.entry(port_id) {
2089 Entry::Vacant(_) => {
2090 warn!(
2091 "Constellation received a port transfer completed msg for unknown messageport {port_id:?}",
2092 );
2093 continue;
2094 },
2095 Entry::Occupied(entry) => entry,
2096 };
2097 match entry.get().state {
2098 TransferState::CompletionInProgress(expected_router_id) => {
2099 if expected_router_id != router_id {
2102 return warn!(
2103 "Transfer completed by an unexpected router: {:?}",
2104 router_id
2105 );
2106 }
2107 let new_info = MessagePortInfo {
2109 state: TransferState::Managed(router_id),
2110 entangled_with: entry.get().entangled_with,
2111 };
2112 entry.insert(new_info);
2113 },
2114 _ => warn!("Constellation received unexpected port transfer completed message"),
2115 }
2116 }
2117 }
2118
2119 fn handle_message_port_transfer_failed(
2120 &mut self,
2121 ports: FxHashMap<MessagePortId, PortTransferInfo>,
2122 ) {
2123 for (port_id, mut transfer_info) in ports.into_iter() {
2124 let Some(entry) = self.message_ports.remove(&port_id) else {
2125 warn!(
2126 "Constellation received a port transfer completed msg for unknown messageport {port_id:?}",
2127 );
2128 continue;
2129 };
2130 let new_info = match entry.state {
2131 TransferState::CompletionFailed(mut current_buffer) => {
2132 while let Some(task) = transfer_info.port_message_queue.pop_back() {
2139 current_buffer.push_front(task);
2140 }
2141 MessagePortInfo {
2143 state: TransferState::TransferInProgress(current_buffer),
2144 entangled_with: entry.entangled_with,
2145 }
2146 },
2147 TransferState::CompletionRequested(target_router_id, mut current_buffer) => {
2148 while let Some(task) = transfer_info.port_message_queue.pop_back() {
2158 current_buffer.push_front(task);
2159 }
2160 if let Some(ipc_sender) = self.message_port_routers.get(&target_router_id) {
2162 if ipc_sender
2163 .send(MessagePortMsg::CompletePendingTransfer(
2164 port_id,
2165 PortTransferInfo {
2166 port_message_queue: current_buffer,
2167 disentangled: entry.entangled_with.is_none(),
2168 },
2169 ))
2170 .is_err()
2171 {
2172 warn!("Constellation failed to send complete port transfer response.");
2173 }
2174 } else {
2175 warn!("No message-port sender for {:?}", target_router_id);
2176 }
2177
2178 MessagePortInfo {
2180 state: TransferState::CompletionInProgress(target_router_id),
2181 entangled_with: entry.entangled_with,
2182 }
2183 },
2184 _ => {
2185 warn!("Unexpected port transfer failed message received");
2186 continue;
2187 },
2188 };
2189 self.message_ports.insert(port_id, new_info);
2190 }
2191 }
2192
2193 #[servo_tracing::instrument(skip_all)]
2194 fn handle_complete_message_port_transfer(
2195 &mut self,
2196 router_id: MessagePortRouterId,
2197 ports: Vec<MessagePortId>,
2198 ) {
2199 let mut response = FxHashMap::default();
2200 for port_id in ports.into_iter() {
2201 let Some(entry) = self.message_ports.remove(&port_id) else {
2202 warn!(
2203 "Constellation asked to complete transfer for unknown messageport {port_id:?}",
2204 );
2205 continue;
2206 };
2207 let new_info = match entry.state {
2208 TransferState::TransferInProgress(buffer) => {
2209 response.insert(
2210 port_id,
2211 PortTransferInfo {
2212 port_message_queue: buffer,
2213 disentangled: entry.entangled_with.is_none(),
2214 },
2215 );
2216
2217 MessagePortInfo {
2220 state: TransferState::CompletionInProgress(router_id),
2221 entangled_with: entry.entangled_with,
2222 }
2223 },
2224 TransferState::CompletionFailed(buffer) |
2225 TransferState::CompletionRequested(_, buffer) => {
2226 MessagePortInfo {
2239 state: TransferState::CompletionRequested(router_id, buffer),
2240 entangled_with: entry.entangled_with,
2241 }
2242 },
2243 _ => {
2244 warn!("Unexpected complete port transfer message received");
2245 continue;
2246 },
2247 };
2248 self.message_ports.insert(port_id, new_info);
2249 }
2250
2251 if !response.is_empty() {
2252 if let Some(ipc_sender) = self.message_port_routers.get(&router_id) {
2254 if ipc_sender
2255 .send(MessagePortMsg::CompleteTransfer(response))
2256 .is_err()
2257 {
2258 warn!("Constellation failed to send complete port transfer response.");
2259 }
2260 } else {
2261 warn!("No message-port sender for {:?}", router_id);
2262 }
2263 }
2264 }
2265
2266 #[servo_tracing::instrument(skip_all)]
2267 fn handle_reroute_messageport(&mut self, port_id: MessagePortId, task: PortMessageTask) {
2268 let Some(info) = self.message_ports.get_mut(&port_id) else {
2269 return warn!(
2270 "Constellation asked to re-route msg to unknown messageport {:?}",
2271 port_id
2272 );
2273 };
2274 match &mut info.state {
2275 TransferState::Managed(router_id) | TransferState::CompletionInProgress(router_id) => {
2276 if let Some(ipc_sender) = self.message_port_routers.get(router_id) {
2280 let _ = ipc_sender.send(MessagePortMsg::NewTask(port_id, task));
2281 } else {
2282 warn!("No message-port sender for {:?}", router_id);
2283 }
2284 },
2285 TransferState::TransferInProgress(queue) => queue.push_back(task),
2286 TransferState::CompletionFailed(queue) => queue.push_back(task),
2287 TransferState::CompletionRequested(_, queue) => queue.push_back(task),
2288 }
2289 }
2290
2291 #[servo_tracing::instrument(skip_all)]
2292 fn handle_messageport_shipped(&mut self, port_id: MessagePortId) {
2293 if let Some(info) = self.message_ports.get_mut(&port_id) {
2294 match info.state {
2295 TransferState::Managed(_) => {
2296 info.state = TransferState::TransferInProgress(VecDeque::new());
2298 },
2299 TransferState::CompletionInProgress(_) => {
2300 info.state = TransferState::CompletionFailed(VecDeque::new());
2305 },
2306 _ => warn!("Unexpected messageport shipped received"),
2307 }
2308 } else {
2309 warn!(
2310 "Constellation asked to mark unknown messageport as shipped {:?}",
2311 port_id
2312 );
2313 }
2314 }
2315
2316 fn handle_new_messageport_router(
2317 &mut self,
2318 router_id: MessagePortRouterId,
2319 message_port_ipc_sender: IpcSender<MessagePortMsg>,
2320 ) {
2321 self.message_port_routers
2322 .insert(router_id, message_port_ipc_sender);
2323 }
2324
2325 fn handle_remove_messageport_router(&mut self, router_id: MessagePortRouterId) {
2326 self.message_port_routers.remove(&router_id);
2327 }
2328
2329 fn handle_new_messageport(&mut self, router_id: MessagePortRouterId, port_id: MessagePortId) {
2330 match self.message_ports.entry(port_id) {
2331 Entry::Occupied(_) => warn!(
2333 "Constellation asked to start tracking an existing messageport {:?}",
2334 port_id
2335 ),
2336 Entry::Vacant(entry) => {
2337 let info = MessagePortInfo {
2338 state: TransferState::Managed(router_id),
2339 entangled_with: None,
2340 };
2341 entry.insert(info);
2342 },
2343 }
2344 }
2345
2346 #[servo_tracing::instrument(skip_all)]
2347 fn handle_entangle_messageports(&mut self, port1: MessagePortId, port2: MessagePortId) {
2348 if let Some(info) = self.message_ports.get_mut(&port1) {
2349 info.entangled_with = Some(port2);
2350 } else {
2351 warn!(
2352 "Constellation asked to entangle unknown messageport: {:?}",
2353 port1
2354 );
2355 }
2356 if let Some(info) = self.message_ports.get_mut(&port2) {
2357 info.entangled_with = Some(port1);
2358 } else {
2359 warn!(
2360 "Constellation asked to entangle unknown messageport: {:?}",
2361 port2
2362 );
2363 }
2364 }
2365
2366 #[servo_tracing::instrument(skip_all)]
2367 fn handle_disentangle_messageports(
2369 &mut self,
2370 port1: MessagePortId,
2371 port2: Option<MessagePortId>,
2372 ) {
2373 let _ = self.message_ports.remove(&port1);
2379
2380 let Some(port2) = port2 else {
2383 return;
2384 };
2385
2386 if let Some(info) = self.message_ports.get_mut(&port2) {
2388 info.entangled_with = None;
2389 match &mut info.state {
2390 TransferState::Managed(router_id) |
2391 TransferState::CompletionInProgress(router_id) => {
2392 if let Some(ipc_sender) = self.message_port_routers.get(router_id) {
2397 let _ = ipc_sender.send(MessagePortMsg::CompleteDisentanglement(port2));
2398 } else {
2399 warn!("No message-port sender for {:?}", router_id);
2400 }
2401 },
2402 _ => {
2403 },
2405 }
2406 } else {
2407 warn!(
2408 "Constellation asked to disentangle unknown messageport: {:?}",
2409 port2
2410 );
2411 }
2412 }
2413
2414 #[servo_tracing::instrument(skip_all)]
2421 fn handle_schedule_serviceworker_job(&mut self, pipeline_id: PipelineId, job: Job) {
2422 let origin = job.scope_url.origin();
2423
2424 if self
2425 .check_origin_against_pipeline(&pipeline_id, &origin)
2426 .is_err()
2427 {
2428 return warn!(
2429 "Attempt to schedule a serviceworker job from an origin not matching the origin of the job."
2430 );
2431 }
2432
2433 let sw_manager = match self.sw_managers.entry(origin.clone()) {
2435 Entry::Occupied(entry) => entry.into_mut(),
2436 Entry::Vacant(entry) => {
2437 let (own_sender, receiver) =
2438 generic_channel::channel().expect("Failed to create IPC channel!");
2439
2440 let sw_senders = SWManagerSenders {
2441 swmanager_sender: self.swmanager_ipc_sender.clone(),
2442 resource_threads: self.public_resource_threads.clone(),
2443 own_sender: own_sender.clone(),
2444 receiver,
2445 compositor_api: self.compositor_proxy.cross_process_compositor_api.clone(),
2446 system_font_service_sender: self.system_font_service.to_sender(),
2447 };
2448
2449 if opts::get().multiprocess {
2450 let (sender, receiver) =
2451 ipc::channel().expect("Failed to create lifeline channel for sw");
2452 let content =
2453 ServiceWorkerUnprivilegedContent::new(sw_senders, origin, Some(sender));
2454
2455 if let Ok(process) = content.spawn_multiprocess() {
2456 let crossbeam_receiver =
2457 route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors(
2458 receiver,
2459 );
2460 self.process_manager.add(crossbeam_receiver, process);
2461 } else {
2462 return warn!("Failed to spawn process for SW manager.");
2463 }
2464 } else {
2465 let content = ServiceWorkerUnprivilegedContent::new(sw_senders, origin, None);
2466 content.start::<SWF>();
2467 }
2468 entry.insert(own_sender)
2469 },
2470 };
2471 let _ = sw_manager.send(ServiceWorkerMsg::ScheduleJob(job));
2472 }
2473
2474 #[servo_tracing::instrument(skip_all)]
2475 fn handle_broadcast_storage_event(
2476 &self,
2477 pipeline_id: PipelineId,
2478 storage: StorageType,
2479 url: ServoUrl,
2480 key: Option<String>,
2481 old_value: Option<String>,
2482 new_value: Option<String>,
2483 ) {
2484 let origin = url.origin();
2485 for pipeline in self.pipelines.values() {
2486 if (pipeline.id != pipeline_id) && (pipeline.url.origin() == origin) {
2487 let msg = ScriptThreadMessage::DispatchStorageEvent(
2488 pipeline.id,
2489 storage,
2490 url.clone(),
2491 key.clone(),
2492 old_value.clone(),
2493 new_value.clone(),
2494 );
2495 if let Err(err) = pipeline.event_loop.send(msg) {
2496 warn!(
2497 "{}: Failed to broadcast storage event to pipeline ({:?}).",
2498 pipeline.id, err
2499 );
2500 }
2501 }
2502 }
2503 }
2504
2505 #[servo_tracing::instrument(skip_all)]
2506 fn handle_exit(&mut self) {
2507 debug!("Handling exit.");
2508
2509 if self.shutting_down {
2511 return;
2512 }
2513 self.shutting_down = true;
2514
2515 self.mem_profiler_chan.send(mem::ProfilerMsg::Exit);
2516
2517 for chan in &self.background_monitor_control_senders {
2522 if let Err(e) = chan.send(BackgroundHangMonitorControlMsg::Exit) {
2526 warn!("error communicating with bhm: {}", e);
2527 continue;
2528 }
2529 }
2530
2531 let browsing_context_ids: Vec<BrowsingContextId> = self
2533 .browsing_contexts
2534 .values()
2535 .filter(|browsing_context| browsing_context.is_top_level())
2536 .map(|browsing_context| browsing_context.id)
2537 .collect();
2538 for browsing_context_id in browsing_context_ids {
2539 debug!(
2540 "{}: Removing top-level browsing context",
2541 browsing_context_id
2542 );
2543 self.close_browsing_context(browsing_context_id, ExitPipelineMode::Normal);
2544 }
2545
2546 while let Some(pending) = self.pending_changes.pop() {
2548 debug!(
2549 "{}: Removing pending browsing context",
2550 pending.browsing_context_id
2551 );
2552 self.close_browsing_context(pending.browsing_context_id, ExitPipelineMode::Normal);
2553 debug!("{}: Removing pending pipeline", pending.new_pipeline_id);
2554 self.close_pipeline(
2555 pending.new_pipeline_id,
2556 DiscardBrowsingContext::Yes,
2557 ExitPipelineMode::Normal,
2558 );
2559 }
2560
2561 let browsing_context_ids: Vec<BrowsingContextId> =
2563 self.browsing_contexts.keys().cloned().collect();
2564 for browsing_context_id in browsing_context_ids {
2565 debug!(
2566 "{}: Removing detached browsing context",
2567 browsing_context_id
2568 );
2569 self.close_browsing_context(browsing_context_id, ExitPipelineMode::Normal);
2570 }
2571
2572 let pipeline_ids: Vec<PipelineId> = self.pipelines.keys().cloned().collect();
2574 for pipeline_id in pipeline_ids {
2575 debug!("{}: Removing detached pipeline", pipeline_id);
2576 self.close_pipeline(
2577 pipeline_id,
2578 DiscardBrowsingContext::Yes,
2579 ExitPipelineMode::Normal,
2580 );
2581 }
2582 }
2583
2584 #[servo_tracing::instrument(skip_all)]
2585 fn handle_shutdown(&mut self) {
2586 debug!("Handling shutdown.");
2587
2588 for (_, join_handle) in self.script_join_handles.drain() {
2591 if join_handle.join().is_err() {
2592 error!("Failed to join on a script-thread.");
2593 }
2594 }
2595
2596 drop(self.background_monitor_register.take());
2598 if let Some(join_handle) = self.background_monitor_register_join_handle.take() {
2599 if join_handle.join().is_err() {
2600 error!("Failed to join on the bhm background thread.");
2601 }
2602 }
2603
2604 let (core_ipc_sender, core_ipc_receiver) =
2608 ipc::channel().expect("Failed to create IPC channel!");
2609 let (storage_ipc_sender, storage_ipc_receiver) =
2610 generic_channel::channel().expect("Failed to create IPC channel!");
2611 let mut webgl_threads_receiver = None;
2612
2613 debug!("Exiting core resource threads.");
2614 if let Err(e) = self
2615 .public_resource_threads
2616 .send(net_traits::CoreResourceMsg::Exit(core_ipc_sender))
2617 {
2618 warn!("Exit resource thread failed ({})", e);
2619 }
2620
2621 if let Some(ref chan) = self.devtools_sender {
2622 debug!("Exiting devtools.");
2623 let msg = DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::ServerExitMsg);
2624 if let Err(e) = chan.send(msg) {
2625 warn!("Exit devtools failed ({:?})", e);
2626 }
2627 }
2628
2629 debug!("Exiting storage resource threads.");
2630 if let Err(e) = generic_channel::GenericSend::send(
2631 &self.public_storage_threads,
2632 WebStorageThreadMsg::Exit(storage_ipc_sender),
2633 ) {
2634 warn!("Exit storage thread failed ({})", e);
2635 }
2636
2637 #[cfg(feature = "bluetooth")]
2638 {
2639 debug!("Exiting bluetooth thread.");
2640 if let Err(e) = self.bluetooth_ipc_sender.send(BluetoothRequest::Exit) {
2641 warn!("Exit bluetooth thread failed ({})", e);
2642 }
2643 }
2644
2645 debug!("Exiting service worker manager thread.");
2646 for (_, mgr) in self.sw_managers.drain() {
2647 if let Err(e) = mgr.send(ServiceWorkerMsg::Exit) {
2648 warn!("Exit service worker manager failed ({})", e);
2649 }
2650 }
2651
2652 let canvas_exit_receiver = if let Some((canvas_sender, _)) = self.canvas.get() {
2653 debug!("Exiting Canvas Paint thread.");
2654 let (canvas_exit_sender, canvas_exit_receiver) = unbounded();
2655 if let Err(e) = canvas_sender.send(ConstellationCanvasMsg::Exit(canvas_exit_sender)) {
2656 warn!("Exit Canvas Paint thread failed ({})", e);
2657 }
2658 Some(canvas_exit_receiver)
2659 } else {
2660 None
2661 };
2662
2663 debug!("Exiting WebGPU threads.");
2664 #[cfg(feature = "webgpu")]
2665 let receivers = self
2666 .browsing_context_group_set
2667 .values()
2668 .flat_map(|browsing_context_group| {
2669 browsing_context_group.webgpus.values().map(|webgpu| {
2670 let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
2671 if let Err(e) = webgpu.exit(sender) {
2672 warn!("Exit WebGPU Thread failed ({})", e);
2673 None
2674 } else {
2675 Some(receiver)
2676 }
2677 })
2678 })
2679 .flatten();
2680
2681 #[cfg(feature = "webgpu")]
2682 for receiver in receivers {
2683 if let Err(e) = receiver.recv() {
2684 warn!("Failed to receive exit response from WebGPU ({:?})", e);
2685 }
2686 }
2687
2688 if let Some(webgl_threads) = self.webgl_threads.as_ref() {
2689 let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
2690 webgl_threads_receiver = Some(receiver);
2691 debug!("Exiting WebGL thread.");
2692
2693 if let Err(e) = webgl_threads.exit(sender) {
2694 warn!("Exit WebGL Thread failed ({e})");
2695 }
2696 }
2697
2698 debug!("Exiting GLPlayer thread.");
2699 WindowGLContext::get().exit();
2700
2701 if let Some(canvas_exit_receiver) = canvas_exit_receiver {
2704 let _ = canvas_exit_receiver.recv();
2705 }
2706
2707 debug!("Exiting the system font service thread.");
2708 self.system_font_service.exit();
2709
2710 if let Err(e) = core_ipc_receiver.recv() {
2712 warn!("Exit resource thread failed ({:?})", e);
2713 }
2714 if let Err(e) = storage_ipc_receiver.recv() {
2715 warn!("Exit storage thread failed ({:?})", e);
2716 }
2717 if self.webgl_threads.is_some() {
2718 if let Err(e) = webgl_threads_receiver
2719 .expect("webgl_threads_receiver to be Some")
2720 .recv()
2721 {
2722 warn!("Exit WebGL thread failed ({:?})", e);
2723 }
2724 }
2725
2726 debug!("Shutting-down IPC router thread in constellation.");
2727 ROUTER.shutdown();
2728
2729 debug!("Shutting-down the async runtime in constellation.");
2730 self.async_runtime.shutdown();
2731 }
2732
2733 fn handle_pipeline_exited(&mut self, pipeline_id: PipelineId) {
2734 debug!("{}: Exited", pipeline_id);
2735 let Some(pipeline) = self.pipelines.remove(&pipeline_id) else {
2736 return;
2737 };
2738
2739 self.compositor_proxy.send(CompositorMsg::PipelineExited(
2743 pipeline.webview_id,
2744 pipeline.id,
2745 PipelineExitSource::Constellation,
2746 ));
2747 }
2748
2749 #[servo_tracing::instrument(skip_all)]
2750 fn handle_send_error(&mut self, pipeline_id: PipelineId, err: IpcError) {
2751 error!("{}: Send error ({})", pipeline_id, err);
2753 let webview_id = self
2754 .pipelines
2755 .get(&pipeline_id)
2756 .map(|pipeline| pipeline.webview_id);
2757 let reason = format!("Send failed ({})", err);
2758 self.handle_panic(webview_id, reason, None);
2759 }
2760
2761 #[servo_tracing::instrument(skip_all)]
2762 fn handle_panic(
2763 &mut self,
2764 webview_id: Option<WebViewId>,
2765 reason: String,
2766 backtrace: Option<String>,
2767 ) {
2768 if self.hard_fail {
2769 println!("Pipeline failed in hard-fail mode. Crashing!");
2772 process::exit(1);
2773 }
2774
2775 let Some(webview_id) = webview_id else {
2776 return;
2777 };
2778
2779 debug!(
2780 "{}: Panic handler for top-level browsing context: {}",
2781 webview_id, reason
2782 );
2783
2784 let browsing_context_id = BrowsingContextId::from(webview_id);
2785
2786 self.embedder_proxy.send(EmbedderMsg::Panic(
2787 webview_id,
2788 reason.clone(),
2789 backtrace.clone(),
2790 ));
2791
2792 let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) else {
2793 return warn!("failed browsing context is missing");
2794 };
2795 let viewport_details = browsing_context.viewport_details;
2796 let pipeline_id = browsing_context.pipeline_id;
2797 let throttled = browsing_context.throttled;
2798
2799 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
2800 return warn!("failed pipeline is missing");
2801 };
2802 let opener = pipeline.opener;
2803
2804 self.close_browsing_context_children(
2805 browsing_context_id,
2806 DiscardBrowsingContext::No,
2807 ExitPipelineMode::Force,
2808 );
2809
2810 let old_pipeline_id = pipeline_id;
2811 let Some(old_load_data) = self.refresh_load_data(pipeline_id) else {
2812 return warn!("failed pipeline is missing");
2813 };
2814 if old_load_data.crash.is_some() {
2815 return error!("crash page crashed");
2816 }
2817
2818 warn!("creating replacement pipeline for crash page");
2819
2820 let new_pipeline_id = PipelineId::new();
2821 let new_load_data = LoadData {
2822 crash: Some(
2823 backtrace
2824 .map(|b| format!("{}\n{}", reason, b))
2825 .unwrap_or(reason),
2826 ),
2827 creation_sandboxing_flag_set: SandboxingFlagSet::all(),
2828 ..old_load_data.clone()
2829 };
2830
2831 let is_private = false;
2832 self.new_pipeline(
2833 new_pipeline_id,
2834 browsing_context_id,
2835 webview_id,
2836 None,
2837 opener,
2838 viewport_details,
2839 new_load_data,
2840 is_private,
2841 throttled,
2842 );
2843 self.add_pending_change(SessionHistoryChange {
2844 webview_id,
2845 browsing_context_id,
2846 new_pipeline_id,
2847 replace: Some(NeedsToReload::Yes(old_pipeline_id, old_load_data)),
2850 new_browsing_context_info: None,
2851 viewport_details,
2852 });
2853 }
2854
2855 #[servo_tracing::instrument(skip_all)]
2856 fn handle_focus_web_view(&mut self, webview_id: WebViewId) {
2857 let focused = self.webviews.focus(webview_id).is_ok();
2858 if !focused {
2859 warn!("{webview_id}: FocusWebView on unknown top-level browsing context");
2860 }
2861 self.embedder_proxy
2862 .send(EmbedderMsg::WebViewFocused(webview_id, focused));
2863 }
2864
2865 #[servo_tracing::instrument(skip_all)]
2866 fn handle_log_entry(
2867 &mut self,
2868 webview_id: Option<WebViewId>,
2869 thread_name: Option<String>,
2870 entry: LogEntry,
2871 ) {
2872 if let LogEntry::Panic(ref reason, ref backtrace) = entry {
2873 self.handle_panic(webview_id, reason.clone(), Some(backtrace.clone()));
2874 }
2875
2876 match entry {
2877 LogEntry::Panic(reason, _) | LogEntry::Error(reason) | LogEntry::Warn(reason) => {
2878 if WARNINGS_BUFFER_SIZE <= self.handled_warnings.len() {
2880 self.handled_warnings.pop_front();
2881 }
2882 self.handled_warnings.push_back((thread_name, reason));
2883 },
2884 }
2885 }
2886
2887 fn update_pressed_mouse_buttons(&mut self, event: &MouseButtonEvent) {
2888 let button_as_bitmask = match event.button {
2892 MouseButton::Left => 1,
2893 MouseButton::Right => 2,
2894 MouseButton::Middle => 4,
2895 MouseButton::Back => 8,
2896 MouseButton::Forward => 16,
2897 MouseButton::Other(_) => return,
2898 };
2899
2900 match event.action {
2901 MouseButtonAction::Down => {
2902 self.pressed_mouse_buttons |= button_as_bitmask;
2903 },
2904 MouseButtonAction::Up => {
2905 self.pressed_mouse_buttons &= !(button_as_bitmask);
2906 },
2907 }
2908 }
2909
2910 #[allow(deprecated)]
2911 fn update_active_keybord_modifiers(&mut self, event: &KeyboardEvent) {
2912 self.active_keyboard_modifiers = event.event.modifiers;
2913
2914 let Key::Named(named_key) = event.event.key else {
2919 return;
2920 };
2921
2922 let modified_modifier = match named_key {
2923 NamedKey::Alt => Modifiers::ALT,
2924 NamedKey::AltGraph => Modifiers::ALT_GRAPH,
2925 NamedKey::CapsLock => Modifiers::CAPS_LOCK,
2926 NamedKey::Control => Modifiers::CONTROL,
2927 NamedKey::Fn => Modifiers::FN,
2928 NamedKey::FnLock => Modifiers::FN_LOCK,
2929 NamedKey::Meta => Modifiers::META,
2930 NamedKey::NumLock => Modifiers::NUM_LOCK,
2931 NamedKey::ScrollLock => Modifiers::SCROLL_LOCK,
2932 NamedKey::Shift => Modifiers::SHIFT,
2933 NamedKey::Symbol => Modifiers::SYMBOL,
2934 NamedKey::SymbolLock => Modifiers::SYMBOL_LOCK,
2935 NamedKey::Hyper => Modifiers::HYPER,
2936 NamedKey::Super => Modifiers::META,
2939 _ => return,
2940 };
2941 match event.event.state {
2942 KeyState::Down => self.active_keyboard_modifiers.insert(modified_modifier),
2943 KeyState::Up => self.active_keyboard_modifiers.remove(modified_modifier),
2944 }
2945 }
2946
2947 fn forward_input_event(
2948 &mut self,
2949 webview_id: WebViewId,
2950 event: InputEventAndId,
2951 hit_test_result: Option<CompositorHitTestResult>,
2952 ) {
2953 if let InputEvent::MouseButton(event) = &event.event {
2954 self.update_pressed_mouse_buttons(event);
2955 }
2956
2957 if let InputEvent::Keyboard(event) = &event.event {
2958 self.update_active_keybord_modifiers(event);
2959 }
2960
2961 let pressed_mouse_buttons = self.pressed_mouse_buttons;
2964 let active_keyboard_modifiers = self.active_keyboard_modifiers;
2965
2966 let event_id = event.id;
2967 let Some(webview) = self.webviews.get_mut(webview_id) else {
2968 warn!("Got input event for unknown WebViewId: {webview_id:?}");
2969 self.embedder_proxy.send(EmbedderMsg::InputEventHandled(
2970 webview_id,
2971 event_id,
2972 Default::default(),
2973 ));
2974 return;
2975 };
2976
2977 let event = ConstellationInputEvent {
2978 hit_test_result,
2979 pressed_mouse_buttons,
2980 active_keyboard_modifiers,
2981 event,
2982 };
2983
2984 if !webview.forward_input_event(event, &self.pipelines, &self.browsing_contexts) {
2985 self.embedder_proxy.send(EmbedderMsg::InputEventHandled(
2986 webview_id,
2987 event_id,
2988 Default::default(),
2989 ));
2990 }
2991 }
2992
2993 #[servo_tracing::instrument(skip_all)]
2994 fn handle_new_top_level_browsing_context(
2995 &mut self,
2996 url: ServoUrl,
2997 webview_id: WebViewId,
2998 viewport_details: ViewportDetails,
2999 ) {
3000 let pipeline_id = PipelineId::new();
3001 let browsing_context_id = BrowsingContextId::from(webview_id);
3002 let load_data = LoadData::new(
3003 LoadOrigin::Constellation,
3004 url,
3005 None,
3006 Referrer::NoReferrer,
3007 ReferrerPolicy::EmptyString,
3008 None,
3009 None,
3010 false,
3011 SandboxingFlagSet::empty(),
3012 );
3013 let is_private = false;
3014 let throttled = false;
3015
3016 self.webviews.add(
3019 webview_id,
3020 ConstellationWebView::new(webview_id, browsing_context_id),
3021 );
3022
3023 let mut new_bc_group: BrowsingContextGroup = Default::default();
3025 let new_bc_group_id = self.next_browsing_context_group_id();
3026 new_bc_group
3027 .top_level_browsing_context_set
3028 .insert(webview_id);
3029 self.browsing_context_group_set
3030 .insert(new_bc_group_id, new_bc_group);
3031
3032 self.new_pipeline(
3033 pipeline_id,
3034 browsing_context_id,
3035 webview_id,
3036 None,
3037 None,
3038 viewport_details,
3039 load_data,
3040 is_private,
3041 throttled,
3042 );
3043 self.add_pending_change(SessionHistoryChange {
3044 webview_id,
3045 browsing_context_id,
3046 new_pipeline_id: pipeline_id,
3047 replace: None,
3048 new_browsing_context_info: Some(NewBrowsingContextInfo {
3049 parent_pipeline_id: None,
3050 is_private,
3051 inherited_secure_context: None,
3052 throttled,
3053 }),
3054 viewport_details,
3055 });
3056
3057 let painter_id = PainterId::from(webview_id);
3058 self.system_font_service
3059 .prefetch_font_keys_for_painter(painter_id);
3060 }
3061
3062 #[servo_tracing::instrument(skip_all)]
3063 fn handle_close_top_level_browsing_context(&mut self, webview_id: WebViewId) {
3064 debug!("{webview_id}: Closing");
3065 let browsing_context_id = BrowsingContextId::from(webview_id);
3066 let browsing_context =
3067 self.close_browsing_context(browsing_context_id, ExitPipelineMode::Normal);
3068 if self.webviews.focused_webview().map(|(id, _)| id) == Some(webview_id) {
3069 self.embedder_proxy.send(EmbedderMsg::WebViewBlurred);
3070 }
3071 self.webviews.remove(webview_id);
3072 self.compositor_proxy
3073 .send(CompositorMsg::RemoveWebView(webview_id));
3074 self.embedder_proxy
3075 .send(EmbedderMsg::WebViewClosed(webview_id));
3076
3077 let Some(browsing_context) = browsing_context else {
3078 return warn!(
3079 "fn handle_close_top_level_browsing_context {}: Closing twice",
3080 browsing_context_id
3081 );
3082 };
3083 let bc_group_id = browsing_context.bc_group_id;
3085 let Some(bc_group) = self.browsing_context_group_set.get_mut(&bc_group_id) else {
3086 warn!("{}: Browsing context group not found!", bc_group_id);
3087 return;
3088 };
3089 if !bc_group.top_level_browsing_context_set.remove(&webview_id) {
3090 warn!("{webview_id}: Top-level browsing context not found in {bc_group_id}",);
3091 }
3092 if bc_group.top_level_browsing_context_set.is_empty() {
3093 self.browsing_context_group_set
3094 .remove(&browsing_context.bc_group_id);
3095 }
3096
3097 self.script_join_handles.remove(&webview_id);
3101
3102 debug!("{webview_id}: Closed");
3103 }
3104
3105 #[servo_tracing::instrument(skip_all)]
3106 fn handle_iframe_size_msg(&mut self, iframe_sizes: Vec<IFrameSizeMsg>) {
3107 for IFrameSizeMsg {
3108 browsing_context_id,
3109 size,
3110 type_,
3111 } in iframe_sizes
3112 {
3113 self.resize_browsing_context(size, type_, browsing_context_id);
3114 }
3115 }
3116
3117 #[servo_tracing::instrument(skip_all)]
3118 fn handle_finish_javascript_evaluation(
3119 &mut self,
3120 evaluation_id: JavaScriptEvaluationId,
3121 result: Result<JSValue, JavaScriptEvaluationError>,
3122 ) {
3123 self.embedder_proxy
3124 .send(EmbedderMsg::FinishJavaScriptEvaluation(
3125 evaluation_id,
3126 result,
3127 ));
3128 }
3129
3130 #[servo_tracing::instrument(skip_all)]
3131 fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId) {
3132 let browsing_context_id = match self.pipelines.get(&pipeline_id) {
3133 Some(pipeline) => pipeline.browsing_context_id,
3134 None => return warn!("{}: Subframe loaded after closure", pipeline_id),
3135 };
3136 let parent_pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
3137 Some(browsing_context) => browsing_context.parent_pipeline_id,
3138 None => {
3139 return warn!(
3140 "{}: Subframe loaded in closed {}",
3141 pipeline_id, browsing_context_id,
3142 );
3143 },
3144 };
3145 let Some(parent_pipeline_id) = parent_pipeline_id else {
3146 return warn!("{}: Subframe has no parent", pipeline_id);
3147 };
3148 let msg = ScriptThreadMessage::DispatchIFrameLoadEvent {
3152 target: browsing_context_id,
3153 parent: parent_pipeline_id,
3154 child: pipeline_id,
3155 };
3156 let result = match self.pipelines.get(&parent_pipeline_id) {
3157 Some(parent) => parent.event_loop.send(msg),
3158 None => {
3159 return warn!(
3160 "{}: Parent pipeline browsing context loaded after closure",
3161 parent_pipeline_id
3162 );
3163 },
3164 };
3165 if let Err(e) = result {
3166 self.handle_send_error(parent_pipeline_id, e);
3167 }
3168 }
3169
3170 #[servo_tracing::instrument(skip_all)]
3175 fn handle_script_loaded_url_in_iframe_msg(&mut self, load_info: IFrameLoadInfoWithData) {
3176 let IFrameLoadInfo {
3177 parent_pipeline_id,
3178 browsing_context_id,
3179 webview_id,
3180 new_pipeline_id,
3181 is_private,
3182 mut history_handling,
3183 ..
3184 } = load_info.info;
3185
3186 let old_pipeline = load_info
3188 .old_pipeline_id
3189 .and_then(|id| self.pipelines.get(&id));
3190
3191 if let Some(old_pipeline) = old_pipeline {
3194 if !old_pipeline.completely_loaded {
3195 history_handling = NavigationHistoryBehavior::Replace;
3196 }
3197 debug!(
3198 "{:?}: Old pipeline is {}completely loaded",
3199 load_info.old_pipeline_id,
3200 if old_pipeline.completely_loaded {
3201 ""
3202 } else {
3203 "not "
3204 }
3205 );
3206 }
3207
3208 let is_parent_private = {
3209 let parent_browsing_context_id = match self.pipelines.get(&parent_pipeline_id) {
3210 Some(pipeline) => pipeline.browsing_context_id,
3211 None => {
3212 return warn!(
3213 "{parent_pipeline_id}: Script loaded url in iframe \
3214 {browsing_context_id} in closed parent pipeline",
3215 );
3216 },
3217 };
3218
3219 let Some(ctx) = self.browsing_contexts.get(&parent_browsing_context_id) else {
3220 return warn!(
3221 "{parent_browsing_context_id}: Script loaded url in \
3222 iframe {browsing_context_id} in closed parent browsing context",
3223 );
3224 };
3225 ctx.is_private
3226 };
3227 let is_private = is_private || is_parent_private;
3228
3229 let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) else {
3230 return warn!(
3231 "{browsing_context_id}: Script loaded url in iframe with closed browsing context",
3232 );
3233 };
3234
3235 let replace = if history_handling == NavigationHistoryBehavior::Replace {
3236 Some(NeedsToReload::No(browsing_context.pipeline_id))
3237 } else {
3238 None
3239 };
3240
3241 let browsing_context_size = browsing_context.viewport_details;
3242 let browsing_context_throttled = browsing_context.throttled;
3243 #[cfg(debug_assertions)]
3245 if !(browsing_context_size == load_info.viewport_details) {
3246 log::warn!(
3247 "debug assertion failed! browsing_context_size == load_info.viewport_details.initial_viewport"
3248 );
3249 }
3250
3251 self.new_pipeline(
3253 new_pipeline_id,
3254 browsing_context_id,
3255 webview_id,
3256 Some(parent_pipeline_id),
3257 None,
3258 browsing_context_size,
3259 load_info.load_data,
3260 is_private,
3261 browsing_context_throttled,
3262 );
3263 self.add_pending_change(SessionHistoryChange {
3264 webview_id,
3265 browsing_context_id,
3266 new_pipeline_id,
3267 replace,
3268 new_browsing_context_info: None,
3270 viewport_details: load_info.viewport_details,
3271 });
3272 }
3273
3274 #[servo_tracing::instrument(skip_all)]
3275 fn handle_script_new_iframe(&mut self, load_info: IFrameLoadInfoWithData) {
3276 let IFrameLoadInfo {
3277 parent_pipeline_id,
3278 new_pipeline_id,
3279 browsing_context_id,
3280 webview_id,
3281 is_private,
3282 ..
3283 } = load_info.info;
3284
3285 let (script_sender, parent_browsing_context_id) =
3286 match self.pipelines.get(&parent_pipeline_id) {
3287 Some(pipeline) => (pipeline.event_loop.clone(), pipeline.browsing_context_id),
3288 None => {
3289 return warn!(
3290 "{}: Script loaded url in closed iframe pipeline",
3291 parent_pipeline_id
3292 );
3293 },
3294 };
3295 let (is_parent_private, is_parent_throttled, is_parent_secure) =
3296 match self.browsing_contexts.get(&parent_browsing_context_id) {
3297 Some(ctx) => (ctx.is_private, ctx.throttled, ctx.inherited_secure_context),
3298 None => {
3299 return warn!(
3300 "{}: New iframe {} loaded in closed parent browsing context",
3301 parent_browsing_context_id, browsing_context_id,
3302 );
3303 },
3304 };
3305 let is_private = is_private || is_parent_private;
3306 let pipeline = Pipeline::new(
3307 new_pipeline_id,
3308 browsing_context_id,
3309 webview_id,
3310 None,
3311 script_sender,
3312 self.compositor_proxy.clone(),
3313 is_parent_throttled,
3314 load_info.load_data,
3315 );
3316
3317 assert!(!self.pipelines.contains_key(&new_pipeline_id));
3318 self.pipelines.insert(new_pipeline_id, pipeline);
3319 self.add_pending_change(SessionHistoryChange {
3320 webview_id,
3321 browsing_context_id,
3322 new_pipeline_id,
3323 replace: None,
3324 new_browsing_context_info: Some(NewBrowsingContextInfo {
3326 parent_pipeline_id: Some(parent_pipeline_id),
3327 is_private,
3328 inherited_secure_context: is_parent_secure,
3329 throttled: is_parent_throttled,
3330 }),
3331 viewport_details: load_info.viewport_details,
3332 });
3333 }
3334
3335 #[servo_tracing::instrument(skip_all)]
3336 fn handle_script_new_auxiliary(&mut self, load_info: AuxiliaryWebViewCreationRequest) {
3337 let AuxiliaryWebViewCreationRequest {
3338 load_data,
3339 opener_webview_id,
3340 opener_pipeline_id,
3341 response_sender,
3342 } = load_info;
3343
3344 let Some((webview_id_sender, webview_id_receiver)) = generic_channel::channel() else {
3345 warn!("Failed to create channel");
3346 let _ = response_sender.send(None);
3347 return;
3348 };
3349 self.embedder_proxy.send(EmbedderMsg::AllowOpeningWebView(
3350 opener_webview_id,
3351 webview_id_sender,
3352 ));
3353 let (new_webview_id, viewport_details) = match webview_id_receiver.recv() {
3354 Ok(Some((webview_id, viewport_details))) => (webview_id, viewport_details),
3355 Ok(None) | Err(_) => {
3356 let _ = response_sender.send(None);
3357 return;
3358 },
3359 };
3360 let new_browsing_context_id = BrowsingContextId::from(new_webview_id);
3361
3362 let (script_sender, opener_browsing_context_id) =
3363 match self.pipelines.get(&opener_pipeline_id) {
3364 Some(pipeline) => (pipeline.event_loop.clone(), pipeline.browsing_context_id),
3365 None => {
3366 return warn!(
3367 "{}: Auxiliary loaded url in closed iframe pipeline",
3368 opener_pipeline_id
3369 );
3370 },
3371 };
3372 let (is_opener_private, is_opener_throttled, is_opener_secure) =
3373 match self.browsing_contexts.get(&opener_browsing_context_id) {
3374 Some(ctx) => (ctx.is_private, ctx.throttled, ctx.inherited_secure_context),
3375 None => {
3376 return warn!(
3377 "{}: New auxiliary {} loaded in closed opener browsing context",
3378 opener_browsing_context_id, new_browsing_context_id,
3379 );
3380 },
3381 };
3382 let new_pipeline_id = PipelineId::new();
3383 let pipeline = Pipeline::new(
3384 new_pipeline_id,
3385 new_browsing_context_id,
3386 new_webview_id,
3387 Some(opener_browsing_context_id),
3388 script_sender,
3389 self.compositor_proxy.clone(),
3390 is_opener_throttled,
3391 load_data,
3392 );
3393 let _ = response_sender.send(Some(AuxiliaryWebViewCreationResponse {
3394 new_webview_id,
3395 new_pipeline_id,
3396 }));
3397
3398 assert!(!self.pipelines.contains_key(&new_pipeline_id));
3399 self.pipelines.insert(new_pipeline_id, pipeline);
3400 self.webviews.add(
3401 new_webview_id,
3402 ConstellationWebView::new(new_webview_id, new_browsing_context_id),
3403 );
3404
3405 let Some(opener) = self.browsing_contexts.get(&opener_browsing_context_id) else {
3407 return warn!("Trying to append an unknown auxiliary to a browsing context group");
3408 };
3409 let Some(bc_group) = self.browsing_context_group_set.get_mut(&opener.bc_group_id) else {
3410 return warn!("Trying to add a top-level to an unknown group.");
3411 };
3412 bc_group
3413 .top_level_browsing_context_set
3414 .insert(new_webview_id);
3415
3416 self.add_pending_change(SessionHistoryChange {
3417 webview_id: new_webview_id,
3418 browsing_context_id: new_browsing_context_id,
3419 new_pipeline_id,
3420 replace: None,
3421 new_browsing_context_info: Some(NewBrowsingContextInfo {
3422 parent_pipeline_id: None,
3424 is_private: is_opener_private,
3425 inherited_secure_context: is_opener_secure,
3426 throttled: is_opener_throttled,
3427 }),
3428 viewport_details,
3429 });
3430 }
3431
3432 #[servo_tracing::instrument(skip_all)]
3433 fn handle_refresh_cursor(&self, pipeline_id: PipelineId) {
3434 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
3435 return;
3436 };
3437
3438 if let Err(error) = pipeline
3439 .event_loop
3440 .send(ScriptThreadMessage::RefreshCursor(pipeline_id))
3441 {
3442 warn!("Could not send RefreshCursor message to pipeline: {error:?}");
3443 }
3444 }
3445
3446 #[servo_tracing::instrument(skip_all)]
3447 fn handle_change_running_animations_state(
3448 &mut self,
3449 pipeline_id: PipelineId,
3450 animation_state: AnimationState,
3451 ) {
3452 if let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) {
3453 if pipeline.animation_state != animation_state {
3454 pipeline.animation_state = animation_state;
3455 self.compositor_proxy
3456 .send(CompositorMsg::ChangeRunningAnimationsState(
3457 pipeline.webview_id,
3458 pipeline_id,
3459 animation_state,
3460 ))
3461 }
3462 }
3463 }
3464
3465 #[servo_tracing::instrument(skip_all)]
3466 fn handle_tick_animation(&mut self, webview_ids: Vec<WebViewId>) {
3467 let mut animating_event_loops = HashSet::new();
3468
3469 for webview_id in webview_ids.iter() {
3470 for browsing_context in self.fully_active_browsing_contexts_iter(*webview_id) {
3471 let Some(pipeline) = self.pipelines.get(&browsing_context.pipeline_id) else {
3472 continue;
3473 };
3474 animating_event_loops.insert(pipeline.event_loop.clone());
3475 }
3476 }
3477
3478 for event_loop in animating_event_loops {
3479 let _ = event_loop.send(ScriptThreadMessage::TickAllAnimations(webview_ids.clone()));
3484 }
3485 }
3486
3487 #[servo_tracing::instrument(skip_all)]
3488 fn handle_no_longer_waiting_on_asynchronous_image_updates(
3489 &mut self,
3490 pipeline_ids: Vec<PipelineId>,
3491 ) {
3492 for pipeline_id in pipeline_ids.into_iter() {
3493 if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
3494 let _ = pipeline.event_loop.send(
3495 ScriptThreadMessage::NoLongerWaitingOnAsychronousImageUpdates(pipeline_id),
3496 );
3497 }
3498 }
3499 }
3500
3501 #[servo_tracing::instrument(skip_all)]
3505 fn schedule_navigation(
3506 &mut self,
3507 webview_id: WebViewId,
3508 source_id: PipelineId,
3509 load_data: LoadData,
3510 history_handling: NavigationHistoryBehavior,
3511 ) {
3512 match self.pending_approval_navigations.entry(source_id) {
3513 Entry::Occupied(_) => {
3514 return warn!(
3515 "{}: Tried to schedule a navigation while one is already pending",
3516 source_id
3517 );
3518 },
3519 Entry::Vacant(entry) => {
3520 let _ = entry.insert((load_data.clone(), history_handling));
3521 },
3522 };
3523 self.embedder_proxy
3525 .send(EmbedderMsg::AllowNavigationRequest(
3526 webview_id,
3527 source_id,
3528 load_data.url.clone(),
3529 ));
3530 }
3531
3532 #[servo_tracing::instrument(skip_all)]
3533 fn load_url(
3534 &mut self,
3535 webview_id: WebViewId,
3536 source_id: PipelineId,
3537 load_data: LoadData,
3538 history_handling: NavigationHistoryBehavior,
3539 ) -> Option<PipelineId> {
3540 debug!(
3541 "{}: Loading ({}replacing): {}",
3542 source_id,
3543 match history_handling {
3544 NavigationHistoryBehavior::Push => "",
3545 NavigationHistoryBehavior::Replace => "not ",
3546 NavigationHistoryBehavior::Auto => "unsure if ",
3547 },
3548 load_data.url,
3549 );
3550 let (browsing_context_id, opener) = match self.pipelines.get(&source_id) {
3557 Some(pipeline) => (pipeline.browsing_context_id, pipeline.opener),
3558 None => {
3559 warn!("{}: Loaded after closure", source_id);
3560 return None;
3561 },
3562 };
3563 let (viewport_details, pipeline_id, parent_pipeline_id, is_private, is_throttled) =
3564 match self.browsing_contexts.get(&browsing_context_id) {
3565 Some(ctx) => (
3566 ctx.viewport_details,
3567 ctx.pipeline_id,
3568 ctx.parent_pipeline_id,
3569 ctx.is_private,
3570 ctx.throttled,
3571 ),
3572 None => {
3573 warn!(
3578 "{}: Loaded url in closed {}",
3579 source_id, browsing_context_id,
3580 );
3581 return None;
3582 },
3583 };
3584
3585 if let Some(ref chan) = self.devtools_sender {
3586 let state = NavigationState::Start(load_data.url.clone());
3587 let _ = chan.send(DevtoolsControlMsg::FromScript(
3588 ScriptToDevtoolsControlMsg::Navigate(browsing_context_id, state),
3589 ));
3590 }
3591
3592 match parent_pipeline_id {
3593 Some(parent_pipeline_id) => {
3594 let msg = ScriptThreadMessage::NavigateIframe(
3597 parent_pipeline_id,
3598 browsing_context_id,
3599 load_data,
3600 history_handling,
3601 );
3602 let result = match self.pipelines.get(&parent_pipeline_id) {
3603 Some(parent_pipeline) => parent_pipeline.event_loop.send(msg),
3604 None => {
3605 warn!("{}: Child loaded after closure", parent_pipeline_id);
3606 return None;
3607 },
3608 };
3609 if let Err(e) = result {
3610 self.handle_send_error(parent_pipeline_id, e);
3611 } else if let Some((sender, id)) = &self.webdriver_load_status_sender {
3612 if source_id == *id {
3613 let _ = sender.send(WebDriverLoadStatus::NavigationStop);
3614 }
3615 }
3616
3617 None
3618 },
3619 None => {
3620 for change in &self.pending_changes {
3622 if change.browsing_context_id == browsing_context_id {
3623 return None;
3625 }
3626 }
3627
3628 if self.get_activity(source_id) == DocumentActivity::Inactive {
3629 return None;
3634 }
3635
3636 let replace = if history_handling == NavigationHistoryBehavior::Replace {
3642 Some(NeedsToReload::No(pipeline_id))
3643 } else {
3644 None
3645 };
3646
3647 let new_pipeline_id = PipelineId::new();
3648 self.new_pipeline(
3649 new_pipeline_id,
3650 browsing_context_id,
3651 webview_id,
3652 None,
3653 opener,
3654 viewport_details,
3655 load_data,
3656 is_private,
3657 is_throttled,
3658 );
3659 self.add_pending_change(SessionHistoryChange {
3660 webview_id,
3661 browsing_context_id,
3662 new_pipeline_id,
3663 replace,
3664 new_browsing_context_info: None,
3666 viewport_details,
3667 });
3668 Some(new_pipeline_id)
3669 },
3670 }
3671 }
3672
3673 #[servo_tracing::instrument(skip_all)]
3674 fn handle_abort_load_url_msg(&mut self, new_pipeline_id: PipelineId) {
3675 let pending_index = self
3676 .pending_changes
3677 .iter()
3678 .rposition(|change| change.new_pipeline_id == new_pipeline_id);
3679
3680 if let Some(pending_index) = pending_index {
3682 self.pending_changes.remove(pending_index);
3683 self.close_pipeline(
3684 new_pipeline_id,
3685 DiscardBrowsingContext::No,
3686 ExitPipelineMode::Normal,
3687 );
3688 }
3689
3690 self.send_screenshot_readiness_requests_to_pipelines();
3691 }
3692
3693 #[servo_tracing::instrument(skip_all)]
3694 fn handle_load_complete_msg(&mut self, webview_id: WebViewId, pipeline_id: PipelineId) {
3695 if let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) {
3696 debug!("{}: Marking as loaded", pipeline_id);
3697 pipeline.completely_loaded = true;
3698 }
3699
3700 let pipeline_is_top_level_pipeline = self
3705 .browsing_contexts
3706 .get(&BrowsingContextId::from(webview_id))
3707 .is_some_and(|ctx| ctx.pipeline_id == pipeline_id);
3708 if !pipeline_is_top_level_pipeline {
3709 self.handle_subframe_loaded(pipeline_id);
3710 }
3711 }
3712
3713 #[servo_tracing::instrument(skip_all)]
3714 fn handle_navigated_to_fragment(
3715 &mut self,
3716 pipeline_id: PipelineId,
3717 new_url: ServoUrl,
3718 history_handling: NavigationHistoryBehavior,
3719 ) {
3720 let (webview_id, old_url) = match self.pipelines.get_mut(&pipeline_id) {
3721 Some(pipeline) => {
3722 let old_url = replace(&mut pipeline.url, new_url.clone());
3723 (pipeline.webview_id, old_url)
3724 },
3725 None => {
3726 return warn!("{}: Navigated to fragment after closure", pipeline_id);
3727 },
3728 };
3729
3730 match history_handling {
3731 NavigationHistoryBehavior::Replace => {},
3732 _ => {
3733 let diff = SessionHistoryDiff::Hash {
3734 pipeline_reloader: NeedsToReload::No(pipeline_id),
3735 new_url,
3736 old_url,
3737 };
3738
3739 self.get_joint_session_history(webview_id).push_diff(diff);
3740
3741 self.notify_history_changed(webview_id);
3742 },
3743 }
3744 }
3745
3746 #[servo_tracing::instrument(skip_all)]
3747 fn handle_traverse_history_msg(
3748 &mut self,
3749 webview_id: WebViewId,
3750 direction: TraversalDirection,
3751 ) {
3752 let mut browsing_context_changes = FxHashMap::<BrowsingContextId, NeedsToReload>::default();
3753 let mut pipeline_changes =
3754 FxHashMap::<PipelineId, (Option<HistoryStateId>, ServoUrl)>::default();
3755 let mut url_to_load = FxHashMap::<PipelineId, ServoUrl>::default();
3756 {
3757 let session_history = self.get_joint_session_history(webview_id);
3758 match direction {
3759 TraversalDirection::Forward(forward) => {
3760 let future_length = session_history.future.len();
3761
3762 if future_length < forward {
3763 return warn!("Cannot traverse that far into the future.");
3764 }
3765
3766 for diff in session_history
3767 .future
3768 .drain(future_length - forward..)
3769 .rev()
3770 {
3771 match diff {
3772 SessionHistoryDiff::BrowsingContext {
3773 browsing_context_id,
3774 ref new_reloader,
3775 ..
3776 } => {
3777 browsing_context_changes
3778 .insert(browsing_context_id, new_reloader.clone());
3779 },
3780 SessionHistoryDiff::Pipeline {
3781 ref pipeline_reloader,
3782 new_history_state_id,
3783 ref new_url,
3784 ..
3785 } => match *pipeline_reloader {
3786 NeedsToReload::No(pipeline_id) => {
3787 pipeline_changes.insert(
3788 pipeline_id,
3789 (Some(new_history_state_id), new_url.clone()),
3790 );
3791 },
3792 NeedsToReload::Yes(pipeline_id, ..) => {
3793 url_to_load.insert(pipeline_id, new_url.clone());
3794 },
3795 },
3796 SessionHistoryDiff::Hash {
3797 ref pipeline_reloader,
3798 ref new_url,
3799 ..
3800 } => match *pipeline_reloader {
3801 NeedsToReload::No(pipeline_id) => {
3802 let state = pipeline_changes
3803 .get(&pipeline_id)
3804 .and_then(|change| change.0);
3805 pipeline_changes.insert(pipeline_id, (state, new_url.clone()));
3806 },
3807 NeedsToReload::Yes(pipeline_id, ..) => {
3808 url_to_load.insert(pipeline_id, new_url.clone());
3809 },
3810 },
3811 }
3812 session_history.past.push(diff);
3813 }
3814 },
3815 TraversalDirection::Back(back) => {
3816 let past_length = session_history.past.len();
3817
3818 if past_length < back {
3819 return warn!("Cannot traverse that far into the past.");
3820 }
3821
3822 for diff in session_history.past.drain(past_length - back..).rev() {
3823 match diff {
3824 SessionHistoryDiff::BrowsingContext {
3825 browsing_context_id,
3826 ref old_reloader,
3827 ..
3828 } => {
3829 browsing_context_changes
3830 .insert(browsing_context_id, old_reloader.clone());
3831 },
3832 SessionHistoryDiff::Pipeline {
3833 ref pipeline_reloader,
3834 old_history_state_id,
3835 ref old_url,
3836 ..
3837 } => match *pipeline_reloader {
3838 NeedsToReload::No(pipeline_id) => {
3839 pipeline_changes.insert(
3840 pipeline_id,
3841 (old_history_state_id, old_url.clone()),
3842 );
3843 },
3844 NeedsToReload::Yes(pipeline_id, ..) => {
3845 url_to_load.insert(pipeline_id, old_url.clone());
3846 },
3847 },
3848 SessionHistoryDiff::Hash {
3849 ref pipeline_reloader,
3850 ref old_url,
3851 ..
3852 } => match *pipeline_reloader {
3853 NeedsToReload::No(pipeline_id) => {
3854 let state = pipeline_changes
3855 .get(&pipeline_id)
3856 .and_then(|change| change.0);
3857 pipeline_changes.insert(pipeline_id, (state, old_url.clone()));
3858 },
3859 NeedsToReload::Yes(pipeline_id, ..) => {
3860 url_to_load.insert(pipeline_id, old_url.clone());
3861 },
3862 },
3863 }
3864 session_history.future.push(diff);
3865 }
3866 },
3867 }
3868 }
3869
3870 for (browsing_context_id, mut pipeline_reloader) in browsing_context_changes.drain() {
3871 if let NeedsToReload::Yes(pipeline_id, ref mut load_data) = pipeline_reloader {
3872 if let Some(url) = url_to_load.get(&pipeline_id) {
3873 load_data.url = url.clone();
3874 }
3875 }
3876 self.update_browsing_context(browsing_context_id, pipeline_reloader);
3877 }
3878
3879 for (pipeline_id, (history_state_id, url)) in pipeline_changes.drain() {
3880 self.update_pipeline(pipeline_id, history_state_id, url);
3881 }
3882
3883 self.notify_history_changed(webview_id);
3884
3885 self.trim_history(webview_id);
3886 self.update_webview_in_compositor(webview_id);
3887 }
3888
3889 #[servo_tracing::instrument(skip_all)]
3890 fn update_browsing_context(
3891 &mut self,
3892 browsing_context_id: BrowsingContextId,
3893 new_reloader: NeedsToReload,
3894 ) {
3895 let new_pipeline_id = match new_reloader {
3896 NeedsToReload::No(pipeline_id) => pipeline_id,
3897 NeedsToReload::Yes(pipeline_id, load_data) => {
3898 debug!(
3899 "{}: Reloading document {}",
3900 browsing_context_id, pipeline_id,
3901 );
3902
3903 let (
3904 webview_id,
3905 old_pipeline_id,
3906 parent_pipeline_id,
3907 viewport_details,
3908 is_private,
3909 throttled,
3910 ) = match self.browsing_contexts.get(&browsing_context_id) {
3911 Some(ctx) => (
3912 ctx.webview_id,
3913 ctx.pipeline_id,
3914 ctx.parent_pipeline_id,
3915 ctx.viewport_details,
3916 ctx.is_private,
3917 ctx.throttled,
3918 ),
3919 None => return warn!("No browsing context to traverse!"),
3920 };
3921 let opener = match self.pipelines.get(&old_pipeline_id) {
3922 Some(pipeline) => pipeline.opener,
3923 None => None,
3924 };
3925 let new_pipeline_id = PipelineId::new();
3926 self.new_pipeline(
3927 new_pipeline_id,
3928 browsing_context_id,
3929 webview_id,
3930 parent_pipeline_id,
3931 opener,
3932 viewport_details,
3933 load_data.clone(),
3934 is_private,
3935 throttled,
3936 );
3937 self.add_pending_change(SessionHistoryChange {
3938 webview_id,
3939 browsing_context_id,
3940 new_pipeline_id,
3941 replace: Some(NeedsToReload::Yes(pipeline_id, load_data)),
3942 new_browsing_context_info: None,
3944 viewport_details,
3945 });
3946 return;
3947 },
3948 };
3949
3950 let (old_pipeline_id, parent_pipeline_id, webview_id) =
3951 match self.browsing_contexts.get_mut(&browsing_context_id) {
3952 Some(browsing_context) => {
3953 let old_pipeline_id = browsing_context.pipeline_id;
3954 browsing_context.update_current_entry(new_pipeline_id);
3955 (
3956 old_pipeline_id,
3957 browsing_context.parent_pipeline_id,
3958 browsing_context.webview_id,
3959 )
3960 },
3961 None => {
3962 return warn!("{}: Closed during traversal", browsing_context_id);
3963 },
3964 };
3965
3966 self.unload_document(old_pipeline_id);
3967
3968 if let Some(new_pipeline) = self.pipelines.get(&new_pipeline_id) {
3969 if let Some(ref chan) = self.devtools_sender {
3970 let state = NavigationState::Start(new_pipeline.url.clone());
3971 let _ = chan.send(DevtoolsControlMsg::FromScript(
3972 ScriptToDevtoolsControlMsg::Navigate(browsing_context_id, state),
3973 ));
3974 let page_info = DevtoolsPageInfo {
3975 title: new_pipeline.title.clone(),
3976 url: new_pipeline.url.clone(),
3977 is_top_level_global: webview_id == browsing_context_id,
3978 };
3979 let state = NavigationState::Stop(new_pipeline.id, page_info);
3980 let _ = chan.send(DevtoolsControlMsg::FromScript(
3981 ScriptToDevtoolsControlMsg::Navigate(browsing_context_id, state),
3982 ));
3983 }
3984
3985 new_pipeline.set_throttled(false);
3986 self.notify_focus_state(new_pipeline_id);
3987 }
3988
3989 self.update_activity(old_pipeline_id);
3990 self.update_activity(new_pipeline_id);
3991
3992 if let Some(parent_pipeline_id) = parent_pipeline_id {
3993 let msg = ScriptThreadMessage::UpdatePipelineId(
3994 parent_pipeline_id,
3995 browsing_context_id,
3996 webview_id,
3997 new_pipeline_id,
3998 UpdatePipelineIdReason::Traversal,
3999 );
4000 self.send_message_to_pipeline(parent_pipeline_id, msg, "Child traversed after closure");
4001 }
4002 }
4003
4004 #[servo_tracing::instrument(skip_all)]
4005 fn update_pipeline(
4006 &mut self,
4007 pipeline_id: PipelineId,
4008 history_state_id: Option<HistoryStateId>,
4009 url: ServoUrl,
4010 ) {
4011 let msg =
4012 ScriptThreadMessage::UpdateHistoryState(pipeline_id, history_state_id, url.clone());
4013 self.send_message_to_pipeline(pipeline_id, msg, "History state updated after closure");
4014 }
4015
4016 #[servo_tracing::instrument(skip_all)]
4017 fn handle_joint_session_history_length(
4018 &self,
4019 webview_id: WebViewId,
4020 response_sender: IpcSender<u32>,
4021 ) {
4022 let length = self
4023 .webviews
4024 .get(webview_id)
4025 .map(|webview| webview.session_history.history_length())
4026 .unwrap_or(1);
4027 let _ = response_sender.send(length as u32);
4028 }
4029
4030 #[servo_tracing::instrument(skip_all)]
4031 fn handle_push_history_state_msg(
4032 &mut self,
4033 pipeline_id: PipelineId,
4034 history_state_id: HistoryStateId,
4035 url: ServoUrl,
4036 ) {
4037 let (webview_id, old_state_id, old_url) = match self.pipelines.get_mut(&pipeline_id) {
4038 Some(pipeline) => {
4039 let old_history_state_id = pipeline.history_state_id;
4040 let old_url = replace(&mut pipeline.url, url.clone());
4041 pipeline.history_state_id = Some(history_state_id);
4042 pipeline.history_states.insert(history_state_id);
4043 (pipeline.webview_id, old_history_state_id, old_url)
4044 },
4045 None => {
4046 return warn!(
4047 "{}: Push history state {} for closed pipeline",
4048 pipeline_id, history_state_id,
4049 );
4050 },
4051 };
4052
4053 let diff = SessionHistoryDiff::Pipeline {
4054 pipeline_reloader: NeedsToReload::No(pipeline_id),
4055 new_history_state_id: history_state_id,
4056 new_url: url,
4057 old_history_state_id: old_state_id,
4058 old_url,
4059 };
4060 self.get_joint_session_history(webview_id).push_diff(diff);
4061 self.notify_history_changed(webview_id);
4062 }
4063
4064 #[servo_tracing::instrument(skip_all)]
4065 fn handle_replace_history_state_msg(
4066 &mut self,
4067 pipeline_id: PipelineId,
4068 history_state_id: HistoryStateId,
4069 url: ServoUrl,
4070 ) {
4071 let webview_id = match self.pipelines.get_mut(&pipeline_id) {
4072 Some(pipeline) => {
4073 pipeline.history_state_id = Some(history_state_id);
4074 pipeline.url = url.clone();
4075 pipeline.webview_id
4076 },
4077 None => {
4078 return warn!(
4079 "{}: Replace history state {} for closed pipeline",
4080 history_state_id, pipeline_id
4081 );
4082 },
4083 };
4084
4085 let session_history = self.get_joint_session_history(webview_id);
4086 session_history.replace_history_state(pipeline_id, history_state_id, url);
4087 self.notify_history_changed(webview_id);
4088 }
4089
4090 #[servo_tracing::instrument(skip_all)]
4091 fn handle_reload_msg(&mut self, webview_id: WebViewId) {
4092 let browsing_context_id = BrowsingContextId::from(webview_id);
4093 let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
4094 Some(browsing_context) => browsing_context.pipeline_id,
4095 None => {
4096 return warn!("{}: Got reload event after closure", browsing_context_id);
4097 },
4098 };
4099 self.send_message_to_pipeline(
4100 pipeline_id,
4101 ScriptThreadMessage::Reload(pipeline_id),
4102 "Got reload event after closure",
4103 );
4104 }
4105
4106 #[servo_tracing::instrument(skip_all)]
4108 fn handle_post_message_msg(
4109 &mut self,
4110 browsing_context_id: BrowsingContextId,
4111 source_pipeline: PipelineId,
4112 origin: Option<ImmutableOrigin>,
4113 source_origin: ImmutableOrigin,
4114 data: StructuredSerializedData,
4115 ) {
4116 let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
4117 None => {
4118 return warn!(
4119 "{}: PostMessage to closed browsing context",
4120 browsing_context_id
4121 );
4122 },
4123 Some(browsing_context) => browsing_context.pipeline_id,
4124 };
4125 let source_webview = match self.pipelines.get(&source_pipeline) {
4126 Some(pipeline) => pipeline.webview_id,
4127 None => return warn!("{}: PostMessage from closed pipeline", source_pipeline),
4128 };
4129
4130 let browsing_context_for_pipeline = |pipeline_id| {
4131 self.pipelines
4132 .get(&pipeline_id)
4133 .and_then(|pipeline| self.browsing_contexts.get(&pipeline.browsing_context_id))
4134 };
4135 let mut maybe_browsing_context = browsing_context_for_pipeline(source_pipeline);
4136 if maybe_browsing_context.is_none() {
4137 return warn!("{source_pipeline}: PostMessage from pipeline with closed parent");
4138 }
4139
4140 let mut source_with_ancestry = vec![];
4146 while let Some(browsing_context) = maybe_browsing_context {
4147 source_with_ancestry.push(browsing_context.id);
4148 maybe_browsing_context = browsing_context
4149 .parent_pipeline_id
4150 .and_then(browsing_context_for_pipeline);
4151 }
4152 let msg = ScriptThreadMessage::PostMessage {
4153 target: pipeline_id,
4154 source_webview,
4155 source_with_ancestry,
4156 target_origin: origin,
4157 source_origin,
4158 data: Box::new(data),
4159 };
4160 self.send_message_to_pipeline(pipeline_id, msg, "PostMessage to closed pipeline");
4161 }
4162
4163 #[servo_tracing::instrument(skip_all)]
4164 fn handle_focus_msg(
4165 &mut self,
4166 pipeline_id: PipelineId,
4167 focused_child_browsing_context_id: Option<BrowsingContextId>,
4168 sequence: FocusSequenceNumber,
4169 ) {
4170 let (browsing_context_id, webview_id) = match self.pipelines.get_mut(&pipeline_id) {
4171 Some(pipeline) => {
4172 pipeline.focus_sequence = sequence;
4173 (pipeline.browsing_context_id, pipeline.webview_id)
4174 },
4175 None => return warn!("{}: Focus parent after closure", pipeline_id),
4176 };
4177
4178 if self.get_activity(pipeline_id) != DocumentActivity::FullyActive {
4180 debug!(
4181 "Ignoring the focus request because pipeline {} is not \
4182 fully active",
4183 pipeline_id
4184 );
4185 return;
4186 }
4187
4188 let focused = self.webviews.focus(webview_id);
4190 self.embedder_proxy
4191 .send(EmbedderMsg::WebViewFocused(webview_id, focused.is_ok()));
4192
4193 let focused_browsing_context_id =
4197 focused_child_browsing_context_id.unwrap_or(browsing_context_id);
4198
4199 self.focus_browsing_context(Some(pipeline_id), focused_browsing_context_id);
4203 }
4204
4205 fn handle_focus_remote_document_msg(&mut self, focused_browsing_context_id: BrowsingContextId) {
4206 let pipeline_id = match self.browsing_contexts.get(&focused_browsing_context_id) {
4207 Some(browsing_context) => browsing_context.pipeline_id,
4208 None => return warn!("Browsing context {} not found", focused_browsing_context_id),
4209 };
4210
4211 if self.get_activity(pipeline_id) != DocumentActivity::FullyActive {
4213 debug!(
4214 "Ignoring the remote focus request because pipeline {} of \
4215 browsing context {} is not fully active",
4216 pipeline_id, focused_browsing_context_id,
4217 );
4218 return;
4219 }
4220
4221 self.focus_browsing_context(None, focused_browsing_context_id);
4222 }
4223
4224 #[servo_tracing::instrument(skip_all)]
4235 fn focus_browsing_context(
4236 &mut self,
4237 initiator_pipeline_id: Option<PipelineId>,
4238 focused_browsing_context_id: BrowsingContextId,
4239 ) {
4240 let webview_id = match self.browsing_contexts.get(&focused_browsing_context_id) {
4241 Some(browsing_context) => browsing_context.webview_id,
4242 None => return warn!("Browsing context {} not found", focused_browsing_context_id),
4243 };
4244
4245 let old_focused_browsing_context_id = match self.webviews.get_mut(webview_id) {
4247 Some(browser) => replace(
4248 &mut browser.focused_browsing_context_id,
4249 focused_browsing_context_id,
4250 ),
4251 None => {
4252 return warn!(
4253 "{}: Browsing context for focus msg does not exist",
4254 webview_id
4255 );
4256 },
4257 };
4258
4259 let mut old_focus_chain_pipelines: Vec<&Pipeline> = self
4266 .ancestor_or_self_pipelines_of_browsing_context_iter(old_focused_browsing_context_id)
4267 .collect();
4268 let mut new_focus_chain_pipelines: Vec<&Pipeline> = self
4269 .ancestor_or_self_pipelines_of_browsing_context_iter(focused_browsing_context_id)
4270 .collect();
4271
4272 debug!(
4273 "old_focus_chain_pipelines = {:?}",
4274 old_focus_chain_pipelines
4275 .iter()
4276 .map(|p| p.id.to_string())
4277 .collect::<Vec<_>>()
4278 );
4279 debug!(
4280 "new_focus_chain_pipelines = {:?}",
4281 new_focus_chain_pipelines
4282 .iter()
4283 .map(|p| p.id.to_string())
4284 .collect::<Vec<_>>()
4285 );
4286
4287 match (
4291 &old_focus_chain_pipelines[..],
4292 &new_focus_chain_pipelines[..],
4293 ) {
4294 ([.., p1], [.., p2]) if p1.id == p2.id => {},
4295 _ => {
4296 warn!("Aborting the focus operation - focus chain sanity check failed");
4297 return;
4298 },
4299 }
4300
4301 let mut first_common_pipeline_in_chain = None;
4305 while let ([.., p1], [.., p2]) = (
4306 &old_focus_chain_pipelines[..],
4307 &new_focus_chain_pipelines[..],
4308 ) {
4309 if p1.id != p2.id {
4310 break;
4311 }
4312 old_focus_chain_pipelines.pop();
4313 first_common_pipeline_in_chain = new_focus_chain_pipelines.pop();
4314 }
4315
4316 let mut send_errors = Vec::new();
4317
4318 for &pipeline in old_focus_chain_pipelines.iter() {
4321 if Some(pipeline.id) != initiator_pipeline_id {
4322 let msg = ScriptThreadMessage::Unfocus(pipeline.id, pipeline.focus_sequence);
4323 trace!("Sending {:?} to {}", msg, pipeline.id);
4324 if let Err(e) = pipeline.event_loop.send(msg) {
4325 send_errors.push((pipeline.id, e));
4326 }
4327 } else {
4328 trace!(
4329 "Not notifying {} - it's the initiator of this focus operation",
4330 pipeline.id
4331 );
4332 }
4333 }
4334
4335 let mut child_browsing_context_id = None;
4338 for &pipeline in new_focus_chain_pipelines.iter().rev() {
4339 if Some(pipeline.id) != initiator_pipeline_id {
4342 let msg = if let Some(child_browsing_context_id) = child_browsing_context_id {
4343 ScriptThreadMessage::FocusIFrame(
4345 pipeline.id,
4346 child_browsing_context_id,
4347 pipeline.focus_sequence,
4348 )
4349 } else {
4350 ScriptThreadMessage::FocusDocument(pipeline.id, pipeline.focus_sequence)
4352 };
4353 trace!("Sending {:?} to {}", msg, pipeline.id);
4354 if let Err(e) = pipeline.event_loop.send(msg) {
4355 send_errors.push((pipeline.id, e));
4356 }
4357 } else {
4358 trace!(
4359 "Not notifying {} - it's the initiator of this focus operation",
4360 pipeline.id
4361 );
4362 }
4363 child_browsing_context_id = Some(pipeline.browsing_context_id);
4364 }
4365
4366 if let (Some(pipeline), Some(child_browsing_context_id)) =
4367 (first_common_pipeline_in_chain, child_browsing_context_id)
4368 {
4369 if Some(pipeline.id) != initiator_pipeline_id {
4370 let msg = ScriptThreadMessage::FocusIFrame(
4372 pipeline.id,
4373 child_browsing_context_id,
4374 pipeline.focus_sequence,
4375 );
4376 trace!("Sending {:?} to {}", msg, pipeline.id);
4377 if let Err(e) = pipeline.event_loop.send(msg) {
4378 send_errors.push((pipeline.id, e));
4379 }
4380 }
4381 }
4382
4383 for (pipeline_id, e) in send_errors {
4384 self.handle_send_error(pipeline_id, e);
4385 }
4386 }
4387
4388 #[servo_tracing::instrument(skip_all)]
4389 fn handle_remove_iframe_msg(
4390 &mut self,
4391 browsing_context_id: BrowsingContextId,
4392 ) -> Vec<PipelineId> {
4393 let result = self
4394 .all_descendant_browsing_contexts_iter(browsing_context_id)
4395 .flat_map(|browsing_context| browsing_context.pipelines.iter().cloned())
4396 .collect();
4397 self.close_browsing_context(browsing_context_id, ExitPipelineMode::Normal);
4398 result
4399 }
4400
4401 #[servo_tracing::instrument(skip_all)]
4402 fn handle_set_throttled_complete(&mut self, pipeline_id: PipelineId, throttled: bool) {
4403 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
4404 return warn!("{pipeline_id}: Visibility change for closed browsing context",);
4405 };
4406 let Some(browsing_context) = self.browsing_contexts.get(&pipeline.browsing_context_id)
4407 else {
4408 return warn!("{}: Visibility change for closed pipeline", pipeline_id);
4409 };
4410 let Some(parent_pipeline_id) = browsing_context.parent_pipeline_id else {
4411 return;
4412 };
4413
4414 let msg = ScriptThreadMessage::SetThrottledInContainingIframe(
4415 pipeline.webview_id,
4416 parent_pipeline_id,
4417 browsing_context.id,
4418 throttled,
4419 );
4420 self.send_message_to_pipeline(parent_pipeline_id, msg, "Parent pipeline closed");
4421 }
4422
4423 #[servo_tracing::instrument(skip_all)]
4424 fn handle_create_canvas_paint_thread_msg(
4425 &mut self,
4426 size: UntypedSize2D<u64>,
4427 response_sender: IpcSender<Option<(GenericSender<CanvasMsg>, CanvasId)>>,
4428 ) {
4429 let (canvas_data_sender, canvas_data_receiver) = unbounded();
4430 let (canvas_sender, canvas_ipc_sender) = self
4431 .canvas
4432 .get_or_init(|| self.create_canvas_paint_thread());
4433
4434 let response = if let Err(e) = canvas_sender.send(ConstellationCanvasMsg::Create {
4435 sender: canvas_data_sender,
4436 size,
4437 }) {
4438 warn!("Create canvas paint thread failed ({})", e);
4439 None
4440 } else {
4441 match canvas_data_receiver.recv() {
4442 Ok(Some(canvas_id)) => Some((canvas_ipc_sender.clone(), canvas_id)),
4443 Ok(None) => None,
4444 Err(e) => {
4445 warn!("Create canvas paint thread id response failed ({})", e);
4446 None
4447 },
4448 }
4449 };
4450 if let Err(e) = response_sender.send(response) {
4451 warn!("Create canvas paint thread response failed ({})", e);
4452 }
4453 }
4454
4455 #[servo_tracing::instrument(skip_all)]
4456 fn handle_webdriver_msg(&mut self, msg: WebDriverCommandMsg) {
4457 match msg {
4460 WebDriverCommandMsg::IsBrowsingContextOpen(browsing_context_id, response_sender) => {
4461 let is_open = self.browsing_contexts.contains_key(&browsing_context_id);
4462 let _ = response_sender.send(is_open);
4463 },
4464 WebDriverCommandMsg::FocusBrowsingContext(browsing_context_id) => {
4465 self.handle_focus_remote_document_msg(browsing_context_id);
4466 },
4467 WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd) => {
4469 let pipeline_id = if let Some(browsing_context) =
4470 self.browsing_contexts.get(&browsing_context_id)
4471 {
4472 browsing_context.pipeline_id
4473 } else {
4474 return warn!("{}: Browsing context is not ready", browsing_context_id);
4475 };
4476
4477 match &cmd {
4478 WebDriverScriptCommand::AddLoadStatusSender(_, sender) => {
4479 self.webdriver_load_status_sender = Some((sender.clone(), pipeline_id));
4480 },
4481 WebDriverScriptCommand::RemoveLoadStatusSender(_) => {
4482 self.webdriver_load_status_sender = None;
4483 },
4484 _ => {},
4485 };
4486
4487 let control_msg = ScriptThreadMessage::WebDriverScriptCommand(pipeline_id, cmd);
4488 self.send_message_to_pipeline(
4489 pipeline_id,
4490 control_msg,
4491 "ScriptCommand after closure",
4492 );
4493 },
4494 WebDriverCommandMsg::CloseWebView(..) |
4495 WebDriverCommandMsg::NewWebView(..) |
4496 WebDriverCommandMsg::FocusWebView(..) |
4497 WebDriverCommandMsg::IsWebViewOpen(..) |
4498 WebDriverCommandMsg::GetWindowRect(..) |
4499 WebDriverCommandMsg::GetViewportSize(..) |
4500 WebDriverCommandMsg::SetWindowRect(..) |
4501 WebDriverCommandMsg::MaximizeWebView(..) |
4502 WebDriverCommandMsg::LoadUrl(..) |
4503 WebDriverCommandMsg::Refresh(..) |
4504 WebDriverCommandMsg::InputEvent(..) |
4505 WebDriverCommandMsg::TakeScreenshot(..) => {
4506 unreachable!("This command should be send directly to the embedder.");
4507 },
4508 _ => {
4509 warn!("Unhandled WebDriver command: {:?}", msg);
4510 },
4511 }
4512 }
4513
4514 #[servo_tracing::instrument(skip_all)]
4515 fn set_webview_throttled(&mut self, webview_id: WebViewId, throttled: bool) {
4516 let browsing_context_id = BrowsingContextId::from(webview_id);
4517 let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
4518 Some(browsing_context) => browsing_context.pipeline_id,
4519 None => {
4520 return warn!("{browsing_context_id}: Tried to SetWebViewThrottled after closure");
4521 },
4522 };
4523 match self.pipelines.get(&pipeline_id) {
4524 None => warn!("{pipeline_id}: Tried to SetWebViewThrottled after closure"),
4525 Some(pipeline) => pipeline.set_throttled(throttled),
4526 }
4527 }
4528
4529 #[servo_tracing::instrument(skip_all)]
4530 fn notify_history_changed(&self, webview_id: WebViewId) {
4531 let session_history = match self.webviews.get(webview_id) {
4538 Some(webview) => &webview.session_history,
4539 None => {
4540 return warn!(
4541 "{}: Session history does not exist for browsing context",
4542 webview_id
4543 );
4544 },
4545 };
4546
4547 let browsing_context_id = BrowsingContextId::from(webview_id);
4548 let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) else {
4549 return warn!("notify_history_changed error after top-level browsing context closed.");
4550 };
4551
4552 let current_url = match self.pipelines.get(&browsing_context.pipeline_id) {
4553 Some(pipeline) => pipeline.url.clone(),
4554 None => {
4555 return warn!("{}: Refresh after closure", browsing_context.pipeline_id);
4556 },
4557 };
4558
4559 let resolve_url_future =
4562 |previous_url: &mut ServoUrl, diff: &SessionHistoryDiff| match *diff {
4563 SessionHistoryDiff::BrowsingContext {
4564 browsing_context_id,
4565 ref new_reloader,
4566 ..
4567 } => {
4568 if browsing_context_id == webview_id {
4569 let url = match *new_reloader {
4570 NeedsToReload::No(pipeline_id) => {
4571 match self.pipelines.get(&pipeline_id) {
4572 Some(pipeline) => pipeline.url.clone(),
4573 None => previous_url.clone(),
4574 }
4575 },
4576 NeedsToReload::Yes(_, ref load_data) => load_data.url.clone(),
4577 };
4578 *previous_url = url.clone();
4579 Some(url)
4580 } else {
4581 Some(previous_url.clone())
4582 }
4583 },
4584 _ => Some(previous_url.clone()),
4585 };
4586
4587 let resolve_url_past = |previous_url: &mut ServoUrl, diff: &SessionHistoryDiff| match *diff
4588 {
4589 SessionHistoryDiff::BrowsingContext {
4590 browsing_context_id,
4591 ref old_reloader,
4592 ..
4593 } => {
4594 if browsing_context_id == webview_id {
4595 let url = match *old_reloader {
4596 NeedsToReload::No(pipeline_id) => match self.pipelines.get(&pipeline_id) {
4597 Some(pipeline) => pipeline.url.clone(),
4598 None => previous_url.clone(),
4599 },
4600 NeedsToReload::Yes(_, ref load_data) => load_data.url.clone(),
4601 };
4602 *previous_url = url.clone();
4603 Some(url)
4604 } else {
4605 Some(previous_url.clone())
4606 }
4607 },
4608 _ => Some(previous_url.clone()),
4609 };
4610
4611 let mut entries: Vec<ServoUrl> = session_history
4612 .past
4613 .iter()
4614 .rev()
4615 .scan(current_url.clone(), &resolve_url_past)
4616 .collect();
4617
4618 entries.reverse();
4619
4620 let current_index = entries.len();
4621
4622 entries.push(current_url.clone());
4623
4624 entries.extend(
4625 session_history
4626 .future
4627 .iter()
4628 .rev()
4629 .scan(current_url, &resolve_url_future),
4630 );
4631 self.embedder_proxy.send(EmbedderMsg::HistoryChanged(
4632 webview_id,
4633 entries,
4634 current_index,
4635 ));
4636 }
4637
4638 #[servo_tracing::instrument(skip_all)]
4639 fn change_session_history(&mut self, change: SessionHistoryChange) {
4640 debug!(
4641 "{}: Setting to {}",
4642 change.browsing_context_id, change.new_pipeline_id
4643 );
4644
4645 if self.focused_browsing_context_is_descendant_of(change.browsing_context_id) {
4649 if let Some(webview) = self.webviews.get_mut(change.webview_id) {
4650 webview.focused_browsing_context_id = change.browsing_context_id;
4651 }
4652 }
4653
4654 let (old_pipeline_id, webview_id) =
4655 match self.browsing_contexts.get_mut(&change.browsing_context_id) {
4656 Some(browsing_context) => {
4657 debug!("Adding pipeline to existing browsing context.");
4658 let old_pipeline_id = browsing_context.pipeline_id;
4659 browsing_context.pipelines.insert(change.new_pipeline_id);
4660 browsing_context.update_current_entry(change.new_pipeline_id);
4661 (Some(old_pipeline_id), Some(browsing_context.webview_id))
4662 },
4663 None => {
4664 debug!("Adding pipeline to new browsing context.");
4665 (None, None)
4666 },
4667 };
4668
4669 match old_pipeline_id {
4670 None => {
4671 let Some(new_context_info) = change.new_browsing_context_info else {
4672 return warn!(
4673 "{}: No NewBrowsingContextInfo for browsing context",
4674 change.browsing_context_id,
4675 );
4676 };
4677 self.new_browsing_context(
4678 change.browsing_context_id,
4679 change.webview_id,
4680 change.new_pipeline_id,
4681 new_context_info.parent_pipeline_id,
4682 change.viewport_details,
4683 new_context_info.is_private,
4684 new_context_info.inherited_secure_context,
4685 new_context_info.throttled,
4686 );
4687 self.update_activity(change.new_pipeline_id);
4688 },
4689 Some(old_pipeline_id) => {
4690 self.unload_document(old_pipeline_id);
4691
4692 let (pipelines_to_close, states_to_close) = if let Some(replace_reloader) =
4694 change.replace
4695 {
4696 self.get_joint_session_history(change.webview_id)
4697 .replace_reloader(
4698 replace_reloader.clone(),
4699 NeedsToReload::No(change.new_pipeline_id),
4700 );
4701
4702 match replace_reloader {
4703 NeedsToReload::No(pipeline_id) => (Some(vec![pipeline_id]), None),
4704 NeedsToReload::Yes(..) => (None, None),
4705 }
4706 } else {
4707 let diff = SessionHistoryDiff::BrowsingContext {
4708 browsing_context_id: change.browsing_context_id,
4709 new_reloader: NeedsToReload::No(change.new_pipeline_id),
4710 old_reloader: NeedsToReload::No(old_pipeline_id),
4711 };
4712
4713 let mut pipelines_to_close = vec![];
4714 let mut states_to_close = FxHashMap::default();
4715
4716 let diffs_to_close = self
4717 .get_joint_session_history(change.webview_id)
4718 .push_diff(diff);
4719
4720 for diff in diffs_to_close {
4721 match diff {
4722 SessionHistoryDiff::BrowsingContext { new_reloader, .. } => {
4723 if let Some(pipeline_id) = new_reloader.alive_pipeline_id() {
4724 pipelines_to_close.push(pipeline_id);
4725 }
4726 },
4727 SessionHistoryDiff::Pipeline {
4728 pipeline_reloader,
4729 new_history_state_id,
4730 ..
4731 } => {
4732 if let Some(pipeline_id) = pipeline_reloader.alive_pipeline_id() {
4733 let states =
4734 states_to_close.entry(pipeline_id).or_insert(Vec::new());
4735 states.push(new_history_state_id);
4736 }
4737 },
4738 _ => {},
4739 }
4740 }
4741
4742 (Some(pipelines_to_close), Some(states_to_close))
4743 };
4744
4745 self.update_activity(old_pipeline_id);
4746 self.update_activity(change.new_pipeline_id);
4747
4748 if let Some(states_to_close) = states_to_close {
4749 for (pipeline_id, states) in states_to_close {
4750 let msg = ScriptThreadMessage::RemoveHistoryStates(pipeline_id, states);
4751 if !self.send_message_to_pipeline(
4752 pipeline_id,
4753 msg,
4754 "Removed history states after closure",
4755 ) {
4756 return;
4757 }
4758 }
4759 }
4760
4761 if let Some(pipelines_to_close) = pipelines_to_close {
4762 for pipeline_id in pipelines_to_close {
4763 self.close_pipeline(
4764 pipeline_id,
4765 DiscardBrowsingContext::No,
4766 ExitPipelineMode::Normal,
4767 );
4768 }
4769 }
4770 },
4771 }
4772
4773 if let Some(webview_id) = webview_id {
4774 self.trim_history(webview_id);
4775 }
4776
4777 self.notify_focus_state(change.new_pipeline_id);
4778
4779 self.notify_history_changed(change.webview_id);
4780 self.update_webview_in_compositor(change.webview_id);
4781 }
4782
4783 fn notify_focus_state(&mut self, pipeline_id: PipelineId) {
4787 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
4788 return warn!("Pipeline {pipeline_id} is closed");
4789 };
4790
4791 let is_focused = match self.webviews.get(pipeline.webview_id) {
4792 Some(webview) => webview.focused_browsing_context_id == pipeline.browsing_context_id,
4793 None => {
4794 return warn!(
4795 "Pipeline {pipeline_id}'s top-level browsing context {} is closed",
4796 pipeline.webview_id
4797 );
4798 },
4799 };
4800
4801 let msg = if is_focused {
4803 ScriptThreadMessage::FocusDocument(pipeline_id, pipeline.focus_sequence)
4804 } else {
4805 ScriptThreadMessage::Unfocus(pipeline_id, pipeline.focus_sequence)
4806 };
4807 if let Err(e) = pipeline.event_loop.send(msg) {
4808 self.handle_send_error(pipeline_id, e);
4809 }
4810 }
4811
4812 #[servo_tracing::instrument(skip_all)]
4813 fn focused_browsing_context_is_descendant_of(
4814 &self,
4815 browsing_context_id: BrowsingContextId,
4816 ) -> bool {
4817 let focused_browsing_context_id = self
4818 .webviews
4819 .focused_webview()
4820 .map(|(_, webview)| webview.focused_browsing_context_id);
4821 focused_browsing_context_id.is_some_and(|focus_ctx_id| {
4822 focus_ctx_id == browsing_context_id ||
4823 self.fully_active_descendant_browsing_contexts_iter(browsing_context_id)
4824 .any(|nested_ctx| nested_ctx.id == focus_ctx_id)
4825 })
4826 }
4827
4828 #[servo_tracing::instrument(skip_all)]
4829 fn trim_history(&mut self, webview_id: WebViewId) {
4830 let pipelines_to_evict = {
4831 let session_history = self.get_joint_session_history(webview_id);
4832
4833 let history_length = pref!(session_history_max_length) as usize;
4834
4835 let mut pipelines_to_evict = session_history
4839 .past
4840 .iter()
4841 .rev()
4842 .map(|diff| diff.alive_old_pipeline())
4843 .skip(history_length)
4844 .flatten()
4845 .collect::<Vec<_>>();
4846
4847 pipelines_to_evict.extend(
4850 session_history
4851 .future
4852 .iter()
4853 .rev()
4854 .map(|diff| diff.alive_new_pipeline())
4855 .skip(history_length)
4856 .flatten(),
4857 );
4858
4859 pipelines_to_evict
4860 };
4861
4862 let mut dead_pipelines = vec![];
4863 for evicted_id in pipelines_to_evict {
4864 let Some(load_data) = self.refresh_load_data(evicted_id) else {
4865 continue;
4866 };
4867
4868 dead_pipelines.push((evicted_id, NeedsToReload::Yes(evicted_id, load_data)));
4869 self.close_pipeline(
4870 evicted_id,
4871 DiscardBrowsingContext::No,
4872 ExitPipelineMode::Normal,
4873 );
4874 }
4875
4876 let session_history = self.get_joint_session_history(webview_id);
4877
4878 for (alive_id, dead) in dead_pipelines {
4879 session_history.replace_reloader(NeedsToReload::No(alive_id), dead);
4880 }
4881 }
4882
4883 #[servo_tracing::instrument(skip_all)]
4884 fn handle_activate_document_msg(&mut self, pipeline_id: PipelineId) {
4885 debug!("{}: Document ready to activate", pipeline_id);
4886
4887 let Some(pending_index) = self
4889 .pending_changes
4890 .iter()
4891 .rposition(|change| change.new_pipeline_id == pipeline_id)
4892 else {
4893 return;
4894 };
4895
4896 let change = self.pending_changes.swap_remove(pending_index);
4899
4900 self.send_screenshot_readiness_requests_to_pipelines();
4901
4902 let parent_pipeline_id = match change.new_browsing_context_info {
4904 Some(ref info) => info.parent_pipeline_id,
4906 None => match self.browsing_contexts.get(&change.browsing_context_id) {
4908 Some(ctx) => ctx.parent_pipeline_id,
4909 None => {
4910 return warn!(
4911 "{}: Activated document after closure of {}",
4912 change.new_pipeline_id, change.browsing_context_id,
4913 );
4914 },
4915 },
4916 };
4917 if let Some(parent_pipeline_id) = parent_pipeline_id {
4918 if let Some(parent_pipeline) = self.pipelines.get(&parent_pipeline_id) {
4919 let msg = ScriptThreadMessage::UpdatePipelineId(
4920 parent_pipeline_id,
4921 change.browsing_context_id,
4922 change.webview_id,
4923 pipeline_id,
4924 UpdatePipelineIdReason::Navigation,
4925 );
4926 let _ = parent_pipeline.event_loop.send(msg);
4927 }
4928 }
4929 self.change_session_history(change);
4930 }
4931
4932 #[servo_tracing::instrument(skip_all)]
4934 fn handle_change_viewport_details_msg(
4935 &mut self,
4936 webview_id: WebViewId,
4937 new_viewport_details: ViewportDetails,
4938 size_type: WindowSizeType,
4939 ) {
4940 debug!(
4941 "handle_change_viewport_details_msg: {:?}",
4942 new_viewport_details
4943 );
4944
4945 let browsing_context_id = BrowsingContextId::from(webview_id);
4946 self.resize_browsing_context(new_viewport_details, size_type, browsing_context_id);
4947 }
4948
4949 #[servo_tracing::instrument(skip_all)]
4951 fn handle_exit_fullscreen_msg(&mut self, webview_id: WebViewId) {
4952 let browsing_context_id = BrowsingContextId::from(webview_id);
4953 self.switch_fullscreen_mode(browsing_context_id);
4954 }
4955
4956 #[servo_tracing::instrument(skip_all)]
4957 fn handle_request_screenshot_readiness(&mut self, webview_id: WebViewId) {
4958 self.screenshot_readiness_requests
4959 .push(ScreenshotReadinessRequest {
4960 webview_id,
4961 pipeline_states: Default::default(),
4962 state: Default::default(),
4963 });
4964 self.send_screenshot_readiness_requests_to_pipelines();
4965 }
4966
4967 fn send_screenshot_readiness_requests_to_pipelines(&mut self) {
4968 if !self.pending_changes.is_empty() {
4970 return;
4971 }
4972
4973 for screenshot_request in &self.screenshot_readiness_requests {
4974 if screenshot_request.state.get() != ScreenshotRequestState::Pending {
4976 return;
4977 }
4978
4979 *screenshot_request.pipeline_states.borrow_mut() =
4980 self.fully_active_browsing_contexts_iter(screenshot_request.webview_id)
4981 .filter_map(|browsing_context| {
4982 let pipeline_id = browsing_context.pipeline_id;
4983 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
4984 return None;
4986 };
4987 if browsing_context.viewport_details.size == Size2D::zero() {
4991 return None;
4992 }
4993 let _ = pipeline.event_loop.send(
4994 ScriptThreadMessage::RequestScreenshotReadiness(
4995 pipeline.webview_id,
4996 pipeline_id,
4997 ),
4998 );
4999 Some((pipeline_id, None))
5000 })
5001 .collect();
5002 screenshot_request
5003 .state
5004 .set(ScreenshotRequestState::WaitingOnScript);
5005 }
5006 }
5007
5008 #[servo_tracing::instrument(skip_all)]
5009 fn handle_screenshot_readiness_response(
5010 &mut self,
5011 updated_pipeline_id: PipelineId,
5012 response: ScreenshotReadinessResponse,
5013 ) {
5014 if self.screenshot_readiness_requests.is_empty() {
5015 return;
5016 }
5017
5018 self.screenshot_readiness_requests
5019 .retain(|screenshot_request| {
5020 if screenshot_request.state.get() != ScreenshotRequestState::WaitingOnScript {
5021 return true;
5022 }
5023
5024 let mut has_pending_pipeline = false;
5025 let mut pipeline_states = screenshot_request.pipeline_states.borrow_mut();
5026 pipeline_states.retain(|pipeline_id, state| {
5027 if *pipeline_id != updated_pipeline_id {
5028 has_pending_pipeline |= state.is_none();
5029 return true;
5030 }
5031 match response {
5032 ScreenshotReadinessResponse::Ready(epoch) => {
5033 *state = Some(epoch);
5034 true
5035 },
5036 ScreenshotReadinessResponse::NoLongerActive => false,
5037 }
5038 });
5039
5040 if has_pending_pipeline {
5041 return true;
5042 }
5043
5044 let pipelines_and_epochs = pipeline_states
5045 .iter()
5046 .map(|(pipeline_id, epoch)| {
5047 (
5048 *pipeline_id,
5049 epoch.expect("Should have an epoch when pipeline is ready."),
5050 )
5051 })
5052 .collect();
5053 self.compositor_proxy
5054 .send(CompositorMsg::ScreenshotReadinessReponse(
5055 screenshot_request.webview_id,
5056 pipelines_and_epochs,
5057 ));
5058
5059 false
5060 });
5061 }
5062
5063 #[servo_tracing::instrument(skip_all)]
5065 fn get_activity(&self, pipeline_id: PipelineId) -> DocumentActivity {
5066 let mut ancestor_id = pipeline_id;
5067 loop {
5068 if let Some(ancestor) = self.pipelines.get(&ancestor_id) {
5069 if let Some(browsing_context) =
5070 self.browsing_contexts.get(&ancestor.browsing_context_id)
5071 {
5072 if browsing_context.pipeline_id == ancestor_id {
5073 if let Some(parent_pipeline_id) = browsing_context.parent_pipeline_id {
5074 ancestor_id = parent_pipeline_id;
5075 continue;
5076 } else {
5077 return DocumentActivity::FullyActive;
5078 }
5079 }
5080 }
5081 }
5082 if pipeline_id == ancestor_id {
5083 return DocumentActivity::Inactive;
5084 } else {
5085 return DocumentActivity::Active;
5086 }
5087 }
5088 }
5089
5090 #[servo_tracing::instrument(skip_all)]
5092 fn set_activity(&self, pipeline_id: PipelineId, activity: DocumentActivity) {
5093 debug!("{}: Setting activity to {:?}", pipeline_id, activity);
5094 if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
5095 pipeline.set_activity(activity);
5096 let child_activity = if activity == DocumentActivity::Inactive {
5097 DocumentActivity::Active
5098 } else {
5099 activity
5100 };
5101 for child_id in &pipeline.children {
5102 if let Some(child) = self.browsing_contexts.get(child_id) {
5103 self.set_activity(child.pipeline_id, child_activity);
5104 }
5105 }
5106 }
5107 }
5108
5109 #[servo_tracing::instrument(skip_all)]
5111 fn update_activity(&self, pipeline_id: PipelineId) {
5112 self.set_activity(pipeline_id, self.get_activity(pipeline_id));
5113 }
5114
5115 #[servo_tracing::instrument(skip_all)]
5118 fn resize_browsing_context(
5119 &mut self,
5120 new_viewport_details: ViewportDetails,
5121 size_type: WindowSizeType,
5122 browsing_context_id: BrowsingContextId,
5123 ) {
5124 if let Some(browsing_context) = self.browsing_contexts.get_mut(&browsing_context_id) {
5125 browsing_context.viewport_details = new_viewport_details;
5126 let pipeline_id = browsing_context.pipeline_id;
5128 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5129 return warn!("{}: Resized after closing", pipeline_id);
5130 };
5131 let _ = pipeline.event_loop.send(ScriptThreadMessage::Resize(
5132 pipeline.id,
5133 new_viewport_details,
5134 size_type,
5135 ));
5136 let pipeline_ids = browsing_context
5137 .pipelines
5138 .iter()
5139 .filter(|pipeline_id| **pipeline_id != pipeline.id);
5140 for id in pipeline_ids {
5141 if let Some(pipeline) = self.pipelines.get(id) {
5142 let _ = pipeline
5143 .event_loop
5144 .send(ScriptThreadMessage::ResizeInactive(
5145 pipeline.id,
5146 new_viewport_details,
5147 ));
5148 }
5149 }
5150 } else {
5151 self.pending_viewport_changes
5152 .insert(browsing_context_id, new_viewport_details);
5153 }
5154
5155 for change in &self.pending_changes {
5157 let pipeline_id = change.new_pipeline_id;
5158 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5159 warn!("{}: Pending pipeline is closed", pipeline_id);
5160 continue;
5161 };
5162 if pipeline.browsing_context_id == browsing_context_id {
5163 let _ = pipeline.event_loop.send(ScriptThreadMessage::Resize(
5164 pipeline.id,
5165 new_viewport_details,
5166 size_type,
5167 ));
5168 }
5169 }
5170 }
5171
5172 #[servo_tracing::instrument(skip_all)]
5174 fn handle_theme_change(&mut self, webview_id: WebViewId, theme: Theme) {
5175 let Some(webview) = self.webviews.get_mut(webview_id) else {
5176 warn!("Received theme change request for uknown WebViewId: {webview_id:?}");
5177 return;
5178 };
5179 if !webview.set_theme(theme) {
5180 return;
5181 }
5182
5183 for pipeline in self.pipelines.values() {
5184 if pipeline.webview_id != webview_id {
5185 continue;
5186 }
5187 if let Err(error) = pipeline
5188 .event_loop
5189 .send(ScriptThreadMessage::ThemeChange(pipeline.id, theme))
5190 {
5191 warn!(
5192 "{}: Failed to send theme change event to pipeline ({error:?}).",
5193 pipeline.id,
5194 );
5195 }
5196 }
5197 }
5198
5199 #[servo_tracing::instrument(skip_all)]
5201 fn switch_fullscreen_mode(&mut self, browsing_context_id: BrowsingContextId) {
5202 if let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) {
5203 let pipeline_id = browsing_context.pipeline_id;
5204 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5205 return warn!("{pipeline_id}: Switched from fullscreen mode after closing",);
5206 };
5207 let _ = pipeline
5208 .event_loop
5209 .send(ScriptThreadMessage::ExitFullScreen(pipeline.id));
5210 }
5211 }
5212
5213 #[servo_tracing::instrument(skip_all)]
5215 fn close_browsing_context(
5216 &mut self,
5217 browsing_context_id: BrowsingContextId,
5218 exit_mode: ExitPipelineMode,
5219 ) -> Option<BrowsingContext> {
5220 debug!("{}: Closing", browsing_context_id);
5221
5222 self.close_browsing_context_children(
5223 browsing_context_id,
5224 DiscardBrowsingContext::Yes,
5225 exit_mode,
5226 );
5227
5228 let _ = self.pending_viewport_changes.remove(&browsing_context_id);
5229
5230 let Some(browsing_context) = self.browsing_contexts.remove(&browsing_context_id) else {
5231 warn!("fn close_browsing_context: {browsing_context_id}: Closing twice");
5232 return None;
5233 };
5234
5235 {
5236 let session_history = self.get_joint_session_history(browsing_context.webview_id);
5237 session_history.remove_entries_for_browsing_context(browsing_context_id);
5238 }
5239
5240 if let Some(parent_pipeline_id) = browsing_context.parent_pipeline_id {
5241 match self.pipelines.get_mut(&parent_pipeline_id) {
5242 None => {
5243 warn!("{parent_pipeline_id}: Child closed after parent");
5244 },
5245 Some(parent_pipeline) => {
5246 parent_pipeline.remove_child(browsing_context_id);
5247
5248 if let Some(webview) = self.webviews.get_mut(browsing_context.webview_id) {
5251 if webview.focused_browsing_context_id == browsing_context_id {
5252 trace!(
5253 "About-to-be-closed browsing context {} is currently focused, so \
5254 focusing its parent {}",
5255 browsing_context_id, parent_pipeline.browsing_context_id
5256 );
5257 webview.focused_browsing_context_id =
5258 parent_pipeline.browsing_context_id;
5259 }
5260 } else {
5261 warn!(
5262 "Browsing context {} contains a reference to \
5263 a non-existent top-level browsing context {}",
5264 browsing_context_id, browsing_context.webview_id
5265 );
5266 }
5267 },
5268 };
5269 }
5270 debug!("{}: Closed", browsing_context_id);
5271 Some(browsing_context)
5272 }
5273
5274 #[servo_tracing::instrument(skip_all)]
5276 fn close_browsing_context_children(
5277 &mut self,
5278 browsing_context_id: BrowsingContextId,
5279 dbc: DiscardBrowsingContext,
5280 exit_mode: ExitPipelineMode,
5281 ) {
5282 debug!("{}: Closing browsing context children", browsing_context_id);
5283 let mut pipelines_to_close: Vec<PipelineId> = self
5288 .pending_changes
5289 .iter()
5290 .filter(|change| change.browsing_context_id == browsing_context_id)
5291 .map(|change| change.new_pipeline_id)
5292 .collect();
5293
5294 if let Some(browsing_context) = self.browsing_contexts.get(&browsing_context_id) {
5295 pipelines_to_close.extend(&browsing_context.pipelines)
5296 }
5297
5298 for pipeline_id in pipelines_to_close {
5299 self.close_pipeline(pipeline_id, dbc, exit_mode);
5300 }
5301
5302 debug!("{}: Closed browsing context children", browsing_context_id);
5303 }
5304
5305 fn refresh_load_data(&self, pipeline_id: PipelineId) -> Option<LoadData> {
5308 self.pipelines.get(&pipeline_id).map(|pipeline| {
5309 let mut load_data = pipeline.load_data.clone();
5310 load_data.url = pipeline.url.clone();
5311 load_data
5312 })
5313 }
5314
5315 #[servo_tracing::instrument(skip_all)]
5317 fn handle_discard_document(&mut self, webview_id: WebViewId, pipeline_id: PipelineId) {
5318 let Some(load_data) = self.refresh_load_data(pipeline_id) else {
5319 return warn!("{}: Discarding closed pipeline", pipeline_id);
5320 };
5321 match self.webviews.get_mut(webview_id) {
5322 Some(webview) => {
5323 webview.session_history.replace_reloader(
5324 NeedsToReload::No(pipeline_id),
5325 NeedsToReload::Yes(pipeline_id, load_data),
5326 );
5327 },
5328 None => {
5329 return warn!("{pipeline_id}: Discarding after closure of {webview_id}",);
5330 },
5331 };
5332 self.close_pipeline(
5333 pipeline_id,
5334 DiscardBrowsingContext::No,
5335 ExitPipelineMode::Normal,
5336 );
5337 }
5338
5339 #[servo_tracing::instrument(skip_all)]
5341 fn unload_document(&self, pipeline_id: PipelineId) {
5342 if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
5343 pipeline.set_throttled(true);
5344 let msg = ScriptThreadMessage::UnloadDocument(pipeline_id);
5345 let _ = pipeline.event_loop.send(msg);
5346 }
5347 }
5348
5349 #[servo_tracing::instrument(skip_all)]
5351 fn close_pipeline(
5352 &mut self,
5353 pipeline_id: PipelineId,
5354 dbc: DiscardBrowsingContext,
5355 exit_mode: ExitPipelineMode,
5356 ) {
5357 debug!("{}: Closing", pipeline_id);
5358
5359 let browsing_context_id = self
5361 .pipelines
5362 .get(&pipeline_id)
5363 .map(|pipeline| pipeline.browsing_context_id);
5364 if let Some(browsing_context) = browsing_context_id
5365 .and_then(|browsing_context_id| self.browsing_contexts.get_mut(&browsing_context_id))
5366 {
5367 browsing_context.pipelines.remove(&pipeline_id);
5368 }
5369
5370 let browsing_contexts_to_close = {
5375 let mut browsing_contexts_to_close = vec![];
5376
5377 if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
5378 browsing_contexts_to_close.extend_from_slice(&pipeline.children);
5379 }
5380
5381 browsing_contexts_to_close
5382 };
5383
5384 for child_browsing_context in &browsing_contexts_to_close {
5386 self.close_browsing_context(*child_browsing_context, exit_mode);
5387 }
5388
5389 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5392 return warn!("fn close_pipeline: {pipeline_id}: Closing twice");
5393 };
5394
5395 let pending_index = self
5397 .pending_changes
5398 .iter()
5399 .position(|change| change.new_pipeline_id == pipeline_id);
5400 if let Some(pending_index) = pending_index {
5401 self.pending_changes.remove(pending_index);
5402 }
5403
5404 pipeline.send_exit_message_to_script(dbc);
5406
5407 self.send_screenshot_readiness_requests_to_pipelines();
5408 self.handle_screenshot_readiness_response(
5409 pipeline_id,
5410 ScreenshotReadinessResponse::NoLongerActive,
5411 );
5412
5413 debug!("{}: Closed", pipeline_id);
5414 }
5415
5416 fn maybe_close_random_pipeline(&mut self) {
5418 match self.random_pipeline_closure {
5419 Some((ref mut rng, probability)) => {
5420 if probability <= rng.random::<f32>() {
5421 return;
5422 }
5423 },
5424 _ => return,
5425 };
5426 let mut pipeline_ids: Vec<&PipelineId> = self.pipelines.keys().collect();
5428 pipeline_ids.sort_unstable();
5429 if let Some((ref mut rng, probability)) = self.random_pipeline_closure {
5430 if let Some(pipeline_id) = pipeline_ids.choose(rng) {
5431 if let Some(pipeline) = self.pipelines.get(pipeline_id) {
5432 if self
5433 .pending_changes
5434 .iter()
5435 .any(|change| change.new_pipeline_id == pipeline.id) &&
5436 probability <= rng.random::<f32>()
5437 {
5438 info!("{}: Not closing pending pipeline", pipeline_id);
5443 } else {
5444 warn!("{}: Randomly closing pipeline", pipeline_id);
5447 pipeline.send_exit_message_to_script(DiscardBrowsingContext::No);
5448 }
5449 }
5450 }
5451 }
5452 }
5453
5454 #[servo_tracing::instrument(skip_all)]
5455 fn get_joint_session_history(&mut self, webview_id: WebViewId) -> &mut JointSessionHistory {
5456 self.webviews
5457 .get_mut(webview_id)
5458 .map(|webview| &mut webview.session_history)
5459 .expect("Unknown top-level browsing context")
5460 }
5461
5462 #[servo_tracing::instrument(skip_all)]
5464 fn browsing_context_to_sendable(
5465 &self,
5466 browsing_context_id: BrowsingContextId,
5467 ) -> Option<SendableFrameTree> {
5468 self.browsing_contexts
5469 .get(&browsing_context_id)
5470 .and_then(|browsing_context| {
5471 self.pipelines
5472 .get(&browsing_context.pipeline_id)
5473 .map(|pipeline| {
5474 let mut frame_tree = SendableFrameTree {
5475 pipeline: pipeline.to_sendable(),
5476 children: vec![],
5477 };
5478
5479 for child_browsing_context_id in &pipeline.children {
5480 if let Some(child) =
5481 self.browsing_context_to_sendable(*child_browsing_context_id)
5482 {
5483 frame_tree.children.push(child);
5484 }
5485 }
5486
5487 frame_tree
5488 })
5489 })
5490 }
5491
5492 #[servo_tracing::instrument(skip_all)]
5494 fn update_webview_in_compositor(&mut self, webview_id: WebViewId) {
5495 let browsing_context_id = BrowsingContextId::from(webview_id);
5499 if let Some(frame_tree) = self.browsing_context_to_sendable(browsing_context_id) {
5500 debug!("{}: Sending frame tree", browsing_context_id);
5501 self.compositor_proxy
5502 .send(CompositorMsg::CreateOrUpdateWebView(frame_tree));
5503 }
5504 }
5505
5506 #[servo_tracing::instrument(skip_all)]
5507 fn handle_media_session_action_msg(&mut self, action: MediaSessionActionType) {
5508 if let Some(media_session_pipeline_id) = self.active_media_session {
5509 self.send_message_to_pipeline(
5510 media_session_pipeline_id,
5511 ScriptThreadMessage::MediaSessionAction(media_session_pipeline_id, action),
5512 "Got media session action request after closure",
5513 );
5514 } else {
5515 error!("Got a media session action but no active media session is registered");
5516 }
5517 }
5518
5519 #[servo_tracing::instrument(skip_all)]
5520 fn handle_set_scroll_states(
5521 &self,
5522 pipeline_id: PipelineId,
5523 scroll_states: FxHashMap<ExternalScrollId, LayoutVector2D>,
5524 ) {
5525 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5526 warn!("Discarding scroll offset update for unknown pipeline");
5527 return;
5528 };
5529 if let Err(error) = pipeline
5530 .event_loop
5531 .send(ScriptThreadMessage::SetScrollStates(
5532 pipeline_id,
5533 scroll_states,
5534 ))
5535 {
5536 warn!("Could not send scroll offsets to pipeline: {pipeline_id:?}: {error:?}");
5537 }
5538 }
5539
5540 #[servo_tracing::instrument(skip_all)]
5541 fn handle_paint_metric(&mut self, pipeline_id: PipelineId, event: PaintMetricEvent) {
5542 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5543 warn!("Discarding paint metric event for unknown pipeline");
5544 return;
5545 };
5546 let (metric_type, metric_value, first_reflow) = match event {
5547 PaintMetricEvent::FirstPaint(metric_value, first_reflow) => (
5548 ProgressiveWebMetricType::FirstPaint,
5549 metric_value,
5550 first_reflow,
5551 ),
5552 PaintMetricEvent::FirstContentfulPaint(metric_value, first_reflow) => (
5553 ProgressiveWebMetricType::FirstContentfulPaint,
5554 metric_value,
5555 first_reflow,
5556 ),
5557 PaintMetricEvent::LargestContentfulPaint(metric_value, area, lcp_type) => (
5558 ProgressiveWebMetricType::LargestContentfulPaint { area, lcp_type },
5559 metric_value,
5560 false, ),
5562 };
5563 if let Err(error) = pipeline.event_loop.send(ScriptThreadMessage::PaintMetric(
5564 pipeline_id,
5565 metric_type,
5566 metric_value,
5567 first_reflow,
5568 )) {
5569 warn!("Could not sent paint metric event to pipeline: {pipeline_id:?}: {error:?}");
5570 }
5571 }
5572
5573 fn create_canvas_paint_thread(
5574 &self,
5575 ) -> (Sender<ConstellationCanvasMsg>, GenericSender<CanvasMsg>) {
5576 CanvasPaintThread::start(self.compositor_proxy.cross_process_compositor_api.clone())
5577 }
5578
5579 fn handle_embedder_control_response(
5580 &self,
5581 id: EmbedderControlId,
5582 response: EmbedderControlResponse,
5583 ) {
5584 let pipeline_id = id.pipeline_id;
5585 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
5586 warn!("Not sending embedder control response for unknown pipeline {pipeline_id:?}");
5587 return;
5588 };
5589
5590 if let Err(error) = pipeline
5591 .event_loop
5592 .send(ScriptThreadMessage::EmbedderControlResponse(id, response))
5593 {
5594 warn!(
5595 "Could not send embedder control response to pipeline {pipeline_id:?}: {error:?}"
5596 );
5597 }
5598 }
5599}
5600
5601#[derive(Clone, Copy, Default, PartialEq)]
5604enum ScreenshotRequestState {
5605 #[default]
5610 Pending,
5611 WaitingOnScript,
5617}
5618
5619struct ScreenshotReadinessRequest {
5620 webview_id: WebViewId,
5621 state: Cell<ScreenshotRequestState>,
5622 pipeline_states: RefCell<FxHashMap<PipelineId, Option<Epoch>>>,
5623}