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