1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
21
22use std::cell::{Cell, RefCell};
23use std::collections::HashSet;
24use std::default::Default;
25use std::option::Option;
26use std::rc::{Rc, Weak};
27use std::result::Result;
28use std::sync::Arc;
29use std::sync::atomic::{AtomicBool, Ordering};
30use std::thread::{self, JoinHandle};
31use std::time::{Duration, Instant, SystemTime};
32
33use background_hang_monitor_api::{
34 BackgroundHangMonitor, BackgroundHangMonitorExitSignal, BackgroundHangMonitorRegister,
35 HangAnnotation, MonitoredComponentId, MonitoredComponentType,
36};
37use bytes::Bytes;
38use chrono::{DateTime, Local};
39use crossbeam_channel::unbounded;
40use data_url::mime::Mime;
41use devtools_traits::{
42 CSSError, DevtoolScriptControlMsg, DevtoolsPageInfo, NavigationState,
43 ScriptToDevtoolsControlMsg, WorkerId,
44};
45use embedder_traits::user_contents::{UserContentManagerId, UserContents, UserScript};
46use embedder_traits::{
47 EmbedderControlId, EmbedderControlResponse, EmbedderMsg, FocusSequenceNumber,
48 InputEventOutcome, JavaScriptEvaluationError, JavaScriptEvaluationId, MediaSessionActionType,
49 ViewportDetails, WebDriverScriptCommand,
50};
51use encoding_rs::Encoding;
52use fonts::{FontContext, SystemFontServiceProxy, WebFontLoadEvent};
53use headers::{HeaderMapExt, LastModified, ReferrerPolicy as ReferrerPolicyHeader};
54use http::header::REFRESH;
55use hyper_serde::Serde;
56use js::context::{JSContext, NoGC};
57use js::glue::GetWindowProxyClass;
58use js::jsapi::{GCReason, JSContext as UnsafeJSContext};
59use js::jsval::UndefinedValue;
60use js::rust::ParentRuntime;
61use js::rust::wrappers2::{JS_AddInterruptCallback, JS_GC, SetWindowProxyClass};
62use layout_api::{LayoutConfig, LayoutFactory, RestyleReason, ScriptThreadFactory};
63use media::WindowGLContext;
64use metrics::MAX_TASK_NS;
65use net_traits::image_cache::{ImageCacheFactory, ImageCacheResponseMessage};
66use net_traits::pub_domains::is_same_site;
67use net_traits::request::{Referrer, RequestId};
68use net_traits::response::ResponseInit;
69use net_traits::{
70 FetchMetadata, FetchResponseMsg, Metadata, NetworkError, ResourceFetchTiming, ResourceThreads,
71 ResourceTimingType,
72};
73use paint_api::{CrossProcessPaintApi, PinchZoomInfos, PipelineExitSource};
74use percent_encoding::percent_decode;
75use profile_traits::mem::{ProcessReports, ReportsChan, perform_memory_report};
76use profile_traits::time::ProfilerCategory;
77use profile_traits::time_profile;
78use rustc_hash::{FxHashMap, FxHashSet};
79use script_bindings::cell::DomRefCell;
80use script_traits::{
81 ConstellationInputEvent, DiscardBrowsingContext, DocumentActivity, InitialScriptState,
82 NewPipelineInfo, Painter, ScriptThreadMessage, UpdatePipelineIdReason, WebViewState,
83};
84use servo_arc::Arc as ServoArc;
85use servo_base::cross_process_instant::CrossProcessInstant;
86use servo_base::generic_channel::GenericSender;
87use servo_base::id::{
88 BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespace, ScriptEventLoopId, WebViewId,
89};
90use servo_base::threadboost::{BoostAffinity, ThreadPriority};
91use servo_base::{Epoch, generic_channel};
92#[cfg(feature = "webgl")]
93use servo_canvas_traits::webgl::WebGLPipeline;
94use servo_config::opts::{self, DiagnosticsLoggingOption};
95use servo_config::{pref, prefs};
96use servo_constellation_traits::{
97 HistoryTraversalSource, LoadData, LoadOrigin, NavigationHistoryBehavior, PaintMetricEvent,
98 RemoteFocusOperation, ScreenshotReadinessResponse, ScriptToConstellationChan,
99 ScriptToConstellationMessage, ScrollStateUpdate, SessionHistoryTraversalRequest,
100 StructuredSerializedData, TargetSnapshotParams, TraversalDirection, WindowSizeType,
101};
102use servo_url::{ImmutableOrigin, MutableOrigin, OriginSnapshot, ServoUrl};
103use smallvec::SmallVec;
104use storage_traits::StorageThreads;
105use storage_traits::webstorage_thread::WebStorageType;
106use style::context::QuirksMode;
107use style::error_reporting::RustLogReporter;
108use style::media_queries::MediaList;
109use style::shared_lock::SharedRwLock;
110use style::stylesheets::{AllowImportRules, DocumentStyleSheet, Origin, Stylesheet};
111use style::thread_state::{self, ThreadState};
112use stylo_atoms::Atom;
113use timers::{TimerEventRequest, TimerId, TimerScheduler};
114use url::Position;
115#[cfg(feature = "webgpu")]
116use webgpu_traits::{WebGPUDevice, WebGPUMsg};
117
118use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
119use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods;
120use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
121use crate::dom::bindings::conversions::{
122 ConversionResult, FromJSValConvertible, StringificationBehavior,
123};
124use crate::dom::bindings::inheritance::Castable;
125use crate::dom::bindings::root::{Dom, DomRoot};
126use crate::dom::bindings::str::DOMString;
127use crate::dom::csp::{CspReporting, GlobalCspReporting, Violation};
128use crate::dom::customelementregistry::{
129 CallbackReaction, CustomElementDefinition, CustomElementReactionStack,
130};
131use crate::dom::document::focus::FocusableArea;
132use crate::dom::document::{
133 AbortReason, Document, HasBrowsingContext, IsHTMLDocument, RenderingUpdateReason,
134};
135use crate::dom::element::Element;
136use crate::dom::globalscope::GlobalScope;
137use crate::dom::html::htmliframeelement::{HTMLIFrameElement, IframeContext, ProcessingMode};
138use crate::dom::node::{Node, NodeTraits};
139use crate::dom::script_execution::{RethrowErrors, ScriptOptions};
140use crate::dom::servoparser::{ParserContext, ServoParser};
141use crate::dom::types::DebuggerGlobalScope;
142#[cfg(feature = "webgpu")]
143use crate::dom::webgpu::identityhub::IdentityHub;
144use crate::dom::window::Window;
145use crate::dom::windowproxy::{CreatorBrowsingContextInfo, WindowProxy};
146use crate::event_loop::devtools::{self, DevtoolsState};
147use crate::event_loop::document_collection::DocumentCollection;
148use crate::event_loop::document_loader::DocumentLoader;
149use crate::event_loop::script_mutation_observers::ScriptMutationObservers;
150use crate::event_loop::script_window_proxies::ScriptWindowProxies;
151use crate::event_loop::svg_font::SvgFontResolver;
152use crate::event_loop::webdriver_handlers::{self, jsval_to_webdriver};
153use crate::fetch::fetch::FetchCanceller;
154use crate::fetch::network_listener::submit_timing;
155use crate::messaging::{
156 CommonScriptMsg, MainThreadScriptMsg, MixedMessage, ScriptEventLoopSender,
157 ScriptThreadReceivers, ScriptThreadSenders,
158};
159use crate::mime::{APPLICATION, CHARSET, MimeExt, TEXT, XML};
160use crate::modules::script_module::ScriptFetchOptions;
161use crate::navigation::{InProgressLoad, NavigationListener};
162use crate::realms::enter_auto_realm;
163use crate::runtime::job_queue::{MicrotaskRunnable, job_queue_microtask_checkpoint};
164use crate::runtime::script_runtime::{
165 IntroductionType, Runtime, ScriptThreadEventCategory, ThreadSafeJSContext, get_reports,
166};
167use crate::tasks::task_queue::TaskQueue;
168
169thread_local!(static SCRIPT_THREAD_ROOT: Cell<Option<*const ScriptThread>> = const { Cell::new(None) });
170
171fn with_optional_script_thread<R>(f: impl FnOnce(Option<&ScriptThread>) -> R) -> R {
172 SCRIPT_THREAD_ROOT.with(|root| {
173 f(root
174 .get()
175 .and_then(|script_thread| unsafe { script_thread.as_ref() }))
176 })
177}
178
179pub(crate) fn with_script_thread<R: Default>(f: impl FnOnce(&ScriptThread) -> R) -> R {
180 with_optional_script_thread(|script_thread| script_thread.map(f).unwrap_or_default())
181}
182
183pub(crate) struct IncompleteParserContexts(RefCell<Vec<(PipelineId, ParserContext)>>);
189
190unsafe_no_jsmanaged_fields!(TaskQueue<MainThreadScriptMsg>);
191
192type NodeIdSet = HashSet<String>;
193
194#[derive(Default)]
196pub(crate) struct ScriptUserInteractingGuard {
197 was_interacting: bool,
198 user_interaction_cell: Rc<Cell<bool>>,
199}
200
201impl ScriptUserInteractingGuard {
202 fn new(user_interaction_cell: Rc<Cell<bool>>) -> Self {
203 let was_interacting = user_interaction_cell.get();
204 user_interaction_cell.set(true);
205 Self {
206 was_interacting,
207 user_interaction_cell,
208 }
209 }
210}
211
212impl Drop for ScriptUserInteractingGuard {
213 fn drop(&mut self) {
214 self.user_interaction_cell.set(self.was_interacting)
215 }
216}
217
218struct ScriptThreadUserContents {
221 user_scripts: Rc<Vec<UserScript>>,
222 user_stylesheets: Rc<Vec<DocumentStyleSheet>>,
223}
224
225impl ScriptThreadUserContents {
226 fn new(user_contents: UserContents, shared_locks: &SharedRwLocks) -> Self {
227 let user_stylesheets = user_contents
228 .stylesheets
229 .iter()
230 .map(|user_stylesheet| {
231 DocumentStyleSheet(ServoArc::new(Stylesheet::from_str(
232 user_stylesheet.source(),
233 user_stylesheet.url().into(),
234 Origin::User,
235 ServoArc::new(shared_locks.ua_or_user.wrap(MediaList::empty())),
236 shared_locks.ua_or_user.clone(),
237 None,
238 Some(&RustLogReporter),
239 QuirksMode::NoQuirks,
240 AllowImportRules::Yes,
241 )))
242 })
243 .collect();
244 Self {
245 user_scripts: Rc::new(user_contents.scripts),
246 user_stylesheets: Rc::new(user_stylesheets),
247 }
248 }
249}
250
251#[derive(Clone, MallocSizeOf)]
252pub struct SharedRwLocks {
253 pub author: SharedRwLock,
254 pub ua_or_user: SharedRwLock,
255}
256
257impl Default for SharedRwLocks {
258 fn default() -> Self {
259 Self {
260 author: SharedRwLock::new(),
261 ua_or_user: SharedRwLock::new(),
262 }
263 }
264}
265
266#[derive(JSTraceable)]
267#[cfg_attr(crown, expect(crown::unrooted_must_root))]
269pub struct ScriptThread {
270 #[no_trace]
273 this: Weak<ScriptThread>,
274
275 last_render_opportunity_time: Cell<Option<Instant>>,
277 #[no_trace]
282 webview_states: RefCell<FxHashMap<WebViewId, Weak<WebViewState>>>,
283
284 documents: DomRefCell<DocumentCollection>,
286
287 window_proxies: Rc<ScriptWindowProxies>,
289
290 incomplete_loads: DomRefCell<Vec<InProgressLoad>>,
292
293 incomplete_parser_contexts: IncompleteParserContexts,
295
296 #[no_trace]
299 image_cache_factory: Arc<dyn ImageCacheFactory>,
300
301 receivers: ScriptThreadReceivers,
304
305 senders: ScriptThreadSenders,
308
309 #[no_trace]
312 resource_threads: ResourceThreads,
313
314 #[no_trace]
315 storage_threads: StorageThreads,
316
317 task_queue: TaskQueue<MainThreadScriptMsg>,
319
320 #[no_trace]
322 background_hang_monitor: Box<dyn BackgroundHangMonitor>,
323 closing: Arc<AtomicBool>,
325
326 #[no_trace]
329 timer_scheduler: RefCell<TimerScheduler>,
330
331 #[no_trace]
333 system_font_service: Arc<SystemFontServiceProxy>,
334
335 js_runtime: Rc<Runtime>,
337
338 #[no_trace]
340 closed_pipelines: DomRefCell<FxHashSet<PipelineId>>,
341
342 mutation_observers: Rc<ScriptMutationObservers>,
343
344 #[no_trace]
346 #[cfg(feature = "webgl")]
347 webgl_chan: Option<WebGLPipeline>,
348
349 #[no_trace]
351 #[cfg(feature = "webxr")]
352 webxr_registry: Option<webxr_api::Registry>,
353
354 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
356
357 #[no_trace]
359 paint_api: CrossProcessPaintApi,
360
361 profile_script_events: bool,
363
364 unminify_js: bool,
366
367 local_script_source: Option<String>,
369
370 unminify_css: bool,
372
373 #[no_trace]
375 shared_style_locks: SharedRwLocks,
376
377 #[no_trace]
381 user_contents_for_manager_id:
382 RefCell<FxHashMap<UserContentManagerId, ScriptThreadUserContents>>,
383
384 #[no_trace]
386 player_context: WindowGLContext,
387
388 #[no_trace]
390 pipeline_to_node_ids: DomRefCell<FxHashMap<PipelineId, NodeIdSet>>,
391
392 is_user_interacting: Rc<Cell<bool>>,
394
395 #[no_trace]
397 #[cfg(feature = "webgpu")]
398 gpu_id_hub: Arc<IdentityHub>,
399
400 #[no_trace]
402 layout_factory: Arc<dyn LayoutFactory>,
403
404 #[no_trace]
408 scheduled_update_the_rendering: RefCell<Option<TimerId>>,
409
410 needs_rendering_update: Arc<AtomicBool>,
417
418 debugger_global: Dom<DebuggerGlobalScope>,
419
420 debugger_paused: Cell<bool>,
421
422 #[no_trace]
424 privileged_urls: Vec<ServoUrl>,
425
426 devtools_state: DevtoolsState,
427}
428
429struct BHMExitSignal {
430 closing: Arc<AtomicBool>,
431 js_context: ThreadSafeJSContext,
432}
433
434impl BackgroundHangMonitorExitSignal for BHMExitSignal {
435 fn signal_to_exit(&self) {
436 self.closing.store(true, Ordering::SeqCst);
437 self.js_context.request_interrupt_callback();
438 }
439}
440
441#[expect(unsafe_code)]
442unsafe extern "C" fn interrupt_callback(_cx: *mut UnsafeJSContext) -> bool {
443 let res = ScriptThread::can_continue_running();
444 if !res {
445 ScriptThread::prepare_for_shutdown();
446 }
447 res
448}
449
450struct ScriptMemoryFailsafe<'a> {
455 owner: Option<&'a ScriptThread>,
456}
457
458impl<'a> ScriptMemoryFailsafe<'a> {
459 fn neuter(&mut self) {
460 self.owner = None;
461 }
462
463 fn new(owner: &'a ScriptThread) -> ScriptMemoryFailsafe<'a> {
464 ScriptMemoryFailsafe { owner: Some(owner) }
465 }
466}
467
468impl Drop for ScriptMemoryFailsafe<'_> {
469 fn drop(&mut self) {
470 if let Some(owner) = self.owner {
471 for (_, document) in owner.documents.borrow().iter() {
472 document.window().clear_js_runtime_for_script_deallocation();
473 }
474 }
475 }
476}
477
478impl ScriptThreadFactory for ScriptThread {
479 fn create(
480 state: InitialScriptState,
481 layout_factory: Arc<dyn LayoutFactory>,
482 image_cache_factory: Arc<dyn ImageCacheFactory>,
483 background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
484 ) -> JoinHandle<()> {
485 PipelineNamespace::set_installer_sender(state.namespace_request_sender.clone());
488
489 let script_thread_id = state.id;
490 thread::Builder::new()
491 .name(format!("Script#{script_thread_id}"))
492 .stack_size(8 * 1024 * 1024) .spawn(move || {
494 profile_traits::debug_event!(
495 "ScriptThread::spawned",
496 script_thread_id = script_thread_id.to_string()
497 );
498 thread_state::initialize(ThreadState::SCRIPT);
499 PipelineNamespace::install(state.pipeline_namespace_id);
500 ScriptEventLoopId::install(state.id);
501 let memory_profiler_sender = state.memory_profiler_sender.clone();
502 let reporter_name = format!("script-reporter-{script_thread_id:?}");
503 let (script_thread, mut cx) = ScriptThread::new(
504 state,
505 layout_factory,
506 image_cache_factory,
507 background_hang_monitor_register,
508 );
509 SCRIPT_THREAD_ROOT.with(|root| {
510 root.set(Some(Rc::as_ptr(&script_thread)));
511 });
512 servo_base::threadboost::boost_thread(
513 ThreadPriority::Critical,
514 BoostAffinity::Boost,
515 );
516 let mut failsafe = ScriptMemoryFailsafe::new(&script_thread);
517
518 memory_profiler_sender.run_with_memory_reporting(
519 || script_thread.start(&mut cx),
520 reporter_name,
521 ScriptEventLoopSender::MainThread(script_thread.senders.self_sender.clone()),
522 CommonScriptMsg::CollectReports,
523 );
524
525 failsafe.neuter();
527 })
528 .expect("Thread spawning failed")
529 }
530}
531
532#[servo_tracing::instrument_all(skip_all)]
533impl ScriptThread {
534 fn shared_webview_state(&self, shared_state: &WebViewState) -> Rc<WebViewState> {
538 let mut webview_states = self.webview_states.borrow_mut();
539
540 webview_states.retain(|_, state| state.upgrade().is_some());
542
543 if let Some(existing_shared_state) =
544 webview_states.get(&shared_state.id).and_then(Weak::upgrade)
545 {
546 return existing_shared_state;
547 }
548
549 let shared_state = Rc::new(shared_state.clone());
550 webview_states.insert(shared_state.id, Rc::downgrade(&shared_state));
551 shared_state
552 }
553
554 pub(crate) fn runtime_handle() -> ParentRuntime {
555 with_optional_script_thread(|script_thread| {
556 script_thread.unwrap().js_runtime.prepare_for_new_child()
557 })
558 }
559
560 pub(crate) fn can_continue_running() -> bool {
561 with_script_thread(|script_thread| script_thread.can_continue_running_inner())
562 }
563
564 pub(crate) fn prepare_for_shutdown() {
565 with_script_thread(|script_thread| {
566 script_thread.prepare_for_shutdown_inner();
567 })
568 }
569
570 pub(crate) fn mutation_observers() -> Rc<ScriptMutationObservers> {
571 with_script_thread(|script_thread| script_thread.mutation_observers.clone())
572 }
573
574 pub(crate) fn shared_style_locks(&self) -> &SharedRwLocks {
575 &self.shared_style_locks
576 }
577
578 pub(crate) fn process_event(msg: CommonScriptMsg, cx: &mut js::context::JSContext) -> bool {
582 with_script_thread(|script_thread| {
583 if !script_thread.can_continue_running_inner() {
584 return false;
585 }
586 script_thread.handle_msg_from_script(MainThreadScriptMsg::Common(msg), cx);
587 true
588 })
589 }
590
591 pub(crate) fn schedule_timer(&self, request: TimerEventRequest) -> TimerId {
593 self.timer_scheduler.borrow_mut().schedule_timer(request)
594 }
595
596 pub(crate) fn cancel_timer(&self, timer_id: TimerId) {
599 self.timer_scheduler.borrow_mut().cancel_timer(timer_id)
600 }
601
602 pub(crate) fn await_stable_state(cx: &JSContext, task: Box<dyn MicrotaskRunnable>) {
604 crate::runtime::job_queue::enqueue(cx, task);
605 }
606
607 fn check_load_origin(source: &LoadOrigin, target: &OriginSnapshot) -> bool {
612 match source {
613 LoadOrigin::Constellation | LoadOrigin::WebDriver => {
614 true
616 },
617 LoadOrigin::Script(source_origin) => source_origin.same_origin_domain(target),
618 }
619 }
620
621 pub(crate) fn set_needs_rendering_update(&self) {
625 self.needs_rendering_update.store(true, Ordering::Relaxed);
626 }
627
628 pub(crate) fn can_navigate_to_javascript_url(
630 cx: &mut js::context::JSContext,
631 initiator_global: &GlobalScope,
632 target_global: &GlobalScope,
633 load_data: &mut LoadData,
634 container: Option<&Element>,
635 ) -> bool {
636 if !Self::check_load_origin(&load_data.load_origin, &target_global.origin().snapshot()) {
640 return false;
641 }
642
643 if initiator_global
646 .get_csp_list()
647 .should_navigation_request_be_blocked(cx, initiator_global, load_data, container)
648 {
649 return false;
650 }
651
652 true
653 }
654
655 pub(crate) fn navigate_to_javascript_url(
658 cx: &mut js::context::JSContext,
659 initiator_global: &GlobalScope,
660 target_global: &GlobalScope,
661 load_data: &mut LoadData,
662 container: Option<&Element>,
663 initial_insertion: Option<bool>,
664 ) -> bool {
665 if !Self::can_navigate_to_javascript_url(
668 cx,
669 initiator_global,
670 target_global,
671 load_data,
672 container,
673 ) {
674 return false;
675 }
676
677 if !Self::evaluate_a_javascript_url(cx, target_global, load_data) {
680 let window_proxy = target_global.as_window().window_proxy();
682 if let Some(frame_element) = window_proxy
683 .frame_element()
684 .and_then(Castable::downcast::<HTMLIFrameElement>)
685 {
686 if initial_insertion == Some(true) && frame_element.is_initial_blank_document() {
690 frame_element.run_iframe_load_event_steps(cx);
691 }
692 }
693 return false;
695 }
696
697 true
698 }
699
700 pub(crate) fn get_top_level_for_browsing_context(
701 sender_webview_id: WebViewId,
702 sender_pipeline_id: PipelineId,
703 browsing_context_id: BrowsingContextId,
704 ) -> Option<WebViewId> {
705 with_script_thread(|script_thread| {
706 script_thread.ask_constellation_for_top_level_info(
707 sender_webview_id,
708 sender_pipeline_id,
709 browsing_context_id,
710 )
711 })
712 }
713
714 pub(crate) fn find_window(id: PipelineId) -> Option<DomRoot<Window>> {
715 with_script_thread(|script_thread| script_thread.documents.borrow().find_window(id))
716 }
717
718 pub(crate) fn find_document(id: PipelineId) -> Option<DomRoot<Document>> {
719 with_script_thread(|script_thread| script_thread.documents.borrow().find_document(id))
720 }
721
722 #[must_use]
726 pub(crate) fn user_interacting_guard() -> ScriptUserInteractingGuard {
727 with_script_thread(|script_thread| {
728 ScriptUserInteractingGuard::new(script_thread.is_user_interacting.clone())
729 })
730 }
731
732 pub(crate) fn is_user_interacting() -> bool {
733 with_script_thread(|script_thread| script_thread.is_user_interacting.get())
734 }
735
736 pub(crate) fn get_fully_active_document_ids(&self) -> FxHashSet<PipelineId> {
737 self.documents
738 .borrow()
739 .iter()
740 .filter_map(|(id, document)| {
741 if document.is_fully_active() {
742 Some(id)
743 } else {
744 None
745 }
746 })
747 .fold(FxHashSet::default(), |mut set, id| {
748 let _ = set.insert(id);
749 set
750 })
751 }
752
753 pub(crate) fn window_proxies() -> Rc<ScriptWindowProxies> {
754 with_script_thread(|script_thread| script_thread.window_proxies.clone())
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 cx: &mut js::context::JSContext,
786 element: &Element,
787 reaction: CallbackReaction,
788 definition: Option<Rc<CustomElementDefinition>>,
789 ) {
790 with_script_thread(|script_thread| {
791 script_thread
792 .custom_element_reaction_stack
793 .enqueue_callback_reaction(cx, element, reaction, definition);
794 })
795 }
796
797 pub(crate) fn enqueue_upgrade_reaction(
798 cx: &js::context::JSContext,
799 element: &Element,
800 definition: Rc<CustomElementDefinition>,
801 ) {
802 with_script_thread(|script_thread| {
803 script_thread
804 .custom_element_reaction_stack
805 .enqueue_upgrade_reaction(cx, element, definition);
806 })
807 }
808
809 pub(crate) fn invoke_backup_element_queue(cx: &mut js::context::JSContext) {
810 with_script_thread(|script_thread| {
811 script_thread
812 .custom_element_reaction_stack
813 .invoke_backup_element_queue(cx);
814 })
815 }
816
817 pub(crate) fn save_node_id(pipeline: PipelineId, node_id: String) {
818 with_script_thread(|script_thread| {
819 script_thread
820 .pipeline_to_node_ids
821 .borrow_mut()
822 .entry(pipeline)
823 .or_default()
824 .insert(node_id);
825 })
826 }
827
828 pub(crate) fn has_node_id(pipeline: PipelineId, node_id: &str) -> bool {
829 with_script_thread(|script_thread| {
830 script_thread
831 .pipeline_to_node_ids
832 .borrow()
833 .get(&pipeline)
834 .is_some_and(|node_ids| node_ids.contains(node_id))
835 })
836 }
837
838 #[servo_tracing::instrument(name = "ScripThread::new", level = "debug", skip_all)]
840 pub(crate) fn new(
841 state: InitialScriptState,
842 layout_factory: Arc<dyn LayoutFactory>,
843 image_cache_factory: Arc<dyn ImageCacheFactory>,
844 background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
845 ) -> (Rc<ScriptThread>, js::context::JSContext) {
846 let (self_sender, self_receiver) = unbounded();
847 let mut runtime =
848 Runtime::new(Some(ScriptEventLoopSender::MainThread(self_sender.clone())));
849
850 let mut cx = unsafe { runtime.cx() };
853
854 unsafe {
855 SetWindowProxyClass(&cx, GetWindowProxyClass());
856 JS_AddInterruptCallback(&cx, Some(interrupt_callback));
857 }
858
859 let constellation_receiver = state
860 .constellation_to_script_receiver
861 .route_preserving_errors();
862
863 let devtools_server_sender = state.devtools_server_sender;
865 let (ipc_devtools_sender, ipc_devtools_receiver) = generic_channel::channel().unwrap();
866 let devtools_server_receiver = ipc_devtools_receiver.route_preserving_errors();
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(state.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.script_to_constellation_sender,
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 #[cfg(feature = "webgpu")]
912 let gpu_id_hub = Arc::new(IdentityHub::default());
913
914 let debugger_global = DebuggerGlobalScope::new(
915 PipelineId::new(),
916 senders.devtools_server_sender.clone(),
917 senders.devtools_client_to_script_thread_sender.clone(),
918 senders.memory_profiler_sender.clone(),
919 senders.time_profiler_sender.clone(),
920 senders.pipeline_to_constellation_sender.clone(),
921 senders.pipeline_to_embedder_sender.clone(),
922 state.resource_threads.clone(),
923 state.storage_threads.clone(),
924 #[cfg(feature = "webgpu")]
925 gpu_id_hub.clone(),
926 &mut cx,
927 );
928
929 debugger_global.execute(&mut cx);
930
931 let shared_style_locks = Default::default();
932 let user_contents_for_manager_id =
933 FxHashMap::from_iter(state.user_contents_for_manager_id.into_iter().map(
934 |(user_content_manager_id, user_contents)| {
935 (
936 user_content_manager_id,
937 ScriptThreadUserContents::new(user_contents, &shared_style_locks),
938 )
939 },
940 ));
941
942 (
943 Rc::new_cyclic(|weak_script_thread| {
944 runtime.set_script_thread(weak_script_thread.clone());
945 Self {
946 webview_states: Default::default(),
947 documents: DomRefCell::new(DocumentCollection::default()),
948 last_render_opportunity_time: Default::default(),
949 window_proxies: Default::default(),
950 incomplete_loads: DomRefCell::new(vec![]),
951 incomplete_parser_contexts: IncompleteParserContexts(RefCell::new(vec![])),
952 senders,
953 receivers,
954 image_cache_factory,
955 resource_threads: state.resource_threads,
956 storage_threads: state.storage_threads,
957 task_queue,
958 background_hang_monitor,
959 closing,
960 timer_scheduler: Default::default(),
961 js_runtime: Rc::new(runtime),
962 closed_pipelines: DomRefCell::new(FxHashSet::default()),
963 mutation_observers: Default::default(),
964 system_font_service: Arc::new(state.system_font_service.to_proxy()),
965 #[cfg(feature = "webgl")]
966 webgl_chan: state.webgl_chan,
967 #[cfg(feature = "webxr")]
968 webxr_registry: state.webxr_registry,
969 custom_element_reaction_stack: Rc::new(CustomElementReactionStack::new()),
970 paint_api: state.cross_process_paint_api,
971 profile_script_events: opts
972 .debug
973 .is_enabled(DiagnosticsLoggingOption::ProfileScriptEvents),
974 unminify_js: opts.unminify_js,
975 local_script_source: opts.local_script_source.clone(),
976 unminify_css: opts.unminify_css,
977 shared_style_locks,
978 user_contents_for_manager_id: RefCell::new(user_contents_for_manager_id),
979 player_context: state.player_context,
980 pipeline_to_node_ids: Default::default(),
981 is_user_interacting: Rc::new(Cell::new(false)),
982 #[cfg(feature = "webgpu")]
983 gpu_id_hub,
984 layout_factory,
985 scheduled_update_the_rendering: Default::default(),
986 needs_rendering_update: Arc::new(AtomicBool::new(false)),
987 debugger_global: debugger_global.as_traced(),
988 debugger_paused: Cell::new(false),
989 privileged_urls: state.privileged_urls,
990 this: weak_script_thread.clone(),
991 devtools_state: Default::default(),
992 }
993 }),
994 cx,
995 )
996 }
997
998 fn can_continue_running_inner(&self) -> bool {
1000 if self.closing.load(Ordering::SeqCst) {
1001 return false;
1002 }
1003 true
1004 }
1005
1006 fn prepare_for_shutdown_inner(&self) {
1008 let docs = self.documents.borrow();
1009 for (_, document) in docs.iter() {
1010 document
1011 .owner_global()
1012 .task_manager()
1013 .cancel_all_tasks_and_ignore_future_tasks();
1014 }
1015 }
1016
1017 pub(crate) fn start(&self, cx: &mut js::context::JSContext) {
1020 debug!("Starting script thread.");
1021 while self.handle_msgs(cx) {
1022 debug!("Running script thread.");
1024 }
1025 debug!("Stopped script thread.");
1026 }
1027
1028 fn process_pending_input_events(
1030 &self,
1031 cx: &mut js::context::JSContext,
1032 pipeline_id: PipelineId,
1033 ) {
1034 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
1035 warn!("Processing pending input events for closed pipeline {pipeline_id}.");
1036 return;
1037 };
1038 if document.window().Closed() {
1040 warn!("Input event sent to a pipeline with a closed window {pipeline_id}.");
1041 return;
1042 }
1043 if !document.event_handler().has_pending_input_events() {
1044 return;
1045 }
1046
1047 let _guard = ScriptUserInteractingGuard::new(self.is_user_interacting.clone());
1048 document.event_handler().handle_pending_input_events(cx);
1049 }
1050
1051 fn cancel_scheduled_update_the_rendering(&self) {
1052 if let Some(timer_id) = self.scheduled_update_the_rendering.borrow_mut().take() {
1053 self.timer_scheduler.borrow_mut().cancel_timer(timer_id);
1054 }
1055 }
1056
1057 fn schedule_update_the_rendering_timer_if_necessary(&self, delay: Duration) {
1058 if self.scheduled_update_the_rendering.borrow().is_some() {
1059 return;
1060 }
1061
1062 debug!("Scheduling ScriptThread animation frame.");
1063 let trigger_script_thread_animation = self.needs_rendering_update.clone();
1064 let timer_id = self.schedule_timer(TimerEventRequest {
1065 callback: Box::new(move || {
1066 trigger_script_thread_animation.store(true, Ordering::Relaxed);
1067 }),
1068 duration: delay,
1069 });
1070
1071 *self.scheduled_update_the_rendering.borrow_mut() = Some(timer_id);
1072 }
1073
1074 pub(crate) fn update_the_rendering(&self, cx: &mut js::context::JSContext) -> bool {
1081 self.last_render_opportunity_time.set(Some(Instant::now()));
1082 self.cancel_scheduled_update_the_rendering();
1083 self.needs_rendering_update.store(false, Ordering::Relaxed);
1084
1085 if !self.can_continue_running_inner() {
1086 return false;
1087 }
1088
1089 let documents_in_order = self.documents.borrow().documents_in_order();
1108
1109 let mut painters_generating_frames = FxHashSet::default();
1114 for pipeline_id in documents_in_order.iter() {
1115 let Some(document) = self.documents.borrow().find_document(*pipeline_id) else {
1116 continue;
1117 };
1118
1119 if !document.is_fully_active() {
1120 continue;
1121 }
1122
1123 if document.waiting_on_canvas_image_updates() {
1124 continue;
1125 }
1126
1127 if
1130 document.is_render_blocked()
1132 {
1143 continue;
1144 }
1145
1146 document.clear_rendering_update_reasons();
1149
1150 self.process_pending_input_events(cx, *pipeline_id);
1157
1158 let resized = document.window().run_the_resize_steps(cx);
1160
1161 document.run_the_scroll_steps(cx);
1163
1164 let media_features_changed = document.window().take_pending_media_query_evaluation();
1171 if resized || media_features_changed {
1172 document
1173 .window()
1174 .evaluate_media_queries_and_report_changes(cx);
1175 }
1176 if resized {
1177 document.react_to_environment_changes(cx);
1180 }
1181
1182 let mut realm = enter_auto_realm(cx, &*document);
1183 let cx = &mut realm.current_realm();
1184
1185 if !document.window().throttled() {
1187 document.update_animations_and_send_events(cx);
1191
1192 document.run_the_animation_frame_callbacks(cx);
1202 }
1203
1204 let mut depth = Default::default();
1206 while document.gather_active_resize_observations_at_depth(cx.no_gc(), &depth) {
1207 depth = document.broadcast_active_resize_observations(cx);
1209 }
1210
1211 if document.has_skipped_resize_observations() {
1212 document.deliver_resize_loop_error_notification(cx);
1213 document.add_rendering_update_reason(
1216 RenderingUpdateReason::ResizeObserverStartedObservingTarget,
1217 );
1218 }
1219
1220 document.focus_handler().perform_focus_fixup_rule(cx);
1225
1226 document.update_intersection_observer_steps(cx, CrossProcessInstant::now());
1234
1235 if document.is_render_blocked() {
1240 continue;
1241 }
1242
1243 if document.update_the_rendering(cx).0.needs_frame() {
1249 painters_generating_frames.insert(document.webview_id().into());
1250 }
1251
1252 }
1255
1256 let should_generate_frame = !painters_generating_frames.is_empty();
1257 if should_generate_frame {
1258 self.paint_api
1259 .generate_frame(painters_generating_frames.into_iter().collect());
1260 }
1261
1262 self.perform_a_microtask_checkpoint(cx);
1265 should_generate_frame
1266 }
1267
1268 fn maybe_schedule_rendering_opportunity_after_ipc_message(
1275 &self,
1276 no_gc: &NoGC,
1277 built_any_display_lists: bool,
1278 ) {
1279 let needs_rendering_update = self
1280 .documents
1281 .borrow()
1282 .iter()
1283 .any(|(_, document)| document.needs_rendering_update(no_gc));
1284 let running_animations = self.documents.borrow().iter().any(|(_, document)| {
1285 document.is_fully_active() &&
1286 !document.window().throttled() &&
1287 (document.animation_manager().running_animation_count() != 0 ||
1288 document.has_active_request_animation_frame_callbacks())
1289 });
1290
1291 if !needs_rendering_update && !running_animations {
1295 return;
1296 }
1297
1298 if running_animations && built_any_display_lists {
1302 return;
1303 }
1304
1305 let animation_delay = if running_animations && !needs_rendering_update {
1313 Duration::from_millis(30)
1317 } else {
1318 Duration::from_millis(20)
1321 };
1322
1323 let time_since_last_rendering_opportunity = self
1324 .last_render_opportunity_time
1325 .get()
1326 .map(|last_render_opportunity_time| Instant::now() - last_render_opportunity_time)
1327 .unwrap_or(Duration::MAX)
1328 .min(animation_delay);
1329 self.schedule_update_the_rendering_timer_if_necessary(
1330 animation_delay - time_since_last_rendering_opportunity,
1331 );
1332 }
1333
1334 fn maybe_fulfill_font_ready_promises(&self, cx: &mut js::context::JSContext) {
1337 let mut sent_message = false;
1338 for (_, document) in self.documents.borrow().iter() {
1339 sent_message = document.maybe_fulfill_font_ready_promise(cx) || sent_message;
1340 }
1341
1342 if sent_message {
1343 self.perform_a_microtask_checkpoint(cx);
1344 }
1345 }
1346
1347 fn maybe_resolve_pending_screenshot_readiness_requests(&self, cx: &mut js::context::JSContext) {
1351 for (_, document) in self.documents.borrow().iter() {
1352 document
1353 .window()
1354 .maybe_resolve_pending_screenshot_readiness_requests(cx);
1355 }
1356 }
1357
1358 fn handle_msgs(&self, cx: &mut js::context::JSContext) -> bool {
1360 let mut sequential: SmallVec<[MixedMessage; 10]> = SmallVec::new();
1362
1363 self.background_hang_monitor.notify_wait();
1365
1366 debug!("Waiting for event.");
1368 let fully_active = self.get_fully_active_document_ids();
1369 let mut event = self.receivers.recv(
1370 &self.task_queue,
1371 &self.timer_scheduler.borrow(),
1372 &fully_active,
1373 );
1374
1375 loop {
1376 debug!("Handling event: {event:?}");
1377
1378 self.timer_scheduler
1380 .borrow_mut()
1381 .dispatch_completed_timers();
1382
1383 match event {
1385 MixedMessage::FromConstellation(ScriptThreadMessage::SpawnPipeline(
1389 new_pipeline_info,
1390 )) => {
1391 self.spawn_pipeline(cx, new_pipeline_info);
1392 },
1393 MixedMessage::FromScript(MainThreadScriptMsg::Inactive) => {
1394 },
1397 MixedMessage::FromConstellation(ScriptThreadMessage::ExitFullScreen(id)) => self
1398 .profile_event(ScriptThreadEventCategory::ExitFullscreen, Some(id), || {
1399 self.handle_exit_fullscreen(id, cx);
1400 }),
1401 _ => {
1402 sequential.push(event);
1403 },
1404 }
1405
1406 match self.receivers.try_recv(&self.task_queue, &fully_active) {
1410 Some(new_event) => event = new_event,
1411 None => break,
1412 }
1413 }
1414
1415 debug!("Processing events.");
1417 for msg in sequential.drain(..) {
1418 debug!("Processing event {:?}.", msg);
1419 let category = self.categorize_msg(&msg);
1420 let pipeline_id = msg.pipeline_id();
1421 macro_rules! handle_message(
1426 ( $cx:ident ) => (
1427 if self.closing.load(Ordering::SeqCst) {
1428 match msg {
1430 MixedMessage::FromConstellation(ScriptThreadMessage::ExitScriptThread) => {
1431 self.handle_exit_script_thread_msg($cx);
1432 return false;
1433 },
1434 MixedMessage::FromConstellation(ScriptThreadMessage::ExitPipeline(
1435 webview_id,
1436 pipeline_id,
1437 discard_browsing_context,
1438 )) => {
1439 self.handle_exit_pipeline_msg(
1440 webview_id,
1441 pipeline_id,
1442 discard_browsing_context,
1443 $cx,
1444 );
1445 },
1446 _ => {},
1447 }
1448 continue;
1449 }
1450
1451 let exiting = self.profile_event(category, pipeline_id, || {
1452 match msg {
1453 MixedMessage::FromConstellation(ScriptThreadMessage::ExitScriptThread) => {
1454 self.handle_exit_script_thread_msg($cx);
1455 return true;
1456 },
1457 MixedMessage::FromConstellation(inner_msg) => {
1458 self.handle_msg_from_constellation(inner_msg, $cx)
1459 },
1460 MixedMessage::FromScript(inner_msg) => {
1461 self.handle_msg_from_script(inner_msg, $cx)
1462 },
1463 MixedMessage::FromDevtools(inner_msg) => {
1464 self.handle_msg_from_devtools(*inner_msg, $cx)
1465 },
1466 MixedMessage::FromImageCache(inner_msg) => {
1467 self.handle_msg_from_image_cache(inner_msg, $cx)
1468 },
1469 #[cfg(feature = "webgpu")]
1470 MixedMessage::FromWebGPUServer(inner_msg) => {
1471 self.handle_msg_from_webgpu_server(inner_msg, $cx)
1472 },
1473 MixedMessage::TimerFired => {},
1474 }
1475
1476 false
1477 });
1478
1479 if exiting {
1481 return false;
1482 }
1483
1484 self.perform_a_microtask_checkpoint($cx);
1487 )
1488 );
1489
1490 let global = pipeline_id.and_then(|id| self.documents.borrow().find_global(id));
1491 match global {
1492 None => {
1493 handle_message!(cx);
1494 },
1495 Some(global) => {
1496 let mut realm = enter_auto_realm(cx, &*global);
1497 let cx = &mut realm.current_realm();
1498 handle_message!(cx);
1499 },
1500 };
1501 }
1502
1503 for (_, doc) in self.documents.borrow().iter() {
1504 let window = doc.window();
1505 window
1506 .upcast::<GlobalScope>()
1507 .perform_a_dom_garbage_collection_checkpoint();
1508 }
1509
1510 let built_any_display_lists =
1511 self.needs_rendering_update.load(Ordering::Relaxed) && self.update_the_rendering(cx);
1512
1513 self.maybe_fulfill_font_ready_promises(cx);
1514 self.maybe_resolve_pending_screenshot_readiness_requests(cx);
1515
1516 self.maybe_schedule_rendering_opportunity_after_ipc_message(
1518 cx.no_gc(),
1519 built_any_display_lists,
1520 );
1521
1522 true
1523 }
1524
1525 fn categorize_msg(&self, msg: &MixedMessage) -> ScriptThreadEventCategory {
1526 match *msg {
1527 MixedMessage::FromConstellation(ref inner_msg) => match *inner_msg {
1528 ScriptThreadMessage::SendInputEvent(..) => ScriptThreadEventCategory::InputEvent,
1529 _ => ScriptThreadEventCategory::ConstellationMsg,
1530 },
1531 MixedMessage::FromDevtools(_) => ScriptThreadEventCategory::DevtoolsMsg,
1532 MixedMessage::FromImageCache(_) => ScriptThreadEventCategory::ImageCacheMsg,
1533 MixedMessage::FromScript(ref inner_msg) => match *inner_msg {
1534 MainThreadScriptMsg::Common(CommonScriptMsg::Task(category, ..)) => category,
1535 MainThreadScriptMsg::RegisterPaintWorklet { .. } => {
1536 ScriptThreadEventCategory::WorkletEvent
1537 },
1538 _ => ScriptThreadEventCategory::ScriptEvent,
1539 },
1540 #[cfg(feature = "webgpu")]
1541 MixedMessage::FromWebGPUServer(_) => ScriptThreadEventCategory::WebGPUMsg,
1542 MixedMessage::TimerFired => ScriptThreadEventCategory::TimerEvent,
1543 }
1544 }
1545
1546 fn profile_event<F, R>(
1547 &self,
1548 category: ScriptThreadEventCategory,
1549 pipeline_id: Option<PipelineId>,
1550 f: F,
1551 ) -> R
1552 where
1553 F: FnOnce() -> R,
1554 {
1555 self.background_hang_monitor
1556 .notify_activity(HangAnnotation::Script(category.into()));
1557 let start = Instant::now();
1558 let value = if self.profile_script_events {
1559 let profiler_chan = self.senders.time_profiler_sender.clone();
1560 match category {
1561 ScriptThreadEventCategory::SpawnPipeline => {
1562 time_profile!(
1563 ProfilerCategory::ScriptSpawnPipeline,
1564 None,
1565 profiler_chan,
1566 f
1567 )
1568 },
1569 ScriptThreadEventCategory::ConstellationMsg => time_profile!(
1570 ProfilerCategory::ScriptConstellationMsg,
1571 None,
1572 profiler_chan,
1573 f
1574 ),
1575 ScriptThreadEventCategory::DatabaseAccessEvent => time_profile!(
1576 ProfilerCategory::ScriptDatabaseAccessEvent,
1577 None,
1578 profiler_chan,
1579 f
1580 ),
1581 ScriptThreadEventCategory::DevtoolsMsg => {
1582 time_profile!(ProfilerCategory::ScriptDevtoolsMsg, None, profiler_chan, f)
1583 },
1584 ScriptThreadEventCategory::DocumentEvent => time_profile!(
1585 ProfilerCategory::ScriptDocumentEvent,
1586 None,
1587 profiler_chan,
1588 f
1589 ),
1590 ScriptThreadEventCategory::InputEvent => {
1591 time_profile!(ProfilerCategory::ScriptInputEvent, None, profiler_chan, f)
1592 },
1593 ScriptThreadEventCategory::FileRead => {
1594 time_profile!(ProfilerCategory::ScriptFileRead, None, profiler_chan, f)
1595 },
1596 ScriptThreadEventCategory::FontLoading => {
1597 time_profile!(ProfilerCategory::ScriptFontLoading, None, profiler_chan, f)
1598 },
1599 ScriptThreadEventCategory::FormPlannedNavigation => time_profile!(
1600 ProfilerCategory::ScriptPlannedNavigation,
1601 None,
1602 profiler_chan,
1603 f
1604 ),
1605 ScriptThreadEventCategory::GeolocationEvent => {
1606 time_profile!(
1607 ProfilerCategory::ScriptGeolocationEvent,
1608 None,
1609 profiler_chan,
1610 f
1611 )
1612 },
1613 ScriptThreadEventCategory::NavigationAndTraversalEvent => {
1614 time_profile!(
1615 ProfilerCategory::ScriptNavigationAndTraversalEvent,
1616 None,
1617 profiler_chan,
1618 f
1619 )
1620 },
1621 ScriptThreadEventCategory::ImageCacheMsg => time_profile!(
1622 ProfilerCategory::ScriptImageCacheMsg,
1623 None,
1624 profiler_chan,
1625 f
1626 ),
1627 ScriptThreadEventCategory::NetworkEvent => {
1628 time_profile!(ProfilerCategory::ScriptNetworkEvent, None, profiler_chan, f)
1629 },
1630 ScriptThreadEventCategory::PortMessage => {
1631 time_profile!(ProfilerCategory::ScriptPortMessage, None, profiler_chan, f)
1632 },
1633 ScriptThreadEventCategory::Resize => {
1634 time_profile!(ProfilerCategory::ScriptResize, None, profiler_chan, f)
1635 },
1636 ScriptThreadEventCategory::ScriptEvent => {
1637 time_profile!(ProfilerCategory::ScriptEvent, None, profiler_chan, f)
1638 },
1639 ScriptThreadEventCategory::SetScrollState => time_profile!(
1640 ProfilerCategory::ScriptSetScrollState,
1641 None,
1642 profiler_chan,
1643 f
1644 ),
1645 ScriptThreadEventCategory::UpdateReplacedElement => time_profile!(
1646 ProfilerCategory::ScriptUpdateReplacedElement,
1647 None,
1648 profiler_chan,
1649 f
1650 ),
1651 ScriptThreadEventCategory::StylesheetLoad => time_profile!(
1652 ProfilerCategory::ScriptStylesheetLoad,
1653 None,
1654 profiler_chan,
1655 f
1656 ),
1657 ScriptThreadEventCategory::SetViewport => {
1658 time_profile!(ProfilerCategory::ScriptSetViewport, None, profiler_chan, f)
1659 },
1660 ScriptThreadEventCategory::TimerEvent => {
1661 time_profile!(ProfilerCategory::ScriptTimerEvent, None, profiler_chan, f)
1662 },
1663 ScriptThreadEventCategory::WebSocketEvent => time_profile!(
1664 ProfilerCategory::ScriptWebSocketEvent,
1665 None,
1666 profiler_chan,
1667 f
1668 ),
1669 ScriptThreadEventCategory::WorkerEvent => {
1670 time_profile!(ProfilerCategory::ScriptWorkerEvent, None, profiler_chan, f)
1671 },
1672 ScriptThreadEventCategory::WorkletEvent => {
1673 time_profile!(ProfilerCategory::ScriptWorkletEvent, None, profiler_chan, f)
1674 },
1675 ScriptThreadEventCategory::ServiceWorkerEvent => time_profile!(
1676 ProfilerCategory::ScriptServiceWorkerEvent,
1677 None,
1678 profiler_chan,
1679 f
1680 ),
1681 ScriptThreadEventCategory::EnterFullscreen => time_profile!(
1682 ProfilerCategory::ScriptEnterFullscreen,
1683 None,
1684 profiler_chan,
1685 f
1686 ),
1687 ScriptThreadEventCategory::ExitFullscreen => time_profile!(
1688 ProfilerCategory::ScriptExitFullscreen,
1689 None,
1690 profiler_chan,
1691 f
1692 ),
1693 ScriptThreadEventCategory::PerformanceTimelineTask => time_profile!(
1694 ProfilerCategory::ScriptPerformanceEvent,
1695 None,
1696 profiler_chan,
1697 f
1698 ),
1699 ScriptThreadEventCategory::Rendering => {
1700 time_profile!(ProfilerCategory::ScriptRendering, None, profiler_chan, f)
1701 },
1702 #[cfg(feature = "webgpu")]
1703 ScriptThreadEventCategory::WebGPUMsg => {
1704 time_profile!(ProfilerCategory::ScriptWebGPUMsg, None, profiler_chan, f)
1705 },
1706 }
1707 } else {
1708 f()
1709 };
1710 let task_duration = start.elapsed();
1711 for (doc_id, doc) in self.documents.borrow().iter() {
1712 if let Some(pipeline_id) = pipeline_id &&
1713 pipeline_id == doc_id &&
1714 task_duration.as_nanos() > MAX_TASK_NS
1715 {
1716 if opts::get()
1717 .debug
1718 .is_enabled(DiagnosticsLoggingOption::ProgressiveWebMetrics)
1719 {
1720 println!(
1721 "Task took longer than max allowed ({category:?}) {:?}",
1722 task_duration.as_nanos()
1723 );
1724 }
1725 doc.start_tti();
1726 }
1727 doc.record_tti_if_necessary();
1728 }
1729 value
1730 }
1731
1732 fn handle_msg_from_constellation(
1733 &self,
1734 msg: ScriptThreadMessage,
1735 cx: &mut js::context::JSContext,
1736 ) {
1737 match msg {
1738 ScriptThreadMessage::StopDelayingLoadEventsMode(pipeline_id) => {
1739 self.handle_stop_delaying_load_events_mode(pipeline_id)
1740 },
1741 ScriptThreadMessage::NavigateIframe(
1742 parent_pipeline_id,
1743 browsing_context_id,
1744 load_data,
1745 history_handling,
1746 target_snapshot_params,
1747 ) => self.handle_navigate_iframe(
1748 parent_pipeline_id,
1749 browsing_context_id,
1750 load_data,
1751 history_handling,
1752 target_snapshot_params,
1753 cx,
1754 ),
1755 ScriptThreadMessage::UnloadDocument(pipeline_id) => {
1756 self.handle_unload_document(cx, pipeline_id)
1757 },
1758 ScriptThreadMessage::ResizeInactive(id, new_size) => {
1759 self.handle_resize_inactive_msg(id, new_size)
1760 },
1761 ScriptThreadMessage::UpdateWebViewState(state) => {
1762 self.handle_update_webview_state(cx, state);
1763 },
1764 ScriptThreadMessage::GetDocumentOrigin(pipeline_id, result_sender) => {
1765 self.handle_get_document_origin(pipeline_id, result_sender);
1766 },
1767 ScriptThreadMessage::GetDocumentOriginDetails(pipeline_id, result_sender) => {
1768 self.handle_get_origin_details(pipeline_id, result_sender);
1769 },
1770 ScriptThreadMessage::GetTitle(pipeline_id) => self.handle_get_title_msg(pipeline_id),
1771 ScriptThreadMessage::SetDocumentActivity(pipeline_id, activity) => {
1772 self.handle_set_document_activity_msg(cx, pipeline_id, activity)
1773 },
1774 ScriptThreadMessage::SetThrottled(pipeline_id, throttled) => {
1775 self.handle_set_throttled_msg(pipeline_id, throttled)
1776 },
1777 ScriptThreadMessage::PostMessage {
1778 target: target_pipeline_id,
1779 source_webview,
1780 source_with_ancestry,
1781 target_origin: origin,
1782 source_origin,
1783 data,
1784 } => self.handle_post_message_msg(
1785 cx,
1786 target_pipeline_id,
1787 source_webview,
1788 source_with_ancestry,
1789 origin,
1790 source_origin,
1791 *data,
1792 ),
1793 ScriptThreadMessage::UpdatePipelineId(
1794 parent_pipeline_id,
1795 browsing_context_id,
1796 webview_id,
1797 new_pipeline_id,
1798 reason,
1799 ) => self.handle_update_pipeline_id(
1800 parent_pipeline_id,
1801 browsing_context_id,
1802 webview_id,
1803 new_pipeline_id,
1804 reason,
1805 cx,
1806 ),
1807 ScriptThreadMessage::UpdateHistoryState(pipeline_id, history_state_id, url) => {
1808 self.handle_update_history_state_msg(cx, pipeline_id, history_state_id, url)
1809 },
1810 ScriptThreadMessage::RemoveHistoryStates(pipeline_id, history_states) => {
1811 self.handle_remove_history_states(cx, pipeline_id, history_states)
1812 },
1813 ScriptThreadMessage::FocusDocumentAsPartOfFocusingSteps(
1814 pipeline_id,
1815 sequence,
1816 iframe_browsing_context_id,
1817 ) => self.handle_focus_document_as_part_of_focusing_steps(
1818 cx,
1819 pipeline_id,
1820 sequence,
1821 iframe_browsing_context_id,
1822 ),
1823 ScriptThreadMessage::UnfocusDocumentAsPartOfFocusingSteps(pipeline_id, sequence) => {
1824 self.handle_unfocus_document_as_part_of_focusing_steps(cx, pipeline_id, sequence);
1825 },
1826 ScriptThreadMessage::FocusDocument(pipeline_id, remote_focus_operation) => {
1827 self.handle_focus_document(cx, pipeline_id, remote_focus_operation);
1828 },
1829 ScriptThreadMessage::WebDriverScriptCommand(pipeline_id, msg) => {
1830 self.handle_webdriver_msg(pipeline_id, msg, cx)
1831 },
1832 ScriptThreadMessage::WebFontLoadFinished(pipeline_id, event) => {
1833 if event == WebFontLoadEvent::LoadedSuccessfully {
1837 self.handle_web_font_loaded(cx.no_gc(), pipeline_id)
1838 }
1839 },
1840 ScriptThreadMessage::DispatchIFrameLoadEvent {
1841 target: browsing_context_id,
1842 parent: parent_id,
1843 child: child_id,
1844 } => self.handle_iframe_load_event(parent_id, browsing_context_id, child_id, cx),
1845 ScriptThreadMessage::DispatchStorageEvent(
1846 pipeline_id,
1847 storage,
1848 url,
1849 key,
1850 old_value,
1851 new_value,
1852 ) => {
1853 self.handle_storage_event(pipeline_id, storage, url, key, old_value, new_value, cx)
1854 },
1855 ScriptThreadMessage::ReportCSSError(pipeline_id, filename, line, column, msg) => {
1856 self.handle_css_error_reporting(pipeline_id, filename, line, column, msg)
1857 },
1858 ScriptThreadMessage::Reload(pipeline_id) => self.handle_reload(pipeline_id, cx),
1859 ScriptThreadMessage::Resize(id, size, size_type) => {
1860 self.handle_resize_message(id, size, size_type);
1861 },
1862 ScriptThreadMessage::ExitPipeline(
1863 webview_id,
1864 pipeline_id,
1865 discard_browsing_context,
1866 ) => {
1867 self.handle_exit_pipeline_msg(webview_id, pipeline_id, discard_browsing_context, cx)
1868 },
1869 ScriptThreadMessage::PaintMetric(pipeline_id, event) => {
1870 self.handle_paint_metric(cx, pipeline_id, event)
1871 },
1872 ScriptThreadMessage::MediaSessionAction(pipeline_id, action) => {
1873 self.handle_media_session_action(cx, pipeline_id, action)
1874 },
1875 ScriptThreadMessage::SendInputEvent(webview_id, id, event) => {
1876 self.handle_input_event(webview_id, id, event)
1877 },
1878 #[cfg(feature = "webgpu")]
1879 ScriptThreadMessage::SetWebGPUPort(port) => {
1880 *self.receivers.webgpu_receiver.borrow_mut() = port.route_preserving_errors();
1881 },
1882 ScriptThreadMessage::TickAllAnimations(_webviews) => {
1883 self.set_needs_rendering_update();
1884 },
1885 ScriptThreadMessage::NoLongerWaitingOnAsychronousImageUpdates(pipeline_id) => {
1886 if let Some(document) = self.documents.borrow().find_document(pipeline_id) {
1887 document.handle_no_longer_waiting_on_asynchronous_image_updates();
1888 }
1889 },
1890 msg @ ScriptThreadMessage::SpawnPipeline(..) |
1891 msg @ ScriptThreadMessage::ExitFullScreen(..) |
1892 msg @ ScriptThreadMessage::ExitScriptThread => {
1893 panic!("should have handled {:?} already", msg)
1894 },
1895 ScriptThreadMessage::SetScrollStates(pipeline_id, scroll_states) => {
1896 self.handle_set_scroll_states(pipeline_id, scroll_states)
1897 },
1898 ScriptThreadMessage::EvaluateJavaScript(
1899 webview_id,
1900 pipeline_id,
1901 evaluation_id,
1902 script,
1903 ) => {
1904 self.handle_evaluate_javascript(webview_id, pipeline_id, evaluation_id, script, cx);
1905 },
1906 ScriptThreadMessage::SendImageKeysBatch(pipeline_id, image_keys) => {
1907 if let Some(window) = self.documents.borrow().find_window(pipeline_id) {
1908 window
1909 .image_cache()
1910 .dispatch_fill_key_cache_with_batch_of_keys(image_keys);
1911 } else {
1912 warn!(
1913 "Could not find window corresponding to an image cache to send image keys to pipeline {:?}",
1914 pipeline_id
1915 );
1916 }
1917 },
1918 ScriptThreadMessage::RefreshCursor(pipeline_id) => {
1919 self.handle_refresh_cursor(pipeline_id);
1920 },
1921 ScriptThreadMessage::PreferencesUpdated(updates) => {
1922 let mut current_preferences = prefs::get().clone();
1923 for (name, value) in updates {
1924 current_preferences.set_value(&name, value);
1925 }
1926 prefs::set(current_preferences);
1927 },
1928 ScriptThreadMessage::ForwardKeyboardScroll(pipeline_id, scroll) => {
1929 if let Some(document) = self.documents.borrow().find_document(pipeline_id) {
1930 document.event_handler().do_keyboard_scroll(cx, scroll);
1931 }
1932 },
1933 ScriptThreadMessage::RequestScreenshotReadiness(webview_id, pipeline_id) => {
1934 self.handle_request_screenshot_readiness(webview_id, pipeline_id, cx);
1935 },
1936 ScriptThreadMessage::EmbedderControlResponse(id, response) => {
1937 self.handle_embedder_control_response(id, response, cx);
1938 },
1939 ScriptThreadMessage::SetUserContents(user_content_manager_id, user_contents) => {
1940 self.user_contents_for_manager_id.borrow_mut().insert(
1941 user_content_manager_id,
1942 ScriptThreadUserContents::new(user_contents, &self.shared_style_locks),
1943 );
1944 },
1945 ScriptThreadMessage::DestroyUserContentManager(user_content_manager_id) => {
1946 self.user_contents_for_manager_id
1947 .borrow_mut()
1948 .remove(&user_content_manager_id);
1949 },
1950 ScriptThreadMessage::UpdatePinchZoomInfos(id, pinch_zoom_infos) => {
1951 self.handle_update_pinch_zoom_infos(cx, id, pinch_zoom_infos);
1952 },
1953 ScriptThreadMessage::SetAccessibilityActive(pipeline_id, active, epoch) => {
1954 self.set_accessibility_active(pipeline_id, active, epoch);
1955 },
1956 ScriptThreadMessage::ForwardAccessibilityAction(pipeline_id, action_request) => {
1957 self.forward_accessibility_action(pipeline_id, action_request);
1958 },
1959 ScriptThreadMessage::TriggerGarbageCollection => unsafe {
1960 JS_GC(cx, GCReason::API);
1961 },
1962 }
1963 }
1964
1965 fn handle_set_scroll_states(&self, pipeline_id: PipelineId, scroll_states: ScrollStateUpdate) {
1966 let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
1967 warn!("Received scroll states for closed pipeline {pipeline_id}");
1968 return;
1969 };
1970
1971 self.profile_event(
1972 ScriptThreadEventCategory::SetScrollState,
1973 Some(pipeline_id),
1974 || {
1975 window
1976 .layout_mut()
1977 .set_scroll_offsets_from_renderer(&scroll_states.offsets);
1978 },
1979 );
1980
1981 window
1982 .Document()
1983 .event_handler()
1984 .handle_embedder_scroll_event(scroll_states.scrolled_node);
1985 }
1986
1987 #[cfg(feature = "webgpu")]
1988 fn handle_msg_from_webgpu_server(&self, msg: WebGPUMsg, cx: &mut js::context::JSContext) {
1989 match msg {
1990 WebGPUMsg::FreeAdapter(id) => self.gpu_id_hub.free_adapter_id(id),
1991 WebGPUMsg::FreeDevice {
1992 device_id,
1993 pipeline_id,
1994 } => {
1995 self.gpu_id_hub.free_device_id(device_id);
1996 if let Some(global) = self.documents.borrow().find_global(pipeline_id) {
1997 global.remove_gpu_device(WebGPUDevice(device_id));
1998 } },
2000 WebGPUMsg::FreeBuffer(id) => self.gpu_id_hub.free_buffer_id(id),
2001 WebGPUMsg::FreePipelineLayout(id) => self.gpu_id_hub.free_pipeline_layout_id(id),
2002 WebGPUMsg::FreeComputePipeline(id) => self.gpu_id_hub.free_compute_pipeline_id(id),
2003 WebGPUMsg::FreeBindGroup(id) => self.gpu_id_hub.free_bind_group_id(id),
2004 WebGPUMsg::FreeBindGroupLayout(id) => self.gpu_id_hub.free_bind_group_layout_id(id),
2005 WebGPUMsg::FreeCommandBuffer(id) => self.gpu_id_hub.free_command_buffer_id(id),
2006 WebGPUMsg::FreeSampler(id) => self.gpu_id_hub.free_sampler_id(id),
2007 WebGPUMsg::FreeShaderModule(id) => self.gpu_id_hub.free_shader_module_id(id),
2008 WebGPUMsg::FreeRenderBundle(id) => self.gpu_id_hub.free_render_bundle_id(id),
2009 WebGPUMsg::FreeRenderPipeline(id) => self.gpu_id_hub.free_render_pipeline_id(id),
2010 WebGPUMsg::FreeTexture(id) => self.gpu_id_hub.free_texture_id(id),
2011 WebGPUMsg::FreeTextureView(id) => self.gpu_id_hub.free_texture_view_id(id),
2012 WebGPUMsg::FreeComputePass(id) => self.gpu_id_hub.free_compute_pass_id(id),
2013 WebGPUMsg::FreeRenderPass(id) => self.gpu_id_hub.free_render_pass_id(id),
2014 WebGPUMsg::Exit => {
2015 *self.receivers.webgpu_receiver.borrow_mut() = crossbeam_channel::never()
2016 },
2017 WebGPUMsg::DeviceLost {
2018 pipeline_id,
2019 device,
2020 reason,
2021 msg,
2022 } => {
2023 let global = self.documents.borrow().find_global(pipeline_id).unwrap();
2024 let _ac = enter_auto_realm(cx, &*global);
2025 global.gpu_device_lost(device, reason, msg);
2026 },
2027 WebGPUMsg::UncapturedError {
2028 device,
2029 pipeline_id,
2030 error,
2031 } => {
2032 let global = self.documents.borrow().find_global(pipeline_id).unwrap();
2033 let _ac = enter_auto_realm(cx, &*global);
2034 global.handle_uncaptured_gpu_error(device, error);
2035 },
2036 _ => {},
2037 }
2038 }
2039
2040 fn handle_msg_from_script(&self, msg: MainThreadScriptMsg, cx: &mut js::context::JSContext) {
2041 match msg {
2042 MainThreadScriptMsg::Common(CommonScriptMsg::Task(_, task, pipeline_id, _)) => {
2043 let global = pipeline_id.and_then(|id| self.documents.borrow().find_global(id));
2044 match global {
2045 None => task.run_box(cx),
2046 Some(global) => {
2047 let mut realm = enter_auto_realm(cx, &*global);
2048 let cx = &mut realm.current_realm();
2049 task.run_box(cx)
2050 },
2051 }
2052 },
2053 MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(chan)) => {
2054 self.collect_reports(cx, chan)
2055 },
2056 MainThreadScriptMsg::Common(CommonScriptMsg::ReportCspViolations(
2057 pipeline_id,
2058 violations,
2059 )) => {
2060 if let Some(global) = self.documents.borrow().find_global(pipeline_id) {
2061 let mut realm = enter_auto_realm(cx, &*global);
2062 let cx = &mut realm.current_realm();
2063 global.run_worker_csp_violation_report_tasks(violations, cx);
2064 }
2065 },
2066 MainThreadScriptMsg::NavigationResponse {
2067 pipeline_id,
2068 message,
2069 } => {
2070 self.handle_navigation_response(cx, pipeline_id, *message);
2071 },
2072 MainThreadScriptMsg::WorkletLoaded(pipeline_id) => {
2073 self.handle_worklet_loaded(pipeline_id)
2074 },
2075 MainThreadScriptMsg::RegisterPaintWorklet {
2076 pipeline_id,
2077 name,
2078 properties,
2079 painter,
2080 } => self.handle_register_paint_worklet(pipeline_id, name, properties, painter),
2081 MainThreadScriptMsg::Inactive => {},
2082 MainThreadScriptMsg::WakeUp => {},
2083 MainThreadScriptMsg::ForwardEmbedderControlResponseFromFileManager(
2084 control_id,
2085 response,
2086 ) => {
2087 self.handle_embedder_control_response(control_id, response, cx);
2088 },
2089 }
2090 }
2091
2092 fn handle_msg_from_devtools(
2093 &self,
2094 msg: DevtoolScriptControlMsg,
2095 cx: &mut js::context::JSContext,
2096 ) {
2097 let documents = self.documents.borrow();
2098 match msg {
2099 DevtoolScriptControlMsg::GetEventListenerInfo(id, node, reply) => {
2100 devtools::handle_get_event_listener_info(&self.devtools_state, id, &node, reply)
2101 },
2102 DevtoolScriptControlMsg::GetRootNode(id, reply) => {
2103 devtools::handle_get_root_node(cx, &self.devtools_state, &documents, id, reply)
2104 },
2105 DevtoolScriptControlMsg::GetDocumentElement(id, reply) => {
2106 devtools::handle_get_document_element(
2107 cx,
2108 &self.devtools_state,
2109 &documents,
2110 id,
2111 reply,
2112 )
2113 },
2114 DevtoolScriptControlMsg::GetStyleSheets(id, reply) => {
2115 devtools::handle_get_stylesheets(cx, &documents, id, reply);
2116 },
2117 DevtoolScriptControlMsg::GetStyleSheetText(id, index, reply) => {
2118 devtools::handle_get_stylesheet_text(cx, &documents, id, index, reply);
2119 },
2120 DevtoolScriptControlMsg::GetChildren(id, node_id, reply) => {
2121 devtools::handle_get_children(cx, &self.devtools_state, id, &node_id, reply)
2122 },
2123 DevtoolScriptControlMsg::GetAttributeStyle(id, node_id, reply) => {
2124 devtools::handle_get_attribute_style(cx, &self.devtools_state, id, &node_id, reply)
2125 },
2126 DevtoolScriptControlMsg::GetStylesheetStyle(id, node_id, matched_rule, reply) => {
2127 devtools::handle_get_stylesheet_style(
2128 cx,
2129 &self.devtools_state,
2130 &documents,
2131 id,
2132 &node_id,
2133 matched_rule,
2134 reply,
2135 )
2136 },
2137 DevtoolScriptControlMsg::GetSelectors(id, node_id, reply) => {
2138 devtools::handle_get_selectors(
2139 cx,
2140 &self.devtools_state,
2141 &documents,
2142 id,
2143 &node_id,
2144 reply,
2145 )
2146 },
2147 DevtoolScriptControlMsg::GetComputedStyle(id, node_id, reply) => {
2148 devtools::handle_get_computed_style(cx, &self.devtools_state, id, &node_id, reply)
2149 },
2150 DevtoolScriptControlMsg::GetLayout(id, node_id, reply) => {
2151 devtools::handle_get_layout(cx, &self.devtools_state, id, &node_id, reply)
2152 },
2153 DevtoolScriptControlMsg::GetXPath(id, node_id, reply) => {
2154 devtools::handle_get_xpath(&self.devtools_state, id, &node_id, reply)
2155 },
2156 DevtoolScriptControlMsg::GetInnerOrOuterHTML(id, node_id, reply, html_type) => {
2157 devtools::handle_get_inner_or_outer_html(
2158 cx,
2159 &self.devtools_state,
2160 id,
2161 &node_id,
2162 reply,
2163 html_type,
2164 )
2165 },
2166 DevtoolScriptControlMsg::ModifyAttribute(id, node_id, modifications) => {
2167 devtools::handle_modify_attribute(
2168 cx,
2169 &self.devtools_state,
2170 &documents,
2171 id,
2172 &node_id,
2173 modifications,
2174 )
2175 },
2176 DevtoolScriptControlMsg::ModifyRule(id, node_id, modifications) => {
2177 devtools::handle_modify_rule(
2178 cx,
2179 &self.devtools_state,
2180 &documents,
2181 id,
2182 &node_id,
2183 modifications,
2184 )
2185 },
2186 DevtoolScriptControlMsg::WantsLiveNotifications(id, to_send) => {
2187 match documents.find_window(id) {
2188 Some(window) => {
2189 window.set_devtools_wants_updates(to_send);
2190 },
2191 None => warn!("Message sent to closed pipeline {}.", id),
2192 }
2193 },
2194 DevtoolScriptControlMsg::SetTimelineMarkers(id, marker_types, reply) => {
2195 devtools::handle_set_timeline_markers(&documents, id, marker_types, reply)
2196 },
2197 DevtoolScriptControlMsg::DropTimelineMarkers(id, marker_types) => {
2198 devtools::handle_drop_timeline_markers(&documents, id, marker_types)
2199 },
2200 DevtoolScriptControlMsg::RequestAnimationFrame(id, name) => {
2201 devtools::handle_request_animation_frame(&documents, id, name)
2202 },
2203 DevtoolScriptControlMsg::NavigateTo(pipeline_id, url) => {
2204 self.handle_navigate_to(pipeline_id, url)
2205 },
2206 DevtoolScriptControlMsg::GoBack(pipeline_id) => {
2207 self.handle_traverse_history(pipeline_id, TraversalDirection::Back(1))
2208 },
2209 DevtoolScriptControlMsg::GoForward(pipeline_id) => {
2210 self.handle_traverse_history(pipeline_id, TraversalDirection::Forward(1))
2211 },
2212 DevtoolScriptControlMsg::Reload(id) => self.handle_reload(id, cx),
2213 DevtoolScriptControlMsg::GetCssDatabase(reply) => {
2214 devtools::handle_get_css_database(reply)
2215 },
2216 DevtoolScriptControlMsg::SimulateColorScheme(id, theme) => {
2217 let Some(document) = documents.find_document(id) else {
2218 return warn!("Message sent to closed pipeline {id}.");
2219 };
2220 document.set_theme_override(theme);
2224 },
2225 DevtoolScriptControlMsg::HighlightDomNode(id, node_id) => {
2226 devtools::handle_highlight_dom_node(
2227 &self.devtools_state,
2228 &documents,
2229 id,
2230 node_id.as_deref(),
2231 )
2232 },
2233 DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, eager, reply) => {
2234 self.debugger_global.fire_eval(
2235 cx,
2236 code.into(),
2237 id,
2238 None,
2239 frame_actor_id,
2240 eager,
2241 reply,
2242 );
2243 },
2244 DevtoolScriptControlMsg::GetPossibleBreakpoints(spidermonkey_id, result_sender) => {
2245 self.debugger_global.fire_get_possible_breakpoints(
2246 cx,
2247 spidermonkey_id,
2248 result_sender,
2249 );
2250 },
2251 DevtoolScriptControlMsg::SetBreakpoint(spidermonkey_id, script_id, offset) => {
2252 self.debugger_global
2253 .fire_set_breakpoint(cx, spidermonkey_id, script_id, offset);
2254 },
2255 DevtoolScriptControlMsg::ClearBreakpoint(spidermonkey_id, script_id, offset) => {
2256 self.debugger_global
2257 .fire_clear_breakpoint(cx, spidermonkey_id, script_id, offset);
2258 },
2259 DevtoolScriptControlMsg::Interrupt => {
2260 self.debugger_global.fire_interrupt(cx);
2261 },
2262 DevtoolScriptControlMsg::ListFrames(pipeline_id, start, count, result_sender) => {
2263 self.debugger_global
2264 .fire_list_frames(cx, pipeline_id, start, count, result_sender);
2265 },
2266 DevtoolScriptControlMsg::GetEnvironment(request, result_sender) => {
2267 self.debugger_global
2268 .fire_get_environment(cx, request, result_sender);
2269 },
2270 DevtoolScriptControlMsg::Resume(resume_limit_type, frame_actor_id) => {
2271 self.debugger_global
2272 .fire_resume(cx, resume_limit_type, frame_actor_id);
2273 self.debugger_paused.set(false);
2274 },
2275 DevtoolScriptControlMsg::Blackbox(spidermonkey_id, coverage) => {
2276 self.debugger_global
2277 .fire_blackbox(cx, spidermonkey_id, coverage);
2278 },
2279 DevtoolScriptControlMsg::Unblackbox(spidermonkey_id, coverage) => {
2280 self.debugger_global
2281 .fire_unblackbox(cx, spidermonkey_id, coverage);
2282 },
2283 }
2284 }
2285
2286 pub(crate) fn enter_debugger_pause_loop(&self) {
2289 self.debugger_paused.set(true);
2290
2291 #[allow(unsafe_code)]
2292 let mut cx = unsafe { js::context::JSContext::from_ptr(js::rust::Runtime::get().unwrap()) };
2293
2294 while self.debugger_paused.get() {
2295 match self.receivers.devtools_server_receiver.recv() {
2296 Ok(Ok(msg)) => self.handle_msg_from_devtools(msg, &mut cx),
2297 _ => {
2298 self.debugger_paused.set(false);
2299 break;
2300 },
2301 }
2302 }
2303 }
2304
2305 fn handle_msg_from_image_cache(
2306 &self,
2307 response: ImageCacheResponseMessage,
2308 cx: &mut js::context::JSContext,
2309 ) {
2310 match response {
2311 ImageCacheResponseMessage::NotifyPendingImageLoadStatus(pending_image_response) => {
2312 let window = self
2313 .documents
2314 .borrow()
2315 .find_window(pending_image_response.pipeline_id);
2316 if let Some(ref window) = window {
2317 window.pending_image_notification(pending_image_response, cx);
2318 }
2319 },
2320 ImageCacheResponseMessage::VectorImageRasterizationComplete(response) => {
2321 let window = self.documents.borrow().find_window(response.pipeline_id);
2322 if let Some(ref window) = window {
2323 window.handle_image_rasterization_complete_notification(cx.no_gc(), response);
2324 }
2325 },
2326 };
2327 }
2328
2329 fn handle_webdriver_msg(
2330 &self,
2331 pipeline_id: PipelineId,
2332 msg: WebDriverScriptCommand,
2333 cx: &mut js::context::JSContext,
2334 ) {
2335 let documents = self.documents.borrow();
2336 match msg {
2337 WebDriverScriptCommand::AddCookie(params, reply) => {
2338 webdriver_handlers::handle_add_cookie(&documents, pipeline_id, params, reply)
2339 },
2340 WebDriverScriptCommand::DeleteCookies(reply) => {
2341 webdriver_handlers::handle_delete_cookies(&documents, pipeline_id, reply)
2342 },
2343 WebDriverScriptCommand::DeleteCookie(name, reply) => {
2344 webdriver_handlers::handle_delete_cookie(&documents, pipeline_id, name, reply)
2345 },
2346 WebDriverScriptCommand::ElementClear(element_id, reply) => {
2347 webdriver_handlers::handle_element_clear(
2348 cx,
2349 &documents,
2350 pipeline_id,
2351 element_id,
2352 reply,
2353 )
2354 },
2355 WebDriverScriptCommand::FindElementsCSSSelector(selector, reply) => {
2356 webdriver_handlers::handle_find_elements_css_selector(
2357 cx,
2358 &documents,
2359 pipeline_id,
2360 selector,
2361 reply,
2362 )
2363 },
2364 WebDriverScriptCommand::FindElementsLinkText(selector, partial, reply) => {
2365 webdriver_handlers::handle_find_elements_link_text(
2366 cx,
2367 &documents,
2368 pipeline_id,
2369 selector,
2370 partial,
2371 reply,
2372 )
2373 },
2374 WebDriverScriptCommand::FindElementsTagName(selector, reply) => {
2375 webdriver_handlers::handle_find_elements_tag_name(
2376 cx,
2377 &documents,
2378 pipeline_id,
2379 selector,
2380 reply,
2381 )
2382 },
2383 WebDriverScriptCommand::FindElementsXpathSelector(selector, reply) => {
2384 webdriver_handlers::handle_find_elements_xpath_selector(
2385 cx,
2386 &documents,
2387 pipeline_id,
2388 selector,
2389 reply,
2390 )
2391 },
2392 WebDriverScriptCommand::FindElementElementsCSSSelector(selector, element_id, reply) => {
2393 webdriver_handlers::handle_find_element_elements_css_selector(
2394 cx,
2395 &documents,
2396 pipeline_id,
2397 element_id,
2398 selector,
2399 reply,
2400 )
2401 },
2402 WebDriverScriptCommand::FindElementElementsLinkText(
2403 selector,
2404 element_id,
2405 partial,
2406 reply,
2407 ) => webdriver_handlers::handle_find_element_elements_link_text(
2408 cx,
2409 &documents,
2410 pipeline_id,
2411 element_id,
2412 selector,
2413 partial,
2414 reply,
2415 ),
2416 WebDriverScriptCommand::FindElementElementsTagName(selector, element_id, reply) => {
2417 webdriver_handlers::handle_find_element_elements_tag_name(
2418 cx,
2419 &documents,
2420 pipeline_id,
2421 element_id,
2422 selector,
2423 reply,
2424 )
2425 },
2426 WebDriverScriptCommand::FindElementElementsXPathSelector(
2427 selector,
2428 element_id,
2429 reply,
2430 ) => webdriver_handlers::handle_find_element_elements_xpath_selector(
2431 cx,
2432 &documents,
2433 pipeline_id,
2434 element_id,
2435 selector,
2436 reply,
2437 ),
2438 WebDriverScriptCommand::FindShadowElementsCSSSelector(
2439 selector,
2440 shadow_root_id,
2441 reply,
2442 ) => webdriver_handlers::handle_find_shadow_elements_css_selector(
2443 cx,
2444 &documents,
2445 pipeline_id,
2446 shadow_root_id,
2447 selector,
2448 reply,
2449 ),
2450 WebDriverScriptCommand::FindShadowElementsLinkText(
2451 selector,
2452 shadow_root_id,
2453 partial,
2454 reply,
2455 ) => webdriver_handlers::handle_find_shadow_elements_link_text(
2456 cx,
2457 &documents,
2458 pipeline_id,
2459 shadow_root_id,
2460 selector,
2461 partial,
2462 reply,
2463 ),
2464 WebDriverScriptCommand::FindShadowElementsTagName(selector, shadow_root_id, reply) => {
2465 webdriver_handlers::handle_find_shadow_elements_tag_name(
2466 cx,
2467 &documents,
2468 pipeline_id,
2469 shadow_root_id,
2470 selector,
2471 reply,
2472 )
2473 },
2474 WebDriverScriptCommand::FindShadowElementsXPathSelector(
2475 selector,
2476 shadow_root_id,
2477 reply,
2478 ) => webdriver_handlers::handle_find_shadow_elements_xpath_selector(
2479 cx,
2480 &documents,
2481 pipeline_id,
2482 shadow_root_id,
2483 selector,
2484 reply,
2485 ),
2486 WebDriverScriptCommand::GetElementShadowRoot(element_id, reply) => {
2487 webdriver_handlers::handle_get_element_shadow_root(
2488 &documents,
2489 pipeline_id,
2490 element_id,
2491 reply,
2492 )
2493 },
2494 WebDriverScriptCommand::ElementClick(element_id, reply) => {
2495 webdriver_handlers::handle_element_click(
2496 cx,
2497 &documents,
2498 pipeline_id,
2499 element_id,
2500 reply,
2501 )
2502 },
2503 WebDriverScriptCommand::GetKnownElement(element_id, reply) => {
2504 webdriver_handlers::handle_get_known_element(
2505 &documents,
2506 pipeline_id,
2507 element_id,
2508 reply,
2509 )
2510 },
2511 WebDriverScriptCommand::GetKnownWindow(webview_id, reply) => {
2512 webdriver_handlers::handle_get_known_window(
2513 &documents,
2514 pipeline_id,
2515 webview_id,
2516 reply,
2517 )
2518 },
2519 WebDriverScriptCommand::GetKnownShadowRoot(element_id, reply) => {
2520 webdriver_handlers::handle_get_known_shadow_root(
2521 &documents,
2522 pipeline_id,
2523 element_id,
2524 reply,
2525 )
2526 },
2527 WebDriverScriptCommand::GetActiveElement(reply) => {
2528 webdriver_handlers::handle_get_active_element(&documents, pipeline_id, reply)
2529 },
2530 WebDriverScriptCommand::GetComputedRole(node_id, reply) => {
2531 webdriver_handlers::handle_get_computed_role(
2532 &documents,
2533 pipeline_id,
2534 node_id,
2535 reply,
2536 )
2537 },
2538 WebDriverScriptCommand::GetPageSource(reply) => {
2539 webdriver_handlers::handle_get_page_source(cx, &documents, pipeline_id, reply)
2540 },
2541 WebDriverScriptCommand::GetCookies(reply) => {
2542 webdriver_handlers::handle_get_cookies(&documents, pipeline_id, reply)
2543 },
2544 WebDriverScriptCommand::GetCookie(name, reply) => {
2545 webdriver_handlers::handle_get_cookie(&documents, pipeline_id, name, reply)
2546 },
2547 WebDriverScriptCommand::GetElementTagName(node_id, reply) => {
2548 webdriver_handlers::handle_get_name(&documents, pipeline_id, node_id, reply)
2549 },
2550 WebDriverScriptCommand::GetElementAttribute(node_id, name, reply) => {
2551 webdriver_handlers::handle_get_attribute(
2552 cx,
2553 &documents,
2554 pipeline_id,
2555 node_id,
2556 name,
2557 reply,
2558 )
2559 },
2560 WebDriverScriptCommand::GetElementProperty(node_id, name, reply) => {
2561 webdriver_handlers::handle_get_property(
2562 &documents,
2563 pipeline_id,
2564 node_id,
2565 name,
2566 reply,
2567 cx,
2568 )
2569 },
2570 WebDriverScriptCommand::GetElementCSS(node_id, name, reply) => {
2571 webdriver_handlers::handle_get_css(
2572 cx,
2573 &documents,
2574 pipeline_id,
2575 node_id,
2576 name,
2577 reply,
2578 )
2579 },
2580 WebDriverScriptCommand::GetElementRect(node_id, reply) => {
2581 webdriver_handlers::handle_get_rect(cx, &documents, pipeline_id, node_id, reply)
2582 },
2583 WebDriverScriptCommand::ScrollAndGetBoundingClientRect(node_id, reply) => {
2584 webdriver_handlers::handle_scroll_and_get_bounding_client_rect(
2585 cx,
2586 &documents,
2587 pipeline_id,
2588 node_id,
2589 reply,
2590 )
2591 },
2592 WebDriverScriptCommand::GetElementText(node_id, reply) => {
2593 webdriver_handlers::handle_get_text(&documents, pipeline_id, node_id, reply)
2594 },
2595 WebDriverScriptCommand::GetElementInViewCenterPoint(node_id, reply) => {
2596 webdriver_handlers::handle_get_element_in_view_center_point(
2597 cx,
2598 &documents,
2599 pipeline_id,
2600 node_id,
2601 reply,
2602 )
2603 },
2604 WebDriverScriptCommand::GetParentFrameId(reply) => {
2605 webdriver_handlers::handle_get_parent_frame_id(&documents, pipeline_id, reply)
2606 },
2607 WebDriverScriptCommand::GetBrowsingContextId(webdriver_frame_id, reply) => {
2608 webdriver_handlers::handle_get_browsing_context_id(
2609 &documents,
2610 pipeline_id,
2611 webdriver_frame_id,
2612 reply,
2613 )
2614 },
2615 WebDriverScriptCommand::GetUrl(reply) => {
2616 webdriver_handlers::handle_get_url(&documents, pipeline_id, reply)
2617 },
2618 WebDriverScriptCommand::IsEnabled(element_id, reply) => {
2619 webdriver_handlers::handle_is_enabled(&documents, pipeline_id, element_id, reply)
2620 },
2621 WebDriverScriptCommand::IsSelected(element_id, reply) => {
2622 webdriver_handlers::handle_is_selected(&documents, pipeline_id, element_id, reply)
2623 },
2624 WebDriverScriptCommand::GetTitle(reply) => {
2625 webdriver_handlers::handle_get_title(&documents, pipeline_id, reply)
2626 },
2627 WebDriverScriptCommand::WillSendKeys(
2628 element_id,
2629 text,
2630 strict_file_interactability,
2631 reply,
2632 ) => webdriver_handlers::handle_will_send_keys(
2633 cx,
2634 &documents,
2635 pipeline_id,
2636 element_id,
2637 text,
2638 strict_file_interactability,
2639 reply,
2640 ),
2641 WebDriverScriptCommand::AddLoadStatusSender(_, response_sender) => {
2642 webdriver_handlers::handle_add_load_status_sender(
2643 &documents,
2644 pipeline_id,
2645 response_sender,
2646 )
2647 },
2648 WebDriverScriptCommand::RemoveLoadStatusSender(_) => {
2649 webdriver_handlers::handle_remove_load_status_sender(&documents, pipeline_id)
2650 },
2651 WebDriverScriptCommand::ExecuteScriptWithCallback(script, reply) => {
2658 let window = documents.find_window(pipeline_id);
2659 drop(documents);
2660 webdriver_handlers::handle_execute_script(window, script, reply, cx);
2661 },
2662 WebDriverScriptCommand::SetProtocolHandlerAutomationMode(mode) => {
2663 webdriver_handlers::set_protocol_handler_automation_mode(
2664 &documents,
2665 pipeline_id,
2666 mode,
2667 )
2668 },
2669 WebDriverScriptCommand::SetPermission(name, state, reply) => {
2670 webdriver_handlers::set_permission(&documents, pipeline_id, name, state, reply)
2671 },
2672 }
2673 }
2674
2675 pub(crate) fn handle_resize_message(
2678 &self,
2679 id: PipelineId,
2680 viewport_details: ViewportDetails,
2681 size_type: WindowSizeType,
2682 ) {
2683 self.profile_event(ScriptThreadEventCategory::Resize, Some(id), || {
2684 let window = self.documents.borrow().find_window(id);
2685 if let Some(ref window) = window {
2686 window.add_resize_event(viewport_details, size_type);
2687 return;
2688 }
2689 let mut loads = self.incomplete_loads.borrow_mut();
2690 if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) {
2691 load.viewport_details = viewport_details;
2692 }
2693 })
2694 }
2695
2696 fn handle_update_webview_state(&self, cx: &mut JSContext, new_state: WebViewState) {
2698 let Some(webview_state) = self
2699 .webview_states
2700 .borrow()
2701 .get(&new_state.id)
2702 .and_then(Weak::upgrade)
2703 else {
2704 return;
2705 };
2706
2707 let new_theme = new_state.theme.get();
2708 let theme_changed = webview_state.theme.replace(new_theme) != new_theme;
2709 let new_has_system_focus = new_state.has_system_focus.get();
2710 let system_focus_changed =
2711 webview_state.has_system_focus.replace(new_has_system_focus) != new_has_system_focus;
2712
2713 if !theme_changed && !system_focus_changed {
2714 return;
2715 }
2716
2717 let documents: Vec<_> = self
2721 .documents
2722 .borrow()
2723 .iter()
2724 .map(|(_, document)| document)
2725 .collect();
2726
2727 for document in documents {
2728 if document.webview_id() != new_state.id {
2729 continue;
2730 }
2731 if theme_changed {
2732 document.window().refresh_theme();
2733 }
2734 if system_focus_changed {
2735 document.gained_or_lost_system_focus(cx, new_has_system_focus);
2736 }
2737 }
2738 }
2739
2740 fn handle_get_document_origin(
2741 &self,
2742 id: PipelineId,
2743 result_sender: GenericSender<Option<OriginSnapshot>>,
2744 ) {
2745 let _ = result_sender.send(
2746 self.documents
2747 .borrow()
2748 .find_document(id)
2749 .map(|document| document.origin().snapshot()),
2750 );
2751 }
2752
2753 fn handle_get_origin_details(
2754 &self,
2755 id: PipelineId,
2756 result_sender: GenericSender<Option<(OriginSnapshot, Vec<ImmutableOrigin>)>>,
2757 ) {
2758 let origin_details = self.documents.borrow().find_document(id).map(|document| {
2759 (
2760 document.origin().snapshot(),
2761 document
2762 .internal_ancestor_origin_objects_list()
2763 .clone()
2764 .unwrap_or_default(),
2765 )
2766 });
2767 let _ = result_sender.send(origin_details);
2768 }
2769
2770 fn handle_exit_fullscreen(&self, id: PipelineId, cx: &mut js::context::JSContext) {
2772 let document = self.documents.borrow().find_document(id);
2773 if let Some(document) = document {
2774 let mut realm = enter_auto_realm(cx, &*document);
2775 document.exit_fullscreen(&mut realm);
2776 }
2777 }
2778
2779 pub(crate) fn spawn_pipeline(
2780 &self,
2781 cx: &mut js::context::JSContext,
2782 new_pipeline_info: NewPipelineInfo,
2783 ) {
2784 self.profile_event(
2785 ScriptThreadEventCategory::SpawnPipeline,
2786 Some(new_pipeline_info.new_pipeline_id),
2787 || {
2788 self.devtools_state
2789 .notify_pipeline_created(new_pipeline_info.new_pipeline_id);
2790
2791 let webview_state = self.shared_webview_state(&new_pipeline_info.webview_state);
2793 self.pre_page_load(cx, InProgressLoad::new(new_pipeline_info, webview_state));
2794 },
2795 );
2796 }
2797
2798 fn collect_reports(&self, cx: &mut js::context::JSContext, reports_chan: ReportsChan) {
2799 let documents = self.documents.borrow();
2800 let urls = itertools::join(documents.iter().map(|(_, d)| d.url().to_string()), ", ");
2801
2802 let mut reports = vec![];
2803 let mut computed_objects = HashSet::new();
2804 perform_memory_report(|ops| {
2805 for (_, document) in documents.iter() {
2806 document
2807 .window()
2808 .layout()
2809 .collect_reports(&mut reports, ops);
2810
2811 computed_objects.extend(document.collect_reports(&mut reports, ops));
2812 }
2813
2814 let prefix = format!("url({urls})");
2815 reports.extend(get_reports(cx, prefix, ops, computed_objects));
2816 });
2817
2818 reports_chan.send(ProcessReports::new(reports));
2819 }
2820
2821 fn handle_set_throttled_msg(&self, pipeline_id: PipelineId, throttled: bool) {
2822 let window = self.documents.borrow().find_window(pipeline_id);
2823 match window {
2824 Some(window) => {
2825 window.set_throttled(throttled);
2826 return;
2827 },
2828 None => {
2829 let mut loads = self.incomplete_loads.borrow_mut();
2830 if let Some(ref mut load) = loads
2831 .iter_mut()
2832 .find(|load| load.pipeline_id == pipeline_id)
2833 {
2834 load.throttled = throttled;
2835 return;
2836 }
2837 },
2838 }
2839
2840 warn!("SetThrottled sent to nonexistent pipeline");
2841 }
2842
2843 fn handle_set_document_activity_msg(
2845 &self,
2846 cx: &mut js::context::JSContext,
2847 id: PipelineId,
2848 activity: DocumentActivity,
2849 ) {
2850 debug!(
2851 "Setting activity of {} to be {:?} in {:?}.",
2852 id,
2853 activity,
2854 thread::current().name()
2855 );
2856
2857 let _ = self.senders.self_sender.send(MainThreadScriptMsg::Inactive);
2862
2863 let document = self.documents.borrow().find_document(id);
2864 if let Some(document) = document {
2865 document.set_activity(cx, activity);
2866 return;
2867 }
2868 let mut loads = self.incomplete_loads.borrow_mut();
2869 if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) {
2870 load.activity = activity;
2871 return;
2872 }
2873 warn!("change of activity sent to nonexistent pipeline");
2874 }
2875
2876 fn handle_focus_document_as_part_of_focusing_steps(
2877 &self,
2878 cx: &mut js::context::JSContext,
2879 pipeline_id: PipelineId,
2880 sequence: FocusSequenceNumber,
2881 browsing_context_id: Option<BrowsingContextId>,
2882 ) {
2883 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
2884 warn!("Unknown {pipeline_id:?} for FocusDocumentAsPartOfFocusingSteps message.");
2885 return;
2886 };
2887
2888 let focus_handler = document.focus_handler();
2889 if focus_handler.focus_sequence() > sequence {
2890 debug!(
2891 "Disregarding the FocusDocumentAsPartOfFocusingSteps message because \
2892 the contained sequence number is too old ({sequence:?} < {:?})",
2893 focus_handler.focus_sequence()
2894 );
2895 return;
2896 }
2897
2898 let iframe_element = browsing_context_id
2901 .and_then(|browsing_context_id| document.iframes().element(browsing_context_id));
2902
2903 rooted!(&in(cx) let focusable_area = iframe_element
2904 .map(|iframe_element| FocusableArea::IFrameViewport {
2905 iframe_element: iframe_element.as_traced(),
2906 kind: iframe_element
2907 .upcast::<Element>()
2908 .focusable_area_kind(cx.no_gc()),
2909 })
2910 .unwrap_or(FocusableArea::Viewport)
2911 );
2912
2913 rooted!(&in(cx) let new_focus_chain = focusable_area.focus_chain());
2914 rooted!(&in(cx) let old_focus_chain = focus_handler.current_focus_chain());
2915
2916 focus_handler.focus_update_steps(
2917 cx,
2918 new_focus_chain,
2919 old_focus_chain,
2920 &focusable_area,
2921 false, );
2923
2924 document.refresh_focus_rendering();
2925 }
2926
2927 fn handle_focus_document(
2928 &self,
2929 cx: &mut js::context::JSContext,
2930 pipeline_id: PipelineId,
2931 remote_focus_operation: RemoteFocusOperation,
2932 ) {
2933 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
2934 warn!("Unknown {pipeline_id:?} for FocusDocument message.");
2935 return;
2936 };
2937 match remote_focus_operation {
2938 RemoteFocusOperation::Viewport => document.window().Focus(cx),
2939 RemoteFocusOperation::Sequential(direction, iframe_browsing_context_id) => document
2940 .focus_handler()
2941 .sequential_focus_from_another_document(cx, iframe_browsing_context_id, direction),
2942 }
2943 }
2944
2945 fn handle_unfocus_document_as_part_of_focusing_steps(
2946 &self,
2947 cx: &mut js::context::JSContext,
2948 pipeline_id: PipelineId,
2949 sequence: FocusSequenceNumber,
2950 ) {
2951 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
2952 warn!("Unknown {pipeline_id:?} for UnfocusDocumentAsPartOfFocusingSteps");
2953 return;
2954 };
2955
2956 let window = document.window();
2959 if window.is_top_level() {
2960 return;
2961 }
2962
2963 let focus_handler = document.focus_handler();
2964 if focus_handler.focus_sequence() > sequence {
2965 debug!(
2966 "Disregarding the Unfocus message because the contained sequence number is \
2967 too old ({:?} < {:?})",
2968 sequence,
2969 focus_handler.focus_sequence()
2970 );
2971 return;
2972 }
2973
2974 rooted!(&in(cx) let new_focus_chain = vec![]);
2975 rooted!(&in(cx) let old_focus_chain = focus_handler.current_focus_chain());
2976
2977 focus_handler.focus_update_steps(
2978 cx,
2979 new_focus_chain,
2980 old_focus_chain,
2981 &FocusableArea::Viewport,
2982 false, );
2984
2985 document.refresh_focus_rendering();
2986 }
2987
2988 #[expect(clippy::too_many_arguments)]
2989 fn handle_post_message_msg(
2991 &self,
2992 cx: &mut js::context::JSContext,
2993 pipeline_id: PipelineId,
2994 source_webview: WebViewId,
2995 source_with_ancestry: Vec<BrowsingContextId>,
2996 origin: Option<ImmutableOrigin>,
2997 source_origin: ImmutableOrigin,
2998 data: StructuredSerializedData,
2999 ) {
3000 let window = self.documents.borrow().find_window(pipeline_id);
3001 match window {
3002 None => warn!("postMessage after target pipeline {} closed.", pipeline_id),
3003 Some(window) => {
3004 let mut last = None;
3005 for browsing_context_id in source_with_ancestry.into_iter().rev() {
3006 if let Some(window_proxy) =
3007 self.window_proxies.find_window_proxy(browsing_context_id)
3008 {
3009 last = Some(window_proxy);
3010 continue;
3011 }
3012 let window_proxy = WindowProxy::new_dissimilar_origin(
3013 cx,
3014 window.upcast::<GlobalScope>(),
3015 browsing_context_id,
3016 source_webview,
3017 last.as_deref(),
3018 None,
3019 CreatorBrowsingContextInfo::from(last.as_deref(), None),
3020 );
3021 self.window_proxies
3022 .insert(browsing_context_id, &window_proxy);
3023 last = Some(window_proxy);
3024 }
3025
3026 let source = last.expect("Source with ancestry should contain at least one bc.");
3029
3030 window.post_message(origin, source_origin, &source, data)
3032 },
3033 }
3034 }
3035
3036 fn handle_stop_delaying_load_events_mode(&self, pipeline_id: PipelineId) {
3037 let window = self.documents.borrow().find_window(pipeline_id);
3038 if let Some(window) = window {
3039 match window.undiscarded_window_proxy() {
3040 Some(window_proxy) => window_proxy.stop_delaying_load_events_mode(),
3041 None => warn!(
3042 "Attempted to take {} of 'delaying-load-events-mode' after having been discarded.",
3043 pipeline_id
3044 ),
3045 };
3046 }
3047 }
3048
3049 fn handle_unload_document(&self, cx: &mut js::context::JSContext, pipeline_id: PipelineId) {
3050 let document = self.documents.borrow().find_document(pipeline_id);
3051 if let Some(document) = document {
3052 document.unload(cx, false);
3053 }
3054 }
3055
3056 fn handle_update_pipeline_id(
3057 &self,
3058 parent_pipeline_id: PipelineId,
3059 browsing_context_id: BrowsingContextId,
3060 webview_id: WebViewId,
3061 new_pipeline_id: PipelineId,
3062 reason: UpdatePipelineIdReason,
3063 cx: &mut js::context::JSContext,
3064 ) {
3065 let frame_element = self
3066 .documents
3067 .borrow()
3068 .find_iframe(parent_pipeline_id, browsing_context_id);
3069 let Some(frame_element) = frame_element else {
3070 return;
3071 };
3072 if !frame_element.update_pipeline_id(cx, new_pipeline_id, reason) {
3073 return;
3074 };
3075
3076 let Some(window) = self.documents.borrow().find_window(new_pipeline_id) else {
3077 return;
3078 };
3079 let _ = self.window_proxies.local_window_proxy(
3082 cx,
3083 &self.senders,
3084 &window,
3085 browsing_context_id,
3086 webview_id,
3087 Some(parent_pipeline_id),
3088 Some(frame_element),
3089 None,
3093 );
3094 }
3095
3096 fn handle_update_history_state_msg(
3097 &self,
3098 cx: &mut js::context::JSContext,
3099 pipeline_id: PipelineId,
3100 history_state_id: Option<HistoryStateId>,
3101 url: ServoUrl,
3102 ) {
3103 let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
3104 return warn!("update history state after pipeline {pipeline_id} closed.",);
3105 };
3106 window.History(cx).activate_state(cx, history_state_id, url);
3107 }
3108
3109 fn handle_remove_history_states(
3110 &self,
3111 cx: &mut js::context::JSContext,
3112 pipeline_id: PipelineId,
3113 history_states: Vec<HistoryStateId>,
3114 ) {
3115 let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
3116 return warn!("update history state after pipeline {pipeline_id} closed.",);
3117 };
3118 window.History(cx).remove_states(history_states);
3119 }
3120
3121 fn handle_resize_inactive_msg(&self, id: PipelineId, new_viewport_details: ViewportDetails) {
3123 let window = self.documents.borrow().find_window(id)
3124 .expect("ScriptThread: received a resize msg for a pipeline not in this script thread. This is a bug.");
3125 window.set_viewport_details(new_viewport_details);
3126 }
3127
3128 pub(crate) fn handle_page_headers_available(
3131 &self,
3132 webview_id: WebViewId,
3133 pipeline_id: PipelineId,
3134 metadata: Option<&Metadata>,
3135 origin: MutableOrigin,
3136 cx: &mut js::context::JSContext,
3137 ) -> Option<DomRoot<Document>> {
3138 if self.closed_pipelines.borrow().contains(&pipeline_id) {
3139 return None;
3141 }
3142
3143 let Some(idx) = self
3144 .incomplete_loads
3145 .borrow()
3146 .iter()
3147 .position(|load| load.pipeline_id == pipeline_id)
3148 else {
3149 unreachable!("Pipeline shouldn't have finished loading.");
3150 };
3151
3152 let is_204_205 = match metadata {
3157 Some(metadata) => metadata.status.in_range(204..=205),
3158 _ => false,
3159 };
3160
3161 if is_204_205 {
3162 if let Some(window) = self.documents.borrow().find_window(pipeline_id) {
3164 let window_proxy = window.window_proxy();
3165 if window_proxy.parent().is_some() {
3168 window_proxy.stop_delaying_load_events_mode();
3174 }
3175 }
3176 self.senders
3177 .pipeline_to_constellation_sender
3178 .send((
3179 webview_id,
3180 pipeline_id,
3181 ScriptToConstellationMessage::AbortLoadUrl,
3182 ))
3183 .unwrap();
3184 return None;
3185 };
3186
3187 let load = self.incomplete_loads.borrow_mut().remove(idx);
3188 metadata.map(|meta| self.load(meta, load, origin, cx))
3189 }
3190
3191 fn handle_get_title_msg(&self, pipeline_id: PipelineId) {
3193 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
3194 return warn!("Message sent to closed pipeline {pipeline_id}.");
3195 };
3196 document.send_title_to_embedder();
3197 }
3198
3199 fn handle_exit_pipeline_msg(
3201 &self,
3202 webview_id: WebViewId,
3203 pipeline_id: PipelineId,
3204 discard_browsing_context: DiscardBrowsingContext,
3205 cx: &mut js::context::JSContext,
3206 ) {
3207 debug!("{pipeline_id}: Starting pipeline exit.");
3208
3209 let document = self.documents.borrow_mut().remove(pipeline_id);
3212 if let Some(document) = document {
3213 debug_assert!(
3215 !self
3216 .incomplete_loads
3217 .borrow()
3218 .iter()
3219 .any(|load| load.pipeline_id == pipeline_id)
3220 );
3221
3222 if let Some(parser) = document.get_current_parser() {
3223 parser.abort(cx, AbortReason::PipelineExited);
3224 }
3225
3226 if !document.window_detached() {
3227 debug!("{pipeline_id}: Shutting down layout");
3228 document.window().layout_mut().exit_now();
3229 }
3230
3231 debug!("{pipeline_id}: Clearing animations");
3233 document.animation_manager().clear();
3234
3235 if !document.window_detached() {
3236 let window = document.window();
3239 if discard_browsing_context == DiscardBrowsingContext::Yes {
3240 let browsing_context_id = window.window_proxy().browsing_context_id();
3241 window.discard_browsing_context();
3242 self.window_proxies.remove(browsing_context_id);
3243 }
3244
3245 window.image_cache().clear();
3248
3249 debug!("{pipeline_id}: Clearing JavaScript runtime");
3250 window.clear_js_runtime();
3251 }
3252 }
3253
3254 self.closed_pipelines.borrow_mut().insert(pipeline_id);
3256 self.task_queue
3257 .remove_tasks_for_exiting_pipeline(&pipeline_id);
3258
3259 debug!("{pipeline_id}: Sending PipelineExited message to constellation");
3260 self.senders
3261 .pipeline_to_constellation_sender
3262 .send((
3263 webview_id,
3264 pipeline_id,
3265 ScriptToConstellationMessage::PipelineExited,
3266 ))
3267 .ok();
3268
3269 self.paint_api
3270 .pipeline_exited(webview_id, pipeline_id, PipelineExitSource::Script);
3271
3272 self.devtools_state.notify_pipeline_exited(pipeline_id);
3273 self.pipeline_to_node_ids.borrow_mut().remove(&pipeline_id);
3274
3275 debug!("{pipeline_id}: Finished pipeline exit");
3276 }
3277
3278 fn handle_exit_script_thread_msg(&self, cx: &mut js::context::JSContext) {
3280 debug!("Exiting script thread.");
3281
3282 let mut webview_and_pipeline_ids = Vec::new();
3283 webview_and_pipeline_ids.extend(
3284 self.incomplete_loads
3285 .borrow()
3286 .iter()
3287 .next()
3288 .map(|load| (load.webview_id(), load.pipeline_id)),
3289 );
3290 webview_and_pipeline_ids.extend(
3291 self.documents
3292 .borrow()
3293 .iter()
3294 .next()
3295 .map(|(pipeline_id, document)| (document.webview_id(), pipeline_id)),
3296 );
3297
3298 for (webview_id, pipeline_id) in webview_and_pipeline_ids {
3299 self.handle_exit_pipeline_msg(webview_id, pipeline_id, DiscardBrowsingContext::Yes, cx);
3300 }
3301
3302 self.background_hang_monitor.unregister();
3303
3304 if opts::get().multiprocess {
3307 debug!("Exiting IPC router thread in script thread.");
3308 ipc_channel::router::ROUTER.shutdown();
3309 }
3310
3311 debug!("Exited script thread.");
3312 }
3313
3314 pub(crate) fn handle_tick_all_animations_for_testing(no_gc: &NoGC, id: PipelineId) {
3316 with_script_thread(|script_thread| {
3317 let Some(document) = script_thread.documents.borrow().find_document(id) else {
3318 warn!("Animation tick for tests for closed pipeline {id}.");
3319 return;
3320 };
3321 document.maybe_mark_animating_nodes_as_dirty(no_gc);
3322 });
3323 }
3324
3325 fn handle_web_font_loaded(&self, no_gc: &NoGC, pipeline_id: PipelineId) {
3327 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
3328 warn!("Web font loaded in closed pipeline {}.", pipeline_id);
3329 return;
3330 };
3331
3332 document.dirty_all_nodes(no_gc);
3334
3335 document
3336 .window()
3337 .font_context()
3338 .decrement_count_of_loading_fonts_by_one();
3339 }
3340
3341 fn handle_worklet_loaded(&self, pipeline_id: PipelineId) {
3344 if let Some(document) = self.documents.borrow().find_document(pipeline_id) {
3345 document.add_restyle_reason(RestyleReason::PaintWorkletLoaded);
3346 }
3347 }
3348
3349 #[allow(clippy::too_many_arguments)]
3351 fn handle_storage_event(
3352 &self,
3353 pipeline_id: PipelineId,
3354 storage_type: WebStorageType,
3355 url: ServoUrl,
3356 key: Option<String>,
3357 old_value: Option<String>,
3358 new_value: Option<String>,
3359 cx: &mut js::context::JSContext,
3360 ) {
3361 let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
3362 return warn!("Storage event sent to closed pipeline {pipeline_id}.");
3363 };
3364
3365 let storage = match storage_type {
3366 WebStorageType::Local => window.GetLocalStorage(cx),
3367 WebStorageType::Session => window.GetSessionStorage(cx),
3368 };
3369 let Ok(storage) = storage else {
3370 return;
3371 };
3372
3373 storage.queue_storage_event(url, key, old_value, new_value);
3374 }
3375
3376 fn handle_iframe_load_event(
3378 &self,
3379 parent_id: PipelineId,
3380 browsing_context_id: BrowsingContextId,
3381 child_id: PipelineId,
3382 cx: &mut js::context::JSContext,
3383 ) {
3384 let iframe = self
3385 .documents
3386 .borrow()
3387 .find_iframe(parent_id, browsing_context_id);
3388 match iframe {
3389 Some(iframe) => iframe.iframe_load_event_steps(cx, child_id),
3390 None => warn!("Message sent to closed pipeline {}.", parent_id),
3391 }
3392 }
3393
3394 fn ask_constellation_for_top_level_info(
3395 &self,
3396 sender_webview_id: WebViewId,
3397 sender_pipeline_id: PipelineId,
3398 browsing_context_id: BrowsingContextId,
3399 ) -> Option<WebViewId> {
3400 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
3401 let msg = ScriptToConstellationMessage::GetTopForBrowsingContext(
3402 browsing_context_id,
3403 result_sender,
3404 );
3405 self.senders
3406 .pipeline_to_constellation_sender
3407 .send((sender_webview_id, sender_pipeline_id, msg))
3408 .expect("Failed to send to constellation.");
3409 result_receiver
3410 .recv()
3411 .expect("Failed to get top-level id from constellation.")
3412 }
3413
3414 fn load(
3417 &self,
3418 metadata: &Metadata,
3419 incomplete: InProgressLoad,
3420 origin: MutableOrigin,
3421 cx: &mut js::context::JSContext,
3422 ) -> DomRoot<Document> {
3423 let webview_id = incomplete.webview_id();
3424 let script_to_constellation_chan = ScriptToConstellationChan {
3425 sender: self.senders.pipeline_to_constellation_sender.clone(),
3426 webview_id,
3427 pipeline_id: incomplete.pipeline_id,
3428 };
3429
3430 let final_url = metadata.final_url.clone();
3431 let _ = script_to_constellation_chan
3432 .send(ScriptToConstellationMessage::SetFinalUrl(final_url.clone()));
3433
3434 debug!(
3435 "ScriptThread: loading {} on pipeline {:?}",
3436 incomplete.load_data.url, incomplete.pipeline_id
3437 );
3438
3439 let font_context = Arc::new(FontContext::new(
3440 self.system_font_service.clone(),
3441 self.paint_api.clone(),
3442 self.resource_threads.clone(),
3443 ));
3444
3445 let font_resolver = Arc::new(SvgFontResolver::new(font_context.clone()));
3446
3447 let image_cache = self.image_cache_factory.create(
3448 webview_id,
3449 incomplete.pipeline_id,
3450 &self.paint_api,
3451 font_resolver,
3452 );
3453
3454 let (user_contents, user_stylesheets) = incomplete
3455 .user_content_manager_id
3456 .and_then(|user_content_manager_id| {
3457 self.user_contents_for_manager_id
3458 .borrow()
3459 .get(&user_content_manager_id)
3460 .map(|script_thread_user_contents| {
3461 (
3462 script_thread_user_contents.user_scripts.clone(),
3463 script_thread_user_contents.user_stylesheets.clone(),
3464 )
3465 })
3466 })
3467 .unwrap_or_default();
3468
3469 let layout_config = LayoutConfig {
3470 id: incomplete.pipeline_id,
3471 webview_id,
3472 url: final_url.clone(),
3473 is_iframe: incomplete.parent_info.is_some(),
3474 script_chan: self.senders.constellation_sender.clone(),
3475 image_cache: image_cache.clone(),
3476 font_context,
3477 time_profiler_chan: self.senders.time_profiler_sender.clone(),
3478 paint_api: self.paint_api.clone(),
3479 viewport_details: incomplete.viewport_details,
3480 user_stylesheets,
3481 theme: incomplete.webview_state.theme.get(),
3482 embedder_chan: self.senders.pipeline_to_embedder_sender.clone(),
3483 };
3484
3485 let creation_url = final_url.clone();
3490 let window = match window_for_replacement(
3491 &self.window_proxies,
3492 incomplete.browsing_context_id,
3493 &origin,
3494 ) {
3495 Some(window) => {
3496 window.set_up_a_window_environment_settings_object(
3497 self.layout_factory.create(layout_config),
3498 creation_url,
3499 final_url.clone(),
3501 incomplete.navigation_start,
3502 incomplete.viewport_details,
3503 );
3504 window
3505 },
3506 None => {
3507 Window::new(
3508 cx,
3509 incomplete.webview_state,
3510 self.js_runtime.clone(),
3511 self.senders.self_sender.clone(),
3512 self.layout_factory.create(layout_config),
3513 self.senders.image_cache_sender.clone(),
3514 self.resource_threads.clone(),
3515 self.storage_threads.clone(),
3516 #[cfg(feature = "bluetooth")]
3517 self.senders.bluetooth_sender.clone(),
3518 self.senders.memory_profiler_sender.clone(),
3519 self.senders.time_profiler_sender.clone(),
3520 self.senders.devtools_server_sender.clone(),
3521 self.senders.pipeline_to_constellation_sender.clone(),
3522 self.senders.pipeline_to_embedder_sender.clone(),
3523 self.senders.constellation_sender.clone(),
3524 incomplete.pipeline_id,
3525 incomplete.parent_info,
3526 incomplete.viewport_details,
3527 origin.clone(),
3528 creation_url,
3529 final_url.clone(),
3534 incomplete.navigation_start,
3535 #[cfg(feature = "webgl")]
3536 self.webgl_chan.as_ref().map(|chan| chan.channel()),
3537 #[cfg(feature = "webxr")]
3538 self.webxr_registry.clone(),
3539 self.paint_api.clone(),
3540 self.unminify_js,
3541 self.unminify_css,
3542 self.local_script_source.clone(),
3543 user_contents,
3544 self.player_context.clone(),
3545 #[cfg(feature = "webgpu")]
3546 self.gpu_id_hub.clone(),
3547 incomplete.load_data.inherited_secure_context,
3548 self.this.clone(),
3549 )
3550 },
3551 };
3552 if self.senders.devtools_server_sender.is_some() {
3553 self.debugger_global.fire_add_debuggee(
3554 cx,
3555 window.upcast(),
3556 incomplete.pipeline_id,
3557 None,
3558 );
3559 }
3560
3561 let mut realm = enter_auto_realm(cx, &*window);
3562 let cx = &mut realm;
3563
3564 let last_modified = metadata.headers.as_ref().and_then(|headers| {
3572 headers.typed_get::<LastModified>().map(|tm| {
3573 let tm: SystemTime = tm.into();
3574 let local_time: DateTime<Local> = tm.into();
3575 local_time.format("%m/%d/%Y %H:%M:%S").to_string()
3576 })
3577 });
3578
3579 let loader = DocumentLoader::new_with_threads(
3580 self.resource_threads.clone(),
3581 Some(final_url.clone()),
3582 );
3583
3584 let content_type: Option<Mime> = metadata
3585 .content_type
3586 .clone()
3587 .map(Serde::into_inner)
3588 .map(Mime::from_ct);
3589 let encoding_hint_from_content_type = content_type
3590 .as_ref()
3591 .and_then(|mime| mime.get_parameter(CHARSET))
3592 .and_then(|charset| Encoding::for_label(charset.as_bytes()));
3593
3594 let is_html_document = match content_type {
3595 Some(ref mime) if mime.type_ == APPLICATION && mime.has_suffix("xml") => {
3596 IsHTMLDocument::NonHTMLDocument
3597 },
3598
3599 Some(ref mime) if mime.matches(TEXT, XML) || mime.matches(APPLICATION, XML) => {
3600 IsHTMLDocument::NonHTMLDocument
3601 },
3602 _ => IsHTMLDocument::HTMLDocument,
3603 };
3604
3605 let referrer = metadata
3612 .referrer
3613 .as_ref()
3614 .map(|referrer| referrer.clone().into_string());
3615
3616 let document = Document::new(
3625 cx,
3626 &window,
3627 HasBrowsingContext::Yes,
3628 Some(final_url.clone()),
3629 incomplete.load_data.about_base_url,
3630 origin,
3631 is_html_document,
3632 content_type,
3633 last_modified,
3634 incomplete.activity,
3635 loader,
3636 referrer,
3637 Some(metadata.status.raw_code()),
3638 incomplete.canceller,
3639 incomplete.load_data.is_initial_about_blank,
3640 true,
3641 incomplete.load_data.inherited_insecure_requests_policy,
3642 incomplete.load_data.has_trustworthy_ancestor_origin,
3643 self.custom_element_reaction_stack.clone(),
3644 incomplete.load_data.creation_sandboxing_flag_set,
3645 incomplete.pipeline_id,
3646 image_cache,
3647 );
3648
3649 document.set_navigation_start(incomplete.navigation_start);
3653
3654 let referrer_policy = metadata
3655 .headers
3656 .as_deref()
3657 .and_then(|h| h.typed_get::<ReferrerPolicyHeader>())
3658 .into();
3659 document.set_referrer_policy(referrer_policy);
3660
3661 self.documents
3662 .borrow_mut()
3663 .insert(incomplete.pipeline_id, &document);
3664
3665 window.init_document(&document);
3667
3668 let iframe = incomplete.parent_info.and_then(|parent_id| {
3669 self.documents
3670 .borrow()
3671 .find_iframe(parent_id, incomplete.browsing_context_id)
3672 });
3673 let iframe_in_this_script_thread = iframe.is_some();
3687 let parent_info = incomplete.parent_info.filter(|parent_id| {
3688 iframe_in_this_script_thread ||
3689 self.documents.borrow().find_document(*parent_id).is_none()
3690 });
3691
3692 let window_proxy = self.window_proxies.local_window_proxy(
3694 cx,
3695 &self.senders,
3696 &window,
3697 incomplete.browsing_context_id,
3698 webview_id,
3699 parent_info,
3700 iframe,
3701 incomplete.opener,
3702 );
3703 if let Some(name) = incomplete.frame_name {
3704 window_proxy.set_name(DOMString::from(name));
3705 }
3706 if window_proxy.parent().is_some() {
3707 window_proxy.stop_delaying_load_events_mode();
3712 }
3713 window.init_window_proxy(&window_proxy);
3714
3715 if let Some(parent_pipeline) = parent_info {
3716 if iframe_in_this_script_thread {
3717 self.handle_update_pipeline_id(
3720 parent_pipeline,
3721 window_proxy.browsing_context_id(),
3722 webview_id,
3723 incomplete.pipeline_id,
3724 UpdatePipelineIdReason::Navigation,
3725 cx,
3726 );
3727 } else {
3728 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
3733 let msg = ScriptToConstellationMessage::IsCurrentlyFullyActive(
3734 parent_pipeline,
3735 result_sender,
3736 );
3737 self.senders
3738 .pipeline_to_constellation_sender
3739 .send((webview_id, incomplete.pipeline_id, msg))
3740 .expect("Failed to send to constellation.");
3741 let is_parent_fully_active = result_receiver
3742 .recv()
3743 .expect("Failed to get top-level id from constellation.");
3744 if is_parent_fully_active {
3745 window_proxy
3746 .parent()
3747 .expect("Must either have a frame element or remote proxy")
3748 .set_pipeline_id(parent_pipeline);
3749 }
3750 }
3751 }
3752
3753 let refresh_header = metadata.headers.as_deref().and_then(|h| h.get(REFRESH));
3754 if let Some(refresh_val) = refresh_header {
3756 document.shared_declarative_refresh_steps(
3761 refresh_val.as_bytes(),
3762 false,
3763 );
3764 }
3765
3766 self.senders
3767 .pipeline_to_constellation_sender
3768 .send((
3769 webview_id,
3770 incomplete.pipeline_id,
3771 ScriptToConstellationMessage::ActivateDocument,
3772 ))
3773 .unwrap();
3774
3775 let incomplete_browsing_context_id: BrowsingContextId = webview_id.into();
3777 let is_top_level_global = incomplete_browsing_context_id == incomplete.browsing_context_id;
3778 self.notify_devtools(
3779 document.Title(),
3780 final_url.clone(),
3781 is_top_level_global,
3782 (
3783 incomplete.browsing_context_id,
3784 incomplete.pipeline_id,
3785 None,
3786 webview_id,
3787 ),
3788 );
3789
3790 if !incomplete.load_data.is_initial_about_blank {
3791 if is_html_document == IsHTMLDocument::NonHTMLDocument {
3792 ServoParser::parse_xml_document(
3793 cx,
3794 &document,
3795 None,
3796 final_url,
3797 encoding_hint_from_content_type,
3798 );
3799 } else {
3800 ServoParser::parse_html_document(
3801 cx,
3802 &document,
3803 None,
3804 final_url,
3805 encoding_hint_from_content_type,
3806 incomplete.load_data.container_document_encoding,
3807 );
3808 }
3809 }
3810
3811 if incomplete.activity == DocumentActivity::FullyActive {
3812 window.resume(cx);
3813 } else {
3814 window.suspend(cx);
3815 }
3816
3817 if incomplete.throttled {
3818 window.set_throttled(true);
3819 }
3820
3821 document
3822 }
3823
3824 fn notify_devtools(
3825 &self,
3826 title: DOMString,
3827 url: ServoUrl,
3828 is_top_level_global: bool,
3829 (browsing_context_id, pipeline_id, worker_id, webview_id): (
3830 BrowsingContextId,
3831 PipelineId,
3832 Option<WorkerId>,
3833 WebViewId,
3834 ),
3835 ) {
3836 if let Some(ref chan) = self.senders.devtools_server_sender {
3837 let page_info = DevtoolsPageInfo {
3838 title: String::from(title),
3839 url,
3840 is_top_level_global,
3841 is_service_worker: false,
3842 };
3843 chan.send(ScriptToDevtoolsControlMsg::NewGlobal(
3844 (browsing_context_id, pipeline_id, worker_id, webview_id),
3845 self.senders.devtools_client_to_script_thread_sender.clone(),
3846 page_info.clone(),
3847 ))
3848 .unwrap();
3849
3850 let state = NavigationState::Stop(pipeline_id, page_info);
3851 let _ = chan.send(ScriptToDevtoolsControlMsg::Navigate(
3852 browsing_context_id,
3853 state,
3854 ));
3855 }
3856 }
3857
3858 fn handle_input_event(
3860 &self,
3861 webview_id: WebViewId,
3862 pipeline_id: PipelineId,
3863 event: ConstellationInputEvent,
3864 ) {
3865 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
3866 warn!("Input event sent to closed pipeline {pipeline_id}.");
3867 let _ = self
3868 .senders
3869 .pipeline_to_embedder_sender
3870 .send(EmbedderMsg::InputEventsHandled(
3871 webview_id,
3872 vec![InputEventOutcome {
3873 id: event.event.id,
3874 result: Default::default(),
3875 }],
3876 ));
3877 return;
3878 };
3879 document.event_handler().note_pending_input_event(event);
3880 }
3881
3882 fn set_accessibility_active(&self, pipeline_id: PipelineId, active: bool, epoch: Epoch) {
3884 if !(pref!(accessibility_enabled)) {
3885 return;
3886 }
3887
3888 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
3889 if active {
3890 error!("Trying to set accessibility active on stale document: {pipeline_id}");
3891 }
3892 return;
3893 };
3894
3895 document
3896 .window()
3897 .layout()
3898 .set_accessibility_active(active, epoch);
3899 }
3900
3901 fn forward_accessibility_action(
3903 &self,
3904 pipeline_id: PipelineId,
3905 action_request: accesskit::ActionRequest,
3906 ) {
3907 if !(pref!(accessibility_enabled)) {
3908 return;
3909 }
3910
3911 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
3912 error!("Trying to forward accessibility action to stale document: {pipeline_id}");
3913 return;
3914 };
3915
3916 document
3917 .window()
3918 .layout()
3919 .handle_accessibility_action(action_request);
3920 }
3921
3922 fn handle_navigate_iframe(
3924 &self,
3925 parent_pipeline_id: PipelineId,
3926 browsing_context_id: BrowsingContextId,
3927 load_data: LoadData,
3928 history_handling: NavigationHistoryBehavior,
3929 target_snapshot_params: TargetSnapshotParams,
3930 cx: &mut js::context::JSContext,
3931 ) {
3932 let iframe = self
3933 .documents
3934 .borrow()
3935 .find_iframe(parent_pipeline_id, browsing_context_id);
3936 if let Some(iframe) = iframe {
3937 iframe.navigate_or_reload_child_browsing_context(
3938 cx,
3939 load_data,
3940 history_handling,
3941 ProcessingMode::NotFirstTime,
3942 target_snapshot_params,
3943 );
3944 }
3945 }
3946
3947 fn evaluate_a_javascript_url(
3952 cx: &mut js::context::JSContext,
3953 global_scope: &GlobalScope,
3954 load_data: &mut LoadData,
3955 ) -> bool {
3956 let encoded = &load_data.url[Position::AfterScheme..][1..];
3960
3961 let script_source = percent_decode(encoded.as_bytes()).decode_utf8_lossy();
3964
3965 let base_url = global_scope.api_base_url();
3969
3970 let mut realm = enter_auto_realm(cx, global_scope);
3973 let cx = &mut realm.current_realm();
3974 let classic_script = global_scope.create_a_classic_script(
3975 cx,
3976 script_source,
3977 base_url,
3978 ScriptOptions::ReturnsAValue,
3979 ScriptFetchOptions::default_classic_script(),
3980 Some(IntroductionType::JAVASCRIPT_URL),
3981 1, );
3983
3984 rooted!(&in(cx) let mut return_value = UndefinedValue());
3986 let evaluation_status = global_scope.run_a_classic_script(
3987 cx,
3988 classic_script,
3989 RethrowErrors::No,
3990 Some(return_value.handle_mut()),
3991 );
3992
3993 if evaluation_status.is_err() || !return_value.get().is_string() {
3998 return false;
3999 }
4000 let Ok(ConversionResult::Success(body)) =
4001 DOMString::from_jsval(cx, return_value.handle(), StringificationBehavior::Empty)
4002 else {
4003 return false;
4004 };
4005
4006 load_data.url = global_scope.get_url();
4011 load_data
4012 .headers
4013 .typed_insert(headers::ContentType::from(mime::TEXT_HTML_UTF_8));
4014 load_data.js_eval_result = Some(body.into());
4015
4016 let policy_container = global_scope.policy_container();
4019 load_data.policy_container = Some(policy_container);
4020
4021 load_data.about_base_url = global_scope.as_window().Document().about_base_url();
4054 true
4055 }
4056
4057 #[servo_tracing::instrument(skip_all)]
4060 fn pre_page_load(&self, cx: &mut js::context::JSContext, mut incomplete: InProgressLoad) {
4061 let origin_from_snapshot = || -> Option<MutableOrigin> {
4062 match incomplete.load_data.load_origin {
4063 LoadOrigin::Script(ref snapshot) => {
4064 Some(MutableOrigin::from_snapshot(snapshot.clone()))
4065 },
4066 _ => None,
4067 }
4068 };
4069
4070 let preserved_origin = || -> Option<MutableOrigin> {
4071 let creator_pipeline_id = incomplete.load_data.creator_pipeline_id?;
4081 Some(
4082 ScriptThread::find_document(creator_pipeline_id)?
4083 .origin()
4084 .clone(),
4085 )
4086 };
4087
4088 let url_str = incomplete.load_data.url.as_str();
4089 if url_str == "about:blank" || incomplete.load_data.js_eval_result.is_some() {
4090 let source_origin = preserved_origin().or(origin_from_snapshot());
4091 self.start_synchronous_page_load(cx, incomplete, source_origin);
4092 return;
4093 }
4094 if url_str == "about:srcdoc" {
4095 let source_origin = preserved_origin().or(origin_from_snapshot());
4096 self.page_load_about_srcdoc(cx, incomplete, source_origin);
4097 return;
4098 }
4099
4100 let source_origin = origin_from_snapshot();
4101 let context = ParserContext::new(
4102 incomplete.webview_id(),
4103 incomplete.pipeline_id,
4104 incomplete.load_data.url.clone(),
4105 incomplete.load_data.creation_sandboxing_flag_set,
4106 incomplete.parent_info,
4107 incomplete.target_snapshot_params,
4108 source_origin,
4109 );
4110 self.incomplete_parser_contexts
4111 .0
4112 .borrow_mut()
4113 .push((incomplete.pipeline_id, context));
4114
4115 let request_builder = incomplete.request_builder();
4116 incomplete.canceller = FetchCanceller::new(
4117 request_builder.id,
4118 false,
4119 self.resource_threads.core_thread.clone(),
4120 );
4121 NavigationListener::new(request_builder, self.senders.self_sender.clone())
4122 .initiate_fetch(&self.resource_threads.core_thread, None);
4123 self.incomplete_loads.borrow_mut().push(incomplete);
4124 }
4125
4126 fn handle_navigation_response(
4127 &self,
4128 cx: &mut js::context::JSContext,
4129 pipeline_id: PipelineId,
4130 message: FetchResponseMsg,
4131 ) {
4132 if let Some(metadata) = NavigationListener::http_redirect_metadata(&message) {
4133 self.handle_navigation_redirect(pipeline_id, metadata);
4134 return;
4135 };
4136
4137 match message {
4138 FetchResponseMsg::ProcessResponse(_request_id, metadata) => {
4139 self.handle_fetch_metadata(cx, pipeline_id, metadata)
4140 },
4141 FetchResponseMsg::ProcessResponseChunk(_request_id, chunk) => {
4142 self.handle_fetch_chunk(cx, pipeline_id, chunk)
4143 },
4144 FetchResponseMsg::ProcessResponseEOF(_request_id, eof, timing) => {
4145 self.handle_fetch_eof(cx, pipeline_id, eof, timing)
4146 },
4147 FetchResponseMsg::ProcessCspViolations(request_id, violations) => {
4148 self.handle_csp_violations(cx, pipeline_id, request_id, violations)
4149 },
4150 FetchResponseMsg::ProcessRequestBody(..) => {},
4151 FetchResponseMsg::ProcessContentLength(_request_id, _size) => {},
4152 }
4153 }
4154
4155 fn handle_fetch_metadata(
4156 &self,
4157 cx: &mut js::context::JSContext,
4158 id: PipelineId,
4159 fetch_metadata: Result<FetchMetadata, NetworkError>,
4160 ) {
4161 match fetch_metadata {
4162 Ok(_) => (),
4163 Err(NetworkError::Crash(..)) => (),
4164 Err(ref e) => {
4165 warn!("Network error: {:?}", e);
4166 },
4167 };
4168
4169 let mut incomplete_parser_contexts = self.incomplete_parser_contexts.0.borrow_mut();
4170 let parser = incomplete_parser_contexts
4171 .iter_mut()
4172 .find(|&&mut (pipeline_id, _)| pipeline_id == id);
4173 if let Some(&mut (_, ref mut ctxt)) = parser {
4174 ctxt.process_response(self, cx, fetch_metadata);
4175 }
4176 }
4177
4178 fn handle_fetch_chunk(
4179 &self,
4180 cx: &mut js::context::JSContext,
4181 pipeline_id: PipelineId,
4182 chunk: Bytes,
4183 ) {
4184 let mut incomplete_parser_contexts = self.incomplete_parser_contexts.0.borrow_mut();
4185 let parser = incomplete_parser_contexts
4186 .iter_mut()
4187 .find(|&&mut (parser_pipeline_id, _)| parser_pipeline_id == pipeline_id);
4188 if let Some(&mut (_, ref mut ctxt)) = parser {
4189 ctxt.process_response_chunk(cx, chunk);
4190 }
4191 }
4192
4193 #[expect(clippy::redundant_clone, reason = "False positive")]
4194 fn handle_fetch_eof(
4195 &self,
4196 cx: &mut js::context::JSContext,
4197 id: PipelineId,
4198 eof: Result<(), NetworkError>,
4199 timing: ResourceFetchTiming,
4200 ) {
4201 let idx = self
4202 .incomplete_parser_contexts
4203 .0
4204 .borrow()
4205 .iter()
4206 .position(|&(pipeline_id, _)| pipeline_id == id);
4207
4208 if let Some(idx) = idx {
4209 let (_, context) = self.incomplete_parser_contexts.0.borrow_mut().remove(idx);
4210
4211 if let Some(window_proxy) = context
4213 .get_document()
4214 .and_then(|document| document.browsing_context()) &&
4215 let Some(frame_element) = window_proxy.frame_element()
4216 {
4217 let iframe_ctx = IframeContext::new(
4218 frame_element
4219 .downcast::<HTMLIFrameElement>()
4220 .expect("WindowProxy::frame_element should be an HTMLIFrameElement"),
4221 );
4222
4223 let mut resource_timing = timing.clone();
4225 resource_timing.timing_type = ResourceTimingType::Resource;
4226 submit_timing(cx, &iframe_ctx, &eof, &resource_timing);
4227 }
4228
4229 context.process_response_eof(cx, eof, timing);
4230 }
4231 }
4232
4233 fn handle_csp_violations(
4234 &self,
4235 cx: &mut js::context::JSContext,
4236 pipeline_id: PipelineId,
4237 _request_id: RequestId,
4238 violations: Vec<Violation>,
4239 ) {
4240 let mut incomplete_parser_contexts = self.incomplete_parser_contexts.0.borrow_mut();
4241 let parser = incomplete_parser_contexts
4242 .iter_mut()
4243 .find(|&&mut (parser_pipeline_id, _)| parser_pipeline_id == pipeline_id);
4244 let Some(&mut (_, ref mut ctxt)) = parser else {
4245 return;
4246 };
4247 let pipeline_id = ctxt.parent_info().unwrap_or(pipeline_id);
4249 if let Some(global) = self.documents.borrow().find_global(pipeline_id) {
4250 global.report_csp_violations(cx, violations, None, None);
4251 }
4252 }
4253
4254 fn handle_navigation_redirect(&self, id: PipelineId, metadata: &Metadata) {
4255 assert!(metadata.location_url.is_some());
4259
4260 let mut incomplete_loads = self.incomplete_loads.borrow_mut();
4261 let Some(incomplete_load) = incomplete_loads
4262 .iter_mut()
4263 .find(|incomplete_load| incomplete_load.pipeline_id == id)
4264 else {
4265 return;
4266 };
4267
4268 incomplete_load.url_list.push(metadata.final_url.clone());
4271
4272 let mut request_builder = incomplete_load.request_builder();
4273 request_builder.referrer = metadata
4274 .referrer
4275 .clone()
4276 .map(Referrer::ReferrerUrl)
4277 .unwrap_or(Referrer::NoReferrer);
4278 request_builder.referrer_policy = metadata.referrer_policy;
4279 request_builder.origin = request_builder
4280 .client
4281 .as_ref()
4282 .expect("Must have a client during redirect")
4283 .origin
4284 .clone();
4285
4286 let headers = metadata
4287 .headers
4288 .as_ref()
4289 .map(|headers| headers.clone().into_inner())
4290 .unwrap_or_default();
4291
4292 let response_init = Some(ResponseInit {
4293 url: metadata.final_url.clone(),
4294 location_url: metadata.location_url.clone(),
4295 headers,
4296 referrer: metadata.referrer.clone(),
4297 status_code: metadata
4298 .status
4299 .try_code()
4300 .map(|code| code.as_u16())
4301 .unwrap_or(200),
4302 });
4303
4304 incomplete_load.canceller = FetchCanceller::new(
4305 request_builder.id,
4306 false,
4307 self.resource_threads.core_thread.clone(),
4308 );
4309 NavigationListener::new(request_builder, self.senders.self_sender.clone())
4310 .initiate_fetch(&self.resource_threads.core_thread, response_init);
4311 }
4312
4313 fn start_synchronous_page_load(
4316 &self,
4317 cx: &mut js::context::JSContext,
4318 mut incomplete: InProgressLoad,
4319 source_origin: Option<MutableOrigin>,
4320 ) {
4321 let mut context = ParserContext::new(
4322 incomplete.webview_id(),
4323 incomplete.pipeline_id,
4324 incomplete.load_data.url.clone(),
4325 incomplete.load_data.creation_sandboxing_flag_set,
4326 incomplete.parent_info,
4327 incomplete.target_snapshot_params,
4328 source_origin,
4329 );
4330
4331 let mut meta = Metadata::default(incomplete.load_data.url.clone());
4332 meta.set_content_type(Some(&mime::TEXT_HTML));
4333 meta.set_referrer_policy(incomplete.load_data.referrer_policy);
4334
4335 let chunk = match incomplete.load_data.js_eval_result {
4338 Some(ref mut content) => std::mem::take(content),
4339 None => String::new(),
4340 };
4341
4342 let policy_container = incomplete.load_data.policy_container.clone();
4343 let about_base_url = incomplete.load_data.about_base_url.clone();
4344 self.incomplete_loads.borrow_mut().push(incomplete);
4345
4346 context.process_response(self, cx, Ok(FetchMetadata::Unfiltered(meta)));
4347 context.set_policy_container(policy_container);
4348 context.set_about_base_url(about_base_url);
4349 context.process_response_chunk(cx, chunk.into());
4350 context.process_response_eof(
4351 cx,
4352 Ok(()),
4353 ResourceFetchTiming::new(ResourceTimingType::None),
4354 );
4355 }
4356
4357 fn page_load_about_srcdoc(
4359 &self,
4360 cx: &mut js::context::JSContext,
4361 mut incomplete: InProgressLoad,
4362 source_origin: Option<MutableOrigin>,
4363 ) {
4364 let url = ServoUrl::parse("about:srcdoc").unwrap();
4365 let mut meta = Metadata::default(url.clone());
4366 meta.set_content_type(Some(&mime::TEXT_HTML));
4367 meta.set_referrer_policy(incomplete.load_data.referrer_policy);
4368
4369 let srcdoc = std::mem::take(&mut incomplete.load_data.srcdoc);
4370 let chunk = srcdoc.into_bytes();
4371
4372 let policy_container = incomplete.load_data.policy_container.clone();
4373 let creation_sandboxing_flag_set = incomplete.load_data.creation_sandboxing_flag_set;
4374
4375 let webview_id = incomplete.webview_id();
4376 let pipeline_id = incomplete.pipeline_id;
4377 let parent_info = incomplete.parent_info;
4378 let about_base_url = incomplete.load_data.about_base_url.clone();
4379 let target_snapshot_params = incomplete.target_snapshot_params;
4380 self.incomplete_loads.borrow_mut().push(incomplete);
4381
4382 let mut context = ParserContext::new(
4383 webview_id,
4384 pipeline_id,
4385 url,
4386 creation_sandboxing_flag_set,
4387 parent_info,
4388 target_snapshot_params,
4389 source_origin,
4390 );
4391 context.process_response(self, cx, Ok(FetchMetadata::Unfiltered(meta)));
4392 context.set_policy_container(policy_container);
4393 context.set_about_base_url(about_base_url);
4394 context.process_response_chunk(cx, Bytes::copy_from_slice(&chunk));
4395 context.process_response_eof(
4396 cx,
4397 Ok(()),
4398 ResourceFetchTiming::new(ResourceTimingType::None),
4399 );
4400 }
4401
4402 fn handle_css_error_reporting(
4403 &self,
4404 pipeline_id: PipelineId,
4405 filename: String,
4406 line: u32,
4407 column: u32,
4408 msg: String,
4409 ) {
4410 let Some(ref sender) = self.senders.devtools_server_sender else {
4411 return;
4412 };
4413
4414 if let Some(window) = self.documents.borrow().find_window(pipeline_id) &&
4415 window.live_devtools_updates()
4416 {
4417 let css_error = CSSError {
4418 filename,
4419 line,
4420 column,
4421 msg,
4422 };
4423 let message = ScriptToDevtoolsControlMsg::ReportCSSError(pipeline_id, css_error);
4424 sender.send(message).unwrap();
4425 }
4426 }
4427
4428 fn handle_navigate_to(&self, pipeline_id: PipelineId, url: ServoUrl) {
4429 if let Some(document) = self.documents.borrow().find_document(pipeline_id) {
4432 self.senders
4433 .pipeline_to_constellation_sender
4434 .send((
4435 document.webview_id(),
4436 pipeline_id,
4437 ScriptToConstellationMessage::LoadUrl(
4438 LoadData::new_for_new_unrelated_webview(url),
4439 NavigationHistoryBehavior::Push,
4440 TargetSnapshotParams::default(),
4441 ),
4442 ))
4443 .unwrap();
4444 }
4445 }
4446
4447 fn handle_traverse_history(&self, pipeline_id: PipelineId, direction: TraversalDirection) {
4448 if let Some(document) = self.documents.borrow().find_document(pipeline_id) {
4451 let webview_id = document.webview_id();
4452 self.senders
4453 .pipeline_to_constellation_sender
4454 .send((
4455 webview_id,
4456 pipeline_id,
4457 ScriptToConstellationMessage::TraverseHistory(
4458 SessionHistoryTraversalRequest::new(
4459 webview_id,
4460 direction,
4461 HistoryTraversalSource::Script,
4462 ),
4463 ),
4464 ))
4465 .unwrap();
4466 }
4467 }
4468
4469 fn handle_reload(&self, pipeline_id: PipelineId, cx: &mut js::context::JSContext) {
4470 let window = self.documents.borrow().find_window(pipeline_id);
4471 if let Some(window) = window {
4472 window.Location(cx).reload_without_origin_check(cx, &window);
4473 }
4474 }
4475
4476 fn handle_paint_metric(
4477 &self,
4478 cx: &mut js::context::JSContext,
4479 pipeline_id: PipelineId,
4480 event: PaintMetricEvent,
4481 ) {
4482 match self.documents.borrow().find_document(pipeline_id) {
4483 Some(document) => document.handle_paint_metric(cx, event),
4484 None => {
4485 warn!("Received paint metric ({event:?}) for unknown document: {pipeline_id:?}")
4486 },
4487 }
4488 }
4489
4490 fn handle_media_session_action(
4491 &self,
4492 cx: &mut js::context::JSContext,
4493 pipeline_id: PipelineId,
4494 action: MediaSessionActionType,
4495 ) {
4496 if let Some(window) = self.documents.borrow().find_window(pipeline_id) {
4497 let media_session = window.Navigator(cx).MediaSession(cx);
4498 media_session.handle_action(cx, action);
4499 } else {
4500 warn!("No MediaSession for this pipeline ID");
4501 };
4502 }
4503
4504 pub(crate) fn enqueue_microtask(cx: &js::context::JSContext, job: Box<dyn MicrotaskRunnable>) {
4505 crate::runtime::job_queue::enqueue(cx, job);
4506 }
4507
4508 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
4509 if self.can_continue_running_inner() {
4511 let globals = self
4512 .documents
4513 .borrow()
4514 .iter()
4515 .map(|(_id, document)| DomRoot::from_ref(document.window().upcast()))
4516 .collect();
4517
4518 job_queue_microtask_checkpoint(cx, globals)
4519 }
4520 }
4521
4522 fn handle_evaluate_javascript(
4523 &self,
4524 webview_id: WebViewId,
4525 pipeline_id: PipelineId,
4526 evaluation_id: JavaScriptEvaluationId,
4527 script: String,
4528 cx: &mut js::context::JSContext,
4529 ) {
4530 let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
4531 let _ = self.senders.pipeline_to_constellation_sender.send((
4532 webview_id,
4533 pipeline_id,
4534 ScriptToConstellationMessage::FinishJavaScriptEvaluation(
4535 evaluation_id,
4536 Err(JavaScriptEvaluationError::WebViewNotReady),
4537 ),
4538 ));
4539 return;
4540 };
4541
4542 let global_scope = window.as_global_scope();
4543 let mut realm = enter_auto_realm(cx, global_scope);
4544 let cx = &mut realm.current_realm();
4545
4546 rooted!(&in(cx) let mut return_value = UndefinedValue());
4547 if let Err(err) = global_scope.evaluate_js_on_global(
4548 cx,
4549 script.into(),
4550 "",
4551 None, Some(return_value.handle_mut()),
4553 ) {
4554 _ = self.senders.pipeline_to_constellation_sender.send((
4555 webview_id,
4556 pipeline_id,
4557 ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, Err(err)),
4558 ));
4559 return;
4560 };
4561
4562 let result = jsval_to_webdriver(cx, global_scope, return_value.handle());
4563 let _ = self.senders.pipeline_to_constellation_sender.send((
4564 webview_id,
4565 pipeline_id,
4566 ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result),
4567 ));
4568 }
4569
4570 fn handle_refresh_cursor(&self, pipeline_id: PipelineId) {
4571 let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
4572 return;
4573 };
4574 document.event_handler().handle_refresh_cursor();
4575 }
4576
4577 pub(crate) fn is_servo_privileged(url: ServoUrl) -> bool {
4578 with_script_thread(|script_thread| script_thread.privileged_urls.contains(&url))
4579 }
4580
4581 fn handle_request_screenshot_readiness(
4582 &self,
4583 webview_id: WebViewId,
4584 pipeline_id: PipelineId,
4585 cx: &mut js::context::JSContext,
4586 ) {
4587 let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
4588 let _ = self.senders.pipeline_to_constellation_sender.send((
4589 webview_id,
4590 pipeline_id,
4591 ScriptToConstellationMessage::RespondToScreenshotReadinessRequest(
4592 ScreenshotReadinessResponse::NoLongerActive,
4593 ),
4594 ));
4595 return;
4596 };
4597 window.request_screenshot_readiness(cx);
4598 }
4599
4600 fn handle_embedder_control_response(
4601 &self,
4602 id: EmbedderControlId,
4603 response: EmbedderControlResponse,
4604 cx: &mut js::context::JSContext,
4605 ) {
4606 let Some(document) = self.documents.borrow().find_document(id.pipeline_id) else {
4607 return;
4608 };
4609 document
4610 .embedder_controls()
4611 .handle_embedder_control_response(cx, id, response);
4612 }
4613
4614 pub(crate) fn handle_update_pinch_zoom_infos(
4615 &self,
4616 cx: &mut JSContext,
4617 pipeline_id: PipelineId,
4618 pinch_zoom_infos: PinchZoomInfos,
4619 ) {
4620 let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
4621 warn!("Visual viewport update for closed pipeline {pipeline_id}.");
4622 return;
4623 };
4624
4625 window.maybe_update_visual_viewport(cx, pinch_zoom_infos);
4626 }
4627
4628 pub(crate) fn devtools_want_updates_for_node(pipeline: PipelineId, node: &Node) -> bool {
4629 with_script_thread(|script_thread| {
4630 script_thread
4631 .devtools_state
4632 .wants_updates_for_node(pipeline, node)
4633 })
4634 }
4635}
4636
4637impl Drop for ScriptThread {
4638 fn drop(&mut self) {
4639 SCRIPT_THREAD_ROOT.with(|root| {
4640 root.set(None);
4641 });
4642 }
4643}
4644
4645fn window_for_replacement(
4647 script_window_proxies: &ScriptWindowProxies,
4648 id: BrowsingContextId,
4649 origin: &MutableOrigin,
4650) -> Option<DomRoot<Window>> {
4651 let browsing_context = obtain_a_browsing_context(script_window_proxies, id, origin);
4654
4655 browsing_context
4660 .and_then(|window_proxy| window_proxy.document())
4661 .filter(|document| {
4662 document.is_initial_about_blank() && document.origin().same_origin_domain(origin)
4663 })
4664 .map(|document| DomRoot::from_ref(document.window()))
4665}
4666
4667fn obtain_a_browsing_context(
4669 script_window_proxies: &ScriptWindowProxies,
4670 id: BrowsingContextId,
4671 destination_origin: &MutableOrigin,
4672) -> Option<DomRoot<WindowProxy>> {
4673 let browsing_context = script_window_proxies.find_window_proxy(id)?;
4675 if browsing_context.parent().is_none() {
4677 return Some(browsing_context);
4678 }
4679 let swap_group = false;
4684 let document = browsing_context.document()?;
4686 let source_origin = document.origin();
4687 if !is_same_site(source_origin.immutable(), destination_origin.immutable()) {
4691 }
4700 if !swap_group {
4705 return Some(browsing_context);
4711 }
4712 Some(browsing_context)
4725}