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