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