1use std::cell::{Cell, RefCell};
21use std::collections::{HashMap, HashSet};
22use std::default::Default;
23use std::option::Option;
24use std::rc::Rc;
25use std::result::Result;
26use std::sync::Arc;
27use std::sync::atomic::{AtomicBool, Ordering};
28use std::thread::{self, JoinHandle};
29use std::time::{Duration, Instant, SystemTime};
30
31use background_hang_monitor_api::{
32 BackgroundHangMonitor, BackgroundHangMonitorExitSignal, HangAnnotation, MonitoredComponentId,
33 MonitoredComponentType,
34};
35use base::cross_process_instant::CrossProcessInstant;
36use base::id::{BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespace, WebViewId};
37use canvas_traits::webgl::WebGLPipeline;
38use chrono::{DateTime, Local};
39use compositing_traits::{CrossProcessCompositorApi, PipelineExitSource};
40use constellation_traits::{
41 JsEvalResult, LoadData, LoadOrigin, NavigationHistoryBehavior, ScriptToConstellationChan,
42 ScriptToConstellationMessage, StructuredSerializedData, WindowSizeType,
43};
44use crossbeam_channel::unbounded;
45use data_url::mime::Mime;
46use devtools_traits::{
47 CSSError, DevtoolScriptControlMsg, DevtoolsPageInfo, NavigationState,
48 ScriptToDevtoolsControlMsg, WorkerId,
49};
50use embedder_traits::user_content_manager::UserContentManager;
51use embedder_traits::{
52 FocusSequenceNumber, InputEvent, JavaScriptEvaluationError, JavaScriptEvaluationId,
53 MediaSessionActionType, MouseButton, MouseButtonAction, MouseButtonEvent, Theme,
54 ViewportDetails, WebDriverScriptCommand,
55};
56use euclid::Point2D;
57use euclid::default::Rect;
58use fonts::{FontContext, SystemFontServiceProxy};
59use headers::{HeaderMapExt, LastModified, ReferrerPolicy as ReferrerPolicyHeader};
60use http::header::REFRESH;
61use hyper_serde::Serde;
62use ipc_channel::ipc;
63use ipc_channel::router::ROUTER;
64use js::glue::GetWindowProxyClass;
65use js::jsapi::{
66 JS_AddInterruptCallback, JSContext as UnsafeJSContext, JSTracer, SetWindowProxyClass,
67};
68use js::jsval::UndefinedValue;
69use js::rust::ParentRuntime;
70use layout_api::{LayoutConfig, LayoutFactory, RestyleReason, ScriptThreadFactory};
71use media::WindowGLContext;
72use metrics::MAX_TASK_NS;
73use net_traits::image_cache::{ImageCache, ImageCacheResponseMessage};
74use net_traits::request::{Referrer, RequestId};
75use net_traits::response::ResponseInit;
76use net_traits::storage_thread::StorageType;
77use net_traits::{
78 FetchMetadata, FetchResponseListener, FetchResponseMsg, Metadata, NetworkError,
79 ResourceFetchTiming, ResourceThreads, ResourceTimingType,
80};
81use percent_encoding::percent_decode;
82use profile_traits::mem::{ProcessReports, ReportsChan, perform_memory_report};
83use profile_traits::time::ProfilerCategory;
84use profile_traits::time_profile;
85use script_traits::{
86 ConstellationInputEvent, DiscardBrowsingContext, DocumentActivity, InitialScriptState,
87 NewLayoutInfo, Painter, ProgressiveWebMetricType, ScriptThreadMessage, UpdatePipelineIdReason,
88};
89use servo_config::{opts, prefs};
90use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
91use style::thread_state::{self, ThreadState};
92use stylo_atoms::Atom;
93use timers::{TimerEventRequest, TimerId, TimerScheduler};
94use url::Position;
95#[cfg(feature = "webgpu")]
96use webgpu_traits::{WebGPUDevice, WebGPUMsg};
97use webrender_api::ExternalScrollId;
98use webrender_api::units::{DevicePixel, LayoutVector2D};
99
100use crate::document_collection::DocumentCollection;
101use crate::document_loader::DocumentLoader;
102use crate::dom::bindings::cell::DomRefCell;
103use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
104 DocumentMethods, DocumentReadyState,
105};
106use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods;
107use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
108use crate::dom::bindings::conversions::{
109 ConversionResult, SafeFromJSValConvertible, StringificationBehavior,
110};
111use crate::dom::bindings::inheritance::Castable;
112use crate::dom::bindings::refcounted::Trusted;
113use crate::dom::bindings::reflector::DomGlobal;
114use crate::dom::bindings::root::{Dom, DomRoot};
115use crate::dom::bindings::settings_stack::AutoEntryScript;
116use crate::dom::bindings::str::DOMString;
117use crate::dom::bindings::trace::{HashMapTracedValues, JSTraceable};
118use crate::dom::csp::{CspReporting, GlobalCspReporting, Violation};
119use crate::dom::customelementregistry::{
120 CallbackReaction, CustomElementDefinition, CustomElementReactionStack,
121};
122use crate::dom::document::{
123 Document, DocumentSource, FocusInitiator, HasBrowsingContext, IsHTMLDocument,
124};
125use crate::dom::element::Element;
126use crate::dom::globalscope::GlobalScope;
127use crate::dom::html::htmliframeelement::HTMLIFrameElement;
128use crate::dom::html::htmlslotelement::HTMLSlotElement;
129use crate::dom::mutationobserver::MutationObserver;
130use crate::dom::node::NodeTraits;
131use crate::dom::servoparser::{ParserContext, ServoParser};
132use crate::dom::types::DebuggerGlobalScope;
133#[cfg(feature = "webgpu")]
134use crate::dom::webgpu::identityhub::IdentityHub;
135use crate::dom::window::Window;
136use crate::dom::windowproxy::{CreatorBrowsingContextInfo, WindowProxy};
137use crate::dom::worklet::WorkletThreadPool;
138use crate::dom::workletglobalscope::WorkletGlobalScopeInit;
139use crate::fetch::FetchCanceller;
140use crate::messaging::{
141 CommonScriptMsg, MainThreadScriptMsg, MixedMessage, ScriptEventLoopSender,
142 ScriptThreadReceivers, ScriptThreadSenders,
143};
144use crate::microtask::{Microtask, MicrotaskQueue};
145use crate::mime::{APPLICATION, MimeExt, TEXT, XML};
146use crate::navigation::{InProgressLoad, NavigationListener};
147use crate::realms::enter_realm;
148use crate::script_module::ScriptFetchOptions;
149use crate::script_runtime::{
150 CanGc, IntroductionType, JSContext, JSContextHelper, Runtime, ScriptThreadEventCategory,
151 ThreadSafeJSContext,
152};
153use crate::task_queue::TaskQueue;
154use crate::task_source::{SendableTaskSource, TaskSourceName};
155use crate::webdriver_handlers::jsval_to_webdriver;
156use crate::{devtools, webdriver_handlers};
157
158thread_local!(static SCRIPT_THREAD_ROOT: Cell<Option<*const ScriptThread>> = const { Cell::new(None) });
159
160fn with_optional_script_thread<R>(f: impl FnOnce(Option<&ScriptThread>) -> R) -> R {
161 SCRIPT_THREAD_ROOT.with(|root| {
162 f(root
163 .get()
164 .and_then(|script_thread| unsafe { script_thread.as_ref() }))
165 })
166}
167
168pub(crate) fn with_script_thread<R: Default>(f: impl FnOnce(&ScriptThread) -> R) -> R {
169 with_optional_script_thread(|script_thread| script_thread.map(f).unwrap_or_default())
170}
171
172pub(crate) unsafe fn trace_thread(tr: *mut JSTracer) {
178 with_script_thread(|script_thread| {
179 trace!("tracing fields of ScriptThread");
180 unsafe { script_thread.trace(tr) };
181 })
182}
183
184pub(crate) struct IncompleteParserContexts(RefCell<Vec<(PipelineId, ParserContext)>>);
190
191unsafe_no_jsmanaged_fields!(TaskQueue<MainThreadScriptMsg>);
192
193type NodeIdSet = HashSet<String>;
194
195#[derive(JSTraceable)]
196#[cfg_attr(crown, allow(crown::unrooted_must_root))]
198pub struct ScriptThread {
199 last_render_opportunity_time: Cell<Option<Instant>>,
201
202 documents: DomRefCell<DocumentCollection>,
204 window_proxies: DomRefCell<HashMapTracedValues<BrowsingContextId, Dom<WindowProxy>>>,
207 incomplete_loads: DomRefCell<Vec<InProgressLoad>>,
209 incomplete_parser_contexts: IncompleteParserContexts,
211 #[no_trace]
213 image_cache: Arc<dyn ImageCache>,
214
215 receivers: ScriptThreadReceivers,
218
219 senders: ScriptThreadSenders,
222
223 #[no_trace]
226 resource_threads: ResourceThreads,
227
228 task_queue: TaskQueue<MainThreadScriptMsg>,
230
231 #[no_trace]
233 background_hang_monitor: Box<dyn BackgroundHangMonitor>,
234 closing: Arc<AtomicBool>,
236
237 #[no_trace]
240 timer_scheduler: RefCell<TimerScheduler>,
241
242 #[no_trace]
244 system_font_service: Arc<SystemFontServiceProxy>,
245
246 js_runtime: Rc<Runtime>,
248
249 #[no_trace]
251 closed_pipelines: DomRefCell<HashSet<PipelineId>>,
252
253 microtask_queue: Rc<MicrotaskQueue>,
255
256 mutation_observer_microtask_queued: Cell<bool>,
258
259 mutation_observers: DomRefCell<Vec<Dom<MutationObserver>>>,
261
262 signal_slots: DomRefCell<Vec<Dom<HTMLSlotElement>>>,
264
265 #[no_trace]
267 webgl_chan: Option<WebGLPipeline>,
268
269 #[no_trace]
271 #[cfg(feature = "webxr")]
272 webxr_registry: Option<webxr_api::Registry>,
273
274 worklet_thread_pool: DomRefCell<Option<Rc<WorkletThreadPool>>>,
276
277 docs_with_no_blocking_loads: DomRefCell<HashSet<Dom<Document>>>,
280
281 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
283
284 #[no_trace]
286 compositor_api: CrossProcessCompositorApi,
287
288 profile_script_events: bool,
290
291 print_pwm: bool,
293
294 unminify_js: bool,
296
297 local_script_source: Option<String>,
299
300 unminify_css: bool,
302
303 #[no_trace]
305 user_content_manager: UserContentManager,
306
307 #[no_trace]
309 player_context: WindowGLContext,
310
311 #[no_trace]
313 pipeline_to_node_ids: DomRefCell<HashMap<PipelineId, NodeIdSet>>,
314
315 is_user_interacting: Cell<bool>,
317
318 #[no_trace]
320 #[cfg(feature = "webgpu")]
321 gpu_id_hub: Arc<IdentityHub>,
322
323 inherited_secure_context: Option<bool>,
325
326 #[no_trace]
328 layout_factory: Arc<dyn LayoutFactory>,
329
330 #[no_trace]
332 relative_mouse_down_point: Cell<Point2D<f32, DevicePixel>>,
333
334 #[no_trace]
338 scheduled_update_the_rendering: RefCell<Option<TimerId>>,
339
340 needs_rendering_update: Arc<AtomicBool>,
347
348 debugger_global: Dom<DebuggerGlobalScope>,
349
350 #[no_trace]
352 privileged_urls: Vec<ServoUrl>,
353}
354
355struct BHMExitSignal {
356 closing: Arc<AtomicBool>,
357 js_context: ThreadSafeJSContext,
358}
359
360impl BackgroundHangMonitorExitSignal for BHMExitSignal {
361 fn signal_to_exit(&self) {
362 self.closing.store(true, Ordering::SeqCst);
363 self.js_context.request_interrupt_callback();
364 }
365}
366
367#[allow(unsafe_code)]
368unsafe extern "C" fn interrupt_callback(_cx: *mut UnsafeJSContext) -> bool {
369 let res = ScriptThread::can_continue_running();
370 if !res {
371 ScriptThread::prepare_for_shutdown();
372 }
373 res
374}
375
376struct ScriptMemoryFailsafe<'a> {
381 owner: Option<&'a ScriptThread>,
382}
383
384impl<'a> ScriptMemoryFailsafe<'a> {
385 fn neuter(&mut self) {
386 self.owner = None;
387 }
388
389 fn new(owner: &'a ScriptThread) -> ScriptMemoryFailsafe<'a> {
390 ScriptMemoryFailsafe { owner: Some(owner) }
391 }
392}
393
394impl Drop for ScriptMemoryFailsafe<'_> {
395 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
396 fn drop(&mut self) {
397 if let Some(owner) = self.owner {
398 for (_, document) in owner.documents.borrow().iter() {
399 document.window().clear_js_runtime_for_script_deallocation();
400 }
401 }
402 }
403}
404
405impl ScriptThreadFactory for ScriptThread {
406 fn create(
407 state: InitialScriptState,
408 layout_factory: Arc<dyn LayoutFactory>,
409 system_font_service: Arc<SystemFontServiceProxy>,
410 load_data: LoadData,
411 ) -> JoinHandle<()> {
412 thread::Builder::new()
413 .name(format!("Script{:?}", state.id))
414 .spawn(move || {
415 thread_state::initialize(ThreadState::SCRIPT | ThreadState::LAYOUT);
416 PipelineNamespace::install(state.pipeline_namespace_id);
417 WebViewId::install(state.webview_id);
418 let memory_profiler_sender = state.memory_profiler_sender.clone();
419
420 let in_progress_load = InProgressLoad::new(
421 state.id,
422 state.browsing_context_id,
423 state.webview_id,
424 state.parent_info,
425 state.opener,
426 state.viewport_details,
427 state.theme,
428 MutableOrigin::new(load_data.url.origin()),
429 load_data,
430 );
431 let reporter_name = format!("script-reporter-{:?}", state.id);
432 let script_thread = ScriptThread::new(state, layout_factory, system_font_service);
433
434 SCRIPT_THREAD_ROOT.with(|root| {
435 root.set(Some(&script_thread as *const _));
436 });
437
438 let mut failsafe = ScriptMemoryFailsafe::new(&script_thread);
439
440 script_thread.pre_page_load(in_progress_load);
441
442 memory_profiler_sender.run_with_memory_reporting(
443 || {
444 script_thread.start(CanGc::note());
445
446 let _ = script_thread
447 .senders
448 .content_process_shutdown_sender
449 .send(());
450 },
451 reporter_name,
452 ScriptEventLoopSender::MainThread(script_thread.senders.self_sender.clone()),
453 CommonScriptMsg::CollectReports,
454 );
455
456 failsafe.neuter();
458 })
459 .expect("Thread spawning failed")
460 }
461}
462
463impl ScriptThread {
464 pub(crate) fn runtime_handle() -> ParentRuntime {
465 with_optional_script_thread(|script_thread| {
466 script_thread.unwrap().js_runtime.prepare_for_new_child()
467 })
468 }
469
470 pub(crate) fn can_continue_running() -> bool {
471 with_script_thread(|script_thread| script_thread.can_continue_running_inner())
472 }
473
474 pub(crate) fn prepare_for_shutdown() {
475 with_script_thread(|script_thread| {
476 script_thread.prepare_for_shutdown_inner();
477 })
478 }
479
480 pub(crate) fn set_mutation_observer_microtask_queued(value: bool) {
481 with_script_thread(|script_thread| {
482 script_thread.mutation_observer_microtask_queued.set(value);
483 })
484 }
485
486 pub(crate) fn is_mutation_observer_microtask_queued() -> bool {
487 with_script_thread(|script_thread| script_thread.mutation_observer_microtask_queued.get())
488 }
489
490 pub(crate) fn add_mutation_observer(observer: &MutationObserver) {
491 with_script_thread(|script_thread| {
492 script_thread
493 .mutation_observers
494 .borrow_mut()
495 .push(Dom::from_ref(observer));
496 })
497 }
498
499 pub(crate) fn get_mutation_observers() -> Vec<DomRoot<MutationObserver>> {
500 with_script_thread(|script_thread| {
501 script_thread
502 .mutation_observers
503 .borrow()
504 .iter()
505 .map(|o| DomRoot::from_ref(&**o))
506 .collect()
507 })
508 }
509
510 pub(crate) fn add_signal_slot(observer: &HTMLSlotElement) {
511 with_script_thread(|script_thread| {
512 script_thread
513 .signal_slots
514 .borrow_mut()
515 .push(Dom::from_ref(observer));
516 })
517 }
518
519 pub(crate) fn take_signal_slots() -> Vec<DomRoot<HTMLSlotElement>> {
520 with_script_thread(|script_thread| {
521 script_thread
522 .signal_slots
523 .take()
524 .into_iter()
525 .inspect(|slot| {
526 slot.remove_from_signal_slots();
527 })
528 .map(|slot| slot.as_rooted())
529 .collect()
530 })
531 }
532
533 pub(crate) fn mark_document_with_no_blocked_loads(doc: &Document) {
534 with_script_thread(|script_thread| {
535 script_thread
536 .docs_with_no_blocking_loads
537 .borrow_mut()
538 .insert(Dom::from_ref(doc));
539 })
540 }
541
542 pub(crate) fn page_headers_available(
543 id: &PipelineId,
544 metadata: Option<Metadata>,
545 can_gc: CanGc,
546 ) -> Option<DomRoot<ServoParser>> {
547 with_script_thread(|script_thread| {
548 script_thread.handle_page_headers_available(id, metadata, can_gc)
549 })
550 }
551
552 pub(crate) fn process_event(msg: CommonScriptMsg) -> bool {
556 with_script_thread(|script_thread| {
557 if !script_thread.can_continue_running_inner() {
558 return false;
559 }
560 script_thread.handle_msg_from_script(MainThreadScriptMsg::Common(msg));
561 true
562 })
563 }
564
565 pub(crate) fn schedule_timer(&self, request: TimerEventRequest) -> TimerId {
567 self.timer_scheduler.borrow_mut().schedule_timer(request)
568 }
569
570 pub(crate) fn cancel_timer(&self, timer_id: TimerId) {
573 self.timer_scheduler.borrow_mut().cancel_timer(timer_id)
574 }
575
576 pub(crate) fn await_stable_state(task: Microtask) {
578 with_script_thread(|script_thread| {
579 script_thread
580 .microtask_queue
581 .enqueue(task, script_thread.get_cx());
582 });
583 }
584
585 pub(crate) fn check_load_origin(source: &LoadOrigin, target: &ImmutableOrigin) -> bool {
590 match (source, target) {
591 (LoadOrigin::Constellation, _) | (LoadOrigin::WebDriver, _) => {
592 true
594 },
595 (_, ImmutableOrigin::Opaque(_)) => {
596 true
600 },
601 (LoadOrigin::Script(source_origin), _) => source_origin == target,
602 }
603 }
604
605 pub(crate) fn set_needs_rendering_update(&self) {
609 self.needs_rendering_update.store(true, Ordering::Relaxed);
610 }
611
612 pub(crate) fn navigate(
614 pipeline_id: PipelineId,
615 mut load_data: LoadData,
616 history_handling: NavigationHistoryBehavior,
617 ) {
618 with_script_thread(|script_thread| {
619 let is_javascript = load_data.url.scheme() == "javascript";
620 if is_javascript {
623 let window = match script_thread.documents.borrow().find_window(pipeline_id) {
624 None => return,
625 Some(window) => window,
626 };
627 let global = window.as_global_scope();
628 let trusted_global = Trusted::new(global);
629 let sender = script_thread
630 .senders
631 .pipeline_to_constellation_sender
632 .clone();
633 let task = task!(navigate_javascript: move || {
634 if trusted_global.root().is::<Window>() {
636 let global = &trusted_global.root();
637 if Self::navigate_to_javascript_url(global, global, &mut load_data, None, CanGc::note()) {
638 sender
639 .send((pipeline_id, ScriptToConstellationMessage::LoadUrl(load_data, history_handling)))
640 .unwrap();
641 }
642 }
643 });
644 global
646 .task_manager()
647 .dom_manipulation_task_source()
648 .queue(task);
649 } else {
650 script_thread
651 .senders
652 .pipeline_to_constellation_sender
653 .send((
654 pipeline_id,
655 ScriptToConstellationMessage::LoadUrl(load_data, history_handling),
656 ))
657 .expect("Sending a LoadUrl message to the constellation failed");
658 }
659 });
660 }
661
662 pub(crate) fn navigate_to_javascript_url(
664 global: &GlobalScope,
665 containing_global: &GlobalScope,
666 load_data: &mut LoadData,
667 container: Option<&Element>,
668 can_gc: CanGc,
669 ) -> bool {
670 if !Self::check_load_origin(&load_data.load_origin, &global.get_url().origin()) {
674 return false;
675 }
676
677 if global
680 .get_csp_list()
681 .should_navigation_request_be_blocked(global, load_data, container, can_gc)
682 {
683 return false;
684 }
685
686 Self::eval_js_url(containing_global, load_data, can_gc);
689 true
690 }
691
692 pub(crate) fn process_attach_layout(new_layout_info: NewLayoutInfo, origin: MutableOrigin) {
693 with_script_thread(|script_thread| {
694 let pipeline_id = Some(new_layout_info.new_pipeline_id);
695 script_thread.profile_event(
696 ScriptThreadEventCategory::AttachLayout,
697 pipeline_id,
698 || {
699 script_thread.handle_new_layout(new_layout_info, origin);
700 },
701 )
702 });
703 }
704
705 pub(crate) fn get_top_level_for_browsing_context(
706 sender_pipeline: PipelineId,
707 browsing_context_id: BrowsingContextId,
708 ) -> Option<WebViewId> {
709 with_script_thread(|script_thread| {
710 script_thread.ask_constellation_for_top_level_info(sender_pipeline, browsing_context_id)
711 })
712 }
713
714 pub(crate) fn find_document(id: PipelineId) -> Option<DomRoot<Document>> {
715 with_script_thread(|script_thread| script_thread.documents.borrow().find_document(id))
716 }
717
718 pub(crate) fn set_user_interacting(interacting: bool) {
719 with_script_thread(|script_thread| {
720 script_thread.is_user_interacting.set(interacting);
721 });
722 }
723
724 pub(crate) fn is_user_interacting() -> bool {
725 with_script_thread(|script_thread| script_thread.is_user_interacting.get())
726 }
727
728 pub(crate) fn get_fully_active_document_ids(&self) -> HashSet<PipelineId> {
729 self.documents
730 .borrow()
731 .iter()
732 .filter_map(|(id, document)| {
733 if document.is_fully_active() {
734 Some(id)
735 } else {
736 None
737 }
738 })
739 .fold(HashSet::new(), |mut set, id| {
740 let _ = set.insert(id);
741 set
742 })
743 }
744
745 pub(crate) fn find_window_proxy(id: BrowsingContextId) -> Option<DomRoot<WindowProxy>> {
746 with_script_thread(|script_thread| {
747 script_thread
748 .window_proxies
749 .borrow()
750 .get(&id)
751 .map(|context| DomRoot::from_ref(&**context))
752 })
753 }
754
755 pub(crate) fn find_window_proxy_by_name(name: &DOMString) -> Option<DomRoot<WindowProxy>> {
756 with_script_thread(|script_thread| {
757 for (_, proxy) in script_thread.window_proxies.borrow().iter() {
758 if proxy.get_name() == *name {
759 return Some(DomRoot::from_ref(&**proxy));
760 }
761 }
762 None
763 })
764 }
765
766 pub(crate) fn worklet_thread_pool(image_cache: Arc<dyn ImageCache>) -> Rc<WorkletThreadPool> {
768 with_optional_script_thread(|script_thread| {
769 let script_thread = script_thread.unwrap();
770 script_thread
771 .worklet_thread_pool
772 .borrow_mut()
773 .get_or_insert_with(|| {
774 let init = WorkletGlobalScopeInit {
775 to_script_thread_sender: script_thread.senders.self_sender.clone(),
776 resource_threads: script_thread.resource_threads.clone(),
777 mem_profiler_chan: script_thread.senders.memory_profiler_sender.clone(),
778 time_profiler_chan: script_thread.senders.time_profiler_sender.clone(),
779 devtools_chan: script_thread.senders.devtools_server_sender.clone(),
780 to_constellation_sender: script_thread
781 .senders
782 .pipeline_to_constellation_sender
783 .clone(),
784 image_cache,
785 #[cfg(feature = "webgpu")]
786 gpu_id_hub: script_thread.gpu_id_hub.clone(),
787 inherited_secure_context: script_thread.inherited_secure_context,
788 };
789 Rc::new(WorkletThreadPool::spawn(init))
790 })
791 .clone()
792 })
793 }
794
795 fn handle_register_paint_worklet(
796 &self,
797 pipeline_id: PipelineId,
798 name: Atom,
799 properties: Vec<Atom>,
800 painter: Box<dyn Painter>,
801 ) {
802 let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
803 warn!("Paint worklet registered after pipeline {pipeline_id} closed.");
804 return;
805 };
806
807 window
808 .layout_mut()
809 .register_paint_worklet_modules(name, properties, painter);
810 }
811
812 pub(crate) fn custom_element_reaction_stack() -> Rc<CustomElementReactionStack> {
813 with_optional_script_thread(|script_thread| {
814 script_thread
815 .as_ref()
816 .unwrap()
817 .custom_element_reaction_stack
818 .clone()
819 })
820 }
821
822 pub(crate) fn enqueue_callback_reaction(
823 element: &Element,
824 reaction: CallbackReaction,
825 definition: Option<Rc<CustomElementDefinition>>,
826 ) {
827 with_script_thread(|script_thread| {
828 script_thread
829 .custom_element_reaction_stack
830 .enqueue_callback_reaction(element, reaction, definition);
831 })
832 }
833
834 pub(crate) fn enqueue_upgrade_reaction(
835 element: &Element,
836 definition: Rc<CustomElementDefinition>,
837 ) {
838 with_script_thread(|script_thread| {
839 script_thread
840 .custom_element_reaction_stack
841 .enqueue_upgrade_reaction(element, definition);
842 })
843 }
844
845 pub(crate) fn invoke_backup_element_queue(can_gc: CanGc) {
846 with_script_thread(|script_thread| {
847 script_thread
848 .custom_element_reaction_stack
849 .invoke_backup_element_queue(can_gc);
850 })
851 }
852
853 pub(crate) fn save_node_id(pipeline: PipelineId, node_id: String) {
854 with_script_thread(|script_thread| {
855 script_thread
856 .pipeline_to_node_ids
857 .borrow_mut()
858 .entry(pipeline)
859 .or_default()
860 .insert(node_id);
861 })
862 }
863
864 pub(crate) fn has_node_id(pipeline: PipelineId, node_id: &str) -> bool {
865 with_script_thread(|script_thread| {
866 script_thread
867 .pipeline_to_node_ids
868 .borrow()
869 .get(&pipeline)
870 .is_some_and(|node_ids| node_ids.contains(node_id))
871 })
872 }
873
874 pub(crate) fn new(
876 state: InitialScriptState,
877 layout_factory: Arc<dyn LayoutFactory>,
878 system_font_service: Arc<SystemFontServiceProxy>,
879 ) -> ScriptThread {
880 let (self_sender, self_receiver) = unbounded();
881 let runtime = Runtime::new(Some(SendableTaskSource {
882 sender: ScriptEventLoopSender::MainThread(self_sender.clone()),
883 pipeline_id: state.id,
884 name: TaskSourceName::Networking,
885 canceller: Default::default(),
886 }));
887 let cx = runtime.cx();
888
889 unsafe {
890 SetWindowProxyClass(cx, GetWindowProxyClass());
891 JS_AddInterruptCallback(cx, Some(interrupt_callback));
892 }
893
894 let constellation_receiver = state.constellation_receiver.route_preserving_errors();
895
896 let devtools_server_sender = state.devtools_server_sender;
898 let (ipc_devtools_sender, ipc_devtools_receiver) = ipc::channel().unwrap();
899 let devtools_server_receiver = devtools_server_sender
900 .as_ref()
901 .map(|_| ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_devtools_receiver))
902 .unwrap_or_else(crossbeam_channel::never);
903
904 let task_queue = TaskQueue::new(self_receiver, self_sender.clone());
905
906 let closing = Arc::new(AtomicBool::new(false));
907 let background_hang_monitor_exit_signal = BHMExitSignal {
908 closing: closing.clone(),
909 js_context: runtime.thread_safe_js_context(),
910 };
911
912 let background_hang_monitor = state.background_hang_monitor_register.register_component(
913 MonitoredComponentId(state.id, MonitoredComponentType::Script),
914 Duration::from_millis(1000),
915 Duration::from_millis(5000),
916 Box::new(background_hang_monitor_exit_signal),
917 );
918
919 let (image_cache_sender, image_cache_receiver) = unbounded();
920 let (ipc_image_cache_sender, ipc_image_cache_receiver) = ipc::channel().unwrap();
921 ROUTER.add_typed_route(
922 ipc_image_cache_receiver,
923 Box::new(move |message| {
924 let _ = image_cache_sender.send(message.unwrap());
925 }),
926 );
927
928 let receivers = ScriptThreadReceivers {
929 constellation_receiver,
930 image_cache_receiver,
931 devtools_server_receiver,
932 #[cfg(feature = "webgpu")]
934 webgpu_receiver: RefCell::new(crossbeam_channel::never()),
935 };
936
937 let opts = opts::get();
938 let senders = ScriptThreadSenders {
939 self_sender,
940 #[cfg(feature = "bluetooth")]
941 bluetooth_sender: state.bluetooth_sender,
942 constellation_sender: state.constellation_sender,
943 pipeline_to_constellation_sender: state.pipeline_to_constellation_sender.sender.clone(),
944 image_cache_sender: ipc_image_cache_sender,
945 time_profiler_sender: state.time_profiler_sender,
946 memory_profiler_sender: state.memory_profiler_sender,
947 devtools_server_sender,
948 devtools_client_to_script_thread_sender: ipc_devtools_sender,
949 content_process_shutdown_sender: state.content_process_shutdown_sender,
950 };
951
952 let microtask_queue = runtime.microtask_queue.clone();
953 let js_runtime = Rc::new(runtime);
954 #[cfg(feature = "webgpu")]
955 let gpu_id_hub = Arc::new(IdentityHub::default());
956
957 let pipeline_id = PipelineId::new();
958 let script_to_constellation_chan = ScriptToConstellationChan {
959 sender: senders.pipeline_to_constellation_sender.clone(),
960 pipeline_id,
961 };
962 let debugger_global = DebuggerGlobalScope::new(
963 PipelineId::new(),
964 senders.devtools_server_sender.clone(),
965 senders.devtools_client_to_script_thread_sender.clone(),
966 senders.memory_profiler_sender.clone(),
967 senders.time_profiler_sender.clone(),
968 script_to_constellation_chan,
969 state.resource_threads.clone(),
970 #[cfg(feature = "webgpu")]
971 gpu_id_hub.clone(),
972 CanGc::note(),
973 );
974 debugger_global.execute(CanGc::note());
975
976 ScriptThread {
977 documents: DomRefCell::new(DocumentCollection::default()),
978 last_render_opportunity_time: Default::default(),
979 window_proxies: DomRefCell::new(HashMapTracedValues::new()),
980 incomplete_loads: DomRefCell::new(vec![]),
981 incomplete_parser_contexts: IncompleteParserContexts(RefCell::new(vec![])),
982 senders,
983 receivers,
984 image_cache: state.image_cache.clone(),
985 resource_threads: state.resource_threads,
986 task_queue,
987 background_hang_monitor,
988 closing,
989 timer_scheduler: Default::default(),
990 microtask_queue,
991 js_runtime,
992 closed_pipelines: DomRefCell::new(HashSet::new()),
993 mutation_observer_microtask_queued: Default::default(),
994 mutation_observers: Default::default(),
995 signal_slots: Default::default(),
996 system_font_service,
997 webgl_chan: state.webgl_chan,
998 #[cfg(feature = "webxr")]
999 webxr_registry: state.webxr_registry,
1000 worklet_thread_pool: Default::default(),
1001 docs_with_no_blocking_loads: Default::default(),
1002 custom_element_reaction_stack: Rc::new(CustomElementReactionStack::new()),
1003 compositor_api: state.compositor_api,
1004 profile_script_events: opts.debug.profile_script_events,
1005 print_pwm: opts.print_pwm,
1006 unminify_js: opts.unminify_js,
1007 local_script_source: opts.local_script_source.clone(),
1008 unminify_css: opts.unminify_css,
1009 user_content_manager: state.user_content_manager,
1010 player_context: state.player_context,
1011 pipeline_to_node_ids: Default::default(),
1012 is_user_interacting: Cell::new(false),
1013 #[cfg(feature = "webgpu")]
1014 gpu_id_hub,
1015 inherited_secure_context: state.inherited_secure_context,
1016 layout_factory,
1017 relative_mouse_down_point: Cell::new(Point2D::zero()),
1018 scheduled_update_the_rendering: Default::default(),
1019 needs_rendering_update: Arc::new(AtomicBool::new(false)),
1020 debugger_global: debugger_global.as_traced(),
1021 privileged_urls: state.privileged_urls,
1022 }
1023 }
1024
1025 #[allow(unsafe_code)]
1026 pub(crate) fn get_cx(&self) -> JSContext {
1027 unsafe { JSContext::from_ptr(self.js_runtime.cx()) }
1028 }
1029
1030 fn can_continue_running_inner(&self) -> bool {
1032 if self.closing.load(Ordering::SeqCst) {
1033 return false;
1034 }
1035 true
1036 }
1037
1038 fn prepare_for_shutdown_inner(&self) {
1040 let docs = self.documents.borrow();
1041 for (_, document) in docs.iter() {
1042 document
1043 .owner_global()
1044 .task_manager()
1045 .cancel_all_tasks_and_ignore_future_tasks();
1046 }
1047 }
1048
1049 pub(crate) fn start(&self, can_gc: CanGc) {
1052 debug!("Starting script thread.");
1053 while self.handle_msgs(can_gc) {
1054 debug!("Running script thread.");
1056 }
1057 debug!("Stopped script thread.");
1058 }
1059
1060 fn process_pending_input_events(&self, pipeline_id: PipelineId, can_gc: CanGc) {
1062 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
1063 warn!("Processing pending compositor events for closed pipeline {pipeline_id}.");
1064 return;
1065 };
1066 if document.window().Closed() {
1068 warn!("Compositor event sent to a pipeline with a closed window {pipeline_id}.");
1069 return;
1070 }
1071 ScriptThread::set_user_interacting(true);
1072
1073 document.event_handler().handle_pending_input_events(can_gc);
1074 ScriptThread::set_user_interacting(false);
1075 }
1076
1077 fn cancel_scheduled_update_the_rendering(&self) {
1078 if let Some(timer_id) = self.scheduled_update_the_rendering.borrow_mut().take() {
1079 self.timer_scheduler.borrow_mut().cancel_timer(timer_id);
1080 }
1081 }
1082
1083 fn schedule_update_the_rendering_timer_if_necessary(&self, delay: Duration) {
1084 if self.scheduled_update_the_rendering.borrow().is_some() {
1085 return;
1086 }
1087
1088 debug!("Scheduling ScriptThread animation frame.");
1089 let trigger_script_thread_animation = self.needs_rendering_update.clone();
1090 let timer_id = self.schedule_timer(TimerEventRequest {
1091 callback: Box::new(move || {
1092 trigger_script_thread_animation.store(true, Ordering::Relaxed);
1093 }),
1094 duration: delay,
1095 });
1096
1097 *self.scheduled_update_the_rendering.borrow_mut() = Some(timer_id);
1098 }
1099
1100 pub(crate) fn update_the_rendering(&self, can_gc: CanGc) -> bool {
1107 self.last_render_opportunity_time.set(Some(Instant::now()));
1108 self.cancel_scheduled_update_the_rendering();
1109 self.needs_rendering_update.store(false, Ordering::Relaxed);
1110
1111 if !self.can_continue_running_inner() {
1112 return false;
1113 }
1114
1115 let documents_in_order = self.documents.borrow().documents_in_order();
1138
1139 let mut should_generate_frame = false;
1144 for pipeline_id in documents_in_order.iter() {
1145 let document = self
1146 .documents
1147 .borrow()
1148 .find_document(*pipeline_id)
1149 .expect("Got pipeline for Document not managed by this ScriptThread.");
1150
1151 if !document.is_fully_active() {
1152 continue;
1153 }
1154
1155 if document.waiting_on_canvas_image_updates() {
1156 continue;
1157 }
1158
1159 self.process_pending_input_events(*pipeline_id, can_gc);
1166
1167 let resized = document.window().run_the_resize_steps(can_gc);
1169
1170 document.run_the_scroll_steps(can_gc);
1172
1173 if resized {
1175 document
1177 .window()
1178 .evaluate_media_queries_and_report_changes(can_gc);
1179
1180 document.react_to_environment_changes()
1183 }
1184
1185 document.update_animations_and_send_events(can_gc);
1189
1190 document.run_the_animation_frame_callbacks(can_gc);
1200
1201 let _realm = enter_realm(&*document);
1203 let mut depth = Default::default();
1204 while document.gather_active_resize_observations_at_depth(&depth) {
1205 depth = document.broadcast_active_resize_observations(can_gc);
1207 }
1208
1209 if document.has_skipped_resize_observations() {
1210 document.deliver_resize_loop_error_notification(can_gc);
1211 }
1212 document.set_resize_observer_started_observing_target(false);
1213
1214 document.update_intersection_observer_steps(CrossProcessInstant::now(), can_gc);
1225
1226 should_generate_frame =
1231 document.update_the_rendering().needs_frame() || should_generate_frame;
1232
1233 }
1236
1237 if should_generate_frame {
1238 self.compositor_api.generate_frame();
1239 }
1240
1241 self.perform_a_microtask_checkpoint(can_gc);
1244 should_generate_frame
1245 }
1246
1247 fn maybe_schedule_rendering_opportunity_after_ipc_message(
1254 &self,
1255 built_any_display_lists: bool,
1256 ) {
1257 let needs_rendering_update = self
1258 .documents
1259 .borrow()
1260 .iter()
1261 .any(|(_, document)| document.needs_rendering_update());
1262 let running_animations = self.documents.borrow().iter().any(|(_, document)| {
1263 document.is_fully_active() &&
1264 !document.window().throttled() &&
1265 (document.animations().running_animation_count() != 0 ||
1266 document.has_active_request_animation_frame_callbacks())
1267 });
1268
1269 if !needs_rendering_update && !running_animations {
1273 return;
1274 }
1275
1276 if running_animations && built_any_display_lists {
1280 return;
1281 }
1282
1283 let animation_delay = if running_animations && !needs_rendering_update {
1291 Duration::from_millis(30)
1295 } else {
1296 Duration::from_millis(20)
1299 };
1300
1301 let time_since_last_rendering_opportunity = self
1302 .last_render_opportunity_time
1303 .get()
1304 .map(|last_render_opportunity_time| Instant::now() - last_render_opportunity_time)
1305 .unwrap_or(Duration::MAX)
1306 .min(animation_delay);
1307 self.schedule_update_the_rendering_timer_if_necessary(
1308 animation_delay - time_since_last_rendering_opportunity,
1309 );
1310 }
1311
1312 fn maybe_fulfill_font_ready_promises(&self, can_gc: CanGc) {
1315 let mut sent_message = false;
1316 for (_, document) in self.documents.borrow().iter() {
1317 sent_message = document.maybe_fulfill_font_ready_promise(can_gc) || sent_message;
1318 }
1319
1320 if sent_message {
1321 self.perform_a_microtask_checkpoint(can_gc);
1322 }
1323 }
1324
1325 fn maybe_send_idle_document_state_to_constellation(&self) {
1329 if !opts::get().wait_for_stable_image {
1330 return;
1331 }
1332 for (_, document) in self.documents.borrow().iter() {
1333 document
1334 .window()
1335 .maybe_send_idle_document_state_to_constellation();
1336 }
1337 }
1338
1339 fn handle_msgs(&self, can_gc: CanGc) -> bool {
1341 let mut sequential = vec![];
1343
1344 self.background_hang_monitor.notify_wait();
1346
1347 debug!("Waiting for event.");
1349 let fully_active = self.get_fully_active_document_ids();
1350 let mut event = self.receivers.recv(
1351 &self.task_queue,
1352 &self.timer_scheduler.borrow(),
1353 &fully_active,
1354 );
1355
1356 loop {
1357 debug!("Handling event: {event:?}");
1358
1359 self.timer_scheduler
1361 .borrow_mut()
1362 .dispatch_completed_timers();
1363
1364 let _realm = event.pipeline_id().map(|id| {
1365 let global = self.documents.borrow().find_global(id);
1366 global.map(|global| enter_realm(&*global))
1367 });
1368
1369 match event {
1371 MixedMessage::FromConstellation(ScriptThreadMessage::AttachLayout(
1375 new_layout_info,
1376 )) => {
1377 let pipeline_id = new_layout_info.new_pipeline_id;
1378 self.profile_event(
1379 ScriptThreadEventCategory::AttachLayout,
1380 Some(pipeline_id),
1381 || {
1382 let not_an_about_blank_and_about_srcdoc_load =
1386 new_layout_info.load_data.url.as_str() != "about:blank" &&
1387 new_layout_info.load_data.url.as_str() != "about:srcdoc";
1388 let origin = if not_an_about_blank_and_about_srcdoc_load {
1389 MutableOrigin::new(new_layout_info.load_data.url.origin())
1390 } else if let Some(parent) =
1391 new_layout_info.parent_info.and_then(|pipeline_id| {
1392 self.documents.borrow().find_document(pipeline_id)
1393 })
1394 {
1395 parent.origin().clone()
1396 } else if let Some(creator) = new_layout_info
1397 .load_data
1398 .creator_pipeline_id
1399 .and_then(|pipeline_id| {
1400 self.documents.borrow().find_document(pipeline_id)
1401 })
1402 {
1403 creator.origin().clone()
1404 } else {
1405 MutableOrigin::new(ImmutableOrigin::new_opaque())
1406 };
1407
1408 self.handle_new_layout(new_layout_info, origin);
1409 },
1410 )
1411 },
1412 MixedMessage::FromConstellation(ScriptThreadMessage::Resize(
1413 id,
1414 size,
1415 size_type,
1416 )) => {
1417 self.handle_resize_message(id, size, size_type);
1418 },
1419 MixedMessage::FromConstellation(ScriptThreadMessage::Viewport(id, rect)) => self
1420 .profile_event(ScriptThreadEventCategory::SetViewport, Some(id), || {
1421 self.handle_viewport(id, rect);
1422 }),
1423 MixedMessage::FromConstellation(ScriptThreadMessage::TickAllAnimations(
1424 _webviews,
1425 )) => {
1426 self.set_needs_rendering_update();
1427 },
1428 MixedMessage::FromConstellation(
1429 ScriptThreadMessage::NoLongerWaitingOnAsychronousImageUpdates(pipeline_id),
1430 ) => {
1431 if let Some(document) = self.documents.borrow().find_document(pipeline_id) {
1432 document.handle_no_longer_waiting_on_asynchronous_image_updates();
1433 }
1434 },
1435 MixedMessage::FromConstellation(ScriptThreadMessage::SendInputEvent(id, event)) => {
1436 self.handle_input_event(id, event)
1437 },
1438 MixedMessage::FromScript(MainThreadScriptMsg::Common(CommonScriptMsg::Task(
1439 _,
1440 _,
1441 _,
1442 TaskSourceName::Rendering,
1443 ))) => {
1444 },
1448 MixedMessage::FromScript(MainThreadScriptMsg::Inactive) => {
1449 },
1452 MixedMessage::FromConstellation(ScriptThreadMessage::ExitFullScreen(id)) => self
1453 .profile_event(ScriptThreadEventCategory::ExitFullscreen, Some(id), || {
1454 self.handle_exit_fullscreen(id, can_gc);
1455 }),
1456 _ => {
1457 sequential.push(event);
1458 },
1459 }
1460
1461 match self.receivers.try_recv(&self.task_queue, &fully_active) {
1465 Some(new_event) => event = new_event,
1466 None => break,
1467 }
1468 }
1469
1470 debug!("Processing events.");
1472 for msg in sequential {
1473 debug!("Processing event {:?}.", msg);
1474 let category = self.categorize_msg(&msg);
1475 let pipeline_id = msg.pipeline_id();
1476 let _realm = pipeline_id.and_then(|id| {
1477 let global = self.documents.borrow().find_global(id);
1478 global.map(|global| enter_realm(&*global))
1479 });
1480
1481 if self.closing.load(Ordering::SeqCst) {
1482 match msg {
1484 MixedMessage::FromConstellation(ScriptThreadMessage::ExitScriptThread) => {
1485 self.handle_exit_script_thread_msg(can_gc);
1486 return false;
1487 },
1488 MixedMessage::FromConstellation(ScriptThreadMessage::ExitPipeline(
1489 webview_id,
1490 pipeline_id,
1491 discard_browsing_context,
1492 )) => {
1493 self.handle_exit_pipeline_msg(
1494 webview_id,
1495 pipeline_id,
1496 discard_browsing_context,
1497 can_gc,
1498 );
1499 },
1500 _ => {},
1501 }
1502 continue;
1503 }
1504
1505 let exiting = self.profile_event(category, pipeline_id, move || {
1506 match msg {
1507 MixedMessage::FromConstellation(ScriptThreadMessage::ExitScriptThread) => {
1508 self.handle_exit_script_thread_msg(can_gc);
1509 return true;
1510 },
1511 MixedMessage::FromConstellation(inner_msg) => {
1512 self.handle_msg_from_constellation(inner_msg, can_gc)
1513 },
1514 MixedMessage::FromScript(inner_msg) => self.handle_msg_from_script(inner_msg),
1515 MixedMessage::FromDevtools(inner_msg) => {
1516 self.handle_msg_from_devtools(inner_msg, can_gc)
1517 },
1518 MixedMessage::FromImageCache(inner_msg) => {
1519 self.handle_msg_from_image_cache(inner_msg)
1520 },
1521 #[cfg(feature = "webgpu")]
1522 MixedMessage::FromWebGPUServer(inner_msg) => {
1523 self.handle_msg_from_webgpu_server(inner_msg)
1524 },
1525 MixedMessage::TimerFired => {},
1526 }
1527
1528 false
1529 });
1530
1531 if exiting {
1533 return false;
1534 }
1535
1536 self.perform_a_microtask_checkpoint(can_gc);
1539 }
1540
1541 for (_, doc) in self.documents.borrow().iter() {
1542 let window = doc.window();
1543 window
1544 .upcast::<GlobalScope>()
1545 .perform_a_dom_garbage_collection_checkpoint();
1546 }
1547
1548 {
1549 let mut docs = self.docs_with_no_blocking_loads.borrow_mut();
1551 for document in docs.iter() {
1552 let _realm = enter_realm(&**document);
1553 document.maybe_queue_document_completion();
1554 }
1555 docs.clear();
1556 }
1557
1558 let built_any_display_lists = self.needs_rendering_update.load(Ordering::Relaxed) &&
1559 self.update_the_rendering(can_gc);
1560
1561 self.maybe_fulfill_font_ready_promises(can_gc);
1562 self.maybe_send_idle_document_state_to_constellation();
1563
1564 self.maybe_schedule_rendering_opportunity_after_ipc_message(built_any_display_lists);
1566
1567 true
1568 }
1569
1570 fn categorize_msg(&self, msg: &MixedMessage) -> ScriptThreadEventCategory {
1571 match *msg {
1572 MixedMessage::FromConstellation(ref inner_msg) => match *inner_msg {
1573 ScriptThreadMessage::SendInputEvent(_, _) => ScriptThreadEventCategory::InputEvent,
1574 _ => ScriptThreadEventCategory::ConstellationMsg,
1575 },
1576 MixedMessage::FromDevtools(_) => ScriptThreadEventCategory::DevtoolsMsg,
1578 MixedMessage::FromImageCache(_) => ScriptThreadEventCategory::ImageCacheMsg,
1579 MixedMessage::FromScript(ref inner_msg) => match *inner_msg {
1580 MainThreadScriptMsg::Common(CommonScriptMsg::Task(category, ..)) => category,
1581 MainThreadScriptMsg::RegisterPaintWorklet { .. } => {
1582 ScriptThreadEventCategory::WorkletEvent
1583 },
1584 _ => ScriptThreadEventCategory::ScriptEvent,
1585 },
1586 #[cfg(feature = "webgpu")]
1587 MixedMessage::FromWebGPUServer(_) => ScriptThreadEventCategory::WebGPUMsg,
1588 MixedMessage::TimerFired => ScriptThreadEventCategory::TimerEvent,
1589 }
1590 }
1591
1592 fn profile_event<F, R>(
1593 &self,
1594 category: ScriptThreadEventCategory,
1595 pipeline_id: Option<PipelineId>,
1596 f: F,
1597 ) -> R
1598 where
1599 F: FnOnce() -> R,
1600 {
1601 self.background_hang_monitor
1602 .notify_activity(HangAnnotation::Script(category.into()));
1603 let start = Instant::now();
1604 let value = if self.profile_script_events {
1605 let profiler_chan = self.senders.time_profiler_sender.clone();
1606 match category {
1607 ScriptThreadEventCategory::AttachLayout => {
1608 time_profile!(ProfilerCategory::ScriptAttachLayout, None, profiler_chan, f)
1609 },
1610 ScriptThreadEventCategory::ConstellationMsg => time_profile!(
1611 ProfilerCategory::ScriptConstellationMsg,
1612 None,
1613 profiler_chan,
1614 f
1615 ),
1616 ScriptThreadEventCategory::DatabaseAccessEvent => time_profile!(
1617 ProfilerCategory::ScriptDatabaseAccessEvent,
1618 None,
1619 profiler_chan,
1620 f
1621 ),
1622 ScriptThreadEventCategory::DevtoolsMsg => {
1623 time_profile!(ProfilerCategory::ScriptDevtoolsMsg, None, profiler_chan, f)
1624 },
1625 ScriptThreadEventCategory::DocumentEvent => time_profile!(
1626 ProfilerCategory::ScriptDocumentEvent,
1627 None,
1628 profiler_chan,
1629 f
1630 ),
1631 ScriptThreadEventCategory::InputEvent => {
1632 time_profile!(ProfilerCategory::ScriptInputEvent, None, profiler_chan, f)
1633 },
1634 ScriptThreadEventCategory::FileRead => {
1635 time_profile!(ProfilerCategory::ScriptFileRead, None, profiler_chan, f)
1636 },
1637 ScriptThreadEventCategory::FontLoading => {
1638 time_profile!(ProfilerCategory::ScriptFontLoading, None, profiler_chan, f)
1639 },
1640 ScriptThreadEventCategory::FormPlannedNavigation => time_profile!(
1641 ProfilerCategory::ScriptPlannedNavigation,
1642 None,
1643 profiler_chan,
1644 f
1645 ),
1646 ScriptThreadEventCategory::HistoryEvent => {
1647 time_profile!(ProfilerCategory::ScriptHistoryEvent, None, profiler_chan, f)
1648 },
1649 ScriptThreadEventCategory::ImageCacheMsg => time_profile!(
1650 ProfilerCategory::ScriptImageCacheMsg,
1651 None,
1652 profiler_chan,
1653 f
1654 ),
1655 ScriptThreadEventCategory::NetworkEvent => {
1656 time_profile!(ProfilerCategory::ScriptNetworkEvent, None, profiler_chan, f)
1657 },
1658 ScriptThreadEventCategory::PortMessage => {
1659 time_profile!(ProfilerCategory::ScriptPortMessage, None, profiler_chan, f)
1660 },
1661 ScriptThreadEventCategory::Resize => {
1662 time_profile!(ProfilerCategory::ScriptResize, None, profiler_chan, f)
1663 },
1664 ScriptThreadEventCategory::ScriptEvent => {
1665 time_profile!(ProfilerCategory::ScriptEvent, None, profiler_chan, f)
1666 },
1667 ScriptThreadEventCategory::SetScrollState => time_profile!(
1668 ProfilerCategory::ScriptSetScrollState,
1669 None,
1670 profiler_chan,
1671 f
1672 ),
1673 ScriptThreadEventCategory::UpdateReplacedElement => time_profile!(
1674 ProfilerCategory::ScriptUpdateReplacedElement,
1675 None,
1676 profiler_chan,
1677 f
1678 ),
1679 ScriptThreadEventCategory::StylesheetLoad => time_profile!(
1680 ProfilerCategory::ScriptStylesheetLoad,
1681 None,
1682 profiler_chan,
1683 f
1684 ),
1685 ScriptThreadEventCategory::SetViewport => {
1686 time_profile!(ProfilerCategory::ScriptSetViewport, None, profiler_chan, f)
1687 },
1688 ScriptThreadEventCategory::TimerEvent => {
1689 time_profile!(ProfilerCategory::ScriptTimerEvent, None, profiler_chan, f)
1690 },
1691 ScriptThreadEventCategory::WebSocketEvent => time_profile!(
1692 ProfilerCategory::ScriptWebSocketEvent,
1693 None,
1694 profiler_chan,
1695 f
1696 ),
1697 ScriptThreadEventCategory::WorkerEvent => {
1698 time_profile!(ProfilerCategory::ScriptWorkerEvent, None, profiler_chan, f)
1699 },
1700 ScriptThreadEventCategory::WorkletEvent => {
1701 time_profile!(ProfilerCategory::ScriptWorkletEvent, None, profiler_chan, f)
1702 },
1703 ScriptThreadEventCategory::ServiceWorkerEvent => time_profile!(
1704 ProfilerCategory::ScriptServiceWorkerEvent,
1705 None,
1706 profiler_chan,
1707 f
1708 ),
1709 ScriptThreadEventCategory::EnterFullscreen => time_profile!(
1710 ProfilerCategory::ScriptEnterFullscreen,
1711 None,
1712 profiler_chan,
1713 f
1714 ),
1715 ScriptThreadEventCategory::ExitFullscreen => time_profile!(
1716 ProfilerCategory::ScriptExitFullscreen,
1717 None,
1718 profiler_chan,
1719 f
1720 ),
1721 ScriptThreadEventCategory::PerformanceTimelineTask => time_profile!(
1722 ProfilerCategory::ScriptPerformanceEvent,
1723 None,
1724 profiler_chan,
1725 f
1726 ),
1727 ScriptThreadEventCategory::Rendering => {
1728 time_profile!(ProfilerCategory::ScriptRendering, None, profiler_chan, f)
1729 },
1730 #[cfg(feature = "webgpu")]
1731 ScriptThreadEventCategory::WebGPUMsg => {
1732 time_profile!(ProfilerCategory::ScriptWebGPUMsg, None, profiler_chan, f)
1733 },
1734 }
1735 } else {
1736 f()
1737 };
1738 let task_duration = start.elapsed();
1739 for (doc_id, doc) in self.documents.borrow().iter() {
1740 if let Some(pipeline_id) = pipeline_id {
1741 if pipeline_id == doc_id && task_duration.as_nanos() > MAX_TASK_NS {
1742 if self.print_pwm {
1743 println!(
1744 "Task took longer than max allowed ({:?}) {:?}",
1745 category,
1746 task_duration.as_nanos()
1747 );
1748 }
1749 doc.start_tti();
1750 }
1751 }
1752 doc.record_tti_if_necessary();
1753 }
1754 value
1755 }
1756
1757 fn handle_msg_from_constellation(&self, msg: ScriptThreadMessage, can_gc: CanGc) {
1758 match msg {
1759 ScriptThreadMessage::StopDelayingLoadEventsMode(pipeline_id) => {
1760 self.handle_stop_delaying_load_events_mode(pipeline_id)
1761 },
1762 ScriptThreadMessage::NavigateIframe(
1763 parent_pipeline_id,
1764 browsing_context_id,
1765 load_data,
1766 history_handling,
1767 ) => self.handle_navigate_iframe(
1768 parent_pipeline_id,
1769 browsing_context_id,
1770 load_data,
1771 history_handling,
1772 can_gc,
1773 ),
1774 ScriptThreadMessage::UnloadDocument(pipeline_id) => {
1775 self.handle_unload_document(pipeline_id, can_gc)
1776 },
1777 ScriptThreadMessage::ResizeInactive(id, new_size) => {
1778 self.handle_resize_inactive_msg(id, new_size)
1779 },
1780 ScriptThreadMessage::ThemeChange(_, theme) => {
1781 self.handle_theme_change_msg(theme);
1782 },
1783 ScriptThreadMessage::GetTitle(pipeline_id) => self.handle_get_title_msg(pipeline_id),
1784 ScriptThreadMessage::SetDocumentActivity(pipeline_id, activity) => {
1785 self.handle_set_document_activity_msg(pipeline_id, activity, can_gc)
1786 },
1787 ScriptThreadMessage::SetThrottled(pipeline_id, throttled) => {
1788 self.handle_set_throttled_msg(pipeline_id, throttled)
1789 },
1790 ScriptThreadMessage::SetThrottledInContainingIframe(
1791 parent_pipeline_id,
1792 browsing_context_id,
1793 throttled,
1794 ) => self.handle_set_throttled_in_containing_iframe_msg(
1795 parent_pipeline_id,
1796 browsing_context_id,
1797 throttled,
1798 ),
1799 ScriptThreadMessage::PostMessage {
1800 target: target_pipeline_id,
1801 source: source_pipeline_id,
1802 source_browsing_context,
1803 target_origin: origin,
1804 source_origin,
1805 data,
1806 } => self.handle_post_message_msg(
1807 target_pipeline_id,
1808 source_pipeline_id,
1809 source_browsing_context,
1810 origin,
1811 source_origin,
1812 *data,
1813 ),
1814 ScriptThreadMessage::UpdatePipelineId(
1815 parent_pipeline_id,
1816 browsing_context_id,
1817 webview_id,
1818 new_pipeline_id,
1819 reason,
1820 ) => self.handle_update_pipeline_id(
1821 parent_pipeline_id,
1822 browsing_context_id,
1823 webview_id,
1824 new_pipeline_id,
1825 reason,
1826 can_gc,
1827 ),
1828 ScriptThreadMessage::UpdateHistoryState(pipeline_id, history_state_id, url) => {
1829 self.handle_update_history_state_msg(pipeline_id, history_state_id, url, can_gc)
1830 },
1831 ScriptThreadMessage::RemoveHistoryStates(pipeline_id, history_states) => {
1832 self.handle_remove_history_states(pipeline_id, history_states)
1833 },
1834 ScriptThreadMessage::FocusIFrame(parent_pipeline_id, frame_id, sequence) => {
1835 self.handle_focus_iframe_msg(parent_pipeline_id, frame_id, sequence, can_gc)
1836 },
1837 ScriptThreadMessage::FocusDocument(pipeline_id, sequence) => {
1838 self.handle_focus_document_msg(pipeline_id, sequence, can_gc)
1839 },
1840 ScriptThreadMessage::Unfocus(pipeline_id, sequence) => {
1841 self.handle_unfocus_msg(pipeline_id, sequence, can_gc)
1842 },
1843 ScriptThreadMessage::WebDriverScriptCommand(pipeline_id, msg) => {
1844 self.handle_webdriver_msg(pipeline_id, msg, can_gc)
1845 },
1846 ScriptThreadMessage::WebFontLoaded(pipeline_id, success) => {
1847 self.handle_web_font_loaded(pipeline_id, success)
1848 },
1849 ScriptThreadMessage::DispatchIFrameLoadEvent {
1850 target: browsing_context_id,
1851 parent: parent_id,
1852 child: child_id,
1853 } => self.handle_iframe_load_event(parent_id, browsing_context_id, child_id, can_gc),
1854 ScriptThreadMessage::DispatchStorageEvent(
1855 pipeline_id,
1856 storage,
1857 url,
1858 key,
1859 old_value,
1860 new_value,
1861 ) => self.handle_storage_event(pipeline_id, storage, url, key, old_value, new_value),
1862 ScriptThreadMessage::ReportCSSError(pipeline_id, filename, line, column, msg) => {
1863 self.handle_css_error_reporting(pipeline_id, filename, line, column, msg)
1864 },
1865 ScriptThreadMessage::Reload(pipeline_id) => self.handle_reload(pipeline_id, can_gc),
1866 ScriptThreadMessage::ExitPipeline(
1867 webview_id,
1868 pipeline_id,
1869 discard_browsing_context,
1870 ) => self.handle_exit_pipeline_msg(
1871 webview_id,
1872 pipeline_id,
1873 discard_browsing_context,
1874 can_gc,
1875 ),
1876 ScriptThreadMessage::PaintMetric(
1877 pipeline_id,
1878 metric_type,
1879 metric_value,
1880 first_reflow,
1881 ) => self.handle_paint_metric(
1882 pipeline_id,
1883 metric_type,
1884 metric_value,
1885 first_reflow,
1886 can_gc,
1887 ),
1888 ScriptThreadMessage::MediaSessionAction(pipeline_id, action) => {
1889 self.handle_media_session_action(pipeline_id, action, can_gc)
1890 },
1891 #[cfg(feature = "webgpu")]
1892 ScriptThreadMessage::SetWebGPUPort(port) => {
1893 *self.receivers.webgpu_receiver.borrow_mut() =
1894 ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(port);
1895 },
1896 msg @ ScriptThreadMessage::AttachLayout(..) |
1897 msg @ ScriptThreadMessage::Viewport(..) |
1898 msg @ ScriptThreadMessage::Resize(..) |
1899 msg @ ScriptThreadMessage::ExitFullScreen(..) |
1900 msg @ ScriptThreadMessage::SendInputEvent(..) |
1901 msg @ ScriptThreadMessage::TickAllAnimations(..) |
1902 msg @ ScriptThreadMessage::NoLongerWaitingOnAsychronousImageUpdates(..) |
1903 msg @ ScriptThreadMessage::ExitScriptThread => {
1904 panic!("should have handled {:?} already", msg)
1905 },
1906 ScriptThreadMessage::SetScrollStates(pipeline_id, scroll_states) => {
1907 self.handle_set_scroll_states(pipeline_id, scroll_states)
1908 },
1909 ScriptThreadMessage::EvaluateJavaScript(pipeline_id, evaluation_id, script) => {
1910 self.handle_evaluate_javascript(pipeline_id, evaluation_id, script, can_gc);
1911 },
1912 ScriptThreadMessage::SendImageKeysBatch(pipeline_id, image_keys) => {
1913 if let Some(window) = self.documents.borrow().find_window(pipeline_id) {
1914 window
1915 .image_cache()
1916 .fill_key_cache_with_batch_of_keys(image_keys);
1917 } else {
1918 warn!(
1919 "Could not find window corresponding to an image cache to send image keys to pipeline {:?}",
1920 pipeline_id
1921 );
1922 }
1923 },
1924 ScriptThreadMessage::RefreshCursor(pipeline_id) => {
1925 self.handle_refresh_cursor(pipeline_id);
1926 },
1927 ScriptThreadMessage::PreferencesUpdated(updates) => {
1928 let mut current_preferences = prefs::get().clone();
1929 for (name, value) in updates {
1930 current_preferences.set_value(&name, value);
1931 }
1932 prefs::set(current_preferences);
1933 },
1934 }
1935 }
1936
1937 fn handle_set_scroll_states(
1938 &self,
1939 pipeline_id: PipelineId,
1940 scroll_states: HashMap<ExternalScrollId, LayoutVector2D>,
1941 ) {
1942 let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
1943 warn!("Received scroll states for closed pipeline {pipeline_id}");
1944 return;
1945 };
1946
1947 self.profile_event(
1948 ScriptThreadEventCategory::SetScrollState,
1949 Some(pipeline_id),
1950 || {
1951 window
1952 .layout_mut()
1953 .set_scroll_offsets_from_renderer(&scroll_states);
1954 },
1955 )
1956 }
1957
1958 #[cfg(feature = "webgpu")]
1959 fn handle_msg_from_webgpu_server(&self, msg: WebGPUMsg) {
1960 match msg {
1961 WebGPUMsg::FreeAdapter(id) => self.gpu_id_hub.free_adapter_id(id),
1962 WebGPUMsg::FreeDevice {
1963 device_id,
1964 pipeline_id,
1965 } => {
1966 self.gpu_id_hub.free_device_id(device_id);
1967 if let Some(global) = self.documents.borrow().find_global(pipeline_id) {
1968 global.remove_gpu_device(WebGPUDevice(device_id));
1969 } },
1971 WebGPUMsg::FreeBuffer(id) => self.gpu_id_hub.free_buffer_id(id),
1972 WebGPUMsg::FreePipelineLayout(id) => self.gpu_id_hub.free_pipeline_layout_id(id),
1973 WebGPUMsg::FreeComputePipeline(id) => self.gpu_id_hub.free_compute_pipeline_id(id),
1974 WebGPUMsg::FreeBindGroup(id) => self.gpu_id_hub.free_bind_group_id(id),
1975 WebGPUMsg::FreeBindGroupLayout(id) => self.gpu_id_hub.free_bind_group_layout_id(id),
1976 WebGPUMsg::FreeCommandBuffer(id) => self
1977 .gpu_id_hub
1978 .free_command_buffer_id(id.into_command_encoder_id()),
1979 WebGPUMsg::FreeSampler(id) => self.gpu_id_hub.free_sampler_id(id),
1980 WebGPUMsg::FreeShaderModule(id) => self.gpu_id_hub.free_shader_module_id(id),
1981 WebGPUMsg::FreeRenderBundle(id) => self.gpu_id_hub.free_render_bundle_id(id),
1982 WebGPUMsg::FreeRenderPipeline(id) => self.gpu_id_hub.free_render_pipeline_id(id),
1983 WebGPUMsg::FreeTexture(id) => self.gpu_id_hub.free_texture_id(id),
1984 WebGPUMsg::FreeTextureView(id) => self.gpu_id_hub.free_texture_view_id(id),
1985 WebGPUMsg::FreeComputePass(id) => self.gpu_id_hub.free_compute_pass_id(id),
1986 WebGPUMsg::FreeRenderPass(id) => self.gpu_id_hub.free_render_pass_id(id),
1987 WebGPUMsg::Exit => {
1988 *self.receivers.webgpu_receiver.borrow_mut() = crossbeam_channel::never()
1989 },
1990 WebGPUMsg::DeviceLost {
1991 pipeline_id,
1992 device,
1993 reason,
1994 msg,
1995 } => {
1996 let global = self.documents.borrow().find_global(pipeline_id).unwrap();
1997 global.gpu_device_lost(device, reason, msg);
1998 },
1999 WebGPUMsg::UncapturedError {
2000 device,
2001 pipeline_id,
2002 error,
2003 } => {
2004 let global = self.documents.borrow().find_global(pipeline_id).unwrap();
2005 let _ac = enter_realm(&*global);
2006 global.handle_uncaptured_gpu_error(device, error);
2007 },
2008 _ => {},
2009 }
2010 }
2011
2012 fn handle_msg_from_script(&self, msg: MainThreadScriptMsg) {
2013 match msg {
2014 MainThreadScriptMsg::Common(CommonScriptMsg::Task(_, task, pipeline_id, _)) => {
2015 let _realm = pipeline_id.and_then(|id| {
2016 let global = self.documents.borrow().find_global(id);
2017 global.map(|global| enter_realm(&*global))
2018 });
2019 task.run_box()
2020 },
2021 MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(chan)) => {
2022 self.collect_reports(chan)
2023 },
2024 MainThreadScriptMsg::Common(CommonScriptMsg::ReportCspViolations(
2025 pipeline_id,
2026 violations,
2027 )) => {
2028 if let Some(global) = self.documents.borrow().find_global(pipeline_id) {
2029 global.report_csp_violations(violations, None, None);
2030 }
2031 },
2032 MainThreadScriptMsg::NavigationResponse {
2033 pipeline_id,
2034 message,
2035 } => {
2036 self.handle_navigation_response(pipeline_id, *message);
2037 },
2038 MainThreadScriptMsg::WorkletLoaded(pipeline_id) => {
2039 self.handle_worklet_loaded(pipeline_id)
2040 },
2041 MainThreadScriptMsg::RegisterPaintWorklet {
2042 pipeline_id,
2043 name,
2044 properties,
2045 painter,
2046 } => self.handle_register_paint_worklet(pipeline_id, name, properties, painter),
2047 MainThreadScriptMsg::Inactive => {},
2048 MainThreadScriptMsg::WakeUp => {},
2049 }
2050 }
2051
2052 fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg, can_gc: CanGc) {
2053 let documents = self.documents.borrow();
2054 match msg {
2055 DevtoolScriptControlMsg::EvaluateJS(id, s, reply) => match documents.find_window(id) {
2056 Some(window) => {
2057 let global = window.as_global_scope();
2058 let _aes = AutoEntryScript::new(global);
2059 devtools::handle_evaluate_js(global, s, reply, can_gc)
2060 },
2061 None => warn!("Message sent to closed pipeline {}.", id),
2062 },
2063 DevtoolScriptControlMsg::GetRootNode(id, reply) => {
2064 devtools::handle_get_root_node(&documents, id, reply, can_gc)
2065 },
2066 DevtoolScriptControlMsg::GetDocumentElement(id, reply) => {
2067 devtools::handle_get_document_element(&documents, id, reply, can_gc)
2068 },
2069 DevtoolScriptControlMsg::GetChildren(id, node_id, reply) => {
2070 devtools::handle_get_children(&documents, id, node_id, reply, can_gc)
2071 },
2072 DevtoolScriptControlMsg::GetAttributeStyle(id, node_id, reply) => {
2073 devtools::handle_get_attribute_style(&documents, id, node_id, reply, can_gc)
2074 },
2075 DevtoolScriptControlMsg::GetStylesheetStyle(
2076 id,
2077 node_id,
2078 selector,
2079 stylesheet,
2080 reply,
2081 ) => devtools::handle_get_stylesheet_style(
2082 &documents, id, node_id, selector, stylesheet, reply, can_gc,
2083 ),
2084 DevtoolScriptControlMsg::GetSelectors(id, node_id, reply) => {
2085 devtools::handle_get_selectors(&documents, id, node_id, reply, can_gc)
2086 },
2087 DevtoolScriptControlMsg::GetComputedStyle(id, node_id, reply) => {
2088 devtools::handle_get_computed_style(&documents, id, node_id, reply)
2089 },
2090 DevtoolScriptControlMsg::GetLayout(id, node_id, reply) => {
2091 devtools::handle_get_layout(&documents, id, node_id, reply, can_gc)
2092 },
2093 DevtoolScriptControlMsg::ModifyAttribute(id, node_id, modifications) => {
2094 devtools::handle_modify_attribute(&documents, id, node_id, modifications, can_gc)
2095 },
2096 DevtoolScriptControlMsg::ModifyRule(id, node_id, modifications) => {
2097 devtools::handle_modify_rule(&documents, id, node_id, modifications, can_gc)
2098 },
2099 DevtoolScriptControlMsg::WantsLiveNotifications(id, to_send) => match documents
2100 .find_window(id)
2101 {
2102 Some(window) => devtools::handle_wants_live_notifications(window.upcast(), to_send),
2103 None => warn!("Message sent to closed pipeline {}.", id),
2104 },
2105 DevtoolScriptControlMsg::SetTimelineMarkers(id, marker_types, reply) => {
2106 devtools::handle_set_timeline_markers(&documents, id, marker_types, reply)
2107 },
2108 DevtoolScriptControlMsg::DropTimelineMarkers(id, marker_types) => {
2109 devtools::handle_drop_timeline_markers(&documents, id, marker_types)
2110 },
2111 DevtoolScriptControlMsg::RequestAnimationFrame(id, name) => {
2112 devtools::handle_request_animation_frame(&documents, id, name)
2113 },
2114 DevtoolScriptControlMsg::Reload(id) => devtools::handle_reload(&documents, id, can_gc),
2115 DevtoolScriptControlMsg::GetCssDatabase(reply) => {
2116 devtools::handle_get_css_database(reply)
2117 },
2118 DevtoolScriptControlMsg::SimulateColorScheme(id, theme) => {
2119 match documents.find_window(id) {
2120 Some(window) => {
2121 window.handle_theme_change(theme);
2122 },
2123 None => warn!("Message sent to closed pipeline {}.", id),
2124 }
2125 },
2126 DevtoolScriptControlMsg::HighlightDomNode(id, node_id) => {
2127 devtools::handle_highlight_dom_node(&documents, id, node_id)
2128 },
2129 DevtoolScriptControlMsg::GetPossibleBreakpoints(spidermonkey_id, result_sender) => {
2130 self.debugger_global.fire_get_possible_breakpoints(
2131 can_gc,
2132 spidermonkey_id,
2133 result_sender,
2134 );
2135 },
2136 }
2137 }
2138
2139 fn handle_msg_from_image_cache(&self, response: ImageCacheResponseMessage) {
2140 match response {
2141 ImageCacheResponseMessage::NotifyPendingImageLoadStatus(pending_image_response) => {
2142 let window = self
2143 .documents
2144 .borrow()
2145 .find_window(pending_image_response.pipeline_id);
2146 if let Some(ref window) = window {
2147 window.pending_image_notification(pending_image_response);
2148 }
2149 },
2150 ImageCacheResponseMessage::VectorImageRasterizationComplete(response) => {
2151 let window = self.documents.borrow().find_window(response.pipeline_id);
2152 if let Some(ref window) = window {
2153 window.handle_image_rasterization_complete_notification(response);
2154 }
2155 },
2156 };
2157 }
2158
2159 fn handle_webdriver_msg(
2160 &self,
2161 pipeline_id: PipelineId,
2162 msg: WebDriverScriptCommand,
2163 can_gc: CanGc,
2164 ) {
2165 match msg {
2170 WebDriverScriptCommand::ExecuteScript(script, reply) => {
2171 let window = self.documents.borrow().find_window(pipeline_id);
2172 return webdriver_handlers::handle_execute_script(window, script, reply, can_gc);
2173 },
2174 WebDriverScriptCommand::ExecuteAsyncScript(script, reply) => {
2175 let window = self.documents.borrow().find_window(pipeline_id);
2176 return webdriver_handlers::handle_execute_async_script(
2177 window, script, reply, can_gc,
2178 );
2179 },
2180 _ => (),
2181 }
2182
2183 let documents = self.documents.borrow();
2184 match msg {
2185 WebDriverScriptCommand::AddCookie(params, reply) => {
2186 webdriver_handlers::handle_add_cookie(&documents, pipeline_id, params, reply)
2187 },
2188 WebDriverScriptCommand::DeleteCookies(reply) => {
2189 webdriver_handlers::handle_delete_cookies(&documents, pipeline_id, reply)
2190 },
2191 WebDriverScriptCommand::DeleteCookie(name, reply) => {
2192 webdriver_handlers::handle_delete_cookie(&documents, pipeline_id, name, reply)
2193 },
2194 WebDriverScriptCommand::ElementClear(element_id, reply) => {
2195 webdriver_handlers::handle_element_clear(
2196 &documents,
2197 pipeline_id,
2198 element_id,
2199 reply,
2200 can_gc,
2201 )
2202 },
2203 WebDriverScriptCommand::FindElementsCSSSelector(selector, reply) => {
2204 webdriver_handlers::handle_find_elements_css_selector(
2205 &documents,
2206 pipeline_id,
2207 selector,
2208 reply,
2209 )
2210 },
2211 WebDriverScriptCommand::FindElementsLinkText(selector, partial, reply) => {
2212 webdriver_handlers::handle_find_elements_link_text(
2213 &documents,
2214 pipeline_id,
2215 selector,
2216 partial,
2217 reply,
2218 )
2219 },
2220 WebDriverScriptCommand::FindElementsTagName(selector, reply) => {
2221 webdriver_handlers::handle_find_elements_tag_name(
2222 &documents,
2223 pipeline_id,
2224 selector,
2225 reply,
2226 can_gc,
2227 )
2228 },
2229 WebDriverScriptCommand::FindElementsXpathSelector(selector, reply) => {
2230 webdriver_handlers::handle_find_elements_xpath_selector(
2231 &documents,
2232 pipeline_id,
2233 selector,
2234 reply,
2235 can_gc,
2236 )
2237 },
2238 WebDriverScriptCommand::FindElementElementsCSSSelector(selector, element_id, reply) => {
2239 webdriver_handlers::handle_find_element_elements_css_selector(
2240 &documents,
2241 pipeline_id,
2242 element_id,
2243 selector,
2244 reply,
2245 )
2246 },
2247 WebDriverScriptCommand::FindElementElementsLinkText(
2248 selector,
2249 element_id,
2250 partial,
2251 reply,
2252 ) => webdriver_handlers::handle_find_element_elements_link_text(
2253 &documents,
2254 pipeline_id,
2255 element_id,
2256 selector,
2257 partial,
2258 reply,
2259 ),
2260 WebDriverScriptCommand::FindElementElementsTagName(selector, element_id, reply) => {
2261 webdriver_handlers::handle_find_element_elements_tag_name(
2262 &documents,
2263 pipeline_id,
2264 element_id,
2265 selector,
2266 reply,
2267 can_gc,
2268 )
2269 },
2270 WebDriverScriptCommand::FindElementElementsXPathSelector(
2271 selector,
2272 element_id,
2273 reply,
2274 ) => webdriver_handlers::handle_find_element_elements_xpath_selector(
2275 &documents,
2276 pipeline_id,
2277 element_id,
2278 selector,
2279 reply,
2280 can_gc,
2281 ),
2282 WebDriverScriptCommand::FindShadowElementsCSSSelector(
2283 selector,
2284 shadow_root_id,
2285 reply,
2286 ) => webdriver_handlers::handle_find_shadow_elements_css_selector(
2287 &documents,
2288 pipeline_id,
2289 shadow_root_id,
2290 selector,
2291 reply,
2292 ),
2293 WebDriverScriptCommand::FindShadowElementsLinkText(
2294 selector,
2295 shadow_root_id,
2296 partial,
2297 reply,
2298 ) => webdriver_handlers::handle_find_shadow_elements_link_text(
2299 &documents,
2300 pipeline_id,
2301 shadow_root_id,
2302 selector,
2303 partial,
2304 reply,
2305 ),
2306 WebDriverScriptCommand::FindShadowElementsTagName(selector, shadow_root_id, reply) => {
2307 webdriver_handlers::handle_find_shadow_elements_tag_name(
2308 &documents,
2309 pipeline_id,
2310 shadow_root_id,
2311 selector,
2312 reply,
2313 )
2314 },
2315 WebDriverScriptCommand::FindShadowElementsXPathSelector(
2316 selector,
2317 shadow_root_id,
2318 reply,
2319 ) => webdriver_handlers::handle_find_shadow_elements_xpath_selector(
2320 &documents,
2321 pipeline_id,
2322 shadow_root_id,
2323 selector,
2324 reply,
2325 can_gc,
2326 ),
2327 WebDriverScriptCommand::GetElementShadowRoot(element_id, reply) => {
2328 webdriver_handlers::handle_get_element_shadow_root(
2329 &documents,
2330 pipeline_id,
2331 element_id,
2332 reply,
2333 )
2334 },
2335 WebDriverScriptCommand::ElementClick(element_id, reply) => {
2336 webdriver_handlers::handle_element_click(
2337 &documents,
2338 pipeline_id,
2339 element_id,
2340 reply,
2341 can_gc,
2342 )
2343 },
2344 WebDriverScriptCommand::GetKnownElement(element_id, reply) => {
2345 webdriver_handlers::handle_get_known_element(
2346 &documents,
2347 pipeline_id,
2348 element_id,
2349 reply,
2350 )
2351 },
2352 WebDriverScriptCommand::GetKnownShadowRoot(element_id, reply) => {
2353 webdriver_handlers::handle_get_known_shadow_root(
2354 &documents,
2355 pipeline_id,
2356 element_id,
2357 reply,
2358 )
2359 },
2360 WebDriverScriptCommand::GetActiveElement(reply) => {
2361 webdriver_handlers::handle_get_active_element(&documents, pipeline_id, reply)
2362 },
2363 WebDriverScriptCommand::GetComputedRole(node_id, reply) => {
2364 webdriver_handlers::handle_get_computed_role(
2365 &documents,
2366 pipeline_id,
2367 node_id,
2368 reply,
2369 )
2370 },
2371 WebDriverScriptCommand::GetPageSource(reply) => {
2372 webdriver_handlers::handle_get_page_source(&documents, pipeline_id, reply, can_gc)
2373 },
2374 WebDriverScriptCommand::GetCookies(reply) => {
2375 webdriver_handlers::handle_get_cookies(&documents, pipeline_id, reply)
2376 },
2377 WebDriverScriptCommand::GetCookie(name, reply) => {
2378 webdriver_handlers::handle_get_cookie(&documents, pipeline_id, name, reply)
2379 },
2380 WebDriverScriptCommand::GetElementTagName(node_id, reply) => {
2381 webdriver_handlers::handle_get_name(&documents, pipeline_id, node_id, reply)
2382 },
2383 WebDriverScriptCommand::GetElementAttribute(node_id, name, reply) => {
2384 webdriver_handlers::handle_get_attribute(
2385 &documents,
2386 pipeline_id,
2387 node_id,
2388 name,
2389 reply,
2390 )
2391 },
2392 WebDriverScriptCommand::GetElementProperty(node_id, name, reply) => {
2393 webdriver_handlers::handle_get_property(
2394 &documents,
2395 pipeline_id,
2396 node_id,
2397 name,
2398 reply,
2399 can_gc,
2400 )
2401 },
2402 WebDriverScriptCommand::GetElementCSS(node_id, name, reply) => {
2403 webdriver_handlers::handle_get_css(&documents, pipeline_id, node_id, name, reply)
2404 },
2405 WebDriverScriptCommand::GetElementRect(node_id, reply) => {
2406 webdriver_handlers::handle_get_rect(&documents, pipeline_id, node_id, reply, can_gc)
2407 },
2408 WebDriverScriptCommand::GetBoundingClientRect(node_id, reply) => {
2409 webdriver_handlers::handle_get_bounding_client_rect(
2410 &documents,
2411 pipeline_id,
2412 node_id,
2413 reply,
2414 can_gc,
2415 )
2416 },
2417 WebDriverScriptCommand::GetElementText(node_id, reply) => {
2418 webdriver_handlers::handle_get_text(&documents, pipeline_id, node_id, reply)
2419 },
2420 WebDriverScriptCommand::GetElementInViewCenterPoint(node_id, reply) => {
2421 webdriver_handlers::handle_get_element_in_view_center_point(
2422 &documents,
2423 pipeline_id,
2424 node_id,
2425 reply,
2426 can_gc,
2427 )
2428 },
2429 WebDriverScriptCommand::GetParentFrameId(reply) => {
2430 webdriver_handlers::handle_get_parent_frame_id(&documents, pipeline_id, reply)
2431 },
2432 WebDriverScriptCommand::GetBrowsingContextId(webdriver_frame_id, reply) => {
2433 webdriver_handlers::handle_get_browsing_context_id(
2434 &documents,
2435 pipeline_id,
2436 webdriver_frame_id,
2437 reply,
2438 )
2439 },
2440 WebDriverScriptCommand::GetUrl(reply) => {
2441 webdriver_handlers::handle_get_url(&documents, pipeline_id, reply, can_gc)
2442 },
2443 WebDriverScriptCommand::IsEnabled(element_id, reply) => {
2444 webdriver_handlers::handle_is_enabled(&documents, pipeline_id, element_id, reply)
2445 },
2446 WebDriverScriptCommand::IsSelected(element_id, reply) => {
2447 webdriver_handlers::handle_is_selected(&documents, pipeline_id, element_id, reply)
2448 },
2449 WebDriverScriptCommand::GetTitle(reply) => {
2450 webdriver_handlers::handle_get_title(&documents, pipeline_id, reply)
2451 },
2452 WebDriverScriptCommand::WillSendKeys(
2453 element_id,
2454 text,
2455 strict_file_interactability,
2456 reply,
2457 ) => webdriver_handlers::handle_will_send_keys(
2458 &documents,
2459 pipeline_id,
2460 element_id,
2461 text,
2462 strict_file_interactability,
2463 reply,
2464 can_gc,
2465 ),
2466 WebDriverScriptCommand::AddLoadStatusSender(_, response_sender) => {
2467 webdriver_handlers::handle_add_load_status_sender(
2468 &documents,
2469 pipeline_id,
2470 response_sender,
2471 )
2472 },
2473 WebDriverScriptCommand::RemoveLoadStatusSender(_) => {
2474 webdriver_handlers::handle_remove_load_status_sender(&documents, pipeline_id)
2475 },
2476 _ => (),
2477 }
2478 }
2479
2480 pub(crate) fn handle_resize_message(
2483 &self,
2484 id: PipelineId,
2485 viewport_details: ViewportDetails,
2486 size_type: WindowSizeType,
2487 ) {
2488 self.profile_event(ScriptThreadEventCategory::Resize, Some(id), || {
2489 let window = self.documents.borrow().find_window(id);
2490 if let Some(ref window) = window {
2491 window.add_resize_event(viewport_details, size_type);
2492 return;
2493 }
2494 let mut loads = self.incomplete_loads.borrow_mut();
2495 if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) {
2496 load.viewport_details = viewport_details;
2497 }
2498 })
2499 }
2500
2501 fn handle_theme_change_msg(&self, theme: Theme) {
2503 for (_, document) in self.documents.borrow().iter() {
2504 document.window().handle_theme_change(theme);
2505 }
2506 }
2507
2508 fn handle_exit_fullscreen(&self, id: PipelineId, can_gc: CanGc) {
2510 let document = self.documents.borrow().find_document(id);
2511 if let Some(document) = document {
2512 let _ac = enter_realm(&*document);
2513 document.exit_fullscreen(can_gc);
2514 }
2515 }
2516
2517 fn handle_viewport(&self, id: PipelineId, rect: Rect<f32>) {
2518 let document = self.documents.borrow().find_document(id);
2519 if let Some(document) = document {
2520 document.window().set_viewport_size(rect.size);
2521 return;
2522 }
2523 let loads = self.incomplete_loads.borrow();
2524 if loads.iter().any(|load| load.pipeline_id == id) {
2525 return;
2526 }
2527 warn!("Page rect message sent to nonexistent pipeline");
2528 }
2529
2530 fn handle_new_layout(&self, new_layout_info: NewLayoutInfo, origin: MutableOrigin) {
2531 let NewLayoutInfo {
2532 parent_info,
2533 new_pipeline_id,
2534 browsing_context_id,
2535 webview_id,
2536 opener,
2537 load_data,
2538 viewport_details,
2539 theme,
2540 } = new_layout_info;
2541
2542 let url = load_data.url.clone();
2544 let new_load = InProgressLoad::new(
2545 new_pipeline_id,
2546 browsing_context_id,
2547 webview_id,
2548 parent_info,
2549 opener,
2550 viewport_details,
2551 theme,
2552 origin,
2553 load_data,
2554 );
2555 if url.as_str() == "about:blank" {
2556 self.start_page_load_about_blank(new_load);
2557 } else if url.as_str() == "about:srcdoc" {
2558 self.page_load_about_srcdoc(new_load);
2559 } else {
2560 self.pre_page_load(new_load);
2561 }
2562 }
2563
2564 fn collect_reports(&self, reports_chan: ReportsChan) {
2565 let documents = self.documents.borrow();
2566 let urls = itertools::join(documents.iter().map(|(_, d)| d.url().to_string()), ", ");
2567
2568 let mut reports = vec![];
2569 perform_memory_report(|ops| {
2570 for (_, document) in documents.iter() {
2571 document
2572 .window()
2573 .layout()
2574 .collect_reports(&mut reports, ops);
2575 }
2576
2577 let prefix = format!("url({urls})");
2578 reports.extend(self.get_cx().get_reports(prefix.clone(), ops));
2579 });
2580
2581 reports_chan.send(ProcessReports::new(reports));
2582 }
2583
2584 fn handle_set_throttled_in_containing_iframe_msg(
2586 &self,
2587 parent_pipeline_id: PipelineId,
2588 browsing_context_id: BrowsingContextId,
2589 throttled: bool,
2590 ) {
2591 let iframe = self
2592 .documents
2593 .borrow()
2594 .find_iframe(parent_pipeline_id, browsing_context_id);
2595 if let Some(iframe) = iframe {
2596 iframe.set_throttled(throttled);
2597 }
2598 }
2599
2600 fn handle_set_throttled_msg(&self, id: PipelineId, throttled: bool) {
2601 self.senders
2604 .pipeline_to_constellation_sender
2605 .send((
2606 id,
2607 ScriptToConstellationMessage::SetThrottledComplete(throttled),
2608 ))
2609 .unwrap();
2610
2611 let window = self.documents.borrow().find_window(id);
2612 match window {
2613 Some(window) => {
2614 window.set_throttled(throttled);
2615 return;
2616 },
2617 None => {
2618 let mut loads = self.incomplete_loads.borrow_mut();
2619 if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) {
2620 load.throttled = throttled;
2621 return;
2622 }
2623 },
2624 }
2625
2626 warn!("SetThrottled sent to nonexistent pipeline");
2627 }
2628
2629 fn handle_set_document_activity_msg(
2631 &self,
2632 id: PipelineId,
2633 activity: DocumentActivity,
2634 can_gc: CanGc,
2635 ) {
2636 debug!(
2637 "Setting activity of {} to be {:?} in {:?}.",
2638 id,
2639 activity,
2640 thread::current().name()
2641 );
2642 let document = self.documents.borrow().find_document(id);
2643 if let Some(document) = document {
2644 document.set_activity(activity, can_gc);
2645 return;
2646 }
2647 let mut loads = self.incomplete_loads.borrow_mut();
2648 if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) {
2649 load.activity = activity;
2650 return;
2651 }
2652 warn!("change of activity sent to nonexistent pipeline");
2653 }
2654
2655 fn handle_focus_iframe_msg(
2656 &self,
2657 parent_pipeline_id: PipelineId,
2658 browsing_context_id: BrowsingContextId,
2659 sequence: FocusSequenceNumber,
2660 can_gc: CanGc,
2661 ) {
2662 let document = self
2663 .documents
2664 .borrow()
2665 .find_document(parent_pipeline_id)
2666 .unwrap();
2667
2668 let Some(iframe_element_root) = ({
2669 let iframes = document.iframes();
2672 iframes
2673 .get(browsing_context_id)
2674 .map(|iframe| DomRoot::from_ref(iframe.element.upcast()))
2675 }) else {
2676 return;
2677 };
2678
2679 if document.get_focus_sequence() > sequence {
2680 debug!(
2681 "Disregarding the FocusIFrame message because the contained sequence number is \
2682 too old ({:?} < {:?})",
2683 sequence,
2684 document.get_focus_sequence()
2685 );
2686 return;
2687 }
2688
2689 document.request_focus(Some(&iframe_element_root), FocusInitiator::Remote, can_gc);
2690 }
2691
2692 fn handle_focus_document_msg(
2693 &self,
2694 pipeline_id: PipelineId,
2695 sequence: FocusSequenceNumber,
2696 can_gc: CanGc,
2697 ) {
2698 if let Some(doc) = self.documents.borrow().find_document(pipeline_id) {
2699 if doc.get_focus_sequence() > sequence {
2700 debug!(
2701 "Disregarding the FocusDocument message because the contained sequence number is \
2702 too old ({:?} < {:?})",
2703 sequence,
2704 doc.get_focus_sequence()
2705 );
2706 return;
2707 }
2708 doc.request_focus(None, FocusInitiator::Remote, can_gc);
2709 } else {
2710 warn!(
2711 "Couldn't find document by pipleline_id:{pipeline_id:?} when handle_focus_document_msg."
2712 );
2713 }
2714 }
2715
2716 fn handle_unfocus_msg(
2717 &self,
2718 pipeline_id: PipelineId,
2719 sequence: FocusSequenceNumber,
2720 can_gc: CanGc,
2721 ) {
2722 if let Some(doc) = self.documents.borrow().find_document(pipeline_id) {
2723 if doc.get_focus_sequence() > sequence {
2724 debug!(
2725 "Disregarding the Unfocus message because the contained sequence number is \
2726 too old ({:?} < {:?})",
2727 sequence,
2728 doc.get_focus_sequence()
2729 );
2730 return;
2731 }
2732 doc.handle_container_unfocus(can_gc);
2733 } else {
2734 warn!(
2735 "Couldn't find document by pipleline_id:{pipeline_id:?} when handle_unfocus_msg."
2736 );
2737 }
2738 }
2739
2740 fn handle_post_message_msg(
2741 &self,
2742 pipeline_id: PipelineId,
2743 source_pipeline_id: PipelineId,
2744 source_browsing_context: WebViewId,
2745 origin: Option<ImmutableOrigin>,
2746 source_origin: ImmutableOrigin,
2747 data: StructuredSerializedData,
2748 ) {
2749 let window = self.documents.borrow().find_window(pipeline_id);
2750 match window {
2751 None => warn!("postMessage after target pipeline {} closed.", pipeline_id),
2752 Some(window) => {
2753 let source = match self.remote_window_proxy(
2756 window.upcast::<GlobalScope>(),
2757 source_browsing_context,
2758 source_pipeline_id,
2759 None,
2760 ) {
2761 None => {
2762 return warn!(
2763 "postMessage after source pipeline {} closed.",
2764 source_pipeline_id,
2765 );
2766 },
2767 Some(source) => source,
2768 };
2769 window.post_message(origin, source_origin, &source, data)
2771 },
2772 }
2773 }
2774
2775 fn handle_stop_delaying_load_events_mode(&self, pipeline_id: PipelineId) {
2776 let window = self.documents.borrow().find_window(pipeline_id);
2777 if let Some(window) = window {
2778 match window.undiscarded_window_proxy() {
2779 Some(window_proxy) => window_proxy.stop_delaying_load_events_mode(),
2780 None => warn!(
2781 "Attempted to take {} of 'delaying-load-events-mode' after having been discarded.",
2782 pipeline_id
2783 ),
2784 };
2785 }
2786 }
2787
2788 fn handle_unload_document(&self, pipeline_id: PipelineId, can_gc: CanGc) {
2789 let document = self.documents.borrow().find_document(pipeline_id);
2790 if let Some(document) = document {
2791 document.unload(false, can_gc);
2792 }
2793 }
2794
2795 fn handle_update_pipeline_id(
2796 &self,
2797 parent_pipeline_id: PipelineId,
2798 browsing_context_id: BrowsingContextId,
2799 webview_id: WebViewId,
2800 new_pipeline_id: PipelineId,
2801 reason: UpdatePipelineIdReason,
2802 can_gc: CanGc,
2803 ) {
2804 let frame_element = self
2805 .documents
2806 .borrow()
2807 .find_iframe(parent_pipeline_id, browsing_context_id);
2808 if let Some(frame_element) = frame_element {
2809 frame_element.update_pipeline_id(new_pipeline_id, reason, can_gc);
2810 }
2811
2812 if let Some(window) = self.documents.borrow().find_window(new_pipeline_id) {
2813 let _ = self.local_window_proxy(
2816 &window,
2817 browsing_context_id,
2818 webview_id,
2819 Some(parent_pipeline_id),
2820 None,
2824 );
2825 }
2826 }
2827
2828 fn handle_update_history_state_msg(
2829 &self,
2830 pipeline_id: PipelineId,
2831 history_state_id: Option<HistoryStateId>,
2832 url: ServoUrl,
2833 can_gc: CanGc,
2834 ) {
2835 let window = self.documents.borrow().find_window(pipeline_id);
2836 match window {
2837 None => {
2838 warn!(
2839 "update history state after pipeline {} closed.",
2840 pipeline_id
2841 );
2842 },
2843 Some(window) => window
2844 .History()
2845 .activate_state(history_state_id, url, can_gc),
2846 }
2847 }
2848
2849 fn handle_remove_history_states(
2850 &self,
2851 pipeline_id: PipelineId,
2852 history_states: Vec<HistoryStateId>,
2853 ) {
2854 let window = self.documents.borrow().find_window(pipeline_id);
2855 match window {
2856 None => {
2857 warn!(
2858 "update history state after pipeline {} closed.",
2859 pipeline_id
2860 );
2861 },
2862 Some(window) => window.History().remove_states(history_states),
2863 }
2864 }
2865
2866 fn handle_resize_inactive_msg(&self, id: PipelineId, new_viewport_details: ViewportDetails) {
2868 let window = self.documents.borrow().find_window(id)
2869 .expect("ScriptThread: received a resize msg for a pipeline not in this script thread. This is a bug.");
2870 window.set_viewport_details(new_viewport_details);
2871 }
2872
2873 fn handle_page_headers_available(
2876 &self,
2877 id: &PipelineId,
2878 metadata: Option<Metadata>,
2879 can_gc: CanGc,
2880 ) -> Option<DomRoot<ServoParser>> {
2881 if self.closed_pipelines.borrow().contains(id) {
2882 return None;
2884 }
2885
2886 let Some(idx) = self
2887 .incomplete_loads
2888 .borrow()
2889 .iter()
2890 .position(|load| load.pipeline_id == *id)
2891 else {
2892 unreachable!("Pipeline shouldn't have finished loading.");
2893 };
2894
2895 let is_204_205 = match metadata {
2898 Some(ref metadata) => metadata.status.in_range(204..=205),
2899 _ => false,
2900 };
2901
2902 if is_204_205 {
2903 if let Some(window) = self.documents.borrow().find_window(*id) {
2905 let window_proxy = window.window_proxy();
2906 if window_proxy.parent().is_some() {
2909 window_proxy.stop_delaying_load_events_mode();
2915 }
2916 }
2917 self.senders
2918 .pipeline_to_constellation_sender
2919 .send((*id, ScriptToConstellationMessage::AbortLoadUrl))
2920 .unwrap();
2921 return None;
2922 };
2923
2924 let load = self.incomplete_loads.borrow_mut().remove(idx);
2925 metadata.map(|meta| self.load(meta, load, can_gc))
2926 }
2927
2928 fn handle_get_title_msg(&self, pipeline_id: PipelineId) {
2930 let document = match self.documents.borrow().find_document(pipeline_id) {
2931 Some(document) => document,
2932 None => return warn!("Message sent to closed pipeline {}.", pipeline_id),
2933 };
2934 document.send_title_to_embedder();
2935 }
2936
2937 fn handle_exit_pipeline_msg(
2939 &self,
2940 webview_id: WebViewId,
2941 id: PipelineId,
2942 discard_bc: DiscardBrowsingContext,
2943 can_gc: CanGc,
2944 ) {
2945 debug!("{id}: Starting pipeline exit.");
2946
2947 let document = self.documents.borrow_mut().remove(id);
2950 if let Some(document) = document {
2951 debug_assert!(
2953 !self
2954 .incomplete_loads
2955 .borrow()
2956 .iter()
2957 .any(|load| load.pipeline_id == id)
2958 );
2959
2960 if let Some(parser) = document.get_current_parser() {
2961 parser.abort(can_gc);
2962 }
2963
2964 debug!("{id}: Shutting down layout");
2965 document.window().layout_mut().exit_now();
2966
2967 debug!("{id}: Clearing animations");
2969 document.animations().clear();
2970
2971 let window = document.window();
2974 if discard_bc == DiscardBrowsingContext::Yes {
2975 window.discard_browsing_context();
2976 }
2977
2978 debug!("{id}: Clearing JavaScript runtime");
2979 window.clear_js_runtime();
2980 }
2981
2982 self.closed_pipelines.borrow_mut().insert(id);
2984
2985 debug!("{id}: Sending PipelineExited message to constellation");
2986 self.senders
2987 .pipeline_to_constellation_sender
2988 .send((id, ScriptToConstellationMessage::PipelineExited))
2989 .ok();
2990
2991 self.compositor_api
2992 .pipeline_exited(webview_id, id, PipelineExitSource::Script);
2993
2994 debug!("{id}: Finished pipeline exit");
2995 }
2996
2997 fn handle_exit_script_thread_msg(&self, can_gc: CanGc) {
2999 debug!("Exiting script thread.");
3000
3001 let mut webview_and_pipeline_ids = Vec::new();
3002 webview_and_pipeline_ids.extend(
3003 self.incomplete_loads
3004 .borrow()
3005 .iter()
3006 .next()
3007 .map(|load| (load.webview_id, load.pipeline_id)),
3008 );
3009 webview_and_pipeline_ids.extend(
3010 self.documents
3011 .borrow()
3012 .iter()
3013 .next()
3014 .map(|(pipeline_id, document)| (document.webview_id(), pipeline_id)),
3015 );
3016
3017 for (webview_id, pipeline_id) in webview_and_pipeline_ids {
3018 self.handle_exit_pipeline_msg(
3019 webview_id,
3020 pipeline_id,
3021 DiscardBrowsingContext::Yes,
3022 can_gc,
3023 );
3024 }
3025
3026 self.background_hang_monitor.unregister();
3027
3028 if opts::get().multiprocess {
3030 debug!("Exiting IPC router thread in script thread.");
3031 ROUTER.shutdown();
3032 }
3033
3034 debug!("Exited script thread.");
3035 }
3036
3037 pub(crate) fn handle_tick_all_animations_for_testing(id: PipelineId) {
3039 with_script_thread(|script_thread| {
3040 let Some(document) = script_thread.documents.borrow().find_document(id) else {
3041 warn!("Animation tick for tests for closed pipeline {id}.");
3042 return;
3043 };
3044 document.maybe_mark_animating_nodes_as_dirty();
3045 });
3046 }
3047
3048 fn handle_web_font_loaded(&self, pipeline_id: PipelineId, _success: bool) {
3050 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
3051 warn!("Web font loaded in closed pipeline {}.", pipeline_id);
3052 return;
3053 };
3054
3055 document.dirty_all_nodes();
3057 }
3058
3059 fn handle_worklet_loaded(&self, pipeline_id: PipelineId) {
3062 if let Some(document) = self.documents.borrow().find_document(pipeline_id) {
3063 document.add_restyle_reason(RestyleReason::PaintWorkletLoaded);
3064 }
3065 }
3066
3067 fn handle_storage_event(
3069 &self,
3070 pipeline_id: PipelineId,
3071 storage_type: StorageType,
3072 url: ServoUrl,
3073 key: Option<String>,
3074 old_value: Option<String>,
3075 new_value: Option<String>,
3076 ) {
3077 let window = match self.documents.borrow().find_window(pipeline_id) {
3078 None => return warn!("Storage event sent to closed pipeline {}.", pipeline_id),
3079 Some(window) => window,
3080 };
3081
3082 let storage = match storage_type {
3083 StorageType::Local => window.LocalStorage(),
3084 StorageType::Session => window.SessionStorage(),
3085 };
3086
3087 storage.queue_storage_event(url, key, old_value, new_value);
3088 }
3089
3090 fn handle_iframe_load_event(
3092 &self,
3093 parent_id: PipelineId,
3094 browsing_context_id: BrowsingContextId,
3095 child_id: PipelineId,
3096 can_gc: CanGc,
3097 ) {
3098 let iframe = self
3099 .documents
3100 .borrow()
3101 .find_iframe(parent_id, browsing_context_id);
3102 match iframe {
3103 Some(iframe) => iframe.iframe_load_event_steps(child_id, can_gc),
3104 None => warn!("Message sent to closed pipeline {}.", parent_id),
3105 }
3106 }
3107
3108 fn ask_constellation_for_browsing_context_info(
3109 &self,
3110 pipeline_id: PipelineId,
3111 ) -> Option<(BrowsingContextId, Option<PipelineId>)> {
3112 let (result_sender, result_receiver) = ipc::channel().unwrap();
3113 let msg = ScriptToConstellationMessage::GetBrowsingContextInfo(pipeline_id, result_sender);
3114 self.senders
3115 .pipeline_to_constellation_sender
3116 .send((pipeline_id, msg))
3117 .expect("Failed to send to constellation.");
3118 result_receiver
3119 .recv()
3120 .expect("Failed to get browsing context info from constellation.")
3121 }
3122
3123 fn ask_constellation_for_top_level_info(
3124 &self,
3125 sender_pipeline: PipelineId,
3126 browsing_context_id: BrowsingContextId,
3127 ) -> Option<WebViewId> {
3128 let (result_sender, result_receiver) = ipc::channel().unwrap();
3129 let msg = ScriptToConstellationMessage::GetTopForBrowsingContext(
3130 browsing_context_id,
3131 result_sender,
3132 );
3133 self.senders
3134 .pipeline_to_constellation_sender
3135 .send((sender_pipeline, msg))
3136 .expect("Failed to send to constellation.");
3137 result_receiver
3138 .recv()
3139 .expect("Failed to get top-level id from constellation.")
3140 }
3141
3142 fn remote_window_proxy(
3149 &self,
3150 global_to_clone: &GlobalScope,
3151 webview_id: WebViewId,
3152 pipeline_id: PipelineId,
3153 opener: Option<BrowsingContextId>,
3154 ) -> Option<DomRoot<WindowProxy>> {
3155 let (browsing_context_id, parent_pipeline_id) =
3156 self.ask_constellation_for_browsing_context_info(pipeline_id)?;
3157 if let Some(window_proxy) = self.window_proxies.borrow().get(&browsing_context_id) {
3158 return Some(DomRoot::from_ref(window_proxy));
3159 }
3160
3161 let parent_browsing_context = parent_pipeline_id.and_then(|parent_id| {
3162 self.remote_window_proxy(global_to_clone, webview_id, parent_id, opener)
3163 });
3164
3165 let opener_browsing_context = opener.and_then(ScriptThread::find_window_proxy);
3166
3167 let creator = CreatorBrowsingContextInfo::from(
3168 parent_browsing_context.as_deref(),
3169 opener_browsing_context.as_deref(),
3170 );
3171
3172 let window_proxy = WindowProxy::new_dissimilar_origin(
3173 global_to_clone,
3174 browsing_context_id,
3175 webview_id,
3176 parent_browsing_context.as_deref(),
3177 opener,
3178 creator,
3179 );
3180 self.window_proxies
3181 .borrow_mut()
3182 .insert(browsing_context_id, Dom::from_ref(&*window_proxy));
3183 Some(window_proxy)
3184 }
3185
3186 fn local_window_proxy(
3193 &self,
3194 window: &Window,
3195 browsing_context_id: BrowsingContextId,
3196 webview_id: WebViewId,
3197 parent_info: Option<PipelineId>,
3198 opener: Option<BrowsingContextId>,
3199 ) -> DomRoot<WindowProxy> {
3200 if let Some(window_proxy) = self.window_proxies.borrow().get(&browsing_context_id) {
3201 return DomRoot::from_ref(window_proxy);
3204 }
3205 let iframe = parent_info.and_then(|parent_id| {
3206 self.documents
3207 .borrow()
3208 .find_iframe(parent_id, browsing_context_id)
3209 });
3210 let parent_browsing_context = match (parent_info, iframe.as_ref()) {
3211 (_, Some(iframe)) => Some(iframe.owner_window().window_proxy()),
3212 (Some(parent_id), _) => {
3213 self.remote_window_proxy(window.upcast(), webview_id, parent_id, opener)
3214 },
3215 _ => None,
3216 };
3217
3218 let opener_browsing_context = opener.and_then(ScriptThread::find_window_proxy);
3219
3220 let creator = CreatorBrowsingContextInfo::from(
3221 parent_browsing_context.as_deref(),
3222 opener_browsing_context.as_deref(),
3223 );
3224
3225 let window_proxy = WindowProxy::new(
3226 window,
3227 browsing_context_id,
3228 webview_id,
3229 iframe.as_deref().map(Castable::upcast),
3230 parent_browsing_context.as_deref(),
3231 opener,
3232 creator,
3233 );
3234 self.window_proxies
3235 .borrow_mut()
3236 .insert(browsing_context_id, Dom::from_ref(&*window_proxy));
3237 window_proxy
3238 }
3239
3240 fn load(
3243 &self,
3244 metadata: Metadata,
3245 incomplete: InProgressLoad,
3246 can_gc: CanGc,
3247 ) -> DomRoot<ServoParser> {
3248 let final_url = metadata.final_url.clone();
3249 {
3250 self.senders
3251 .pipeline_to_constellation_sender
3252 .send((
3253 incomplete.pipeline_id,
3254 ScriptToConstellationMessage::SetFinalUrl(final_url.clone()),
3255 ))
3256 .unwrap();
3257 }
3258 debug!(
3259 "ScriptThread: loading {} on pipeline {:?}",
3260 incomplete.load_data.url, incomplete.pipeline_id
3261 );
3262
3263 let origin = if final_url.as_str() == "about:blank" || final_url.as_str() == "about:srcdoc"
3264 {
3265 incomplete.origin.clone()
3266 } else {
3267 MutableOrigin::new(final_url.origin())
3268 };
3269
3270 let script_to_constellation_chan = ScriptToConstellationChan {
3271 sender: self.senders.pipeline_to_constellation_sender.clone(),
3272 pipeline_id: incomplete.pipeline_id,
3273 };
3274
3275 let font_context = Arc::new(FontContext::new(
3276 self.system_font_service.clone(),
3277 self.compositor_api.clone(),
3278 self.resource_threads.clone(),
3279 ));
3280
3281 let image_cache = self
3282 .image_cache
3283 .create_new_image_cache(Some(incomplete.pipeline_id), self.compositor_api.clone());
3284
3285 let layout_config = LayoutConfig {
3286 id: incomplete.pipeline_id,
3287 webview_id: incomplete.webview_id,
3288 url: final_url.clone(),
3289 is_iframe: incomplete.parent_info.is_some(),
3290 script_chan: self.senders.constellation_sender.clone(),
3291 image_cache: image_cache.clone(),
3292 font_context: font_context.clone(),
3293 time_profiler_chan: self.senders.time_profiler_sender.clone(),
3294 compositor_api: self.compositor_api.clone(),
3295 viewport_details: incomplete.viewport_details,
3296 theme: incomplete.theme,
3297 };
3298
3299 let window = Window::new(
3301 incomplete.webview_id,
3302 self.js_runtime.clone(),
3303 self.senders.self_sender.clone(),
3304 self.layout_factory.create(layout_config),
3305 font_context,
3306 self.senders.image_cache_sender.clone(),
3307 image_cache.clone(),
3308 self.resource_threads.clone(),
3309 #[cfg(feature = "bluetooth")]
3310 self.senders.bluetooth_sender.clone(),
3311 self.senders.memory_profiler_sender.clone(),
3312 self.senders.time_profiler_sender.clone(),
3313 self.senders.devtools_server_sender.clone(),
3314 script_to_constellation_chan,
3315 self.senders.constellation_sender.clone(),
3316 incomplete.pipeline_id,
3317 incomplete.parent_info,
3318 incomplete.viewport_details,
3319 origin.clone(),
3320 final_url.clone(),
3321 final_url.clone(),
3326 incomplete.navigation_start,
3327 self.webgl_chan.as_ref().map(|chan| chan.channel()),
3328 #[cfg(feature = "webxr")]
3329 self.webxr_registry.clone(),
3330 self.microtask_queue.clone(),
3331 self.compositor_api.clone(),
3332 self.unminify_js,
3333 self.unminify_css,
3334 self.local_script_source.clone(),
3335 self.user_content_manager.clone(),
3336 self.player_context.clone(),
3337 #[cfg(feature = "webgpu")]
3338 self.gpu_id_hub.clone(),
3339 incomplete.load_data.inherited_secure_context,
3340 incomplete.theme,
3341 );
3342 self.debugger_global.fire_add_debuggee(
3343 can_gc,
3344 window.upcast(),
3345 incomplete.pipeline_id,
3346 None,
3347 );
3348
3349 let _realm = enter_realm(&*window);
3350
3351 let window_proxy = self.local_window_proxy(
3353 &window,
3354 incomplete.browsing_context_id,
3355 incomplete.webview_id,
3356 incomplete.parent_info,
3357 incomplete.opener,
3358 );
3359 if window_proxy.parent().is_some() {
3360 window_proxy.stop_delaying_load_events_mode();
3365 }
3366 window.init_window_proxy(&window_proxy);
3367
3368 let last_modified = metadata.headers.as_ref().and_then(|headers| {
3369 headers.typed_get::<LastModified>().map(|tm| {
3370 let tm: SystemTime = tm.into();
3371 let local_time: DateTime<Local> = tm.into();
3372 local_time.format("%m/%d/%Y %H:%M:%S").to_string()
3373 })
3374 });
3375
3376 let loader = DocumentLoader::new_with_threads(
3377 self.resource_threads.clone(),
3378 Some(final_url.clone()),
3379 );
3380
3381 let content_type: Option<Mime> = metadata
3382 .content_type
3383 .map(Serde::into_inner)
3384 .map(Mime::from_ct);
3385
3386 let is_html_document = match content_type {
3387 Some(ref mime) if mime.type_ == APPLICATION && mime.has_suffix("xml") => {
3388 IsHTMLDocument::NonHTMLDocument
3389 },
3390
3391 Some(ref mime) if mime.matches(TEXT, XML) || mime.matches(APPLICATION, XML) => {
3392 IsHTMLDocument::NonHTMLDocument
3393 },
3394 _ => IsHTMLDocument::HTMLDocument,
3395 };
3396
3397 let referrer = metadata
3398 .referrer
3399 .as_ref()
3400 .map(|referrer| referrer.clone().into_string());
3401
3402 let is_initial_about_blank = final_url.as_str() == "about:blank";
3403
3404 let document = Document::new(
3405 &window,
3406 HasBrowsingContext::Yes,
3407 Some(final_url.clone()),
3408 origin,
3409 is_html_document,
3410 content_type,
3411 last_modified,
3412 incomplete.activity,
3413 DocumentSource::FromParser,
3414 loader,
3415 referrer,
3416 Some(metadata.status.raw_code()),
3417 incomplete.canceller,
3418 is_initial_about_blank,
3419 true,
3420 incomplete.load_data.inherited_insecure_requests_policy,
3421 incomplete.load_data.has_trustworthy_ancestor_origin,
3422 self.custom_element_reaction_stack.clone(),
3423 can_gc,
3424 );
3425
3426 let referrer_policy = metadata
3427 .headers
3428 .as_deref()
3429 .and_then(|h| h.typed_get::<ReferrerPolicyHeader>())
3430 .into();
3431 document.set_referrer_policy(referrer_policy);
3432
3433 let refresh_header = metadata.headers.as_deref().and_then(|h| h.get(REFRESH));
3434 if let Some(refresh_val) = refresh_header {
3435 document.shared_declarative_refresh_steps(refresh_val.as_bytes());
3437 }
3438
3439 document.set_ready_state(DocumentReadyState::Loading, can_gc);
3440
3441 self.documents
3442 .borrow_mut()
3443 .insert(incomplete.pipeline_id, &document);
3444
3445 window.init_document(&document);
3446
3447 if let Some(frame) = window_proxy
3450 .frame_element()
3451 .and_then(|e| e.downcast::<HTMLIFrameElement>())
3452 {
3453 let parent_pipeline = frame.global().pipeline_id();
3454 self.handle_update_pipeline_id(
3455 parent_pipeline,
3456 window_proxy.browsing_context_id(),
3457 window_proxy.webview_id(),
3458 incomplete.pipeline_id,
3459 UpdatePipelineIdReason::Navigation,
3460 can_gc,
3461 );
3462 }
3463
3464 self.senders
3465 .pipeline_to_constellation_sender
3466 .send((
3467 incomplete.pipeline_id,
3468 ScriptToConstellationMessage::ActivateDocument,
3469 ))
3470 .unwrap();
3471
3472 let is_top_level_global = incomplete.webview_id.0 == incomplete.browsing_context_id;
3474 self.notify_devtools(
3475 document.Title(),
3476 final_url.clone(),
3477 is_top_level_global,
3478 (
3479 incomplete.browsing_context_id,
3480 incomplete.pipeline_id,
3481 None,
3482 incomplete.webview_id,
3483 ),
3484 );
3485
3486 document.set_https_state(metadata.https_state);
3487 document.set_navigation_start(incomplete.navigation_start);
3488
3489 if is_html_document == IsHTMLDocument::NonHTMLDocument {
3490 ServoParser::parse_xml_document(&document, None, final_url, can_gc);
3491 } else {
3492 ServoParser::parse_html_document(&document, None, final_url, can_gc);
3493 }
3494
3495 if incomplete.activity == DocumentActivity::FullyActive {
3496 window.resume(can_gc);
3497 } else {
3498 window.suspend(can_gc);
3499 }
3500
3501 if incomplete.throttled {
3502 window.set_throttled(true);
3503 }
3504
3505 document.get_current_parser().unwrap()
3506 }
3507
3508 fn notify_devtools(
3509 &self,
3510 title: DOMString,
3511 url: ServoUrl,
3512 is_top_level_global: bool,
3513 (browsing_context_id, pipeline_id, worker_id, webview_id): (
3514 BrowsingContextId,
3515 PipelineId,
3516 Option<WorkerId>,
3517 WebViewId,
3518 ),
3519 ) {
3520 if let Some(ref chan) = self.senders.devtools_server_sender {
3521 let page_info = DevtoolsPageInfo {
3522 title: String::from(title),
3523 url,
3524 is_top_level_global,
3525 };
3526 chan.send(ScriptToDevtoolsControlMsg::NewGlobal(
3527 (browsing_context_id, pipeline_id, worker_id, webview_id),
3528 self.senders.devtools_client_to_script_thread_sender.clone(),
3529 page_info.clone(),
3530 ))
3531 .unwrap();
3532
3533 let state = NavigationState::Stop(pipeline_id, page_info);
3534 let _ = chan.send(ScriptToDevtoolsControlMsg::Navigate(
3535 browsing_context_id,
3536 state,
3537 ));
3538 }
3539 }
3540
3541 fn handle_input_event(&self, pipeline_id: PipelineId, event: ConstellationInputEvent) {
3544 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
3545 warn!("Compositor event sent to closed pipeline {pipeline_id}.");
3546 return;
3547 };
3548
3549 if let InputEvent::MouseButton(mouse_button_event) = &event.event {
3562 if let MouseButton::Left = mouse_button_event.button {
3563 match mouse_button_event.action {
3564 MouseButtonAction::Up => {
3565 let pixel_dist =
3566 self.relative_mouse_down_point.get() - mouse_button_event.point;
3567 let pixel_dist =
3568 (pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y).sqrt();
3569 if pixel_dist < 10.0 * document.window().device_pixel_ratio().get() {
3570 document.event_handler().note_pending_input_event(
3572 ConstellationInputEvent {
3573 hit_test_result: event.hit_test_result.clone(),
3574 pressed_mouse_buttons: event.pressed_mouse_buttons,
3575 active_keyboard_modifiers: event.active_keyboard_modifiers,
3576 event: event.event.clone().with_webdriver_message_id(None),
3577 },
3578 );
3579 document.event_handler().note_pending_input_event(
3580 ConstellationInputEvent {
3581 hit_test_result: event.hit_test_result,
3582 pressed_mouse_buttons: event.pressed_mouse_buttons,
3583 active_keyboard_modifiers: event.active_keyboard_modifiers,
3584 event: InputEvent::MouseButton(MouseButtonEvent::new(
3585 MouseButtonAction::Click,
3586 mouse_button_event.button,
3587 mouse_button_event.point,
3588 ))
3589 .with_webdriver_message_id(event.event.webdriver_message_id()),
3590 },
3591 );
3592 return;
3593 }
3594 },
3595 MouseButtonAction::Down => {
3596 self.relative_mouse_down_point.set(mouse_button_event.point)
3597 },
3598 MouseButtonAction::Click => {},
3599 }
3600 }
3601 }
3602
3603 document.event_handler().note_pending_input_event(event);
3604 }
3605
3606 fn handle_navigate_iframe(
3608 &self,
3609 parent_pipeline_id: PipelineId,
3610 browsing_context_id: BrowsingContextId,
3611 load_data: LoadData,
3612 history_handling: NavigationHistoryBehavior,
3613 can_gc: CanGc,
3614 ) {
3615 let iframe = self
3616 .documents
3617 .borrow()
3618 .find_iframe(parent_pipeline_id, browsing_context_id);
3619 if let Some(iframe) = iframe {
3620 iframe.navigate_or_reload_child_browsing_context(load_data, history_handling, can_gc);
3621 }
3622 }
3623
3624 pub(crate) fn eval_js_url(global_scope: &GlobalScope, load_data: &mut LoadData, can_gc: CanGc) {
3627 let encoded = &load_data.url[Position::AfterScheme..][1..];
3632
3633 let script_source = percent_decode(encoded.as_bytes()).decode_utf8_lossy();
3635
3636 let _ac = enter_realm(global_scope);
3638 rooted!(in(*GlobalScope::get_cx()) let mut jsval = UndefinedValue());
3639 _ = global_scope.evaluate_js_on_global_with_result(
3640 &script_source,
3641 jsval.handle_mut(),
3642 ScriptFetchOptions::default_classic_script(global_scope),
3643 global_scope.api_base_url(),
3644 can_gc,
3645 Some(IntroductionType::JAVASCRIPT_URL),
3646 );
3647
3648 load_data.js_eval_result = if jsval.get().is_string() {
3649 let strval = DOMString::safe_from_jsval(
3650 GlobalScope::get_cx(),
3651 jsval.handle(),
3652 StringificationBehavior::Empty,
3653 );
3654 match strval {
3655 Ok(ConversionResult::Success(s)) => {
3656 Some(JsEvalResult::Ok(String::from(s).as_bytes().to_vec()))
3657 },
3658 _ => None,
3659 }
3660 } else {
3661 Some(JsEvalResult::NoContent)
3662 };
3663
3664 load_data.url = ServoUrl::parse("about:blank").unwrap();
3665 }
3666
3667 fn pre_page_load(&self, mut incomplete: InProgressLoad) {
3670 let context = ParserContext::new(incomplete.pipeline_id, incomplete.load_data.url.clone());
3671 self.incomplete_parser_contexts
3672 .0
3673 .borrow_mut()
3674 .push((incomplete.pipeline_id, context));
3675
3676 let request_builder = incomplete.request_builder();
3677 incomplete.canceller = FetchCanceller::new(
3678 request_builder.id,
3679 self.resource_threads.core_thread.clone(),
3680 );
3681 NavigationListener::new(request_builder, self.senders.self_sender.clone())
3682 .initiate_fetch(&self.resource_threads.core_thread, None);
3683 self.incomplete_loads.borrow_mut().push(incomplete);
3684 }
3685
3686 fn handle_navigation_response(&self, pipeline_id: PipelineId, message: FetchResponseMsg) {
3687 if let Some(metadata) = NavigationListener::http_redirect_metadata(&message) {
3688 self.handle_navigation_redirect(pipeline_id, metadata);
3689 return;
3690 };
3691
3692 match message {
3693 FetchResponseMsg::ProcessResponse(request_id, metadata) => {
3694 self.handle_fetch_metadata(pipeline_id, request_id, metadata)
3695 },
3696 FetchResponseMsg::ProcessResponseChunk(request_id, chunk) => {
3697 self.handle_fetch_chunk(pipeline_id, request_id, chunk)
3698 },
3699 FetchResponseMsg::ProcessResponseEOF(request_id, eof) => {
3700 self.handle_fetch_eof(pipeline_id, request_id, eof)
3701 },
3702 FetchResponseMsg::ProcessCspViolations(request_id, violations) => {
3703 self.handle_csp_violations(pipeline_id, request_id, violations)
3704 },
3705 FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => {
3706 },
3707 }
3708 }
3709
3710 fn handle_fetch_metadata(
3711 &self,
3712 id: PipelineId,
3713 request_id: RequestId,
3714 fetch_metadata: Result<FetchMetadata, NetworkError>,
3715 ) {
3716 match fetch_metadata {
3717 Ok(_) => (),
3718 Err(NetworkError::Crash(..)) => (),
3719 Err(ref e) => {
3720 warn!("Network error: {:?}", e);
3721 },
3722 };
3723
3724 let mut incomplete_parser_contexts = self.incomplete_parser_contexts.0.borrow_mut();
3725 let parser = incomplete_parser_contexts
3726 .iter_mut()
3727 .find(|&&mut (pipeline_id, _)| pipeline_id == id);
3728 if let Some(&mut (_, ref mut ctxt)) = parser {
3729 ctxt.process_response(request_id, fetch_metadata);
3730 }
3731 }
3732
3733 fn handle_fetch_chunk(&self, pipeline_id: PipelineId, request_id: RequestId, chunk: Vec<u8>) {
3734 let mut incomplete_parser_contexts = self.incomplete_parser_contexts.0.borrow_mut();
3735 let parser = incomplete_parser_contexts
3736 .iter_mut()
3737 .find(|&&mut (parser_pipeline_id, _)| parser_pipeline_id == pipeline_id);
3738 if let Some(&mut (_, ref mut ctxt)) = parser {
3739 ctxt.process_response_chunk(request_id, chunk);
3740 }
3741 }
3742
3743 fn handle_fetch_eof(
3744 &self,
3745 id: PipelineId,
3746 request_id: RequestId,
3747 eof: Result<ResourceFetchTiming, NetworkError>,
3748 ) {
3749 let idx = self
3750 .incomplete_parser_contexts
3751 .0
3752 .borrow()
3753 .iter()
3754 .position(|&(pipeline_id, _)| pipeline_id == id);
3755
3756 if let Some(idx) = idx {
3757 let (_, mut ctxt) = self.incomplete_parser_contexts.0.borrow_mut().remove(idx);
3758 ctxt.process_response_eof(request_id, eof);
3759 }
3760 }
3761
3762 fn handle_csp_violations(&self, id: PipelineId, _: RequestId, violations: Vec<Violation>) {
3763 if let Some(global) = self.documents.borrow().find_global(id) {
3764 global.report_csp_violations(violations, None, None);
3766 }
3767 }
3768
3769 fn handle_navigation_redirect(&self, id: PipelineId, metadata: &Metadata) {
3770 assert!(metadata.location_url.is_some());
3774
3775 let mut incomplete_loads = self.incomplete_loads.borrow_mut();
3776 let Some(incomplete_load) = incomplete_loads
3777 .iter_mut()
3778 .find(|incomplete_load| incomplete_load.pipeline_id == id)
3779 else {
3780 return;
3781 };
3782
3783 incomplete_load.url_list.push(metadata.final_url.clone());
3786
3787 let mut request_builder = incomplete_load.request_builder();
3788 request_builder.referrer = metadata
3789 .referrer
3790 .clone()
3791 .map(Referrer::ReferrerUrl)
3792 .unwrap_or(Referrer::NoReferrer);
3793 request_builder.referrer_policy = metadata.referrer_policy;
3794
3795 let headers = metadata
3796 .headers
3797 .as_ref()
3798 .map(|headers| headers.clone().into_inner())
3799 .unwrap_or_default();
3800
3801 let response_init = Some(ResponseInit {
3802 url: metadata.final_url.clone(),
3803 location_url: metadata.location_url.clone(),
3804 headers,
3805 referrer: metadata.referrer.clone(),
3806 status_code: metadata
3807 .status
3808 .try_code()
3809 .map(|code| code.as_u16())
3810 .unwrap_or(200),
3811 });
3812
3813 incomplete_load.canceller = FetchCanceller::new(
3814 request_builder.id,
3815 self.resource_threads.core_thread.clone(),
3816 );
3817 NavigationListener::new(request_builder, self.senders.self_sender.clone())
3818 .initiate_fetch(&self.resource_threads.core_thread, response_init);
3819 }
3820
3821 fn start_page_load_about_blank(&self, mut incomplete: InProgressLoad) {
3824 let id = incomplete.pipeline_id;
3825
3826 let url = ServoUrl::parse("about:blank").unwrap();
3827 let mut context = ParserContext::new(id, url.clone());
3828
3829 let mut meta = Metadata::default(url);
3830 meta.set_content_type(Some(&mime::TEXT_HTML));
3831 meta.set_referrer_policy(incomplete.load_data.referrer_policy);
3832
3833 let chunk = match incomplete.load_data.js_eval_result {
3836 Some(JsEvalResult::Ok(ref mut content)) => std::mem::take(content),
3837 Some(JsEvalResult::NoContent) => {
3838 meta.status = http::StatusCode::NO_CONTENT.into();
3839 vec![]
3840 },
3841 None => vec![],
3842 };
3843
3844 let policy_container = incomplete.load_data.policy_container.clone();
3845 self.incomplete_loads.borrow_mut().push(incomplete);
3846
3847 let dummy_request_id = RequestId::default();
3848 context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
3849 context.append_parent_to_csp_list(policy_container.as_ref());
3850 context.process_response_chunk(dummy_request_id, chunk);
3851 context.process_response_eof(
3852 dummy_request_id,
3853 Ok(ResourceFetchTiming::new(ResourceTimingType::None)),
3854 );
3855 }
3856
3857 fn page_load_about_srcdoc(&self, mut incomplete: InProgressLoad) {
3859 let id = incomplete.pipeline_id;
3860
3861 let url = ServoUrl::parse("about:srcdoc").unwrap();
3862 let mut meta = Metadata::default(url.clone());
3863 meta.set_content_type(Some(&mime::TEXT_HTML));
3864 meta.set_referrer_policy(incomplete.load_data.referrer_policy);
3865
3866 let srcdoc = std::mem::take(&mut incomplete.load_data.srcdoc);
3867 let chunk = srcdoc.into_bytes();
3868
3869 let policy_container = incomplete.load_data.policy_container.clone();
3870 self.incomplete_loads.borrow_mut().push(incomplete);
3871
3872 let mut context = ParserContext::new(id, url);
3873 let dummy_request_id = RequestId::default();
3874
3875 context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
3876 context.append_parent_to_csp_list(policy_container.as_ref());
3877 context.process_response_chunk(dummy_request_id, chunk);
3878 context.process_response_eof(
3879 dummy_request_id,
3880 Ok(ResourceFetchTiming::new(ResourceTimingType::None)),
3881 );
3882 }
3883
3884 fn handle_css_error_reporting(
3885 &self,
3886 pipeline_id: PipelineId,
3887 filename: String,
3888 line: u32,
3889 column: u32,
3890 msg: String,
3891 ) {
3892 let sender = match self.senders.devtools_server_sender {
3893 Some(ref sender) => sender,
3894 None => return,
3895 };
3896
3897 if let Some(global) = self.documents.borrow().find_global(pipeline_id) {
3898 if global.live_devtools_updates() {
3899 let css_error = CSSError {
3900 filename,
3901 line,
3902 column,
3903 msg,
3904 };
3905 let message = ScriptToDevtoolsControlMsg::ReportCSSError(pipeline_id, css_error);
3906 sender.send(message).unwrap();
3907 }
3908 }
3909 }
3910
3911 fn handle_reload(&self, pipeline_id: PipelineId, can_gc: CanGc) {
3912 let window = self.documents.borrow().find_window(pipeline_id);
3913 if let Some(window) = window {
3914 window.Location().reload_without_origin_check(can_gc);
3915 }
3916 }
3917
3918 fn handle_paint_metric(
3919 &self,
3920 pipeline_id: PipelineId,
3921 metric_type: ProgressiveWebMetricType,
3922 metric_value: CrossProcessInstant,
3923 first_reflow: bool,
3924 can_gc: CanGc,
3925 ) {
3926 match self.documents.borrow().find_document(pipeline_id) {
3927 Some(document) => {
3928 document.handle_paint_metric(metric_type, metric_value, first_reflow, can_gc)
3929 },
3930 None => warn!(
3931 "Received paint metric ({metric_type:?}) for unknown document: {pipeline_id:?}"
3932 ),
3933 }
3934 }
3935
3936 fn handle_media_session_action(
3937 &self,
3938 pipeline_id: PipelineId,
3939 action: MediaSessionActionType,
3940 can_gc: CanGc,
3941 ) {
3942 if let Some(window) = self.documents.borrow().find_window(pipeline_id) {
3943 let media_session = window.Navigator().MediaSession();
3944 media_session.handle_action(action, can_gc);
3945 } else {
3946 warn!("No MediaSession for this pipeline ID");
3947 };
3948 }
3949
3950 pub(crate) fn enqueue_microtask(job: Microtask) {
3951 with_script_thread(|script_thread| {
3952 script_thread
3953 .microtask_queue
3954 .enqueue(job, script_thread.get_cx());
3955 });
3956 }
3957
3958 fn perform_a_microtask_checkpoint(&self, can_gc: CanGc) {
3959 if self.can_continue_running_inner() {
3961 let globals = self
3962 .documents
3963 .borrow()
3964 .iter()
3965 .map(|(_id, document)| DomRoot::from_ref(document.window().upcast()))
3966 .collect();
3967
3968 self.microtask_queue.checkpoint(
3969 self.get_cx(),
3970 |id| self.documents.borrow().find_global(id),
3971 globals,
3972 can_gc,
3973 )
3974 }
3975 }
3976
3977 fn handle_evaluate_javascript(
3978 &self,
3979 pipeline_id: PipelineId,
3980 evaluation_id: JavaScriptEvaluationId,
3981 script: String,
3982 can_gc: CanGc,
3983 ) {
3984 let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
3985 let _ = self.senders.pipeline_to_constellation_sender.send((
3986 pipeline_id,
3987 ScriptToConstellationMessage::FinishJavaScriptEvaluation(
3988 evaluation_id,
3989 Err(JavaScriptEvaluationError::WebViewNotReady),
3990 ),
3991 ));
3992 return;
3993 };
3994
3995 let global_scope = window.as_global_scope();
3996 let realm = enter_realm(global_scope);
3997 let context = window.get_cx();
3998
3999 rooted!(in(*context) let mut return_value = UndefinedValue());
4000 if let Err(err) = global_scope.evaluate_js_on_global_with_result(
4001 &script,
4002 return_value.handle_mut(),
4003 ScriptFetchOptions::default_classic_script(global_scope),
4004 global_scope.api_base_url(),
4005 can_gc,
4006 None, ) {
4008 _ = self.senders.pipeline_to_constellation_sender.send((
4009 pipeline_id,
4010 ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, Err(err)),
4011 ));
4012 return;
4013 };
4014
4015 let result = jsval_to_webdriver(
4016 context,
4017 global_scope,
4018 return_value.handle(),
4019 (&realm).into(),
4020 can_gc,
4021 )
4022 .map_err(|_| JavaScriptEvaluationError::SerializationError);
4023
4024 let _ = self.senders.pipeline_to_constellation_sender.send((
4025 pipeline_id,
4026 ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result),
4027 ));
4028 }
4029
4030 fn handle_refresh_cursor(&self, pipeline_id: PipelineId) {
4031 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
4032 return;
4033 };
4034 document.event_handler().handle_refresh_cursor();
4035 }
4036
4037 pub(crate) fn is_servo_privileged(url: ServoUrl) -> bool {
4038 with_script_thread(|script_thread| script_thread.privileged_urls.contains(&url))
4039 }
4040}
4041
4042impl Drop for ScriptThread {
4043 fn drop(&mut self) {
4044 SCRIPT_THREAD_ROOT.with(|root| {
4045 root.set(None);
4046 });
4047 }
4048}