1use std::borrow::ToOwned;
6use std::cell::{Cell, RefCell, RefMut};
7use std::cmp;
8use std::collections::hash_map::Entry;
9use std::collections::{HashMap, HashSet};
10use std::default::Default;
11use std::ffi::c_void;
12use std::io::{Write, stderr, stdout};
13use std::ptr::NonNull;
14use std::rc::{Rc, Weak};
15use std::sync::Arc;
16use std::time::{Duration, Instant};
17
18use app_units::Au;
19use base64::Engine;
20use content_security_policy::Violation;
21use content_security_policy::sandboxing_directive::SandboxingFlagSet;
22use crossbeam_channel::{Sender, unbounded};
23use cssparser::SourceLocation;
24use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
25use dom_struct::dom_struct;
26use embedder_traits::user_contents::UserScript;
27use embedder_traits::{
28 AlertResponse, ConfirmResponse, EmbedderMsg, JavaScriptEvaluationError, PromptResponse,
29 ScriptToEmbedderChan, SimpleDialogRequest, Theme, UntrustedNodeAddress, ViewportDetails,
30 WebDriverJSResult, WebDriverLoadStatus,
31};
32use euclid::default::Rect as UntypedRect;
33use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
34use fonts::{CspViolationHandler, FontContext, NetworkTimingHandler, WebFontDocumentContext};
35use js::context::JSContext;
36use js::glue::DumpJSStack;
37use js::jsapi::{GCReason, Heap, JS_GC, JSContext as RawJSContext, JSObject, JSPROP_ENUMERATE};
38use js::jsval::{NullValue, UndefinedValue};
39use js::realm::{AutoRealm, CurrentRealm};
40use js::rust::wrappers::JS_DefineProperty;
41use js::rust::{
42 CustomAutoRooter, CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleObject,
43 MutableHandleValue,
44};
45use layout_api::{
46 AxesOverflow, BoxAreaType, CSSPixelRectVec, ElementsFromPointFlags, ElementsFromPointResult,
47 FragmentType, Layout, LayoutImageDestination, PendingImage, PendingImageState,
48 PendingRasterizationImage, PhysicalSides, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest,
49 ReflowRequestRestyle, ReflowStatistics, RestyleReason, ScrollContainerQueryFlags,
50 ScrollContainerResponse, TrustedNodeAddress, combine_id_with_fragment_type,
51};
52use malloc_size_of::MallocSizeOf;
53use media::WindowGLContext;
54use net_traits::image_cache::{
55 ImageCache, ImageCacheResponseCallback, ImageCacheResponseMessage, ImageLoadListener,
56 ImageResponse, PendingImageId, PendingImageResponse, RasterizationCompleteResponse,
57};
58use net_traits::request::Referrer;
59use net_traits::{ResourceFetchTiming, ResourceThreads};
60use num_traits::ToPrimitive;
61use paint_api::{CrossProcessPaintApi, PinchZoomInfos};
62use profile_traits::generic_channel as ProfiledGenericChannel;
63use profile_traits::mem::ProfilerChan as MemProfilerChan;
64use profile_traits::time::ProfilerChan as TimeProfilerChan;
65use rustc_hash::{FxBuildHasher, FxHashMap};
66use script_bindings::cell::{DomRefCell, Ref};
67use script_bindings::codegen::GenericBindings::WindowBinding::ScrollToOptions;
68use script_bindings::conversions::SafeToJSValConvertible;
69use script_bindings::interfaces::WindowHelpers;
70use script_bindings::reflector::DomObject;
71use script_bindings::root::Root;
72use script_traits::{ConstellationInputEvent, ScriptThreadMessage};
73use selectors::attr::CaseSensitivity;
74use servo_arc::Arc as ServoArc;
75use servo_base::cross_process_instant::CrossProcessInstant;
76use servo_base::generic_channel::{self, GenericCallback, GenericSender};
77use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
78#[cfg(feature = "bluetooth")]
79use servo_bluetooth_traits::BluetoothRequest;
80use servo_canvas_traits::webgl::WebGLChan;
81use servo_config::pref;
82use servo_constellation_traits::{
83 LoadData, LoadOrigin, ScreenshotReadinessResponse, ScriptToConstellationChan,
84 ScriptToConstellationMessage, StructuredSerializedData, WindowSizeType,
85};
86use servo_geometry::DeviceIndependentIntRect;
87use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
88use storage_traits::StorageThreads;
89use storage_traits::webstorage_thread::WebStorageType;
90use style::error_reporting::{ContextualParseError, ParseErrorReporter};
91use style::properties::PropertyId;
92use style::properties::style_structs::Font;
93use style::selector_parser::PseudoElement;
94use style::str::HTML_SPACE_CHARACTERS;
95use style::stylesheets::UrlExtraData;
96use style_traits::CSSPixel;
97use stylo_atoms::Atom;
98use time::Duration as TimeDuration;
99use webrender_api::ExternalScrollId;
100use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint};
101
102use super::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
103use super::bindings::trace::HashMapTracedValues;
104use super::performanceresourcetiming::InitiatorType;
105use super::types::SVGSVGElement;
106use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
107 DocumentMethods, DocumentReadyState, NamedPropertyValue,
108};
109use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
110use crate::dom::bindings::codegen::Bindings::HistoryBinding::History_Binding::HistoryMethods;
111use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
112 ImageBitmapOptions, ImageBitmapSource,
113};
114use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryList_Binding::MediaQueryListMethods;
115use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
116use crate::dom::bindings::codegen::Bindings::RequestBinding::{RequestInfo, RequestInit};
117use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
118use crate::dom::bindings::codegen::Bindings::WindowBinding::{
119 self, DeferredRequestInit, FrameRequestCallback, ScrollBehavior, WindowMethods,
120 WindowPostMessageOptions,
121};
122use crate::dom::bindings::codegen::UnionTypes::{
123 RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
124};
125use crate::dom::bindings::error::{
126 Error, ErrorInfo, ErrorResult, Fallible, javascript_error_info_from_error_info,
127};
128use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
129use crate::dom::bindings::num::Finite;
130use crate::dom::bindings::refcounted::Trusted;
131use crate::dom::bindings::reflector::DomGlobal;
132use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
133use crate::dom::bindings::str::{DOMString, USVString};
134use crate::dom::bindings::structuredclone;
135use crate::dom::bindings::trace::{CustomTraceable, JSTraceable, RootedTraceableBox};
136use crate::dom::bindings::utils::GlobalStaticData;
137use crate::dom::bindings::weakref::DOMTracker;
138#[cfg(feature = "bluetooth")]
139use crate::dom::bluetooth::BluetoothExtraPermissionData;
140use crate::dom::cookiestore::CookieStore;
141use crate::dom::crypto::Crypto;
142use crate::dom::csp::GlobalCspReporting;
143use crate::dom::css::cssstyledeclaration::{
144 CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner,
145};
146use crate::dom::customelementregistry::CustomElementRegistry;
147use crate::dom::document::focus::FocusableArea;
148use crate::dom::document::{
149 AnimationFrameCallback, Document, SameOriginDescendantNavigablesIterator,
150};
151use crate::dom::element::Element;
152use crate::dom::event::{Event, EventBubbles, EventCancelable};
153use crate::dom::eventtarget::EventTarget;
154use crate::dom::fetchlaterresult::FetchLaterResult;
155use crate::dom::globalscope::GlobalScope;
156use crate::dom::history::History;
157use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
158use crate::dom::html::htmliframeelement::HTMLIFrameElement;
159use crate::dom::idbfactory::IDBFactory;
160use crate::dom::inputevent::HitTestResult;
161use crate::dom::location::Location;
162use crate::dom::medialist::MediaList;
163use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
164use crate::dom::mediaquerylistevent::MediaQueryListEvent;
165use crate::dom::messageevent::MessageEvent;
166use crate::dom::navigator::Navigator;
167use crate::dom::node::{Node, NodeDamage, NodeTraits, from_untrusted_node_address};
168use crate::dom::performance::performance::Performance;
169use crate::dom::promise::Promise;
170use crate::dom::reporting::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
171use crate::dom::reporting::reportingobserver::ReportingObserver;
172use crate::dom::screen::Screen;
173use crate::dom::scrolling_box::{ScrollingBox, ScrollingBoxSource};
174use crate::dom::selection::Selection;
175use crate::dom::shadowroot::ShadowRoot;
176use crate::dom::storage::Storage;
177#[cfg(feature = "bluetooth")]
178use crate::dom::testrunner::TestRunner;
179use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
180use crate::dom::types::{ImageBitmap, MouseEvent, UIEvent};
181use crate::dom::useractivation::UserActivationTimestamp;
182use crate::dom::visualviewport::{VisualViewport, VisualViewportChanges};
183#[cfg(feature = "webgpu")]
184use crate::dom::webgpu::identityhub::IdentityHub;
185use crate::dom::windowproxy::{WindowProxy, WindowProxyHandler};
186use crate::dom::worklet::Worklet;
187use crate::dom::workletglobalscope::WorkletGlobalScopeType;
188use crate::layout_image::fetch_image_for_layout;
189use crate::messaging::{MainThreadScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
190use crate::microtask::{Microtask, UserMicrotask};
191use crate::network_listener::{ResourceTimingListener, submit_timing};
192use crate::realms::{enter_auto_realm, enter_realm};
193use crate::script_runtime::{CanGc, JSContext as SafeJSContext, Runtime};
194use crate::script_thread::ScriptThread;
195use crate::script_window_proxies::ScriptWindowProxies;
196use crate::task_source::SendableTaskSource;
197use crate::timers::{IsInterval, TimerCallback};
198use crate::unminify::unminified_path;
199use crate::webdriver_handlers::{find_node_by_unique_id_in_document, jsval_to_webdriver};
200use crate::{fetch, window_named_properties};
201
202#[derive(MallocSizeOf)]
207pub struct PendingImageCallback(
208 #[ignore_malloc_size_of = "dyn Fn is currently impossible to measure"]
209 #[expect(clippy::type_complexity)]
210 Box<dyn Fn(PendingImageResponse, &mut js::context::JSContext) + 'static>,
211);
212
213#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
215enum WindowState {
216 Alive,
217 Zombie, }
219
220const INITIAL_REFLOW_DELAY: Duration = Duration::from_millis(200);
223
224#[derive(Clone, Copy, MallocSizeOf)]
235enum LayoutBlocker {
236 WaitingForParse,
238 Parsing(Instant),
240 FiredLoadEventOrParsingTimerExpired,
244}
245
246impl LayoutBlocker {
247 fn layout_blocked(&self) -> bool {
248 !matches!(self, Self::FiredLoadEventOrParsingTimerExpired)
249 }
250}
251
252#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf, PartialEq)]
255pub(crate) struct OngoingNavigation(u32);
256
257type PendingImageRasterizationKey = (PendingImageId, DeviceIntSize);
258
259#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
263#[derive(JSTraceable, MallocSizeOf)]
264struct PendingLayoutImageAncillaryData {
265 node: Dom<Node>,
266 #[no_trace]
267 destination: LayoutImageDestination,
268}
269
270#[dom_struct]
271pub(crate) struct Window {
272 globalscope: GlobalScope,
273
274 #[ignore_malloc_size_of = "Weak does not need to be accounted"]
278 #[no_trace]
279 weak_script_thread: Weak<ScriptThread>,
280
281 #[no_trace]
285 webview_id: WebViewId,
286 script_chan: Sender<MainThreadScriptMsg>,
287 #[no_trace]
288 #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"]
289 layout: RefCell<Box<dyn Layout>>,
290 navigator: MutNullableDom<Navigator>,
291 crypto: MutNullableDom<Crypto>,
292 #[ignore_malloc_size_of = "ImageCache"]
293 #[no_trace]
294 image_cache: Arc<dyn ImageCache>,
295 #[no_trace]
296 image_cache_sender: Sender<ImageCacheResponseMessage>,
297 window_proxy: MutNullableDom<WindowProxy>,
298 document: MutNullableDom<Document>,
299 location: MutNullableDom<Location>,
300 history: MutNullableDom<History>,
301 custom_element_registry: MutNullableDom<CustomElementRegistry>,
302 performance: MutNullableDom<Performance>,
303 #[no_trace]
304 navigation_start: Cell<CrossProcessInstant>,
305 screen: MutNullableDom<Screen>,
306 session_storage: MutNullableDom<Storage>,
307 local_storage: MutNullableDom<Storage>,
308 cookie_store: MutNullableDom<CookieStore>,
310 status: DomRefCell<DOMString>,
311 trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
312
313 ongoing_navigation: Cell<OngoingNavigation>,
316
317 #[no_trace]
320 devtools_markers: DomRefCell<HashSet<TimelineMarkerType>>,
321 #[no_trace]
322 devtools_marker_sender: DomRefCell<Option<GenericSender<Option<TimelineMarker>>>>,
323
324 #[no_trace]
326 unhandled_resize_event: DomRefCell<Option<(ViewportDetails, WindowSizeType)>>,
327
328 #[no_trace]
330 theme: Cell<Theme>,
331
332 #[no_trace]
334 parent_info: Option<PipelineId>,
335
336 dom_static: GlobalStaticData,
338
339 #[conditional_malloc_size_of]
341 js_runtime: DomRefCell<Option<Rc<Runtime>>>,
342
343 #[no_trace]
345 viewport_details: Cell<ViewportDetails>,
346
347 #[no_trace]
349 #[cfg(feature = "bluetooth")]
350 bluetooth_thread: GenericSender<BluetoothRequest>,
351
352 #[cfg(feature = "bluetooth")]
353 bluetooth_extra_permission_data: BluetoothExtraPermissionData,
354
355 #[no_trace]
359 layout_blocker: Cell<LayoutBlocker>,
360
361 #[no_trace]
363 webdriver_script_chan: DomRefCell<Option<GenericSender<WebDriverJSResult>>>,
364
365 #[no_trace]
367 webdriver_load_status_sender: RefCell<Option<GenericSender<WebDriverLoadStatus>>>,
368
369 current_state: Cell<WindowState>,
371
372 error_reporter: CSSErrorReporter,
373
374 media_query_lists: DOMTracker<MediaQueryList>,
376
377 #[cfg(feature = "bluetooth")]
378 test_runner: MutNullableDom<TestRunner>,
379
380 #[no_trace]
382 webgl_chan: Option<WebGLChan>,
383
384 #[ignore_malloc_size_of = "defined in webxr"]
385 #[no_trace]
386 #[cfg(feature = "webxr")]
387 webxr_registry: Option<webxr_api::Registry>,
388
389 #[no_trace]
393 pending_image_callbacks: DomRefCell<FxHashMap<PendingImageId, Vec<PendingImageCallback>>>,
394
395 pending_layout_images: DomRefCell<
400 HashMapTracedValues<PendingImageId, Vec<PendingLayoutImageAncillaryData>, FxBuildHasher>,
401 >,
402
403 pending_images_for_rasterization: DomRefCell<
407 HashMapTracedValues<PendingImageRasterizationKey, Vec<Dom<Node>>, FxBuildHasher>,
408 >,
409
410 unminified_css_dir: DomRefCell<Option<String>>,
413
414 local_script_source: Option<String>,
416
417 test_worklet: MutNullableDom<Worklet>,
419 paint_worklet: MutNullableDom<Worklet>,
421
422 exists_mut_observer: Cell<bool>,
424
425 #[no_trace]
427 paint_api: CrossProcessPaintApi,
428
429 #[no_trace]
432 #[conditional_malloc_size_of]
433 user_scripts: Rc<Vec<UserScript>>,
434
435 #[ignore_malloc_size_of = "defined in script_thread"]
437 #[no_trace]
438 player_context: WindowGLContext,
439
440 throttled: Cell<bool>,
441
442 #[conditional_malloc_size_of]
446 layout_marker: DomRefCell<Rc<Cell<bool>>>,
447
448 current_event: DomRefCell<Option<Dom<Event>>>,
450
451 reporting_observer_list: DomRefCell<Vec<DomRoot<ReportingObserver>>>,
453
454 report_list: DomRefCell<Vec<Report>>,
456
457 #[no_trace]
459 endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
460
461 #[conditional_malloc_size_of]
463 script_window_proxies: Rc<ScriptWindowProxies>,
464
465 has_pending_screenshot_readiness_request: Cell<bool>,
467
468 visual_viewport: MutNullableDom<VisualViewport>,
471
472 has_changed_visual_viewport_dimension: Cell<bool>,
474
475 #[no_trace]
477 last_activation_timestamp: Cell<UserActivationTimestamp>,
478
479 devtools_wants_updates: Cell<bool>,
482}
483
484impl Window {
485 pub(crate) fn script_thread(&self) -> Rc<ScriptThread> {
486 Weak::upgrade(&self.weak_script_thread)
487 .expect("Weak reference should always be upgradable when a ScriptThread is running")
488 }
489
490 pub(crate) fn webview_id(&self) -> WebViewId {
491 self.webview_id
492 }
493
494 pub(crate) fn as_global_scope(&self) -> &GlobalScope {
495 self.upcast::<GlobalScope>()
496 }
497
498 pub(crate) fn layout(&self) -> Ref<'_, Box<dyn Layout>> {
499 self.layout.borrow()
500 }
501
502 pub(crate) fn layout_mut(&self) -> RefMut<'_, Box<dyn Layout>> {
503 self.layout.borrow_mut()
504 }
505
506 pub(crate) fn get_exists_mut_observer(&self) -> bool {
507 self.exists_mut_observer.get()
508 }
509
510 pub(crate) fn set_exists_mut_observer(&self) {
511 self.exists_mut_observer.set(true);
512 }
513
514 #[expect(unsafe_code)]
515 pub(crate) fn clear_js_runtime_for_script_deallocation(&self) {
516 self.as_global_scope()
517 .remove_web_messaging_and_dedicated_workers_infra();
518 unsafe {
519 *self.js_runtime.borrow_for_script_deallocation() = None;
520 self.window_proxy.set(None);
521 self.current_state.set(WindowState::Zombie);
522 self.as_global_scope()
523 .task_manager()
524 .cancel_all_tasks_and_ignore_future_tasks();
525 }
526 }
527
528 pub(crate) fn discard_browsing_context(&self) {
531 let proxy = match self.window_proxy.get() {
532 Some(proxy) => proxy,
533 None => panic!("Discarding a BC from a window that has none"),
534 };
535 proxy.discard_browsing_context();
536 self.as_global_scope()
540 .task_manager()
541 .cancel_all_tasks_and_ignore_future_tasks();
542 }
543
544 pub(crate) fn time_profiler_chan(&self) -> &TimeProfilerChan {
546 self.globalscope.time_profiler_chan()
547 }
548
549 pub(crate) fn origin(&self) -> &MutableOrigin {
550 self.globalscope.origin()
551 }
552
553 #[expect(unsafe_code)]
554 pub(crate) fn get_cx(&self) -> SafeJSContext {
555 unsafe { SafeJSContext::from_ptr(js::rust::Runtime::get().unwrap().as_ptr()) }
556 }
557
558 pub(crate) fn get_js_runtime(&self) -> Ref<'_, Option<Rc<Runtime>>> {
559 self.js_runtime.borrow()
560 }
561
562 pub(crate) fn main_thread_script_chan(&self) -> &Sender<MainThreadScriptMsg> {
563 &self.script_chan
564 }
565
566 pub(crate) fn parent_info(&self) -> Option<PipelineId> {
567 self.parent_info
568 }
569
570 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
571 let (sender, receiver) = unbounded();
572 (
573 ScriptEventLoopSender::MainThread(sender),
574 ScriptEventLoopReceiver::MainThread(receiver),
575 )
576 }
577
578 pub(crate) fn event_loop_sender(&self) -> ScriptEventLoopSender {
579 ScriptEventLoopSender::MainThread(self.script_chan.clone())
580 }
581
582 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
583 self.image_cache.clone()
584 }
585
586 pub(crate) fn window_proxy(&self) -> DomRoot<WindowProxy> {
588 self.window_proxy.get().unwrap()
589 }
590
591 pub(crate) fn append_reporting_observer(&self, reporting_observer: DomRoot<ReportingObserver>) {
592 self.reporting_observer_list
593 .borrow_mut()
594 .push(reporting_observer);
595 }
596
597 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
598 let index = {
599 let list = self.reporting_observer_list.borrow();
600 list.iter()
601 .position(|observer| &**observer == reporting_observer)
602 };
603
604 if let Some(index) = index {
605 self.reporting_observer_list.borrow_mut().remove(index);
606 }
607 }
608
609 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
610 self.reporting_observer_list.borrow().clone()
611 }
612
613 pub(crate) fn append_report(&self, report: Report) {
614 self.report_list.borrow_mut().push(report);
615 let trusted_window = Trusted::new(self);
616 self.upcast::<GlobalScope>()
617 .task_manager()
618 .dom_manipulation_task_source()
619 .queue(task!(send_to_reporting_endpoints: move || {
620 let window = trusted_window.root();
621 let reports = std::mem::take(&mut *window.report_list.borrow_mut());
622 window.upcast::<GlobalScope>().send_reports_to_endpoints(
623 reports,
624 window.endpoints_list.borrow().clone(),
625 );
626 }));
627 }
628
629 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
630 self.report_list.borrow().clone()
631 }
632
633 pub(crate) fn set_endpoints_list(&self, endpoints: Vec<ReportingEndpoint>) {
634 *self.endpoints_list.borrow_mut() = endpoints;
635 }
636
637 pub(crate) fn undiscarded_window_proxy(&self) -> Option<DomRoot<WindowProxy>> {
640 self.window_proxy.get().and_then(|window_proxy| {
641 if window_proxy.is_browsing_context_discarded() {
642 None
643 } else {
644 Some(window_proxy)
645 }
646 })
647 }
648
649 pub(crate) fn top_level_document_if_local(&self) -> Option<DomRoot<Document>> {
654 if self.is_top_level() {
655 return Some(self.Document());
656 }
657
658 let window_proxy = self.undiscarded_window_proxy()?;
659 self.script_window_proxies
660 .find_window_proxy(window_proxy.webview_id().into())?
661 .document()
662 }
663
664 #[cfg(feature = "bluetooth")]
665 pub(crate) fn bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
666 self.bluetooth_thread.clone()
667 }
668
669 #[cfg(feature = "bluetooth")]
670 pub(crate) fn bluetooth_extra_permission_data(&self) -> &BluetoothExtraPermissionData {
671 &self.bluetooth_extra_permission_data
672 }
673
674 pub(crate) fn css_error_reporter(&self) -> &CSSErrorReporter {
675 &self.error_reporter
676 }
677
678 pub(crate) fn webgl_chan(&self) -> Option<WebGLChan> {
679 self.webgl_chan.clone()
680 }
681
682 pub(crate) fn webgl_chan_value(&self) -> Option<WebGLChan> {
684 self.webgl_chan.clone()
685 }
686
687 #[cfg(feature = "webxr")]
688 pub(crate) fn webxr_registry(&self) -> Option<webxr_api::Registry> {
689 self.webxr_registry.clone()
690 }
691
692 fn new_paint_worklet(&self, cx: &mut JSContext) -> DomRoot<Worklet> {
693 debug!("Creating new paint worklet.");
694 Worklet::new(cx, self, WorkletGlobalScopeType::Paint)
695 }
696
697 pub(crate) fn register_image_cache_listener(
698 &self,
699 id: PendingImageId,
700 callback: impl Fn(PendingImageResponse, &mut js::context::JSContext) + 'static,
701 ) -> ImageCacheResponseCallback {
702 self.pending_image_callbacks
703 .borrow_mut()
704 .entry(id)
705 .or_default()
706 .push(PendingImageCallback(Box::new(callback)));
707
708 let image_cache_sender = self.image_cache_sender.clone();
709 Box::new(move |message| {
710 let _ = image_cache_sender.send(message);
711 })
712 }
713
714 fn pending_layout_image_notification(&self, response: PendingImageResponse) {
715 let mut images = self.pending_layout_images.borrow_mut();
716 let nodes = images.entry(response.id);
717 let nodes = match nodes {
718 Entry::Occupied(nodes) => nodes,
719 Entry::Vacant(_) => return,
720 };
721 if matches!(
722 response.response,
723 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode
724 ) {
725 for ancillary_data in nodes.get() {
726 match ancillary_data.destination {
727 LayoutImageDestination::BoxTreeConstruction => {
728 ancillary_data.node.dirty(NodeDamage::Other);
729 },
730 LayoutImageDestination::DisplayListBuilding => {
731 self.layout().set_needs_new_display_list();
732 },
733 }
734 }
735 }
736
737 match response.response {
738 ImageResponse::MetadataLoaded(_) => {},
739 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode => {
740 nodes.remove();
741 },
742 }
743 }
744
745 pub(crate) fn handle_image_rasterization_complete_notification(
746 &self,
747 response: RasterizationCompleteResponse,
748 ) {
749 let mut images = self.pending_images_for_rasterization.borrow_mut();
750 let nodes = images.entry((response.image_id, response.requested_size));
751 let nodes = match nodes {
752 Entry::Occupied(nodes) => nodes,
753 Entry::Vacant(_) => return,
754 };
755 for node in nodes.get() {
756 node.dirty(NodeDamage::Other);
757 }
758 nodes.remove();
759 }
760
761 pub(crate) fn pending_image_notification(
762 &self,
763 response: PendingImageResponse,
764 cx: &mut js::context::JSContext,
765 ) {
766 let mut images = std::mem::take(&mut *self.pending_image_callbacks.borrow_mut());
771 let Entry::Occupied(callbacks) = images.entry(response.id) else {
772 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
773 return;
774 };
775
776 for callback in callbacks.get() {
777 callback.0(response.clone(), cx);
778 }
779
780 match response.response {
781 ImageResponse::MetadataLoaded(_) => {},
782 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode => {
783 callbacks.remove();
784 },
785 }
786
787 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
788 }
789
790 pub(crate) fn paint_api(&self) -> &CrossProcessPaintApi {
791 &self.paint_api
792 }
793
794 pub(crate) fn userscripts(&self) -> &[UserScript] {
795 &self.user_scripts
796 }
797
798 pub(crate) fn get_player_context(&self) -> WindowGLContext {
799 self.player_context.clone()
800 }
801
802 pub(crate) fn dispatch_event_with_target_override(&self, cx: &mut JSContext, event: &Event) {
804 event.dispatch(cx, self.upcast(), true);
805 }
806
807 pub(crate) fn font_context(&self) -> &Arc<FontContext> {
808 self.as_global_scope()
809 .font_context()
810 .expect("A `Window` should always have a `FontContext`")
811 }
812
813 pub(crate) fn ongoing_navigation(&self) -> OngoingNavigation {
814 self.ongoing_navigation.get()
815 }
816
817 pub(crate) fn set_ongoing_navigation(&self) -> OngoingNavigation {
819 let new_value = self.ongoing_navigation.get().0.wrapping_add(1);
823
824 self.ongoing_navigation.set(OngoingNavigation(new_value));
831
832 OngoingNavigation(new_value)
834 }
835
836 fn stop_loading(&self, cx: &mut js::context::JSContext) {
838 let doc = self.Document();
840
841 self.set_ongoing_navigation();
851
852 doc.abort_a_document_and_its_descendants(cx);
854 }
855
856 fn destroy_top_level_traversable(&self, cx: &mut js::context::JSContext) {
858 let document = self.Document();
864 document.destroy_document_and_its_descendants(cx);
866 self.send_to_constellation(ScriptToConstellationMessage::DiscardTopLevelBrowsingContext);
868 }
869
870 fn definitely_close(&self, cx: &mut js::context::JSContext) {
872 let document = self.Document();
873 if !document.check_if_unloading_is_cancelled(cx, false) {
878 return;
879 }
880 document.unload(cx, false);
884 self.destroy_top_level_traversable(cx);
886 }
887
888 fn cannot_show_simple_dialogs(&self) -> bool {
890 if self
893 .Document()
894 .has_active_sandboxing_flag(SandboxingFlagSet::SANDBOXED_MODALS_FLAG)
895 {
896 return true;
897 }
898
899 false
918 }
919
920 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
921 self.script_thread().perform_a_microtask_checkpoint(cx);
922 }
923
924 pub(crate) fn web_font_context(&self) -> WebFontDocumentContext {
925 let global = self.as_global_scope();
926 WebFontDocumentContext {
927 policy_container: global.policy_container(),
928 request_client: global.request_client(),
929 document_url: global.api_base_url(),
930 has_trustworthy_ancestor_origin: global.has_trustworthy_ancestor_origin(),
931 insecure_requests_policy: global.insecure_requests_policy(),
932 csp_handler: Box::new(FontCspHandler {
933 global: Trusted::new(global),
934 task_source: global
935 .task_manager()
936 .dom_manipulation_task_source()
937 .to_sendable(),
938 }),
939 network_timing_handler: Box::new(FontNetworkTimingHandler {
940 global: Trusted::new(global),
941 task_source: global
942 .task_manager()
943 .dom_manipulation_task_source()
944 .to_sendable(),
945 }),
946 }
947 }
948
949 #[expect(unsafe_code)]
950 pub(crate) fn gc(&self) {
951 unsafe {
952 JS_GC(*self.get_cx(), GCReason::API);
953 }
954 }
955}
956
957#[derive(Debug)]
958struct FontCspHandler {
959 global: Trusted<GlobalScope>,
960 task_source: SendableTaskSource,
961}
962
963impl CspViolationHandler for FontCspHandler {
964 fn process_violations(&self, violations: Vec<Violation>) {
965 let global = self.global.clone();
966 self.task_source.queue(task!(csp_violation: move || {
967 global.root().report_csp_violations(violations, None, None);
968 }));
969 }
970
971 fn clone(&self) -> Box<dyn CspViolationHandler> {
972 Box::new(Self {
973 global: self.global.clone(),
974 task_source: self.task_source.clone(),
975 })
976 }
977}
978
979#[derive(Debug)]
980struct FontNetworkTimingHandler {
981 global: Trusted<GlobalScope>,
982 task_source: SendableTaskSource,
983}
984
985impl NetworkTimingHandler for FontNetworkTimingHandler {
986 fn submit_timing(&self, url: ServoUrl, response: ResourceFetchTiming) {
987 let global = self.global.clone();
988 self.task_source.queue(task!(network_timing: move |cx| {
989 submit_timing(
990 cx,
991 &FontFetchListener {
992 url,
993 global
994 },
995 &Ok(()),
996 &response,
997 );
998 }));
999 }
1000
1001 fn clone(&self) -> Box<dyn NetworkTimingHandler> {
1002 Box::new(Self {
1003 global: self.global.clone(),
1004 task_source: self.task_source.clone(),
1005 })
1006 }
1007}
1008
1009#[derive(Debug)]
1010struct FontFetchListener {
1011 global: Trusted<GlobalScope>,
1012 url: ServoUrl,
1013}
1014
1015impl ResourceTimingListener for FontFetchListener {
1016 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1017 (InitiatorType::Css, self.url.clone())
1018 }
1019
1020 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1021 self.global.root()
1022 }
1023}
1024
1025pub(crate) fn base64_btoa(input: DOMString) -> Fallible<DOMString> {
1027 if input.str().chars().any(|c: char| c > '\u{FF}') {
1031 Err(Error::InvalidCharacter(None))
1032 } else {
1033 let octets = input
1038 .str()
1039 .chars()
1040 .map(|c: char| c as u8)
1041 .collect::<Vec<u8>>();
1042
1043 let config =
1046 base64::engine::general_purpose::GeneralPurposeConfig::new().with_encode_padding(true);
1047 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
1048 Ok(DOMString::from(engine.encode(octets)))
1049 }
1050}
1051
1052pub(crate) fn base64_atob(input: DOMString) -> Fallible<DOMString> {
1054 fn is_html_space(c: char) -> bool {
1056 HTML_SPACE_CHARACTERS.contains(&c)
1057 }
1058 let without_spaces = input
1059 .str()
1060 .chars()
1061 .filter(|&c| !is_html_space(c))
1062 .collect::<String>();
1063 let mut input = &*without_spaces;
1064
1065 if input.len() % 4 == 0 {
1069 if input.ends_with("==") {
1070 input = &input[..input.len() - 2]
1071 } else if input.ends_with('=') {
1072 input = &input[..input.len() - 1]
1073 }
1074 }
1075
1076 if input.len() % 4 == 1 {
1079 return Err(Error::InvalidCharacter(None));
1080 }
1081
1082 if input
1090 .chars()
1091 .any(|c| c != '+' && c != '/' && !c.is_alphanumeric())
1092 {
1093 return Err(Error::InvalidCharacter(None));
1094 }
1095
1096 let config = base64::engine::general_purpose::GeneralPurposeConfig::new()
1097 .with_decode_padding_mode(base64::engine::DecodePaddingMode::RequireNone)
1098 .with_decode_allow_trailing_bits(true);
1099 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
1100
1101 let data = engine
1102 .decode(input)
1103 .map_err(|_| Error::InvalidCharacter(None))?;
1104 Ok(data.iter().map(|&b| b as char).collect::<String>().into())
1105}
1106
1107impl WindowMethods<crate::DomTypeHolder> for Window {
1108 fn Alert_(&self) {
1110 self.Alert(DOMString::new());
1113 }
1114
1115 fn Alert(&self, mut message: DOMString) {
1117 if self.cannot_show_simple_dialogs() {
1119 return;
1120 }
1121
1122 message.normalize_newlines();
1126
1127 {
1138 let stderr = stderr();
1142 let mut stderr = stderr.lock();
1143 let stdout = stdout();
1144 let mut stdout = stdout.lock();
1145 writeln!(&mut stdout, "\nALERT: {message}").unwrap();
1146 stdout.flush().unwrap();
1147 stderr.flush().unwrap();
1148 }
1149
1150 let (sender, receiver) =
1151 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1152 let dialog = SimpleDialogRequest::Alert {
1153 id: self.Document().embedder_controls().next_control_id(),
1154 message: message.to_string(),
1155 response_sender: sender,
1156 };
1157 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1158 receiver.recv().unwrap_or_else(|_| {
1159 debug!("Alert dialog was cancelled or failed to show.");
1161 AlertResponse::Ok
1162 });
1163
1164 }
1167
1168 fn Confirm(&self, mut message: DOMString) -> bool {
1170 if self.cannot_show_simple_dialogs() {
1172 return false;
1173 }
1174
1175 message.normalize_newlines();
1177
1178 let (sender, receiver) =
1184 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1185 let dialog = SimpleDialogRequest::Confirm {
1186 id: self.Document().embedder_controls().next_control_id(),
1187 message: message.to_string(),
1188 response_sender: sender,
1189 };
1190 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1191
1192 match receiver.recv() {
1208 Ok(ConfirmResponse::Ok) => true,
1209 Ok(ConfirmResponse::Cancel) => false,
1210 Err(_) => {
1211 warn!("Confirm dialog was cancelled or failed to show.");
1212 false
1213 },
1214 }
1215 }
1216
1217 fn Prompt(&self, mut message: DOMString, default: DOMString) -> Option<DOMString> {
1219 if self.cannot_show_simple_dialogs() {
1221 return None;
1222 }
1223
1224 message.normalize_newlines();
1226
1227 let (sender, receiver) =
1235 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1236 let dialog = SimpleDialogRequest::Prompt {
1237 id: self.Document().embedder_controls().next_control_id(),
1238 message: message.to_string(),
1239 default: default.to_string(),
1240 response_sender: sender,
1241 };
1242 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1243
1244 match receiver.recv() {
1263 Ok(PromptResponse::Ok(input)) => Some(input.into()),
1264 Ok(PromptResponse::Cancel) => None,
1265 Err(_) => {
1266 warn!("Prompt dialog was cancelled or failed to show.");
1267 None
1268 },
1269 }
1270 }
1271
1272 fn Stop(&self, cx: &mut js::context::JSContext) {
1274 self.stop_loading(cx);
1279 }
1280
1281 fn Focus(&self, cx: &mut js::context::JSContext) {
1283 let document = self.Document();
1292 if !document.is_active() || self.undiscarded_window_proxy().is_none() {
1293 return;
1294 }
1295
1296 document.focus_handler().focus(cx, FocusableArea::Viewport);
1301
1302 }
1307
1308 fn Blur(&self) {
1310 }
1313
1314 fn Open(
1316 &self,
1317 cx: &mut JSContext,
1318 url: USVString,
1319 target: DOMString,
1320 features: DOMString,
1321 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
1322 self.window_proxy().open(cx, url, target, features)
1323 }
1324
1325 fn GetOpener(&self, cx: &mut CurrentRealm, mut retval: MutableHandleValue) -> Fallible<()> {
1327 let current = match self.window_proxy.get() {
1329 Some(proxy) => proxy,
1330 None => {
1332 retval.set(NullValue());
1333 return Ok(());
1334 },
1335 };
1336 if current.is_browsing_context_discarded() {
1341 retval.set(NullValue());
1342 return Ok(());
1343 }
1344 current.opener(cx, retval);
1346 Ok(())
1347 }
1348
1349 #[expect(unsafe_code)]
1350 fn SetOpener(&self, cx: SafeJSContext, value: HandleValue) -> ErrorResult {
1352 if value.is_null() {
1354 if let Some(proxy) = self.window_proxy.get() {
1355 proxy.disown();
1356 }
1357 return Ok(());
1358 }
1359 let obj = self.reflector().get_jsobject();
1361 unsafe {
1362 let result =
1363 JS_DefineProperty(*cx, obj, c"opener".as_ptr(), value, JSPROP_ENUMERATE as u32);
1364
1365 if result { Ok(()) } else { Err(Error::JSFailed) }
1366 }
1367 }
1368
1369 fn Closed(&self) -> bool {
1371 self.window_proxy
1372 .get()
1373 .map(|ref proxy| proxy.is_browsing_context_discarded() || proxy.is_closing())
1374 .unwrap_or(true)
1375 }
1376
1377 fn Close(&self) {
1379 let window_proxy = match self.window_proxy.get() {
1381 Some(proxy) => proxy,
1382 None => return,
1384 };
1385 if window_proxy.is_closing() {
1387 return;
1388 }
1389 if let Ok(history_length) = self.History().GetLength() {
1392 let is_auxiliary = window_proxy.is_auxiliary();
1393
1394 let is_script_closable = (self.is_top_level() && history_length == 1) ||
1396 is_auxiliary ||
1397 pref!(dom_allow_scripts_to_close_windows);
1398
1399 if is_script_closable {
1403 window_proxy.close();
1405
1406 let this = Trusted::new(self);
1408 let task = task!(window_close_browsing_context: move |cx| {
1409 let window = this.root();
1410 window.definitely_close(cx);
1411 });
1412 self.as_global_scope()
1413 .task_manager()
1414 .dom_manipulation_task_source()
1415 .queue(task);
1416 }
1417 }
1418 }
1419
1420 fn Document(&self) -> DomRoot<Document> {
1422 self.document
1423 .get()
1424 .expect("Document accessed before initialization.")
1425 }
1426
1427 fn History(&self) -> DomRoot<History> {
1429 self.history
1430 .or_init(|| History::new(self, CanGc::deprecated_note()))
1431 }
1432
1433 fn IndexedDB(&self) -> DomRoot<IDBFactory> {
1435 self.upcast::<GlobalScope>().get_indexeddb()
1436 }
1437
1438 fn CustomElements(&self) -> DomRoot<CustomElementRegistry> {
1440 self.custom_element_registry
1441 .or_init(|| CustomElementRegistry::new(self, CanGc::deprecated_note()))
1442 }
1443
1444 fn Location(&self, cx: &mut js::context::JSContext) -> DomRoot<Location> {
1446 self.location.or_init(|| Location::new(cx, self))
1447 }
1448
1449 fn GetSessionStorage(&self, cx: &mut js::context::JSContext) -> Fallible<DomRoot<Storage>> {
1451 if let Some(storage) = self.session_storage.get() {
1454 return Ok(storage);
1455 }
1456
1457 if !self.origin().is_tuple() {
1461 return Err(Error::Security(Some(
1462 "Cannot access sessionStorage from opaque origin.".to_string(),
1463 )));
1464 }
1465
1466 let storage = Storage::new(self, WebStorageType::Session, CanGc::from_cx(cx));
1468
1469 self.session_storage.set(Some(&storage));
1471
1472 Ok(storage)
1474 }
1475
1476 fn GetLocalStorage(&self, cx: &mut js::context::JSContext) -> Fallible<DomRoot<Storage>> {
1478 if let Some(storage) = self.local_storage.get() {
1481 return Ok(storage);
1482 }
1483
1484 if !self.origin().is_tuple() {
1488 return Err(Error::Security(Some(
1489 "Cannot access localStorage from opaque origin.".to_string(),
1490 )));
1491 }
1492
1493 let storage = Storage::new(self, WebStorageType::Local, CanGc::from_cx(cx));
1495
1496 self.local_storage.set(Some(&storage));
1498
1499 Ok(storage)
1501 }
1502
1503 fn CookieStore(&self, can_gc: CanGc) -> DomRoot<CookieStore> {
1505 self.cookie_store
1506 .or_init(|| CookieStore::new(self.upcast::<GlobalScope>(), can_gc))
1507 }
1508
1509 fn Crypto(&self) -> DomRoot<Crypto> {
1511 self.crypto
1512 .or_init(|| Crypto::new(self.as_global_scope(), CanGc::deprecated_note()))
1513 }
1514
1515 fn GetFrameElement(&self) -> Option<DomRoot<Element>> {
1517 let window_proxy = self.window_proxy.get()?;
1519
1520 let container = window_proxy.frame_element()?;
1522
1523 let container_doc = container.owner_document();
1525 let current_doc = GlobalScope::current()
1526 .expect("No current global object")
1527 .as_window()
1528 .Document();
1529 if !current_doc
1530 .origin()
1531 .same_origin_domain(&container_doc.origin())
1532 {
1533 return None;
1534 }
1535 Some(DomRoot::from_ref(container))
1537 }
1538
1539 fn ReportError(&self, cx: SafeJSContext, error: HandleValue, can_gc: CanGc) {
1541 self.as_global_scope()
1542 .report_an_exception(cx, error, can_gc);
1543 }
1544
1545 fn Navigator(&self) -> DomRoot<Navigator> {
1547 self.navigator
1548 .or_init(|| Navigator::new(self, CanGc::deprecated_note()))
1549 }
1550
1551 fn ClientInformation(&self) -> DomRoot<Navigator> {
1553 self.Navigator()
1554 }
1555
1556 fn SetTimeout(
1558 &self,
1559 cx: &mut js::context::JSContext,
1560 callback: TrustedScriptOrStringOrFunction,
1561 timeout: i32,
1562 args: Vec<HandleValue>,
1563 ) -> Fallible<i32> {
1564 let callback = match callback {
1565 TrustedScriptOrStringOrFunction::String(i) => {
1566 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1567 },
1568 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1569 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1570 },
1571 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1572 };
1573 self.as_global_scope().set_timeout_or_interval(
1574 cx,
1575 callback,
1576 args,
1577 Duration::from_millis(timeout.max(0) as u64),
1578 IsInterval::NonInterval,
1579 )
1580 }
1581
1582 fn ClearTimeout(&self, handle: i32) {
1584 self.as_global_scope().clear_timeout_or_interval(handle);
1585 }
1586
1587 fn SetInterval(
1589 &self,
1590 cx: &mut js::context::JSContext,
1591 callback: TrustedScriptOrStringOrFunction,
1592 timeout: i32,
1593 args: Vec<HandleValue>,
1594 ) -> Fallible<i32> {
1595 let callback = match callback {
1596 TrustedScriptOrStringOrFunction::String(i) => {
1597 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1598 },
1599 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1600 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1601 },
1602 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1603 };
1604 self.as_global_scope().set_timeout_or_interval(
1605 cx,
1606 callback,
1607 args,
1608 Duration::from_millis(timeout.max(0) as u64),
1609 IsInterval::Interval,
1610 )
1611 }
1612
1613 fn ClearInterval(&self, handle: i32) {
1615 self.ClearTimeout(handle);
1616 }
1617
1618 fn QueueMicrotask(&self, callback: Rc<VoidFunction>) {
1620 ScriptThread::enqueue_microtask(Microtask::User(UserMicrotask {
1621 callback,
1622 pipeline: self.pipeline_id(),
1623 }));
1624 }
1625
1626 fn CreateImageBitmap(
1628 &self,
1629 realm: &mut CurrentRealm,
1630 image: ImageBitmapSource,
1631 options: &ImageBitmapOptions,
1632 ) -> Rc<Promise> {
1633 ImageBitmap::create_image_bitmap(
1634 self.as_global_scope(),
1635 image,
1636 0,
1637 0,
1638 None,
1639 None,
1640 options,
1641 realm,
1642 )
1643 }
1644
1645 fn CreateImageBitmap_(
1647 &self,
1648 realm: &mut CurrentRealm,
1649 image: ImageBitmapSource,
1650 sx: i32,
1651 sy: i32,
1652 sw: i32,
1653 sh: i32,
1654 options: &ImageBitmapOptions,
1655 ) -> Rc<Promise> {
1656 ImageBitmap::create_image_bitmap(
1657 self.as_global_scope(),
1658 image,
1659 sx,
1660 sy,
1661 Some(sw),
1662 Some(sh),
1663 options,
1664 realm,
1665 )
1666 }
1667
1668 fn Window(&self) -> DomRoot<WindowProxy> {
1670 self.window_proxy()
1671 }
1672
1673 fn Self_(&self) -> DomRoot<WindowProxy> {
1675 self.window_proxy()
1676 }
1677
1678 fn Frames(&self) -> DomRoot<WindowProxy> {
1680 self.window_proxy()
1681 }
1682
1683 fn Length(&self) -> u32 {
1685 self.Document().iframes().iter().count() as u32
1686 }
1687
1688 fn GetParent(&self) -> Option<DomRoot<WindowProxy>> {
1690 let window_proxy = self.undiscarded_window_proxy()?;
1692
1693 if let Some(parent) = window_proxy.parent() {
1695 return Some(DomRoot::from_ref(parent));
1696 }
1697 Some(window_proxy)
1699 }
1700
1701 fn GetTop(&self) -> Option<DomRoot<WindowProxy>> {
1703 let window_proxy = self.undiscarded_window_proxy()?;
1705
1706 Some(DomRoot::from_ref(window_proxy.top()))
1708 }
1709
1710 fn Performance(&self) -> DomRoot<Performance> {
1713 self.performance.or_init(|| {
1714 Performance::new(
1715 self.as_global_scope(),
1716 self.navigation_start.get(),
1717 CanGc::deprecated_note(),
1718 )
1719 })
1720 }
1721
1722 global_event_handlers!();
1724
1725 window_event_handlers!();
1727
1728 fn Screen(&self, can_gc: CanGc) -> DomRoot<Screen> {
1730 self.screen.or_init(|| Screen::new(self, can_gc))
1731 }
1732
1733 fn GetVisualViewport(&self, can_gc: CanGc) -> Option<DomRoot<VisualViewport>> {
1735 if !self.Document().is_fully_active() {
1739 return None;
1740 }
1741
1742 Some(self.get_or_init_visual_viewport(can_gc))
1743 }
1744
1745 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
1747 base64_btoa(btoa)
1748 }
1749
1750 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
1752 base64_atob(atob)
1753 }
1754
1755 fn RequestAnimationFrame(&self, callback: Rc<FrameRequestCallback>) -> u32 {
1757 self.Document()
1758 .request_animation_frame(AnimationFrameCallback::FrameRequestCallback { callback })
1759 }
1760
1761 fn CancelAnimationFrame(&self, ident: u32) {
1763 let doc = self.Document();
1764 doc.cancel_animation_frame(ident);
1765 }
1766
1767 fn PostMessage(
1769 &self,
1770 cx: &mut JSContext,
1771 message: HandleValue,
1772 target_origin: USVString,
1773 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1774 ) -> ErrorResult {
1775 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1776 let source = incumbent.as_window();
1777 let source_origin = source.Document().origin().immutable().clone();
1778
1779 self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
1780 }
1781
1782 fn PostMessage_(
1784 &self,
1785 cx: &mut JSContext,
1786 message: HandleValue,
1787 options: RootedTraceableBox<WindowPostMessageOptions>,
1788 ) -> ErrorResult {
1789 let mut rooted = CustomAutoRooter::new(
1790 options
1791 .parent
1792 .transfer
1793 .iter()
1794 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
1795 .collect(),
1796 );
1797 #[expect(unsafe_code)]
1798 let transfer = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
1799
1800 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1801 let source = incumbent.as_window();
1802
1803 let source_origin = source.Document().origin().immutable().clone();
1804
1805 self.post_message_impl(
1806 &options.targetOrigin,
1807 source_origin,
1808 source,
1809 cx,
1810 message,
1811 transfer,
1812 )
1813 }
1814
1815 fn CaptureEvents(&self) {
1817 }
1819
1820 fn ReleaseEvents(&self) {
1822 }
1824
1825 fn WebdriverCallback(&self, realm: &mut CurrentRealm, value: HandleValue) {
1827 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1828 if let Some(webdriver_script_sender) = webdriver_script_sender {
1829 let result = jsval_to_webdriver(realm, &self.globalscope, value);
1830 let _ = webdriver_script_sender.send(result);
1831 }
1832 }
1833
1834 fn WebdriverException(&self, cx: &mut JSContext, value: HandleValue) {
1835 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1836 if let Some(webdriver_script_sender) = webdriver_script_sender {
1837 let error_info = ErrorInfo::from_value(value, cx.into(), CanGc::from_cx(cx));
1838 let _ = webdriver_script_sender.send(Err(
1839 JavaScriptEvaluationError::EvaluationFailure(Some(
1840 javascript_error_info_from_error_info(cx, &error_info, value),
1841 )),
1842 ));
1843 }
1844 }
1845
1846 fn WebdriverElement(&self, id: DOMString) -> Option<DomRoot<Element>> {
1847 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1848 }
1849
1850 fn WebdriverFrame(&self, browsing_context_id: DOMString) -> Option<DomRoot<WindowProxy>> {
1851 self.Document()
1852 .iframes()
1853 .iter()
1854 .find(|iframe| {
1855 iframe
1856 .browsing_context_id()
1857 .as_ref()
1858 .map(BrowsingContextId::to_string) ==
1859 Some(browsing_context_id.to_string())
1860 })
1861 .and_then(|iframe| iframe.GetContentWindow())
1862 }
1863
1864 fn WebdriverWindow(&self, webview_id: DOMString) -> DomRoot<WindowProxy> {
1865 let window_proxy = &self
1866 .window_proxy
1867 .get()
1868 .expect("Should always have a WindowProxy when calling WebdriverWindow");
1869 assert!(
1870 self.is_top_level(),
1871 "Window must be top level browsing context."
1872 );
1873 assert!(self.webview_id().to_string() == webview_id);
1874 DomRoot::from_ref(window_proxy)
1875 }
1876
1877 fn WebdriverShadowRoot(&self, id: DOMString) -> Option<DomRoot<ShadowRoot>> {
1878 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1879 }
1880
1881 fn GetComputedStyle(
1883 &self,
1884 element: &Element,
1885 pseudo: Option<DOMString>,
1886 ) -> DomRoot<CSSStyleDeclaration> {
1887 let mut is_null = false;
1891
1892 let pseudo = pseudo.map(|mut s| {
1898 s.make_ascii_lowercase();
1899 s
1900 });
1901 let pseudo = match pseudo {
1902 Some(ref pseudo) if pseudo == ":before" || pseudo == "::before" => {
1903 Some(PseudoElement::Before)
1904 },
1905 Some(ref pseudo) if pseudo == ":after" || pseudo == "::after" => {
1906 Some(PseudoElement::After)
1907 },
1908 Some(ref pseudo) if pseudo == "::selection" => Some(PseudoElement::Selection),
1909 Some(ref pseudo) if pseudo == "::marker" => Some(PseudoElement::Marker),
1910 Some(ref pseudo) if pseudo == "::placeholder" => Some(PseudoElement::Placeholder),
1911 Some(ref pseudo) if pseudo.starts_with(':') => {
1912 is_null = true;
1915 None
1916 },
1917 _ => None,
1918 };
1919
1920 CSSStyleDeclaration::new(
1936 self,
1937 if is_null {
1938 CSSStyleOwner::Null
1939 } else {
1940 CSSStyleOwner::Element(Dom::from_ref(element))
1941 },
1942 pseudo,
1943 CSSModificationAccess::Readonly,
1944 CanGc::deprecated_note(),
1945 )
1946 }
1947
1948 fn InnerHeight(&self) -> i32 {
1951 self.viewport_details
1952 .get()
1953 .size
1954 .height
1955 .to_i32()
1956 .unwrap_or(0)
1957 }
1958
1959 fn InnerWidth(&self) -> i32 {
1962 self.viewport_details.get().size.width.to_i32().unwrap_or(0)
1963 }
1964
1965 fn ScrollX(&self) -> i32 {
1967 self.scroll_offset().x as i32
1968 }
1969
1970 fn PageXOffset(&self) -> i32 {
1972 self.ScrollX()
1973 }
1974
1975 fn ScrollY(&self) -> i32 {
1977 self.scroll_offset().y as i32
1978 }
1979
1980 fn PageYOffset(&self) -> i32 {
1982 self.ScrollY()
1983 }
1984
1985 fn Scroll(&self, cx: &mut JSContext, options: &ScrollToOptions) {
1987 let x = options.left.unwrap_or(0.0) as f32;
1992
1993 let y = options.top.unwrap_or(0.0) as f32;
1996
1997 self.scroll(cx, x, y, options.parent.behavior);
1999 }
2000
2001 fn Scroll_(&self, cx: &mut JSContext, x: f64, y: f64) {
2003 self.scroll(cx, x as f32, y as f32, ScrollBehavior::Auto);
2007 }
2008
2009 fn ScrollTo(&self, cx: &mut JSContext, options: &ScrollToOptions) {
2014 self.Scroll(cx, options);
2015 }
2016
2017 fn ScrollTo_(&self, cx: &mut JSContext, x: f64, y: f64) {
2022 self.Scroll_(cx, x, y)
2023 }
2024
2025 fn ScrollBy(&self, cx: &mut JSContext, options: &ScrollToOptions) {
2027 let mut options = options.clone();
2033 let x = options.left.unwrap_or(0.0);
2034 let x = if x.is_finite() { x } else { 0.0 };
2035 let y = options.top.unwrap_or(0.0);
2036 let y = if y.is_finite() { y } else { 0.0 };
2037
2038 options.left.replace(x + self.ScrollX() as f64);
2040
2041 options.top.replace(y + self.ScrollY() as f64);
2043
2044 self.Scroll(cx, &options)
2046 }
2047
2048 fn ScrollBy_(&self, cx: &mut JSContext, x: f64, y: f64) {
2050 let mut options = ScrollToOptions::empty();
2054
2055 options.left.replace(x);
2058
2059 options.top.replace(y);
2061
2062 self.ScrollBy(cx, &options);
2064 }
2065
2066 fn ResizeTo(&self, width: i32, height: i32) {
2068 let window_proxy = match self.window_proxy.get() {
2070 Some(proxy) => proxy,
2071 None => return,
2072 };
2073
2074 if !window_proxy.is_auxiliary() {
2077 return;
2078 }
2079
2080 let dpr = self.device_pixel_ratio();
2081 let size = Size2D::new(width, height).to_f32() * dpr;
2082 self.send_to_embedder(EmbedderMsg::ResizeTo(self.webview_id(), size.to_i32()));
2083 }
2084
2085 fn ResizeBy(&self, x: i32, y: i32) {
2087 let size = self.client_window().size();
2088 self.ResizeTo(x + size.width, y + size.height)
2090 }
2091
2092 fn MoveTo(&self, x: i32, y: i32) {
2094 let dpr = self.device_pixel_ratio();
2097 let point = Point2D::new(x, y).to_f32() * dpr;
2098 let msg = EmbedderMsg::MoveTo(self.webview_id(), point.to_i32());
2099 self.send_to_embedder(msg);
2100 }
2101
2102 fn MoveBy(&self, x: i32, y: i32) {
2104 let origin = self.client_window().min;
2105 self.MoveTo(x + origin.x, y + origin.y)
2107 }
2108
2109 fn ScreenX(&self) -> i32 {
2111 self.client_window().min.x
2112 }
2113
2114 fn ScreenY(&self) -> i32 {
2116 self.client_window().min.y
2117 }
2118
2119 fn OuterHeight(&self) -> i32 {
2121 self.client_window().height()
2122 }
2123
2124 fn OuterWidth(&self) -> i32 {
2126 self.client_window().width()
2127 }
2128
2129 fn DevicePixelRatio(&self) -> Finite<f64> {
2131 Finite::wrap(self.device_pixel_ratio().get() as f64)
2132 }
2133
2134 fn Status(&self) -> DOMString {
2136 self.status.borrow().clone()
2137 }
2138
2139 fn SetStatus(&self, status: DOMString) {
2141 *self.status.borrow_mut() = status
2142 }
2143
2144 fn MatchMedia(&self, query: DOMString) -> DomRoot<MediaQueryList> {
2146 let media_query_list = MediaList::parse_media_list(&query.str(), self);
2147 let document = self.Document();
2148 let mql = MediaQueryList::new(&document, media_query_list, CanGc::deprecated_note());
2149 self.media_query_lists.track(&*mql);
2150 mql
2151 }
2152
2153 fn Fetch(
2155 &self,
2156 realm: &mut CurrentRealm,
2157 input: RequestOrUSVString,
2158 init: RootedTraceableBox<RequestInit>,
2159 ) -> Rc<Promise> {
2160 fetch::Fetch(self.upcast(), input, init, realm)
2161 }
2162
2163 fn FetchLater(
2165 &self,
2166 cx: &mut js::context::JSContext,
2167 input: RequestInfo,
2168 init: RootedTraceableBox<DeferredRequestInit>,
2169 ) -> Fallible<DomRoot<FetchLaterResult>> {
2170 fetch::FetchLater(cx, self, input, init)
2171 }
2172
2173 #[cfg(feature = "bluetooth")]
2174 fn TestRunner(&self) -> DomRoot<TestRunner> {
2175 self.test_runner
2176 .or_init(|| TestRunner::new(self.upcast(), CanGc::deprecated_note()))
2177 }
2178
2179 fn RunningAnimationCount(&self) -> u32 {
2180 self.document
2181 .get()
2182 .map_or(0, |d| d.animations().running_animation_count() as u32)
2183 }
2184
2185 fn SetName(&self, name: DOMString) {
2187 if let Some(proxy) = self.undiscarded_window_proxy() {
2188 proxy.set_name(name);
2189 }
2190 }
2191
2192 fn Name(&self) -> DOMString {
2194 match self.undiscarded_window_proxy() {
2195 Some(proxy) => proxy.get_name(),
2196 None => "".into(),
2197 }
2198 }
2199
2200 fn Origin(&self) -> USVString {
2202 USVString(self.origin().immutable().ascii_serialization())
2203 }
2204
2205 fn GetSelection(&self, cx: &mut JSContext) -> Option<DomRoot<Selection>> {
2207 self.document.get().and_then(|d| d.GetSelection(cx))
2208 }
2209
2210 fn Event(&self, cx: SafeJSContext, rval: MutableHandleValue) {
2212 if let Some(ref event) = *self.current_event.borrow() {
2213 event
2214 .reflector()
2215 .get_jsobject()
2216 .safe_to_jsval(cx, rval, CanGc::deprecated_note());
2217 }
2218 }
2219
2220 fn IsSecureContext(&self) -> bool {
2221 self.as_global_scope().is_secure_context()
2222 }
2223
2224 fn NamedGetter(
2226 &self,
2227 cx: &mut js::context::JSContext,
2228 name: DOMString,
2229 ) -> Option<NamedPropertyValue> {
2230 if name.is_empty() {
2231 return None;
2232 }
2233 let document = self.Document();
2234
2235 let iframes: Vec<_> = document
2237 .iframes()
2238 .iter()
2239 .filter(|iframe| {
2240 if let Some(window) = iframe.GetContentWindow() {
2241 return window.get_name() == name;
2242 }
2243 false
2244 })
2245 .collect();
2246
2247 let iframe_iter = iframes.iter().map(|iframe| iframe.upcast::<Element>());
2248
2249 let name = Atom::from(name);
2250
2251 let elements_with_name = document.get_elements_with_name(&name);
2253 let name_iter = elements_with_name
2254 .iter()
2255 .map(|element| &**element)
2256 .filter(|elem| is_named_element_with_name_attribute(elem));
2257 let elements_with_id = document.get_elements_with_id(&name);
2258 let id_iter = elements_with_id
2259 .iter()
2260 .map(|element| &**element)
2261 .filter(|elem| is_named_element_with_id_attribute(elem));
2262
2263 for elem in iframe_iter.clone() {
2265 if let Some(nested_window_proxy) = elem
2266 .downcast::<HTMLIFrameElement>()
2267 .and_then(|iframe| iframe.GetContentWindow())
2268 {
2269 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
2270 }
2271 }
2272
2273 let mut elements = iframe_iter.chain(name_iter).chain(id_iter);
2274
2275 let first = elements.next()?;
2276
2277 if elements.next().is_none() {
2278 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
2280 }
2281
2282 #[derive(JSTraceable, MallocSizeOf)]
2284 struct WindowNamedGetter {
2285 #[no_trace]
2286 name: Atom,
2287 }
2288 impl CollectionFilter for WindowNamedGetter {
2289 fn filter(&self, elem: &Element, _root: &Node) -> bool {
2290 let type_ = match elem.upcast::<Node>().type_id() {
2291 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
2292 _ => return false,
2293 };
2294 if elem.get_id().as_ref() == Some(&self.name) {
2295 return true;
2296 }
2297 match type_ {
2298 HTMLElementTypeId::HTMLEmbedElement |
2299 HTMLElementTypeId::HTMLFormElement |
2300 HTMLElementTypeId::HTMLImageElement |
2301 HTMLElementTypeId::HTMLObjectElement => {
2302 elem.get_name().as_ref() == Some(&self.name)
2303 },
2304 _ => false,
2305 }
2306 }
2307 }
2308 let collection = HTMLCollection::create(
2309 cx,
2310 self,
2311 document.upcast(),
2312 Box::new(WindowNamedGetter { name }),
2313 );
2314 Some(NamedPropertyValue::HTMLCollection(collection))
2315 }
2316
2317 fn SupportedPropertyNames(&self) -> Vec<DOMString> {
2319 let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
2320
2321 let document = self.Document();
2322 let name_map = document.name_map();
2323 for (name, elements) in &name_map.0 {
2324 if name.is_empty() {
2325 continue;
2326 }
2327 let mut name_iter = elements
2328 .iter()
2329 .filter(|elem| is_named_element_with_name_attribute(elem));
2330 if let Some(first) = name_iter.next() {
2331 names_with_first_named_element_map.insert(name, first);
2332 }
2333 }
2334 let id_map = document.id_map();
2335 for (id, elements) in &id_map.0 {
2336 if id.is_empty() {
2337 continue;
2338 }
2339 let mut id_iter = elements
2340 .iter()
2341 .filter(|elem| is_named_element_with_id_attribute(elem));
2342 if let Some(first) = id_iter.next() {
2343 match names_with_first_named_element_map.entry(id) {
2344 Entry::Vacant(entry) => drop(entry.insert(first)),
2345 Entry::Occupied(mut entry) => {
2346 if first.upcast::<Node>().is_before(entry.get().upcast()) {
2347 *entry.get_mut() = first;
2348 }
2349 },
2350 }
2351 }
2352 }
2353
2354 let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
2355 names_with_first_named_element_map
2356 .iter()
2357 .map(|(k, v)| (*k, *v))
2358 .collect();
2359 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
2360 if a.1 == b.1 {
2361 a.0.cmp(b.0)
2364 } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
2365 cmp::Ordering::Less
2366 } else {
2367 cmp::Ordering::Greater
2368 }
2369 });
2370
2371 names_with_first_named_element_vec
2372 .iter()
2373 .map(|(k, _v)| DOMString::from(&***k))
2374 .collect()
2375 }
2376
2377 fn StructuredClone(
2379 &self,
2380 cx: &mut JSContext,
2381 value: HandleValue,
2382 options: RootedTraceableBox<StructuredSerializeOptions>,
2383 retval: MutableHandleValue,
2384 ) -> Fallible<()> {
2385 self.as_global_scope()
2386 .structured_clone(cx, value, options, retval)
2387 }
2388
2389 fn TrustedTypes(&self, cx: &mut JSContext) -> DomRoot<TrustedTypePolicyFactory> {
2390 self.trusted_types
2391 .or_init(|| TrustedTypePolicyFactory::new(cx, self.as_global_scope()))
2392 }
2393}
2394
2395impl Window {
2396 pub(crate) fn scroll_offset(&self) -> Vector2D<f32, LayoutPixel> {
2397 self.scroll_offset_query_with_external_scroll_id(self.pipeline_id().root_scroll_id())
2398 }
2399
2400 pub(crate) fn create_named_properties_object(
2403 cx: SafeJSContext,
2404 proto: HandleObject,
2405 object: MutableHandleObject,
2406 ) {
2407 window_named_properties::create(cx, proto, object)
2408 }
2409
2410 pub(crate) fn current_event(&self) -> Option<DomRoot<Event>> {
2411 self.current_event
2412 .borrow()
2413 .as_ref()
2414 .map(|e| DomRoot::from_ref(&**e))
2415 }
2416
2417 pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
2418 let current = self.current_event();
2419 *self.current_event.borrow_mut() = event.map(Dom::from_ref);
2420 current
2421 }
2422
2423 fn post_message_impl(
2425 &self,
2426 target_origin: &USVString,
2427 source_origin: ImmutableOrigin,
2428 source: &Window,
2429 cx: &mut JSContext,
2430 message: HandleValue,
2431 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
2432 ) -> ErrorResult {
2433 let data = structuredclone::write(cx, message, Some(transfer))?;
2435
2436 let target_origin = match target_origin.0[..].as_ref() {
2438 "*" => None,
2439 "/" => Some(source_origin.clone()),
2440 url => match ServoUrl::parse(url) {
2441 Ok(url) => Some(url.origin()),
2442 Err(_) => return Err(Error::Syntax(None)),
2443 },
2444 };
2445
2446 self.post_message(target_origin, source_origin, &source.window_proxy(), data);
2448 Ok(())
2449 }
2450
2451 pub(crate) fn paint_worklet(&self, cx: &mut JSContext) -> DomRoot<Worklet> {
2453 self.paint_worklet.or_init(|| self.new_paint_worklet(cx))
2454 }
2455
2456 pub(crate) fn has_document(&self) -> bool {
2457 self.document.get().is_some()
2458 }
2459
2460 pub(crate) fn clear_js_runtime(&self) {
2461 self.as_global_scope()
2462 .remove_web_messaging_and_dedicated_workers_infra();
2463
2464 if let Some(custom_elements) = self.custom_element_registry.get() {
2467 custom_elements.teardown();
2468 }
2469
2470 self.current_state.set(WindowState::Zombie);
2471 *self.js_runtime.borrow_mut() = None;
2472
2473 if let Some(proxy) = self.window_proxy.get() {
2476 let pipeline_id = self.pipeline_id();
2477 if let Some(currently_active) = proxy.currently_active() &&
2478 currently_active == pipeline_id
2479 {
2480 self.window_proxy.set(None);
2481 }
2482 }
2483
2484 if let Some(performance) = self.performance.get() {
2485 performance.clear_and_disable_performance_entry_buffer();
2486 }
2487 self.as_global_scope()
2488 .task_manager()
2489 .cancel_all_tasks_and_ignore_future_tasks();
2490 }
2491
2492 pub(crate) fn scroll(&self, cx: &mut JSContext, x: f32, y: f32, behavior: ScrollBehavior) {
2494 let xfinite = if x.is_finite() { x } else { 0.0 };
2496 let yfinite = if y.is_finite() { y } else { 0.0 };
2497
2498 let viewport = self.viewport_details.get().size;
2508
2509 let scrolling_area = self.scrolling_area_query(None).to_f32();
2528 let x = xfinite.clamp(0.0, 0.0f32.max(scrolling_area.width() - viewport.width));
2529 let y = yfinite.clamp(0.0, 0.0f32.max(scrolling_area.height() - viewport.height));
2530
2531 let scroll_offset = self.scroll_offset();
2534 if x == scroll_offset.x && y == scroll_offset.y {
2535 return;
2536 }
2537
2538 self.perform_a_scroll(
2543 cx,
2544 x,
2545 y,
2546 self.pipeline_id().root_scroll_id(),
2547 behavior,
2548 None,
2549 );
2550 }
2551
2552 pub(crate) fn perform_a_scroll(
2554 &self,
2555 cx: &mut JSContext,
2556 x: f32,
2557 y: f32,
2558 scroll_id: ExternalScrollId,
2559 _behavior: ScrollBehavior,
2560 element: Option<&Element>,
2561 ) {
2562 let (reflow_phases_run, _) = self.reflow(
2566 cx,
2567 ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)),
2568 );
2569 if reflow_phases_run.needs_frame() {
2570 self.paint_api()
2571 .generate_frame(vec![self.webview_id().into()]);
2572 }
2573
2574 if reflow_phases_run.contains(ReflowPhasesRun::UpdatedScrollNodeOffset) {
2579 match element {
2580 Some(element) if !scroll_id.is_root() => element.handle_scroll_event(),
2581 _ => self.Document().handle_viewport_scroll_event(),
2582 };
2583 }
2584 }
2585
2586 pub(crate) fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
2587 self.viewport_details.get().hidpi_scale_factor
2588 }
2589
2590 fn client_window(&self) -> DeviceIndependentIntRect {
2591 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel!");
2592
2593 self.send_to_embedder(EmbedderMsg::GetWindowRect(self.webview_id(), sender));
2594
2595 receiver.recv().unwrap_or_default()
2596 }
2597
2598 pub(crate) fn advance_animation_clock(&self, delta: TimeDuration) {
2601 self.Document()
2602 .advance_animation_timeline_for_testing(delta);
2603 ScriptThread::handle_tick_all_animations_for_testing(self.pipeline_id());
2604 }
2605
2606 pub(crate) fn reflow(
2614 &self,
2615 cx: &mut JSContext,
2616 reflow_goal: ReflowGoal,
2617 ) -> (ReflowPhasesRun, ReflowStatistics) {
2618 let document = self.Document();
2619
2620 if !document.is_fully_active() {
2622 return Default::default();
2623 }
2624
2625 self.Document().ensure_safe_to_run_script_or_layout();
2626
2627 let pipeline_id = self.pipeline_id();
2631 if reflow_goal == ReflowGoal::UpdateTheRendering &&
2632 self.layout_blocker.get().layout_blocked()
2633 {
2634 debug!("Suppressing pre-load-event reflow pipeline {pipeline_id}");
2635 return Default::default();
2636 }
2637
2638 debug!("script: performing reflow for goal {reflow_goal:?}");
2639 let marker = if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) {
2640 Some(TimelineMarker::start("Reflow".to_owned()))
2641 } else {
2642 None
2643 };
2644
2645 let restyle_reason = document.restyle_reason();
2646 document.clear_restyle_reasons();
2647 let restyle = if restyle_reason.needs_restyle() {
2648 debug!("Invalidating layout cache due to reflow condition {restyle_reason:?}",);
2649 self.layout_marker.borrow().set(false);
2651 *self.layout_marker.borrow_mut() = Rc::new(Cell::new(true));
2653
2654 let stylesheets_changed = document.flush_stylesheets_for_reflow();
2655 let pending_restyles = document.drain_pending_restyles();
2656 let dirty_root = document
2657 .take_dirty_root()
2658 .filter(|_| !stylesheets_changed)
2659 .or_else(|| document.GetDocumentElement())
2660 .map(|root| root.upcast::<Node>().to_trusted_node_address());
2661
2662 Some(ReflowRequestRestyle {
2663 reason: restyle_reason,
2664 dirty_root,
2665 stylesheets_changed,
2666 pending_restyles,
2667 })
2668 } else {
2669 None
2670 };
2671
2672 let document_context = self.web_font_context();
2673
2674 let reflow = ReflowRequest {
2676 document: document.upcast::<Node>().to_trusted_node_address(),
2677 epoch: document.current_rendering_epoch(),
2678 restyle,
2679 viewport_details: self.viewport_details.get(),
2680 origin: self.origin().immutable().clone(),
2681 reflow_goal,
2682 animation_timeline_value: document.current_animation_timeline_value(),
2683 animations: document.animations().sets.clone(),
2684 animating_images: document.image_animation_manager().animating_images(),
2685 highlighted_dom_node: document.highlighted_dom_node().map(|node| node.to_opaque()),
2686 document_context,
2687 };
2688
2689 let Some(reflow_result) = self.layout.borrow_mut().reflow(reflow) else {
2690 return Default::default();
2691 };
2692
2693 debug!("script: layout complete");
2694 if let Some(marker) = marker {
2695 self.emit_timeline_marker(marker.end());
2696 }
2697
2698 self.handle_pending_images_post_reflow(
2699 cx,
2700 reflow_result.pending_images,
2701 reflow_result.pending_rasterization_images,
2702 reflow_result.pending_svg_elements_for_serialization,
2703 );
2704
2705 if let Some(iframe_sizes) = reflow_result.iframe_sizes {
2706 document
2707 .iframes_mut()
2708 .handle_new_iframe_sizes_after_layout(self, iframe_sizes);
2709 }
2710
2711 document.update_animations_post_reflow();
2712
2713 (
2714 reflow_result.reflow_phases_run,
2715 reflow_result.reflow_statistics,
2716 )
2717 }
2718
2719 pub(crate) fn request_screenshot_readiness(&self, cx: &mut js::context::JSContext) {
2720 self.has_pending_screenshot_readiness_request.set(true);
2721 self.maybe_resolve_pending_screenshot_readiness_requests(cx);
2722 }
2723
2724 pub(crate) fn maybe_resolve_pending_screenshot_readiness_requests(
2725 &self,
2726 cx: &mut js::context::JSContext,
2727 ) {
2728 let pending_request = self.has_pending_screenshot_readiness_request.get();
2729 if !pending_request {
2730 return;
2731 }
2732
2733 let document = self.Document();
2734 if document.ReadyState() != DocumentReadyState::Complete {
2735 return;
2736 }
2737
2738 if document.render_blocking_element_count() > 0 {
2739 return;
2740 }
2741
2742 if document.GetDocumentElement().is_some_and(|elem| {
2746 elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
2747 elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
2748 }) {
2749 return;
2750 }
2751
2752 if self.font_context().web_fonts_still_loading() != 0 {
2753 return;
2754 }
2755
2756 if self.Document().Fonts(cx).waiting_to_fullfill_promise() {
2757 return;
2758 }
2759
2760 if !self.pending_layout_images.borrow().is_empty() ||
2761 !self.pending_images_for_rasterization.borrow().is_empty()
2762 {
2763 return;
2764 }
2765
2766 let document = self.Document();
2767 if document.needs_rendering_update() {
2768 return;
2769 }
2770
2771 let epoch = document.current_rendering_epoch();
2774 let pipeline_id = self.pipeline_id();
2775 debug!("Ready to take screenshot of {pipeline_id:?} at epoch={epoch:?}");
2776
2777 self.send_to_constellation(
2778 ScriptToConstellationMessage::RespondToScreenshotReadinessRequest(
2779 ScreenshotReadinessResponse::Ready(epoch),
2780 ),
2781 );
2782 self.has_pending_screenshot_readiness_request.set(false);
2783 }
2784
2785 pub(crate) fn reflow_if_reflow_timer_expired(&self, cx: &mut JSContext) {
2788 if !matches!(
2791 self.layout_blocker.get(),
2792 LayoutBlocker::Parsing(instant) if instant + INITIAL_REFLOW_DELAY < Instant::now()
2793 ) {
2794 return;
2795 }
2796 self.allow_layout_if_necessary(cx);
2797 }
2798
2799 pub(crate) fn prevent_layout_until_load_event(&self) {
2803 if !matches!(self.layout_blocker.get(), LayoutBlocker::WaitingForParse) {
2806 return;
2807 }
2808
2809 self.layout_blocker
2810 .set(LayoutBlocker::Parsing(Instant::now()));
2811 }
2812
2813 pub(crate) fn allow_layout_if_necessary(&self, cx: &mut JSContext) {
2816 if matches!(
2817 self.layout_blocker.get(),
2818 LayoutBlocker::FiredLoadEventOrParsingTimerExpired
2819 ) {
2820 return;
2821 }
2822
2823 self.layout_blocker
2824 .set(LayoutBlocker::FiredLoadEventOrParsingTimerExpired);
2825
2826 let document = self.Document();
2838 if !document.is_render_blocked() && document.update_the_rendering(cx).0.needs_frame() {
2839 self.paint_api()
2840 .generate_frame(vec![self.webview_id().into()]);
2841 }
2842 }
2843
2844 pub(crate) fn layout_blocked(&self) -> bool {
2845 self.layout_blocker.get().layout_blocked()
2846 }
2847
2848 #[expect(unsafe_code)]
2850 pub(crate) fn layout_reflow(&self, query_msg: QueryMsg) {
2851 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
2853
2854 self.reflow(&mut cx, ReflowGoal::LayoutQuery(query_msg));
2855 }
2856
2857 pub(crate) fn resolved_font_style_query(
2858 &self,
2859 node: &Node,
2860 value: String,
2861 ) -> Option<ServoArc<Font>> {
2862 self.layout_reflow(QueryMsg::ResolvedFontStyleQuery);
2863
2864 let document = self.Document();
2865 let animations = document.animations().sets.clone();
2866 self.layout.borrow().query_resolved_font_style(
2867 node.to_trusted_node_address(),
2868 &value,
2869 animations,
2870 document.current_animation_timeline_value(),
2871 )
2872 }
2873
2874 #[expect(unsafe_code)]
2877 pub(crate) fn containing_block_node_query_without_reflow(
2878 &self,
2879 node: &Node,
2880 ) -> Option<DomRoot<Node>> {
2881 self.layout
2882 .borrow()
2883 .query_containing_block(node.to_trusted_node_address())
2884 .map(|address| unsafe { from_untrusted_node_address(address) })
2885 }
2886
2887 pub(crate) fn padding_query_without_reflow(&self, node: &Node) -> Option<PhysicalSides> {
2892 let layout = self.layout.borrow();
2893 layout.query_padding(node.to_trusted_node_address())
2894 }
2895
2896 pub(crate) fn box_area_query_without_reflow(
2901 &self,
2902 node: &Node,
2903 area: BoxAreaType,
2904 exclude_transform_and_inline: bool,
2905 ) -> Option<Rect<Au, CSSPixel>> {
2906 let layout = self.layout.borrow();
2907 layout.ensure_stacking_context_tree(self.viewport_details.get());
2908 layout.query_box_area(
2909 node.to_trusted_node_address(),
2910 area,
2911 exclude_transform_and_inline,
2912 )
2913 }
2914
2915 pub(crate) fn box_area_query(
2916 &self,
2917 node: &Node,
2918 area: BoxAreaType,
2919 exclude_transform_and_inline: bool,
2920 ) -> Option<Rect<Au, CSSPixel>> {
2921 self.layout_reflow(QueryMsg::BoxArea);
2922 self.box_area_query_without_reflow(node, area, exclude_transform_and_inline)
2923 }
2924
2925 pub(crate) fn box_areas_query(&self, node: &Node, area: BoxAreaType) -> CSSPixelRectVec {
2926 self.layout_reflow(QueryMsg::BoxAreas);
2927 self.layout
2928 .borrow()
2929 .query_box_areas(node.to_trusted_node_address(), area)
2930 }
2931
2932 pub(crate) fn client_rect_query(&self, node: &Node) -> Rect<i32, CSSPixel> {
2933 self.layout_reflow(QueryMsg::ClientRectQuery);
2934 self.layout
2935 .borrow()
2936 .query_client_rect(node.to_trusted_node_address())
2937 }
2938
2939 pub(crate) fn current_css_zoom_query(&self, node: &Node) -> f32 {
2940 self.layout_reflow(QueryMsg::CurrentCSSZoomQuery);
2941 self.layout
2942 .borrow()
2943 .query_current_css_zoom(node.to_trusted_node_address())
2944 }
2945
2946 pub(crate) fn scrolling_area_query(&self, node: Option<&Node>) -> Rect<i32, CSSPixel> {
2949 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2950 self.layout
2951 .borrow()
2952 .query_scrolling_area(node.map(Node::to_trusted_node_address))
2953 }
2954
2955 pub(crate) fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32, LayoutPixel> {
2956 let external_scroll_id = ExternalScrollId(
2957 combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody),
2958 self.pipeline_id().into(),
2959 );
2960 self.scroll_offset_query_with_external_scroll_id(external_scroll_id)
2961 }
2962
2963 fn scroll_offset_query_with_external_scroll_id(
2964 &self,
2965 external_scroll_id: ExternalScrollId,
2966 ) -> Vector2D<f32, LayoutPixel> {
2967 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2968 self.scroll_offset_query_with_external_scroll_id_no_reflow(external_scroll_id)
2969 }
2970
2971 fn scroll_offset_query_with_external_scroll_id_no_reflow(
2972 &self,
2973 external_scroll_id: ExternalScrollId,
2974 ) -> Vector2D<f32, LayoutPixel> {
2975 self.layout
2976 .borrow()
2977 .scroll_offset(external_scroll_id)
2978 .unwrap_or_default()
2979 }
2980
2981 pub(crate) fn scroll_an_element(
2984 &self,
2985 cx: &mut JSContext,
2986 element: &Element,
2987 x: f32,
2988 y: f32,
2989 behavior: ScrollBehavior,
2990 ) {
2991 let scroll_id = ExternalScrollId(
2992 combine_id_with_fragment_type(
2993 element.upcast::<Node>().to_opaque().id(),
2994 FragmentType::FragmentBody,
2995 ),
2996 self.pipeline_id().into(),
2997 );
2998
2999 self.perform_a_scroll(cx, x, y, scroll_id, behavior, Some(element));
3003 }
3004
3005 pub(crate) fn resolved_style_query(
3006 &self,
3007 element: TrustedNodeAddress,
3008 pseudo: Option<PseudoElement>,
3009 property: PropertyId,
3010 ) -> DOMString {
3011 self.layout_reflow(QueryMsg::ResolvedStyleQuery);
3012
3013 let document = self.Document();
3014 let animations = document.animations().sets.clone();
3015 DOMString::from(self.layout.borrow().query_resolved_style(
3016 element,
3017 pseudo,
3018 property,
3019 animations,
3020 document.current_animation_timeline_value(),
3021 ))
3022 }
3023
3024 pub(crate) fn get_iframe_viewport_details_if_known(
3028 &self,
3029 browsing_context_id: BrowsingContextId,
3030 ) -> Option<ViewportDetails> {
3031 self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery);
3033 self.Document()
3034 .iframes()
3035 .get(browsing_context_id)
3036 .and_then(|iframe| iframe.size)
3037 }
3038
3039 #[expect(unsafe_code)]
3040 pub(crate) fn offset_parent_query(
3041 &self,
3042 node: &Node,
3043 ) -> (Option<DomRoot<Element>>, Rect<Au, CSSPixel>) {
3044 self.layout_reflow(QueryMsg::OffsetParentQuery);
3045 let response = self
3046 .layout
3047 .borrow()
3048 .query_offset_parent(node.to_trusted_node_address());
3049 let element = response.node_address.and_then(|parent_node_address| {
3050 let node = unsafe { from_untrusted_node_address(parent_node_address) };
3051 DomRoot::downcast(node)
3052 });
3053 (element, response.rect)
3054 }
3055
3056 pub(crate) fn scroll_container_query(
3057 &self,
3058 node: Option<&Node>,
3059 flags: ScrollContainerQueryFlags,
3060 ) -> Option<ScrollContainerResponse> {
3061 self.layout_reflow(QueryMsg::ScrollParentQuery);
3062 self.layout
3063 .borrow()
3064 .query_scroll_container(node.map(Node::to_trusted_node_address), flags)
3065 }
3066
3067 #[expect(unsafe_code)]
3068 pub(crate) fn scrolling_box_query(
3069 &self,
3070 node: Option<&Node>,
3071 flags: ScrollContainerQueryFlags,
3072 ) -> Option<ScrollingBox> {
3073 self.scroll_container_query(node, flags)
3074 .and_then(|response| {
3075 Some(match response {
3076 ScrollContainerResponse::Viewport(overflow) => {
3077 (ScrollingBoxSource::Viewport(self.Document()), overflow)
3078 },
3079 ScrollContainerResponse::Element(parent_node_address, overflow) => {
3080 let node = unsafe { from_untrusted_node_address(parent_node_address) };
3081 (
3082 ScrollingBoxSource::Element(DomRoot::downcast(node)?),
3083 overflow,
3084 )
3085 },
3086 })
3087 })
3088 .map(|(source, overflow)| ScrollingBox::new(source, overflow))
3089 }
3090
3091 pub(crate) fn text_index_query_on_node_for_event(
3092 &self,
3093 node: &Node,
3094 mouse_event: &MouseEvent,
3095 ) -> Option<usize> {
3096 let point_in_viewport = mouse_event.point_in_viewport()?.map(Au::from_f32_px);
3100
3101 self.layout_reflow(QueryMsg::TextIndexQuery);
3102 self.layout
3103 .borrow()
3104 .query_text_index(node.to_trusted_node_address(), point_in_viewport)
3105 }
3106
3107 pub(crate) fn elements_from_point_query(
3108 &self,
3109 point: LayoutPoint,
3110 flags: ElementsFromPointFlags,
3111 ) -> Vec<ElementsFromPointResult> {
3112 self.layout_reflow(QueryMsg::ElementsFromPoint);
3113 self.layout().query_elements_from_point(point, flags)
3114 }
3115
3116 pub(crate) fn query_effective_overflow(&self, node: &Node) -> Option<AxesOverflow> {
3117 self.layout_reflow(QueryMsg::EffectiveOverflow);
3118 self.query_effective_overflow_without_reflow(node)
3119 }
3120
3121 pub(crate) fn query_effective_overflow_without_reflow(
3122 &self,
3123 node: &Node,
3124 ) -> Option<AxesOverflow> {
3125 self.layout
3126 .borrow()
3127 .query_effective_overflow(node.to_trusted_node_address())
3128 }
3129
3130 pub(crate) fn hit_test_from_input_event(
3131 &self,
3132 input_event: &ConstellationInputEvent,
3133 ) -> Option<HitTestResult> {
3134 self.hit_test_from_point_in_viewport(
3135 input_event.hit_test_result.as_ref()?.point_in_viewport,
3136 )
3137 }
3138
3139 #[expect(unsafe_code)]
3140 pub(crate) fn hit_test_from_point_in_viewport(
3141 &self,
3142 point_in_frame: Point2D<f32, CSSPixel>,
3143 ) -> Option<HitTestResult> {
3144 let result = self
3145 .elements_from_point_query(point_in_frame.cast_unit(), ElementsFromPointFlags::empty())
3146 .into_iter()
3147 .nth(0)?;
3148
3149 let point_relative_to_initial_containing_block =
3150 point_in_frame + self.scroll_offset().cast_unit();
3151
3152 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
3155 Some(HitTestResult {
3156 node: unsafe { from_untrusted_node_address(address) },
3157 cursor: result.cursor,
3158 point_in_node: result.point_in_target,
3159 point_in_frame,
3160 point_relative_to_initial_containing_block,
3161 })
3162 }
3163
3164 pub(crate) fn init_window_proxy(&self, window_proxy: &WindowProxy) {
3165 assert!(self.window_proxy.get().is_none());
3166 self.window_proxy.set(Some(window_proxy));
3167 }
3168
3169 pub(crate) fn init_document(&self, document: &Document) {
3170 assert!(self.document.get().is_none());
3171 assert!(document.window() == self);
3172 self.document.set(Some(document));
3173 }
3174
3175 pub(crate) fn load_data_for_document(
3176 &self,
3177 url: ServoUrl,
3178 pipeline_id: PipelineId,
3179 ) -> LoadData {
3180 let source_document = self.Document();
3181 let secure_context = if self.is_top_level() {
3182 None
3183 } else {
3184 Some(self.IsSecureContext())
3185 };
3186 LoadData::new(
3187 LoadOrigin::Script(self.origin().snapshot()),
3188 url,
3189 source_document.about_base_url(),
3190 Some(pipeline_id),
3191 Referrer::ReferrerUrl(source_document.url()),
3192 source_document.get_referrer_policy(),
3193 secure_context,
3194 Some(source_document.insecure_requests_policy()),
3195 source_document.has_trustworthy_ancestor_origin(),
3196 source_document.creation_sandboxing_flag_set_considering_parent_iframe(),
3197 )
3198 }
3199
3200 pub(crate) fn set_viewport_details(&self, viewport_details: ViewportDetails) {
3203 self.viewport_details.set(viewport_details);
3204 if !self.layout_mut().set_viewport_details(viewport_details) {
3205 return;
3206 }
3207 self.Document()
3208 .add_restyle_reason(RestyleReason::ViewportChanged);
3209 }
3210
3211 pub(crate) fn viewport_details(&self) -> ViewportDetails {
3212 self.viewport_details.get()
3213 }
3214
3215 pub(crate) fn get_or_init_visual_viewport(&self, can_gc: CanGc) -> DomRoot<VisualViewport> {
3216 self.visual_viewport.or_init(|| {
3217 VisualViewport::new_from_layout_viewport(self, self.viewport_details().size, can_gc)
3218 })
3219 }
3220
3221 pub(crate) fn maybe_update_visual_viewport(
3223 &self,
3224 pinch_zoom_infos: PinchZoomInfos,
3225 can_gc: CanGc,
3226 ) {
3227 if pinch_zoom_infos.rect == Rect::from_size(self.viewport_details().size) &&
3230 self.visual_viewport.get().is_none()
3231 {
3232 return;
3233 }
3234
3235 let visual_viewport = self.get_or_init_visual_viewport(can_gc);
3236 let changes = visual_viewport.update_from_pinch_zoom_infos(pinch_zoom_infos);
3237
3238 if changes.intersects(VisualViewportChanges::DimensionChanged) {
3239 self.has_changed_visual_viewport_dimension.set(true);
3240 }
3241 if changes.intersects(VisualViewportChanges::OffsetChanged) {
3242 visual_viewport.handle_scroll_event();
3243 }
3244 }
3245
3246 pub(crate) fn theme(&self) -> Theme {
3248 self.theme.get()
3249 }
3250
3251 pub(crate) fn set_theme(&self, new_theme: Theme) {
3253 self.theme.set(new_theme);
3254 if !self.layout_mut().set_theme(new_theme) {
3255 return;
3256 }
3257 self.Document()
3258 .add_restyle_reason(RestyleReason::ThemeChanged);
3259 }
3260
3261 pub(crate) fn get_url(&self) -> ServoUrl {
3262 self.Document().url()
3263 }
3264
3265 pub(crate) fn windowproxy_handler(&self) -> &'static WindowProxyHandler {
3266 self.dom_static.windowproxy_handler
3267 }
3268
3269 pub(crate) fn add_resize_event(&self, event: ViewportDetails, event_type: WindowSizeType) {
3270 *self.unhandled_resize_event.borrow_mut() = Some((event, event_type))
3273 }
3274
3275 pub(crate) fn take_unhandled_resize_event(&self) -> Option<(ViewportDetails, WindowSizeType)> {
3276 self.unhandled_resize_event.borrow_mut().take()
3277 }
3278
3279 pub(crate) fn has_unhandled_resize_event(&self) -> bool {
3281 self.unhandled_resize_event.borrow().is_some()
3282 }
3283
3284 pub(crate) fn suspend(&self, cx: &mut js::context::JSContext) {
3285 self.as_global_scope().suspend();
3287
3288 if self.window_proxy().currently_active() == Some(self.global().pipeline_id()) {
3290 self.window_proxy().unset_currently_active(cx);
3291 }
3292
3293 self.gc();
3298 }
3299
3300 pub(crate) fn resume(&self, can_gc: CanGc) {
3301 self.as_global_scope().resume();
3303
3304 self.window_proxy().set_currently_active(self, can_gc);
3306
3307 self.Document().title_changed();
3310 }
3311
3312 pub(crate) fn need_emit_timeline_marker(&self, timeline_type: TimelineMarkerType) -> bool {
3313 let markers = self.devtools_markers.borrow();
3314 markers.contains(&timeline_type)
3315 }
3316
3317 pub(crate) fn emit_timeline_marker(&self, marker: TimelineMarker) {
3318 let sender = self.devtools_marker_sender.borrow();
3319 let sender = sender.as_ref().expect("There is no marker sender");
3320 sender.send(Some(marker)).unwrap();
3321 }
3322
3323 pub(crate) fn set_devtools_timeline_markers(
3324 &self,
3325 markers: Vec<TimelineMarkerType>,
3326 reply: GenericSender<Option<TimelineMarker>>,
3327 ) {
3328 *self.devtools_marker_sender.borrow_mut() = Some(reply);
3329 self.devtools_markers.borrow_mut().extend(markers);
3330 }
3331
3332 pub(crate) fn drop_devtools_timeline_markers(&self, markers: Vec<TimelineMarkerType>) {
3333 let mut devtools_markers = self.devtools_markers.borrow_mut();
3334 for marker in markers {
3335 devtools_markers.remove(&marker);
3336 }
3337 if devtools_markers.is_empty() {
3338 *self.devtools_marker_sender.borrow_mut() = None;
3339 }
3340 }
3341
3342 pub(crate) fn set_webdriver_script_chan(&self, chan: Option<GenericSender<WebDriverJSResult>>) {
3343 *self.webdriver_script_chan.borrow_mut() = chan;
3344 }
3345
3346 pub(crate) fn set_webdriver_load_status_sender(
3347 &self,
3348 sender: Option<GenericSender<WebDriverLoadStatus>>,
3349 ) {
3350 *self.webdriver_load_status_sender.borrow_mut() = sender;
3351 }
3352
3353 pub(crate) fn webdriver_load_status_sender(
3354 &self,
3355 ) -> Option<GenericSender<WebDriverLoadStatus>> {
3356 self.webdriver_load_status_sender.borrow().clone()
3357 }
3358
3359 pub(crate) fn is_alive(&self) -> bool {
3360 self.current_state.get() == WindowState::Alive
3361 }
3362
3363 pub(crate) fn is_top_level(&self) -> bool {
3365 self.parent_info.is_none()
3366 }
3367
3368 fn run_resize_steps_for_layout_viewport(&self, cx: &mut js::context::JSContext) -> bool {
3373 let Some((new_size, size_type)) = self.take_unhandled_resize_event() else {
3374 return false;
3375 };
3376
3377 if self.viewport_details() == new_size {
3378 return false;
3379 }
3380
3381 let _realm = enter_realm(self);
3382 debug!(
3383 "Resizing Window for pipeline {:?} from {:?} to {new_size:?}",
3384 self.pipeline_id(),
3385 self.viewport_details(),
3386 );
3387 self.set_viewport_details(new_size);
3388
3389 self.Document()
3393 .add_restyle_reason(RestyleReason::ViewportChanged);
3394
3395 if self.layout().device().used_viewport_size() {
3398 self.Document().dirty_all_nodes();
3399 }
3400
3401 if size_type == WindowSizeType::Resize {
3403 let uievent = UIEvent::new(
3404 self,
3405 atom!("resize"),
3406 EventBubbles::DoesNotBubble,
3407 EventCancelable::NotCancelable,
3408 Some(self),
3409 0i32,
3410 0u32,
3411 CanGc::from_cx(cx),
3412 );
3413 uievent
3414 .upcast::<Event>()
3415 .fire(self.upcast(), CanGc::from_cx(cx));
3416 }
3417
3418 true
3419 }
3420
3421 pub(crate) fn run_the_resize_steps(&self, cx: &mut js::context::JSContext) -> bool {
3426 let layout_viewport_resized = self.run_resize_steps_for_layout_viewport(cx);
3427
3428 if self.has_changed_visual_viewport_dimension.get() {
3429 let visual_viewport = self.get_or_init_visual_viewport(CanGc::from_cx(cx));
3430
3431 let uievent = UIEvent::new(
3432 self,
3433 atom!("resize"),
3434 EventBubbles::DoesNotBubble,
3435 EventCancelable::NotCancelable,
3436 Some(self),
3437 0i32,
3438 0u32,
3439 CanGc::from_cx(cx),
3440 );
3441 uievent
3442 .upcast::<Event>()
3443 .fire(visual_viewport.upcast(), CanGc::from_cx(cx));
3444
3445 self.has_changed_visual_viewport_dimension.set(false);
3446 }
3447
3448 layout_viewport_resized
3449 }
3450
3451 pub(crate) fn evaluate_media_queries_and_report_changes(
3454 &self,
3455 cx: &mut js::context::JSContext,
3456 ) {
3457 let mut realm = enter_auto_realm(cx, self);
3458 let cx = &mut realm.current_realm();
3459 rooted_vec!(let mut mql_list);
3460
3461 self.media_query_lists.for_each(|mql| {
3462 if let MediaQueryListMatchState::Changed = mql.evaluate_changes() {
3463 mql_list.push(Dom::from_ref(&*mql));
3465 }
3466 });
3467 for mql in mql_list.iter() {
3469 let event = MediaQueryListEvent::new(
3470 &mql.global(),
3471 atom!("change"),
3472 false,
3473 false,
3474 mql.Media(),
3475 mql.Matches(),
3476 CanGc::from_cx(cx),
3477 );
3478 event
3479 .upcast::<Event>()
3480 .fire(mql.upcast::<EventTarget>(), CanGc::from_cx(cx));
3481 }
3482 }
3483
3484 pub(crate) fn set_throttled(&self, throttled: bool) {
3486 self.throttled.set(throttled);
3487 if throttled {
3488 self.as_global_scope().slow_down_timers();
3489 } else {
3490 self.as_global_scope().speed_up_timers();
3491 }
3492 }
3493
3494 pub(crate) fn throttled(&self) -> bool {
3495 self.throttled.get()
3496 }
3497
3498 pub(crate) fn unminified_css_dir(&self) -> Option<String> {
3499 self.unminified_css_dir.borrow().clone()
3500 }
3501
3502 pub(crate) fn local_script_source(&self) -> &Option<String> {
3503 &self.local_script_source
3504 }
3505
3506 pub(crate) fn set_navigation_start(&self) {
3507 self.navigation_start.set(CrossProcessInstant::now());
3508 }
3509
3510 pub(crate) fn navigation_start(&self) -> CrossProcessInstant {
3511 self.navigation_start.get()
3512 }
3513
3514 pub(crate) fn set_last_activation_timestamp(&self, time: UserActivationTimestamp) {
3515 self.last_activation_timestamp.set(time);
3516 }
3517
3518 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
3519 self.as_global_scope()
3520 .script_to_embedder_chan()
3521 .send(msg)
3522 .unwrap();
3523 }
3524
3525 pub(crate) fn send_to_constellation(&self, msg: ScriptToConstellationMessage) {
3526 self.as_global_scope()
3527 .script_to_constellation_chan()
3528 .send(msg)
3529 .unwrap();
3530 }
3531
3532 #[cfg(feature = "webxr")]
3533 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3534 self.navigator
3535 .get()
3536 .as_ref()
3537 .and_then(|nav| nav.xr())
3538 .is_some_and(|xr| xr.pending_or_active_session())
3539 }
3540
3541 #[cfg(not(feature = "webxr"))]
3542 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3543 false
3544 }
3545
3546 #[expect(unsafe_code)]
3547 fn handle_pending_images_post_reflow(
3548 &self,
3549 cx: &mut JSContext,
3550 pending_images: Vec<PendingImage>,
3551 pending_rasterization_images: Vec<PendingRasterizationImage>,
3552 pending_svg_element_for_serialization: Vec<UntrustedNodeAddress>,
3553 ) {
3554 let pipeline_id = self.pipeline_id();
3555 for image in pending_images {
3556 let id = image.id;
3557 let node = unsafe { from_untrusted_node_address(image.node) };
3558
3559 if let PendingImageState::Unrequested(ref url) = image.state {
3560 fetch_image_for_layout(
3561 url.clone(),
3562 &node,
3563 id,
3564 image.is_internal_request,
3565 self.image_cache.clone(),
3566 );
3567 }
3568
3569 let mut images = self.pending_layout_images.borrow_mut();
3570 if !images.contains_key(&id) {
3571 let trusted_node = Trusted::new(&*node);
3572 let sender = self.register_image_cache_listener(id, move |response, _| {
3573 trusted_node
3574 .root()
3575 .owner_window()
3576 .pending_layout_image_notification(response);
3577 });
3578
3579 self.image_cache
3580 .add_listener(ImageLoadListener::new(sender, pipeline_id, id));
3581 }
3582
3583 let nodes = images.entry(id).or_default();
3584 if !nodes.iter().any(|n| std::ptr::eq(&*(n.node), &*node)) {
3585 nodes.push(PendingLayoutImageAncillaryData {
3586 node: Dom::from_ref(&*node),
3587 destination: image.destination,
3588 });
3589 }
3590 }
3591
3592 for image in pending_rasterization_images {
3593 let node = unsafe { from_untrusted_node_address(image.node) };
3594
3595 let mut images = self.pending_images_for_rasterization.borrow_mut();
3596 if !images.contains_key(&(image.id, image.size)) {
3597 let image_cache_sender = self.image_cache_sender.clone();
3598 self.image_cache.add_rasterization_complete_listener(
3599 pipeline_id,
3600 image.id,
3601 image.size,
3602 Box::new(move |response| {
3603 let _ = image_cache_sender.send(response);
3604 }),
3605 );
3606 }
3607
3608 let nodes = images.entry((image.id, image.size)).or_default();
3609 if !nodes.iter().any(|n| std::ptr::eq(&**n, &*node)) {
3610 nodes.push(Dom::from_ref(&*node));
3611 }
3612 }
3613
3614 for node in pending_svg_element_for_serialization.into_iter() {
3615 let node = unsafe { from_untrusted_node_address(node) };
3616 let svg = node.downcast::<SVGSVGElement>().unwrap();
3617 svg.serialize_and_cache_subtree(cx);
3618 node.dirty(NodeDamage::Other);
3619 }
3620 }
3621
3622 pub(crate) fn has_sticky_activation(&self) -> bool {
3624 UserActivationTimestamp::TimeStamp(CrossProcessInstant::now()) >=
3626 self.last_activation_timestamp.get()
3627 }
3628
3629 pub(crate) fn has_transient_activation(&self) -> bool {
3631 let current_time = CrossProcessInstant::now();
3634 UserActivationTimestamp::TimeStamp(current_time) >= self.last_activation_timestamp.get() &&
3635 UserActivationTimestamp::TimeStamp(current_time) <
3636 self.last_activation_timestamp.get() +
3637 pref!(dom_transient_activation_duration_ms)
3638 }
3639
3640 pub(crate) fn consume_last_activation_timestamp(&self) {
3641 if self.last_activation_timestamp.get() != UserActivationTimestamp::PositiveInfinity {
3642 self.set_last_activation_timestamp(UserActivationTimestamp::NegativeInfinity);
3643 }
3644 }
3645
3646 pub(crate) fn consume_user_activation(&self) {
3648 if self.undiscarded_window_proxy().is_none() {
3651 return;
3652 }
3653
3654 let Some(top_level_document) = self.top_level_document_if_local() else {
3658 return;
3659 };
3660
3661 top_level_document
3669 .window()
3670 .consume_last_activation_timestamp();
3671 for document in SameOriginDescendantNavigablesIterator::new(top_level_document) {
3672 document.window().consume_last_activation_timestamp();
3673 }
3674 }
3675
3676 #[allow(clippy::too_many_arguments)]
3677 pub(crate) fn new(
3678 cx: &mut js::context::JSContext,
3679 webview_id: WebViewId,
3680 runtime: Rc<Runtime>,
3681 script_chan: Sender<MainThreadScriptMsg>,
3682 layout: Box<dyn Layout>,
3683 font_context: Arc<FontContext>,
3684 image_cache_sender: Sender<ImageCacheResponseMessage>,
3685 image_cache: Arc<dyn ImageCache>,
3686 resource_threads: ResourceThreads,
3687 storage_threads: StorageThreads,
3688 #[cfg(feature = "bluetooth")] bluetooth_thread: GenericSender<BluetoothRequest>,
3689 mem_profiler_chan: MemProfilerChan,
3690 time_profiler_chan: TimeProfilerChan,
3691 devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
3692 constellation_chan: ScriptToConstellationChan,
3693 embedder_chan: ScriptToEmbedderChan,
3694 control_chan: GenericSender<ScriptThreadMessage>,
3695 pipeline_id: PipelineId,
3696 parent_info: Option<PipelineId>,
3697 viewport_details: ViewportDetails,
3698 origin: MutableOrigin,
3699 creation_url: ServoUrl,
3700 top_level_creation_url: ServoUrl,
3701 navigation_start: CrossProcessInstant,
3702 webgl_chan: Option<WebGLChan>,
3703 #[cfg(feature = "webxr")] webxr_registry: Option<webxr_api::Registry>,
3704 paint_api: CrossProcessPaintApi,
3705 unminify_js: bool,
3706 unminify_css: bool,
3707 local_script_source: Option<String>,
3708 user_scripts: Rc<Vec<UserScript>>,
3709 player_context: WindowGLContext,
3710 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
3711 inherited_secure_context: Option<bool>,
3712 theme: Theme,
3713 weak_script_thread: Weak<ScriptThread>,
3714 ) -> DomRoot<Self> {
3715 let error_reporter = CSSErrorReporter {
3716 pipelineid: pipeline_id,
3717 script_chan: control_chan,
3718 };
3719
3720 let win = Box::new(Self {
3721 webview_id,
3722 globalscope: GlobalScope::new_inherited(
3723 pipeline_id,
3724 devtools_chan,
3725 mem_profiler_chan,
3726 time_profiler_chan,
3727 constellation_chan,
3728 embedder_chan,
3729 resource_threads,
3730 storage_threads,
3731 origin,
3732 creation_url,
3733 Some(top_level_creation_url),
3734 #[cfg(feature = "webgpu")]
3735 gpu_id_hub,
3736 inherited_secure_context,
3737 unminify_js,
3738 Some(font_context),
3739 ),
3740 ongoing_navigation: Default::default(),
3741 script_chan,
3742 layout: RefCell::new(layout),
3743 image_cache_sender,
3744 image_cache,
3745 navigator: Default::default(),
3746 crypto: Default::default(),
3747 location: Default::default(),
3748 history: Default::default(),
3749 custom_element_registry: Default::default(),
3750 window_proxy: Default::default(),
3751 document: Default::default(),
3752 performance: Default::default(),
3753 navigation_start: Cell::new(navigation_start),
3754 screen: Default::default(),
3755 session_storage: Default::default(),
3756 local_storage: Default::default(),
3757 cookie_store: Default::default(),
3758 status: DomRefCell::new(DOMString::new()),
3759 parent_info,
3760 dom_static: GlobalStaticData::new(),
3761 js_runtime: DomRefCell::new(Some(runtime)),
3762 #[cfg(feature = "bluetooth")]
3763 bluetooth_thread,
3764 #[cfg(feature = "bluetooth")]
3765 bluetooth_extra_permission_data: BluetoothExtraPermissionData::new(),
3766 unhandled_resize_event: Default::default(),
3767 viewport_details: Cell::new(viewport_details),
3768 layout_blocker: Cell::new(LayoutBlocker::WaitingForParse),
3769 current_state: Cell::new(WindowState::Alive),
3770 devtools_marker_sender: Default::default(),
3771 devtools_markers: Default::default(),
3772 webdriver_script_chan: Default::default(),
3773 webdriver_load_status_sender: Default::default(),
3774 error_reporter,
3775 media_query_lists: DOMTracker::new(),
3776 #[cfg(feature = "bluetooth")]
3777 test_runner: Default::default(),
3778 webgl_chan,
3779 #[cfg(feature = "webxr")]
3780 webxr_registry,
3781 pending_image_callbacks: Default::default(),
3782 pending_layout_images: Default::default(),
3783 pending_images_for_rasterization: Default::default(),
3784 unminified_css_dir: DomRefCell::new(if unminify_css {
3785 Some(unminified_path("unminified-css"))
3786 } else {
3787 None
3788 }),
3789 local_script_source,
3790 test_worklet: Default::default(),
3791 paint_worklet: Default::default(),
3792 exists_mut_observer: Cell::new(false),
3793 paint_api,
3794 user_scripts,
3795 player_context,
3796 throttled: Cell::new(false),
3797 layout_marker: DomRefCell::new(Rc::new(Cell::new(true))),
3798 current_event: DomRefCell::new(None),
3799 theme: Cell::new(theme),
3800 trusted_types: Default::default(),
3801 reporting_observer_list: Default::default(),
3802 report_list: Default::default(),
3803 endpoints_list: Default::default(),
3804 script_window_proxies: ScriptThread::window_proxies(),
3805 has_pending_screenshot_readiness_request: Default::default(),
3806 visual_viewport: Default::default(),
3807 weak_script_thread,
3808 has_changed_visual_viewport_dimension: Default::default(),
3809 last_activation_timestamp: Cell::new(UserActivationTimestamp::PositiveInfinity),
3810 devtools_wants_updates: Default::default(),
3811 });
3812
3813 WindowBinding::Wrap::<crate::DomTypeHolder>(cx, win)
3814 }
3815
3816 pub(crate) fn pipeline_id(&self) -> PipelineId {
3817 self.as_global_scope().pipeline_id()
3818 }
3819
3820 pub(crate) fn live_devtools_updates(&self) -> bool {
3821 self.devtools_wants_updates.get()
3822 }
3823
3824 pub(crate) fn set_devtools_wants_updates(&self, value: bool) {
3825 self.devtools_wants_updates.set(value);
3826 }
3827
3828 pub(crate) fn cache_layout_value<T>(&self, value: T) -> LayoutValue<T>
3830 where
3831 T: Copy + MallocSizeOf,
3832 {
3833 LayoutValue::new(self.layout_marker.borrow().clone(), value)
3834 }
3835}
3836
3837#[derive(MallocSizeOf)]
3842pub(crate) struct LayoutValue<T: MallocSizeOf> {
3843 #[conditional_malloc_size_of]
3844 is_valid: Rc<Cell<bool>>,
3845 value: T,
3846}
3847
3848#[expect(unsafe_code)]
3849unsafe impl<T: JSTraceable + MallocSizeOf> JSTraceable for LayoutValue<T> {
3850 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
3851 unsafe { self.value.trace(trc) };
3852 }
3853}
3854
3855impl<T: Copy + MallocSizeOf> LayoutValue<T> {
3856 fn new(marker: Rc<Cell<bool>>, value: T) -> Self {
3857 LayoutValue {
3858 is_valid: marker,
3859 value,
3860 }
3861 }
3862
3863 pub(crate) fn get(&self) -> Result<T, ()> {
3865 if self.is_valid.get() {
3866 return Ok(self.value);
3867 }
3868 Err(())
3869 }
3870}
3871
3872fn should_move_clip_rect(clip_rect: UntypedRect<Au>, new_viewport: UntypedRect<f32>) -> bool {
3873 let clip_rect = UntypedRect::new(
3874 Point2D::new(
3875 clip_rect.origin.x.to_f32_px(),
3876 clip_rect.origin.y.to_f32_px(),
3877 ),
3878 Size2D::new(
3879 clip_rect.size.width.to_f32_px(),
3880 clip_rect.size.height.to_f32_px(),
3881 ),
3882 );
3883
3884 static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5;
3888 let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE;
3889
3890 (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width ||
3891 (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width ||
3892 (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height ||
3893 (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height
3894}
3895
3896impl Window {
3897 pub(crate) fn post_message(
3899 &self,
3900 target_origin: Option<ImmutableOrigin>,
3901 source_origin: ImmutableOrigin,
3902 source: &WindowProxy,
3903 data: StructuredSerializedData,
3904 ) {
3905 let this = Trusted::new(self);
3906 let source = Trusted::new(source);
3907 let task = task!(post_serialised_message: move |cx| {
3908 let this = this.root();
3909 let source = source.root();
3910 let document = this.Document();
3911
3912 if let Some(ref target_origin) = target_origin
3914 && !target_origin.same_origin(&*document.origin()) {
3915 return;
3916 }
3917
3918 let obj = this.reflector().get_jsobject();
3920 let mut realm = AutoRealm::new(cx, NonNull::new(obj.get()).unwrap());
3921 let cx = &mut *realm;
3922 rooted!(&in(cx) let mut message_clone = UndefinedValue());
3923 if let Ok(ports) = structuredclone::read(cx, this.upcast(), data, message_clone.handle_mut()) {
3924 MessageEvent::dispatch_jsval(
3926 this.upcast(),
3927 this.upcast(),
3928 message_clone.handle(),
3929 Some(&source_origin.ascii_serialization()),
3930 Some(&*source),
3931 ports,
3932 CanGc::from_cx(cx),
3933 );
3934 } else {
3935 MessageEvent::dispatch_error(
3937 cx,
3938 this.upcast(),
3939 this.upcast(),
3940 );
3941 }
3942 });
3943 self.as_global_scope()
3945 .task_manager()
3946 .dom_manipulation_task_source()
3947 .queue(task);
3948 }
3949}
3950
3951#[derive(Clone, MallocSizeOf)]
3952pub(crate) struct CSSErrorReporter {
3953 pub(crate) pipelineid: PipelineId,
3954 pub(crate) script_chan: GenericSender<ScriptThreadMessage>,
3955}
3956unsafe_no_jsmanaged_fields!(CSSErrorReporter);
3957
3958impl ParseErrorReporter for CSSErrorReporter {
3959 fn report_error(
3960 &self,
3961 url: &UrlExtraData,
3962 location: SourceLocation,
3963 error: ContextualParseError,
3964 ) {
3965 if log_enabled!(log::Level::Info) {
3966 info!(
3967 "Url:\t{}\n{}:{} {}",
3968 url.0.as_str(),
3969 location.line,
3970 location.column,
3971 error
3972 )
3973 }
3974
3975 let _ = self.script_chan.send(ScriptThreadMessage::ReportCSSError(
3977 self.pipelineid,
3978 url.0.to_string(),
3979 location.line,
3980 location.column,
3981 error.to_string(),
3982 ));
3983 }
3984}
3985
3986fn is_named_element_with_name_attribute(elem: &Element) -> bool {
3987 let type_ = match elem.upcast::<Node>().type_id() {
3988 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
3989 _ => return false,
3990 };
3991 matches!(
3992 type_,
3993 HTMLElementTypeId::HTMLEmbedElement |
3994 HTMLElementTypeId::HTMLFormElement |
3995 HTMLElementTypeId::HTMLImageElement |
3996 HTMLElementTypeId::HTMLObjectElement
3997 )
3998}
3999
4000fn is_named_element_with_id_attribute(elem: &Element) -> bool {
4001 elem.is_html_element()
4002}
4003
4004#[expect(unsafe_code)]
4005#[unsafe(no_mangle)]
4006unsafe extern "C" fn dump_js_stack(cx: *mut RawJSContext) {
4008 unsafe {
4009 DumpJSStack(cx, true, false, false);
4010 }
4011}
4012
4013impl WindowHelpers for Window {
4014 fn create_named_properties_object(
4015 cx: SafeJSContext,
4016 proto: HandleObject,
4017 object: MutableHandleObject,
4018 ) {
4019 Self::create_named_properties_object(cx, proto, object)
4020 }
4021}