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