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