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