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