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