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