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