Skip to main content

script/dom/globalscope/
globalscope.rs

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