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