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