script/dom/
globalscope.rs

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