Skip to main content

script/dom/globalscope/
globalscope.rs

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