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