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::{EmbedderMsg, JavaScriptEvaluationError, ScriptToEmbedderChan};
24use fonts::FontContext;
25use indexmap::IndexSet;
26use ipc_channel::ipc::{self};
27use ipc_channel::router::ROUTER;
28use js::jsapi::{
29 CurrentGlobalOrNull, GetNonCCWObjectGlobal, HandleObject, Heap, JSContext, JSObject, JSScript,
30};
31use js::jsval::UndefinedValue;
32use js::panic::maybe_resume_unwind;
33use js::realm::CurrentRealm;
34use js::rust::{
35 CustomAutoRooter, CustomAutoRooterGuard, HandleValue, MutableHandleValue, ParentRuntime,
36 Runtime, get_object_class,
37};
38use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
39use net_traits::blob_url_store::BlobBuf;
40use net_traits::filemanager_thread::{
41 FileManagerResult, FileManagerThreadMsg, ReadFileProgress, RelativePos,
42};
43use net_traits::image_cache::ImageCache;
44use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
45use net_traits::request::{
46 InsecureRequestsPolicy, Origin as RequestOrigin, Referrer, RequestBuilder, RequestClient,
47};
48use net_traits::response::HttpsState;
49use net_traits::{
50 CoreResourceMsg, CoreResourceThread, ReferrerPolicy, ResourceThreads, fetch_async,
51};
52use profile_traits::{
53 generic_channel as profile_generic_channel, ipc as profile_ipc, mem as profile_mem,
54 time as profile_time,
55};
56use rustc_hash::{FxBuildHasher, FxHashMap};
57use script_bindings::interfaces::GlobalScopeHelpers;
58use script_bindings::settings_stack::run_a_script;
59use servo_base::generic_channel;
60use servo_base::generic_channel::{GenericCallback, GenericSend};
61use servo_base::id::{
62 BlobId, BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineId,
63 ServiceWorkerId, ServiceWorkerRegistrationId, WebViewId,
64};
65use servo_constellation_traits::{
66 BlobData, BlobImpl, BroadcastChannelMsg, FileBlob, MessagePortImpl, MessagePortMsg,
67 PortMessageTask, ScriptToConstellationChan, ScriptToConstellationMessage,
68};
69use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
70use storage_traits::StorageThreads;
71use strum::VariantArray;
72use timers::{TimerEventRequest, TimerId};
73use uuid::Uuid;
74#[cfg(feature = "webgpu")]
75use webgpu_traits::{DeviceLostReason, WebGPUDevice};
76
77use super::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
78#[cfg(feature = "webgpu")]
79use super::bindings::codegen::Bindings::WebGPUBinding::GPUDeviceLostReason;
80use super::bindings::trace::{HashMapTracedValues, RootedTraceableBox};
81use super::serviceworkerglobalscope::ServiceWorkerGlobalScope;
82use super::transformstream::CrossRealmTransform;
83use crate::DomTypeHolder;
84use crate::dom::bindings::cell::{DomRefCell, RefMut};
85use crate::dom::bindings::codegen::Bindings::BroadcastChannelBinding::BroadcastChannelMethods;
86use crate::dom::bindings::codegen::Bindings::EventSourceBinding::EventSource_Binding::EventSourceMethods;
87use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
88use crate::dom::bindings::codegen::Bindings::NotificationBinding::NotificationPermissionCallback;
89use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{
90 PermissionName, PermissionState,
91};
92use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
93use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
94use crate::dom::bindings::conversions::{root_from_object, root_from_object_static};
95#[cfg(feature = "js_backtrace")]
96use crate::dom::bindings::error::LAST_EXCEPTION_BACKTRACE;
97use crate::dom::bindings::error::{
98 Error, ErrorInfo, Fallible, report_pending_exception, take_and_report_pending_exception_for_api,
99};
100use crate::dom::bindings::frozenarray::CachedFrozenArray;
101use crate::dom::bindings::inheritance::Castable;
102use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
103use crate::dom::bindings::reflector::{DomGlobal, DomObject};
104use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
105use crate::dom::bindings::settings_stack::{entry_global, incumbent_global};
106use crate::dom::bindings::str::DOMString;
107use crate::dom::bindings::structuredclone;
108use crate::dom::bindings::trace::CustomTraceable;
109use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
110use crate::dom::blob::Blob;
111use crate::dom::broadcastchannel::BroadcastChannel;
112use crate::dom::crypto::Crypto;
113use crate::dom::dedicatedworkerglobalscope::{
114 DedicatedWorkerControlMsg, DedicatedWorkerGlobalScope,
115};
116use crate::dom::errorevent::ErrorEvent;
117use crate::dom::event::{Event, EventBubbles, EventCancelable};
118use crate::dom::eventsource::EventSource;
119use crate::dom::eventtarget::EventTarget;
120use crate::dom::file::File;
121use crate::dom::global_scope_script_execution::{ErrorReporting, compile_script, evaluate_script};
122use crate::dom::idbfactory::IDBFactory;
123use crate::dom::messageport::MessagePort;
124use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
125use crate::dom::performance::performance::Performance;
126use crate::dom::performance::performanceentry::EntryType;
127use crate::dom::promise::Promise;
128use crate::dom::readablestream::{CrossRealmTransformReadable, ReadableStream};
129use crate::dom::serviceworker::ServiceWorker;
130use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
131use crate::dom::stream::underlyingsourcecontainer::UnderlyingSourceType;
132use crate::dom::stream::writablestream::CrossRealmTransformWritable;
133use crate::dom::types::{AbortSignal, CookieStore, DebuggerGlobalScope, MessageEvent};
134#[cfg(feature = "webgpu")]
135use crate::dom::webgpu::gpudevice::GPUDevice;
136#[cfg(feature = "webgpu")]
137use crate::dom::webgpu::identityhub::IdentityHub;
138use crate::dom::window::Window;
139use crate::dom::workerglobalscope::WorkerGlobalScope;
140use crate::dom::workletglobalscope::WorkletGlobalScope;
141use crate::fetch::{DeferredFetchRecordId, FetchGroup, QueuedDeferredFetchRecord};
142use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
143use crate::microtask::Microtask;
144use crate::network_listener::{FetchResponseListener, NetworkListener};
145use crate::realms::{InRealm, enter_auto_realm};
146use crate::script_module::{
147 ImportMap, ModuleRequest, ModuleStatus, ResolvedModule, ScriptFetchOptions,
148};
149use crate::script_runtime::{CanGc, JSContext as SafeJSContext, ThreadSafeJSContext};
150use crate::script_thread::{ScriptThread, with_script_thread};
151use crate::task_manager::TaskManager;
152use crate::task_source::SendableTaskSource;
153use crate::timers::{
154 IsInterval, OneshotTimerCallback, OneshotTimerHandle, OneshotTimers, TimerCallback,
155 TimerEventId, TimerSource,
156};
157use crate::unminify::unminified_path;
158
159#[derive(JSTraceable, MallocSizeOf)]
160pub(crate) struct AutoCloseWorker {
161 #[conditional_malloc_size_of]
163 closing: Arc<AtomicBool>,
164 #[ignore_malloc_size_of = "JoinHandle"]
166 join_handle: Option<JoinHandle<()>>,
167 #[no_trace]
170 control_sender: Sender<DedicatedWorkerControlMsg>,
171 #[ignore_malloc_size_of = "mozjs"]
173 #[no_trace]
174 context: ThreadSafeJSContext,
175}
176
177impl Drop for AutoCloseWorker {
178 fn drop(&mut self) {
180 self.closing.store(true, Ordering::SeqCst);
182
183 if self
184 .control_sender
185 .send(DedicatedWorkerControlMsg::Exit)
186 .is_err()
187 {
188 warn!("Couldn't send an exit message to a dedicated worker.");
189 }
190
191 self.context.request_interrupt_callback();
192
193 if self
196 .join_handle
197 .take()
198 .expect("No handle to join on worker.")
199 .join()
200 .is_err()
201 {
202 warn!("Failed to join on dedicated worker thread.");
203 }
204 }
205}
206
207#[dom_struct]
208pub(crate) struct GlobalScope {
209 eventtarget: EventTarget,
210 crypto: MutNullableDom<Crypto>,
211
212 task_manager: OnceCell<TaskManager>,
214
215 message_port_state: DomRefCell<MessagePortState>,
217
218 broadcast_channel_state: DomRefCell<BroadcastChannelState>,
220
221 blob_state: DomRefCell<HashMapTracedValues<BlobId, BlobInfo, FxBuildHasher>>,
223
224 registration_map: DomRefCell<
226 HashMapTracedValues<
227 ServiceWorkerRegistrationId,
228 Dom<ServiceWorkerRegistration>,
229 FxBuildHasher,
230 >,
231 >,
232
233 cookie_store: MutNullableDom<CookieStore>,
235
236 indexeddb: MutNullableDom<IDBFactory>,
238
239 worker_map: DomRefCell<HashMapTracedValues<ServiceWorkerId, Dom<ServiceWorker>, FxBuildHasher>>,
241
242 #[no_trace]
244 pipeline_id: PipelineId,
245
246 devtools_wants_updates: Cell<bool>,
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 || {
613 let global = context.root();
614 global.try_complete_disentanglement(port_id, CanGc::note());
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(stream: &ReadableStream, bytes: Fallible<Vec<u8>>, can_gc: CanGc) {
630 match bytes {
631 Ok(b) => {
632 stream.enqueue_native(b, can_gc);
633 },
634 Err(e) => {
635 stream.error_native(e, can_gc);
636 },
637 }
638}
639
640fn stream_handle_eof(stream: &ReadableStream, can_gc: CanGc) {
642 stream.controller_close_native(can_gc);
643}
644
645impl FileListener {
646 fn handle(&mut self, msg: FileManagerResult<ReadFileProgress>) {
647 match msg {
648 Ok(ReadFileProgress::Meta(blob_buf)) => match self.state.take() {
649 Some(FileListenerState::Empty(target)) => {
650 let bytes = if let FileListenerTarget::Stream(ref trusted_stream) = target {
651 let trusted = trusted_stream.clone();
652
653 let task = task!(enqueue_stream_chunk: move || {
654 let stream = trusted.root();
655 stream_handle_incoming(&stream, Ok(blob_buf.bytes), CanGc::note());
656 });
657 self.task_source.queue(task);
658
659 Vec::with_capacity(0)
660 } else {
661 blob_buf.bytes
662 };
663
664 self.state = Some(FileListenerState::Receiving(bytes, target));
665 },
666 _ => panic!(
667 "Unexpected FileListenerState when receiving ReadFileProgress::Meta msg."
668 ),
669 },
670 Ok(ReadFileProgress::Partial(mut bytes_in)) => match self.state.take() {
671 Some(FileListenerState::Receiving(mut bytes, target)) => {
672 if let FileListenerTarget::Stream(ref trusted_stream) = target {
673 let trusted = trusted_stream.clone();
674
675 let task = task!(enqueue_stream_chunk: move || {
676 let stream = trusted.root();
677 stream_handle_incoming(&stream, Ok(bytes_in), CanGc::note());
678 });
679
680 self.task_source.queue(task);
681 } else {
682 bytes.append(&mut bytes_in);
683 };
684
685 self.state = Some(FileListenerState::Receiving(bytes, target));
686 },
687 _ => panic!(
688 "Unexpected FileListenerState when receiving ReadFileProgress::Partial msg."
689 ),
690 },
691 Ok(ReadFileProgress::EOF) => match self.state.take() {
692 Some(FileListenerState::Receiving(bytes, target)) => match target {
693 FileListenerTarget::Promise(trusted_promise, callback) => {
694 let task = task!(resolve_promise: move |cx| {
695 let promise = trusted_promise.root();
696 let mut realm = enter_auto_realm(cx, &*promise.global());
697 callback(&mut realm, promise, Ok(bytes));
698 });
699
700 self.task_source.queue(task);
701 },
702 FileListenerTarget::Stream(trusted_stream) => {
703 let task = task!(enqueue_stream_chunk: move || {
704 let stream = trusted_stream.root();
705 stream_handle_eof(&stream, CanGc::note());
706 });
707
708 self.task_source.queue(task);
709 },
710 },
711 _ => {
712 panic!("Unexpected FileListenerState when receiving ReadFileProgress::EOF msg.")
713 },
714 },
715 Err(_) => match self.state.take() {
716 Some(FileListenerState::Receiving(_, target)) |
717 Some(FileListenerState::Empty(target)) => {
718 let error = Err(Error::Network(None));
719
720 match target {
721 FileListenerTarget::Promise(trusted_promise, callback) => {
722 self.task_source.queue(task!(reject_promise: move |cx| {
723 let promise = trusted_promise.root();
724 let mut realm = enter_auto_realm(cx, &*promise.global());
725 callback(&mut realm, promise, error);
726 }));
727 },
728 FileListenerTarget::Stream(trusted_stream) => {
729 self.task_source.queue(task!(error_stream: move || {
730 let stream = trusted_stream.root();
731 stream_handle_incoming(&stream, error, CanGc::note());
732 }));
733 },
734 }
735 },
736 _ => panic!("Unexpected FileListenerState when receiving Err msg."),
737 },
738 }
739 }
740}
741
742impl GlobalScope {
743 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
747 if let Some(window) = self.downcast::<Window>() {
748 return Some(window.webview_id());
749 }
750 self.downcast::<DedicatedWorkerGlobalScope>()
754 .map(DedicatedWorkerGlobalScope::webview_id)
755 }
756
757 #[allow(clippy::too_many_arguments)]
758 pub(crate) fn new_inherited(
759 pipeline_id: PipelineId,
760 devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
761 mem_profiler_chan: profile_mem::ProfilerChan,
762 time_profiler_chan: profile_time::ProfilerChan,
763 script_to_constellation_chan: ScriptToConstellationChan,
764 script_to_embedder_chan: ScriptToEmbedderChan,
765 resource_threads: ResourceThreads,
766 storage_threads: StorageThreads,
767 origin: MutableOrigin,
768 creation_url: ServoUrl,
769 top_level_creation_url: Option<ServoUrl>,
770 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
771 inherited_secure_context: Option<bool>,
772 unminify_js: bool,
773 font_context: Option<Arc<FontContext>>,
774 ) -> Self {
775 Self {
776 task_manager: Default::default(),
777 message_port_state: DomRefCell::new(MessagePortState::UnManaged),
778 broadcast_channel_state: DomRefCell::new(BroadcastChannelState::UnManaged),
779 blob_state: Default::default(),
780 eventtarget: EventTarget::new_inherited(),
781 crypto: Default::default(),
782 registration_map: DomRefCell::new(HashMapTracedValues::new_fx()),
783 cookie_store: Default::default(),
784 indexeddb: Default::default(),
785 worker_map: DomRefCell::new(HashMapTracedValues::new_fx()),
786 pipeline_id,
787 devtools_wants_updates: Default::default(),
788 console_timers: DomRefCell::new(Default::default()),
789 module_map: DomRefCell::new(Default::default()),
790 devtools_chan,
791 mem_profiler_chan,
792 time_profiler_chan,
793 script_to_constellation_chan,
794 script_to_embedder_chan,
795 in_error_reporting_mode: Default::default(),
796 resource_threads,
797 storage_threads,
798 timers: OnceCell::default(),
799 origin,
800 creation_url: DomRefCell::new(creation_url),
801 top_level_creation_url,
802 permission_state_invocation_results: Default::default(),
803 list_auto_close_worker: Default::default(),
804 event_source_tracker: DOMTracker::new(),
805 abort_signal_dependents: Default::default(),
806 uncaught_rejections: Default::default(),
807 consumed_rejections: Default::default(),
808 #[cfg(feature = "webgpu")]
809 gpu_id_hub,
810 #[cfg(feature = "webgpu")]
811 gpu_devices: DomRefCell::new(HashMapTracedValues::new_fx()),
812 frozen_supported_performance_entry_types: CachedFrozenArray::new(),
813 https_state: Cell::new(HttpsState::None),
814 console_group_stack: DomRefCell::new(Vec::new()),
815 console_count_map: Default::default(),
816 inherited_secure_context,
817 unminified_js_dir: unminify_js.then(|| unminified_path("unminified-js")),
818 byte_length_queuing_strategy_size_function: OnceCell::new(),
819 count_queuing_strategy_size_function: OnceCell::new(),
820 notification_permission_request_callback_map: Default::default(),
821 import_map: Default::default(),
822 resolved_module_set: Default::default(),
823 font_context,
824 fetch_group: Default::default(),
825 }
826 }
827
828 fn port_router_id(&self) -> Option<MessagePortRouterId> {
830 if let MessagePortState::Managed(id, _message_ports) = &*self.message_port_state.borrow() {
831 Some(*id)
832 } else {
833 None
834 }
835 }
836
837 fn is_managing_port(&self, port_id: &MessagePortId) -> bool {
839 if let MessagePortState::Managed(_router_id, message_ports) =
840 &*self.message_port_state.borrow()
841 {
842 return message_ports.contains_key(port_id);
843 }
844 false
845 }
846
847 fn timers(&self) -> &OneshotTimers {
848 self.timers.get_or_init(|| OneshotTimers::new(self))
849 }
850
851 pub(crate) fn font_context(&self) -> Option<&Arc<FontContext>> {
852 self.font_context.as_ref()
853 }
854
855 #[allow(clippy::too_many_arguments)]
857 pub(crate) fn get_serviceworker_registration(
858 &self,
859 script_url: &ServoUrl,
860 scope: &ServoUrl,
861 registration_id: ServiceWorkerRegistrationId,
862 installing_worker: Option<ServiceWorkerId>,
863 _waiting_worker: Option<ServiceWorkerId>,
864 _active_worker: Option<ServiceWorkerId>,
865 can_gc: CanGc,
866 ) -> DomRoot<ServiceWorkerRegistration> {
867 let mut registrations = self.registration_map.borrow_mut();
869
870 if let Some(registration) = registrations.get(®istration_id) {
871 return DomRoot::from_ref(&**registration);
873 }
874
875 let new_registration =
877 ServiceWorkerRegistration::new(self, scope.clone(), registration_id, can_gc);
878
879 if let Some(worker_id) = installing_worker {
881 let worker = self.get_serviceworker(script_url, scope, worker_id, can_gc);
882 new_registration.set_installing(&worker);
883 }
884
885 registrations.insert(registration_id, Dom::from_ref(&*new_registration));
891
892 new_registration
894 }
895
896 pub(crate) fn get_serviceworker(
898 &self,
899 script_url: &ServoUrl,
900 scope: &ServoUrl,
901 worker_id: ServiceWorkerId,
902 can_gc: CanGc,
903 ) -> DomRoot<ServiceWorker> {
904 let mut workers = self.worker_map.borrow_mut();
906
907 if let Some(worker) = workers.get(&worker_id) {
908 DomRoot::from_ref(&**worker)
910 } else {
911 let new_worker =
914 ServiceWorker::new(self, script_url.clone(), scope.clone(), worker_id, can_gc);
915
916 workers.insert(worker_id, Dom::from_ref(&*new_worker));
918
919 new_worker
921 }
922 }
923
924 fn complete_port_transfer(
926 &self,
927 cx: &mut js::context::JSContext,
928 port_id: MessagePortId,
929 tasks: VecDeque<PortMessageTask>,
930 disentangled: bool,
931 ) {
932 let should_start = if let MessagePortState::Managed(_id, message_ports) =
933 &mut *self.message_port_state.borrow_mut()
934 {
935 match message_ports.get_mut(&port_id) {
936 None => {
937 panic!("complete_port_transfer called for an unknown port.");
938 },
939 Some(managed_port) => {
940 if managed_port.pending {
941 panic!("CompleteTransfer msg received for a pending port.");
942 }
943 if let Some(port_impl) = managed_port.port_impl.as_mut() {
944 port_impl.complete_transfer(tasks);
945 if disentangled {
946 port_impl.disentangle();
947 managed_port.dom_port.disentangle();
948 }
949 port_impl.enabled()
950 } else {
951 panic!("managed-port has no port-impl.");
952 }
953 },
954 }
955 } else {
956 panic!("complete_port_transfer called for an unknown port.");
957 };
958 if should_start {
959 self.start_message_port(cx, &port_id);
960 }
961 }
962
963 fn try_complete_disentanglement(&self, port_id: MessagePortId, can_gc: CanGc) {
966 let dom_port = if let MessagePortState::Managed(_id, message_ports) =
967 &mut *self.message_port_state.borrow_mut()
968 {
969 if let Some(managed_port) = message_ports.get_mut(&port_id) {
970 if managed_port.pending {
971 unreachable!("CompleteDisentanglement msg received for a pending port.");
972 }
973 let port_impl = managed_port
974 .port_impl
975 .as_mut()
976 .expect("managed-port has no port-impl.");
977 port_impl.disentangle();
978 managed_port.dom_port.as_rooted()
979 } else {
980 return;
984 }
985 } else {
986 return;
987 };
988
989 dom_port.upcast().fire_event(atom!("close"), can_gc);
991
992 let res = self.script_to_constellation_chan().send(
993 ScriptToConstellationMessage::DisentanglePorts(port_id, None),
994 );
995 if res.is_err() {
996 warn!("Sending DisentanglePorts failed");
997 }
998 }
999
1000 pub(crate) fn perform_a_dom_garbage_collection_checkpoint(&self) {
1002 self.perform_a_message_port_garbage_collection_checkpoint();
1003 self.perform_a_blob_garbage_collection_checkpoint();
1004 self.perform_a_broadcast_channel_garbage_collection_checkpoint();
1005 self.perform_an_abort_signal_garbage_collection_checkpoint();
1006 }
1007
1008 pub(crate) fn remove_web_messaging_and_dedicated_workers_infra(&self) {
1011 self.remove_message_ports_router();
1012 self.remove_broadcast_channel_router();
1013
1014 self.list_auto_close_worker
1018 .borrow_mut()
1019 .drain(0..)
1020 .for_each(drop);
1021 }
1022
1023 fn remove_message_ports_router(&self) {
1026 if let MessagePortState::Managed(router_id, _message_ports) =
1027 &*self.message_port_state.borrow()
1028 {
1029 let _ = self.script_to_constellation_chan().send(
1030 ScriptToConstellationMessage::RemoveMessagePortRouter(*router_id),
1031 );
1032 }
1033 *self.message_port_state.borrow_mut() = MessagePortState::UnManaged;
1034 }
1035
1036 fn remove_broadcast_channel_router(&self) {
1039 if let BroadcastChannelState::Managed(router_id, _channels) =
1040 &*self.broadcast_channel_state.borrow()
1041 {
1042 let _ = self.script_to_constellation_chan().send(
1043 ScriptToConstellationMessage::RemoveBroadcastChannelRouter(
1044 *router_id,
1045 self.origin().immutable().clone(),
1046 ),
1047 );
1048 }
1049 *self.broadcast_channel_state.borrow_mut() = BroadcastChannelState::UnManaged;
1050 }
1051
1052 pub(crate) fn disentangle_port(&self, port: &MessagePort, can_gc: CanGc) {
1054 let initiator_port = port.message_port_id();
1055 let Some(other_port) = port.disentangle() else {
1057 return;
1061 };
1062
1063 let dom_port = if let MessagePortState::Managed(_id, message_ports) =
1066 &mut *self.message_port_state.borrow_mut()
1067 {
1068 let mut dom_port = None;
1069 for port_id in &[initiator_port, &other_port] {
1070 match message_ports.get_mut(port_id) {
1071 None => {
1072 continue;
1073 },
1074 Some(managed_port) => {
1075 let port_impl = managed_port
1076 .port_impl
1077 .as_mut()
1078 .expect("managed-port has no port-impl.");
1079 managed_port.dom_port.disentangle();
1080 port_impl.disentangle();
1081
1082 if **port_id == other_port {
1083 dom_port = Some(managed_port.dom_port.as_rooted())
1084 }
1085 },
1086 }
1087 }
1088 dom_port
1089 } else {
1090 panic!("disentangle_port called on a global not managing any ports.");
1091 };
1092
1093 if let Some(dom_port) = dom_port {
1096 dom_port.upcast().fire_event(atom!("close"), can_gc);
1097 }
1098
1099 let chan = self.script_to_constellation_chan().clone();
1100 let initiator_port = *initiator_port;
1101 self.task_manager()
1102 .port_message_queue()
1103 .queue(task!(post_message: move || {
1104 let res = chan.send(ScriptToConstellationMessage::DisentanglePorts(initiator_port, Some(other_port)));
1107 if res.is_err() {
1108 warn!("Sending DisentanglePorts failed");
1109 }
1110 }));
1111 }
1112
1113 pub(crate) fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) {
1115 if let MessagePortState::Managed(_id, message_ports) =
1116 &mut *self.message_port_state.borrow_mut()
1117 {
1118 for (port_id, entangled_id) in &[(port1, port2), (port2, port1)] {
1119 match message_ports.get_mut(port_id) {
1120 None => {
1121 return warn!("entangled_ports called on a global not managing the port.");
1122 },
1123 Some(managed_port) => {
1124 if let Some(port_impl) = managed_port.port_impl.as_mut() {
1125 managed_port.dom_port.entangle(*entangled_id);
1126 port_impl.entangle(*entangled_id);
1127 } else {
1128 panic!("managed-port has no port-impl.");
1129 }
1130 },
1131 }
1132 }
1133 } else {
1134 panic!("entangled_ports called on a global not managing any ports.");
1135 }
1136
1137 let _ = self
1138 .script_to_constellation_chan()
1139 .send(ScriptToConstellationMessage::EntanglePorts(port1, port2));
1140 }
1141
1142 pub(crate) fn mark_port_as_transferred(&self, port_id: &MessagePortId) -> MessagePortImpl {
1144 if let MessagePortState::Managed(_id, message_ports) =
1145 &mut *self.message_port_state.borrow_mut()
1146 {
1147 let mut port_impl = message_ports
1148 .remove(port_id)
1149 .map(|ref mut managed_port| {
1150 managed_port
1151 .port_impl
1152 .take()
1153 .expect("Managed port doesn't have a port-impl.")
1154 })
1155 .expect("mark_port_as_transferred called on a global not managing the port.");
1156 port_impl.set_has_been_shipped();
1157 let _ = self
1158 .script_to_constellation_chan()
1159 .send(ScriptToConstellationMessage::MessagePortShipped(*port_id));
1160 port_impl
1161 } else {
1162 panic!("mark_port_as_transferred called on a global not managing any ports.");
1163 }
1164 }
1165
1166 pub(crate) fn start_message_port(
1168 &self,
1169 cx: &mut js::context::JSContext,
1170 port_id: &MessagePortId,
1171 ) {
1172 let (message_buffer, dom_port) = if let MessagePortState::Managed(_id, message_ports) =
1173 &mut *self.message_port_state.borrow_mut()
1174 {
1175 let (message_buffer, dom_port) = match message_ports.get_mut(port_id) {
1176 None => panic!("start_message_port called on a unknown port."),
1177 Some(managed_port) => {
1178 if let Some(port_impl) = managed_port.port_impl.as_mut() {
1179 (port_impl.start(), managed_port.dom_port.as_rooted())
1180 } else {
1181 panic!("managed-port has no port-impl.");
1182 }
1183 },
1184 };
1185 (message_buffer, dom_port)
1186 } else {
1187 return warn!("start_message_port called on a global not managing any ports.");
1188 };
1189 if let Some(message_buffer) = message_buffer {
1190 for task in message_buffer {
1191 self.route_task_to_port(cx, *port_id, task);
1192 }
1193 if dom_port.disentangled() {
1194 dom_port
1197 .upcast()
1198 .fire_event(atom!("close"), CanGc::from_cx(cx));
1199
1200 let res = self.script_to_constellation_chan().send(
1201 ScriptToConstellationMessage::DisentanglePorts(*port_id, None),
1202 );
1203 if res.is_err() {
1204 warn!("Sending DisentanglePorts failed");
1205 }
1206 }
1207 }
1208 }
1209
1210 pub(crate) fn close_message_port(&self, port_id: &MessagePortId) {
1212 if let MessagePortState::Managed(_id, message_ports) =
1213 &mut *self.message_port_state.borrow_mut()
1214 {
1215 match message_ports.get_mut(port_id) {
1216 None => panic!("close_message_port called on an unknown port."),
1217 Some(managed_port) => {
1218 if let Some(port_impl) = managed_port.port_impl.as_mut() {
1219 port_impl.close();
1220 managed_port.explicitly_closed = true;
1221 } else {
1222 panic!("managed-port has no port-impl.");
1223 }
1224 },
1225 };
1226 } else {
1227 warn!("close_message_port called on a global not managing any ports.")
1228 }
1229 }
1230
1231 pub(crate) fn post_messageport_msg(&self, port_id: MessagePortId, task: PortMessageTask) {
1234 if let MessagePortState::Managed(_id, message_ports) =
1235 &mut *self.message_port_state.borrow_mut()
1236 {
1237 let entangled_port = match message_ports.get_mut(&port_id) {
1238 None => panic!("post_messageport_msg called on an unknown port."),
1239 Some(managed_port) => {
1240 if let Some(port_impl) = managed_port.port_impl.as_mut() {
1241 port_impl.entangled_port_id()
1242 } else {
1243 panic!("managed-port has no port-impl.");
1244 }
1245 },
1246 };
1247 if let Some(entangled_id) = entangled_port {
1248 let this = Trusted::new(self);
1250 self.task_manager()
1251 .port_message_queue()
1252 .queue(task!(post_message: move |cx| {
1253 let global = this.root();
1254 global.route_task_to_port(cx, entangled_id, task);
1257 }));
1258 }
1259 } else {
1260 warn!("post_messageport_msg called on a global not managing any ports.");
1261 }
1262 }
1263
1264 fn re_route_port_task(&self, port_id: MessagePortId, task: PortMessageTask) {
1267 let _ = self.script_to_constellation_chan().send(
1268 ScriptToConstellationMessage::RerouteMessagePort(port_id, task),
1269 );
1270 }
1271
1272 pub(crate) fn schedule_broadcast(&self, msg: BroadcastChannelMsg, channel_id: &Uuid) {
1275 self.broadcast_message_event(msg.clone(), Some(channel_id));
1277
1278 if let BroadcastChannelState::Managed(router_id, _) =
1279 &*self.broadcast_channel_state.borrow()
1280 {
1281 let _ = self.script_to_constellation_chan().send(
1286 ScriptToConstellationMessage::ScheduleBroadcast(*router_id, msg),
1287 );
1288 } else {
1289 panic!("Attemps to broadcast a message via global not managing any channels.");
1290 }
1291 }
1292
1293 pub(crate) fn broadcast_message_event(
1296 &self,
1297 event: BroadcastChannelMsg,
1298 channel_id: Option<&Uuid>,
1299 ) {
1300 if let BroadcastChannelState::Managed(_, channels) = &*self.broadcast_channel_state.borrow()
1301 {
1302 let BroadcastChannelMsg {
1303 data,
1304 origin,
1305 channel_name,
1306 } = event;
1307
1308 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
1312 if worker.is_closing() {
1313 return;
1314 }
1315 }
1316
1317 if let Some(window) = self.downcast::<Window>() {
1319 if !window.Document().is_fully_active() {
1320 return;
1321 }
1322 }
1323
1324 if let Some(channels) = channels.get(&channel_name.into()) {
1326 channels
1327 .iter()
1328 .filter(|channel| {
1329 if let Some(id) = channel_id {
1332 channel.id() != id
1333 } else {
1334 true
1335 }
1336 })
1337 .map(|channel| DomRoot::from_ref(&**channel))
1338 .for_each(|channel| {
1341 let data = data.clone_for_broadcast();
1342 let origin = origin.clone();
1343
1344 let channel = Trusted::new(&*channel);
1347 let global = Trusted::new(self);
1348 self.task_manager().dom_manipulation_task_source().queue(
1349 task!(process_pending_port_messages: move || {
1350 let destination = channel.root();
1351 let global = global.root();
1352
1353 if destination.closed() {
1355 return;
1356 }
1357
1358 rooted!(in(*GlobalScope::get_cx()) let mut message = UndefinedValue());
1359
1360 if let Ok(ports) = structuredclone::read(&global, data, message.handle_mut(), CanGc::note()) {
1362 MessageEvent::dispatch_jsval(
1364 destination.upcast(),
1365 &global,
1366 message.handle(),
1367 Some(&origin.ascii_serialization()),
1368 None,
1369 ports,
1370 CanGc::note()
1371 );
1372 } else {
1373 MessageEvent::dispatch_error(destination.upcast(), &global, CanGc::note());
1375 }
1376 })
1377 );
1378 });
1379 }
1380 }
1381 }
1382
1383 pub(crate) fn encoding_parse_a_url(&self, url: &str) -> Result<ServoUrl, url::ParseError> {
1385 if let Some(window) = self.downcast::<Window>() {
1386 return window.Document().encoding_parse_a_url(url);
1387 }
1388
1389 let base = self.api_base_url();
1391 base.join(url)
1392 }
1393
1394 pub(crate) fn note_cross_realm_transform_readable(
1398 &self,
1399 cross_realm_transform_readable: &CrossRealmTransformReadable,
1400 port_id: &MessagePortId,
1401 ) {
1402 let MessagePortState::Managed(_id, message_ports) =
1403 &mut *self.message_port_state.borrow_mut()
1404 else {
1405 unreachable!(
1406 "Cross realm transform readable must be called on a global managing ports"
1407 );
1408 };
1409
1410 let Some(managed_port) = message_ports.get_mut(port_id) else {
1411 unreachable!("Cross realm transform readable must match a managed port");
1412 };
1413
1414 managed_port.cross_realm_transform = Some(CrossRealmTransform::Readable(
1415 cross_realm_transform_readable.clone(),
1416 ));
1417 }
1418
1419 pub(crate) fn note_cross_realm_transform_writable(
1423 &self,
1424 cross_realm_transform_writable: &CrossRealmTransformWritable,
1425 port_id: &MessagePortId,
1426 ) {
1427 let MessagePortState::Managed(_id, message_ports) =
1428 &mut *self.message_port_state.borrow_mut()
1429 else {
1430 unreachable!(
1431 "Cross realm transform writable must be called on a global managing ports"
1432 );
1433 };
1434
1435 let Some(managed_port) = message_ports.get_mut(port_id) else {
1436 unreachable!("Cross realm transform writable must match a managed port");
1437 };
1438
1439 managed_port.cross_realm_transform = Some(CrossRealmTransform::Writable(
1440 cross_realm_transform_writable.clone(),
1441 ));
1442 }
1443
1444 fn route_task_to_port(
1447 &self,
1448 cx: &mut js::context::JSContext,
1449 port_id: MessagePortId,
1450 task: PortMessageTask,
1451 ) {
1452 rooted!(&in(cx) let mut cross_realm_transform = None);
1453
1454 let should_dispatch = if let MessagePortState::Managed(_id, message_ports) =
1455 &mut *self.message_port_state.borrow_mut()
1456 {
1457 if !message_ports.contains_key(&port_id) {
1458 self.re_route_port_task(port_id, task);
1459 return;
1460 }
1461 match message_ports.get_mut(&port_id) {
1462 None => panic!("route_task_to_port called for an unknown port."),
1463 Some(managed_port) => {
1464 if let Some(port_impl) = managed_port.port_impl.as_mut() {
1467 let to_dispatch = port_impl.handle_incoming(task).map(|to_dispatch| {
1468 (DomRoot::from_ref(&*managed_port.dom_port), to_dispatch)
1469 });
1470 cross_realm_transform.set(managed_port.cross_realm_transform.clone());
1471 to_dispatch
1472 } else {
1473 panic!("managed-port has no port-impl.");
1474 }
1475 },
1476 }
1477 } else {
1478 self.re_route_port_task(port_id, task);
1479 return;
1480 };
1481
1482 if let Some((dom_port, PortMessageTask { origin, data })) = should_dispatch {
1487 let message_event_target = dom_port.upcast();
1489
1490 rooted!(&in(cx) let mut message_clone = UndefinedValue());
1496
1497 let mut realm = enter_auto_realm(cx, self);
1498 let cx = &mut realm.current_realm();
1499
1500 run_a_script::<DomTypeHolder, _>(self, || {
1504 if let Ok(ports) = structuredclone::read(
1510 self,
1511 data,
1512 message_clone.handle_mut(),
1513 CanGc::from_cx(cx),
1514 ) {
1515 if let Some(transform) = cross_realm_transform.deref().as_ref() {
1517 match transform {
1518 CrossRealmTransform::Readable(readable) => {
1521 readable.handle_message(
1522 cx,
1523 self,
1524 &dom_port,
1525 message_clone.handle(),
1526 );
1527 },
1528 CrossRealmTransform::Writable(writable) => {
1531 writable.handle_message(cx, self, message_clone.handle());
1532 },
1533 }
1534 } else {
1535 MessageEvent::dispatch_jsval(
1540 message_event_target,
1541 self,
1542 message_clone.handle(),
1543 Some(&origin.ascii_serialization()),
1544 None,
1545 ports,
1546 CanGc::from_cx(cx),
1547 );
1548 }
1549 } else if let Some(transform) = cross_realm_transform.deref().as_ref() {
1550 match transform {
1551 CrossRealmTransform::Readable(readable) => {
1554 readable.handle_error(cx, self, &dom_port);
1555 },
1556 CrossRealmTransform::Writable(writable) => {
1559 writable.handle_error(cx, self, &dom_port);
1560 },
1561 }
1562 } else {
1563 MessageEvent::dispatch_error(message_event_target, self, CanGc::from_cx(cx));
1567 }
1568 });
1569 }
1570 }
1571
1572 pub(crate) fn maybe_add_pending_ports(&self) {
1575 if let MessagePortState::Managed(router_id, message_ports) =
1576 &mut *self.message_port_state.borrow_mut()
1577 {
1578 let to_be_added: Vec<MessagePortId> = message_ports
1579 .iter()
1580 .filter_map(|(id, managed_port)| {
1581 if managed_port.pending {
1582 Some(*id)
1583 } else {
1584 None
1585 }
1586 })
1587 .collect();
1588 for id in to_be_added.iter() {
1589 let managed_port = message_ports
1590 .get_mut(id)
1591 .expect("Collected port-id to match an entry");
1592 if !managed_port.pending {
1593 panic!("Only pending ports should be found in to_be_added")
1594 }
1595 managed_port.pending = false;
1596 }
1597 let _ = self.script_to_constellation_chan().send(
1598 ScriptToConstellationMessage::CompleteMessagePortTransfer(*router_id, to_be_added),
1599 );
1600 } else {
1601 warn!("maybe_add_pending_ports called on a global not managing any ports.");
1602 }
1603 }
1604
1605 pub(crate) fn perform_a_message_port_garbage_collection_checkpoint(&self) {
1607 let is_empty = if let MessagePortState::Managed(_id, message_ports) =
1608 &mut *self.message_port_state.borrow_mut()
1609 {
1610 let to_be_removed: Vec<MessagePortId> = message_ports
1611 .iter()
1612 .filter_map(|(id, managed_port)| {
1613 if managed_port.explicitly_closed {
1614 Some(*id)
1615 } else {
1616 None
1617 }
1618 })
1619 .collect();
1620 for id in to_be_removed {
1621 message_ports.remove(&id);
1622 }
1623 message_ports.is_empty()
1627 } else {
1628 false
1629 };
1630 if is_empty {
1631 self.remove_message_ports_router();
1632 }
1633 }
1634
1635 pub(crate) fn perform_a_broadcast_channel_garbage_collection_checkpoint(&self) {
1639 let is_empty = if let BroadcastChannelState::Managed(router_id, channels) =
1640 &mut *self.broadcast_channel_state.borrow_mut()
1641 {
1642 channels.retain(|name, ref mut channels| {
1643 channels.retain(|chan| !chan.closed());
1644 if channels.is_empty() {
1645 let _ = self.script_to_constellation_chan().send(
1646 ScriptToConstellationMessage::RemoveBroadcastChannelNameInRouter(
1647 *router_id,
1648 name.to_string(),
1649 self.origin().immutable().clone(),
1650 ),
1651 );
1652 false
1653 } else {
1654 true
1655 }
1656 });
1657 channels.is_empty()
1658 } else {
1659 false
1660 };
1661 if is_empty {
1662 self.remove_broadcast_channel_router();
1663 }
1664 }
1665
1666 pub(crate) fn register_dependent_abort_signal(&self, signal: &AbortSignal) {
1669 self.abort_signal_dependents
1670 .borrow_mut()
1671 .insert(Dom::from_ref(signal));
1672 }
1673
1674 pub(crate) fn perform_an_abort_signal_garbage_collection_checkpoint(&self) {
1676 let mut set = self.abort_signal_dependents.borrow_mut();
1677
1678 set.retain(|dom_signal| dom_signal.must_keep_alive_for_gc());
1679 }
1680
1681 pub(crate) fn track_broadcast_channel(&self, dom_channel: &BroadcastChannel) {
1683 let mut current_state = self.broadcast_channel_state.borrow_mut();
1684
1685 if let BroadcastChannelState::UnManaged = &*current_state {
1686 let (broadcast_control_sender, broadcast_control_receiver) =
1688 ipc::channel().expect("ipc channel failure");
1689 let context = Trusted::new(self);
1690 let listener = BroadcastListener {
1691 task_source: self.task_manager().dom_manipulation_task_source().into(),
1692 context,
1693 };
1694 ROUTER.add_typed_route(
1695 broadcast_control_receiver,
1696 Box::new(move |message| match message {
1697 Ok(msg) => listener.handle(msg),
1698 Err(err) => warn!("Error receiving a BroadcastChannelMsg: {:?}", err),
1699 }),
1700 );
1701 let router_id = BroadcastChannelRouterId::new();
1702 *current_state = BroadcastChannelState::Managed(router_id, HashMap::new());
1703 let _ = self.script_to_constellation_chan().send(
1704 ScriptToConstellationMessage::NewBroadcastChannelRouter(
1705 router_id,
1706 broadcast_control_sender,
1707 self.origin().immutable().clone(),
1708 ),
1709 );
1710 }
1711
1712 if let BroadcastChannelState::Managed(router_id, channels) = &mut *current_state {
1713 let entry = channels.entry(dom_channel.Name()).or_insert_with(|| {
1714 let _ = self.script_to_constellation_chan().send(
1715 ScriptToConstellationMessage::NewBroadcastChannelNameInRouter(
1716 *router_id,
1717 dom_channel.Name().to_string(),
1718 self.origin().immutable().clone(),
1719 ),
1720 );
1721 VecDeque::new()
1722 });
1723 entry.push_back(Dom::from_ref(dom_channel));
1724 } else {
1725 panic!("track_broadcast_channel should have first switched the state to managed.");
1726 }
1727 }
1728
1729 pub(crate) fn track_message_port(
1731 &self,
1732 dom_port: &MessagePort,
1733 port_impl: Option<MessagePortImpl>,
1734 ) {
1735 let mut current_state = self.message_port_state.borrow_mut();
1736
1737 if let MessagePortState::UnManaged = &*current_state {
1738 let context = Trusted::new(self);
1740 let listener = MessageListener {
1741 task_source: self.task_manager().port_message_queue().into(),
1742 context,
1743 };
1744
1745 let port_control_callback = GenericCallback::new(move |message| match message {
1746 Ok(msg) => listener.notify(msg),
1747 Err(err) => warn!("Error receiving a MessagePortMsg: {:?}", err),
1748 })
1749 .expect("Could not create callback");
1750 let router_id = MessagePortRouterId::new();
1751 *current_state = MessagePortState::Managed(router_id, HashMapTracedValues::new_fx());
1752 let _ = self.script_to_constellation_chan().send(
1753 ScriptToConstellationMessage::NewMessagePortRouter(
1754 router_id,
1755 port_control_callback,
1756 ),
1757 );
1758 }
1759
1760 if let MessagePortState::Managed(router_id, message_ports) = &mut *current_state {
1761 if let Some(port_impl) = port_impl {
1762 message_ports.insert(
1766 *dom_port.message_port_id(),
1767 ManagedMessagePort {
1768 port_impl: Some(port_impl),
1769 dom_port: Dom::from_ref(dom_port),
1770 pending: true,
1771 explicitly_closed: false,
1772 cross_realm_transform: None,
1773 },
1774 );
1775
1776 let this = Trusted::new(self);
1779 self.task_manager().port_message_queue().queue(
1780 task!(process_pending_port_messages: move || {
1781 let target_global = this.root();
1782 target_global.maybe_add_pending_ports();
1783 }),
1784 );
1785 } else {
1786 let port_impl = MessagePortImpl::new(*dom_port.message_port_id());
1788 message_ports.insert(
1789 *dom_port.message_port_id(),
1790 ManagedMessagePort {
1791 port_impl: Some(port_impl),
1792 dom_port: Dom::from_ref(dom_port),
1793 pending: false,
1794 explicitly_closed: false,
1795 cross_realm_transform: None,
1796 },
1797 );
1798 let _ = self.script_to_constellation_chan().send(
1799 ScriptToConstellationMessage::NewMessagePort(
1800 *router_id,
1801 *dom_port.message_port_id(),
1802 ),
1803 );
1804 };
1805 } else {
1806 panic!("track_message_port should have first switched the state to managed.");
1807 }
1808 }
1809
1810 pub(crate) fn serialize_blob(&self, blob_id: &BlobId) -> BlobImpl {
1814 let bytes = self
1818 .get_blob_bytes(blob_id)
1819 .expect("Could not read bytes from blob as part of serialization steps.");
1820 let type_string = self.get_blob_type_string(blob_id);
1821
1822 BlobImpl::new_from_bytes(bytes, type_string)
1824 }
1825
1826 fn track_blob_info(&self, blob_info: BlobInfo, blob_id: BlobId) {
1827 self.blob_state.borrow_mut().insert(blob_id, blob_info);
1828 }
1829
1830 pub(crate) fn track_blob(&self, dom_blob: &Blob, blob_impl: BlobImpl) {
1832 let blob_id = blob_impl.blob_id();
1833
1834 let blob_info = BlobInfo {
1835 blob_impl,
1836 tracker: BlobTracker::Blob(WeakRef::new(dom_blob)),
1837 has_url: false,
1838 };
1839
1840 self.track_blob_info(blob_info, blob_id);
1841 }
1842
1843 pub(crate) fn track_file(&self, file: &File, blob_impl: BlobImpl) {
1845 let blob_id = blob_impl.blob_id();
1846
1847 let blob_info = BlobInfo {
1848 blob_impl,
1849 tracker: BlobTracker::File(WeakRef::new(file)),
1850 has_url: false,
1851 };
1852
1853 self.track_blob_info(blob_info, blob_id);
1854 }
1855
1856 fn perform_a_blob_garbage_collection_checkpoint(&self) {
1860 let mut blob_state = self.blob_state.borrow_mut();
1861 blob_state.0.retain(|_id, blob_info| {
1862 let garbage_collected = match &blob_info.tracker {
1863 BlobTracker::File(weak) => weak.root().is_none(),
1864 BlobTracker::Blob(weak) => weak.root().is_none(),
1865 };
1866 if garbage_collected && !blob_info.has_url {
1867 if let BlobData::File(f) = blob_info.blob_impl.blob_data() {
1868 self.decrement_file_ref(f.get_id());
1869 }
1870 false
1871 } else {
1872 true
1873 }
1874 });
1875 }
1876
1877 pub(crate) fn clean_up_all_file_resources(&self) {
1880 self.blob_state
1881 .borrow_mut()
1882 .drain()
1883 .for_each(|(_id, blob_info)| {
1884 if let BlobData::File(f) = blob_info.blob_impl.blob_data() {
1885 self.decrement_file_ref(f.get_id());
1886 }
1887 });
1888 }
1889
1890 fn decrement_file_ref(&self, id: Uuid) {
1891 let origin = self.origin().immutable();
1892
1893 let (tx, rx) = profile_generic_channel::channel(self.time_profiler_chan().clone()).unwrap();
1894
1895 let msg = FileManagerThreadMsg::DecRef(id, origin.clone(), tx);
1896 self.send_to_file_manager(msg);
1897 let _ = rx.recv();
1898 }
1899
1900 pub(crate) fn get_blob_bytes(&self, blob_id: &BlobId) -> Result<Vec<u8>, ()> {
1903 let parent = {
1904 match *self.get_blob_data(blob_id) {
1905 BlobData::Sliced(parent, rel_pos) => Some((parent, rel_pos)),
1906 _ => None,
1907 }
1908 };
1909
1910 match parent {
1911 Some((parent_id, rel_pos)) => self.get_blob_bytes_non_sliced(&parent_id).map(|v| {
1912 let range = rel_pos.to_abs_range(v.len());
1913 v.index(range).to_vec()
1914 }),
1915 None => self.get_blob_bytes_non_sliced(blob_id),
1916 }
1917 }
1918
1919 pub(crate) fn get_blob_data<'a>(&'a self, blob_id: &BlobId) -> Ref<'a, BlobData> {
1924 Ref::map(self.blob_state.borrow(), |blob_state| {
1925 blob_state
1926 .get(blob_id)
1927 .expect("get_blob_impl called for a unknown blob")
1928 .blob_impl
1929 .blob_data()
1930 })
1931 }
1932
1933 fn get_blob_bytes_non_sliced(&self, blob_id: &BlobId) -> Result<Vec<u8>, ()> {
1935 match *self.get_blob_data(blob_id) {
1936 BlobData::File(ref f) => {
1937 let (buffer, is_new_buffer) = match f.get_cache() {
1938 Some(bytes) => (bytes, false),
1939 None => {
1940 let bytes = self.read_file(f.get_id())?;
1941 (bytes, true)
1942 },
1943 };
1944
1945 if is_new_buffer {
1947 f.cache_bytes(buffer.clone());
1948 }
1949
1950 Ok(buffer)
1951 },
1952 BlobData::Memory(ref s) => Ok(s.clone()),
1953 BlobData::Sliced(_, _) => panic!("This blob doesn't have a parent."),
1954 }
1955 }
1956
1957 fn get_blob_bytes_or_file_id(&self, blob_id: &BlobId) -> BlobResult {
1964 let parent = {
1965 match *self.get_blob_data(blob_id) {
1966 BlobData::Sliced(parent, rel_pos) => Some((parent, rel_pos)),
1967 _ => None,
1968 }
1969 };
1970
1971 match parent {
1972 Some((parent_id, rel_pos)) => {
1973 match self.get_blob_bytes_non_sliced_or_file_id(&parent_id) {
1974 BlobResult::Bytes(bytes) => {
1975 let range = rel_pos.to_abs_range(bytes.len());
1976 BlobResult::Bytes(bytes.index(range).to_vec())
1977 },
1978 res => res,
1979 }
1980 },
1981 None => self.get_blob_bytes_non_sliced_or_file_id(blob_id),
1982 }
1983 }
1984
1985 fn get_blob_bytes_non_sliced_or_file_id(&self, blob_id: &BlobId) -> BlobResult {
1991 match *self.get_blob_data(blob_id) {
1992 BlobData::File(ref f) => match f.get_cache() {
1993 Some(bytes) => BlobResult::Bytes(bytes),
1994 None => BlobResult::File(f.get_id(), f.get_size() as usize),
1995 },
1996 BlobData::Memory(ref s) => BlobResult::Bytes(s.clone()),
1997 BlobData::Sliced(_, _) => panic!("This blob doesn't have a parent."),
1998 }
1999 }
2000
2001 pub(crate) fn get_blob_type_string(&self, blob_id: &BlobId) -> String {
2003 let blob_state = self.blob_state.borrow();
2004 let blob_info = blob_state
2005 .get(blob_id)
2006 .expect("get_blob_type_string called for a unknown blob.");
2007 blob_info.blob_impl.type_string()
2008 }
2009
2010 pub(crate) fn get_blob_size(&self, blob_id: &BlobId) -> u64 {
2012 let parent = {
2013 match *self.get_blob_data(blob_id) {
2014 BlobData::Sliced(parent, rel_pos) => Some((parent, rel_pos)),
2015 _ => None,
2016 }
2017 };
2018 match parent {
2019 Some((parent_id, rel_pos)) => {
2020 let parent_size = match *self.get_blob_data(&parent_id) {
2021 BlobData::File(ref f) => f.get_size(),
2022 BlobData::Memory(ref v) => v.len() as u64,
2023 BlobData::Sliced(_, _) => panic!("Blob ancestry should be only one level."),
2024 };
2025 rel_pos.to_abs_range(parent_size as usize).len() as u64
2026 },
2027 None => match *self.get_blob_data(blob_id) {
2028 BlobData::File(ref f) => f.get_size(),
2029 BlobData::Memory(ref v) => v.len() as u64,
2030 BlobData::Sliced(_, _) => {
2031 panic!("It was previously checked that this blob does not have a parent.")
2032 },
2033 },
2034 }
2035 }
2036
2037 pub(crate) fn get_blob_url_id(&self, blob_id: &BlobId) -> Uuid {
2038 let mut blob_state = self.blob_state.borrow_mut();
2039 let parent = {
2040 let blob_info = blob_state
2041 .get_mut(blob_id)
2042 .expect("get_blob_url_id called for a unknown blob.");
2043
2044 blob_info.has_url = true;
2046
2047 match blob_info.blob_impl.blob_data() {
2048 BlobData::Sliced(parent, rel_pos) => Some((*parent, *rel_pos)),
2049 _ => None,
2050 }
2051 };
2052 match parent {
2053 Some((parent_id, rel_pos)) => {
2054 let parent_info = blob_state
2055 .get_mut(&parent_id)
2056 .expect("Parent of blob whose url is requested is unknown.");
2057 let parent_file_id = self.promote(parent_info, false);
2058 let parent_size = match parent_info.blob_impl.blob_data() {
2059 BlobData::File(f) => f.get_size(),
2060 BlobData::Memory(v) => v.len() as u64,
2061 BlobData::Sliced(_, _) => panic!("Blob ancestry should be only one level."),
2062 };
2063 let parent_size = rel_pos.to_abs_range(parent_size as usize).len() as u64;
2064 let blob_info = blob_state
2065 .get_mut(blob_id)
2066 .expect("Blob whose url is requested is unknown.");
2067 self.create_sliced_url_id(blob_info, &parent_file_id, &rel_pos, parent_size)
2068 },
2069 None => {
2070 let blob_info = blob_state
2071 .get_mut(blob_id)
2072 .expect("Blob whose url is requested is unknown.");
2073 self.promote(blob_info, true)
2074 },
2075 }
2076 }
2077
2078 fn create_sliced_url_id(
2080 &self,
2081 blob_info: &mut BlobInfo,
2082 parent_file_id: &Uuid,
2083 rel_pos: &RelativePos,
2084 parent_len: u64,
2085 ) -> Uuid {
2086 let origin = self.origin().immutable();
2087
2088 let (tx, rx) = profile_generic_channel::channel(self.time_profiler_chan().clone()).unwrap();
2089 let msg =
2090 FileManagerThreadMsg::AddSlicedURLEntry(*parent_file_id, *rel_pos, tx, origin.clone());
2091 self.send_to_file_manager(msg);
2092 match rx.recv().expect("File manager thread is down.") {
2093 Ok(new_id) => {
2094 *blob_info.blob_impl.blob_data_mut() = BlobData::File(FileBlob::new(
2095 new_id,
2096 None,
2097 None,
2098 rel_pos.to_abs_range(parent_len as usize).len() as u64,
2099 ));
2100
2101 new_id
2103 },
2104 Err(_) => {
2105 Uuid::new_v4()
2107 },
2108 }
2109 }
2110
2111 pub(crate) fn promote(&self, blob_info: &mut BlobInfo, set_valid: bool) -> Uuid {
2117 let mut bytes = vec![];
2118
2119 match blob_info.blob_impl.blob_data_mut() {
2120 BlobData::Sliced(_, _) => {
2121 panic!("Sliced blobs should use create_sliced_url_id instead of promote.");
2122 },
2123 BlobData::File(f) => {
2124 if set_valid {
2125 let origin = self.origin().immutable();
2126 let (tx, rx) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
2127
2128 let msg = FileManagerThreadMsg::ActivateBlobURL(f.get_id(), tx, origin.clone());
2129 self.send_to_file_manager(msg);
2130
2131 match rx.recv().unwrap() {
2132 Ok(_) => return f.get_id(),
2133 Err(_) => return Uuid::new_v4(),
2135 }
2136 } else {
2137 return f.get_id();
2139 }
2140 },
2141 BlobData::Memory(bytes_in) => mem::swap(bytes_in, &mut bytes),
2142 };
2143
2144 let origin = self.origin().immutable();
2145
2146 let blob_buf = BlobBuf {
2147 filename: None,
2148 type_string: blob_info.blob_impl.type_string(),
2149 size: bytes.len() as u64,
2150 bytes: bytes.to_vec(),
2151 };
2152
2153 let id = Uuid::new_v4();
2154 let msg = FileManagerThreadMsg::PromoteMemory(id, blob_buf, set_valid, origin.clone());
2155 self.send_to_file_manager(msg);
2156
2157 *blob_info.blob_impl.blob_data_mut() = BlobData::File(FileBlob::new(
2158 id,
2159 None,
2160 Some(bytes.to_vec()),
2161 bytes.len() as u64,
2162 ));
2163
2164 id
2165 }
2166
2167 fn send_to_file_manager(&self, msg: FileManagerThreadMsg) {
2168 let resource_threads = self.resource_threads();
2169 let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
2170 }
2171
2172 fn read_file(&self, id: Uuid) -> Result<Vec<u8>, ()> {
2173 let recv = self.send_msg(id);
2174 GlobalScope::read_msg(recv)
2175 }
2176
2177 pub(crate) fn get_blob_stream(
2179 &self,
2180 blob_id: &BlobId,
2181 can_gc: CanGc,
2182 ) -> Fallible<DomRoot<ReadableStream>> {
2183 let (file_id, size) = match self.get_blob_bytes_or_file_id(blob_id) {
2184 BlobResult::Bytes(bytes) => {
2185 return ReadableStream::new_from_bytes(self, bytes, can_gc);
2187 },
2188 BlobResult::File(id, size) => (id, size),
2189 };
2190
2191 let stream = ReadableStream::new_with_external_underlying_source(
2192 self,
2193 UnderlyingSourceType::Blob(size),
2194 can_gc,
2195 )?;
2196
2197 let recv = self.send_msg(file_id);
2198
2199 let trusted_stream = Trusted::new(&*stream);
2200 let mut file_listener = FileListener {
2201 state: Some(FileListenerState::Empty(FileListenerTarget::Stream(
2202 trusted_stream,
2203 ))),
2204 task_source: self.task_manager().file_reading_task_source().into(),
2205 };
2206
2207 ROUTER.add_typed_route(
2208 recv.to_ipc_receiver(),
2209 Box::new(move |msg| {
2210 file_listener.handle(msg.expect("Deserialization of file listener msg failed."));
2211 }),
2212 );
2213
2214 Ok(stream)
2215 }
2216
2217 pub(crate) fn read_file_async(
2218 &self,
2219 id: Uuid,
2220 promise: Rc<Promise>,
2221 callback: FileListenerCallback,
2222 ) {
2223 let recv = self.send_msg(id);
2224
2225 let trusted_promise = TrustedPromise::new(promise);
2226 let mut file_listener = FileListener {
2227 state: Some(FileListenerState::Empty(FileListenerTarget::Promise(
2228 trusted_promise,
2229 callback,
2230 ))),
2231 task_source: self.task_manager().file_reading_task_source().into(),
2232 };
2233
2234 ROUTER.add_typed_route(
2235 recv.to_ipc_receiver(),
2236 Box::new(move |msg| {
2237 file_listener.handle(msg.expect("Deserialization of file listener msg failed."));
2238 }),
2239 );
2240 }
2241
2242 fn send_msg(&self, id: Uuid) -> profile_ipc::IpcReceiver<FileManagerResult<ReadFileProgress>> {
2243 let resource_threads = self.resource_threads();
2244 let (chan, recv) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
2245 let origin = self.origin().immutable();
2246 let msg = FileManagerThreadMsg::ReadFile(chan, id, origin.clone());
2247 let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
2248 recv
2249 }
2250
2251 fn read_msg(
2252 receiver: profile_ipc::IpcReceiver<FileManagerResult<ReadFileProgress>>,
2253 ) -> Result<Vec<u8>, ()> {
2254 let mut bytes = vec![];
2255
2256 loop {
2257 match receiver.recv().unwrap() {
2258 Ok(ReadFileProgress::Meta(mut blob_buf)) => {
2259 bytes.append(&mut blob_buf.bytes);
2260 },
2261 Ok(ReadFileProgress::Partial(mut bytes_in)) => {
2262 bytes.append(&mut bytes_in);
2263 },
2264 Ok(ReadFileProgress::EOF) => {
2265 return Ok(bytes);
2266 },
2267 Err(_) => return Err(()),
2268 }
2269 }
2270 }
2271
2272 pub(crate) fn permission_state_invocation_results(
2273 &self,
2274 ) -> &DomRefCell<HashMap<PermissionName, PermissionState>> {
2275 &self.permission_state_invocation_results
2276 }
2277
2278 pub(crate) fn track_worker(
2279 &self,
2280 closing: Arc<AtomicBool>,
2281 join_handle: JoinHandle<()>,
2282 control_sender: Sender<DedicatedWorkerControlMsg>,
2283 context: ThreadSafeJSContext,
2284 ) {
2285 self.list_auto_close_worker
2286 .borrow_mut()
2287 .push(AutoCloseWorker {
2288 closing,
2289 join_handle: Some(join_handle),
2290 control_sender,
2291 context,
2292 });
2293 }
2294
2295 pub(crate) fn track_event_source(&self, event_source: &EventSource) {
2296 self.event_source_tracker.track(event_source);
2297 }
2298
2299 pub(crate) fn close_event_sources(&self) -> bool {
2300 let mut canceled_any_fetch = false;
2301 self.event_source_tracker
2302 .for_each(
2303 |event_source: DomRoot<EventSource>| match event_source.ReadyState() {
2304 2 => {},
2305 _ => {
2306 event_source.cancel();
2307 canceled_any_fetch = true;
2308 },
2309 },
2310 );
2311 canceled_any_fetch
2312 }
2313
2314 #[expect(unsafe_code)]
2317 pub(crate) fn from_reflector<T: DomObject>(reflector: &T, _realm: InRealm) -> DomRoot<Self> {
2318 unsafe { GlobalScope::from_object(*reflector.reflector().get_jsobject()) }
2319 }
2320
2321 #[expect(unsafe_code)]
2323 pub(crate) unsafe fn from_object(obj: *mut JSObject) -> DomRoot<Self> {
2324 assert!(!obj.is_null());
2325 let global = unsafe { GetNonCCWObjectGlobal(obj) };
2326 unsafe { global_scope_from_global_static(global) }
2327 }
2328
2329 #[expect(unsafe_code)]
2331 pub(crate) unsafe fn from_context(cx: *mut JSContext, _realm: InRealm) -> DomRoot<Self> {
2332 let global = unsafe { CurrentGlobalOrNull(cx) };
2333 assert!(!global.is_null());
2334 unsafe { global_scope_from_global(global, cx) }
2335 }
2336
2337 #[expect(unsafe_code)]
2341 pub(crate) fn from_current_realm(realm: &'_ CurrentRealm) -> DomRoot<Self> {
2342 let global = realm.global();
2343 unsafe { global_scope_from_global(global.get(), realm.raw_cx_no_gc()) }
2344 }
2345
2346 #[expect(unsafe_code)]
2348 pub(crate) fn from_safe_context(cx: SafeJSContext, realm: InRealm) -> DomRoot<Self> {
2349 unsafe { Self::from_context(*cx, realm) }
2350 }
2351
2352 pub(crate) fn add_uncaught_rejection(&self, rejection: HandleObject) {
2353 self.uncaught_rejections
2354 .borrow_mut()
2355 .push(Heap::boxed(rejection.get()));
2356 }
2357
2358 pub(crate) fn remove_uncaught_rejection(&self, rejection: HandleObject) {
2359 let mut uncaught_rejections = self.uncaught_rejections.borrow_mut();
2360
2361 if let Some(index) = uncaught_rejections
2362 .iter()
2363 .position(|promise| *promise == Heap::boxed(rejection.get()))
2364 {
2365 uncaught_rejections.remove(index);
2366 }
2367 }
2368
2369 #[allow(clippy::vec_box)]
2372 pub(crate) fn get_uncaught_rejections(&self) -> &DomRefCell<Vec<Box<Heap<*mut JSObject>>>> {
2374 &self.uncaught_rejections
2375 }
2376
2377 pub(crate) fn add_consumed_rejection(&self, rejection: HandleObject) {
2378 self.consumed_rejections
2379 .borrow_mut()
2380 .push(Heap::boxed(rejection.get()));
2381 }
2382
2383 pub(crate) fn remove_consumed_rejection(&self, rejection: HandleObject) {
2384 let mut consumed_rejections = self.consumed_rejections.borrow_mut();
2385
2386 if let Some(index) = consumed_rejections
2387 .iter()
2388 .position(|promise| *promise == Heap::boxed(rejection.get()))
2389 {
2390 consumed_rejections.remove(index);
2391 }
2392 }
2393
2394 #[allow(clippy::vec_box)]
2397 pub(crate) fn get_consumed_rejections(&self) -> &DomRefCell<Vec<Box<Heap<*mut JSObject>>>> {
2398 &self.consumed_rejections
2399 }
2400
2401 pub(crate) fn set_module_map(&self, request: ModuleRequest, module: ModuleStatus) {
2402 self.module_map.borrow_mut().insert(request, module);
2403 }
2404
2405 pub(crate) fn get_module_map_entry(&self, request: &ModuleRequest) -> Option<ModuleStatus> {
2406 self.module_map.borrow().get(request).cloned()
2407 }
2408
2409 #[expect(unsafe_code)]
2410 pub(crate) fn get_cx() -> SafeJSContext {
2411 let cx = Runtime::get()
2412 .expect("Can't obtain context after runtime shutdown")
2413 .as_ptr();
2414 unsafe { SafeJSContext::from_ptr(cx) }
2415 }
2416
2417 pub(crate) fn crypto(&self, can_gc: CanGc) -> DomRoot<Crypto> {
2418 self.crypto.or_init(|| Crypto::new(self, can_gc))
2419 }
2420
2421 pub(crate) fn cookie_store(&self, can_gc: CanGc) -> DomRoot<CookieStore> {
2422 self.cookie_store.or_init(|| CookieStore::new(self, can_gc))
2423 }
2424
2425 pub(crate) fn live_devtools_updates(&self) -> bool {
2426 self.devtools_wants_updates.get()
2427 }
2428
2429 pub(crate) fn set_devtools_wants_updates(&self, value: bool) {
2430 self.devtools_wants_updates.set(value);
2431 }
2432
2433 pub(crate) fn time(&self, label: DOMString) -> Result<(), ()> {
2434 let mut timers = self.console_timers.borrow_mut();
2435 if timers.len() >= 10000 {
2436 return Err(());
2437 }
2438 match timers.entry(label) {
2439 Entry::Vacant(entry) => {
2440 entry.insert(Instant::now());
2441 Ok(())
2442 },
2443 Entry::Occupied(_) => Err(()),
2444 }
2445 }
2446
2447 pub(crate) fn time_log(&self, label: &DOMString) -> Result<u64, ()> {
2451 self.console_timers
2452 .borrow()
2453 .get(label)
2454 .ok_or(())
2455 .map(|&start| (Instant::now() - start).as_millis() as u64)
2456 }
2457
2458 pub(crate) fn time_end(&self, label: &DOMString) -> Result<u64, ()> {
2463 self.console_timers
2464 .borrow_mut()
2465 .remove(label)
2466 .ok_or(())
2467 .map(|start| (Instant::now() - start).as_millis() as u64)
2468 }
2469
2470 pub(crate) fn devtools_chan(&self) -> Option<&GenericCallback<ScriptToDevtoolsControlMsg>> {
2473 self.devtools_chan.as_ref()
2474 }
2475
2476 pub(crate) fn mem_profiler_chan(&self) -> &profile_mem::ProfilerChan {
2478 &self.mem_profiler_chan
2479 }
2480
2481 pub(crate) fn time_profiler_chan(&self) -> &profile_time::ProfilerChan {
2483 &self.time_profiler_chan
2484 }
2485
2486 pub(crate) fn script_to_constellation_chan(&self) -> &ScriptToConstellationChan {
2488 &self.script_to_constellation_chan
2489 }
2490
2491 pub(crate) fn script_to_embedder_chan(&self) -> &ScriptToEmbedderChan {
2492 &self.script_to_embedder_chan
2493 }
2494
2495 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
2496 self.script_to_embedder_chan().send(msg).unwrap();
2497 }
2498
2499 pub(crate) fn pipeline_id(&self) -> PipelineId {
2501 self.pipeline_id
2502 }
2503
2504 pub(crate) fn origin(&self) -> &MutableOrigin {
2506 &self.origin
2507 }
2508
2509 pub(crate) fn creation_url(&self) -> ServoUrl {
2511 self.creation_url.borrow().clone()
2512 }
2513
2514 pub(crate) fn set_creation_url(&self, creation_url: ServoUrl) {
2515 *self.creation_url.borrow_mut() = creation_url;
2516 }
2517
2518 pub(crate) fn top_level_creation_url(&self) -> &Option<ServoUrl> {
2520 &self.top_level_creation_url
2521 }
2522
2523 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
2524 if let Some(window) = self.downcast::<Window>() {
2525 return window.image_cache();
2526 }
2527 if let Some(worker) = self.downcast::<DedicatedWorkerGlobalScope>() {
2528 return worker.image_cache();
2529 }
2530 if let Some(worker) = self.downcast::<PaintWorkletGlobalScope>() {
2531 return worker.image_cache();
2532 }
2533 unreachable!();
2534 }
2535
2536 pub(crate) fn schedule_timer(&self, request: TimerEventRequest) -> Option<TimerId> {
2540 match self.downcast::<WorkerGlobalScope>() {
2541 Some(worker_global) => Some(worker_global.timer_scheduler().schedule_timer(request)),
2542 _ => with_script_thread(|script_thread| Some(script_thread.schedule_timer(request))),
2543 }
2544 }
2545
2546 pub(crate) fn is_nested_browsing_context(&self) -> bool {
2548 self.downcast::<Window>()
2549 .is_some_and(|window| !window.is_top_level())
2550 }
2551
2552 pub(crate) fn total_size_of_in_flight_keep_alive_records(&self) -> u64 {
2558 let (sender, receiver) = generic_channel::channel().unwrap();
2559 if self
2560 .core_resource_thread()
2561 .send(CoreResourceMsg::TotalSizeOfInFlightKeepAliveRecords(
2562 self.pipeline_id(),
2563 sender,
2564 ))
2565 .is_err()
2566 {
2567 return u64::MAX;
2568 }
2569 receiver.recv().unwrap_or(u64::MAX)
2570 }
2571
2572 pub(crate) fn request_client(&self) -> RequestClient {
2574 let window = self.downcast::<Window>();
2577 let preloaded_resources = window
2578 .map(|window: &Window| window.Document().preloaded_resources().clone())
2579 .unwrap_or_default();
2580 let is_nested_browsing_context = window.is_some_and(|window| !window.is_top_level());
2581 RequestClient {
2582 preloaded_resources,
2583 policy_container: RequestPolicyContainer::PolicyContainer(self.policy_container()),
2584 origin: RequestOrigin::Origin(self.origin().immutable().clone()),
2585 is_nested_browsing_context,
2586 insecure_requests_policy: self.insecure_requests_policy(),
2587 }
2588 }
2589
2590 pub(crate) fn policy_container(&self) -> PolicyContainer {
2592 if let Some(window) = self.downcast::<Window>() {
2593 return window.Document().policy_container().to_owned();
2594 }
2595 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2596 return worker.policy_container().to_owned();
2597 }
2598 unreachable!();
2599 }
2600
2601 pub(crate) fn api_base_url(&self) -> ServoUrl {
2604 if let Some(window) = self.downcast::<Window>() {
2605 return window.Document().base_url();
2607 }
2608 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2609 return worker.get_url().clone();
2611 }
2612 if let Some(worklet) = self.downcast::<WorkletGlobalScope>() {
2613 return worklet.base_url();
2615 }
2616 if let Some(_debugger_global) = self.downcast::<DebuggerGlobalScope>() {
2617 return self.creation_url();
2618 }
2619 unreachable!();
2620 }
2621
2622 pub(crate) fn get_url(&self) -> ServoUrl {
2624 if let Some(window) = self.downcast::<Window>() {
2625 return window.get_url();
2626 }
2627 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2628 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_referrer_policy(&self) -> ReferrerPolicy {
2642 if let Some(window) = self.downcast::<Window>() {
2643 let document = window.Document();
2644
2645 return document.get_referrer_policy();
2646 }
2647 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2648 return worker.policy_container().get_referrer_policy();
2649 }
2650 unreachable!();
2651 }
2652
2653 pub(crate) fn get_referrer(&self) -> Referrer {
2656 if let Some(window) = self.downcast::<Window>() {
2658 let mut document = window.Document();
2660
2661 if let ImmutableOrigin::Opaque(_) = document.origin().immutable() {
2663 return Referrer::NoReferrer;
2664 }
2665
2666 let mut url = document.url();
2667
2668 while url.as_str() == "about:srcdoc" {
2671 let Some(parent_document) =
2674 document.browsing_context().and_then(|browsing_context| {
2675 browsing_context
2676 .parent()
2677 .and_then(|parent| parent.document())
2678 })
2679 else {
2680 return Referrer::NoReferrer;
2681 };
2682 document = parent_document;
2683 url = document.url();
2684 }
2685
2686 Referrer::Client(url)
2688 } else {
2689 Referrer::Client(self.creation_url())
2691 }
2692 }
2693
2694 pub(crate) fn as_window(&self) -> &Window {
2696 self.downcast::<Window>().expect("expected a Window scope")
2697 }
2698
2699 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
2701 if let Some(window) = self.downcast::<Window>() {
2702 return window.Document().insecure_requests_policy();
2703 }
2704 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2705 return worker.insecure_requests_policy();
2706 }
2707 debug!("unsupported global, defaulting insecure requests policy to DoNotUpgrade");
2708 InsecureRequestsPolicy::DoNotUpgrade
2709 }
2710
2711 pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
2713 self.downcast::<Window>()
2714 .is_some_and(|window| window.Document().has_trustworthy_ancestor_origin())
2715 }
2716
2717 pub(crate) fn has_trustworthy_ancestor_or_current_origin(&self) -> bool {
2719 self.downcast::<Window>().is_some_and(|window| {
2720 window
2721 .Document()
2722 .has_trustworthy_ancestor_or_current_origin()
2723 })
2724 }
2725
2726 pub(crate) fn report_an_exception(&self, cx: SafeJSContext, error: HandleValue, can_gc: CanGc) {
2728 let error_info = crate::dom::bindings::error::ErrorInfo::from_value(error, cx, can_gc);
2739 self.report_an_error(error_info, error, can_gc);
2745 }
2746
2747 pub(crate) fn report_an_error(&self, error_info: ErrorInfo, value: HandleValue, can_gc: CanGc) {
2749 error!(
2750 "Error at {}:{}:{} {}",
2751 error_info.filename, error_info.lineno, error_info.column, error_info.message
2752 );
2753
2754 #[cfg(feature = "js_backtrace")]
2755 LAST_EXCEPTION_BACKTRACE.with(|backtrace| {
2756 if let Some((js_backtrace, rust_backtrace)) = backtrace.borrow_mut().take() {
2757 if let Some(stack) = js_backtrace {
2758 error!("JS backtrace:\n{}", stack);
2759 }
2760 error!("Rust backtrace:\n{}", rust_backtrace);
2761 }
2762 });
2763
2764 if self.in_error_reporting_mode.get() {
2766 return;
2767 }
2768
2769 self.in_error_reporting_mode.set(true);
2771
2772 let event = ErrorEvent::new(
2778 self,
2779 atom!("error"),
2780 EventBubbles::DoesNotBubble,
2781 EventCancelable::Cancelable,
2782 error_info.message.as_str().into(),
2783 error_info.filename.as_str().into(),
2784 error_info.lineno,
2785 error_info.column,
2786 value,
2787 can_gc,
2788 );
2789
2790 let not_handled = event
2791 .upcast::<Event>()
2792 .fire(self.upcast::<EventTarget>(), can_gc);
2793
2794 self.in_error_reporting_mode.set(false);
2796
2797 if not_handled {
2799 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
2805 dedicated.forward_error_to_worker_object(error_info);
2806 } else if self.is::<Window>() {
2807 if let Some(ref chan) = self.devtools_chan {
2809 let _ = chan.send(ScriptToDevtoolsControlMsg::ReportPageError(
2810 self.pipeline_id,
2811 PageError {
2812 error_message: error_info.message.clone(),
2813 source_name: error_info.filename.clone(),
2814 line_number: error_info.lineno,
2815 column_number: error_info.column,
2816 time_stamp: get_time_stamp(),
2817 },
2818 ));
2819 }
2820 }
2821 }
2822 }
2823
2824 pub(crate) fn resource_threads(&self) -> &ResourceThreads {
2826 &self.resource_threads
2827 }
2828
2829 pub(crate) fn core_resource_thread(&self) -> CoreResourceThread {
2831 self.resource_threads().sender()
2832 }
2833
2834 pub(crate) fn storage_threads(&self) -> &StorageThreads {
2836 &self.storage_threads
2837 }
2838
2839 pub(crate) fn event_loop_sender(&self) -> Option<ScriptEventLoopSender> {
2843 if let Some(window) = self.downcast::<Window>() {
2844 Some(window.event_loop_sender())
2845 } else if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
2846 dedicated.event_loop_sender()
2847 } else if let Some(service_worker) = self.downcast::<ServiceWorkerGlobalScope>() {
2848 Some(service_worker.event_loop_sender())
2849 } else {
2850 unreachable!(
2851 "Tried to access event loop sender for incompatible \
2852 GlobalScope (PaintWorklet or DissimilarOriginWindow)"
2853 );
2854 }
2855 }
2856
2857 pub(crate) fn task_manager(&self) -> &TaskManager {
2859 let shared_canceller = self
2860 .downcast::<WorkerGlobalScope>()
2861 .map(WorkerGlobalScope::shared_task_canceller);
2862 self.task_manager.get_or_init(|| {
2863 TaskManager::new(
2864 self.event_loop_sender(),
2865 self.pipeline_id(),
2866 shared_canceller,
2867 )
2868 })
2869 }
2870
2871 pub(crate) fn evaluate_js_on_global(
2873 &self,
2874 cx: &mut CurrentRealm,
2875 code: Cow<'_, str>,
2876 filename: &str,
2877 introduction_type: Option<&'static CStr>,
2878 rval: Option<MutableHandleValue>,
2879 ) -> Result<(), JavaScriptEvaluationError> {
2880 let in_realm_proof = cx.into();
2881 let in_realm = InRealm::Already(&in_realm_proof);
2882
2883 run_a_script::<DomTypeHolder, _>(self, || {
2884 let url = self.api_base_url();
2885 let fetch_options = ScriptFetchOptions::default_classic_script();
2886
2887 let no_script_rval = rval.is_none();
2888
2889 rooted!(&in(cx) let mut compiled_script = std::ptr::null_mut::<JSScript>());
2890 compiled_script.set(compile_script(
2891 cx,
2892 &code,
2893 filename,
2894 1,
2895 introduction_type,
2896 ErrorReporting::Unmuted,
2897 no_script_rval,
2898 ));
2899
2900 if compiled_script.is_null() {
2901 debug!("error compiling Dom string");
2902 report_pending_exception(cx.into(), in_realm, CanGc::from_cx(cx));
2903 return Err(JavaScriptEvaluationError::CompilationFailure);
2904 }
2905
2906 let script = NonNull::new(*compiled_script).expect("Can't be null");
2907
2908 rooted!(&in(cx) let mut value = UndefinedValue());
2909 let rval = rval.unwrap_or_else(|| value.handle_mut());
2910
2911 if !evaluate_script(cx, script, url, fetch_options, rval) {
2912 let error_info = take_and_report_pending_exception_for_api(cx);
2913 return Err(JavaScriptEvaluationError::EvaluationFailure(error_info));
2914 }
2915
2916 maybe_resume_unwind();
2917 Ok(())
2918 })
2919 }
2920
2921 pub(crate) fn schedule_callback(
2923 &self,
2924 callback: OneshotTimerCallback,
2925 duration: Duration,
2926 ) -> OneshotTimerHandle {
2927 self.timers()
2928 .schedule_callback(callback, duration, self.timer_source())
2929 }
2930
2931 pub(crate) fn unschedule_callback(&self, handle: OneshotTimerHandle) {
2932 self.timers().unschedule_callback(handle);
2933 }
2934
2935 pub(crate) fn set_timeout_or_interval(
2937 &self,
2938 cx: &mut js::context::JSContext,
2939 callback: TimerCallback,
2940 arguments: Vec<HandleValue>,
2941 timeout: Duration,
2942 is_interval: IsInterval,
2943 ) -> Fallible<i32> {
2944 self.timers().set_timeout_or_interval(
2945 cx,
2946 self,
2947 callback,
2948 arguments,
2949 timeout,
2950 is_interval,
2951 self.timer_source(),
2952 )
2953 }
2954
2955 pub(crate) fn clear_timeout_or_interval(&self, handle: i32) {
2956 self.timers().clear_timeout_or_interval(self, handle);
2957 }
2958
2959 pub(crate) fn fire_timer(&self, handle: TimerEventId, cx: &mut js::context::JSContext) {
2960 self.timers().fire_timer(handle, self, cx);
2961 }
2962
2963 pub(crate) fn resume(&self) {
2964 self.timers().resume();
2965 }
2966
2967 pub(crate) fn suspend(&self) {
2968 self.timers().suspend();
2969 }
2970
2971 pub(crate) fn slow_down_timers(&self) {
2972 self.timers().slow_down();
2973 }
2974
2975 pub(crate) fn speed_up_timers(&self) {
2976 self.timers().speed_up();
2977 }
2978
2979 fn timer_source(&self) -> TimerSource {
2980 if self.is::<Window>() {
2981 return TimerSource::FromWindow(self.pipeline_id());
2982 }
2983 if self.is::<WorkerGlobalScope>() {
2984 return TimerSource::FromWorker;
2985 }
2986 unreachable!();
2987 }
2988
2989 pub(crate) fn can_continue_running(&self) -> bool {
2992 if self.is::<Window>() {
2993 return ScriptThread::can_continue_running();
2994 }
2995 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2996 return !worker.is_closing();
2997 }
2998
2999 true
3001 }
3002
3003 pub(crate) fn get_indexeddb(&self) -> DomRoot<IDBFactory> {
3005 self.indexeddb
3006 .or_init(|| IDBFactory::new(self, CanGc::note()))
3007 }
3008
3009 pub(crate) fn get_existing_indexeddb(&self) -> Option<DomRoot<IDBFactory>> {
3010 self.indexeddb.get()
3011 }
3012
3013 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
3015 if let Some(window) = self.downcast::<Window>() {
3016 window.perform_a_microtask_checkpoint(cx);
3017 } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3018 worker.perform_a_microtask_checkpoint(cx);
3019 }
3020 }
3021
3022 pub(crate) fn enqueue_microtask(&self, job: Microtask) {
3024 if self.is::<Window>() {
3025 ScriptThread::enqueue_microtask(job);
3026 } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3027 worker.enqueue_microtask(job);
3028 }
3029 }
3030
3031 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
3035 if let Some(window) = self.downcast::<Window>() {
3036 return window.new_script_pair();
3037 }
3038 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3039 return worker.new_script_pair();
3040 }
3041 unreachable!();
3042 }
3043
3044 pub(crate) fn process_event(
3048 &self,
3049 msg: CommonScriptMsg,
3050 cx: &mut js::context::JSContext,
3051 ) -> bool {
3052 if self.is::<Window>() {
3053 return ScriptThread::process_event(msg, cx);
3054 }
3055 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3056 return worker.process_event(msg, cx);
3057 }
3058 unreachable!();
3059 }
3060
3061 pub(crate) fn runtime_handle(&self) -> ParentRuntime {
3062 if self.is::<Window>() {
3063 ScriptThread::runtime_handle()
3064 } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3065 worker.runtime_handle()
3066 } else {
3067 unreachable!()
3068 }
3069 }
3070
3071 #[expect(unsafe_code)]
3075 pub(crate) fn current() -> Option<DomRoot<Self>> {
3076 let cx = Runtime::get()?;
3077 unsafe {
3078 let global = CurrentGlobalOrNull(cx.as_ptr());
3079 if global.is_null() {
3080 None
3081 } else {
3082 Some(global_scope_from_global(global, cx.as_ptr()))
3083 }
3084 }
3085 }
3086
3087 pub(crate) fn entry() -> DomRoot<Self> {
3091 entry_global()
3092 }
3093
3094 pub(crate) fn incumbent() -> Option<DomRoot<Self>> {
3098 incumbent_global()
3099 }
3100
3101 pub(crate) fn performance(&self) -> DomRoot<Performance> {
3102 if let Some(window) = self.downcast::<Window>() {
3103 return window.Performance();
3104 }
3105 if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3106 return worker.Performance();
3107 }
3108 unreachable!();
3109 }
3110
3111 pub(crate) fn supported_performance_entry_types(
3113 &self,
3114 cx: SafeJSContext,
3115 retval: MutableHandleValue,
3116 can_gc: CanGc,
3117 ) {
3118 self.frozen_supported_performance_entry_types.get_or_init(
3119 || {
3120 EntryType::VARIANTS
3121 .iter()
3122 .map(|t| DOMString::from(t.as_str()))
3123 .collect()
3124 },
3125 cx,
3126 retval,
3127 can_gc,
3128 );
3129 }
3130
3131 pub(crate) fn get_https_state(&self) -> HttpsState {
3132 self.https_state.get()
3133 }
3134
3135 pub(crate) fn set_https_state(&self, https_state: HttpsState) {
3136 self.https_state.set(https_state);
3137 }
3138
3139 pub(crate) fn inherited_secure_context(&self) -> Option<bool> {
3140 self.inherited_secure_context
3141 }
3142
3143 pub(crate) fn is_secure_context(&self) -> bool {
3145 if Some(false) == self.inherited_secure_context {
3150 return false;
3151 }
3152 match self.top_level_creation_url() {
3155 None => {
3156 assert!(
3158 self.downcast::<WorkerGlobalScope>().is_some() ||
3159 self.downcast::<WorkletGlobalScope>().is_some()
3160 );
3161 true
3162 },
3163 Some(top_level_creation_url) => {
3164 assert!(self.downcast::<Window>().is_some());
3165 if top_level_creation_url.scheme() == "blob" &&
3169 Some(true) == self.inherited_secure_context
3170 {
3171 return true;
3172 }
3173 top_level_creation_url.is_potentially_trustworthy()
3174 },
3175 }
3176 }
3177
3178 pub(crate) fn get_csp_list(&self) -> Option<CspList> {
3180 if self.downcast::<Window>().is_some() || self.downcast::<WorkerGlobalScope>().is_some() {
3181 return self.policy_container().csp_list;
3182 }
3183 None
3185 }
3186
3187 pub(crate) fn status_code(&self) -> Option<u16> {
3188 if let Some(window) = self.downcast::<Window>() {
3189 return window.Document().status_code();
3190 }
3191 None
3192 }
3193
3194 #[cfg(feature = "webgpu")]
3195 pub(crate) fn wgpu_id_hub(&self) -> Arc<IdentityHub> {
3196 self.gpu_id_hub.clone()
3197 }
3198
3199 #[cfg(feature = "webgpu")]
3200 pub(crate) fn add_gpu_device(&self, device: &GPUDevice) {
3201 self.gpu_devices
3202 .borrow_mut()
3203 .insert(device.id(), WeakRef::new(device));
3204 }
3205
3206 #[cfg(feature = "webgpu")]
3207 pub(crate) fn remove_gpu_device(&self, device: WebGPUDevice) {
3208 let device = self
3209 .gpu_devices
3210 .borrow_mut()
3211 .remove(&device)
3212 .expect("GPUDevice should still be in devices hashmap");
3213 assert!(device.root().is_none())
3214 }
3215
3216 #[cfg(feature = "webgpu")]
3217 pub(crate) fn gpu_device_lost(
3218 &self,
3219 device: WebGPUDevice,
3220 reason: DeviceLostReason,
3221 msg: String,
3222 ) {
3223 let reason = match reason {
3224 DeviceLostReason::Unknown => GPUDeviceLostReason::Unknown,
3225 DeviceLostReason::Destroyed => GPUDeviceLostReason::Destroyed,
3226 };
3227 let _ac = crate::realms::enter_realm(self);
3228 if let Some(device) = self
3229 .gpu_devices
3230 .borrow_mut()
3231 .get_mut(&device)
3232 .expect("GPUDevice should still be in devices hashmap")
3233 .root()
3234 {
3235 device.lose(reason, msg);
3236 }
3237 }
3238
3239 #[cfg(feature = "webgpu")]
3240 pub(crate) fn handle_uncaptured_gpu_error(
3241 &self,
3242 device: WebGPUDevice,
3243 error: webgpu_traits::Error,
3244 ) {
3245 if let Some(gpu_device) = self
3246 .gpu_devices
3247 .borrow()
3248 .get(&device)
3249 .and_then(|device| device.root())
3250 {
3251 gpu_device.fire_uncaptured_error(error);
3252 } else {
3253 warn!("Recived error for lost GPUDevice!")
3254 }
3255 }
3256
3257 pub(crate) fn current_group_label(&self) -> Option<DOMString> {
3258 self.console_group_stack
3259 .borrow()
3260 .last()
3261 .map(|label| DOMString::from(format!("[{}]", label)))
3262 }
3263
3264 pub(crate) fn push_console_group(&self, group: DOMString) {
3265 self.console_group_stack.borrow_mut().push(group);
3266 }
3267
3268 pub(crate) fn pop_console_group(&self) {
3269 let _ = self.console_group_stack.borrow_mut().pop();
3270 }
3271
3272 pub(crate) fn increment_console_count(&self, label: &DOMString) -> usize {
3273 *self
3274 .console_count_map
3275 .borrow_mut()
3276 .entry(label.clone())
3277 .and_modify(|e| *e += 1)
3278 .or_insert(1)
3279 }
3280
3281 pub(crate) fn reset_console_count(&self, label: &DOMString) -> Result<(), ()> {
3282 match self.console_count_map.borrow_mut().get_mut(label) {
3283 Some(value) => {
3284 *value = 0;
3285 Ok(())
3286 },
3287 None => Err(()),
3288 }
3289 }
3290
3291 pub(crate) fn structured_clone(
3292 &self,
3293 cx: SafeJSContext,
3294 value: HandleValue,
3295 options: RootedTraceableBox<StructuredSerializeOptions>,
3296 retval: MutableHandleValue,
3297 can_gc: CanGc,
3298 ) -> Fallible<()> {
3299 let mut rooted = CustomAutoRooter::new(
3300 options
3301 .transfer
3302 .iter()
3303 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
3304 .collect(),
3305 );
3306 let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
3307
3308 let data = structuredclone::write(cx, value, Some(guard))?;
3309
3310 structuredclone::read(self, data, retval, can_gc)?;
3311
3312 Ok(())
3313 }
3314
3315 pub(crate) fn fetch<Listener: FetchResponseListener>(
3316 &self,
3317 request_builder: RequestBuilder,
3318 context: Listener,
3319 task_source: SendableTaskSource,
3320 ) {
3321 let network_listener = NetworkListener::new(context, task_source);
3322 self.fetch_with_network_listener(request_builder, network_listener);
3323 }
3324
3325 pub(crate) fn fetch_with_network_listener<Listener: FetchResponseListener>(
3326 &self,
3327 request_builder: RequestBuilder,
3328 network_listener: NetworkListener<Listener>,
3329 ) {
3330 fetch_async(
3331 &self.core_resource_thread(),
3332 request_builder,
3333 None,
3334 network_listener.into_callback(),
3335 );
3336 }
3337
3338 pub(crate) fn unminify_js(&self) -> bool {
3339 self.unminified_js_dir.is_some()
3340 }
3341
3342 pub(crate) fn unminified_js_dir(&self) -> Option<String> {
3343 self.unminified_js_dir.clone()
3344 }
3345
3346 pub(crate) fn set_byte_length_queuing_strategy_size(&self, function: Rc<Function>) {
3347 if self
3348 .byte_length_queuing_strategy_size_function
3349 .set(function)
3350 .is_err()
3351 {
3352 warn!("byte length queuing strategy size function is set twice.");
3353 };
3354 }
3355
3356 pub(crate) fn get_byte_length_queuing_strategy_size(&self) -> Option<Rc<Function>> {
3357 self.byte_length_queuing_strategy_size_function
3358 .get()
3359 .cloned()
3360 }
3361
3362 pub(crate) fn set_count_queuing_strategy_size(&self, function: Rc<Function>) {
3363 if self
3364 .count_queuing_strategy_size_function
3365 .set(function)
3366 .is_err()
3367 {
3368 warn!("count queuing strategy size function is set twice.");
3369 };
3370 }
3371
3372 pub(crate) fn get_count_queuing_strategy_size(&self) -> Option<Rc<Function>> {
3373 self.count_queuing_strategy_size_function.get().cloned()
3374 }
3375
3376 pub(crate) fn add_notification_permission_request_callback(
3377 &self,
3378 callback_id: String,
3379 callback: Rc<NotificationPermissionCallback>,
3380 ) {
3381 self.notification_permission_request_callback_map
3382 .borrow_mut()
3383 .insert(callback_id, callback);
3384 }
3385
3386 pub(crate) fn remove_notification_permission_request_callback(
3387 &self,
3388 callback_id: String,
3389 ) -> Option<Rc<NotificationPermissionCallback>> {
3390 self.notification_permission_request_callback_map
3391 .borrow_mut()
3392 .remove(&callback_id)
3393 }
3394
3395 pub(crate) fn append_deferred_fetch(
3396 &self,
3397 deferred_fetch: QueuedDeferredFetchRecord,
3398 ) -> DeferredFetchRecordId {
3399 let deferred_record_id = DeferredFetchRecordId::default();
3400 self.fetch_group
3401 .borrow_mut()
3402 .deferred_fetch_records
3403 .insert(deferred_record_id, deferred_fetch);
3404 deferred_record_id
3405 }
3406
3407 pub(crate) fn deferred_fetches(&self) -> Vec<QueuedDeferredFetchRecord> {
3408 self.fetch_group
3409 .borrow()
3410 .deferred_fetch_records
3411 .values()
3412 .cloned()
3413 .collect()
3414 }
3415
3416 pub(crate) fn deferred_fetch_record_for_id(
3417 &self,
3418 deferred_fetch_record_id: &DeferredFetchRecordId,
3419 ) -> QueuedDeferredFetchRecord {
3420 self.fetch_group
3421 .borrow()
3422 .deferred_fetch_records
3423 .get(deferred_fetch_record_id)
3424 .expect("Should always use a generated fetch_record_id instead of passing your own")
3425 .clone()
3426 }
3427
3428 pub(crate) fn process_deferred_fetches(&self) {
3430 for deferred_fetch in self.deferred_fetches() {
3433 deferred_fetch.process(self);
3434 }
3435 }
3436
3437 pub(crate) fn import_map(&self) -> Ref<'_, ImportMap> {
3438 self.import_map.borrow()
3439 }
3440
3441 pub(crate) fn import_map_mut(&self) -> RefMut<'_, ImportMap> {
3442 self.import_map.borrow_mut()
3443 }
3444
3445 pub(crate) fn resolved_module_set(&self) -> Ref<'_, HashSet<ResolvedModule>> {
3446 self.resolved_module_set.borrow()
3447 }
3448
3449 pub(crate) fn add_module_to_resolved_module_set(
3451 &self,
3452 base_url: &str,
3453 specifier: &str,
3454 specifier_url: Option<ServoUrl>,
3455 ) {
3456 if self.is::<Window>() {
3459 let record =
3463 ResolvedModule::new(base_url.to_owned(), specifier.to_owned(), specifier_url);
3464 self.resolved_module_set.borrow_mut().insert(record);
3466 }
3467 }
3468
3469 pub(crate) fn run_steps_after_a_timeout<F>(
3473 &self,
3474 ordering_identifier: DOMString,
3475 milliseconds: i64,
3476 completion_steps: F,
3477 ) -> i32
3478 where
3479 F: 'static + FnOnce(&mut js::context::JSContext, &GlobalScope),
3480 {
3481 let timers = self.timers();
3482
3483 let timer_key = timers.fresh_runsteps_key();
3485
3486 let start_time = timers.now_for_runsteps();
3488
3489 let ms = milliseconds.max(0) as u64;
3491 let delay = std::time::Duration::from_millis(ms);
3492 let deadline = start_time + delay;
3493 timers.runsteps_set_active(timer_key, deadline);
3494
3495 let callback = crate::timers::OneshotTimerCallback::RunStepsAfterTimeout {
3498 timer_key,
3500 ordering_id: ordering_identifier,
3502 milliseconds: ms,
3504 completion: Box::new(completion_steps),
3506 };
3507 let _ = self.schedule_callback(callback, delay);
3508
3509 timer_key
3511 }
3512}
3513
3514#[expect(unsafe_code)]
3516unsafe fn global_scope_from_global(
3517 global: *mut JSObject,
3518 cx: *mut JSContext,
3519) -> DomRoot<GlobalScope> {
3520 unsafe {
3521 assert!(!global.is_null());
3522 let clasp = get_object_class(global);
3523 assert_ne!(
3524 ((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)),
3525 0
3526 );
3527 root_from_object(global, cx).unwrap()
3528 }
3529}
3530
3531#[expect(unsafe_code)]
3533unsafe fn global_scope_from_global_static(global: *mut JSObject) -> DomRoot<GlobalScope> {
3534 assert!(!global.is_null());
3535 let clasp = unsafe { get_object_class(global) };
3536
3537 unsafe {
3538 assert_ne!(
3539 ((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)),
3540 0
3541 );
3542 }
3543
3544 root_from_object_static(global).unwrap()
3545}
3546
3547#[expect(unsafe_code)]
3548impl GlobalScopeHelpers<crate::DomTypeHolder> for GlobalScope {
3549 unsafe fn from_context(cx: *mut JSContext, realm: InRealm) -> DomRoot<Self> {
3550 unsafe { GlobalScope::from_context(cx, realm) }
3551 }
3552
3553 fn from_current_realm(realm: &'_ CurrentRealm) -> DomRoot<Self> {
3554 GlobalScope::from_current_realm(realm)
3555 }
3556
3557 fn get_cx() -> SafeJSContext {
3558 GlobalScope::get_cx()
3559 }
3560
3561 unsafe fn from_object(obj: *mut JSObject) -> DomRoot<Self> {
3562 unsafe { GlobalScope::from_object(obj) }
3563 }
3564
3565 fn from_reflector(reflector: &impl DomObject, realm: InRealm) -> DomRoot<Self> {
3566 GlobalScope::from_reflector(reflector, realm)
3567 }
3568
3569 fn origin(&self) -> &MutableOrigin {
3570 GlobalScope::origin(self)
3571 }
3572
3573 fn incumbent() -> Option<DomRoot<Self>> {
3574 GlobalScope::incumbent()
3575 }
3576
3577 fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
3578 GlobalScope::perform_a_microtask_checkpoint(self, cx)
3579 }
3580
3581 fn get_url(&self) -> ServoUrl {
3582 self.get_url()
3583 }
3584
3585 fn is_secure_context(&self) -> bool {
3586 self.is_secure_context()
3587 }
3588}