Skip to main content

script/dom/globalscope/
globalscope.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::borrow::Cow;
6use std::cell::{Cell, OnceCell, Ref, RefCell};
7use std::collections::hash_map::Entry;
8use std::collections::{HashMap, HashSet, VecDeque};
9use std::ffi::CStr;
10use std::mem;
11use std::ops::{Deref, Index};
12use std::ptr::NonNull;
13use std::rc::Rc;
14use std::sync::Arc;
15use std::sync::atomic::{AtomicBool, Ordering};
16use std::thread::JoinHandle;
17use std::time::{Duration, Instant};
18
19use content_security_policy::CspList;
20use crossbeam_channel::Sender;
21use devtools_traits::{PageError, ScriptToDevtoolsControlMsg, get_time_stamp};
22use dom_struct::dom_struct;
23use embedder_traits::{
24    ConsoleLogLevel, EmbedderMsg, JavaScriptEvaluationError, ScriptToEmbedderChan,
25};
26use fonts::FontContext;
27use indexmap::IndexSet;
28use ipc_channel::router::ROUTER;
29use js::context::{JSContext, NoGC};
30use js::jsapi::{GetNonCCWObjectGlobal, HandleObject, Heap, JSObject};
31use js::jsval::UndefinedValue;
32use js::panic::maybe_resume_unwind;
33use js::realm::CurrentRealm;
34use js::rust::wrappers2::{Compile1, CurrentGlobalOrNull};
35use js::rust::{
36    CustomAutoRooter, CustomAutoRooterGuard, HandleValue, MutableHandleValue, ParentRuntime,
37    get_object_class, transform_str_to_source_text,
38};
39use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
40use net_traits::blob_url_store::BlobBuf;
41use net_traits::filemanager_thread::{
42    FileManagerResult, FileManagerThreadMsg, ReadFileProgress, RelativePos,
43};
44use net_traits::image_cache::ImageCache;
45use net_traits::policy_container::PolicyContainer;
46use net_traits::request::{
47    InsecureRequestsPolicy, Origin as RequestOrigin, Referrer, RequestBuilder, RequestClient,
48};
49use net_traits::{
50    CoreResourceMsg, CoreResourceThread, ReferrerPolicy, ResourceThreads, fetch_async,
51};
52use profile_traits::{
53    generic_channel as profile_generic_channel, ipc as profile_ipc, mem as profile_mem,
54    time as profile_time,
55};
56use rustc_hash::{FxBuildHasher, FxHashMap};
57use script_bindings::callback::OwnerWindow;
58use script_bindings::cell::{DomRefCell, RefMut};
59use script_bindings::interfaces::GlobalScopeHelpers;
60use script_bindings::reflector::DomObject;
61use script_bindings::settings_stack::run_a_script;
62use servo_base::generic_channel;
63use servo_base::generic_channel::{GenericCallback, GenericSend};
64use servo_base::id::{
65    BlobId, BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineId,
66    ServiceWorkerId, ServiceWorkerRegistrationId, TEST_WEBVIEW_ID, WebViewId,
67};
68use servo_config::pref;
69use servo_constellation_traits::{
70    BlobData, BlobImpl, BroadcastChannelMsg, ConstellationInterest, FileBlob, MessagePortImpl,
71    MessagePortMsg, PortMessageTask, ScriptToConstellationChan, ScriptToConstellationMessage,
72    ScriptToConstellationSender,
73};
74use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
75use storage_traits::StorageThreads;
76use strum::VariantArray;
77use timers::{TimerEventRequest, TimerId};
78use uuid::Uuid;
79#[cfg(feature = "webgpu")]
80use webgpu_traits::{DeviceLostReason, WebGPUDevice};
81
82use crate::DomTypeHolder;
83use crate::dom::bindings::codegen::Bindings::BroadcastChannelBinding::BroadcastChannelMethods;
84use crate::dom::bindings::codegen::Bindings::EventSourceBinding::EventSource_Binding::EventSourceMethods;
85use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
86use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
87use crate::dom::bindings::codegen::Bindings::NotificationBinding::NotificationPermissionCallback;
88use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{
89    PermissionName, PermissionState,
90};
91#[cfg(feature = "webgpu")]
92use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUDeviceLostReason;
93use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
94use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
95use crate::dom::bindings::conversions::{root_from_object, root_from_object_static};
96#[cfg(feature = "js_backtrace")]
97use crate::dom::bindings::error::LAST_EXCEPTION_BACKTRACE;
98use crate::dom::bindings::error::{
99    Error, ErrorInfo, Fallible, report_pending_exception, take_and_report_pending_exception_for_api,
100};
101use crate::dom::bindings::frozenarray::CachedFrozenArray;
102use crate::dom::bindings::inheritance::Castable;
103use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
104use crate::dom::bindings::reflector::DomGlobal;
105use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
106use crate::dom::bindings::settings_stack::{entry_global, incumbent_global};
107use crate::dom::bindings::str::DOMString;
108use crate::dom::bindings::structuredclone;
109use crate::dom::bindings::trace::{CustomTraceable, HashMapTracedValues, RootedTraceableBox};
110use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
111use crate::dom::blob::Blob;
112use crate::dom::broadcastchannel::BroadcastChannel;
113use crate::dom::dedicatedworkerglobalscope::{
114    DedicatedWorkerControlMsg, DedicatedWorkerGlobalScope,
115};
116use crate::dom::dissimilaroriginwindow::DissimilarOriginWindow;
117use crate::dom::errorevent::ErrorEvent;
118use crate::dom::event::{Event, EventBubbles, EventCancelable};
119use crate::dom::eventsource::EventSource;
120use crate::dom::eventtarget::EventTarget;
121use crate::dom::file::File;
122use crate::dom::globalscope::script_execution::{
123    ErrorReporting, evaluate_script, fill_compile_options,
124};
125use crate::dom::idbfactory::IDBFactory;
126use crate::dom::messageport::MessagePort;
127use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
128use crate::dom::performance::performance::Performance;
129use crate::dom::performance::performanceentry::EntryType;
130use crate::dom::promise::Promise;
131use crate::dom::readablestream::{CrossRealmTransformReadable, ReadableStream};
132use crate::dom::serviceworker::ServiceWorker;
133use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
134use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
135use crate::dom::sharedworkerglobalscope::SharedWorkerGlobalScope;
136use crate::dom::stream::underlyingsourcecontainer::UnderlyingSourceType;
137use crate::dom::stream::writablestream::CrossRealmTransformWritable;
138use crate::dom::transformstream::CrossRealmTransform;
139use crate::dom::types::{AbortSignal, DebuggerGlobalScope, MessageEvent};
140#[cfg(feature = "webgpu")]
141use crate::dom::webgpu::gpudevice::GPUDevice;
142#[cfg(feature = "webgpu")]
143use crate::dom::webgpu::identityhub::IdentityHub;
144use crate::dom::window::Window;
145use crate::dom::workerglobalscope::WorkerGlobalScope;
146use crate::dom::workletglobalscope::WorkletGlobalScope;
147use crate::fetch::{DeferredFetchRecordId, FetchGroup, QueuedDeferredFetchRecord};
148use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
149use crate::microtask::MicrotaskRunnable;
150use crate::network_listener::{FetchResponseListener, NetworkListener};
151use crate::realms::enter_auto_realm;
152use crate::script_module::{
153    ImportMap, ModuleRequest, ModuleStatus, ResolvedModule, ScriptFetchOptions,
154};
155use crate::script_runtime::ThreadSafeJSContext;
156use crate::script_thread::{ScriptThread, with_script_thread};
157use crate::task_manager::TaskManager;
158use crate::task_source::SendableTaskSource;
159use crate::timers::{
160    IsInterval, OneshotTimerCallback, OneshotTimerHandle, OneshotTimers, TimerCallback,
161    TimerEventId, TimerSource,
162};
163use crate::unminify::unminified_path;
164
165#[derive(JSTraceable, MallocSizeOf)]
166pub(crate) struct AutoCloseWorker {
167    /// <https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-closing>
168    #[conditional_malloc_size_of]
169    closing: Arc<AtomicBool>,
170    /// A handle to join on the worker thread.
171    #[ignore_malloc_size_of = "JoinHandle"]
172    join_handle: Option<JoinHandle<()>>,
173    /// A sender of control messages,
174    /// currently only used to signal shutdown.
175    #[no_trace]
176    control_sender: Sender<DedicatedWorkerControlMsg>,
177    /// The context to request an interrupt on the worker thread.
178    #[ignore_malloc_size_of = "mozjs"]
179    #[no_trace]
180    context: ThreadSafeJSContext,
181}
182
183impl Drop for AutoCloseWorker {
184    /// <https://html.spec.whatwg.org/multipage/#terminate-a-worker>
185    fn drop(&mut self) {
186        // Step 1. Set the worker's `WorkerGlobalScope` object's closing flag to true.
187        self.closing.store(true, Ordering::SeqCst);
188
189        if self
190            .control_sender
191            .send(DedicatedWorkerControlMsg::Exit)
192            .is_err()
193        {
194            warn!("Couldn't send an exit message to a dedicated worker.");
195        }
196
197        self.context.request_interrupt_callback();
198
199        // Step 2. If there are any tasks queued in the `WorkerGlobalScope` object's relevant agent's event loop's task queues, discard them without processing them.
200        // Step 3. Abort the script currently running in the worker.
201        // Step 4. If the worker's WorkerGlobalScope object is actually a DedicatedWorkerGlobalScope object (i.e. the worker is a dedicated worker), then empty the port message queue of the port that the worker's implicit port is entangled with.
202        // TODO Steps 2-4.
203        if self
204            .join_handle
205            .take()
206            .expect("No handle to join on worker.")
207            .join()
208            .is_err()
209        {
210            warn!("Failed to join on dedicated worker thread.");
211        }
212    }
213}
214
215#[dom_struct]
216pub(crate) struct GlobalScope {
217    eventtarget: EventTarget,
218
219    /// The message-port router id for this global, if it is managing ports.
220    message_port_state: DomRefCell<MessagePortState>,
221
222    /// The broadcast channels state this global, if it is managing any.
223    broadcast_channel_state: DomRefCell<BroadcastChannelState>,
224
225    /// Tracks the number of active listeners per constellation interest category.
226    /// When the count transitions from 0 to 1, a RegisterInterest message is sent.
227    /// When it transitions from 1 to 0, an UnregisterInterest message is sent.
228    #[no_trace]
229    constellation_interest_counts: RefCell<HashMap<ConstellationInterest, usize>>,
230
231    /// The blobs managed by this global, if any.
232    blob_state: DomRefCell<HashMapTracedValues<BlobId, BlobInfo, FxBuildHasher>>,
233
234    /// <https://w3c.github.io/ServiceWorker/#environment-settings-object-service-worker-registration-object-map>
235    registration_map: DomRefCell<
236        HashMapTracedValues<
237            ServiceWorkerRegistrationId,
238            Dom<ServiceWorkerRegistration>,
239            FxBuildHasher,
240        >,
241    >,
242
243    /// <https://w3c.github.io/IndexedDB/#factory-interface>
244    indexeddb: MutNullableDom<IDBFactory>,
245
246    /// <https://w3c.github.io/ServiceWorker/#environment-settings-object-service-worker-object-map>
247    worker_map: DomRefCell<HashMapTracedValues<ServiceWorkerId, Dom<ServiceWorker>, FxBuildHasher>>,
248
249    /// Timers (milliseconds) used by the Console API.
250    console_timers: DomRefCell<HashMap<DOMString, Instant>>,
251
252    /// module map is used when importing JavaScript modules
253    /// <https://html.spec.whatwg.org/multipage/#concept-settings-object-module-map>
254    #[ignore_malloc_size_of = "mozjs"]
255    module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
256
257    /// For providing instructions to an optional devtools server.
258    #[no_trace]
259    devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
260
261    /// For sending messages to the memory profiler.
262    #[no_trace]
263    mem_profiler_chan: profile_mem::ProfilerChan,
264
265    /// For sending messages to the time profiler.
266    #[no_trace]
267    time_profiler_chan: profile_time::ProfilerChan,
268
269    /// A handle for communicating messages to the constellation thread.
270    #[no_trace]
271    script_to_constellation_sender: ScriptToConstellationSender,
272
273    /// A handle for communicating messages to the Embedder.
274    #[no_trace]
275    script_to_embedder_chan: ScriptToEmbedderChan,
276
277    /// <https://html.spec.whatwg.org/multipage/#in-error-reporting-mode>
278    in_error_reporting_mode: Cell<bool>,
279
280    /// Associated resource threads for use by DOM objects like XMLHttpRequest,
281    /// including resource_thread and filemanager_thread
282    #[no_trace]
283    resource_threads: ResourceThreads,
284
285    /// Associated resource threads for use by DOM objects like XMLHttpRequest,
286    /// including indexeddb thread and storage_thread
287    #[no_trace]
288    storage_threads: StorageThreads,
289
290    /// <https://html.spec.whatwg.org/multipage/#concept-environment-creation-url>
291    #[no_trace]
292    creation_url: DomRefCell<ServoUrl>,
293
294    /// <https://html.spec.whatwg.org/multipage/#concept-environment-top-level-creation-url>
295    #[no_trace]
296    top_level_creation_url: Option<ServoUrl>,
297
298    /// A map for storing the previous permission state read results.
299    permission_state_invocation_results: DomRefCell<HashMap<PermissionName, PermissionState>>,
300
301    /// Vector storing closing references of all workers
302    list_auto_close_worker: DomRefCell<Vec<AutoCloseWorker>>,
303
304    /// Vector storing references of all eventsources.
305    event_source_tracker: DOMTracker<EventSource>,
306
307    /// Dependent AbortSignals that must be kept alive per
308    /// <https://dom.spec.whatwg.org/#abort-signal-garbage-collection?
309    abort_signal_dependents: DomRefCell<IndexSet<Dom<AbortSignal>>>,
310
311    /// Storage for watching rejected promises waiting for some client to
312    /// consume their rejection.
313    /// Promises in this list have been rejected in the last turn of the
314    /// event loop without the rejection being handled.
315    /// Note that this can contain nullptrs in place of promises removed because
316    /// they're consumed before it'd be reported.
317    ///
318    /// <https://html.spec.whatwg.org/multipage/#about-to-be-notified-rejected-promises-list>
319    #[ignore_malloc_size_of = "mozjs"]
320    // `Heap` values must stay boxed, as they need semantics like `Pin`
321    // (that is, they cannot be moved).
322    #[allow(clippy::vec_box)]
323    uncaught_rejections: DomRefCell<Vec<Box<Heap<*mut JSObject>>>>,
324
325    /// Promises in this list have previously been reported as rejected
326    /// (because they were in the above list), but the rejection was handled
327    /// in the last turn of the event loop.
328    ///
329    /// <https://html.spec.whatwg.org/multipage/#outstanding-rejected-promises-weak-set>
330    #[ignore_malloc_size_of = "mozjs"]
331    // `Heap` values must stay boxed, as they need semantics like `Pin`
332    // (that is, they cannot be moved).
333    #[allow(clippy::vec_box)]
334    consumed_rejections: DomRefCell<Vec<Box<Heap<*mut JSObject>>>>,
335
336    /// Identity Manager for WebGPU resources
337    #[ignore_malloc_size_of = "defined in wgpu"]
338    #[no_trace]
339    #[cfg(feature = "webgpu")]
340    gpu_id_hub: Arc<IdentityHub>,
341
342    /// WebGPU devices
343    #[cfg(feature = "webgpu")]
344    gpu_devices: DomRefCell<HashMapTracedValues<WebGPUDevice, WeakRef<GPUDevice>, FxBuildHasher>>,
345
346    // https://w3c.github.io/performance-timeline/#supportedentrytypes-attribute
347    #[ignore_malloc_size_of = "mozjs"]
348    frozen_supported_performance_entry_types: CachedFrozenArray,
349
350    /// The stack of active group labels for the Console APIs.
351    console_group_stack: DomRefCell<Vec<DOMString>>,
352
353    /// The count map for the Console APIs.
354    ///
355    /// <https://console.spec.whatwg.org/#count>
356    console_count_map: DomRefCell<HashMap<DOMString, usize>>,
357
358    /// Is considered in a secure context
359    inherited_secure_context: Option<bool>,
360
361    /// Directory to store unminified scripts for this window if unminify-js
362    /// opt is enabled.
363    unminified_js_dir: Option<String>,
364
365    /// The byte length queuing strategy size function that will be initialized once
366    /// `size` getter of `ByteLengthQueuingStrategy` is called.
367    ///
368    /// <https://streams.spec.whatwg.org/#byte-length-queuing-strategy-size-function>
369    #[ignore_malloc_size_of = "callbacks are hard"]
370    byte_length_queuing_strategy_size_function: OnceCell<Rc<Function>>,
371
372    /// The count queuing strategy size function that will be initialized once
373    /// `size` getter of `CountQueuingStrategy` is called.
374    ///
375    /// <https://streams.spec.whatwg.org/#count-queuing-strategy-size-function>
376    #[ignore_malloc_size_of = "callbacks are hard"]
377    count_queuing_strategy_size_function: OnceCell<Rc<Function>>,
378
379    #[ignore_malloc_size_of = "callbacks are hard"]
380    notification_permission_request_callback_map:
381        DomRefCell<HashMap<String, Rc<NotificationPermissionCallback>>>,
382
383    /// An import map allows control over module specifier resolution.
384    /// For now, only Window global objects have their import map modified from the initial empty one.
385    ///
386    /// <https://html.spec.whatwg.org/multipage/#import-maps>
387    import_map: DomRefCell<ImportMap>,
388
389    /// <https://html.spec.whatwg.org/multipage/#resolved-module-set>
390    resolved_module_set: DomRefCell<HashSet<ResolvedModule>>,
391
392    /// The [`FontContext`] for this [`GlobalScope`] if it has one. This is used for
393    /// canvas and layout, so if this [`GlobalScope`] doesn't need to use either, this
394    /// might be `None`.
395    #[conditional_malloc_size_of]
396    #[no_trace]
397    font_context: Option<Arc<FontContext>>,
398
399    /// <https://fetch.spec.whatwg.org/#environment-settings-object-fetch-group>
400    #[no_trace]
401    fetch_group: RefCell<FetchGroup>,
402}
403
404/// A wrapper for glue-code between the ipc router and the event-loop.
405struct MessageListener {
406    task_source: SendableTaskSource,
407    context: Trusted<GlobalScope>,
408}
409
410/// A wrapper for broadcasts coming in over IPC, and the event-loop.
411struct BroadcastListener {
412    task_source: SendableTaskSource,
413    context: Trusted<GlobalScope>,
414}
415
416type FileListenerCallback =
417    Box<dyn Fn(&mut js::context::JSContext, Rc<Promise>, Fallible<Vec<u8>>) + Send>;
418
419/// A wrapper for the handling of file data received by the ipc router
420struct FileListener {
421    /// State should progress as either of:
422    /// - Some(Empty) => Some(Receiving) => None
423    /// - Some(Empty) => None
424    state: Option<FileListenerState>,
425    task_source: SendableTaskSource,
426}
427
428enum FileListenerTarget {
429    Promise(TrustedPromise, FileListenerCallback),
430    Stream(Trusted<ReadableStream>),
431}
432
433enum FileListenerState {
434    Empty(FileListenerTarget),
435    Receiving(Vec<u8>, FileListenerTarget),
436}
437
438#[derive(JSTraceable, MallocSizeOf)]
439/// A holder of a weak reference for a DOM blob or file.
440pub(crate) enum BlobTracker {
441    /// A weak ref to a DOM file.
442    File(WeakRef<File>),
443    /// A weak ref to a DOM blob.
444    Blob(WeakRef<Blob>),
445}
446
447#[derive(JSTraceable, MallocSizeOf)]
448/// The info pertaining to a blob managed by this global.
449pub(crate) struct BlobInfo {
450    /// The weak ref to the corresponding DOM object.
451    tracker: BlobTracker,
452    /// The data and logic backing the DOM object.
453    #[no_trace]
454    blob_impl: BlobImpl,
455    /// Whether this blob has an outstanding URL,
456    /// <https://w3c.github.io/FileAPI/#url>.
457    has_url: bool,
458}
459
460/// The result of looking-up the data for a Blob,
461/// containing either the in-memory bytes,
462/// or the file-id.
463enum BlobResult {
464    Bytes(Vec<u8>),
465    File(Uuid, usize),
466}
467
468/// Data representing a message-port managed by this global.
469#[derive(JSTraceable, MallocSizeOf)]
470#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
471pub(crate) struct ManagedMessagePort {
472    /// The DOM port.
473    dom_port: Dom<MessagePort>,
474    /// The logic and data backing the DOM port.
475    /// The option is needed to take out the port-impl
476    /// as part of its transferring steps,
477    /// without having to worry about rooting the dom-port.
478    #[no_trace]
479    port_impl: Option<MessagePortImpl>,
480    /// We keep ports pending when they are first transfer-received,
481    /// and only add them, and ask the constellation to complete the transfer,
482    /// in a subsequent task if the port hasn't been re-transfered.
483    pending: bool,
484    /// Whether the port has been closed by script in this global,
485    /// so it can be removed.
486    explicitly_closed: bool,
487    /// The handler for `message` or `messageerror` used in the cross realm transform,
488    /// if any was setup with this port.
489    cross_realm_transform: Option<CrossRealmTransform>,
490}
491
492/// State representing whether this global is currently managing broadcast channels.
493#[derive(JSTraceable, MallocSizeOf)]
494#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
495pub(crate) enum BroadcastChannelState {
496    /// The broadcast-channel router id for this global, and a queue of managed channels.
497    /// Step 9, "sort destinations"
498    /// of <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage>
499    /// requires keeping track of creation order, hence the queue.
500    Managed(
501        #[no_trace] BroadcastChannelRouterId,
502        /// The map of channel-name to queue of channels, in order of creation.
503        HashMap<DOMString, VecDeque<Dom<BroadcastChannel>>>,
504    ),
505    /// This global is not managing any broadcast channels at this time.
506    UnManaged,
507}
508
509/// State representing whether this global is currently managing messageports.
510#[derive(JSTraceable, MallocSizeOf)]
511#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
512pub(crate) enum MessagePortState {
513    /// The message-port router id for this global, and a map of managed ports.
514    Managed(
515        #[no_trace] MessagePortRouterId,
516        HashMapTracedValues<MessagePortId, ManagedMessagePort, FxBuildHasher>,
517    ),
518    /// This global is not managing any ports at this time.
519    UnManaged,
520}
521
522impl BroadcastListener {
523    /// Handle a broadcast coming in over IPC,
524    /// by queueing the appropriate task on the relevant event-loop.
525    fn handle(&self, event: BroadcastChannelMsg) {
526        let context = self.context.clone();
527
528        // Note: strictly speaking we should just queue the message event tasks,
529        // not queue a task that then queues more tasks.
530        // This however seems to be hard to avoid in the light of the IPC.
531        // One can imagine queueing tasks directly,
532        // for channels that would be in the same script-thread.
533        self.task_source
534            .queue(task!(broadcast_message_event: move || {
535                let global = context.root();
536                // Step 10 of https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage,
537                // For each BroadcastChannel object destination in destinations, queue a task.
538                global.broadcast_message_event(event, None);
539            }));
540    }
541}
542
543impl MessageListener {
544    /// A new message came in, handle it via a task enqueued on the event-loop.
545    /// A task is required, since we are using a trusted globalscope,
546    /// and we can only access the root from the event-loop.
547    fn notify(&self, msg: MessagePortMsg) {
548        match msg {
549            MessagePortMsg::CompleteTransfer(ports) => {
550                let context = self.context.clone();
551                self.task_source.queue(
552                    task!(process_complete_transfer: move |cx| {
553                        let global = context.root();
554
555                        let router_id = match global.port_router_id() {
556                            Some(router_id) => router_id,
557                            None => {
558                                // If not managing any ports, no transfer can succeed,
559                                // so just send back everything.
560                                let _ = global.script_to_constellation_chan().send(
561                                    ScriptToConstellationMessage::MessagePortTransferResult(None, vec![], ports),
562                                );
563                                return;
564                            }
565                        };
566
567                        let mut succeeded = vec![];
568                        let mut failed = FxHashMap::default();
569
570                        for (id, info) in ports.into_iter() {
571                            if global.is_managing_port(&id) {
572                                succeeded.push(id);
573                                global.complete_port_transfer(
574                                    cx,
575                                    id,
576                                    info.port_message_queue,
577                                    info.disentangled,
578                                );
579                            } else {
580                                failed.insert(id, info);
581                            }
582                        }
583                        let _ = global.script_to_constellation_chan().send(
584                            ScriptToConstellationMessage::MessagePortTransferResult(Some(router_id), succeeded, failed),
585                        );
586                    })
587                );
588            },
589            MessagePortMsg::CompletePendingTransfer(port_id, info) => {
590                let context = self.context.clone();
591                self.task_source.queue(task!(complete_pending: move |cx| {
592                    let global = context.root();
593                    global.complete_port_transfer(cx, port_id, info.port_message_queue, info.disentangled);
594                }));
595            },
596            MessagePortMsg::CompleteDisentanglement(port_id) => {
597                let context = self.context.clone();
598                self.task_source
599                    .queue(task!(try_complete_disentanglement: move |cx| {
600                        let global = context.root();
601                        global.try_complete_disentanglement(cx, port_id);
602                    }));
603            },
604            MessagePortMsg::NewTask(port_id, task) => {
605                let context = self.context.clone();
606                self.task_source.queue(task!(process_new_task: move |cx| {
607                    let global = context.root();
608                    global.route_task_to_port(cx, port_id, task);
609                }));
610            },
611        }
612    }
613}
614
615/// Callback used to enqueue file chunks to streams as part of FileListener.
616fn stream_handle_incoming(
617    cx: &mut js::context::JSContext,
618    stream: &ReadableStream,
619    bytes: Fallible<Vec<u8>>,
620) {
621    match bytes {
622        Ok(b) => {
623            stream.enqueue_native(cx, b);
624        },
625        Err(e) => {
626            stream.error_native(cx, e);
627        },
628    }
629}
630
631/// Callback used to close streams as part of FileListener.
632fn stream_handle_eof(cx: &mut js::context::JSContext, stream: &ReadableStream) {
633    stream.controller_close_native(cx);
634}
635
636impl FileListener {
637    fn handle(&mut self, msg: FileManagerResult<ReadFileProgress>) {
638        match msg {
639            Ok(ReadFileProgress::Meta(blob_buf)) => match self.state.take() {
640                Some(FileListenerState::Empty(target)) => {
641                    let bytes = if let FileListenerTarget::Stream(ref trusted_stream) = target {
642                        let trusted = trusted_stream.clone();
643
644                        let task = task!(enqueue_stream_chunk: move |cx| {
645                            let stream = trusted.root();
646                            stream_handle_incoming(cx, &stream, Ok(blob_buf.bytes));
647                        });
648                        self.task_source.queue(task);
649
650                        Vec::with_capacity(0)
651                    } else {
652                        blob_buf.bytes
653                    };
654
655                    self.state = Some(FileListenerState::Receiving(bytes, target));
656                },
657                _ => panic!(
658                    "Unexpected FileListenerState when receiving ReadFileProgress::Meta msg."
659                ),
660            },
661            Ok(ReadFileProgress::Partial(mut bytes_in)) => match self.state.take() {
662                Some(FileListenerState::Receiving(mut bytes, target)) => {
663                    if let FileListenerTarget::Stream(ref trusted_stream) = target {
664                        let trusted = trusted_stream.clone();
665
666                        let task = task!(enqueue_stream_chunk: move |cx| {
667                            let stream = trusted.root();
668                            stream_handle_incoming(cx, &stream, Ok(bytes_in));
669                        });
670
671                        self.task_source.queue(task);
672                    } else {
673                        bytes.append(&mut bytes_in);
674                    };
675
676                    self.state = Some(FileListenerState::Receiving(bytes, target));
677                },
678                _ => panic!(
679                    "Unexpected FileListenerState when receiving ReadFileProgress::Partial msg."
680                ),
681            },
682            Ok(ReadFileProgress::EOF) => match self.state.take() {
683                Some(FileListenerState::Receiving(bytes, target)) => match target {
684                    FileListenerTarget::Promise(trusted_promise, callback) => {
685                        let task = task!(resolve_promise: move |cx| {
686                            let promise = trusted_promise.root();
687                            let mut realm = enter_auto_realm(cx, &*promise.global());
688                            callback(&mut realm, promise, Ok(bytes));
689                        });
690
691                        self.task_source.queue(task);
692                    },
693                    FileListenerTarget::Stream(trusted_stream) => {
694                        let task = task!(enqueue_stream_chunk: move |cx| {
695                            let stream = trusted_stream.root();
696                            stream_handle_eof(cx, &stream);
697                        });
698
699                        self.task_source.queue(task);
700                    },
701                },
702                _ => {
703                    panic!("Unexpected FileListenerState when receiving ReadFileProgress::EOF msg.")
704                },
705            },
706            Err(_) => match self.state.take() {
707                Some(FileListenerState::Receiving(_, target)) |
708                Some(FileListenerState::Empty(target)) => {
709                    let error = Err(Error::Network(None));
710
711                    match target {
712                        FileListenerTarget::Promise(trusted_promise, callback) => {
713                            self.task_source.queue(task!(reject_promise: move |cx| {
714                                let promise = trusted_promise.root();
715                                let mut realm = enter_auto_realm(cx, &*promise.global());
716                                callback(&mut realm, promise, error);
717                            }));
718                        },
719                        FileListenerTarget::Stream(trusted_stream) => {
720                            self.task_source.queue(task!(error_stream: move |cx| {
721                                let stream = trusted_stream.root();
722                                stream_handle_incoming(cx, &stream, error);
723                            }));
724                        },
725                    }
726                },
727                _ => panic!("Unexpected FileListenerState when receiving Err msg."),
728            },
729        }
730    }
731}
732
733impl GlobalScope {
734    /// <https://storage.spec.whatwg.org/#obtain-a-storage-key-for-non-storage-purposes>
735    pub(crate) fn obtain_storage_key_for_non_storage_purposes(&self) -> ImmutableOrigin {
736        // Step 1: Let origin be environment’s origin if environment is an environment settings object; otherwise environment’s creation URL’s origin.
737        // Step 2: Return a tuple consisting of origin.
738        self.origin().immutable().clone()
739    }
740
741    /// <https://storage.spec.whatwg.org/#obtain-a-storage-key>
742    pub(crate) fn obtain_storage_key(&self) -> Option<ImmutableOrigin> {
743        // Step 1: Let key be the result of running obtain a storage key for non-storage purposes
744        // with environment.
745        let key = self.obtain_storage_key_for_non_storage_purposes();
746
747        // Step 2: If key's origin is an opaque origin, then return failure.
748        if let ImmutableOrigin::Opaque(_) = key {
749            return None;
750        }
751
752        // Step 3: If the user has disabled storage, then return failure.
753        if !pref!(dom_indexeddb_enabled) {
754            return None;
755        }
756
757        // Step 4: Return key.
758        Some(key)
759    }
760
761    /// A sender to the event loop of this global scope. This either sends to the Worker event loop
762    /// or the ScriptThread event loop in the case of a `Window`. This can be `None` for dedicated
763    /// workers that are not currently handling a message.
764    pub(crate) fn webview_id(&self) -> Option<WebViewId> {
765        if let Some(window) = self.downcast::<Window>() {
766            return Some(window.webview_id());
767        }
768        if let Some(worker) = self.downcast::<DedicatedWorkerGlobalScope>() {
769            return Some(worker.webview_id());
770        }
771        if let Some(worker) = self.downcast::<SharedWorkerGlobalScope>() {
772            return Some(worker.webview_id());
773        }
774        // TODO: This should only return None for ServiceWorkerGlobalScope.
775        None
776    }
777
778    #[allow(clippy::too_many_arguments)]
779    pub(crate) fn new_inherited(
780        devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
781        mem_profiler_chan: profile_mem::ProfilerChan,
782        time_profiler_chan: profile_time::ProfilerChan,
783        script_to_constellation_sender: ScriptToConstellationSender,
784        script_to_embedder_chan: ScriptToEmbedderChan,
785        resource_threads: ResourceThreads,
786        storage_threads: StorageThreads,
787        creation_url: ServoUrl,
788        top_level_creation_url: Option<ServoUrl>,
789        #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
790        inherited_secure_context: Option<bool>,
791        unminify_js: bool,
792        font_context: Option<Arc<FontContext>>,
793    ) -> Self {
794        Self {
795            message_port_state: DomRefCell::new(MessagePortState::UnManaged),
796            broadcast_channel_state: DomRefCell::new(BroadcastChannelState::UnManaged),
797            constellation_interest_counts: RefCell::new(HashMap::new()),
798            blob_state: Default::default(),
799            eventtarget: EventTarget::new_inherited(),
800            registration_map: DomRefCell::new(HashMapTracedValues::new_fx()),
801            indexeddb: Default::default(),
802            worker_map: DomRefCell::new(HashMapTracedValues::new_fx()),
803            console_timers: DomRefCell::new(Default::default()),
804            module_map: DomRefCell::new(Default::default()),
805            devtools_chan,
806            mem_profiler_chan,
807            time_profiler_chan,
808            script_to_constellation_sender,
809            script_to_embedder_chan,
810            in_error_reporting_mode: Default::default(),
811            resource_threads,
812            storage_threads,
813            creation_url: DomRefCell::new(creation_url),
814            top_level_creation_url,
815            permission_state_invocation_results: Default::default(),
816            list_auto_close_worker: Default::default(),
817            event_source_tracker: DOMTracker::new(),
818            abort_signal_dependents: Default::default(),
819            uncaught_rejections: Default::default(),
820            consumed_rejections: Default::default(),
821            #[cfg(feature = "webgpu")]
822            gpu_id_hub,
823            #[cfg(feature = "webgpu")]
824            gpu_devices: DomRefCell::new(HashMapTracedValues::new_fx()),
825            frozen_supported_performance_entry_types: CachedFrozenArray::new(),
826            console_group_stack: DomRefCell::new(Vec::new()),
827            console_count_map: Default::default(),
828            inherited_secure_context,
829            unminified_js_dir: unminify_js.then(|| unminified_path("unminified-js")),
830            byte_length_queuing_strategy_size_function: OnceCell::new(),
831            count_queuing_strategy_size_function: OnceCell::new(),
832            notification_permission_request_callback_map: Default::default(),
833            import_map: Default::default(),
834            resolved_module_set: Default::default(),
835            font_context,
836            fetch_group: Default::default(),
837        }
838    }
839
840    /// The message-port router Id of the global, if any
841    fn port_router_id(&self) -> Option<MessagePortRouterId> {
842        if let MessagePortState::Managed(id, _message_ports) = &*self.message_port_state.borrow() {
843            Some(*id)
844        } else {
845            None
846        }
847    }
848
849    /// Is this global managing a given port?
850    fn is_managing_port(&self, port_id: &MessagePortId) -> bool {
851        if let MessagePortState::Managed(_router_id, message_ports) =
852            &*self.message_port_state.borrow()
853        {
854            return message_ports.contains_key(port_id);
855        }
856        false
857    }
858
859    fn with_timers<T>(&self, f: impl FnOnce(&OneshotTimers) -> T) -> T {
860        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
861            f(worker.timers())
862        } else if let Some(window) = self.downcast::<Window>() {
863            window.with_timers(f)
864        } else {
865            unreachable!("Unsupported global type retrieving timers")
866        }
867    }
868
869    pub(crate) fn font_context(&self) -> Option<&Arc<FontContext>> {
870        self.font_context.as_ref()
871    }
872
873    /// <https://w3c.github.io/ServiceWorker/#get-the-service-worker-registration-object>
874    #[allow(clippy::too_many_arguments)]
875    pub(crate) fn get_serviceworker_registration(
876        &self,
877        cx: &mut js::context::JSContext,
878        script_url: &ServoUrl,
879        scope: &ServoUrl,
880        registration_id: ServiceWorkerRegistrationId,
881        installing_worker: Option<ServiceWorkerId>,
882        _waiting_worker: Option<ServiceWorkerId>,
883        _active_worker: Option<ServiceWorkerId>,
884    ) -> DomRoot<ServiceWorkerRegistration> {
885        // Step 1
886        {
887            let registrations = self.registration_map.borrow_mut();
888
889            if let Some(registration) = registrations.get(&registration_id) {
890                // Step 3
891                return DomRoot::from_ref(&**registration);
892            }
893        }
894
895        // Step 2.1 -> 2.5
896        let new_registration =
897            ServiceWorkerRegistration::new(cx, self, scope.clone(), registration_id);
898
899        // Step 2.6
900        if let Some(worker_id) = installing_worker {
901            let worker = self.get_serviceworker(cx, script_url, scope, worker_id);
902            new_registration.set_installing(&worker);
903        }
904
905        // TODO: 2.7 (waiting worker)
906
907        // TODO: 2.8 (active worker)
908
909        // Step 2.9
910        self.registration_map
911            .borrow_mut()
912            .insert(registration_id, Dom::from_ref(&*new_registration));
913
914        // Step 3
915        new_registration
916    }
917
918    /// <https://w3c.github.io/ServiceWorker/#get-the-service-worker-object>
919    pub(crate) fn get_serviceworker(
920        &self,
921        cx: &mut js::context::JSContext,
922        script_url: &ServoUrl,
923        scope: &ServoUrl,
924        worker_id: ServiceWorkerId,
925    ) -> DomRoot<ServiceWorker> {
926        // Step 1
927        {
928            let workers = self.worker_map.borrow_mut();
929
930            if let Some(worker) = workers.get(&worker_id) {
931                // Step 3
932                return DomRoot::from_ref(&**worker);
933            }
934        }
935
936        // Step 2.1
937        // TODO: step 2.2, worker state.
938        let new_worker = ServiceWorker::new(cx, self, script_url.clone(), scope.clone(), worker_id);
939
940        // Step 2.3
941        self.worker_map
942            .borrow_mut()
943            .insert(worker_id, Dom::from_ref(&*new_worker));
944
945        // Step 3
946        new_worker
947    }
948
949    /// Complete the transfer of a message-port.
950    fn complete_port_transfer(
951        &self,
952        cx: &mut js::context::JSContext,
953        port_id: MessagePortId,
954        tasks: VecDeque<PortMessageTask>,
955        disentangled: bool,
956    ) {
957        let should_start = if let MessagePortState::Managed(_id, message_ports) =
958            &mut *self.message_port_state.borrow_mut()
959        {
960            match message_ports.get_mut(&port_id) {
961                None => {
962                    panic!("complete_port_transfer called for an unknown port.");
963                },
964                Some(managed_port) => {
965                    if managed_port.pending {
966                        panic!("CompleteTransfer msg received for a pending port.");
967                    }
968                    if let Some(port_impl) = managed_port.port_impl.as_mut() {
969                        port_impl.complete_transfer(tasks);
970                        if disentangled {
971                            port_impl.disentangle();
972                            managed_port.dom_port.disentangle();
973                        }
974                        port_impl.enabled()
975                    } else {
976                        panic!("managed-port has no port-impl.");
977                    }
978                },
979            }
980        } else {
981            panic!("complete_port_transfer called for an unknown port.");
982        };
983        if should_start {
984            self.start_message_port(cx, &port_id);
985        }
986    }
987
988    /// The closing of `otherPort`, if it is in a different global.
989    /// <https://html.spec.whatwg.org/multipage/#disentangle>
990    fn try_complete_disentanglement(
991        &self,
992        cx: &mut js::context::JSContext,
993        port_id: MessagePortId,
994    ) {
995        let dom_port = if let MessagePortState::Managed(_id, message_ports) =
996            &mut *self.message_port_state.borrow_mut()
997        {
998            if let Some(managed_port) = message_ports.get_mut(&port_id) {
999                if managed_port.pending {
1000                    unreachable!("CompleteDisentanglement msg received for a pending port.");
1001                }
1002                let port_impl = managed_port
1003                    .port_impl
1004                    .as_mut()
1005                    .expect("managed-port has no port-impl.");
1006                port_impl.disentangle();
1007                managed_port.dom_port.as_rooted()
1008            } else {
1009                // Note: this, and the other return below,
1010                // can happen if the port has already been transferred out of this global,
1011                // in which case the disentanglement will complete along with the transfer.
1012                return;
1013            }
1014        } else {
1015            return;
1016        };
1017
1018        // Fire an event named close at otherPort.
1019        dom_port.upcast().fire_event(cx, atom!("close"));
1020
1021        let res = self.script_to_constellation_chan().send(
1022            ScriptToConstellationMessage::DisentanglePorts(port_id, None),
1023        );
1024        if res.is_err() {
1025            warn!("Sending DisentanglePorts failed");
1026        }
1027    }
1028
1029    /// Clean-up DOM related resources
1030    pub(crate) fn perform_a_dom_garbage_collection_checkpoint(&self) {
1031        self.perform_a_message_port_garbage_collection_checkpoint();
1032        self.perform_a_blob_garbage_collection_checkpoint();
1033        self.perform_a_broadcast_channel_garbage_collection_checkpoint();
1034        self.perform_an_abort_signal_garbage_collection_checkpoint();
1035    }
1036
1037    /// Remove the routers for ports and broadcast-channels.
1038    /// Drain the list of workers.
1039    pub(crate) fn remove_web_messaging_and_dedicated_workers_infra(&self) {
1040        self.remove_message_ports_router();
1041        self.remove_broadcast_channel_router();
1042
1043        // Drop each ref to a worker explicitly now,
1044        // which will send a shutdown signal,
1045        // and join on the worker thread.
1046        self.list_auto_close_worker
1047            .borrow_mut()
1048            .drain(0..)
1049            .for_each(drop);
1050    }
1051
1052    /// Update our state to un-managed,
1053    /// and tell the constellation to drop the sender to our message-port router.
1054    fn remove_message_ports_router(&self) {
1055        if let MessagePortState::Managed(router_id, _message_ports) =
1056            &*self.message_port_state.borrow()
1057        {
1058            let _ = self.script_to_constellation_chan().send(
1059                ScriptToConstellationMessage::RemoveMessagePortRouter(*router_id),
1060            );
1061        }
1062        *self.message_port_state.borrow_mut() = MessagePortState::UnManaged;
1063    }
1064
1065    /// Update our state to un-managed,
1066    /// and tell the constellation to drop the sender to our broadcast router.
1067    fn remove_broadcast_channel_router(&self) {
1068        if let BroadcastChannelState::Managed(router_id, _channels) =
1069            &*self.broadcast_channel_state.borrow()
1070        {
1071            let _ = self.script_to_constellation_chan().send(
1072                ScriptToConstellationMessage::RemoveBroadcastChannelRouter(
1073                    *router_id,
1074                    self.origin().immutable().clone(),
1075                ),
1076            );
1077        }
1078        *self.broadcast_channel_state.borrow_mut() = BroadcastChannelState::UnManaged;
1079    }
1080
1081    /// <https://html.spec.whatwg.org/multipage/#disentangle>
1082    pub(crate) fn disentangle_port(&self, cx: &mut js::context::JSContext, port: &MessagePort) {
1083        let initiator_port = port.message_port_id();
1084        // Let otherPort be the MessagePort which initiatorPort was entangled with.
1085        let Some(other_port) = port.disentangle() else {
1086            // Assert: otherPort exists.
1087            // Note: ignoring the assert,
1088            // because the streams spec seems to disentangle ports that are disentangled already.
1089            return;
1090        };
1091
1092        // Disentangle initiatorPort and otherPort, so that they are no longer entangled or associated with each other.
1093        // Note: this is done in part here, and in part at the constellation(if otherPort is in another global).
1094        let dom_port = if let MessagePortState::Managed(_id, message_ports) =
1095            &mut *self.message_port_state.borrow_mut()
1096        {
1097            let mut dom_port = None;
1098            for port_id in &[initiator_port, &other_port] {
1099                match message_ports.get_mut(port_id) {
1100                    None => {
1101                        continue;
1102                    },
1103                    Some(managed_port) => {
1104                        let port_impl = managed_port
1105                            .port_impl
1106                            .as_mut()
1107                            .expect("managed-port has no port-impl.");
1108                        managed_port.dom_port.disentangle();
1109                        port_impl.disentangle();
1110
1111                        if **port_id == other_port {
1112                            dom_port = Some(managed_port.dom_port.as_rooted())
1113                        }
1114                    },
1115                }
1116            }
1117            dom_port
1118        } else {
1119            panic!("disentangle_port called on a global not managing any ports.");
1120        };
1121
1122        // Fire an event named close at `otherPort`.
1123        // Note: done here if the port is managed by the same global as `initialPort`.
1124        if let Some(dom_port) = dom_port {
1125            dom_port.upcast().fire_event(cx, atom!("close"));
1126        }
1127
1128        let chan = self.script_to_constellation_chan();
1129        let initiator_port = *initiator_port;
1130        self.task_manager()
1131            .port_message_queue()
1132            .queue(task!(post_message: move || {
1133                // Note: we do this in a task to ensure it doesn't affect messages that are still to be routed,
1134                // see the task queueing in `post_messageport_msg`.
1135                let res = chan.send(ScriptToConstellationMessage::DisentanglePorts(initiator_port, Some(other_port)));
1136                if res.is_err() {
1137                    warn!("Sending DisentanglePorts failed");
1138                }
1139            }));
1140    }
1141
1142    /// <https://html.spec.whatwg.org/multipage/#entangle>
1143    pub(crate) fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) {
1144        if let MessagePortState::Managed(_id, message_ports) =
1145            &mut *self.message_port_state.borrow_mut()
1146        {
1147            for (port_id, entangled_id) in &[(port1, port2), (port2, port1)] {
1148                match message_ports.get_mut(port_id) {
1149                    None => {
1150                        return warn!("entangled_ports called on a global not managing the port.");
1151                    },
1152                    Some(managed_port) => {
1153                        if let Some(port_impl) = managed_port.port_impl.as_mut() {
1154                            managed_port.dom_port.entangle(*entangled_id);
1155                            port_impl.entangle(*entangled_id);
1156                        } else {
1157                            panic!("managed-port has no port-impl.");
1158                        }
1159                    },
1160                }
1161            }
1162        } else {
1163            panic!("entangled_ports called on a global not managing any ports.");
1164        }
1165
1166        let _ = self
1167            .script_to_constellation_chan()
1168            .send(ScriptToConstellationMessage::EntanglePorts(port1, port2));
1169    }
1170
1171    /// Handle the transfer of a port in the current task.
1172    pub(crate) fn mark_port_as_transferred(&self, port_id: &MessagePortId) -> MessagePortImpl {
1173        if let MessagePortState::Managed(_id, message_ports) =
1174            &mut *self.message_port_state.borrow_mut()
1175        {
1176            let mut port_impl = message_ports
1177                .remove(port_id)
1178                .map(|ref mut managed_port| {
1179                    managed_port
1180                        .port_impl
1181                        .take()
1182                        .expect("Managed port doesn't have a port-impl.")
1183                })
1184                .expect("mark_port_as_transferred called on a global not managing the port.");
1185            port_impl.set_has_been_shipped();
1186            let _ = self
1187                .script_to_constellation_chan()
1188                .send(ScriptToConstellationMessage::MessagePortShipped(*port_id));
1189            port_impl
1190        } else {
1191            panic!("mark_port_as_transferred called on a global not managing any ports.");
1192        }
1193    }
1194
1195    /// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
1196    pub(crate) fn start_message_port(
1197        &self,
1198        cx: &mut js::context::JSContext,
1199        port_id: &MessagePortId,
1200    ) {
1201        let (message_buffer, dom_port) = if let MessagePortState::Managed(_id, message_ports) =
1202            &mut *self.message_port_state.borrow_mut()
1203        {
1204            let (message_buffer, dom_port) = match message_ports.get_mut(port_id) {
1205                None => panic!("start_message_port called on a unknown port."),
1206                Some(managed_port) => {
1207                    if let Some(port_impl) = managed_port.port_impl.as_mut() {
1208                        (port_impl.start(), managed_port.dom_port.as_rooted())
1209                    } else {
1210                        panic!("managed-port has no port-impl.");
1211                    }
1212                },
1213            };
1214            (message_buffer, dom_port)
1215        } else {
1216            return warn!("start_message_port called on a global not managing any ports.");
1217        };
1218        if let Some(message_buffer) = message_buffer {
1219            for task in message_buffer {
1220                self.route_task_to_port(cx, *port_id, task);
1221            }
1222            if dom_port.disentangled() {
1223                // <https://html.spec.whatwg.org/multipage/#disentangle>
1224                // Fire an event named close at otherPort.
1225                dom_port.upcast().fire_event(cx, atom!("close"));
1226
1227                let res = self.script_to_constellation_chan().send(
1228                    ScriptToConstellationMessage::DisentanglePorts(*port_id, None),
1229                );
1230                if res.is_err() {
1231                    warn!("Sending DisentanglePorts failed");
1232                }
1233            }
1234        }
1235    }
1236
1237    /// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
1238    pub(crate) fn close_message_port(&self, port_id: &MessagePortId) {
1239        if let MessagePortState::Managed(_id, message_ports) =
1240            &mut *self.message_port_state.borrow_mut()
1241        {
1242            match message_ports.get_mut(port_id) {
1243                None => panic!("close_message_port called on an unknown port."),
1244                Some(managed_port) => {
1245                    if let Some(port_impl) = managed_port.port_impl.as_mut() {
1246                        port_impl.close();
1247                        managed_port.explicitly_closed = true;
1248                    } else {
1249                        panic!("managed-port has no port-impl.");
1250                    }
1251                },
1252            };
1253        } else {
1254            warn!("close_message_port called on a global not managing any ports.")
1255        }
1256    }
1257
1258    /// <https://html.spec.whatwg.org/multipage/#message-port-post-message-steps>
1259    // Steps 6 and 7
1260    pub(crate) fn post_messageport_msg(&self, port_id: MessagePortId, task: PortMessageTask) {
1261        if let MessagePortState::Managed(_id, message_ports) =
1262            &mut *self.message_port_state.borrow_mut()
1263        {
1264            let entangled_port = match message_ports.get_mut(&port_id) {
1265                None => panic!("post_messageport_msg called on an unknown port."),
1266                Some(managed_port) => {
1267                    if let Some(port_impl) = managed_port.port_impl.as_mut() {
1268                        port_impl.entangled_port_id()
1269                    } else {
1270                        panic!("managed-port has no port-impl.");
1271                    }
1272                },
1273            };
1274            if let Some(entangled_id) = entangled_port {
1275                // Step 7
1276                let this = Trusted::new(self);
1277                self.task_manager()
1278                    .port_message_queue()
1279                    .queue(task!(post_message: move |cx| {
1280                        let global = this.root();
1281                        // Note: we do this in a task, as this will ensure the global and constellation
1282                        // are aware of any transfer that might still take place in the current task.
1283                        global.route_task_to_port(cx, entangled_id, task);
1284                    }));
1285            }
1286        } else {
1287            warn!("post_messageport_msg called on a global not managing any ports.");
1288        }
1289    }
1290
1291    /// If we don't know about the port,
1292    /// send the message to the constellation for routing.
1293    fn re_route_port_task(&self, port_id: MessagePortId, task: PortMessageTask) {
1294        let _ = self.script_to_constellation_chan().send(
1295            ScriptToConstellationMessage::RerouteMessagePort(port_id, task),
1296        );
1297    }
1298
1299    /// <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage>
1300    /// Step 7 and following steps.
1301    pub(crate) fn schedule_broadcast(&self, msg: BroadcastChannelMsg, channel_id: &Uuid) {
1302        // First, broadcast locally.
1303        self.broadcast_message_event(msg.clone(), Some(channel_id));
1304
1305        if let BroadcastChannelState::Managed(router_id, _) =
1306            &*self.broadcast_channel_state.borrow()
1307        {
1308            // Second, broadcast to other globals via the constellation.
1309            //
1310            // Note: for globals in the same script-thread,
1311            // we could skip the hop to the constellation.
1312            let _ = self.script_to_constellation_chan().send(
1313                ScriptToConstellationMessage::ScheduleBroadcast(*router_id, msg),
1314            );
1315        } else {
1316            panic!("Attemps to broadcast a message via global not managing any channels.");
1317        }
1318    }
1319
1320    /// <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage>
1321    /// Step 7 and following steps.
1322    pub(crate) fn broadcast_message_event(
1323        &self,
1324        event: BroadcastChannelMsg,
1325        channel_id: Option<&Uuid>,
1326    ) {
1327        let BroadcastChannelState::Managed(_, channels) = &*self.broadcast_channel_state.borrow()
1328        else {
1329            return;
1330        };
1331
1332        let BroadcastChannelMsg {
1333            data,
1334            origin,
1335            channel_name,
1336        } = event;
1337
1338        // Step 7, a few preliminary steps.
1339
1340        // - Check the worker is not closing.
1341        if let Some(worker) = self.downcast::<WorkerGlobalScope>() &&
1342            worker.is_closing()
1343        {
1344            return;
1345        }
1346
1347        // - Check the associated document is fully-active.
1348        if let Some(window) = self.downcast::<Window>() &&
1349            !window.Document().is_fully_active()
1350        {
1351            return;
1352        }
1353
1354        // - Check for a case-sensitive match for the name of the channel.
1355        let Some(channels) = channels.get(&channel_name.into()) else {
1356            return;
1357        };
1358        channels
1359            .iter()
1360            .filter(|channel| {
1361                // Step 8.
1362                // Filter out the sender.
1363                if let Some(id) = channel_id {
1364                    channel.id() != id
1365                } else {
1366                    true
1367                }
1368            })
1369            .map(|channel| DomRoot::from_ref(&**channel))
1370            // Step 9, sort by creation order,
1371            // done by using a queue to store channels in creation order.
1372            .for_each(|channel| {
1373                let data = data.clone_for_broadcast();
1374                let origin = origin.clone();
1375
1376                // Step 10: Queue a task on the DOM manipulation task-source,
1377                // to fire the message event
1378                let channel = Trusted::new(&*channel);
1379                let global = Trusted::new(self);
1380                self.task_manager().dom_manipulation_task_source().queue(
1381                    task!(process_pending_port_messages: move |cx| {
1382                        let destination = channel.root();
1383                        let global = global.root();
1384
1385                        // 10.1 Check for closed flag.
1386                        if destination.closed() {
1387                            return;
1388                        }
1389
1390                        rooted!(&in(cx) let mut message = UndefinedValue());
1391
1392                        // Step 10.3 StructuredDeserialize(serialized, targetRealm).
1393                        if let Ok(ports) = structuredclone::read(cx, &global, data, message.handle_mut()) {
1394                            // Step 10.4, Fire an event named message at destination.
1395                            MessageEvent::dispatch_jsval(
1396                                cx,
1397                                destination.upcast(),
1398                                &global,
1399                                message.handle(),
1400                                Some(&origin.ascii_serialization()),
1401                                None,
1402                                ports,
1403                            );
1404                        } else {
1405                            // Step 10.3, fire an event named messageerror at destination.
1406                            MessageEvent::dispatch_error(cx, destination.upcast(), &global);
1407                        }
1408                    })
1409                );
1410            });
1411    }
1412
1413    /// <https://html.spec.whatwg.org/multipage/#encoding-parsing-a-url>
1414    pub(crate) fn encoding_parse_a_url(&self, url: &str) -> Result<ServoUrl, url::ParseError> {
1415        if let Some(window) = self.downcast::<Window>() {
1416            return window.Document().encoding_parse_a_url(url);
1417        }
1418
1419        // encoding parsing for worker environments.
1420        let base = self.api_base_url();
1421        base.join(url)
1422    }
1423
1424    /// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable>
1425    /// The "Add a handler for port’s message event with the following steps:"
1426    /// and "Add a handler for port’s messageerror event with the following steps:" part.
1427    pub(crate) fn note_cross_realm_transform_readable(
1428        &self,
1429        cross_realm_transform_readable: &CrossRealmTransformReadable,
1430        port_id: &MessagePortId,
1431    ) {
1432        let MessagePortState::Managed(_id, message_ports) =
1433            &mut *self.message_port_state.borrow_mut()
1434        else {
1435            unreachable!(
1436                "Cross realm transform readable must be called on a global managing ports"
1437            );
1438        };
1439
1440        let Some(managed_port) = message_ports.get_mut(port_id) else {
1441            unreachable!("Cross realm transform readable must match a managed port");
1442        };
1443
1444        managed_port.cross_realm_transform = Some(CrossRealmTransform::Readable(
1445            cross_realm_transform_readable.clone(),
1446        ));
1447    }
1448
1449    /// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
1450    /// The "Add a handler for port’s message event with the following steps:"
1451    /// and "Add a handler for port’s messageerror event with the following steps:" part.
1452    pub(crate) fn note_cross_realm_transform_writable(
1453        &self,
1454        cross_realm_transform_writable: &CrossRealmTransformWritable,
1455        port_id: &MessagePortId,
1456    ) {
1457        let MessagePortState::Managed(_id, message_ports) =
1458            &mut *self.message_port_state.borrow_mut()
1459        else {
1460            unreachable!(
1461                "Cross realm transform writable must be called on a global managing ports"
1462            );
1463        };
1464
1465        let Some(managed_port) = message_ports.get_mut(port_id) else {
1466            unreachable!("Cross realm transform writable must match a managed port");
1467        };
1468
1469        managed_port.cross_realm_transform = Some(CrossRealmTransform::Writable(
1470            cross_realm_transform_writable.clone(),
1471        ));
1472    }
1473
1474    /// Custom routing logic, followed by the task steps of
1475    /// <https://html.spec.whatwg.org/multipage/#message-port-post-message-steps>
1476    fn route_task_to_port(
1477        &self,
1478        cx: &mut js::context::JSContext,
1479        port_id: MessagePortId,
1480        task: PortMessageTask,
1481    ) {
1482        rooted!(&in(cx) let mut cross_realm_transform = None);
1483
1484        let should_dispatch = if let MessagePortState::Managed(_id, message_ports) =
1485            &mut *self.message_port_state.borrow_mut()
1486        {
1487            if !message_ports.contains_key(&port_id) {
1488                self.re_route_port_task(port_id, task);
1489                return;
1490            }
1491            match message_ports.get_mut(&port_id) {
1492                None => panic!("route_task_to_port called for an unknown port."),
1493                Some(managed_port) => {
1494                    // If the port is not enabled yet, or if is awaiting the completion of it's transfer,
1495                    // the task will be buffered and dispatched upon enablement or completion of the transfer.
1496                    if let Some(port_impl) = managed_port.port_impl.as_mut() {
1497                        let to_dispatch = port_impl.handle_incoming(task).map(|to_dispatch| {
1498                            (DomRoot::from_ref(&*managed_port.dom_port), to_dispatch)
1499                        });
1500                        cross_realm_transform.set(managed_port.cross_realm_transform.clone());
1501                        to_dispatch
1502                    } else {
1503                        panic!("managed-port has no port-impl.");
1504                    }
1505                },
1506            }
1507        } else {
1508            self.re_route_port_task(port_id, task);
1509            return;
1510        };
1511
1512        // Add a task that runs the following steps to the port message queue of targetPort:
1513        // Note: we are in the task, and running the relevant steps.
1514
1515        // Let finalTargetPort be the MessagePort in whose port message queue the task now finds itself.
1516        if let Some((dom_port, PortMessageTask { origin, data })) = should_dispatch {
1517            // Let messageEventTarget be finalTargetPort's message event target.
1518            let message_event_target = dom_port.upcast();
1519
1520            // Let targetRealm be finalTargetPort's relevant realm.
1521            // Done via the routing logic here and in the constellation: `self` is the target realm.
1522
1523            // Let messageClone be deserializeRecord.[[Deserialized]].
1524            // Re-ordered because we need to pass it to `structuredclone::read`.
1525            rooted!(&in(cx) let mut message_clone = UndefinedValue());
1526
1527            let mut realm = enter_auto_realm(cx, self);
1528            let cx = &mut realm.current_realm();
1529
1530            // Note: this is necessary, on top of entering the realm above,
1531            // for the call to `GlobalScope::incumbent`,
1532            // in `MessagePort::post_message_impl` to succeed.
1533            run_a_script::<DomTypeHolder, _, _>(cx, self, |cx| {
1534                // Let deserializeRecord be StructuredDeserializeWithTransfer(serializeWithTransferResult, targetRealm).
1535                // Let newPorts be a new frozen array
1536                // consisting of all MessagePort objects in deserializeRecord.[[TransferredValues]],
1537                // if any, maintaining their relative order.
1538                // Note: both done in `structuredclone::read`.
1539                if let Ok(ports) = structuredclone::read(cx, self, data, message_clone.handle_mut())
1540                {
1541                    // Note: if this port is used to transfer a stream, we handle the events in Rust.
1542                    if let Some(transform) = cross_realm_transform.deref().as_ref() {
1543                        match transform {
1544                            // Add a handler for port’s message event with the following steps:
1545                            // from <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable>
1546                            CrossRealmTransform::Readable(readable) => {
1547                                readable.handle_message(
1548                                    cx,
1549                                    self,
1550                                    &dom_port,
1551                                    message_clone.handle(),
1552                                );
1553                            },
1554                            // Add a handler for port’s message event with the following steps:
1555                            // from <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
1556                            CrossRealmTransform::Writable(writable) => {
1557                                writable.handle_message(cx, self, message_clone.handle());
1558                            },
1559                        }
1560                    } else {
1561                        // Fire an event named message at messageEventTarget,
1562                        // using MessageEvent,
1563                        // with the data attribute initialized to messageClone
1564                        // and the ports attribute initialized to newPorts.
1565                        MessageEvent::dispatch_jsval(
1566                            cx,
1567                            message_event_target,
1568                            self,
1569                            message_clone.handle(),
1570                            Some(&origin.ascii_serialization()),
1571                            None,
1572                            ports,
1573                        );
1574                    }
1575                } else if let Some(transform) = cross_realm_transform.deref().as_ref() {
1576                    match transform {
1577                        // Add a handler for port’s messageerror event with the following steps:
1578                        // from <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable>
1579                        CrossRealmTransform::Readable(readable) => {
1580                            readable.handle_error(cx, self, &dom_port);
1581                        },
1582                        // Add a handler for port’s messageerror event with the following steps:
1583                        // from <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
1584                        CrossRealmTransform::Writable(writable) => {
1585                            writable.handle_error(cx, self, &dom_port);
1586                        },
1587                    }
1588                } else {
1589                    // If this throws an exception, catch it,
1590                    // fire an event named messageerror at messageEventTarget,
1591                    // using MessageEvent, and then return.
1592                    MessageEvent::dispatch_error(cx, message_event_target, self);
1593                }
1594            });
1595        }
1596    }
1597
1598    /// Check all ports that have been transfer-received in the previous task,
1599    /// and complete their transfer if they haven't been re-transferred.
1600    pub(crate) fn maybe_add_pending_ports(&self) {
1601        if let MessagePortState::Managed(router_id, message_ports) =
1602            &mut *self.message_port_state.borrow_mut()
1603        {
1604            let to_be_added: Vec<MessagePortId> = message_ports
1605                .iter()
1606                .filter_map(|(id, managed_port)| {
1607                    if managed_port.pending {
1608                        Some(*id)
1609                    } else {
1610                        None
1611                    }
1612                })
1613                .collect();
1614            for id in to_be_added.iter() {
1615                let managed_port = message_ports
1616                    .get_mut(id)
1617                    .expect("Collected port-id to match an entry");
1618                if !managed_port.pending {
1619                    panic!("Only pending ports should be found in to_be_added")
1620                }
1621                managed_port.pending = false;
1622            }
1623            let _ = self.script_to_constellation_chan().send(
1624                ScriptToConstellationMessage::CompleteMessagePortTransfer(*router_id, to_be_added),
1625            );
1626        } else {
1627            warn!("maybe_add_pending_ports called on a global not managing any ports.");
1628        }
1629    }
1630
1631    /// <https://html.spec.whatwg.org/multipage/#ports-and-garbage-collection>
1632    pub(crate) fn perform_a_message_port_garbage_collection_checkpoint(&self) {
1633        let is_empty = if let MessagePortState::Managed(_id, message_ports) =
1634            &mut *self.message_port_state.borrow_mut()
1635        {
1636            let to_be_removed: Vec<MessagePortId> = message_ports
1637                .iter()
1638                .filter_map(|(id, managed_port)| {
1639                    if managed_port.explicitly_closed {
1640                        Some(*id)
1641                    } else {
1642                        None
1643                    }
1644                })
1645                .collect();
1646            for id in to_be_removed {
1647                message_ports.remove(&id);
1648            }
1649            // Note: ports are only removed throught explicit closure by script in this global.
1650            // TODO: #25772
1651            // TODO: remove ports when we can be sure their port message queue is empty(via the constellation).
1652            message_ports.is_empty()
1653        } else {
1654            false
1655        };
1656        if is_empty {
1657            self.remove_message_ports_router();
1658        }
1659    }
1660
1661    /// Remove broadcast-channels that are closed.
1662    /// TODO: Also remove them if they do not have an event-listener.
1663    /// see <https://github.com/servo/servo/issues/25772>
1664    pub(crate) fn perform_a_broadcast_channel_garbage_collection_checkpoint(&self) {
1665        let is_empty = if let BroadcastChannelState::Managed(router_id, channels) =
1666            &mut *self.broadcast_channel_state.borrow_mut()
1667        {
1668            channels.retain(|name, ref mut channels| {
1669                channels.retain(|chan| !chan.closed());
1670                if channels.is_empty() {
1671                    let _ = self.script_to_constellation_chan().send(
1672                        ScriptToConstellationMessage::RemoveBroadcastChannelNameInRouter(
1673                            *router_id,
1674                            name.to_string(),
1675                            self.origin().immutable().clone(),
1676                        ),
1677                    );
1678                    false
1679                } else {
1680                    true
1681                }
1682            });
1683            channels.is_empty()
1684        } else {
1685            false
1686        };
1687        if is_empty {
1688            self.remove_broadcast_channel_router();
1689        }
1690    }
1691
1692    /// Register a dependent AbortSignal that may need to be kept alive
1693    /// <https://dom.spec.whatwg.org/#abort-signal-garbage-collection>
1694    pub(crate) fn register_dependent_abort_signal(&self, signal: &AbortSignal) {
1695        self.abort_signal_dependents
1696            .borrow_mut()
1697            .insert(Dom::from_ref(signal));
1698    }
1699
1700    /// Clean up dependent AbortSignals that no longer satisfy the GC predicate.
1701    pub(crate) fn perform_an_abort_signal_garbage_collection_checkpoint(&self) {
1702        let mut set = self.abort_signal_dependents.borrow_mut();
1703
1704        set.retain(|dom_signal| dom_signal.must_keep_alive_for_gc());
1705    }
1706
1707    /// Start tracking a broadcast-channel.
1708    pub(crate) fn track_broadcast_channel(&self, dom_channel: &BroadcastChannel) {
1709        let mut current_state = self.broadcast_channel_state.borrow_mut();
1710
1711        if let BroadcastChannelState::UnManaged = &*current_state {
1712            let context = Trusted::new(self);
1713            let listener = BroadcastListener {
1714                task_source: self.task_manager().dom_manipulation_task_source().into(),
1715                context,
1716            };
1717            let broadcast_control_callback = GenericCallback::new(move |message| match message {
1718                Ok(msg) => listener.handle(msg),
1719                Err(err) => warn!("Error receiving a BroadcastChannelMsg: {:?}", err),
1720            })
1721            .expect("Could not generate callback");
1722            let router_id = BroadcastChannelRouterId::new();
1723            *current_state = BroadcastChannelState::Managed(router_id, HashMap::new());
1724            let _ = self.script_to_constellation_chan().send(
1725                ScriptToConstellationMessage::NewBroadcastChannelRouter(
1726                    router_id,
1727                    broadcast_control_callback,
1728                    self.origin().immutable().clone(),
1729                ),
1730            );
1731        }
1732
1733        if let BroadcastChannelState::Managed(router_id, channels) = &mut *current_state {
1734            let entry = channels.entry(dom_channel.Name()).or_insert_with(|| {
1735                let _ = self.script_to_constellation_chan().send(
1736                    ScriptToConstellationMessage::NewBroadcastChannelNameInRouter(
1737                        *router_id,
1738                        String::from(dom_channel.Name()),
1739                        self.origin().immutable().clone(),
1740                    ),
1741                );
1742                VecDeque::new()
1743            });
1744            entry.push_back(Dom::from_ref(dom_channel));
1745        } else {
1746            panic!("track_broadcast_channel should have first switched the state to managed.");
1747        }
1748    }
1749
1750    /// Start tracking a message-port
1751    pub(crate) fn track_message_port(
1752        &self,
1753        dom_port: &MessagePort,
1754        port_impl: Option<MessagePortImpl>,
1755    ) {
1756        let mut current_state = self.message_port_state.borrow_mut();
1757
1758        if let MessagePortState::UnManaged = &*current_state {
1759            // Setup a route for IPC, for messages from the constellation to our ports.
1760            let context = Trusted::new(self);
1761            let listener = MessageListener {
1762                task_source: self.task_manager().port_message_queue().into(),
1763                context,
1764            };
1765
1766            let port_control_callback = GenericCallback::new(move |message| match message {
1767                Ok(msg) => listener.notify(msg),
1768                Err(err) => warn!("Error receiving a MessagePortMsg: {:?}", err),
1769            })
1770            .expect("Could not create callback");
1771            let router_id = MessagePortRouterId::new();
1772            *current_state = MessagePortState::Managed(router_id, HashMapTracedValues::new_fx());
1773            let _ = self.script_to_constellation_chan().send(
1774                ScriptToConstellationMessage::NewMessagePortRouter(
1775                    router_id,
1776                    port_control_callback,
1777                ),
1778            );
1779        }
1780
1781        if let MessagePortState::Managed(router_id, message_ports) = &mut *current_state {
1782            if let Some(port_impl) = port_impl {
1783                // We keep transfer-received ports as "pending",
1784                // and only ask the constellation to complete the transfer
1785                // if they're not re-shipped in the current task.
1786                message_ports.insert(
1787                    *dom_port.message_port_id(),
1788                    ManagedMessagePort {
1789                        port_impl: Some(port_impl),
1790                        dom_port: Dom::from_ref(dom_port),
1791                        pending: true,
1792                        explicitly_closed: false,
1793                        cross_realm_transform: None,
1794                    },
1795                );
1796
1797                // Queue a task to complete the transfer,
1798                // unless the port is re-transferred in the current task.
1799                let this = Trusted::new(self);
1800                self.task_manager().port_message_queue().queue(
1801                    task!(process_pending_port_messages: move || {
1802                        let target_global = this.root();
1803                        target_global.maybe_add_pending_ports();
1804                    }),
1805                );
1806            } else {
1807                // If this is a newly-created port, let the constellation immediately know.
1808                let port_impl = MessagePortImpl::new(*dom_port.message_port_id());
1809                message_ports.insert(
1810                    *dom_port.message_port_id(),
1811                    ManagedMessagePort {
1812                        port_impl: Some(port_impl),
1813                        dom_port: Dom::from_ref(dom_port),
1814                        pending: false,
1815                        explicitly_closed: false,
1816                        cross_realm_transform: None,
1817                    },
1818                );
1819                let _ = self.script_to_constellation_chan().send(
1820                    ScriptToConstellationMessage::NewMessagePort(
1821                        *router_id,
1822                        *dom_port.message_port_id(),
1823                    ),
1824                );
1825            };
1826        } else {
1827            panic!("track_message_port should have first switched the state to managed.");
1828        }
1829    }
1830
1831    /// <https://html.spec.whatwg.org/multipage/#serialization-steps>
1832    /// defined at <https://w3c.github.io/FileAPI/#blob-section>.
1833    /// Get the snapshot state and underlying bytes of the blob.
1834    pub(crate) fn serialize_blob(&self, blob_id: &BlobId) -> BlobImpl {
1835        // Note: we combine the snapshot state and underlying bytes into one call,
1836        // which seems spec compliant.
1837        // See https://w3c.github.io/FileAPI/#snapshot-state
1838        let bytes = self
1839            .get_blob_bytes(blob_id)
1840            .expect("Could not read bytes from blob as part of serialization steps.");
1841        let type_string = self.get_blob_type_string(blob_id);
1842
1843        // Note: the new BlobImpl is a clone, but with it's own BlobId.
1844        BlobImpl::new_from_bytes(bytes, type_string)
1845    }
1846
1847    fn track_blob_info(&self, blob_info: BlobInfo, blob_id: BlobId) {
1848        self.blob_state.borrow_mut().insert(blob_id, blob_info);
1849    }
1850
1851    /// Start tracking a blob
1852    pub(crate) fn track_blob(&self, dom_blob: &Blob, blob_impl: BlobImpl) {
1853        let blob_id = blob_impl.blob_id();
1854
1855        let blob_info = BlobInfo {
1856            blob_impl,
1857            tracker: BlobTracker::Blob(WeakRef::new(dom_blob)),
1858            has_url: false,
1859        };
1860
1861        self.track_blob_info(blob_info, blob_id);
1862    }
1863
1864    /// Start tracking a file
1865    pub(crate) fn track_file(&self, file: &File, blob_impl: BlobImpl) {
1866        let blob_id = blob_impl.blob_id();
1867
1868        let blob_info = BlobInfo {
1869            blob_impl,
1870            tracker: BlobTracker::File(WeakRef::new(file)),
1871            has_url: false,
1872        };
1873
1874        self.track_blob_info(blob_info, blob_id);
1875    }
1876
1877    /// Clean-up any file or blob that is unreachable from script,
1878    /// unless it has an oustanding blob url.
1879    /// <https://w3c.github.io/FileAPI/#lifeTime>
1880    fn perform_a_blob_garbage_collection_checkpoint(&self) {
1881        let mut blob_state = self.blob_state.borrow_mut();
1882        blob_state.0.retain(|_id, blob_info| {
1883            let garbage_collected = match &blob_info.tracker {
1884                BlobTracker::File(weak) => weak.root().is_none(),
1885                BlobTracker::Blob(weak) => weak.root().is_none(),
1886            };
1887            if garbage_collected && !blob_info.has_url {
1888                if let BlobData::File(f) = blob_info.blob_impl.blob_data() {
1889                    self.decrement_file_ref(f.get_id());
1890                }
1891                false
1892            } else {
1893                true
1894            }
1895        });
1896    }
1897
1898    /// Clean-up all file related resources on document unload.
1899    /// <https://w3c.github.io/FileAPI/#lifeTime>
1900    pub(crate) fn clean_up_all_file_resources(&self) {
1901        self.blob_state
1902            .borrow_mut()
1903            .drain()
1904            .for_each(|(_id, blob_info)| {
1905                if let BlobData::File(f) = blob_info.blob_impl.blob_data() {
1906                    self.decrement_file_ref(f.get_id());
1907                }
1908            });
1909    }
1910
1911    fn decrement_file_ref(&self, id: Uuid) {
1912        let origin = self.origin().immutable().clone();
1913
1914        let (tx, rx) = profile_generic_channel::channel(self.time_profiler_chan().clone()).unwrap();
1915
1916        let msg = FileManagerThreadMsg::DecRef(id, origin, tx);
1917        self.send_to_file_manager(msg);
1918        let _ = rx.recv();
1919    }
1920
1921    /// Get a slice to the inner data of a Blob,
1922    /// In the case of a File-backed blob, this might incur synchronous read and caching.
1923    pub(crate) fn get_blob_bytes(&self, blob_id: &BlobId) -> Result<Vec<u8>, ()> {
1924        let parent = {
1925            match *self.get_blob_data(blob_id) {
1926                BlobData::Sliced(parent, rel_pos) => Some((parent, rel_pos)),
1927                _ => None,
1928            }
1929        };
1930
1931        match parent {
1932            Some((parent_id, rel_pos)) => self.get_blob_bytes_non_sliced(&parent_id).map(|v| {
1933                let range = rel_pos.to_abs_range(v.len());
1934                v.index(range).to_vec()
1935            }),
1936            None => self.get_blob_bytes_non_sliced(blob_id),
1937        }
1938    }
1939
1940    /// Retrieve information about a specific blob from the blob store
1941    ///
1942    /// # Panics
1943    /// This function panics if there is no blob with the given ID.
1944    pub(crate) fn get_blob_data<'a>(&'a self, blob_id: &BlobId) -> Ref<'a, BlobData> {
1945        Ref::map(self.blob_state.borrow(), |blob_state| {
1946            blob_state
1947                .get(blob_id)
1948                .expect("get_blob_impl called for a unknown blob")
1949                .blob_impl
1950                .blob_data()
1951        })
1952    }
1953
1954    /// Get bytes from a non-sliced blob
1955    fn get_blob_bytes_non_sliced(&self, blob_id: &BlobId) -> Result<Vec<u8>, ()> {
1956        match *self.get_blob_data(blob_id) {
1957            BlobData::File(ref f) => {
1958                let (buffer, is_new_buffer) = match f.get_cache() {
1959                    Some(bytes) => (bytes, false),
1960                    None => {
1961                        let bytes = self.read_file(f.get_id())?;
1962                        (bytes, true)
1963                    },
1964                };
1965
1966                // Cache
1967                if is_new_buffer {
1968                    f.cache_bytes(buffer.clone());
1969                }
1970
1971                Ok(buffer)
1972            },
1973            BlobData::Memory(ref s) => Ok(s.clone()),
1974            BlobData::Sliced(_, _) => panic!("This blob doesn't have a parent."),
1975        }
1976    }
1977
1978    /// Get a slice to the inner data of a Blob,
1979    /// if it's a memory blob, or it's file-id and file-size otherwise.
1980    ///
1981    /// Note: this is almost a duplicate of `get_blob_bytes`,
1982    /// tweaked for integration with streams.
1983    /// TODO: merge with `get_blob_bytes` by way of broader integration with blob streams.
1984    fn get_blob_bytes_or_file_id(&self, blob_id: &BlobId) -> BlobResult {
1985        let parent = {
1986            match *self.get_blob_data(blob_id) {
1987                BlobData::Sliced(parent, rel_pos) => Some((parent, rel_pos)),
1988                _ => None,
1989            }
1990        };
1991
1992        match parent {
1993            Some((parent_id, rel_pos)) => {
1994                match self.get_blob_bytes_non_sliced_or_file_id(&parent_id) {
1995                    BlobResult::Bytes(bytes) => {
1996                        let range = rel_pos.to_abs_range(bytes.len());
1997                        BlobResult::Bytes(bytes.index(range).to_vec())
1998                    },
1999                    res => res,
2000                }
2001            },
2002            None => self.get_blob_bytes_non_sliced_or_file_id(blob_id),
2003        }
2004    }
2005
2006    /// Get bytes from a non-sliced blob if in memory, or it's file-id and file-size.
2007    ///
2008    /// Note: this is almost a duplicate of `get_blob_bytes_non_sliced`,
2009    /// tweaked for integration with streams.
2010    /// TODO: merge with `get_blob_bytes` by way of broader integration with blob streams.
2011    fn get_blob_bytes_non_sliced_or_file_id(&self, blob_id: &BlobId) -> BlobResult {
2012        match *self.get_blob_data(blob_id) {
2013            BlobData::File(ref f) => match f.get_cache() {
2014                Some(bytes) => BlobResult::Bytes(bytes),
2015                None => BlobResult::File(f.get_id(), f.get_size() as usize),
2016            },
2017            BlobData::Memory(ref s) => BlobResult::Bytes(s.clone()),
2018            BlobData::Sliced(_, _) => panic!("This blob doesn't have a parent."),
2019        }
2020    }
2021
2022    /// Get a copy of the type_string of a blob.
2023    pub(crate) fn get_blob_type_string(&self, blob_id: &BlobId) -> String {
2024        let blob_state = self.blob_state.borrow();
2025        let blob_info = blob_state
2026            .get(blob_id)
2027            .expect("get_blob_type_string called for a unknown blob.");
2028        blob_info.blob_impl.type_string()
2029    }
2030
2031    /// <https://w3c.github.io/FileAPI/#dfn-size>
2032    pub(crate) fn get_blob_size(&self, blob_id: &BlobId) -> u64 {
2033        let parent = {
2034            match *self.get_blob_data(blob_id) {
2035                BlobData::Sliced(parent, rel_pos) => Some((parent, rel_pos)),
2036                _ => None,
2037            }
2038        };
2039        match parent {
2040            Some((parent_id, rel_pos)) => {
2041                let parent_size = match *self.get_blob_data(&parent_id) {
2042                    BlobData::File(ref f) => f.get_size(),
2043                    BlobData::Memory(ref v) => v.len() as u64,
2044                    BlobData::Sliced(_, _) => panic!("Blob ancestry should be only one level."),
2045                };
2046                rel_pos.to_abs_range(parent_size as usize).len() as u64
2047            },
2048            None => match *self.get_blob_data(blob_id) {
2049                BlobData::File(ref f) => f.get_size(),
2050                BlobData::Memory(ref v) => v.len() as u64,
2051                BlobData::Sliced(_, _) => {
2052                    panic!("It was previously checked that this blob does not have a parent.")
2053                },
2054            },
2055        }
2056    }
2057
2058    pub(crate) fn get_blob_url_id(&self, blob_id: &BlobId) -> Uuid {
2059        let mut blob_state = self.blob_state.borrow_mut();
2060        let parent = {
2061            let blob_info = blob_state
2062                .get_mut(blob_id)
2063                .expect("get_blob_url_id called for a unknown blob.");
2064
2065            // Keep track of blobs with outstanding URLs.
2066            blob_info.has_url = true;
2067
2068            match blob_info.blob_impl.blob_data() {
2069                BlobData::Sliced(parent, rel_pos) => Some((*parent, *rel_pos)),
2070                _ => None,
2071            }
2072        };
2073        match parent {
2074            Some((parent_id, rel_pos)) => {
2075                let parent_info = blob_state
2076                    .get_mut(&parent_id)
2077                    .expect("Parent of blob whose url is requested is unknown.");
2078                let parent_file_id = self.promote(parent_info, /* set_valid is */ false);
2079                let parent_size = match parent_info.blob_impl.blob_data() {
2080                    BlobData::File(f) => f.get_size(),
2081                    BlobData::Memory(v) => v.len() as u64,
2082                    BlobData::Sliced(_, _) => panic!("Blob ancestry should be only one level."),
2083                };
2084                let parent_size = rel_pos.to_abs_range(parent_size as usize).len() as u64;
2085                let blob_info = blob_state
2086                    .get_mut(blob_id)
2087                    .expect("Blob whose url is requested is unknown.");
2088                self.create_sliced_url_id(blob_info, &parent_file_id, &rel_pos, parent_size)
2089            },
2090            None => {
2091                let blob_info = blob_state
2092                    .get_mut(blob_id)
2093                    .expect("Blob whose url is requested is unknown.");
2094                self.promote(blob_info, /* set_valid is */ true)
2095            },
2096        }
2097    }
2098
2099    /// Get a FileID representing sliced parent-blob content
2100    fn create_sliced_url_id(
2101        &self,
2102        blob_info: &mut BlobInfo,
2103        parent_file_id: &Uuid,
2104        rel_pos: &RelativePos,
2105        parent_len: u64,
2106    ) -> Uuid {
2107        let origin = self.origin().immutable().clone();
2108
2109        let (tx, rx) = profile_generic_channel::channel(self.time_profiler_chan().clone()).unwrap();
2110        let msg = FileManagerThreadMsg::AddSlicedURLEntry(*parent_file_id, *rel_pos, tx, origin);
2111        self.send_to_file_manager(msg);
2112        match rx.recv().expect("File manager thread is down.") {
2113            Ok(new_id) => {
2114                *blob_info.blob_impl.blob_data_mut() = BlobData::File(FileBlob::new(
2115                    new_id,
2116                    None,
2117                    None,
2118                    rel_pos.to_abs_range(parent_len as usize).len() as u64,
2119                ));
2120
2121                // Return the indirect id reference
2122                new_id
2123            },
2124            Err(_) => {
2125                // Return dummy id
2126                Uuid::new_v4()
2127            },
2128        }
2129    }
2130
2131    /// Send a PromoteMemory message to register a new blob URL entry
2132    /// with the file manager for the given byte data.
2133    /// Return the generated UUID.
2134    fn promote_memory_entry(
2135        &self,
2136        blob_info: &BlobInfo,
2137        blob_bytes: &[u8],
2138        set_valid: bool,
2139    ) -> Uuid {
2140        let origin = self.origin().immutable().clone();
2141        let blob_buf = BlobBuf {
2142            filename: None,
2143            type_string: blob_info.blob_impl.type_string(),
2144            size: blob_bytes.len() as u64,
2145            bytes: blob_bytes.to_vec(),
2146        };
2147        let id = Uuid::new_v4();
2148        let msg = FileManagerThreadMsg::PromoteMemory(id, blob_buf, set_valid, origin);
2149        self.send_to_file_manager(msg);
2150        id
2151    }
2152
2153    /// Promote non-Slice blob:
2154    /// 1. Memory-based: The bytes in data slice will be transferred to file manager thread.
2155    /// 2. File-based: If set_valid, then activate the FileID so it can serve as URL
2156    ///    Depending on set_valid, the returned FileID can be part of
2157    ///    valid or invalid Blob URL.
2158    pub(crate) fn promote(&self, blob_info: &mut BlobInfo, set_valid: bool) -> Uuid {
2159        let mut bytes = vec![];
2160
2161        match blob_info.blob_impl.blob_data_mut() {
2162            BlobData::Sliced(_, _) => {
2163                panic!("Sliced blobs should use create_sliced_url_id instead of promote.");
2164            },
2165            BlobData::File(f) => {
2166                if set_valid {
2167                    // File blobs with cached byte data (converted from Memory)
2168                    // need a unique UUID per URL.createObjectURL call.
2169                    if let Some(cached_bytes) = f.get_cache() {
2170                        return self.promote_memory_entry(blob_info, &cached_bytes, true);
2171                    }
2172
2173                    let origin = self.origin().immutable().clone();
2174                    let (tx, rx) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
2175
2176                    let msg = FileManagerThreadMsg::ActivateBlobURL(f.get_id(), tx, origin);
2177                    self.send_to_file_manager(msg);
2178
2179                    match rx.recv().unwrap() {
2180                        Ok(_) => return f.get_id(),
2181                        // Return a dummy id on error
2182                        Err(_) => return Uuid::new_v4(),
2183                    }
2184                } else {
2185                    // no need to activate
2186                    return f.get_id();
2187                }
2188            },
2189            BlobData::Memory(bytes_in) => mem::swap(bytes_in, &mut bytes),
2190        };
2191
2192        let id = self.promote_memory_entry(blob_info, &bytes, set_valid);
2193
2194        *blob_info.blob_impl.blob_data_mut() = BlobData::File(FileBlob::new(
2195            id,
2196            None,
2197            Some(bytes.to_vec()),
2198            bytes.len() as u64,
2199        ));
2200
2201        id
2202    }
2203
2204    fn send_to_file_manager(&self, msg: FileManagerThreadMsg) {
2205        let resource_threads = self.resource_threads();
2206        let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
2207    }
2208
2209    fn read_file(&self, id: Uuid) -> Result<Vec<u8>, ()> {
2210        let recv = self.send_msg(id);
2211        GlobalScope::read_msg(recv)
2212    }
2213
2214    /// <https://w3c.github.io/FileAPI/#blob-get-stream>
2215    pub(crate) fn get_blob_stream(
2216        &self,
2217        cx: &mut js::context::JSContext,
2218        blob_id: &BlobId,
2219    ) -> Fallible<DomRoot<ReadableStream>> {
2220        let (file_id, size) = match self.get_blob_bytes_or_file_id(blob_id) {
2221            BlobResult::Bytes(bytes) => {
2222                // If we have all the bytes in memory, queue them and close the stream.
2223                return ReadableStream::new_from_bytes_with_byte_reading_support(cx, self, bytes);
2224            },
2225            BlobResult::File(id, size) => (id, size),
2226        };
2227
2228        let stream = ReadableStream::new_with_external_underlying_byte_source(
2229            cx,
2230            self,
2231            UnderlyingSourceType::Blob(size),
2232        )?;
2233
2234        let recv = self.send_msg(file_id);
2235
2236        let trusted_stream = Trusted::new(&*stream);
2237        let mut file_listener = FileListener {
2238            state: Some(FileListenerState::Empty(FileListenerTarget::Stream(
2239                trusted_stream,
2240            ))),
2241            task_source: self.task_manager().file_reading_task_source().into(),
2242        };
2243
2244        ROUTER.add_typed_route(
2245            recv.to_ipc_receiver(),
2246            Box::new(move |msg| {
2247                file_listener.handle(msg.expect("Deserialization of file listener msg failed."));
2248            }),
2249        );
2250
2251        Ok(stream)
2252    }
2253
2254    pub(crate) fn read_file_async(
2255        &self,
2256        id: Uuid,
2257        promise: Rc<Promise>,
2258        callback: FileListenerCallback,
2259    ) {
2260        let recv = self.send_msg(id);
2261
2262        let trusted_promise = TrustedPromise::new(promise);
2263        let mut file_listener = FileListener {
2264            state: Some(FileListenerState::Empty(FileListenerTarget::Promise(
2265                trusted_promise,
2266                callback,
2267            ))),
2268            task_source: self.task_manager().file_reading_task_source().into(),
2269        };
2270
2271        ROUTER.add_typed_route(
2272            recv.to_ipc_receiver(),
2273            Box::new(move |msg| {
2274                file_listener.handle(msg.expect("Deserialization of file listener msg failed."));
2275            }),
2276        );
2277    }
2278
2279    fn send_msg(&self, id: Uuid) -> profile_ipc::IpcReceiver<FileManagerResult<ReadFileProgress>> {
2280        let resource_threads = self.resource_threads();
2281        let (chan, recv) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
2282        let origin = self.origin().immutable().clone();
2283        let msg = FileManagerThreadMsg::ReadFile(chan, id, origin);
2284        let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
2285        recv
2286    }
2287
2288    fn read_msg(
2289        receiver: profile_ipc::IpcReceiver<FileManagerResult<ReadFileProgress>>,
2290    ) -> Result<Vec<u8>, ()> {
2291        let mut bytes = vec![];
2292
2293        loop {
2294            match receiver.recv().unwrap() {
2295                Ok(ReadFileProgress::Meta(mut blob_buf)) => {
2296                    bytes.append(&mut blob_buf.bytes);
2297                },
2298                Ok(ReadFileProgress::Partial(mut bytes_in)) => {
2299                    bytes.append(&mut bytes_in);
2300                },
2301                Ok(ReadFileProgress::EOF) => {
2302                    return Ok(bytes);
2303                },
2304                Err(_) => return Err(()),
2305            }
2306        }
2307    }
2308
2309    pub(crate) fn permission_state_invocation_results(
2310        &self,
2311    ) -> &DomRefCell<HashMap<PermissionName, PermissionState>> {
2312        &self.permission_state_invocation_results
2313    }
2314
2315    pub(crate) fn track_worker(
2316        &self,
2317        closing: Arc<AtomicBool>,
2318        join_handle: JoinHandle<()>,
2319        control_sender: Sender<DedicatedWorkerControlMsg>,
2320        context: ThreadSafeJSContext,
2321    ) {
2322        self.list_auto_close_worker
2323            .borrow_mut()
2324            .push(AutoCloseWorker {
2325                closing,
2326                join_handle: Some(join_handle),
2327                control_sender,
2328                context,
2329            });
2330    }
2331
2332    pub(crate) fn track_event_source(&self, event_source: &EventSource) {
2333        self.event_source_tracker.track(event_source);
2334    }
2335
2336    pub(crate) fn close_event_sources(&self) -> bool {
2337        let mut canceled_any_fetch = false;
2338        self.event_source_tracker
2339            .for_each(
2340                |event_source: DomRoot<EventSource>| match event_source.ReadyState() {
2341                    2 => {},
2342                    _ => {
2343                        event_source.cancel();
2344                        canceled_any_fetch = true;
2345                    },
2346                },
2347            );
2348        canceled_any_fetch
2349    }
2350
2351    /// Returns the global scope of the realm that the given DOM object's reflector
2352    /// was created in.
2353    #[expect(unsafe_code)]
2354    pub(crate) fn from_reflector<T: DomObject>(reflector: &T) -> DomRoot<Self> {
2355        unsafe { GlobalScope::from_object(*reflector.reflector().get_jsobject()) }
2356    }
2357
2358    /// Returns the global scope of the realm that the given JS object was created in.
2359    #[expect(unsafe_code)]
2360    pub(crate) unsafe fn from_object(obj: *mut JSObject) -> DomRoot<Self> {
2361        assert!(!obj.is_null());
2362        let global = unsafe { GetNonCCWObjectGlobal(obj) };
2363        unsafe { global_scope_from_global_static(global) }
2364    }
2365
2366    /// Return global scope asociated with current realm
2367    ///
2368    /// Eventually we could return Handle here as global is already rooted by realm.
2369    #[expect(unsafe_code)]
2370    pub(crate) fn from_current_realm(realm: &'_ mut CurrentRealm) -> DomRoot<Self> {
2371        let global = realm.global().get();
2372        unsafe { global_scope_from_global(realm, global) }
2373    }
2374
2375    pub(crate) fn add_uncaught_rejection(&self, rejection: HandleObject) {
2376        self.uncaught_rejections
2377            .borrow_mut()
2378            .push(Heap::boxed(rejection.get()));
2379    }
2380
2381    pub(crate) fn remove_uncaught_rejection(&self, rejection: HandleObject) {
2382        let mut uncaught_rejections = self.uncaught_rejections.borrow_mut();
2383
2384        if let Some(index) = uncaught_rejections
2385            .iter()
2386            .position(|promise| *promise == Heap::boxed(rejection.get()))
2387        {
2388            uncaught_rejections.remove(index);
2389        }
2390    }
2391
2392    // `Heap` values must stay boxed, as they need semantics like `Pin`
2393    // (that is, they cannot be moved).
2394    #[allow(clippy::vec_box)]
2395    /// <https://html.spec.whatwg.org/multipage/#about-to-be-notified-rejected-promises-list>
2396    pub(crate) fn get_uncaught_rejections(&self) -> &DomRefCell<Vec<Box<Heap<*mut JSObject>>>> {
2397        &self.uncaught_rejections
2398    }
2399
2400    pub(crate) fn add_consumed_rejection(&self, rejection: HandleObject) {
2401        self.consumed_rejections
2402            .borrow_mut()
2403            .push(Heap::boxed(rejection.get()));
2404    }
2405
2406    pub(crate) fn remove_consumed_rejection(&self, rejection: HandleObject) {
2407        let mut consumed_rejections = self.consumed_rejections.borrow_mut();
2408
2409        if let Some(index) = consumed_rejections
2410            .iter()
2411            .position(|promise| *promise == Heap::boxed(rejection.get()))
2412        {
2413            consumed_rejections.remove(index);
2414        }
2415    }
2416
2417    // `Heap` values must stay boxed, as they need semantics like `Pin`
2418    // (that is, they cannot be moved).
2419    #[allow(clippy::vec_box)]
2420    pub(crate) fn get_consumed_rejections(&self) -> &DomRefCell<Vec<Box<Heap<*mut JSObject>>>> {
2421        &self.consumed_rejections
2422    }
2423
2424    pub(crate) fn set_module_map(&self, request: ModuleRequest, module: ModuleStatus) {
2425        self.module_map.borrow_mut().insert(request, module);
2426    }
2427
2428    pub(crate) fn get_module_map_entry(&self, request: &ModuleRequest) -> Option<ModuleStatus> {
2429        self.module_map.borrow().get(request).cloned()
2430    }
2431
2432    pub(crate) fn time(&self, label: DOMString) -> Result<(), ()> {
2433        let mut timers = self.console_timers.borrow_mut();
2434        if timers.len() >= 10000 {
2435            return Err(());
2436        }
2437        match timers.entry(label) {
2438            Entry::Vacant(entry) => {
2439                entry.insert(Instant::now());
2440                Ok(())
2441            },
2442            Entry::Occupied(_) => Err(()),
2443        }
2444    }
2445
2446    /// Computes the delta time since a label has been created
2447    ///
2448    /// Returns an error if the label does not exist.
2449    pub(crate) fn time_log(&self, label: &DOMString) -> Result<u64, ()> {
2450        self.console_timers
2451            .borrow()
2452            .get(label)
2453            .ok_or(())
2454            .map(|&start| (Instant::now() - start).as_millis() as u64)
2455    }
2456
2457    /// Computes the delta time since a label has been created and stops
2458    /// tracking the label.
2459    ///
2460    /// Returns an error if the label does not exist.
2461    pub(crate) fn time_end(&self, label: &DOMString) -> Result<u64, ()> {
2462        self.console_timers
2463            .borrow_mut()
2464            .remove(label)
2465            .ok_or(())
2466            .map(|start| (Instant::now() - start).as_millis() as u64)
2467    }
2468
2469    /// Get an `&IpcSender<ScriptToDevtoolsControlMsg>` to send messages
2470    /// to the devtools thread when available.
2471    pub(crate) fn devtools_chan(&self) -> Option<&GenericCallback<ScriptToDevtoolsControlMsg>> {
2472        self.devtools_chan.as_ref()
2473    }
2474
2475    /// Get a sender to the memory profiler thread.
2476    pub(crate) fn mem_profiler_chan(&self) -> &profile_mem::ProfilerChan {
2477        &self.mem_profiler_chan
2478    }
2479
2480    /// Get a sender to the time profiler thread.
2481    pub(crate) fn time_profiler_chan(&self) -> &profile_time::ProfilerChan {
2482        &self.time_profiler_chan
2483    }
2484
2485    /// Get a sender to the constellation thread.
2486    pub(crate) fn script_to_constellation_chan(&self) -> ScriptToConstellationChan {
2487        ScriptToConstellationChan {
2488            sender: self.script_to_constellation_sender.clone(),
2489            webview_id: self.webview_id().unwrap_or(TEST_WEBVIEW_ID),
2490            pipeline_id: self.pipeline_id(),
2491        }
2492    }
2493
2494    pub(crate) fn script_to_embedder_chan(&self) -> &ScriptToEmbedderChan {
2495        &self.script_to_embedder_chan
2496    }
2497
2498    pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
2499        self.script_to_embedder_chan().send(msg).unwrap();
2500    }
2501
2502    /// Get the `PipelineId` for this global scope.
2503    pub(crate) fn pipeline_id(&self) -> PipelineId {
2504        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2505            worker.pipeline_id()
2506        } else if let Some(window) = self.downcast::<Window>() {
2507            window.pipeline_id()
2508        } else if let Some(debugger) = self.downcast::<DebuggerGlobalScope>() {
2509            debugger.pipeline_id()
2510        } else if let Some(worklet) = self.downcast::<WorkletGlobalScope>() {
2511            worklet.pipeline_id()
2512        } else if let Some(dissimilar) = self.downcast::<DissimilarOriginWindow>() {
2513            dissimilar.pipeline_id()
2514        } else {
2515            unreachable!("Unsupported global type for pipeline id")
2516        }
2517    }
2518
2519    /// Register interest in a notification category. Sends a `RegisterInterest`
2520    /// message to the constellation when the first listener is registered.
2521    pub(crate) fn register_interest(&self, interest: ConstellationInterest) {
2522        let mut counts = self.constellation_interest_counts.borrow_mut();
2523        let count = counts.entry(interest).or_insert(0);
2524        *count += 1;
2525        if *count == 1 {
2526            let _ = self
2527                .script_to_constellation_chan()
2528                .send(ScriptToConstellationMessage::RegisterInterest(interest));
2529        }
2530    }
2531
2532    /// Unregister interest in a notification category. Sends an `UnregisterInterest`
2533    /// message to the constellation when the last listener is removed.
2534    pub(crate) fn unregister_interest(&self, interest: ConstellationInterest) {
2535        let mut counts = self.constellation_interest_counts.borrow_mut();
2536        if let Some(count) = counts.get_mut(&interest) {
2537            *count = count.saturating_sub(1);
2538            if *count == 0 {
2539                counts.remove(&interest);
2540                let _ = self
2541                    .script_to_constellation_chan()
2542                    .send(ScriptToConstellationMessage::UnregisterInterest(interest));
2543            }
2544        }
2545    }
2546
2547    /// Get the origin for this global scope
2548    pub(crate) fn origin(&self) -> MutableOrigin {
2549        if let Some(window) = self.downcast::<Window>() {
2550            window.origin()
2551        } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2552            worker.origin()
2553        } else if let Some(worklet) = self.downcast::<WorkletGlobalScope>() {
2554            worklet.origin()
2555        } else if let Some(dissimilar_window) = self.downcast::<DissimilarOriginWindow>() {
2556            dissimilar_window.origin()
2557        } else if let Some(debugger) = self.downcast::<DebuggerGlobalScope>() {
2558            debugger.origin()
2559        } else {
2560            unreachable!("Unexpected origin check against global")
2561        }
2562    }
2563
2564    /// Get the creation_url for this global scope
2565    pub(crate) fn creation_url(&self) -> ServoUrl {
2566        self.creation_url.borrow().clone()
2567    }
2568
2569    pub(crate) fn set_creation_url(&self, creation_url: ServoUrl) {
2570        *self.creation_url.borrow_mut() = creation_url;
2571    }
2572
2573    /// Get the top_level_creation_url for this global scope
2574    pub(crate) fn top_level_creation_url(&self) -> &Option<ServoUrl> {
2575        &self.top_level_creation_url
2576    }
2577
2578    pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
2579        if let Some(window) = self.downcast::<Window>() {
2580            return window.image_cache();
2581        }
2582        if let Some(worker) = self.downcast::<DedicatedWorkerGlobalScope>() {
2583            return worker.image_cache();
2584        }
2585        if let Some(worker) = self.downcast::<SharedWorkerGlobalScope>() {
2586            return worker.image_cache();
2587        }
2588        if let Some(worker) = self.downcast::<PaintWorkletGlobalScope>() {
2589            return worker.image_cache();
2590        }
2591        unreachable!();
2592    }
2593
2594    /// Schedule a [`TimerEventRequest`] on this [`GlobalScope`]'s [`timers::TimerScheduler`].
2595    /// Every Worker has its own scheduler, which handles events in the Worker event loop,
2596    /// but `Window`s use a shared scheduler associated with their [`ScriptThread`].
2597    pub(crate) fn schedule_timer(&self, request: TimerEventRequest) -> Option<TimerId> {
2598        match self.downcast::<WorkerGlobalScope>() {
2599            Some(worker_global) => Some(worker_global.timer_scheduler().schedule_timer(request)),
2600            _ => with_script_thread(|script_thread| Some(script_thread.schedule_timer(request))),
2601        }
2602    }
2603
2604    /// <https://html.spec.whatwg.org/multipage/#nested-browsing-context>
2605    pub(crate) fn is_nested_browsing_context(&self) -> bool {
2606        self.downcast::<Window>()
2607            .is_some_and(|window| !window.is_top_level())
2608    }
2609
2610    /// Obtain the size of in flight keep alive records from the resource thread.
2611    /// If we can't communicate with the thread, we return u64::MAX to ensure
2612    /// the limit is higher than what is allowed. This ensures that whenever
2613    /// we want to initiate a keep alive request and the thread doesn't communicate,
2614    /// we block additional keep alive requests.
2615    pub(crate) fn total_size_of_in_flight_keep_alive_records(&self) -> u64 {
2616        let (sender, receiver) = generic_channel::channel().unwrap();
2617        if self
2618            .core_resource_thread()
2619            .send(CoreResourceMsg::TotalSizeOfInFlightKeepAliveRecords(
2620                self.pipeline_id(),
2621                sender,
2622            ))
2623            .is_err()
2624        {
2625            return u64::MAX;
2626        }
2627        receiver.recv().unwrap_or(u64::MAX)
2628    }
2629
2630    /// Part of <https://fetch.spec.whatwg.org/#populate-request-from-client>
2631    pub(crate) fn request_client(&self, no_gc: Option<&NoGC>) -> RequestClient {
2632        if let Some(window) = self.downcast::<Window>() {
2633            return window.request_client(no_gc);
2634        }
2635        RequestClient {
2636            preloaded_resources: Default::default(),
2637            policy_container: self.policy_container(),
2638            origin: RequestOrigin::Origin(self.origin().immutable().clone()),
2639            is_nested_browsing_context: false,
2640            insecure_requests_policy: self.insecure_requests_policy(),
2641            has_trustworthy_ancestor_origin: false,
2642        }
2643    }
2644
2645    /// <https://html.spec.whatwg.org/multipage/#concept-settings-object-policy-container>
2646    pub(crate) fn policy_container(&self) -> PolicyContainer {
2647        if let Some(window) = self.downcast::<Window>() {
2648            return window.Document().policy_container().to_owned();
2649        }
2650        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2651            return worker.policy_container().to_owned();
2652        }
2653        unreachable!();
2654    }
2655
2656    /// Get the [base url](https://html.spec.whatwg.org/multipage/#api-base-url)
2657    /// for this global scope.
2658    pub(crate) fn api_base_url(&self) -> ServoUrl {
2659        if let Some(window) = self.downcast::<Window>() {
2660            // https://html.spec.whatwg.org/multipage/#script-settings-for-browsing-contexts:api-base-url
2661            return window.Document().base_url();
2662        }
2663        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2664            // https://html.spec.whatwg.org/multipage/#script-settings-for-workers:api-base-url
2665            return worker.get_url().clone();
2666        }
2667        if let Some(worklet) = self.downcast::<WorkletGlobalScope>() {
2668            // https://drafts.css-houdini.org/worklets/#script-settings-for-worklets
2669            return worklet.base_url();
2670        }
2671        if let Some(_debugger_global) = self.downcast::<DebuggerGlobalScope>() {
2672            return self.creation_url();
2673        }
2674        unreachable!();
2675    }
2676
2677    /// Get the URL for this global scope.
2678    pub(crate) fn get_url(&self) -> ServoUrl {
2679        if let Some(window) = self.downcast::<Window>() {
2680            return window.get_url();
2681        }
2682        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2683            return worker.get_url().clone();
2684        }
2685        if let Some(worklet) = self.downcast::<WorkletGlobalScope>() {
2686            // TODO: is this the right URL to return?
2687            return worklet.base_url();
2688        }
2689        if let Some(_debugger_global) = self.downcast::<DebuggerGlobalScope>() {
2690            return self.creation_url();
2691        }
2692        unreachable!();
2693    }
2694
2695    /// Get the Referrer Policy for this global scope.
2696    pub(crate) fn get_referrer_policy(&self) -> ReferrerPolicy {
2697        if let Some(window) = self.downcast::<Window>() {
2698            let document = window.Document();
2699
2700            return document.get_referrer_policy();
2701        }
2702        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2703            return worker.policy_container().get_referrer_policy();
2704        }
2705        unreachable!();
2706    }
2707
2708    /// Step 3."client" of <https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer>
2709    /// Determine the Referrer for a request whose Referrer is "client"
2710    pub(crate) fn get_referrer(&self) -> Referrer {
2711        // Substep 3."client".2. If environment’s global object is a Window object, then
2712        if let Some(window) = self.downcast::<Window>() {
2713            // Substep 3."client".2.1. Let document be the associated Document of environment’s global object.
2714            let mut document = window.Document();
2715
2716            // Substep 3."client".2.2. If document’s origin is an opaque origin, return no referrer.
2717            if let ImmutableOrigin::Opaque(_) = document.origin().immutable() {
2718                return Referrer::NoReferrer;
2719            }
2720
2721            let mut url = document.url();
2722
2723            // Substep 3."client".2.3. While document is an iframe srcdoc document,
2724            // let document be document’s browsing context’s browsing context container’s node document.
2725            while url.as_str() == "about:srcdoc" {
2726                // Return early if we cannot get a parent document. This might happen if
2727                // this iframe was already removed from the parent page.
2728                let Some(parent_document) =
2729                    document.browsing_context().and_then(|browsing_context| {
2730                        browsing_context
2731                            .parent()
2732                            .and_then(|parent| parent.document())
2733                    })
2734                else {
2735                    return Referrer::NoReferrer;
2736                };
2737                document = parent_document;
2738                url = document.url();
2739            }
2740
2741            // Substep 3."client".2.4. Let referrerSource be document’s URL.
2742            Referrer::Client(url)
2743        } else {
2744            // Substep 3."client".3. Otherwise, let referrerSource be environment’s creation URL.
2745            Referrer::Client(self.creation_url())
2746        }
2747    }
2748
2749    /// Extract a `Window`, panic if the global object is not a `Window`.
2750    pub(crate) fn as_window(&self) -> &Window {
2751        self.downcast::<Window>().expect("expected a Window scope")
2752    }
2753
2754    /// Returns a policy that should be used for fetches initiated from this global.
2755    pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
2756        if let Some(window) = self.downcast::<Window>() {
2757            return window.Document().insecure_requests_policy();
2758        }
2759        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2760            return worker.insecure_requests_policy();
2761        }
2762        debug!("unsupported global, defaulting insecure requests policy to DoNotUpgrade");
2763        InsecureRequestsPolicy::DoNotUpgrade
2764    }
2765
2766    /// Whether this document has ancestor navigables that are trustworthy
2767    pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
2768        self.downcast::<Window>()
2769            .is_some_and(|window| window.Document().has_trustworthy_ancestor_origin())
2770    }
2771
2772    /// <https://html.spec.whatwg.org/multipage/#report-an-exception>
2773    pub(crate) fn report_an_exception(&self, cx: &mut js::context::JSContext, error: HandleValue) {
2774        // Step 2. Let errorInfo be the result of extracting error information from exception.
2775        let error_info = ErrorInfo::from_value(cx, error);
2776
2777        // Step 3. Let script be a script found in an implementation-defined way, or null.
2778        // This should usually be the running script (most notably during run a classic script).
2779        // Step 4. If script is a classic script and script's muted errors is true, then set
2780        // errorInfo[error] to null, errorInfo[message] to "Script error.", errorInfo[filename]
2781        // to the empty string, errorInfo[lineno] to 0, and errorInfo[colno] to 0.
2782        // Note: This is handled in 'run_a_classic_script'.
2783
2784        // Step 5. If omitError is true, then set errorInfo[error] to null.
2785        //
2786        // `omitError` defaults to `false`
2787
2788        // Steps 6-7
2789        self.report_an_error(cx, error_info, error);
2790    }
2791
2792    /// Steps 6-7 of <https://html.spec.whatwg.org/multipage/#report-an-exception>
2793    pub(crate) fn report_an_error(
2794        &self,
2795        cx: &mut js::context::JSContext,
2796        error_info: ErrorInfo,
2797        value: HandleValue,
2798    ) {
2799        self.send_to_embedder(EmbedderMsg::ShowConsoleApiMessage(
2800            self.webview_id(),
2801            ConsoleLogLevel::Error,
2802            format!(
2803                "Error at {}:{}:{} {}",
2804                error_info.filename, error_info.lineno, error_info.column, error_info.message
2805            ),
2806        ));
2807
2808        #[cfg(feature = "js_backtrace")]
2809        LAST_EXCEPTION_BACKTRACE.with(|backtrace| {
2810            if let Some((js_backtrace, rust_backtrace)) = backtrace.borrow_mut().take() {
2811                if let Some(stack) = js_backtrace {
2812                    error!("JS backtrace:\n{}", stack);
2813                }
2814                error!("Rust backtrace:\n{}", rust_backtrace);
2815            }
2816        });
2817
2818        // Step 1. Let notHandled be true.
2819        let mut not_handled = true;
2820
2821        // Step 6. If global is not in error reporting mode:
2822        if !self.in_error_reporting_mode.get() {
2823            // Step 6.1. Set global's in error reporting mode to true.
2824            self.in_error_reporting_mode.set(true);
2825
2826            // Step 6.2 If global implements EventTarget, then set notHandled to the result of
2827            // firing an event named error at global, using ErrorEvent, with the cancelable
2828            // attribute initialized to true, and additional attributes initialized according to
2829            // errorInfo.
2830            let event = ErrorEvent::new(
2831                cx,
2832                self,
2833                atom!("error"),
2834                EventBubbles::DoesNotBubble,
2835                EventCancelable::Cancelable,
2836                error_info.message.as_str().into(),
2837                error_info.filename.as_str().into(),
2838                error_info.lineno,
2839                error_info.column,
2840                value,
2841            );
2842            not_handled = event
2843                .upcast::<Event>()
2844                .fire(cx, self.upcast::<EventTarget>());
2845
2846            // Step 6.3. Set global's in error reporting mode to false.
2847            self.in_error_reporting_mode.set(false);
2848        }
2849
2850        // Step 7. If notHandled is true, then:
2851        if not_handled {
2852            // Step 7.2. If global implements DedicatedWorkerGlobalScope,
2853            // queue a global task on the DOM manipulation task source with the
2854            // global's associated Worker's relevant global object to run these steps:
2855            //
2856            // https://html.spec.whatwg.org/multipage/#runtime-script-errors-2
2857            if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
2858                dedicated.forward_error_to_worker_object(error_info);
2859            } else if self.is::<Window>() {
2860                // Step 7.3. Otherwise, the user agent may report exception to a developer console.
2861                if let Some(ref chan) = self.devtools_chan {
2862                    let _ = chan.send(ScriptToDevtoolsControlMsg::ReportPageError(
2863                        self.pipeline_id(),
2864                        PageError {
2865                            error_message: error_info.message.clone(),
2866                            source_name: error_info.filename.clone(),
2867                            line_number: error_info.lineno,
2868                            column_number: error_info.column,
2869                            time_stamp: get_time_stamp(),
2870                        },
2871                    ));
2872                }
2873            }
2874        }
2875    }
2876
2877    /// Get the `&ResourceThreads` for this global scope.
2878    pub(crate) fn resource_threads(&self) -> &ResourceThreads {
2879        &self.resource_threads
2880    }
2881
2882    /// Get the `CoreResourceThread` for this global scope.
2883    pub(crate) fn core_resource_thread(&self) -> CoreResourceThread {
2884        self.resource_threads().sender()
2885    }
2886
2887    /// Get a reference to the [`StorageThreads`] for this [`GlobalScope`].
2888    pub(crate) fn storage_threads(&self) -> &StorageThreads {
2889        &self.storage_threads
2890    }
2891
2892    /// A sender to the event loop of this global scope. This either sends to the Worker event loop
2893    /// or the ScriptThread event loop in the case of a `Window`. This can be `None` for dedicated
2894    /// workers that are not currently handling a message.
2895    pub(crate) fn event_loop_sender(&self) -> Option<ScriptEventLoopSender> {
2896        if let Some(window) = self.downcast::<Window>() {
2897            Some(window.event_loop_sender())
2898        } else if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
2899            dedicated.event_loop_sender()
2900        } else if let Some(shared_worker) = self.downcast::<SharedWorkerGlobalScope>() {
2901            Some(shared_worker.event_loop_sender())
2902        } else if let Some(service_worker) = self.downcast::<ServiceWorkerGlobalScope>() {
2903            Some(service_worker.event_loop_sender())
2904        } else {
2905            unreachable!(
2906                "Tried to access event loop sender for incompatible \
2907                 GlobalScope (PaintWorklet or DissimilarOriginWindow)"
2908            );
2909        }
2910    }
2911
2912    /// A reference to the [`TaskManager`] used to schedule tasks for this [`GlobalScope`].
2913    pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
2914        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
2915            worker.task_manager()
2916        } else if let Some(window) = self.downcast::<Window>() {
2917            window.task_manager()
2918        } else {
2919            unreachable!("Attempted to use task manager with unsupported global");
2920        }
2921    }
2922
2923    /// Evaluate JS code on this global scope.
2924    #[expect(unsafe_code)]
2925    pub(crate) fn evaluate_js_on_global(
2926        &self,
2927        cx: &mut CurrentRealm,
2928        code: Cow<'_, str>,
2929        filename: &str,
2930        introduction_type: Option<&'static CStr>,
2931        rval: Option<MutableHandleValue>,
2932    ) -> Result<(), JavaScriptEvaluationError> {
2933        assert!(self.can_run_script());
2934
2935        run_a_script::<DomTypeHolder, _, _>(cx, self, |cx| {
2936            let url = self.api_base_url();
2937            let fetch_options = ScriptFetchOptions::default_classic_script();
2938
2939            let options = fill_compile_options(
2940                cx,
2941                filename,
2942                introduction_type,
2943                ErrorReporting::Unmuted,
2944                rval.is_none(), // noScriptRval
2945                1,              // lineno
2946            );
2947
2948            let mut source = transform_str_to_source_text(&code);
2949            rooted!(&in(cx) let compiled_script = unsafe { Compile1(cx, options.ptr, &mut source) });
2950
2951            let Some(script) = NonNull::new(*compiled_script) else {
2952                debug!("error compiling Dom string");
2953                report_pending_exception(cx);
2954                return Err(JavaScriptEvaluationError::CompilationFailure);
2955            };
2956
2957            rooted!(&in(cx) let mut value = UndefinedValue());
2958            let rval = rval.unwrap_or_else(|| value.handle_mut());
2959
2960            if !evaluate_script(cx, script, url, fetch_options, rval) {
2961                let error_info = take_and_report_pending_exception_for_api(cx);
2962                return Err(JavaScriptEvaluationError::EvaluationFailure(error_info));
2963            }
2964
2965            maybe_resume_unwind();
2966            Ok(())
2967        })
2968    }
2969
2970    /// <https://html.spec.whatwg.org/multipage/#timer-initialisation-steps>
2971    pub(crate) fn schedule_callback(
2972        &self,
2973        callback: OneshotTimerCallback,
2974        duration: Duration,
2975    ) -> OneshotTimerHandle {
2976        self.with_timers(|timers| timers.schedule_callback(callback, duration, self.timer_source()))
2977    }
2978
2979    pub(crate) fn unschedule_callback(&self, handle: OneshotTimerHandle) {
2980        self.with_timers(|timers| timers.unschedule_callback(handle));
2981    }
2982
2983    /// <https://html.spec.whatwg.org/multipage/#timer-initialisation-steps>
2984    pub(crate) fn set_timeout_or_interval(
2985        &self,
2986        cx: &mut js::context::JSContext,
2987        callback: TimerCallback,
2988        arguments: Vec<HandleValue>,
2989        timeout: Duration,
2990        is_interval: IsInterval,
2991    ) -> Fallible<i32> {
2992        self.with_timers(|timers| {
2993            timers.set_timeout_or_interval(
2994                cx,
2995                self,
2996                callback,
2997                arguments,
2998                timeout,
2999                is_interval,
3000                self.timer_source(),
3001            )
3002        })
3003    }
3004
3005    pub(crate) fn clear_timeout_or_interval(&self, handle: i32) {
3006        self.with_timers(|timers| timers.clear_timeout_or_interval(self, handle));
3007    }
3008
3009    pub(crate) fn fire_timer(&self, handle: TimerEventId, cx: &mut js::context::JSContext) {
3010        self.with_timers(|timers| timers.fire_timer(handle, cx));
3011    }
3012
3013    pub(crate) fn resume(&self) {
3014        self.with_timers(|timers| timers.resume());
3015    }
3016
3017    pub(crate) fn suspend(&self) {
3018        self.with_timers(|timers| timers.suspend());
3019    }
3020
3021    pub(crate) fn slow_down_timers(&self) {
3022        self.with_timers(|timers| timers.slow_down());
3023    }
3024
3025    pub(crate) fn speed_up_timers(&self) {
3026        self.with_timers(|timers| timers.speed_up());
3027    }
3028
3029    fn timer_source(&self) -> TimerSource {
3030        if self.is::<Window>() {
3031            return TimerSource::FromWindow(self.pipeline_id());
3032        }
3033        if self.is::<WorkerGlobalScope>() {
3034            return TimerSource::FromWorker;
3035        }
3036        unreachable!();
3037    }
3038
3039    /// Returns a boolean indicating whether the event-loop
3040    /// where this global is running on can continue running JS.
3041    pub(crate) fn can_continue_running(&self) -> bool {
3042        if self.is::<Window>() {
3043            return ScriptThread::can_continue_running();
3044        }
3045        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3046            return !worker.is_closing();
3047        }
3048
3049        // TODO: plug worklets into this.
3050        true
3051    }
3052
3053    /// Potentially instantiate and return this [`GlobalScope`]'s [`IDBFactory`].
3054    pub(crate) fn ensure_indexeddb_factory(
3055        &self,
3056        cx: &mut js::context::JSContext,
3057    ) -> DomRoot<IDBFactory> {
3058        self.indexeddb.or_init(|| IDBFactory::new(cx, self))
3059    }
3060
3061    /// Return this [`GlobalScope`]'s [`IDBFactory`] if it has previously been instantiated.
3062    pub(crate) fn indexeddb_factory(&self) -> Option<DomRoot<IDBFactory>> {
3063        self.indexeddb.get()
3064    }
3065
3066    /// Perform a microtask checkpoint.
3067    pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
3068        if let Some(window) = self.downcast::<Window>() {
3069            window.perform_a_microtask_checkpoint(cx);
3070        } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3071            worker.perform_a_microtask_checkpoint(cx);
3072        }
3073    }
3074
3075    /// Enqueue a microtask for subsequent execution.
3076    pub(crate) fn enqueue_microtask(
3077        &self,
3078        cx: &js::context::JSContext,
3079        job: Box<dyn MicrotaskRunnable>,
3080    ) {
3081        if self.is::<Window>() {
3082            ScriptThread::enqueue_microtask(cx, job);
3083        } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3084            worker.enqueue_microtask(cx, job);
3085        }
3086    }
3087
3088    /// Create a new sender/receiver pair that can be used to implement an on-demand
3089    /// event loop. Used for implementing web APIs that require blocking semantics
3090    /// without resorting to nested event loops.
3091    pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
3092        if let Some(window) = self.downcast::<Window>() {
3093            return window.new_script_pair();
3094        }
3095        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3096            return worker.new_script_pair();
3097        }
3098        unreachable!();
3099    }
3100
3101    /// Process a single event as if it were the next event
3102    /// in the queue for the event-loop where this global scope is running on.
3103    /// Returns a boolean indicating whether further events should be processed.
3104    pub(crate) fn process_event(
3105        &self,
3106        msg: CommonScriptMsg,
3107        cx: &mut js::context::JSContext,
3108    ) -> bool {
3109        if self.is::<Window>() {
3110            return ScriptThread::process_event(msg, cx);
3111        }
3112        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3113            return worker.process_event(msg, cx);
3114        }
3115        unreachable!();
3116    }
3117
3118    pub(crate) fn runtime_handle(&self) -> ParentRuntime {
3119        if self.is::<Window>() {
3120            ScriptThread::runtime_handle()
3121        } else if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3122            worker.runtime_handle()
3123        } else {
3124            unreachable!()
3125        }
3126    }
3127
3128    /// Returns the ["current"] global object.
3129    ///
3130    /// ["current"]: https://html.spec.whatwg.org/multipage/#current
3131    #[expect(unsafe_code)]
3132    pub(crate) fn current() -> Option<DomRoot<Self>> {
3133        let mut cx = unsafe { JSContext::get_from_thread()? };
3134        unsafe {
3135            let global = CurrentGlobalOrNull(&cx);
3136            if global.is_null() {
3137                None
3138            } else {
3139                Some(global_scope_from_global(&mut cx, global))
3140            }
3141        }
3142    }
3143
3144    /// Returns the ["entry"] global object.
3145    ///
3146    /// ["entry"]: https://html.spec.whatwg.org/multipage/#entry
3147    pub(crate) fn entry() -> DomRoot<Self> {
3148        entry_global()
3149    }
3150
3151    /// Returns the ["incumbent"] global object.
3152    ///
3153    /// ["incumbent"]: https://html.spec.whatwg.org/multipage/#incumbent
3154    pub(crate) fn incumbent() -> Option<DomRoot<Self>> {
3155        incumbent_global()
3156    }
3157
3158    pub(crate) fn performance(&self, cx: &mut JSContext) -> DomRoot<Performance> {
3159        if let Some(window) = self.downcast::<Window>() {
3160            return window.Performance(cx);
3161        }
3162        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
3163            return worker.Performance(cx);
3164        }
3165        unreachable!();
3166    }
3167
3168    /// <https://w3c.github.io/performance-timeline/#supportedentrytypes-attribute>
3169    pub(crate) fn supported_performance_entry_types(
3170        &self,
3171        cx: &mut js::context::JSContext,
3172        retval: MutableHandleValue,
3173    ) {
3174        self.frozen_supported_performance_entry_types.get_or_init(
3175            cx,
3176            || {
3177                EntryType::VARIANTS
3178                    .iter()
3179                    .map(|t| DOMString::from(t.as_str()))
3180                    .collect()
3181            },
3182            retval,
3183        );
3184    }
3185
3186    pub(crate) fn inherited_secure_context(&self) -> Option<bool> {
3187        self.inherited_secure_context
3188    }
3189
3190    /// <https://html.spec.whatwg.org/multipage/#secure-context>
3191    pub(crate) fn is_secure_context(&self) -> bool {
3192        // This differs from the specification, but it seems that
3193        // `inherited_secure_context` implements more-or-less the exact same logic, in a
3194        // different manner. Workers inherit whether or not their in a secure context and
3195        // worklets do as well (they can only be created in secure contexts).
3196        if Some(false) == self.inherited_secure_context {
3197            return false;
3198        }
3199        // Step 1. If environment is an environment settings object, then:
3200        // Step 1.1. Let global be environment's global object.
3201        match self.top_level_creation_url() {
3202            None => {
3203                // Workers and worklets don't have a top-level creation URL
3204                assert!(self.is::<WorkerGlobalScope>() || self.is::<WorkletGlobalScope>());
3205                true
3206            },
3207            Some(top_level_creation_url) => {
3208                assert!(self.is::<Window>());
3209                // Step 2. If the result of Is url potentially trustworthy?
3210                // given environment's top-level creation URL is "Potentially Trustworthy", then return true.
3211                // Step 3. Return false.
3212                if top_level_creation_url.scheme() == "blob" &&
3213                    Some(true) == self.inherited_secure_context
3214                {
3215                    return true;
3216                }
3217                top_level_creation_url.is_potentially_trustworthy()
3218            },
3219        }
3220    }
3221
3222    /// <https://www.w3.org/TR/CSP/#get-csp-of-object>
3223    pub(crate) fn get_csp_list(&self) -> Option<CspList> {
3224        if self.is::<Window>() || self.is::<WorkerGlobalScope>() {
3225            return self.policy_container().csp_list;
3226        }
3227        // TODO: Worklet global scopes.
3228        None
3229    }
3230
3231    pub(crate) fn status_code(&self) -> Option<u16> {
3232        if let Some(window) = self.downcast::<Window>() {
3233            return window.Document().status_code();
3234        }
3235        None
3236    }
3237
3238    #[cfg(feature = "webgpu")]
3239    pub(crate) fn wgpu_id_hub(&self) -> Arc<IdentityHub> {
3240        self.gpu_id_hub.clone()
3241    }
3242
3243    #[cfg(feature = "webgpu")]
3244    pub(crate) fn add_gpu_device(&self, device: &GPUDevice) {
3245        self.gpu_devices
3246            .borrow_mut()
3247            .insert(device.id(), WeakRef::new(device));
3248    }
3249
3250    #[cfg(feature = "webgpu")]
3251    pub(crate) fn remove_gpu_device(&self, device: WebGPUDevice) {
3252        let device = self
3253            .gpu_devices
3254            .borrow_mut()
3255            .remove(&device)
3256            .expect("GPUDevice should still be in devices hashmap");
3257        assert!(device.root().is_none())
3258    }
3259
3260    #[cfg(feature = "webgpu")]
3261    pub(crate) fn gpu_device_lost(
3262        &self,
3263        device: WebGPUDevice,
3264        reason: DeviceLostReason,
3265        msg: String,
3266    ) {
3267        let reason = match reason {
3268            DeviceLostReason::Unknown => GPUDeviceLostReason::Unknown,
3269            DeviceLostReason::Destroyed => GPUDeviceLostReason::Destroyed,
3270        };
3271        if let Some(device) = self
3272            .gpu_devices
3273            .borrow_mut()
3274            .get_mut(&device)
3275            .expect("GPUDevice should still be in devices hashmap")
3276            .root()
3277        {
3278            device.lose(reason, msg);
3279        }
3280    }
3281
3282    #[cfg(feature = "webgpu")]
3283    pub(crate) fn handle_uncaptured_gpu_error(
3284        &self,
3285        device: WebGPUDevice,
3286        error: webgpu_traits::Error,
3287    ) {
3288        if let Some(gpu_device) = self
3289            .gpu_devices
3290            .borrow()
3291            .get(&device)
3292            .and_then(|device| device.root())
3293        {
3294            gpu_device.fire_uncaptured_error(error);
3295        } else {
3296            warn!("Recived error for lost GPUDevice!")
3297        }
3298    }
3299
3300    pub(crate) fn current_group_label(&self) -> Option<DOMString> {
3301        self.console_group_stack
3302            .borrow()
3303            .last()
3304            .map(|label| DOMString::from(format!("[{}]", label)))
3305    }
3306
3307    pub(crate) fn push_console_group(&self, group: DOMString) {
3308        self.console_group_stack.borrow_mut().push(group);
3309    }
3310
3311    pub(crate) fn pop_console_group(&self) {
3312        let _ = self.console_group_stack.borrow_mut().pop();
3313    }
3314
3315    pub(crate) fn increment_console_count(&self, label: &DOMString) -> usize {
3316        *self
3317            .console_count_map
3318            .borrow_mut()
3319            .entry(label.clone())
3320            .and_modify(|e| *e += 1)
3321            .or_insert(1)
3322    }
3323
3324    pub(crate) fn reset_console_count(&self, label: &DOMString) -> Result<(), ()> {
3325        match self.console_count_map.borrow_mut().get_mut(label) {
3326            Some(value) => {
3327                *value = 0;
3328                Ok(())
3329            },
3330            None => Err(()),
3331        }
3332    }
3333
3334    pub(crate) fn structured_clone(
3335        &self,
3336        cx: &mut js::context::JSContext,
3337        value: HandleValue,
3338        options: RootedTraceableBox<StructuredSerializeOptions>,
3339        retval: MutableHandleValue,
3340    ) -> Fallible<()> {
3341        let mut rooted = CustomAutoRooter::new(
3342            options
3343                .transfer
3344                .iter()
3345                .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
3346                .collect(),
3347        );
3348
3349        #[expect(unsafe_code)]
3350        let guard = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
3351
3352        let data = structuredclone::write(cx, value, Some(guard))?;
3353
3354        structuredclone::read(cx, self, data, retval)?;
3355
3356        Ok(())
3357    }
3358
3359    pub(crate) fn fetch<Listener: FetchResponseListener>(
3360        &self,
3361        request_builder: RequestBuilder,
3362        context: Listener,
3363        task_source: SendableTaskSource,
3364    ) {
3365        let network_listener = NetworkListener::new(context, task_source);
3366        self.fetch_with_network_listener(request_builder, network_listener);
3367    }
3368
3369    pub(crate) fn fetch_with_network_listener<Listener: FetchResponseListener>(
3370        &self,
3371        request_builder: RequestBuilder,
3372        network_listener: NetworkListener<Listener>,
3373    ) {
3374        fetch_async(
3375            &self.core_resource_thread(),
3376            request_builder,
3377            None,
3378            network_listener.into_callback(),
3379        );
3380    }
3381
3382    pub(crate) fn unminify_js(&self) -> bool {
3383        self.unminified_js_dir.is_some()
3384    }
3385
3386    pub(crate) fn unminified_js_dir(&self) -> Option<String> {
3387        self.unminified_js_dir.clone()
3388    }
3389
3390    pub(crate) fn set_byte_length_queuing_strategy_size(&self, function: Rc<Function>) {
3391        if self
3392            .byte_length_queuing_strategy_size_function
3393            .set(function)
3394            .is_err()
3395        {
3396            warn!("byte length queuing strategy size function is set twice.");
3397        };
3398    }
3399
3400    pub(crate) fn get_byte_length_queuing_strategy_size(&self) -> Option<Rc<Function>> {
3401        self.byte_length_queuing_strategy_size_function
3402            .get()
3403            .cloned()
3404    }
3405
3406    pub(crate) fn set_count_queuing_strategy_size(&self, function: Rc<Function>) {
3407        if self
3408            .count_queuing_strategy_size_function
3409            .set(function)
3410            .is_err()
3411        {
3412            warn!("count queuing strategy size function is set twice.");
3413        };
3414    }
3415
3416    pub(crate) fn get_count_queuing_strategy_size(&self) -> Option<Rc<Function>> {
3417        self.count_queuing_strategy_size_function.get().cloned()
3418    }
3419
3420    pub(crate) fn add_notification_permission_request_callback(
3421        &self,
3422        callback_id: String,
3423        callback: Rc<NotificationPermissionCallback>,
3424    ) {
3425        self.notification_permission_request_callback_map
3426            .borrow_mut()
3427            .insert(callback_id, callback);
3428    }
3429
3430    pub(crate) fn remove_notification_permission_request_callback(
3431        &self,
3432        callback_id: String,
3433    ) -> Option<Rc<NotificationPermissionCallback>> {
3434        self.notification_permission_request_callback_map
3435            .borrow_mut()
3436            .remove(&callback_id)
3437    }
3438
3439    pub(crate) fn append_deferred_fetch(
3440        &self,
3441        deferred_fetch: QueuedDeferredFetchRecord,
3442    ) -> DeferredFetchRecordId {
3443        let deferred_record_id = DeferredFetchRecordId::default();
3444        self.fetch_group
3445            .borrow_mut()
3446            .deferred_fetch_records
3447            .insert(deferred_record_id, deferred_fetch);
3448        deferred_record_id
3449    }
3450
3451    pub(crate) fn deferred_fetches(&self) -> Vec<QueuedDeferredFetchRecord> {
3452        self.fetch_group
3453            .borrow()
3454            .deferred_fetch_records
3455            .values()
3456            .cloned()
3457            .collect()
3458    }
3459
3460    pub(crate) fn deferred_fetch_record_for_id(
3461        &self,
3462        deferred_fetch_record_id: &DeferredFetchRecordId,
3463    ) -> QueuedDeferredFetchRecord {
3464        self.fetch_group
3465            .borrow()
3466            .deferred_fetch_records
3467            .get(deferred_fetch_record_id)
3468            .expect("Should always use a generated fetch_record_id instead of passing your own")
3469            .clone()
3470    }
3471
3472    /// <https://fetch.spec.whatwg.org/#process-deferred-fetches>
3473    pub(crate) fn process_deferred_fetches(&self) {
3474        // Step 1. For each deferred fetch record deferredRecord of fetchGroup’s
3475        // deferred fetch records, process a deferred fetch deferredRecord.
3476        for deferred_fetch in self.deferred_fetches() {
3477            deferred_fetch.process(self);
3478        }
3479    }
3480
3481    pub(crate) fn import_map(&self) -> Ref<'_, ImportMap> {
3482        self.import_map.borrow()
3483    }
3484
3485    pub(crate) fn import_map_mut(&self) -> RefMut<'_, ImportMap> {
3486        self.import_map.borrow_mut()
3487    }
3488
3489    pub(crate) fn resolved_module_set(&self) -> Ref<'_, HashSet<ResolvedModule>> {
3490        self.resolved_module_set.borrow()
3491    }
3492
3493    /// <https://html.spec.whatwg.org/multipage/#add-module-to-resolved-module-set>
3494    pub(crate) fn add_module_to_resolved_module_set(
3495        &self,
3496        base_url: &str,
3497        specifier: &str,
3498        specifier_url: Option<ServoUrl>,
3499    ) {
3500        // Step 1. Let global be settingsObject's global object.
3501        // Step 2. If global does not implement Window, then return.
3502        if self.is::<Window>() {
3503            // Step 3. Let record be a new specifier resolution record, with serialized base URL
3504            // set to serializedBaseURL, specifier set to normalizedSpecifier, and specifier as
3505            // a URL set to asURL.
3506            let record =
3507                ResolvedModule::new(base_url.to_owned(), specifier.to_owned(), specifier_url);
3508            // Step 4. Append record to global's resolved module set.
3509            self.resolved_module_set.borrow_mut().insert(record);
3510        }
3511    }
3512
3513    /// <https://html.spec.whatwg.org/multipage/#run-steps-after-a-timeout>
3514    /// TODO: This should end-up being used in the other timer mechanism
3515    /// integrate as per <https://html.spec.whatwg.org/multipage/#timers:run-steps-after-a-timeout?
3516    pub(crate) fn run_steps_after_a_timeout<F>(
3517        &self,
3518        ordering_identifier: DOMString,
3519        milliseconds: i64,
3520        completion_steps: F,
3521    ) -> i32
3522    where
3523        F: 'static + FnOnce(&mut js::context::JSContext, &GlobalScope),
3524    {
3525        let ms = milliseconds.max(0) as u64;
3526        let delay = std::time::Duration::from_millis(ms);
3527
3528        let (callback, timer_key) = self.with_timers(|timers| {
3529            // Step 1. Let timerKey be a new unique internal value.
3530            let timer_key = timers.fresh_runsteps_key();
3531
3532            // Step 2. Let startTime be the current high resolution time given global.
3533            let start_time = timers.now_for_runsteps();
3534
3535            // Step 3. Set global's map of active timers[timerKey] to startTime plus milliseconds.
3536            let deadline = start_time + delay;
3537            timers.runsteps_set_active(timer_key, deadline);
3538
3539            // Step 4. Run the following steps in parallel:
3540            //   (We schedule a oneshot that will enforce the sub-steps when it fires.)
3541            let callback = crate::timers::OneshotTimerCallback::RunStepsAfterTimeout {
3542                // Step 1. timerKey
3543                timer_key,
3544                // Step 4. orderingIdentifier
3545                ordering_id: ordering_identifier,
3546                // Spec: milliseconds
3547                milliseconds: ms,
3548                // Step 4.4 Perform completionSteps.
3549                completion: Box::new(completion_steps),
3550            };
3551            (callback, timer_key)
3552        });
3553        let _ = self.schedule_callback(callback, delay);
3554
3555        // Step 5. Return timerKey.
3556        timer_key
3557    }
3558}
3559
3560/// Returns the Rust global scope from a JS global object.
3561#[expect(unsafe_code)]
3562unsafe fn global_scope_from_global(
3563    cx: &mut js::context::JSContext,
3564    global: *mut JSObject,
3565) -> DomRoot<GlobalScope> {
3566    unsafe {
3567        assert!(!global.is_null());
3568        let clasp = get_object_class(global);
3569        assert_ne!(
3570            ((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)),
3571            0
3572        );
3573        root_from_object(cx, global).unwrap()
3574    }
3575}
3576
3577/// Returns the Rust global scope from a JS global object.
3578#[expect(unsafe_code)]
3579unsafe fn global_scope_from_global_static(global: *mut JSObject) -> DomRoot<GlobalScope> {
3580    assert!(!global.is_null());
3581    let clasp = unsafe { get_object_class(global) };
3582
3583    unsafe {
3584        assert_ne!(
3585            ((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)),
3586            0
3587        );
3588    }
3589
3590    root_from_object_static(global).unwrap()
3591}
3592
3593#[expect(unsafe_code)]
3594impl GlobalScopeHelpers<crate::DomTypeHolder> for GlobalScope {
3595    fn from_current_realm(realm: &'_ mut CurrentRealm) -> DomRoot<Self> {
3596        GlobalScope::from_current_realm(realm)
3597    }
3598
3599    unsafe fn from_object(obj: *mut JSObject) -> DomRoot<Self> {
3600        unsafe { GlobalScope::from_object(obj) }
3601    }
3602
3603    fn from_reflector(reflector: &impl DomObject) -> DomRoot<Self> {
3604        GlobalScope::from_reflector(reflector)
3605    }
3606
3607    fn origin(&self) -> MutableOrigin {
3608        GlobalScope::origin(self)
3609    }
3610
3611    fn incumbent() -> Option<DomRoot<Self>> {
3612        GlobalScope::incumbent()
3613    }
3614
3615    fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
3616        GlobalScope::perform_a_microtask_checkpoint(self, cx)
3617    }
3618
3619    fn get_url(&self) -> ServoUrl {
3620        self.get_url()
3621    }
3622
3623    fn is_secure_context(&self) -> bool {
3624        self.is_secure_context()
3625    }
3626}
3627
3628impl OwnerWindow<DomTypeHolder> for GlobalScope {}