Skip to main content

script/dom/globalscope/
globalscope.rs

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