script/dom/
globalscope.rs

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