Skip to main content

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