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