script/dom/
globalscope.rs

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