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