Skip to main content

script/dom/globalscope/
globalscope.rs

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