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