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