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