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