1use std::borrow::Cow;
6use std::cell::{Cell, OnceCell, Ref, RefCell};
7use std::collections::hash_map::Entry;
8use std::collections::{HashMap, HashSet, VecDeque};
9use std::ffi::CStr;
10use std::mem;
11use std::ops::{Deref, Index};
12use std::ptr::NonNull;
13use std::rc::Rc;
14use std::sync::Arc;
15use std::sync::atomic::{AtomicBool, Ordering};
16use std::thread::JoinHandle;
17use std::time::{Duration, Instant};
18
19use content_security_policy::CspList;
20use crossbeam_channel::Sender;
21use devtools_traits::{PageError, ScriptToDevtoolsControlMsg, get_time_stamp};
22use dom_struct::dom_struct;
23use embedder_traits::{
24 ConsoleLogLevel, EmbedderMsg, JavaScriptEvaluationError, ScriptToEmbedderChan,
25};
26use fonts::FontContext;
27use indexmap::IndexSet;
28use ipc_channel::router::ROUTER;
29use js::jsapi::{
30 CurrentGlobalOrNull, GetNonCCWObjectGlobal, HandleObject, Heap, JSContext, JSObject,
31};
32use js::jsval::UndefinedValue;
33use js::panic::maybe_resume_unwind;
34use js::realm::CurrentRealm;
35use js::rust::wrappers2::Compile1;
36use js::rust::{
37 CustomAutoRooter, CustomAutoRooterGuard, HandleValue, MutableHandleValue, ParentRuntime,
38 Runtime, get_object_class, transform_str_to_source_text,
39};
40use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
41use net_traits::blob_url_store::BlobBuf;
42use net_traits::filemanager_thread::{
43 FileManagerResult, FileManagerThreadMsg, ReadFileProgress, RelativePos,
44};
45use net_traits::image_cache::ImageCache;
46use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
47use net_traits::request::{
48 InsecureRequestsPolicy, Origin as RequestOrigin, Referrer, RequestBuilder, RequestClient,
49};
50use net_traits::{
51 CoreResourceMsg, CoreResourceThread, ReferrerPolicy, ResourceThreads, fetch_async,
52};
53use profile_traits::{
54 generic_channel as profile_generic_channel, ipc as profile_ipc, mem as profile_mem,
55 time as profile_time,
56};
57use rustc_hash::{FxBuildHasher, FxHashMap};
58use script_bindings::cell::{DomRefCell, RefMut};
59use script_bindings::interfaces::GlobalScopeHelpers;
60use script_bindings::reflector::DomObject;
61use script_bindings::settings_stack::run_a_script;
62use servo_base::generic_channel;
63use servo_base::generic_channel::{GenericCallback, GenericSend};
64use servo_base::id::{
65 BlobId, BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineId,
66 ServiceWorkerId, ServiceWorkerRegistrationId, TEST_WEBVIEW_ID, WebViewId,
67};
68use servo_config::pref;
69use servo_constellation_traits::{
70 BlobData, BlobImpl, BroadcastChannelMsg, ConstellationInterest, FileBlob, MessagePortImpl,
71 MessagePortMsg, PortMessageTask, ScriptToConstellationChan, ScriptToConstellationMessage,
72 ScriptToConstellationSender,
73};
74use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
75use storage_traits::StorageThreads;
76use strum::VariantArray;
77use timers::{TimerEventRequest, TimerId};
78use uuid::Uuid;
79#[cfg(feature = "webgpu")]
80use webgpu_traits::{DeviceLostReason, WebGPUDevice};
81
82use crate::DomTypeHolder;
83use crate::dom::bindings::codegen::Bindings::BroadcastChannelBinding::BroadcastChannelMethods;
84use crate::dom::bindings::codegen::Bindings::EventSourceBinding::EventSource_Binding::EventSourceMethods;
85use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
86use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
87use crate::dom::bindings::codegen::Bindings::NotificationBinding::NotificationPermissionCallback;
88use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{
89 PermissionName, PermissionState,
90};
91#[cfg(feature = "webgpu")]
92use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUDeviceLostReason;
93use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
94use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
95use crate::dom::bindings::conversions::{root_from_object, root_from_object_static};
96#[cfg(feature = "js_backtrace")]
97use crate::dom::bindings::error::LAST_EXCEPTION_BACKTRACE;
98use crate::dom::bindings::error::{
99 Error, ErrorInfo, Fallible, report_pending_exception, take_and_report_pending_exception_for_api,
100};
101use crate::dom::bindings::frozenarray::CachedFrozenArray;
102use crate::dom::bindings::inheritance::Castable;
103use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
104use crate::dom::bindings::reflector::DomGlobal;
105use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
106use crate::dom::bindings::settings_stack::{entry_global, incumbent_global};
107use crate::dom::bindings::str::DOMString;
108use crate::dom::bindings::structuredclone;
109use crate::dom::bindings::trace::{CustomTraceable, HashMapTracedValues, RootedTraceableBox};
110use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
111use crate::dom::blob::Blob;
112use crate::dom::broadcastchannel::BroadcastChannel;
113use crate::dom::dedicatedworkerglobalscope::{
114 DedicatedWorkerControlMsg, DedicatedWorkerGlobalScope,
115};
116use crate::dom::dissimilaroriginwindow::DissimilarOriginWindow;
117use crate::dom::errorevent::ErrorEvent;
118use crate::dom::event::{Event, EventBubbles, EventCancelable};
119use crate::dom::eventsource::EventSource;
120use crate::dom::eventtarget::EventTarget;
121use crate::dom::file::File;
122use crate::dom::globalscope::script_execution::{
123 ErrorReporting, evaluate_script, fill_compile_options,
124};
125use crate::dom::idbfactory::IDBFactory;
126use crate::dom::messageport::MessagePort;
127use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
128use crate::dom::performance::performance::Performance;
129use crate::dom::performance::performanceentry::EntryType;
130use crate::dom::promise::Promise;
131use crate::dom::readablestream::{CrossRealmTransformReadable, ReadableStream};
132use crate::dom::serviceworker::ServiceWorker;
133use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
134use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
135use crate::dom::sharedworkerglobalscope::SharedWorkerGlobalScope;
136use crate::dom::stream::underlyingsourcecontainer::UnderlyingSourceType;
137use crate::dom::stream::writablestream::CrossRealmTransformWritable;
138use crate::dom::transformstream::CrossRealmTransform;
139use crate::dom::types::{AbortSignal, DebuggerGlobalScope, MessageEvent};
140#[cfg(feature = "webgpu")]
141use crate::dom::webgpu::gpudevice::GPUDevice;
142#[cfg(feature = "webgpu")]
143use crate::dom::webgpu::identityhub::IdentityHub;
144use crate::dom::window::Window;
145use crate::dom::workerglobalscope::WorkerGlobalScope;
146use crate::dom::workletglobalscope::WorkletGlobalScope;
147use crate::fetch::{DeferredFetchRecordId, FetchGroup, QueuedDeferredFetchRecord};
148use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
149use crate::microtask::Microtask;
150use crate::network_listener::{FetchResponseListener, NetworkListener};
151use crate::realms::enter_auto_realm;
152use crate::script_module::{
153 ImportMap, ModuleRequest, ModuleStatus, ResolvedModule, ScriptFetchOptions,
154};
155use crate::script_runtime::{CanGc, JSContext as SafeJSContext, ThreadSafeJSContext};
156use crate::script_thread::{ScriptThread, with_script_thread};
157use crate::task_manager::TaskManager;
158use crate::task_source::SendableTaskSource;
159use crate::timers::{
160 IsInterval, OneshotTimerCallback, OneshotTimerHandle, OneshotTimers, TimerCallback,
161 TimerEventId, TimerSource,
162};
163use crate::unminify::unminified_path;
164
165#[derive(JSTraceable, MallocSizeOf)]
166pub(crate) struct AutoCloseWorker {
167 #[conditional_malloc_size_of]
169 closing: Arc<AtomicBool>,
170 #[ignore_malloc_size_of = "JoinHandle"]
172 join_handle: Option<JoinHandle<()>>,
173 #[no_trace]
176 control_sender: Sender<DedicatedWorkerControlMsg>,
177 #[ignore_malloc_size_of = "mozjs"]
179 #[no_trace]
180 context: ThreadSafeJSContext,
181}
182
183impl Drop for AutoCloseWorker {
184 fn drop(&mut self) {
186 self.closing.store(true, Ordering::SeqCst);
188
189 if self
190 .control_sender
191 .send(DedicatedWorkerControlMsg::Exit)
192 .is_err()
193 {
194 warn!("Couldn't send an exit message to a dedicated worker.");
195 }
196
197 self.context.request_interrupt_callback();
198
199 if self
204 .join_handle
205 .take()
206 .expect("No handle to join on worker.")
207 .join()
208 .is_err()
209 {
210 warn!("Failed to join on dedicated worker thread.");
211 }
212 }
213}
214
215#[dom_struct]
216pub(crate) struct GlobalScope {
217 eventtarget: EventTarget,
218
219 message_port_state: DomRefCell<MessagePortState>,
221
222 broadcast_channel_state: DomRefCell<BroadcastChannelState>,
224
225 #[no_trace]
229 constellation_interest_counts: RefCell<HashMap<ConstellationInterest, usize>>,
230
231 blob_state: DomRefCell<HashMapTracedValues<BlobId, BlobInfo, FxBuildHasher>>,
233
234 registration_map: DomRefCell<
236 HashMapTracedValues<
237 ServiceWorkerRegistrationId,
238 Dom<ServiceWorkerRegistration>,
239 FxBuildHasher,
240 >,
241 >,
242
243 indexeddb: MutNullableDom<IDBFactory>,
245
246 worker_map: DomRefCell<HashMapTracedValues<ServiceWorkerId, Dom<ServiceWorker>, FxBuildHasher>>,
248
249 console_timers: DomRefCell<HashMap<DOMString, Instant>>,
251
252 #[ignore_malloc_size_of = "mozjs"]
255 module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
256
257 #[no_trace]
259 devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
260
261 #[no_trace]
263 mem_profiler_chan: profile_mem::ProfilerChan,
264
265 #[no_trace]
267 time_profiler_chan: profile_time::ProfilerChan,
268
269 #[no_trace]
271 script_to_constellation_sender: ScriptToConstellationSender,
272
273 #[no_trace]
275 script_to_embedder_chan: ScriptToEmbedderChan,
276
277 in_error_reporting_mode: Cell<bool>,
279
280 #[no_trace]
283 resource_threads: ResourceThreads,
284
285 #[no_trace]
288 storage_threads: StorageThreads,
289
290 #[no_trace]
292 creation_url: DomRefCell<ServoUrl>,
293
294 #[no_trace]
296 top_level_creation_url: Option<ServoUrl>,
297
298 permission_state_invocation_results: DomRefCell<HashMap<PermissionName, PermissionState>>,
300
301 list_auto_close_worker: DomRefCell<Vec<AutoCloseWorker>>,
303
304 event_source_tracker: DOMTracker<EventSource>,
306
307 abort_signal_dependents: DomRefCell<IndexSet<Dom<AbortSignal>>>,
310
311 #[ignore_malloc_size_of = "mozjs"]
320 #[allow(clippy::vec_box)]
323 uncaught_rejections: DomRefCell<Vec<Box<Heap<*mut JSObject>>>>,
324
325 #[ignore_malloc_size_of = "mozjs"]
331 #[allow(clippy::vec_box)]
334 consumed_rejections: DomRefCell<Vec<Box<Heap<*mut JSObject>>>>,
335
336 #[ignore_malloc_size_of = "defined in wgpu"]
338 #[no_trace]
339 #[cfg(feature = "webgpu")]
340 gpu_id_hub: Arc<IdentityHub>,
341
342 #[cfg(feature = "webgpu")]
344 gpu_devices: DomRefCell<HashMapTracedValues<WebGPUDevice, WeakRef<GPUDevice>, FxBuildHasher>>,
345
346 #[ignore_malloc_size_of = "mozjs"]
348 frozen_supported_performance_entry_types: CachedFrozenArray,
349
350 console_group_stack: DomRefCell<Vec<DOMString>>,
352
353 console_count_map: DomRefCell<HashMap<DOMString, usize>>,
357
358 inherited_secure_context: Option<bool>,
360
361 unminified_js_dir: Option<String>,
364
365 #[ignore_malloc_size_of = "callbacks are hard"]
370 byte_length_queuing_strategy_size_function: OnceCell<Rc<Function>>,
371
372 #[ignore_malloc_size_of = "callbacks are hard"]
377 count_queuing_strategy_size_function: OnceCell<Rc<Function>>,
378
379 #[ignore_malloc_size_of = "callbacks are hard"]
380 notification_permission_request_callback_map:
381 DomRefCell<HashMap<String, Rc<NotificationPermissionCallback>>>,
382
383 import_map: DomRefCell<ImportMap>,
388
389 resolved_module_set: DomRefCell<HashSet<ResolvedModule>>,
391
392 #[conditional_malloc_size_of]
396 #[no_trace]
397 font_context: Option<Arc<FontContext>>,
398
399 #[no_trace]
401 fetch_group: RefCell<FetchGroup>,
402}
403
404struct MessageListener {
406 task_source: SendableTaskSource,
407 context: Trusted<GlobalScope>,
408}
409
410struct BroadcastListener {
412 task_source: SendableTaskSource,
413 context: Trusted<GlobalScope>,
414}
415
416type FileListenerCallback =
417 Box<dyn Fn(&mut js::context::JSContext, Rc<Promise>, Fallible<Vec<u8>>) + Send>;
418
419struct FileListener {
421 state: Option<FileListenerState>,
425 task_source: SendableTaskSource,
426}
427
428enum FileListenerTarget {
429 Promise(TrustedPromise, FileListenerCallback),
430 Stream(Trusted<ReadableStream>),
431}
432
433enum FileListenerState {
434 Empty(FileListenerTarget),
435 Receiving(Vec<u8>, FileListenerTarget),
436}
437
438#[derive(JSTraceable, MallocSizeOf)]
439pub(crate) enum BlobTracker {
441 File(WeakRef<File>),
443 Blob(WeakRef<Blob>),
445}
446
447#[derive(JSTraceable, MallocSizeOf)]
448pub(crate) struct BlobInfo {
450 tracker: BlobTracker,
452 #[no_trace]
454 blob_impl: BlobImpl,
455 has_url: bool,
458}
459
460enum BlobResult {
464 Bytes(Vec<u8>),
465 File(Uuid, usize),
466}
467
468#[derive(JSTraceable, MallocSizeOf)]
470#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
471pub(crate) struct ManagedMessagePort {
472 dom_port: Dom<MessagePort>,
474 #[no_trace]
479 port_impl: Option<MessagePortImpl>,
480 pending: bool,
484 explicitly_closed: bool,
487 cross_realm_transform: Option<CrossRealmTransform>,
490}
491
492#[derive(JSTraceable, MallocSizeOf)]
494#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
495pub(crate) enum BroadcastChannelState {
496 Managed(
501 #[no_trace] BroadcastChannelRouterId,
502 HashMap<DOMString, VecDeque<Dom<BroadcastChannel>>>,
504 ),
505 UnManaged,
507}
508
509#[derive(JSTraceable, MallocSizeOf)]
511#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
512pub(crate) enum MessagePortState {
513 Managed(
515 #[no_trace] MessagePortRouterId,
516 HashMapTracedValues<MessagePortId, ManagedMessagePort, FxBuildHasher>,
517 ),
518 UnManaged,
520}
521
522impl BroadcastListener {
523 fn handle(&self, event: BroadcastChannelMsg) {
526 let context = self.context.clone();
527
528 self.task_source
534 .queue(task!(broadcast_message_event: move || {
535 let global = context.root();
536 global.broadcast_message_event(event, None);
539 }));
540 }
541}
542
543impl MessageListener {
544 fn notify(&self, msg: MessagePortMsg) {
548 match msg {
549 MessagePortMsg::CompleteTransfer(ports) => {
550 let context = self.context.clone();
551 self.task_source.queue(
552 task!(process_complete_transfer: move |cx| {
553 let global = context.root();
554
555 let router_id = match global.port_router_id() {
556 Some(router_id) => router_id,
557 None => {
558 let _ = global.script_to_constellation_chan().send(
561 ScriptToConstellationMessage::MessagePortTransferResult(None, vec![], ports),
562 );
563 return;
564 }
565 };
566
567 let mut succeeded = vec![];
568 let mut failed = FxHashMap::default();
569
570 for (id, info) in ports.into_iter() {
571 if global.is_managing_port(&id) {
572 succeeded.push(id);
573 global.complete_port_transfer(
574 cx,
575 id,
576 info.port_message_queue,
577 info.disentangled,
578 );
579 } else {
580 failed.insert(id, info);
581 }
582 }
583 let _ = global.script_to_constellation_chan().send(
584 ScriptToConstellationMessage::MessagePortTransferResult(Some(router_id), succeeded, failed),
585 );
586 })
587 );
588 },
589 MessagePortMsg::CompletePendingTransfer(port_id, info) => {
590 let context = self.context.clone();
591 self.task_source.queue(task!(complete_pending: move |cx| {
592 let global = context.root();
593 global.complete_port_transfer(cx, port_id, info.port_message_queue, info.disentangled);
594 }));
595 },
596 MessagePortMsg::CompleteDisentanglement(port_id) => {
597 let context = self.context.clone();
598 self.task_source
599 .queue(task!(try_complete_disentanglement: move |cx| {
600 let global = context.root();
601 global.try_complete_disentanglement(cx, port_id);
602 }));
603 },
604 MessagePortMsg::NewTask(port_id, task) => {
605 let context = self.context.clone();
606 self.task_source.queue(task!(process_new_task: move |cx| {
607 let global = context.root();
608 global.route_task_to_port(cx, port_id, task);
609 }));
610 },
611 }
612 }
613}
614
615fn stream_handle_incoming(
617 cx: &mut js::context::JSContext,
618 stream: &ReadableStream,
619 bytes: Fallible<Vec<u8>>,
620) {
621 match bytes {
622 Ok(b) => {
623 stream.enqueue_native(cx, b);
624 },
625 Err(e) => {
626 stream.error_native(cx, e);
627 },
628 }
629}
630
631fn stream_handle_eof(cx: &mut js::context::JSContext, stream: &ReadableStream) {
633 stream.controller_close_native(cx);
634}
635
636impl FileListener {
637 fn handle(&mut self, msg: FileManagerResult<ReadFileProgress>) {
638 match msg {
639 Ok(ReadFileProgress::Meta(blob_buf)) => match self.state.take() {
640 Some(FileListenerState::Empty(target)) => {
641 let bytes = if let FileListenerTarget::Stream(ref trusted_stream) = target {
642 let trusted = trusted_stream.clone();
643
644 let task = task!(enqueue_stream_chunk: move |cx| {
645 let stream = trusted.root();
646 stream_handle_incoming(cx, &stream, Ok(blob_buf.bytes));
647 });
648 self.task_source.queue(task);
649
650 Vec::with_capacity(0)
651 } else {
652 blob_buf.bytes
653 };
654
655 self.state = Some(FileListenerState::Receiving(bytes, target));
656 },
657 _ => panic!(
658 "Unexpected FileListenerState when receiving ReadFileProgress::Meta msg."
659 ),
660 },
661 Ok(ReadFileProgress::Partial(mut bytes_in)) => match self.state.take() {
662 Some(FileListenerState::Receiving(mut bytes, target)) => {
663 if let FileListenerTarget::Stream(ref trusted_stream) = target {
664 let trusted = trusted_stream.clone();
665
666 let task = task!(enqueue_stream_chunk: move |cx| {
667 let stream = trusted.root();
668 stream_handle_incoming(cx, &stream, Ok(bytes_in));
669 });
670
671 self.task_source.queue(task);
672 } else {
673 bytes.append(&mut bytes_in);
674 };
675
676 self.state = Some(FileListenerState::Receiving(bytes, target));
677 },
678 _ => panic!(
679 "Unexpected FileListenerState when receiving ReadFileProgress::Partial msg."
680 ),
681 },
682 Ok(ReadFileProgress::EOF) => match self.state.take() {
683 Some(FileListenerState::Receiving(bytes, target)) => match target {
684 FileListenerTarget::Promise(trusted_promise, callback) => {
685 let task = task!(resolve_promise: move |cx| {
686 let promise = trusted_promise.root();
687 let mut realm = enter_auto_realm(cx, &*promise.global());
688 callback(&mut realm, promise, Ok(bytes));
689 });
690
691 self.task_source.queue(task);
692 },
693 FileListenerTarget::Stream(trusted_stream) => {
694 let task = task!(enqueue_stream_chunk: move |cx| {
695 let stream = trusted_stream.root();
696 stream_handle_eof(cx, &stream);
697 });
698
699 self.task_source.queue(task);
700 },
701 },
702 _ => {
703 panic!("Unexpected FileListenerState when receiving ReadFileProgress::EOF msg.")
704 },
705 },
706 Err(_) => match self.state.take() {
707 Some(FileListenerState::Receiving(_, target)) |
708 Some(FileListenerState::Empty(target)) => {
709 let error = Err(Error::Network(None));
710
711 match target {
712 FileListenerTarget::Promise(trusted_promise, callback) => {
713 self.task_source.queue(task!(reject_promise: move |cx| {
714 let promise = trusted_promise.root();
715 let mut realm = enter_auto_realm(cx, &*promise.global());
716 callback(&mut realm, promise, error);
717 }));
718 },
719 FileListenerTarget::Stream(trusted_stream) => {
720 self.task_source.queue(task!(error_stream: move |cx| {
721 let stream = trusted_stream.root();
722 stream_handle_incoming(cx, &stream, error);
723 }));
724 },
725 }
726 },
727 _ => panic!("Unexpected FileListenerState when receiving Err msg."),
728 },
729 }
730 }
731}
732
733impl GlobalScope {
734 pub(crate) fn obtain_storage_key_for_non_storage_purposes(&self) -> ImmutableOrigin {
736 self.origin().immutable().clone()
739 }
740
741 pub(crate) fn obtain_storage_key(&self) -> Option<ImmutableOrigin> {
743 let key = self.obtain_storage_key_for_non_storage_purposes();
746
747 if let ImmutableOrigin::Opaque(_) = key {
749 return None;
750 }
751
752 if !pref!(dom_indexeddb_enabled) {
754 return None;
755 }
756
757 Some(key)
759 }
760
761 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
765 if let Some(window) = self.downcast::<Window>() {
766 return Some(window.webview_id());
767 }
768 if let Some(worker) = self.downcast::<DedicatedWorkerGlobalScope>() {
769 return Some(worker.webview_id());
770 }
771 if let Some(worker) = self.downcast::<SharedWorkerGlobalScope>() {
772 return Some(worker.webview_id());
773 }
774 None
776 }
777
778 #[allow(clippy::too_many_arguments)]
779 pub(crate) fn new_inherited(
780 devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
781 mem_profiler_chan: profile_mem::ProfilerChan,
782 time_profiler_chan: profile_time::ProfilerChan,
783 script_to_constellation_sender: ScriptToConstellationSender,
784 script_to_embedder_chan: ScriptToEmbedderChan,
785 resource_threads: ResourceThreads,
786 storage_threads: StorageThreads,
787 creation_url: ServoUrl,
788 top_level_creation_url: Option<ServoUrl>,
789 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
790 inherited_secure_context: Option<bool>,
791 unminify_js: bool,
792 font_context: Option<Arc<FontContext>>,
793 ) -> Self {
794 Self {
795 message_port_state: DomRefCell::new(MessagePortState::UnManaged),
796 broadcast_channel_state: DomRefCell::new(BroadcastChannelState::UnManaged),
797 constellation_interest_counts: RefCell::new(HashMap::new()),
798 blob_state: Default::default(),
799 eventtarget: EventTarget::new_inherited(),
800 registration_map: DomRefCell::new(HashMapTracedValues::new_fx()),
801 indexeddb: Default::default(),
802 worker_map: DomRefCell::new(HashMapTracedValues::new_fx()),
803 console_timers: DomRefCell::new(Default::default()),
804 module_map: DomRefCell::new(Default::default()),
805 devtools_chan,
806 mem_profiler_chan,
807 time_profiler_chan,
808 script_to_constellation_sender,
809 script_to_embedder_chan,
810 in_error_reporting_mode: Default::default(),
811 resource_threads,
812 storage_threads,
813 creation_url: DomRefCell::new(creation_url),
814 top_level_creation_url,
815 permission_state_invocation_results: Default::default(),
816 list_auto_close_worker: Default::default(),
817 event_source_tracker: DOMTracker::new(),
818 abort_signal_dependents: Default::default(),
819 uncaught_rejections: Default::default(),
820 consumed_rejections: Default::default(),
821 #[cfg(feature = "webgpu")]
822 gpu_id_hub,
823 #[cfg(feature = "webgpu")]
824 gpu_devices: DomRefCell::new(HashMapTracedValues::new_fx()),
825 frozen_supported_performance_entry_types: CachedFrozenArray::new(),
826 console_group_stack: DomRefCell::new(Vec::new()),
827 console_count_map: Default::default(),
828 inherited_secure_context,
829 unminified_js_dir: unminify_js.then(|| unminified_path("unminified-js")),
830 byte_length_queuing_strategy_size_function: OnceCell::new(),
831 count_queuing_strategy_size_function: OnceCell::new(),
832 notification_permission_request_callback_map: Default::default(),
833 import_map: Default::default(),
834 resolved_module_set: Default::default(),
835 font_context,
836 fetch_group: Default::default(),
837 }
838 }
839
840 fn port_router_id(&self) -> Option<MessagePortRouterId> {
842 if let MessagePortState::Managed(id, _message_ports) = &*self.message_port_state.borrow() {
843 Some(*id)
844 } else {
845 None
846 }
847 }
848
849 fn is_managing_port(&self, port_id: &MessagePortId) -> bool {
851 if let MessagePortState::Managed(_router_id, message_ports) =
852 &*self.message_port_state.borrow()
853 {
854 return message_ports.contains_key(port_id);
855 }
856 false
857 }
858
859 fn with_timers<T>(&self, f: impl FnOnce(&OneshotTimers) -> T) -> T {
860 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
861 f(worker.timers())
862 } else if let Some(window) = self.downcast::<Window>() {
863 window.with_timers(f)
864 } else {
865 unreachable!("Unsupported global type retrieving timers")
866 }
867 }
868
869 pub(crate) fn font_context(&self) -> Option<&Arc<FontContext>> {
870 self.font_context.as_ref()
871 }
872
873 #[allow(clippy::too_many_arguments)]
875 pub(crate) fn get_serviceworker_registration(
876 &self,
877 script_url: &ServoUrl,
878 scope: &ServoUrl,
879 registration_id: ServiceWorkerRegistrationId,
880 installing_worker: Option<ServiceWorkerId>,
881 _waiting_worker: Option<ServiceWorkerId>,
882 _active_worker: Option<ServiceWorkerId>,
883 can_gc: CanGc,
884 ) -> DomRoot<ServiceWorkerRegistration> {
885 let mut registrations = self.registration_map.borrow_mut();
887
888 if let Some(registration) = registrations.get(®istration_id) {
889 return DomRoot::from_ref(&**registration);
891 }
892
893 let new_registration =
895 ServiceWorkerRegistration::new(self, scope.clone(), registration_id, can_gc);
896
897 if let Some(worker_id) = installing_worker {
899 let worker = self.get_serviceworker(script_url, scope, worker_id, can_gc);
900 new_registration.set_installing(&worker);
901 }
902
903 registrations.insert(registration_id, Dom::from_ref(&*new_registration));
909
910 new_registration
912 }
913
914 pub(crate) fn get_serviceworker(
916 &self,
917 script_url: &ServoUrl,
918 scope: &ServoUrl,
919 worker_id: ServiceWorkerId,
920 can_gc: CanGc,
921 ) -> DomRoot<ServiceWorker> {
922 let mut workers = self.worker_map.borrow_mut();
924
925 if let Some(worker) = workers.get(&worker_id) {
926 DomRoot::from_ref(&**worker)
928 } else {
929 let new_worker =
932 ServiceWorker::new(self, script_url.clone(), scope.clone(), worker_id, can_gc);
933
934 workers.insert(worker_id, Dom::from_ref(&*new_worker));
936
937 new_worker
939 }
940 }
941
942 fn complete_port_transfer(
944 &self,
945 cx: &mut js::context::JSContext,
946 port_id: MessagePortId,
947 tasks: VecDeque<PortMessageTask>,
948 disentangled: bool,
949 ) {
950 let should_start = if let MessagePortState::Managed(_id, message_ports) =
951 &mut *self.message_port_state.borrow_mut()
952 {
953 match message_ports.get_mut(&port_id) {
954 None => {
955 panic!("complete_port_transfer called for an unknown port.");
956 },
957 Some(managed_port) => {
958 if managed_port.pending {
959 panic!("CompleteTransfer msg received for a pending port.");
960 }
961 if let Some(port_impl) = managed_port.port_impl.as_mut() {
962 port_impl.complete_transfer(tasks);
963 if disentangled {
964 port_impl.disentangle();
965 managed_port.dom_port.disentangle();
966 }
967 port_impl.enabled()
968 } else {
969 panic!("managed-port has no port-impl.");
970 }
971 },
972 }
973 } else {
974 panic!("complete_port_transfer called for an unknown port.");
975 };
976 if should_start {
977 self.start_message_port(cx, &port_id);
978 }
979 }
980
981 fn try_complete_disentanglement(
984 &self,
985 cx: &mut js::context::JSContext,
986 port_id: MessagePortId,
987 ) {
988 let dom_port = if let MessagePortState::Managed(_id, message_ports) =
989 &mut *self.message_port_state.borrow_mut()
990 {
991 if let Some(managed_port) = message_ports.get_mut(&port_id) {
992 if managed_port.pending {
993 unreachable!("CompleteDisentanglement msg received for a pending port.");
994 }
995 let port_impl = managed_port
996 .port_impl
997 .as_mut()
998 .expect("managed-port has no port-impl.");
999 port_impl.disentangle();
1000 managed_port.dom_port.as_rooted()
1001 } else {
1002 return;
1006 }
1007 } else {
1008 return;
1009 };
1010
1011 dom_port.upcast().fire_event(cx, atom!("close"));
1013
1014 let res = self.script_to_constellation_chan().send(
1015 ScriptToConstellationMessage::DisentanglePorts(port_id, None),
1016 );
1017 if res.is_err() {
1018 warn!("Sending DisentanglePorts failed");
1019 }
1020 }
1021
1022 pub(crate) fn perform_a_dom_garbage_collection_checkpoint(&self) {
1024 self.perform_a_message_port_garbage_collection_checkpoint();
1025 self.perform_a_blob_garbage_collection_checkpoint();
1026 self.perform_a_broadcast_channel_garbage_collection_checkpoint();
1027 self.perform_an_abort_signal_garbage_collection_checkpoint();
1028 }
1029
1030 pub(crate) fn remove_web_messaging_and_dedicated_workers_infra(&self) {
1033 self.remove_message_ports_router();
1034 self.remove_broadcast_channel_router();
1035
1036 self.list_auto_close_worker
1040 .borrow_mut()
1041 .drain(0..)
1042 .for_each(drop);
1043 }
1044
1045 fn remove_message_ports_router(&self) {
1048 if let MessagePortState::Managed(router_id, _message_ports) =
1049 &*self.message_port_state.borrow()
1050 {
1051 let _ = self.script_to_constellation_chan().send(
1052 ScriptToConstellationMessage::RemoveMessagePortRouter(*router_id),
1053 );
1054 }
1055 *self.message_port_state.borrow_mut() = MessagePortState::UnManaged;
1056 }
1057
1058 fn remove_broadcast_channel_router(&self) {
1061 if let BroadcastChannelState::Managed(router_id, _channels) =
1062 &*self.broadcast_channel_state.borrow()
1063 {
1064 let _ = self.script_to_constellation_chan().send(
1065 ScriptToConstellationMessage::RemoveBroadcastChannelRouter(
1066 *router_id,
1067 self.origin().immutable().clone(),
1068 ),
1069 );
1070 }
1071 *self.broadcast_channel_state.borrow_mut() = BroadcastChannelState::UnManaged;
1072 }
1073
1074 pub(crate) fn disentangle_port(&self, cx: &mut js::context::JSContext, port: &MessagePort) {
1076 let initiator_port = port.message_port_id();
1077 let Some(other_port) = port.disentangle() else {
1079 return;
1083 };
1084
1085 let dom_port = if let MessagePortState::Managed(_id, message_ports) =
1088 &mut *self.message_port_state.borrow_mut()
1089 {
1090 let mut dom_port = None;
1091 for port_id in &[initiator_port, &other_port] {
1092 match message_ports.get_mut(port_id) {
1093 None => {
1094 continue;
1095 },
1096 Some(managed_port) => {
1097 let port_impl = managed_port
1098 .port_impl
1099 .as_mut()
1100 .expect("managed-port has no port-impl.");
1101 managed_port.dom_port.disentangle();
1102 port_impl.disentangle();
1103
1104 if **port_id == other_port {
1105 dom_port = Some(managed_port.dom_port.as_rooted())
1106 }
1107 },
1108 }
1109 }
1110 dom_port
1111 } else {
1112 panic!("disentangle_port called on a global not managing any ports.");
1113 };
1114
1115 if let Some(dom_port) = dom_port {
1118 dom_port.upcast().fire_event(cx, atom!("close"));
1119 }
1120
1121 let chan = self.script_to_constellation_chan();
1122 let initiator_port = *initiator_port;
1123 self.task_manager()
1124 .port_message_queue()
1125 .queue(task!(post_message: move || {
1126 let res = chan.send(ScriptToConstellationMessage::DisentanglePorts(initiator_port, Some(other_port)));
1129 if res.is_err() {
1130 warn!("Sending DisentanglePorts failed");
1131 }
1132 }));
1133 }
1134
1135 pub(crate) fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) {
1137 if let MessagePortState::Managed(_id, message_ports) =
1138 &mut *self.message_port_state.borrow_mut()
1139 {
1140 for (port_id, entangled_id) in &[(port1, port2), (port2, port1)] {
1141 match message_ports.get_mut(port_id) {
1142 None => {
1143 return warn!("entangled_ports called on a global not managing the port.");
1144 },
1145 Some(managed_port) => {
1146 if let Some(port_impl) = managed_port.port_impl.as_mut() {
1147 managed_port.dom_port.entangle(*entangled_id);
1148 port_impl.entangle(*entangled_id);
1149 } else {
1150 panic!("managed-port has no port-impl.");
1151 }
1152 },
1153 }
1154 }
1155 } else {
1156 panic!("entangled_ports called on a global not managing any ports.");
1157 }
1158
1159 let _ = self
1160 .script_to_constellation_chan()
1161 .send(ScriptToConstellationMessage::EntanglePorts(port1, port2));
1162 }
1163
1164 pub(crate) fn mark_port_as_transferred(&self, port_id: &MessagePortId) -> MessagePortImpl {
1166 if let MessagePortState::Managed(_id, message_ports) =
1167 &mut *self.message_port_state.borrow_mut()
1168 {
1169 let mut port_impl = message_ports
1170 .remove(port_id)
1171 .map(|ref mut managed_port| {
1172 managed_port
1173 .port_impl
1174 .take()
1175 .expect("Managed port doesn't have a port-impl.")
1176 })
1177 .expect("mark_port_as_transferred called on a global not managing the port.");
1178 port_impl.set_has_been_shipped();
1179 let _ = self
1180 .script_to_constellation_chan()
1181 .send(ScriptToConstellationMessage::MessagePortShipped(*port_id));
1182 port_impl
1183 } else {
1184 panic!("mark_port_as_transferred called on a global not managing any ports.");
1185 }
1186 }
1187
1188 pub(crate) fn start_message_port(
1190 &self,
1191 cx: &mut js::context::JSContext,
1192 port_id: &MessagePortId,
1193 ) {
1194 let (message_buffer, dom_port) = if let MessagePortState::Managed(_id, message_ports) =
1195 &mut *self.message_port_state.borrow_mut()
1196 {
1197 let (message_buffer, dom_port) = match message_ports.get_mut(port_id) {
1198 None => panic!("start_message_port called on a unknown port."),
1199 Some(managed_port) => {
1200 if let Some(port_impl) = managed_port.port_impl.as_mut() {
1201 (port_impl.start(), managed_port.dom_port.as_rooted())
1202 } else {
1203 panic!("managed-port has no port-impl.");
1204 }
1205 },
1206 };
1207 (message_buffer, dom_port)
1208 } else {
1209 return warn!("start_message_port called on a global not managing any ports.");
1210 };
1211 if let Some(message_buffer) = message_buffer {
1212 for task in message_buffer {
1213 self.route_task_to_port(cx, *port_id, task);
1214 }
1215 if dom_port.disentangled() {
1216 dom_port.upcast().fire_event(cx, atom!("close"));
1219
1220 let res = self.script_to_constellation_chan().send(
1221 ScriptToConstellationMessage::DisentanglePorts(*port_id, None),
1222 );
1223 if res.is_err() {
1224 warn!("Sending DisentanglePorts failed");
1225 }
1226 }
1227 }
1228 }
1229
1230 pub(crate) fn close_message_port(&self, port_id: &MessagePortId) {
1232 if let MessagePortState::Managed(_id, message_ports) =
1233 &mut *self.message_port_state.borrow_mut()
1234 {
1235 match message_ports.get_mut(port_id) {
1236 None => panic!("close_message_port called on an unknown port."),
1237 Some(managed_port) => {
1238 if let Some(port_impl) = managed_port.port_impl.as_mut() {
1239 port_impl.close();
1240 managed_port.explicitly_closed = true;
1241 } else {
1242 panic!("managed-port has no port-impl.");
1243 }
1244 },
1245 };
1246 } else {
1247 warn!("close_message_port called on a global not managing any ports.")
1248 }
1249 }
1250
1251 pub(crate) fn post_messageport_msg(&self, port_id: MessagePortId, task: PortMessageTask) {
1254 if let MessagePortState::Managed(_id, message_ports) =
1255 &mut *self.message_port_state.borrow_mut()
1256 {
1257 let entangled_port = match message_ports.get_mut(&port_id) {
1258 None => panic!("post_messageport_msg called on an unknown port."),
1259 Some(managed_port) => {
1260 if let Some(port_impl) = managed_port.port_impl.as_mut() {
1261 port_impl.entangled_port_id()
1262 } else {
1263 panic!("managed-port has no port-impl.");
1264 }
1265 },
1266 };
1267 if let Some(entangled_id) = entangled_port {
1268 let this = Trusted::new(self);
1270 self.task_manager()
1271 .port_message_queue()
1272 .queue(task!(post_message: move |cx| {
1273 let global = this.root();
1274 global.route_task_to_port(cx, entangled_id, task);
1277 }));
1278 }
1279 } else {
1280 warn!("post_messageport_msg called on a global not managing any ports.");
1281 }
1282 }
1283
1284 fn re_route_port_task(&self, port_id: MessagePortId, task: PortMessageTask) {
1287 let _ = self.script_to_constellation_chan().send(
1288 ScriptToConstellationMessage::RerouteMessagePort(port_id, task),
1289 );
1290 }
1291
1292 pub(crate) fn schedule_broadcast(&self, msg: BroadcastChannelMsg, channel_id: &Uuid) {
1295 self.broadcast_message_event(msg.clone(), Some(channel_id));
1297
1298 if let BroadcastChannelState::Managed(router_id, _) =
1299 &*self.broadcast_channel_state.borrow()
1300 {
1301 let _ = self.script_to_constellation_chan().send(
1306 ScriptToConstellationMessage::ScheduleBroadcast(*router_id, msg),
1307 );
1308 } else {
1309 panic!("Attemps to broadcast a message via global not managing any channels.");
1310 }
1311 }
1312
1313 pub(crate) fn broadcast_message_event(
1316 &self,
1317 event: BroadcastChannelMsg,
1318 channel_id: Option<&Uuid>,
1319 ) {
1320 let BroadcastChannelState::Managed(_, channels) = &*self.broadcast_channel_state.borrow()
1321 else {
1322 return;
1323 };
1324
1325 let BroadcastChannelMsg {
1326 data,
1327 origin,
1328 channel_name,
1329 } = event;
1330
1331 if let Some(worker) = self.downcast::<WorkerGlobalScope>() &&
1335 worker.is_closing()
1336 {
1337 return;
1338 }
1339
1340 if let Some(window) = self.downcast::<Window>() &&
1342 !window.Document().is_fully_active()
1343 {
1344 return;
1345 }
1346
1347 let Some(channels) = channels.get(&channel_name.into()) else {
1349 return;
1350 };
1351 channels
1352 .iter()
1353 .filter(|channel| {
1354 if let Some(id) = channel_id {
1357 channel.id() != id
1358 } else {
1359 true
1360 }
1361 })
1362 .map(|channel| DomRoot::from_ref(&**channel))
1363 .for_each(|channel| {
1366 let data = data.clone_for_broadcast();
1367 let origin = origin.clone();
1368
1369 let channel = Trusted::new(&*channel);
1372 let global = Trusted::new(self);
1373 self.task_manager().dom_manipulation_task_source().queue(
1374 task!(process_pending_port_messages: move |cx| {
1375 let destination = channel.root();
1376 let global = global.root();
1377
1378 if destination.closed() {
1380 return;
1381 }
1382
1383 rooted!(&in(cx) let mut message = UndefinedValue());
1384
1385 if let Ok(ports) = structuredclone::read(cx, &global, data, message.handle_mut()) {
1387 MessageEvent::dispatch_jsval(
1389 cx,
1390 destination.upcast(),
1391 &global,
1392 message.handle(),
1393 Some(&origin.ascii_serialization()),
1394 None,
1395 ports,
1396 );
1397 } else {
1398 MessageEvent::dispatch_error(cx, destination.upcast(), &global);
1400 }
1401 })
1402 );
1403 });
1404 }
1405
1406 pub(crate) fn encoding_parse_a_url(&self, url: &str) -> Result<ServoUrl, url::ParseError> {
1408 if let Some(window) = self.downcast::<Window>() {
1409 return window.Document().encoding_parse_a_url(url);
1410 }
1411
1412 let base = self.api_base_url();
1414 base.join(url)
1415 }
1416
1417 pub(crate) fn note_cross_realm_transform_readable(
1421 &self,
1422 cross_realm_transform_readable: &CrossRealmTransformReadable,
1423 port_id: &MessagePortId,
1424 ) {
1425 let MessagePortState::Managed(_id, message_ports) =
1426 &mut *self.message_port_state.borrow_mut()
1427 else {
1428 unreachable!(
1429 "Cross realm transform readable must be called on a global managing ports"
1430 );
1431 };
1432
1433 let Some(managed_port) = message_ports.get_mut(port_id) else {
1434 unreachable!("Cross realm transform readable must match a managed port");
1435 };
1436
1437 managed_port.cross_realm_transform = Some(CrossRealmTransform::Readable(
1438 cross_realm_transform_readable.clone(),
1439 ));
1440 }
1441
1442 pub(crate) fn note_cross_realm_transform_writable(
1446 &self,
1447 cross_realm_transform_writable: &CrossRealmTransformWritable,
1448 port_id: &MessagePortId,
1449 ) {
1450 let MessagePortState::Managed(_id, message_ports) =
1451 &mut *self.message_port_state.borrow_mut()
1452 else {
1453 unreachable!(
1454 "Cross realm transform writable must be called on a global managing ports"
1455 );
1456 };
1457
1458 let Some(managed_port) = message_ports.get_mut(port_id) else {
1459 unreachable!("Cross realm transform writable must match a managed port");
1460 };
1461
1462 managed_port.cross_realm_transform = Some(CrossRealmTransform::Writable(
1463 cross_realm_transform_writable.clone(),
1464 ));
1465 }
1466
1467 fn route_task_to_port(
1470 &self,
1471 cx: &mut js::context::JSContext,
1472 port_id: MessagePortId,
1473 task: PortMessageTask,
1474 ) {
1475 rooted!(&in(cx) let mut cross_realm_transform = None);
1476
1477 let should_dispatch = if let MessagePortState::Managed(_id, message_ports) =
1478 &mut *self.message_port_state.borrow_mut()
1479 {
1480 if !message_ports.contains_key(&port_id) {
1481 self.re_route_port_task(port_id, task);
1482 return;
1483 }
1484 match message_ports.get_mut(&port_id) {
1485 None => panic!("route_task_to_port called for an unknown port."),
1486 Some(managed_port) => {
1487 if let Some(port_impl) = managed_port.port_impl.as_mut() {
1490 let to_dispatch = port_impl.handle_incoming(task).map(|to_dispatch| {
1491 (DomRoot::from_ref(&*managed_port.dom_port), to_dispatch)
1492 });
1493 cross_realm_transform.set(managed_port.cross_realm_transform.clone());
1494 to_dispatch
1495 } else {
1496 panic!("managed-port has no port-impl.");
1497 }
1498 },
1499 }
1500 } else {
1501 self.re_route_port_task(port_id, task);
1502 return;
1503 };
1504
1505 if let Some((dom_port, PortMessageTask { origin, data })) = should_dispatch {
1510 let message_event_target = dom_port.upcast();
1512
1513 rooted!(&in(cx) let mut message_clone = UndefinedValue());
1519
1520 let mut realm = enter_auto_realm(cx, self);
1521 let cx = &mut realm.current_realm();
1522
1523 run_a_script::<DomTypeHolder, _, _>(cx, self, |cx| {
1527 if let Ok(ports) = structuredclone::read(cx, self, data, message_clone.handle_mut())
1533 {
1534 if let Some(transform) = cross_realm_transform.deref().as_ref() {
1536 match transform {
1537 CrossRealmTransform::Readable(readable) => {
1540 readable.handle_message(
1541 cx,
1542 self,
1543 &dom_port,
1544 message_clone.handle(),
1545 );
1546 },
1547 CrossRealmTransform::Writable(writable) => {
1550 writable.handle_message(cx, self, message_clone.handle());
1551 },
1552 }
1553 } else {
1554 MessageEvent::dispatch_jsval(
1559 cx,
1560 message_event_target,
1561 self,
1562 message_clone.handle(),
1563 Some(&origin.ascii_serialization()),
1564 None,
1565 ports,
1566 );
1567 }
1568 } else if let Some(transform) = cross_realm_transform.deref().as_ref() {
1569 match transform {
1570 CrossRealmTransform::Readable(readable) => {
1573 readable.handle_error(cx, self, &dom_port);
1574 },
1575 CrossRealmTransform::Writable(writable) => {
1578 writable.handle_error(cx, self, &dom_port);
1579 },
1580 }
1581 } else {
1582 MessageEvent::dispatch_error(cx, message_event_target, self);
1586 }
1587 });
1588 }
1589 }
1590
1591 pub(crate) fn maybe_add_pending_ports(&self) {
1594 if let MessagePortState::Managed(router_id, message_ports) =
1595 &mut *self.message_port_state.borrow_mut()
1596 {
1597 let to_be_added: Vec<MessagePortId> = message_ports
1598 .iter()
1599 .filter_map(|(id, managed_port)| {
1600 if managed_port.pending {
1601 Some(*id)
1602 } else {
1603 None
1604 }
1605 })
1606 .collect();
1607 for id in to_be_added.iter() {
1608 let managed_port = message_ports
1609 .get_mut(id)
1610 .expect("Collected port-id to match an entry");
1611 if !managed_port.pending {
1612 panic!("Only pending ports should be found in to_be_added")
1613 }
1614 managed_port.pending = false;
1615 }
1616 let _ = self.script_to_constellation_chan().send(
1617 ScriptToConstellationMessage::CompleteMessagePortTransfer(*router_id, to_be_added),
1618 );
1619 } else {
1620 warn!("maybe_add_pending_ports called on a global not managing any ports.");
1621 }
1622 }
1623
1624 pub(crate) fn perform_a_message_port_garbage_collection_checkpoint(&self) {
1626 let is_empty = if let MessagePortState::Managed(_id, message_ports) =
1627 &mut *self.message_port_state.borrow_mut()
1628 {
1629 let to_be_removed: Vec<MessagePortId> = message_ports
1630 .iter()
1631 .filter_map(|(id, managed_port)| {
1632 if managed_port.explicitly_closed {
1633 Some(*id)
1634 } else {
1635 None
1636 }
1637 })
1638 .collect();
1639 for id in to_be_removed {
1640 message_ports.remove(&id);
1641 }
1642 message_ports.is_empty()
1646 } else {
1647 false
1648 };
1649 if is_empty {
1650 self.remove_message_ports_router();
1651 }
1652 }
1653
1654 pub(crate) fn perform_a_broadcast_channel_garbage_collection_checkpoint(&self) {
1658 let is_empty = if let BroadcastChannelState::Managed(router_id, channels) =
1659 &mut *self.broadcast_channel_state.borrow_mut()
1660 {
1661 channels.retain(|name, ref mut channels| {
1662 channels.retain(|chan| !chan.closed());
1663 if channels.is_empty() {
1664 let _ = self.script_to_constellation_chan().send(
1665 ScriptToConstellationMessage::RemoveBroadcastChannelNameInRouter(
1666 *router_id,
1667 name.to_string(),
1668 self.origin().immutable().clone(),
1669 ),
1670 );
1671 false
1672 } else {
1673 true
1674 }
1675 });
1676 channels.is_empty()
1677 } else {
1678 false
1679 };
1680 if is_empty {
1681 self.remove_broadcast_channel_router();
1682 }
1683 }
1684
1685 pub(crate) fn register_dependent_abort_signal(&self, signal: &AbortSignal) {
1688 self.abort_signal_dependents
1689 .borrow_mut()
1690 .insert(Dom::from_ref(signal));
1691 }
1692
1693 pub(crate) fn perform_an_abort_signal_garbage_collection_checkpoint(&self) {
1695 let mut set = self.abort_signal_dependents.borrow_mut();
1696
1697 set.retain(|dom_signal| dom_signal.must_keep_alive_for_gc());
1698 }
1699
1700 pub(crate) fn track_broadcast_channel(&self, dom_channel: &BroadcastChannel) {
1702 let mut current_state = self.broadcast_channel_state.borrow_mut();
1703
1704 if let BroadcastChannelState::UnManaged = &*current_state {
1705 let context = Trusted::new(self);
1706 let listener = BroadcastListener {
1707 task_source: self.task_manager().dom_manipulation_task_source().into(),
1708 context,
1709 };
1710 let broadcast_control_callback = GenericCallback::new(move |message| match message {
1711 Ok(msg) => listener.handle(msg),
1712 Err(err) => warn!("Error receiving a BroadcastChannelMsg: {:?}", err),
1713 })
1714 .expect("Could not generate callback");
1715 let router_id = BroadcastChannelRouterId::new();
1716 *current_state = BroadcastChannelState::Managed(router_id, HashMap::new());
1717 let _ = self.script_to_constellation_chan().send(
1718 ScriptToConstellationMessage::NewBroadcastChannelRouter(
1719 router_id,
1720 broadcast_control_callback,
1721 self.origin().immutable().clone(),
1722 ),
1723 );
1724 }
1725
1726 if let BroadcastChannelState::Managed(router_id, channels) = &mut *current_state {
1727 let entry = channels.entry(dom_channel.Name()).or_insert_with(|| {
1728 let _ = self.script_to_constellation_chan().send(
1729 ScriptToConstellationMessage::NewBroadcastChannelNameInRouter(
1730 *router_id,
1731 String::from(dom_channel.Name()),
1732 self.origin().immutable().clone(),
1733 ),
1734 );
1735 VecDeque::new()
1736 });
1737 entry.push_back(Dom::from_ref(dom_channel));
1738 } else {
1739 panic!("track_broadcast_channel should have first switched the state to managed.");
1740 }
1741 }
1742
1743 pub(crate) fn track_message_port(
1745 &self,
1746 dom_port: &MessagePort,
1747 port_impl: Option<MessagePortImpl>,
1748 ) {
1749 let mut current_state = self.message_port_state.borrow_mut();
1750
1751 if let MessagePortState::UnManaged = &*current_state {
1752 let context = Trusted::new(self);
1754 let listener = MessageListener {
1755 task_source: self.task_manager().port_message_queue().into(),
1756 context,
1757 };
1758
1759 let port_control_callback = GenericCallback::new(move |message| match message {
1760 Ok(msg) => listener.notify(msg),
1761 Err(err) => warn!("Error receiving a MessagePortMsg: {:?}", err),
1762 })
1763 .expect("Could not create callback");
1764 let router_id = MessagePortRouterId::new();
1765 *current_state = MessagePortState::Managed(router_id, HashMapTracedValues::new_fx());
1766 let _ = self.script_to_constellation_chan().send(
1767 ScriptToConstellationMessage::NewMessagePortRouter(
1768 router_id,
1769 port_control_callback,
1770 ),
1771 );
1772 }
1773
1774 if let MessagePortState::Managed(router_id, message_ports) = &mut *current_state {
1775 if let Some(port_impl) = port_impl {
1776 message_ports.insert(
1780 *dom_port.message_port_id(),
1781 ManagedMessagePort {
1782 port_impl: Some(port_impl),
1783 dom_port: Dom::from_ref(dom_port),
1784 pending: true,
1785 explicitly_closed: false,
1786 cross_realm_transform: None,
1787 },
1788 );
1789
1790 let this = Trusted::new(self);
1793 self.task_manager().port_message_queue().queue(
1794 task!(process_pending_port_messages: move || {
1795 let target_global = this.root();
1796 target_global.maybe_add_pending_ports();
1797 }),
1798 );
1799 } else {
1800 let port_impl = MessagePortImpl::new(*dom_port.message_port_id());
1802 message_ports.insert(
1803 *dom_port.message_port_id(),
1804 ManagedMessagePort {
1805 port_impl: Some(port_impl),
1806 dom_port: Dom::from_ref(dom_port),
1807 pending: false,
1808 explicitly_closed: false,
1809 cross_realm_transform: None,
1810 },
1811 );
1812 let _ = self.script_to_constellation_chan().send(
1813 ScriptToConstellationMessage::NewMessagePort(
1814 *router_id,
1815 *dom_port.message_port_id(),
1816 ),
1817 );
1818 };
1819 } else {
1820 panic!("track_message_port should have first switched the state to managed.");
1821 }
1822 }
1823
1824 pub(crate) fn serialize_blob(&self, blob_id: &BlobId) -> BlobImpl {
1828 let bytes = self
1832 .get_blob_bytes(blob_id)
1833 .expect("Could not read bytes from blob as part of serialization steps.");
1834 let type_string = self.get_blob_type_string(blob_id);
1835
1836 BlobImpl::new_from_bytes(bytes, type_string)
1838 }
1839
1840 fn track_blob_info(&self, blob_info: BlobInfo, blob_id: BlobId) {
1841 self.blob_state.borrow_mut().insert(blob_id, blob_info);
1842 }
1843
1844 pub(crate) fn track_blob(&self, dom_blob: &Blob, blob_impl: BlobImpl) {
1846 let blob_id = blob_impl.blob_id();
1847
1848 let blob_info = BlobInfo {
1849 blob_impl,
1850 tracker: BlobTracker::Blob(WeakRef::new(dom_blob)),
1851 has_url: false,
1852 };
1853
1854 self.track_blob_info(blob_info, blob_id);
1855 }
1856
1857 pub(crate) fn track_file(&self, file: &File, blob_impl: BlobImpl) {
1859 let blob_id = blob_impl.blob_id();
1860
1861 let blob_info = BlobInfo {
1862 blob_impl,
1863 tracker: BlobTracker::File(WeakRef::new(file)),
1864 has_url: false,
1865 };
1866
1867 self.track_blob_info(blob_info, blob_id);
1868 }
1869
1870 fn perform_a_blob_garbage_collection_checkpoint(&self) {
1874 let mut blob_state = self.blob_state.borrow_mut();
1875 blob_state.0.retain(|_id, blob_info| {
1876 let garbage_collected = match &blob_info.tracker {
1877 BlobTracker::File(weak) => weak.root().is_none(),
1878 BlobTracker::Blob(weak) => weak.root().is_none(),
1879 };
1880 if garbage_collected && !blob_info.has_url {
1881 if let BlobData::File(f) = blob_info.blob_impl.blob_data() {
1882 self.decrement_file_ref(f.get_id());
1883 }
1884 false
1885 } else {
1886 true
1887 }
1888 });
1889 }
1890
1891 pub(crate) fn clean_up_all_file_resources(&self) {
1894 self.blob_state
1895 .borrow_mut()
1896 .drain()
1897 .for_each(|(_id, blob_info)| {
1898 if let BlobData::File(f) = blob_info.blob_impl.blob_data() {
1899 self.decrement_file_ref(f.get_id());
1900 }
1901 });
1902 }
1903
1904 fn decrement_file_ref(&self, id: Uuid) {
1905 let origin = self.origin().immutable().clone();
1906
1907 let (tx, rx) = profile_generic_channel::channel(self.time_profiler_chan().clone()).unwrap();
1908
1909 let msg = FileManagerThreadMsg::DecRef(id, origin, tx);
1910 self.send_to_file_manager(msg);
1911 let _ = rx.recv();
1912 }
1913
1914 pub(crate) fn get_blob_bytes(&self, blob_id: &BlobId) -> Result<Vec<u8>, ()> {
1917 let parent = {
1918 match *self.get_blob_data(blob_id) {
1919 BlobData::Sliced(parent, rel_pos) => Some((parent, rel_pos)),
1920 _ => None,
1921 }
1922 };
1923
1924 match parent {
1925 Some((parent_id, rel_pos)) => self.get_blob_bytes_non_sliced(&parent_id).map(|v| {
1926 let range = rel_pos.to_abs_range(v.len());
1927 v.index(range).to_vec()
1928 }),
1929 None => self.get_blob_bytes_non_sliced(blob_id),
1930 }
1931 }
1932
1933 pub(crate) fn get_blob_data<'a>(&'a self, blob_id: &BlobId) -> Ref<'a, BlobData> {
1938 Ref::map(self.blob_state.borrow(), |blob_state| {
1939 blob_state
1940 .get(blob_id)
1941 .expect("get_blob_impl called for a unknown blob")
1942 .blob_impl
1943 .blob_data()
1944 })
1945 }
1946
1947 fn get_blob_bytes_non_sliced(&self, blob_id: &BlobId) -> Result<Vec<u8>, ()> {
1949 match *self.get_blob_data(blob_id) {
1950 BlobData::File(ref f) => {
1951 let (buffer, is_new_buffer) = match f.get_cache() {
1952 Some(bytes) => (bytes, false),
1953 None => {
1954 let bytes = self.read_file(f.get_id())?;
1955 (bytes, true)
1956 },
1957 };
1958
1959 if is_new_buffer {
1961 f.cache_bytes(buffer.clone());
1962 }
1963
1964 Ok(buffer)
1965 },
1966 BlobData::Memory(ref s) => Ok(s.clone()),
1967 BlobData::Sliced(_, _) => panic!("This blob doesn't have a parent."),
1968 }
1969 }
1970
1971 fn get_blob_bytes_or_file_id(&self, blob_id: &BlobId) -> BlobResult {
1978 let parent = {
1979 match *self.get_blob_data(blob_id) {
1980 BlobData::Sliced(parent, rel_pos) => Some((parent, rel_pos)),
1981 _ => None,
1982 }
1983 };
1984
1985 match parent {
1986 Some((parent_id, rel_pos)) => {
1987 match self.get_blob_bytes_non_sliced_or_file_id(&parent_id) {
1988 BlobResult::Bytes(bytes) => {
1989 let range = rel_pos.to_abs_range(bytes.len());
1990 BlobResult::Bytes(bytes.index(range).to_vec())
1991 },
1992 res => res,
1993 }
1994 },
1995 None => self.get_blob_bytes_non_sliced_or_file_id(blob_id),
1996 }
1997 }
1998
1999 fn get_blob_bytes_non_sliced_or_file_id(&self, blob_id: &BlobId) -> BlobResult {
2005 match *self.get_blob_data(blob_id) {
2006 BlobData::File(ref f) => match f.get_cache() {
2007 Some(bytes) => BlobResult::Bytes(bytes),
2008 None => BlobResult::File(f.get_id(), f.get_size() as usize),
2009 },
2010 BlobData::Memory(ref s) => BlobResult::Bytes(s.clone()),
2011 BlobData::Sliced(_, _) => panic!("This blob doesn't have a parent."),
2012 }
2013 }
2014
2015 pub(crate) fn get_blob_type_string(&self, blob_id: &BlobId) -> String {
2017 let blob_state = self.blob_state.borrow();
2018 let blob_info = blob_state
2019 .get(blob_id)
2020 .expect("get_blob_type_string called for a unknown blob.");
2021 blob_info.blob_impl.type_string()
2022 }
2023
2024 pub(crate) fn get_blob_size(&self, blob_id: &BlobId) -> u64 {
2026 let parent = {
2027 match *self.get_blob_data(blob_id) {
2028 BlobData::Sliced(parent, rel_pos) => Some((parent, rel_pos)),
2029 _ => None,
2030 }
2031 };
2032 match parent {
2033 Some((parent_id, rel_pos)) => {
2034 let parent_size = match *self.get_blob_data(&parent_id) {
2035 BlobData::File(ref f) => f.get_size(),
2036 BlobData::Memory(ref v) => v.len() as u64,
2037 BlobData::Sliced(_, _) => panic!("Blob ancestry should be only one level."),
2038 };
2039 rel_pos.to_abs_range(parent_size as usize).len() as u64
2040 },
2041 None => match *self.get_blob_data(blob_id) {
2042 BlobData::File(ref f) => f.get_size(),
2043 BlobData::Memory(ref v) => v.len() as u64,
2044 BlobData::Sliced(_, _) => {
2045 panic!("It was previously checked that this blob does not have a parent.")
2046 },
2047 },
2048 }
2049 }
2050
2051 pub(crate) fn get_blob_url_id(&self, blob_id: &BlobId) -> Uuid {
2052 let mut blob_state = self.blob_state.borrow_mut();
2053 let parent = {
2054 let blob_info = blob_state
2055 .get_mut(blob_id)
2056 .expect("get_blob_url_id called for a unknown blob.");
2057
2058 blob_info.has_url = true;
2060
2061 match blob_info.blob_impl.blob_data() {
2062 BlobData::Sliced(parent, rel_pos) => Some((*parent, *rel_pos)),
2063 _ => None,
2064 }
2065 };
2066 match parent {
2067 Some((parent_id, rel_pos)) => {
2068 let parent_info = blob_state
2069 .get_mut(&parent_id)
2070 .expect("Parent of blob whose url is requested is unknown.");
2071 let parent_file_id = self.promote(parent_info, false);
2072 let parent_size = match parent_info.blob_impl.blob_data() {
2073 BlobData::File(f) => f.get_size(),
2074 BlobData::Memory(v) => v.len() as u64,
2075 BlobData::Sliced(_, _) => panic!("Blob ancestry should be only one level."),
2076 };
2077 let parent_size = rel_pos.to_abs_range(parent_size as usize).len() as u64;
2078 let blob_info = blob_state
2079 .get_mut(blob_id)
2080 .expect("Blob whose url is requested is unknown.");
2081 self.create_sliced_url_id(blob_info, &parent_file_id, &rel_pos, parent_size)
2082 },
2083 None => {
2084 let blob_info = blob_state
2085 .get_mut(blob_id)
2086 .expect("Blob whose url is requested is unknown.");
2087 self.promote(blob_info, true)
2088 },
2089 }
2090 }
2091
2092 fn create_sliced_url_id(
2094 &self,
2095 blob_info: &mut BlobInfo,
2096 parent_file_id: &Uuid,
2097 rel_pos: &RelativePos,
2098 parent_len: u64,
2099 ) -> Uuid {
2100 let origin = self.origin().immutable().clone();
2101
2102 let (tx, rx) = profile_generic_channel::channel(self.time_profiler_chan().clone()).unwrap();
2103 let msg = FileManagerThreadMsg::AddSlicedURLEntry(*parent_file_id, *rel_pos, tx, origin);
2104 self.send_to_file_manager(msg);
2105 match rx.recv().expect("File manager thread is down.") {
2106 Ok(new_id) => {
2107 *blob_info.blob_impl.blob_data_mut() = BlobData::File(FileBlob::new(
2108 new_id,
2109 None,
2110 None,
2111 rel_pos.to_abs_range(parent_len as usize).len() as u64,
2112 ));
2113
2114 new_id
2116 },
2117 Err(_) => {
2118 Uuid::new_v4()
2120 },
2121 }
2122 }
2123
2124 fn promote_memory_entry(
2128 &self,
2129 blob_info: &BlobInfo,
2130 blob_bytes: &[u8],
2131 set_valid: bool,
2132 ) -> Uuid {
2133 let origin = self.origin().immutable().clone();
2134 let blob_buf = BlobBuf {
2135 filename: None,
2136 type_string: blob_info.blob_impl.type_string(),
2137 size: blob_bytes.len() as u64,
2138 bytes: blob_bytes.to_vec(),
2139 };
2140 let id = Uuid::new_v4();
2141 let msg = FileManagerThreadMsg::PromoteMemory(id, blob_buf, set_valid, origin);
2142 self.send_to_file_manager(msg);
2143 id
2144 }
2145
2146 pub(crate) fn promote(&self, blob_info: &mut BlobInfo, set_valid: bool) -> Uuid {
2152 let mut bytes = vec![];
2153
2154 match blob_info.blob_impl.blob_data_mut() {
2155 BlobData::Sliced(_, _) => {
2156 panic!("Sliced blobs should use create_sliced_url_id instead of promote.");
2157 },
2158 BlobData::File(f) => {
2159 if set_valid {
2160 if let Some(cached_bytes) = f.get_cache() {
2163 return self.promote_memory_entry(blob_info, &cached_bytes, true);
2164 }
2165
2166 let origin = self.origin().immutable().clone();
2167 let (tx, rx) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
2168
2169 let msg = FileManagerThreadMsg::ActivateBlobURL(f.get_id(), tx, origin);
2170 self.send_to_file_manager(msg);
2171
2172 match rx.recv().unwrap() {
2173 Ok(_) => return f.get_id(),
2174 Err(_) => return Uuid::new_v4(),
2176 }
2177 } else {
2178 return f.get_id();
2180 }
2181 },
2182 BlobData::Memory(bytes_in) => mem::swap(bytes_in, &mut bytes),
2183 };
2184
2185 let id = self.promote_memory_entry(blob_info, &bytes, set_valid);
2186
2187 *blob_info.blob_impl.blob_data_mut() = BlobData::File(FileBlob::new(
2188 id,
2189 None,
2190 Some(bytes.to_vec()),
2191 bytes.len() as u64,
2192 ));
2193
2194 id
2195 }
2196
2197 fn send_to_file_manager(&self, msg: FileManagerThreadMsg) {
2198 let resource_threads = self.resource_threads();
2199 let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
2200 }
2201
2202 fn read_file(&self, id: Uuid) -> Result<Vec<u8>, ()> {
2203 let recv = self.send_msg(id);
2204 GlobalScope::read_msg(recv)
2205 }
2206
2207 pub(crate) fn get_blob_stream(
2209 &self,
2210 cx: &mut js::context::JSContext,
2211 blob_id: &BlobId,
2212 ) -> Fallible<DomRoot<ReadableStream>> {
2213 let (file_id, size) = match self.get_blob_bytes_or_file_id(blob_id) {
2214 BlobResult::Bytes(bytes) => {
2215 return ReadableStream::new_from_bytes_with_byte_reading_support(cx, self, bytes);
2217 },
2218 BlobResult::File(id, size) => (id, size),
2219 };
2220
2221 let stream = ReadableStream::new_with_external_underlying_byte_source(
2222 cx,
2223 self,
2224 UnderlyingSourceType::Blob(size),
2225 )?;
2226
2227 let recv = self.send_msg(file_id);
2228
2229 let trusted_stream = Trusted::new(&*stream);
2230 let mut file_listener = FileListener {
2231 state: Some(FileListenerState::Empty(FileListenerTarget::Stream(
2232 trusted_stream,
2233 ))),
2234 task_source: self.task_manager().file_reading_task_source().into(),
2235 };
2236
2237 ROUTER.add_typed_route(
2238 recv.to_ipc_receiver(),
2239 Box::new(move |msg| {
2240 file_listener.handle(msg.expect("Deserialization of file listener msg failed."));
2241 }),
2242 );
2243
2244 Ok(stream)
2245 }
2246
2247 pub(crate) fn read_file_async(
2248 &self,
2249 id: Uuid,
2250 promise: Rc<Promise>,
2251 callback: FileListenerCallback,
2252 ) {
2253 let recv = self.send_msg(id);
2254
2255 let trusted_promise = TrustedPromise::new(promise);
2256 let mut file_listener = FileListener {
2257 state: Some(FileListenerState::Empty(FileListenerTarget::Promise(
2258 trusted_promise,
2259 callback,
2260 ))),
2261 task_source: self.task_manager().file_reading_task_source().into(),
2262 };
2263
2264 ROUTER.add_typed_route(
2265 recv.to_ipc_receiver(),
2266 Box::new(move |msg| {
2267 file_listener.handle(msg.expect("Deserialization of file listener msg failed."));
2268 }),
2269 );
2270 }
2271
2272 fn send_msg(&self, id: Uuid) -> profile_ipc::IpcReceiver<FileManagerResult<ReadFileProgress>> {
2273 let resource_threads = self.resource_threads();
2274 let (chan, recv) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
2275 let origin = self.origin().immutable().clone();
2276 let msg = FileManagerThreadMsg::ReadFile(chan, id, origin);
2277 let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
2278 recv
2279 }
2280
2281 fn read_msg(
2282 receiver: profile_ipc::IpcReceiver<FileManagerResult<ReadFileProgress>>,
2283 ) -> Result<Vec<u8>, ()> {
2284 let mut bytes = vec![];
2285
2286 loop {
2287 match receiver.recv().unwrap() {
2288 Ok(ReadFileProgress::Meta(mut blob_buf)) => {
2289 bytes.append(&mut blob_buf.bytes);
2290 },
2291 Ok(ReadFileProgress::Partial(mut bytes_in)) => {
2292 bytes.append(&mut bytes_in);
2293 },
2294 Ok(ReadFileProgress::EOF) => {
2295 return Ok(bytes);
2296 },
2297 Err(_) => return Err(()),
2298 }
2299 }
2300 }
2301
2302 pub(crate) fn permission_state_invocation_results(
2303 &self,
2304 ) -> &DomRefCell<HashMap<PermissionName, PermissionState>> {
2305 &self.permission_state_invocation_results
2306 }
2307
2308 pub(crate) fn track_worker(
2309 &self,
2310 closing: Arc<AtomicBool>,
2311 join_handle: JoinHandle<()>,
2312 control_sender: Sender<DedicatedWorkerControlMsg>,
2313 context: ThreadSafeJSContext,
2314 ) {
2315 self.list_auto_close_worker
2316 .borrow_mut()
2317 .push(AutoCloseWorker {
2318 closing,
2319 join_handle: Some(join_handle),
2320 control_sender,
2321 context,
2322 });
2323 }
2324
2325 pub(crate) fn track_event_source(&self, event_source: &EventSource) {
2326 self.event_source_tracker.track(event_source);
2327 }
2328
2329 pub(crate) fn close_event_sources(&self) -> bool {
2330 let mut canceled_any_fetch = false;
2331 self.event_source_tracker
2332 .for_each(
2333 |event_source: DomRoot<EventSource>| match event_source.ReadyState() {
2334 2 => {},
2335 _ => {
2336 event_source.cancel();
2337 canceled_any_fetch = true;
2338 },
2339 },
2340 );
2341 canceled_any_fetch
2342 }
2343
2344 #[expect(unsafe_code)]
2347 pub(crate) fn from_reflector<T: DomObject>(reflector: &T) -> DomRoot<Self> {
2348 unsafe { GlobalScope::from_object(*reflector.reflector().get_jsobject()) }
2349 }
2350
2351 #[expect(unsafe_code)]
2353 pub(crate) unsafe fn from_object(obj: *mut JSObject) -> DomRoot<Self> {
2354 assert!(!obj.is_null());
2355 let global = unsafe { GetNonCCWObjectGlobal(obj) };
2356 unsafe { global_scope_from_global_static(global) }
2357 }
2358
2359 #[expect(unsafe_code)]
2363 pub(crate) fn from_current_realm(realm: &'_ CurrentRealm) -> DomRoot<Self> {
2364 let global = realm.global();
2365 unsafe { global_scope_from_global(global.get(), realm.raw_cx_no_gc()) }
2366 }
2367
2368 pub(crate) fn add_uncaught_rejection(&self, rejection: HandleObject) {
2369 self.uncaught_rejections
2370 .borrow_mut()
2371 .push(Heap::boxed(rejection.get()));
2372 }
2373
2374 pub(crate) fn remove_uncaught_rejection(&self, rejection: HandleObject) {
2375 let mut uncaught_rejections = self.uncaught_rejections.borrow_mut();
2376
2377 if let Some(index) = uncaught_rejections
2378 .iter()
2379 .position(|promise| *promise == Heap::boxed(rejection.get()))
2380 {
2381 uncaught_rejections.remove(index);
2382 }
2383 }
2384
2385 #[allow(clippy::vec_box)]
2388 pub(crate) fn get_uncaught_rejections(&self) -> &DomRefCell<Vec<Box<Heap<*mut JSObject>>>> {
2390 &self.uncaught_rejections
2391 }
2392
2393 pub(crate) fn add_consumed_rejection(&self, rejection: HandleObject) {
2394 self.consumed_rejections
2395 .borrow_mut()
2396 .push(Heap::boxed(rejection.get()));
2397 }
2398
2399 pub(crate) fn remove_consumed_rejection(&self, rejection: HandleObject) {
2400 let mut consumed_rejections = self.consumed_rejections.borrow_mut();
2401
2402 if let Some(index) = consumed_rejections
2403 .iter()
2404 .position(|promise| *promise == Heap::boxed(rejection.get()))
2405 {
2406 consumed_rejections.remove(index);
2407 }
2408 }
2409
2410 #[allow(clippy::vec_box)]
2413 pub(crate) fn get_consumed_rejections(&self) -> &DomRefCell<Vec<Box<Heap<*mut JSObject>>>> {
2414 &self.consumed_rejections
2415 }
2416
2417 pub(crate) fn set_module_map(&self, request: ModuleRequest, module: ModuleStatus) {
2418 self.module_map.borrow_mut().insert(request, module);
2419 }
2420
2421 pub(crate) fn get_module_map_entry(&self, request: &ModuleRequest) -> Option<ModuleStatus> {
2422 self.module_map.borrow().get(request).cloned()
2423 }
2424
2425 #[expect(unsafe_code)]
2426 pub(crate) fn get_cx() -> SafeJSContext {
2427 let cx = Runtime::get()
2428 .expect("Can't obtain context after runtime shutdown")
2429 .as_ptr();
2430 unsafe { SafeJSContext::from_ptr(cx) }
2431 }
2432
2433 pub(crate) fn time(&self, label: DOMString) -> Result<(), ()> {
2434 let mut timers = self.console_timers.borrow_mut();
2435 if timers.len() >= 10000 {
2436 return Err(());
2437 }
2438 match timers.entry(label) {
2439 Entry::Vacant(entry) => {
2440 entry.insert(Instant::now());
2441 Ok(())
2442 },
2443 Entry::Occupied(_) => Err(()),
2444 }
2445 }
2446
2447 pub(crate) fn time_log(&self, label: &DOMString) -> Result<u64, ()> {
2451 self.console_timers
2452 .borrow()
2453 .get(label)
2454 .ok_or(())
2455 .map(|&start| (Instant::now() - start).as_millis() as u64)
2456 }
2457
2458 pub(crate) fn time_end(&self, label: &DOMString) -> Result<u64, ()> {
2463 self.console_timers
2464 .borrow_mut()
2465 .remove(label)
2466 .ok_or(())
2467 .map(|start| (Instant::now() - start).as_millis() as u64)
2468 }
2469
2470 pub(crate) fn devtools_chan(&self) -> Option<&GenericCallback<ScriptToDevtoolsControlMsg>> {
2473 self.devtools_chan.as_ref()
2474 }
2475
2476 pub(crate) fn mem_profiler_chan(&self) -> &profile_mem::ProfilerChan {
2478 &self.mem_profiler_chan
2479 }
2480
2481 pub(crate) fn time_profiler_chan(&self) -> &profile_time::ProfilerChan {
2483 &self.time_profiler_chan
2484 }
2485
2486 pub(crate) fn script_to_constellation_chan(&self) -> ScriptToConstellationChan {
2488 ScriptToConstellationChan {
2489 sender: self.script_to_constellation_sender.clone(),
2490 webview_id: self.webview_id().unwrap_or(TEST_WEBVIEW_ID),
2491 pipeline_id: self.pipeline_id(),
2492 }
2493 }
2494
2495 pub(crate) fn script_to_embedder_chan(&self) -> &ScriptToEmbedderChan {
2496 &self.script_to_embedder_chan
2497 }
2498
2499 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
2500 self.script_to_embedder_chan().send(msg).unwrap();
2501 }
2502
2503 pub(crate) fn pipeline_id(&self) -> PipelineId {
2505 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2506 worker.pipeline_id()
2507 } else if let Some(window) = self.downcast::<Window>() {
2508 window.pipeline_id()
2509 } else if let Some(debugger) = self.downcast::<DebuggerGlobalScope>() {
2510 debugger.pipeline_id()
2511 } else if let Some(worklet) = self.downcast::<WorkletGlobalScope>() {
2512 worklet.pipeline_id()
2513 } else if let Some(dissimilar) = self.downcast::<DissimilarOriginWindow>() {
2514 dissimilar.pipeline_id()
2515 } else {
2516 unreachable!("Unsupported global type for pipeline id")
2517 }
2518 }
2519
2520 pub(crate) fn register_interest(&self, interest: ConstellationInterest) {
2523 let mut counts = self.constellation_interest_counts.borrow_mut();
2524 let count = counts.entry(interest).or_insert(0);
2525 *count += 1;
2526 if *count == 1 {
2527 let _ = self
2528 .script_to_constellation_chan()
2529 .send(ScriptToConstellationMessage::RegisterInterest(interest));
2530 }
2531 }
2532
2533 pub(crate) fn unregister_interest(&self, interest: ConstellationInterest) {
2536 let mut counts = self.constellation_interest_counts.borrow_mut();
2537 if let Some(count) = counts.get_mut(&interest) {
2538 *count = count.saturating_sub(1);
2539 if *count == 0 {
2540 counts.remove(&interest);
2541 let _ = self
2542 .script_to_constellation_chan()
2543 .send(ScriptToConstellationMessage::UnregisterInterest(interest));
2544 }
2545 }
2546 }
2547
2548 pub(crate) fn origin(&self) -> MutableOrigin {
2550 if let Some(window) = self.downcast::<Window>() {
2551 window.origin()
2552 } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2553 worker.origin()
2554 } else if let Some(worklet) = self.downcast::<WorkletGlobalScope>() {
2555 worklet.origin()
2556 } else if let Some(dissimilar_window) = self.downcast::<DissimilarOriginWindow>() {
2557 dissimilar_window.origin()
2558 } else if let Some(debugger) = self.downcast::<DebuggerGlobalScope>() {
2559 debugger.origin()
2560 } else {
2561 unreachable!("Unexpected origin check against global")
2562 }
2563 }
2564
2565 pub(crate) fn creation_url(&self) -> ServoUrl {
2567 self.creation_url.borrow().clone()
2568 }
2569
2570 pub(crate) fn set_creation_url(&self, creation_url: ServoUrl) {
2571 *self.creation_url.borrow_mut() = creation_url;
2572 }
2573
2574 pub(crate) fn top_level_creation_url(&self) -> &Option<ServoUrl> {
2576 &self.top_level_creation_url
2577 }
2578
2579 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
2580 if let Some(window) = self.downcast::<Window>() {
2581 return window.image_cache();
2582 }
2583 if let Some(worker) = self.downcast::<DedicatedWorkerGlobalScope>() {
2584 return worker.image_cache();
2585 }
2586 if let Some(worker) = self.downcast::<SharedWorkerGlobalScope>() {
2587 return worker.image_cache();
2588 }
2589 if let Some(worker) = self.downcast::<PaintWorkletGlobalScope>() {
2590 return worker.image_cache();
2591 }
2592 unreachable!();
2593 }
2594
2595 pub(crate) fn schedule_timer(&self, request: TimerEventRequest) -> Option<TimerId> {
2599 match self.downcast::<WorkerGlobalScope>() {
2600 Some(worker_global) => Some(worker_global.timer_scheduler().schedule_timer(request)),
2601 _ => with_script_thread(|script_thread| Some(script_thread.schedule_timer(request))),
2602 }
2603 }
2604
2605 pub(crate) fn is_nested_browsing_context(&self) -> bool {
2607 self.downcast::<Window>()
2608 .is_some_and(|window| !window.is_top_level())
2609 }
2610
2611 pub(crate) fn total_size_of_in_flight_keep_alive_records(&self) -> u64 {
2617 let (sender, receiver) = generic_channel::channel().unwrap();
2618 if self
2619 .core_resource_thread()
2620 .send(CoreResourceMsg::TotalSizeOfInFlightKeepAliveRecords(
2621 self.pipeline_id(),
2622 sender,
2623 ))
2624 .is_err()
2625 {
2626 return u64::MAX;
2627 }
2628 receiver.recv().unwrap_or(u64::MAX)
2629 }
2630
2631 pub(crate) fn request_client(&self) -> RequestClient {
2633 let window = self.downcast::<Window>();
2636 let preloaded_resources = window
2637 .map(|window: &Window| window.Document().preloaded_resources().clone())
2638 .unwrap_or_default();
2639 let is_nested_browsing_context = window.is_some_and(|window| !window.is_top_level());
2640 RequestClient {
2641 preloaded_resources,
2642 policy_container: RequestPolicyContainer::PolicyContainer(self.policy_container()),
2643 origin: RequestOrigin::Origin(self.origin().immutable().clone()),
2644 is_nested_browsing_context,
2645 insecure_requests_policy: self.insecure_requests_policy(),
2646 }
2647 }
2648
2649 pub(crate) fn policy_container(&self) -> PolicyContainer {
2651 if let Some(window) = self.downcast::<Window>() {
2652 return window.Document().policy_container().to_owned();
2653 }
2654 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2655 return worker.policy_container().to_owned();
2656 }
2657 unreachable!();
2658 }
2659
2660 pub(crate) fn api_base_url(&self) -> ServoUrl {
2663 if let Some(window) = self.downcast::<Window>() {
2664 return window.Document().base_url();
2666 }
2667 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2668 return worker.get_url().clone();
2670 }
2671 if let Some(worklet) = self.downcast::<WorkletGlobalScope>() {
2672 return worklet.base_url();
2674 }
2675 if let Some(_debugger_global) = self.downcast::<DebuggerGlobalScope>() {
2676 return self.creation_url();
2677 }
2678 unreachable!();
2679 }
2680
2681 pub(crate) fn get_url(&self) -> ServoUrl {
2683 if let Some(window) = self.downcast::<Window>() {
2684 return window.get_url();
2685 }
2686 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2687 return worker.get_url().clone();
2688 }
2689 if let Some(worklet) = self.downcast::<WorkletGlobalScope>() {
2690 return worklet.base_url();
2692 }
2693 if let Some(_debugger_global) = self.downcast::<DebuggerGlobalScope>() {
2694 return self.creation_url();
2695 }
2696 unreachable!();
2697 }
2698
2699 pub(crate) fn get_referrer_policy(&self) -> ReferrerPolicy {
2701 if let Some(window) = self.downcast::<Window>() {
2702 let document = window.Document();
2703
2704 return document.get_referrer_policy();
2705 }
2706 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2707 return worker.policy_container().get_referrer_policy();
2708 }
2709 unreachable!();
2710 }
2711
2712 pub(crate) fn get_referrer(&self) -> Referrer {
2715 if let Some(window) = self.downcast::<Window>() {
2717 let mut document = window.Document();
2719
2720 if let ImmutableOrigin::Opaque(_) = document.origin().immutable() {
2722 return Referrer::NoReferrer;
2723 }
2724
2725 let mut url = document.url();
2726
2727 while url.as_str() == "about:srcdoc" {
2730 let Some(parent_document) =
2733 document.browsing_context().and_then(|browsing_context| {
2734 browsing_context
2735 .parent()
2736 .and_then(|parent| parent.document())
2737 })
2738 else {
2739 return Referrer::NoReferrer;
2740 };
2741 document = parent_document;
2742 url = document.url();
2743 }
2744
2745 Referrer::Client(url)
2747 } else {
2748 Referrer::Client(self.creation_url())
2750 }
2751 }
2752
2753 pub(crate) fn as_window(&self) -> &Window {
2755 self.downcast::<Window>().expect("expected a Window scope")
2756 }
2757
2758 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
2760 if let Some(window) = self.downcast::<Window>() {
2761 return window.Document().insecure_requests_policy();
2762 }
2763 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2764 return worker.insecure_requests_policy();
2765 }
2766 debug!("unsupported global, defaulting insecure requests policy to DoNotUpgrade");
2767 InsecureRequestsPolicy::DoNotUpgrade
2768 }
2769
2770 pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
2772 self.downcast::<Window>()
2773 .is_some_and(|window| window.Document().has_trustworthy_ancestor_origin())
2774 }
2775
2776 pub(crate) fn has_trustworthy_ancestor_or_current_origin(&self) -> bool {
2778 self.downcast::<Window>().is_some_and(|window| {
2779 window
2780 .Document()
2781 .has_trustworthy_ancestor_or_current_origin()
2782 })
2783 }
2784
2785 pub(crate) fn report_an_exception(&self, cx: &mut js::context::JSContext, error: HandleValue) {
2787 let error_info = ErrorInfo::from_value(cx, error);
2789
2790 self.report_an_error(cx, error_info, error);
2803 }
2804
2805 pub(crate) fn report_an_error(
2807 &self,
2808 cx: &mut js::context::JSContext,
2809 error_info: ErrorInfo,
2810 value: HandleValue,
2811 ) {
2812 self.send_to_embedder(EmbedderMsg::ShowConsoleApiMessage(
2813 self.webview_id(),
2814 ConsoleLogLevel::Error,
2815 format!(
2816 "Error at {}:{}:{} {}",
2817 error_info.filename, error_info.lineno, error_info.column, error_info.message
2818 ),
2819 ));
2820
2821 #[cfg(feature = "js_backtrace")]
2822 LAST_EXCEPTION_BACKTRACE.with(|backtrace| {
2823 if let Some((js_backtrace, rust_backtrace)) = backtrace.borrow_mut().take() {
2824 if let Some(stack) = js_backtrace {
2825 error!("JS backtrace:\n{}", stack);
2826 }
2827 error!("Rust backtrace:\n{}", rust_backtrace);
2828 }
2829 });
2830
2831 let mut not_handled = true;
2833
2834 if !self.in_error_reporting_mode.get() {
2836 self.in_error_reporting_mode.set(true);
2838
2839 let event = ErrorEvent::new(
2844 cx,
2845 self,
2846 atom!("error"),
2847 EventBubbles::DoesNotBubble,
2848 EventCancelable::Cancelable,
2849 error_info.message.as_str().into(),
2850 error_info.filename.as_str().into(),
2851 error_info.lineno,
2852 error_info.column,
2853 value,
2854 );
2855 not_handled = event
2856 .upcast::<Event>()
2857 .fire(cx, self.upcast::<EventTarget>());
2858
2859 self.in_error_reporting_mode.set(false);
2861 }
2862
2863 if not_handled {
2865 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
2871 dedicated.forward_error_to_worker_object(error_info);
2872 } else if self.is::<Window>() {
2873 if let Some(ref chan) = self.devtools_chan {
2875 let _ = chan.send(ScriptToDevtoolsControlMsg::ReportPageError(
2876 self.pipeline_id(),
2877 PageError {
2878 error_message: error_info.message.clone(),
2879 source_name: error_info.filename.clone(),
2880 line_number: error_info.lineno,
2881 column_number: error_info.column,
2882 time_stamp: get_time_stamp(),
2883 },
2884 ));
2885 }
2886 }
2887 }
2888 }
2889
2890 pub(crate) fn resource_threads(&self) -> &ResourceThreads {
2892 &self.resource_threads
2893 }
2894
2895 pub(crate) fn core_resource_thread(&self) -> CoreResourceThread {
2897 self.resource_threads().sender()
2898 }
2899
2900 pub(crate) fn storage_threads(&self) -> &StorageThreads {
2902 &self.storage_threads
2903 }
2904
2905 pub(crate) fn event_loop_sender(&self) -> Option<ScriptEventLoopSender> {
2909 if let Some(window) = self.downcast::<Window>() {
2910 Some(window.event_loop_sender())
2911 } else if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
2912 dedicated.event_loop_sender()
2913 } else if let Some(shared_worker) = self.downcast::<SharedWorkerGlobalScope>() {
2914 Some(shared_worker.event_loop_sender())
2915 } else if let Some(service_worker) = self.downcast::<ServiceWorkerGlobalScope>() {
2916 Some(service_worker.event_loop_sender())
2917 } else {
2918 unreachable!(
2919 "Tried to access event loop sender for incompatible \
2920 GlobalScope (PaintWorklet or DissimilarOriginWindow)"
2921 );
2922 }
2923 }
2924
2925 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
2927 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2928 worker.task_manager()
2929 } else if let Some(window) = self.downcast::<Window>() {
2930 window.task_manager()
2931 } else {
2932 unreachable!("Attempted to use task manager with unsupported global");
2933 }
2934 }
2935
2936 #[expect(unsafe_code)]
2938 pub(crate) fn evaluate_js_on_global(
2939 &self,
2940 cx: &mut CurrentRealm,
2941 code: Cow<'_, str>,
2942 filename: &str,
2943 introduction_type: Option<&'static CStr>,
2944 rval: Option<MutableHandleValue>,
2945 ) -> Result<(), JavaScriptEvaluationError> {
2946 assert!(self.can_run_script());
2947
2948 run_a_script::<DomTypeHolder, _, _>(cx, self, |cx| {
2949 let url = self.api_base_url();
2950 let fetch_options = ScriptFetchOptions::default_classic_script();
2951
2952 let options = fill_compile_options(
2953 cx,
2954 filename,
2955 introduction_type,
2956 ErrorReporting::Unmuted,
2957 rval.is_none(), 1, );
2960
2961 let mut source = transform_str_to_source_text(&code);
2962 rooted!(&in(cx) let compiled_script = unsafe { Compile1(cx, options.ptr, &mut source) });
2963
2964 let Some(script) = NonNull::new(*compiled_script) else {
2965 debug!("error compiling Dom string");
2966 report_pending_exception(cx);
2967 return Err(JavaScriptEvaluationError::CompilationFailure);
2968 };
2969
2970 rooted!(&in(cx) let mut value = UndefinedValue());
2971 let rval = rval.unwrap_or_else(|| value.handle_mut());
2972
2973 if !evaluate_script(cx, script, url, fetch_options, rval) {
2974 let error_info = take_and_report_pending_exception_for_api(cx);
2975 return Err(JavaScriptEvaluationError::EvaluationFailure(error_info));
2976 }
2977
2978 maybe_resume_unwind();
2979 Ok(())
2980 })
2981 }
2982
2983 pub(crate) fn schedule_callback(
2985 &self,
2986 callback: OneshotTimerCallback,
2987 duration: Duration,
2988 ) -> OneshotTimerHandle {
2989 self.with_timers(|timers| timers.schedule_callback(callback, duration, self.timer_source()))
2990 }
2991
2992 pub(crate) fn unschedule_callback(&self, handle: OneshotTimerHandle) {
2993 self.with_timers(|timers| timers.unschedule_callback(handle));
2994 }
2995
2996 pub(crate) fn set_timeout_or_interval(
2998 &self,
2999 cx: &mut js::context::JSContext,
3000 callback: TimerCallback,
3001 arguments: Vec<HandleValue>,
3002 timeout: Duration,
3003 is_interval: IsInterval,
3004 ) -> Fallible<i32> {
3005 self.with_timers(|timers| {
3006 timers.set_timeout_or_interval(
3007 cx,
3008 self,
3009 callback,
3010 arguments,
3011 timeout,
3012 is_interval,
3013 self.timer_source(),
3014 )
3015 })
3016 }
3017
3018 pub(crate) fn clear_timeout_or_interval(&self, handle: i32) {
3019 self.with_timers(|timers| timers.clear_timeout_or_interval(self, handle));
3020 }
3021
3022 pub(crate) fn fire_timer(&self, handle: TimerEventId, cx: &mut js::context::JSContext) {
3023 self.with_timers(|timers| timers.fire_timer(handle, self, cx));
3024 }
3025
3026 pub(crate) fn resume(&self) {
3027 self.with_timers(|timers| timers.resume());
3028 }
3029
3030 pub(crate) fn suspend(&self) {
3031 self.with_timers(|timers| timers.suspend());
3032 }
3033
3034 pub(crate) fn slow_down_timers(&self) {
3035 self.with_timers(|timers| timers.slow_down());
3036 }
3037
3038 pub(crate) fn speed_up_timers(&self) {
3039 self.with_timers(|timers| timers.speed_up());
3040 }
3041
3042 fn timer_source(&self) -> TimerSource {
3043 if self.is::<Window>() {
3044 return TimerSource::FromWindow(self.pipeline_id());
3045 }
3046 if self.is::<WorkerGlobalScope>() {
3047 return TimerSource::FromWorker;
3048 }
3049 unreachable!();
3050 }
3051
3052 pub(crate) fn can_continue_running(&self) -> bool {
3055 if self.is::<Window>() {
3056 return ScriptThread::can_continue_running();
3057 }
3058 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3059 return !worker.is_closing();
3060 }
3061
3062 true
3064 }
3065
3066 pub(crate) fn get_indexeddb(&self, cx: &mut js::context::JSContext) -> DomRoot<IDBFactory> {
3068 self.indexeddb.or_init(|| IDBFactory::new(cx, self))
3069 }
3070
3071 pub(crate) fn get_existing_indexeddb(&self) -> Option<DomRoot<IDBFactory>> {
3072 self.indexeddb.get()
3073 }
3074
3075 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
3077 if let Some(window) = self.downcast::<Window>() {
3078 window.perform_a_microtask_checkpoint(cx);
3079 } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3080 worker.perform_a_microtask_checkpoint(cx);
3081 }
3082 }
3083
3084 pub(crate) fn enqueue_microtask(&self, cx: &mut js::context::JSContext, job: Microtask) {
3086 if self.is::<Window>() {
3087 ScriptThread::enqueue_microtask(job);
3088 } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3089 worker.enqueue_microtask(cx, job);
3090 }
3091 }
3092
3093 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
3097 if let Some(window) = self.downcast::<Window>() {
3098 return window.new_script_pair();
3099 }
3100 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3101 return worker.new_script_pair();
3102 }
3103 unreachable!();
3104 }
3105
3106 pub(crate) fn process_event(
3110 &self,
3111 msg: CommonScriptMsg,
3112 cx: &mut js::context::JSContext,
3113 ) -> bool {
3114 if self.is::<Window>() {
3115 return ScriptThread::process_event(msg, cx);
3116 }
3117 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3118 return worker.process_event(msg, cx);
3119 }
3120 unreachable!();
3121 }
3122
3123 pub(crate) fn runtime_handle(&self) -> ParentRuntime {
3124 if self.is::<Window>() {
3125 ScriptThread::runtime_handle()
3126 } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3127 worker.runtime_handle()
3128 } else {
3129 unreachable!()
3130 }
3131 }
3132
3133 #[expect(unsafe_code)]
3137 pub(crate) fn current() -> Option<DomRoot<Self>> {
3138 let cx = Runtime::get()?;
3139 unsafe {
3140 let global = CurrentGlobalOrNull(cx.as_ptr());
3141 if global.is_null() {
3142 None
3143 } else {
3144 Some(global_scope_from_global(global, cx.as_ptr()))
3145 }
3146 }
3147 }
3148
3149 pub(crate) fn entry() -> DomRoot<Self> {
3153 entry_global()
3154 }
3155
3156 pub(crate) fn incumbent() -> Option<DomRoot<Self>> {
3160 incumbent_global()
3161 }
3162
3163 pub(crate) fn performance(&self) -> DomRoot<Performance> {
3164 if let Some(window) = self.downcast::<Window>() {
3165 return window.Performance();
3166 }
3167 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3168 return worker.Performance();
3169 }
3170 unreachable!();
3171 }
3172
3173 pub(crate) fn supported_performance_entry_types(
3175 &self,
3176 cx: &mut js::context::JSContext,
3177 retval: MutableHandleValue,
3178 ) {
3179 self.frozen_supported_performance_entry_types.get_or_init(
3180 cx,
3181 || {
3182 EntryType::VARIANTS
3183 .iter()
3184 .map(|t| DOMString::from(t.as_str()))
3185 .collect()
3186 },
3187 retval,
3188 );
3189 }
3190
3191 pub(crate) fn inherited_secure_context(&self) -> Option<bool> {
3192 self.inherited_secure_context
3193 }
3194
3195 pub(crate) fn is_secure_context(&self) -> bool {
3197 if Some(false) == self.inherited_secure_context {
3202 return false;
3203 }
3204 match self.top_level_creation_url() {
3207 None => {
3208 assert!(self.is::<WorkerGlobalScope>() || self.is::<WorkletGlobalScope>());
3210 true
3211 },
3212 Some(top_level_creation_url) => {
3213 assert!(self.is::<Window>());
3214 if top_level_creation_url.scheme() == "blob" &&
3218 Some(true) == self.inherited_secure_context
3219 {
3220 return true;
3221 }
3222 top_level_creation_url.is_potentially_trustworthy()
3223 },
3224 }
3225 }
3226
3227 pub(crate) fn get_csp_list(&self) -> Option<CspList> {
3229 if self.is::<Window>() || self.is::<WorkerGlobalScope>() {
3230 return self.policy_container().csp_list;
3231 }
3232 None
3234 }
3235
3236 pub(crate) fn status_code(&self) -> Option<u16> {
3237 if let Some(window) = self.downcast::<Window>() {
3238 return window.Document().status_code();
3239 }
3240 None
3241 }
3242
3243 #[cfg(feature = "webgpu")]
3244 pub(crate) fn wgpu_id_hub(&self) -> Arc<IdentityHub> {
3245 self.gpu_id_hub.clone()
3246 }
3247
3248 #[cfg(feature = "webgpu")]
3249 pub(crate) fn add_gpu_device(&self, device: &GPUDevice) {
3250 self.gpu_devices
3251 .borrow_mut()
3252 .insert(device.id(), WeakRef::new(device));
3253 }
3254
3255 #[cfg(feature = "webgpu")]
3256 pub(crate) fn remove_gpu_device(&self, device: WebGPUDevice) {
3257 let device = self
3258 .gpu_devices
3259 .borrow_mut()
3260 .remove(&device)
3261 .expect("GPUDevice should still be in devices hashmap");
3262 assert!(device.root().is_none())
3263 }
3264
3265 #[cfg(feature = "webgpu")]
3266 pub(crate) fn gpu_device_lost(
3267 &self,
3268 device: WebGPUDevice,
3269 reason: DeviceLostReason,
3270 msg: String,
3271 ) {
3272 let reason = match reason {
3273 DeviceLostReason::Unknown => GPUDeviceLostReason::Unknown,
3274 DeviceLostReason::Destroyed => GPUDeviceLostReason::Destroyed,
3275 };
3276 if let Some(device) = self
3277 .gpu_devices
3278 .borrow_mut()
3279 .get_mut(&device)
3280 .expect("GPUDevice should still be in devices hashmap")
3281 .root()
3282 {
3283 device.lose(reason, msg);
3284 }
3285 }
3286
3287 #[cfg(feature = "webgpu")]
3288 pub(crate) fn handle_uncaptured_gpu_error(
3289 &self,
3290 device: WebGPUDevice,
3291 error: webgpu_traits::Error,
3292 ) {
3293 if let Some(gpu_device) = self
3294 .gpu_devices
3295 .borrow()
3296 .get(&device)
3297 .and_then(|device| device.root())
3298 {
3299 gpu_device.fire_uncaptured_error(error);
3300 } else {
3301 warn!("Recived error for lost GPUDevice!")
3302 }
3303 }
3304
3305 pub(crate) fn current_group_label(&self) -> Option<DOMString> {
3306 self.console_group_stack
3307 .borrow()
3308 .last()
3309 .map(|label| DOMString::from(format!("[{}]", label)))
3310 }
3311
3312 pub(crate) fn push_console_group(&self, group: DOMString) {
3313 self.console_group_stack.borrow_mut().push(group);
3314 }
3315
3316 pub(crate) fn pop_console_group(&self) {
3317 let _ = self.console_group_stack.borrow_mut().pop();
3318 }
3319
3320 pub(crate) fn increment_console_count(&self, label: &DOMString) -> usize {
3321 *self
3322 .console_count_map
3323 .borrow_mut()
3324 .entry(label.clone())
3325 .and_modify(|e| *e += 1)
3326 .or_insert(1)
3327 }
3328
3329 pub(crate) fn reset_console_count(&self, label: &DOMString) -> Result<(), ()> {
3330 match self.console_count_map.borrow_mut().get_mut(label) {
3331 Some(value) => {
3332 *value = 0;
3333 Ok(())
3334 },
3335 None => Err(()),
3336 }
3337 }
3338
3339 pub(crate) fn structured_clone(
3340 &self,
3341 cx: &mut js::context::JSContext,
3342 value: HandleValue,
3343 options: RootedTraceableBox<StructuredSerializeOptions>,
3344 retval: MutableHandleValue,
3345 ) -> Fallible<()> {
3346 let mut rooted = CustomAutoRooter::new(
3347 options
3348 .transfer
3349 .iter()
3350 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
3351 .collect(),
3352 );
3353
3354 #[expect(unsafe_code)]
3355 let guard = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
3356
3357 let data = structuredclone::write(cx, value, Some(guard))?;
3358
3359 structuredclone::read(cx, self, data, retval)?;
3360
3361 Ok(())
3362 }
3363
3364 pub(crate) fn fetch<Listener: FetchResponseListener>(
3365 &self,
3366 request_builder: RequestBuilder,
3367 context: Listener,
3368 task_source: SendableTaskSource,
3369 ) {
3370 let network_listener = NetworkListener::new(context, task_source);
3371 self.fetch_with_network_listener(request_builder, network_listener);
3372 }
3373
3374 pub(crate) fn fetch_with_network_listener<Listener: FetchResponseListener>(
3375 &self,
3376 request_builder: RequestBuilder,
3377 network_listener: NetworkListener<Listener>,
3378 ) {
3379 fetch_async(
3380 &self.core_resource_thread(),
3381 request_builder,
3382 None,
3383 network_listener.into_callback(),
3384 );
3385 }
3386
3387 pub(crate) fn unminify_js(&self) -> bool {
3388 self.unminified_js_dir.is_some()
3389 }
3390
3391 pub(crate) fn unminified_js_dir(&self) -> Option<String> {
3392 self.unminified_js_dir.clone()
3393 }
3394
3395 pub(crate) fn set_byte_length_queuing_strategy_size(&self, function: Rc<Function>) {
3396 if self
3397 .byte_length_queuing_strategy_size_function
3398 .set(function)
3399 .is_err()
3400 {
3401 warn!("byte length queuing strategy size function is set twice.");
3402 };
3403 }
3404
3405 pub(crate) fn get_byte_length_queuing_strategy_size(&self) -> Option<Rc<Function>> {
3406 self.byte_length_queuing_strategy_size_function
3407 .get()
3408 .cloned()
3409 }
3410
3411 pub(crate) fn set_count_queuing_strategy_size(&self, function: Rc<Function>) {
3412 if self
3413 .count_queuing_strategy_size_function
3414 .set(function)
3415 .is_err()
3416 {
3417 warn!("count queuing strategy size function is set twice.");
3418 };
3419 }
3420
3421 pub(crate) fn get_count_queuing_strategy_size(&self) -> Option<Rc<Function>> {
3422 self.count_queuing_strategy_size_function.get().cloned()
3423 }
3424
3425 pub(crate) fn add_notification_permission_request_callback(
3426 &self,
3427 callback_id: String,
3428 callback: Rc<NotificationPermissionCallback>,
3429 ) {
3430 self.notification_permission_request_callback_map
3431 .borrow_mut()
3432 .insert(callback_id, callback);
3433 }
3434
3435 pub(crate) fn remove_notification_permission_request_callback(
3436 &self,
3437 callback_id: String,
3438 ) -> Option<Rc<NotificationPermissionCallback>> {
3439 self.notification_permission_request_callback_map
3440 .borrow_mut()
3441 .remove(&callback_id)
3442 }
3443
3444 pub(crate) fn append_deferred_fetch(
3445 &self,
3446 deferred_fetch: QueuedDeferredFetchRecord,
3447 ) -> DeferredFetchRecordId {
3448 let deferred_record_id = DeferredFetchRecordId::default();
3449 self.fetch_group
3450 .borrow_mut()
3451 .deferred_fetch_records
3452 .insert(deferred_record_id, deferred_fetch);
3453 deferred_record_id
3454 }
3455
3456 pub(crate) fn deferred_fetches(&self) -> Vec<QueuedDeferredFetchRecord> {
3457 self.fetch_group
3458 .borrow()
3459 .deferred_fetch_records
3460 .values()
3461 .cloned()
3462 .collect()
3463 }
3464
3465 pub(crate) fn deferred_fetch_record_for_id(
3466 &self,
3467 deferred_fetch_record_id: &DeferredFetchRecordId,
3468 ) -> QueuedDeferredFetchRecord {
3469 self.fetch_group
3470 .borrow()
3471 .deferred_fetch_records
3472 .get(deferred_fetch_record_id)
3473 .expect("Should always use a generated fetch_record_id instead of passing your own")
3474 .clone()
3475 }
3476
3477 pub(crate) fn process_deferred_fetches(&self) {
3479 for deferred_fetch in self.deferred_fetches() {
3482 deferred_fetch.process(self);
3483 }
3484 }
3485
3486 pub(crate) fn import_map(&self) -> Ref<'_, ImportMap> {
3487 self.import_map.borrow()
3488 }
3489
3490 pub(crate) fn import_map_mut(&self) -> RefMut<'_, ImportMap> {
3491 self.import_map.borrow_mut()
3492 }
3493
3494 pub(crate) fn resolved_module_set(&self) -> Ref<'_, HashSet<ResolvedModule>> {
3495 self.resolved_module_set.borrow()
3496 }
3497
3498 pub(crate) fn add_module_to_resolved_module_set(
3500 &self,
3501 base_url: &str,
3502 specifier: &str,
3503 specifier_url: Option<ServoUrl>,
3504 ) {
3505 if self.is::<Window>() {
3508 let record =
3512 ResolvedModule::new(base_url.to_owned(), specifier.to_owned(), specifier_url);
3513 self.resolved_module_set.borrow_mut().insert(record);
3515 }
3516 }
3517
3518 pub(crate) fn run_steps_after_a_timeout<F>(
3522 &self,
3523 ordering_identifier: DOMString,
3524 milliseconds: i64,
3525 completion_steps: F,
3526 ) -> i32
3527 where
3528 F: 'static + FnOnce(&mut js::context::JSContext, &GlobalScope),
3529 {
3530 let ms = milliseconds.max(0) as u64;
3531 let delay = std::time::Duration::from_millis(ms);
3532
3533 let (callback, timer_key) = self.with_timers(|timers| {
3534 let timer_key = timers.fresh_runsteps_key();
3536
3537 let start_time = timers.now_for_runsteps();
3539
3540 let deadline = start_time + delay;
3542 timers.runsteps_set_active(timer_key, deadline);
3543
3544 let callback = crate::timers::OneshotTimerCallback::RunStepsAfterTimeout {
3547 timer_key,
3549 ordering_id: ordering_identifier,
3551 milliseconds: ms,
3553 completion: Box::new(completion_steps),
3555 };
3556 (callback, timer_key)
3557 });
3558 let _ = self.schedule_callback(callback, delay);
3559
3560 timer_key
3562 }
3563}
3564
3565#[expect(unsafe_code)]
3567unsafe fn global_scope_from_global(
3568 global: *mut JSObject,
3569 cx: *mut JSContext,
3570) -> DomRoot<GlobalScope> {
3571 unsafe {
3572 assert!(!global.is_null());
3573 let clasp = get_object_class(global);
3574 assert_ne!(
3575 ((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)),
3576 0
3577 );
3578 root_from_object(global, cx).unwrap()
3579 }
3580}
3581
3582#[expect(unsafe_code)]
3584unsafe fn global_scope_from_global_static(global: *mut JSObject) -> DomRoot<GlobalScope> {
3585 assert!(!global.is_null());
3586 let clasp = unsafe { get_object_class(global) };
3587
3588 unsafe {
3589 assert_ne!(
3590 ((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)),
3591 0
3592 );
3593 }
3594
3595 root_from_object_static(global).unwrap()
3596}
3597
3598#[expect(unsafe_code)]
3599impl GlobalScopeHelpers<crate::DomTypeHolder> for GlobalScope {
3600 fn from_current_realm(realm: &'_ CurrentRealm) -> DomRoot<Self> {
3601 GlobalScope::from_current_realm(realm)
3602 }
3603
3604 unsafe fn from_object(obj: *mut JSObject) -> DomRoot<Self> {
3605 unsafe { GlobalScope::from_object(obj) }
3606 }
3607
3608 fn from_reflector(reflector: &impl DomObject) -> DomRoot<Self> {
3609 GlobalScope::from_reflector(reflector)
3610 }
3611
3612 fn origin(&self) -> MutableOrigin {
3613 GlobalScope::origin(self)
3614 }
3615
3616 fn incumbent() -> Option<DomRoot<Self>> {
3617 GlobalScope::incumbent()
3618 }
3619
3620 fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
3621 GlobalScope::perform_a_microtask_checkpoint(self, cx)
3622 }
3623
3624 fn get_url(&self) -> ServoUrl {
3625 self.get_url()
3626 }
3627
3628 fn is_secure_context(&self) -> bool {
3629 self.is_secure_context()
3630 }
3631}