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, ElementsFromPointResult, FragmentType, Layout,
47 LayoutImageDestination, PendingImage, PendingImageState, PendingRasterizationImage,
48 PhysicalSides, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest, ReflowRequestRestyle,
49 ReflowStatistics, RestyleReason, ScrollContainerQueryFlags, ScrollContainerResponse,
50 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: String::from(message),
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: String::from(message),
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: String::from(message),
1239 default: String::from(default),
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: &mut js::context::JSContext, error: HandleValue) {
1541 self.as_global_scope().report_an_exception(cx, error);
1542 }
1543
1544 fn Navigator(&self) -> DomRoot<Navigator> {
1546 self.navigator
1547 .or_init(|| Navigator::new(self, CanGc::deprecated_note()))
1548 }
1549
1550 fn ClientInformation(&self) -> DomRoot<Navigator> {
1552 self.Navigator()
1553 }
1554
1555 fn SetTimeout(
1557 &self,
1558 cx: &mut js::context::JSContext,
1559 callback: TrustedScriptOrStringOrFunction,
1560 timeout: i32,
1561 args: Vec<HandleValue>,
1562 ) -> Fallible<i32> {
1563 let callback = match callback {
1564 TrustedScriptOrStringOrFunction::String(i) => {
1565 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1566 },
1567 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1568 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1569 },
1570 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1571 };
1572 self.as_global_scope().set_timeout_or_interval(
1573 cx,
1574 callback,
1575 args,
1576 Duration::from_millis(timeout.max(0) as u64),
1577 IsInterval::NonInterval,
1578 )
1579 }
1580
1581 fn ClearTimeout(&self, handle: i32) {
1583 self.as_global_scope().clear_timeout_or_interval(handle);
1584 }
1585
1586 fn SetInterval(
1588 &self,
1589 cx: &mut js::context::JSContext,
1590 callback: TrustedScriptOrStringOrFunction,
1591 timeout: i32,
1592 args: Vec<HandleValue>,
1593 ) -> Fallible<i32> {
1594 let callback = match callback {
1595 TrustedScriptOrStringOrFunction::String(i) => {
1596 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1597 },
1598 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1599 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1600 },
1601 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1602 };
1603 self.as_global_scope().set_timeout_or_interval(
1604 cx,
1605 callback,
1606 args,
1607 Duration::from_millis(timeout.max(0) as u64),
1608 IsInterval::Interval,
1609 )
1610 }
1611
1612 fn ClearInterval(&self, handle: i32) {
1614 self.ClearTimeout(handle);
1615 }
1616
1617 fn QueueMicrotask(&self, callback: Rc<VoidFunction>) {
1619 ScriptThread::enqueue_microtask(Microtask::User(UserMicrotask {
1620 callback,
1621 pipeline: self.pipeline_id(),
1622 }));
1623 }
1624
1625 fn CreateImageBitmap(
1627 &self,
1628 realm: &mut CurrentRealm,
1629 image: ImageBitmapSource,
1630 options: &ImageBitmapOptions,
1631 ) -> Rc<Promise> {
1632 ImageBitmap::create_image_bitmap(
1633 self.as_global_scope(),
1634 image,
1635 0,
1636 0,
1637 None,
1638 None,
1639 options,
1640 realm,
1641 )
1642 }
1643
1644 fn CreateImageBitmap_(
1646 &self,
1647 realm: &mut CurrentRealm,
1648 image: ImageBitmapSource,
1649 sx: i32,
1650 sy: i32,
1651 sw: i32,
1652 sh: i32,
1653 options: &ImageBitmapOptions,
1654 ) -> Rc<Promise> {
1655 ImageBitmap::create_image_bitmap(
1656 self.as_global_scope(),
1657 image,
1658 sx,
1659 sy,
1660 Some(sw),
1661 Some(sh),
1662 options,
1663 realm,
1664 )
1665 }
1666
1667 fn Window(&self) -> DomRoot<WindowProxy> {
1669 self.window_proxy()
1670 }
1671
1672 fn Self_(&self) -> DomRoot<WindowProxy> {
1674 self.window_proxy()
1675 }
1676
1677 fn Frames(&self) -> DomRoot<WindowProxy> {
1679 self.window_proxy()
1680 }
1681
1682 fn Length(&self) -> u32 {
1684 self.Document().iframes().iter().count() as u32
1685 }
1686
1687 fn GetParent(&self) -> Option<DomRoot<WindowProxy>> {
1689 let window_proxy = self.undiscarded_window_proxy()?;
1691
1692 if let Some(parent) = window_proxy.parent() {
1694 return Some(DomRoot::from_ref(parent));
1695 }
1696 Some(window_proxy)
1698 }
1699
1700 fn GetTop(&self) -> Option<DomRoot<WindowProxy>> {
1702 let window_proxy = self.undiscarded_window_proxy()?;
1704
1705 Some(DomRoot::from_ref(window_proxy.top()))
1707 }
1708
1709 fn Performance(&self) -> DomRoot<Performance> {
1712 self.performance.or_init(|| {
1713 Performance::new(
1714 self.as_global_scope(),
1715 self.navigation_start.get(),
1716 CanGc::deprecated_note(),
1717 )
1718 })
1719 }
1720
1721 global_event_handlers!();
1723
1724 window_event_handlers!();
1726
1727 fn Screen(&self, can_gc: CanGc) -> DomRoot<Screen> {
1729 self.screen.or_init(|| Screen::new(self, can_gc))
1730 }
1731
1732 fn GetVisualViewport(&self, can_gc: CanGc) -> Option<DomRoot<VisualViewport>> {
1734 if !self.Document().is_fully_active() {
1738 return None;
1739 }
1740
1741 Some(self.get_or_init_visual_viewport(can_gc))
1742 }
1743
1744 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
1746 base64_btoa(btoa)
1747 }
1748
1749 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
1751 base64_atob(atob)
1752 }
1753
1754 fn RequestAnimationFrame(&self, callback: Rc<FrameRequestCallback>) -> u32 {
1756 self.Document()
1757 .request_animation_frame(AnimationFrameCallback::FrameRequestCallback { callback })
1758 }
1759
1760 fn CancelAnimationFrame(&self, ident: u32) {
1762 let doc = self.Document();
1763 doc.cancel_animation_frame(ident);
1764 }
1765
1766 fn PostMessage(
1768 &self,
1769 cx: &mut JSContext,
1770 message: HandleValue,
1771 target_origin: USVString,
1772 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1773 ) -> ErrorResult {
1774 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1775 let source = incumbent.as_window();
1776 let source_origin = source.Document().origin().immutable().clone();
1777
1778 self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
1779 }
1780
1781 fn PostMessage_(
1783 &self,
1784 cx: &mut JSContext,
1785 message: HandleValue,
1786 options: RootedTraceableBox<WindowPostMessageOptions>,
1787 ) -> ErrorResult {
1788 let mut rooted = CustomAutoRooter::new(
1789 options
1790 .parent
1791 .transfer
1792 .iter()
1793 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
1794 .collect(),
1795 );
1796 #[expect(unsafe_code)]
1797 let transfer = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
1798
1799 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1800 let source = incumbent.as_window();
1801
1802 let source_origin = source.Document().origin().immutable().clone();
1803
1804 self.post_message_impl(
1805 &options.targetOrigin,
1806 source_origin,
1807 source,
1808 cx,
1809 message,
1810 transfer,
1811 )
1812 }
1813
1814 fn CaptureEvents(&self) {
1816 }
1818
1819 fn ReleaseEvents(&self) {
1821 }
1823
1824 fn WebdriverCallback(&self, realm: &mut CurrentRealm, value: HandleValue) {
1826 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1827 if let Some(webdriver_script_sender) = webdriver_script_sender {
1828 let result = jsval_to_webdriver(realm, &self.globalscope, value);
1829 let _ = webdriver_script_sender.send(result);
1830 }
1831 }
1832
1833 fn WebdriverException(&self, cx: &mut JSContext, value: HandleValue) {
1834 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1835 if let Some(webdriver_script_sender) = webdriver_script_sender {
1836 let error_info = ErrorInfo::from_value(value, cx.into(), CanGc::from_cx(cx));
1837 let _ = webdriver_script_sender.send(Err(
1838 JavaScriptEvaluationError::EvaluationFailure(Some(
1839 javascript_error_info_from_error_info(cx, &error_info, value),
1840 )),
1841 ));
1842 }
1843 }
1844
1845 fn WebdriverElement(&self, id: DOMString) -> Option<DomRoot<Element>> {
1846 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1847 }
1848
1849 fn WebdriverFrame(&self, browsing_context_id: DOMString) -> Option<DomRoot<WindowProxy>> {
1850 self.Document()
1851 .iframes()
1852 .iter()
1853 .find(|iframe| {
1854 iframe
1855 .browsing_context_id()
1856 .as_ref()
1857 .map(BrowsingContextId::to_string) ==
1858 Some(browsing_context_id.to_string())
1859 })
1860 .and_then(|iframe| iframe.GetContentWindow())
1861 }
1862
1863 fn WebdriverWindow(&self, webview_id: DOMString) -> DomRoot<WindowProxy> {
1864 let window_proxy = &self
1865 .window_proxy
1866 .get()
1867 .expect("Should always have a WindowProxy when calling WebdriverWindow");
1868 assert!(
1869 self.is_top_level(),
1870 "Window must be top level browsing context."
1871 );
1872 assert!(self.webview_id().to_string() == webview_id);
1873 DomRoot::from_ref(window_proxy)
1874 }
1875
1876 fn WebdriverShadowRoot(&self, id: DOMString) -> Option<DomRoot<ShadowRoot>> {
1877 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1878 }
1879
1880 fn GetComputedStyle(
1882 &self,
1883 cx: &mut JSContext,
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 cx,
1937 self,
1938 if is_null {
1939 CSSStyleOwner::Null
1940 } else {
1941 CSSStyleOwner::Element(Dom::from_ref(element))
1942 },
1943 pseudo,
1944 CSSModificationAccess::Readonly,
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 ScreenLeft(&self) -> i32 {
2116 self.client_window().min.x
2117 }
2118
2119 fn ScreenY(&self) -> i32 {
2121 self.client_window().min.y
2122 }
2123
2124 fn ScreenTop(&self) -> i32 {
2126 self.client_window().min.y
2127 }
2128
2129 fn OuterHeight(&self) -> i32 {
2131 self.client_window().height()
2132 }
2133
2134 fn OuterWidth(&self) -> i32 {
2136 self.client_window().width()
2137 }
2138
2139 fn DevicePixelRatio(&self) -> Finite<f64> {
2141 Finite::wrap(self.device_pixel_ratio().get() as f64)
2142 }
2143
2144 fn Status(&self) -> DOMString {
2146 self.status.borrow().clone()
2147 }
2148
2149 fn SetStatus(&self, status: DOMString) {
2151 *self.status.borrow_mut() = status
2152 }
2153
2154 fn MatchMedia(&self, query: DOMString) -> DomRoot<MediaQueryList> {
2156 let media_query_list = MediaList::parse_media_list(&query.str(), self);
2157 let document = self.Document();
2158 let mql = MediaQueryList::new(&document, media_query_list, CanGc::deprecated_note());
2159 self.media_query_lists.track(&*mql);
2160 mql
2161 }
2162
2163 fn Fetch(
2165 &self,
2166 realm: &mut CurrentRealm,
2167 input: RequestOrUSVString,
2168 init: RootedTraceableBox<RequestInit>,
2169 ) -> Rc<Promise> {
2170 fetch::Fetch(self.upcast(), input, init, realm)
2171 }
2172
2173 fn FetchLater(
2175 &self,
2176 cx: &mut js::context::JSContext,
2177 input: RequestInfo,
2178 init: RootedTraceableBox<DeferredRequestInit>,
2179 ) -> Fallible<DomRoot<FetchLaterResult>> {
2180 fetch::FetchLater(cx, self, input, init)
2181 }
2182
2183 #[cfg(feature = "bluetooth")]
2184 fn TestRunner(&self, cx: &mut js::context::JSContext) -> DomRoot<TestRunner> {
2185 self.test_runner
2186 .or_init(|| TestRunner::new(cx, self.upcast()))
2187 }
2188
2189 fn RunningAnimationCount(&self) -> u32 {
2190 self.document
2191 .get()
2192 .map_or(0, |d| d.animations().running_animation_count() as u32)
2193 }
2194
2195 fn SetName(&self, name: DOMString) {
2197 if let Some(proxy) = self.undiscarded_window_proxy() {
2198 proxy.set_name(name);
2199 }
2200 }
2201
2202 fn Name(&self) -> DOMString {
2204 match self.undiscarded_window_proxy() {
2205 Some(proxy) => proxy.get_name(),
2206 None => "".into(),
2207 }
2208 }
2209
2210 fn Origin(&self) -> USVString {
2212 USVString(self.origin().immutable().ascii_serialization())
2213 }
2214
2215 fn GetSelection(&self, cx: &mut JSContext) -> Option<DomRoot<Selection>> {
2217 self.document.get().and_then(|d| d.GetSelection(cx))
2218 }
2219
2220 fn Event(&self, cx: SafeJSContext, rval: MutableHandleValue) {
2222 if let Some(ref event) = *self.current_event.borrow() {
2223 event
2224 .reflector()
2225 .get_jsobject()
2226 .safe_to_jsval(cx, rval, CanGc::deprecated_note());
2227 }
2228 }
2229
2230 fn IsSecureContext(&self) -> bool {
2231 self.as_global_scope().is_secure_context()
2232 }
2233
2234 fn NamedGetter(
2236 &self,
2237 cx: &mut js::context::JSContext,
2238 name: DOMString,
2239 ) -> Option<NamedPropertyValue> {
2240 if name.is_empty() {
2241 return None;
2242 }
2243 let document = self.Document();
2244
2245 let iframes: Vec<_> = document
2247 .iframes()
2248 .iter()
2249 .filter(|iframe| {
2250 if let Some(window) = iframe.GetContentWindow() {
2251 return window.get_name() == name;
2252 }
2253 false
2254 })
2255 .collect();
2256
2257 let iframe_iter = iframes.iter().map(|iframe| iframe.upcast::<Element>());
2258
2259 let name = Atom::from(name);
2260
2261 let elements_with_name = document.get_elements_with_name(cx, &name);
2263 let name_iter = elements_with_name
2264 .iter()
2265 .map(|element| &**element)
2266 .filter(|elem| is_named_element_with_name_attribute(elem));
2267
2268 let elements_with_id = document.get_elements_with_id(cx, &name);
2269 let id_iter = elements_with_id
2270 .iter()
2271 .map(|element| &**element)
2272 .filter(|elem| is_named_element_with_id_attribute(elem));
2273
2274 for elem in iframe_iter.clone() {
2276 if let Some(nested_window_proxy) = elem
2277 .downcast::<HTMLIFrameElement>()
2278 .and_then(|iframe| iframe.GetContentWindow())
2279 {
2280 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
2281 }
2282 }
2283
2284 let mut elements = iframe_iter.chain(name_iter).chain(id_iter);
2285
2286 let first = elements.next()?;
2287
2288 if elements.next().is_none() {
2289 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
2291 }
2292
2293 #[derive(JSTraceable, MallocSizeOf)]
2295 struct WindowNamedGetter {
2296 #[no_trace]
2297 name: Atom,
2298 }
2299 impl CollectionFilter for WindowNamedGetter {
2300 fn filter(&self, elem: &Element, _root: &Node) -> bool {
2301 let type_ = match elem.upcast::<Node>().type_id() {
2302 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
2303 _ => return false,
2304 };
2305 if elem.get_id().as_ref() == Some(&self.name) {
2306 return true;
2307 }
2308 match type_ {
2309 HTMLElementTypeId::HTMLEmbedElement |
2310 HTMLElementTypeId::HTMLFormElement |
2311 HTMLElementTypeId::HTMLImageElement |
2312 HTMLElementTypeId::HTMLObjectElement => {
2313 elem.get_name().as_ref() == Some(&self.name)
2314 },
2315 _ => false,
2316 }
2317 }
2318 }
2319 let collection = HTMLCollection::create(
2320 cx,
2321 self,
2322 document.upcast(),
2323 Box::new(WindowNamedGetter { name }),
2324 );
2325 Some(NamedPropertyValue::HTMLCollection(collection))
2326 }
2327
2328 fn SupportedPropertyNames(&self, cx: &mut js::context::JSContext) -> Vec<DOMString> {
2330 let document = self.Document();
2331 let mut names_with_first_named_element_map = HashMap::new();
2332 document
2333 .name_map()
2334 .for_each(cx.no_gc(), document.upcast(), |name, elements| {
2335 if name.is_empty() {
2336 return;
2337 }
2338 let mut name_iter = elements
2339 .iter()
2340 .filter(|elem| is_named_element_with_name_attribute(elem));
2341 if let Some(first) = name_iter.next() {
2342 names_with_first_named_element_map.insert(name.clone(), first.as_rooted());
2343 }
2344 });
2345
2346 document
2347 .id_map()
2348 .for_each(cx.no_gc(), document.upcast(), |id, elements| {
2349 if id.is_empty() {
2350 return;
2351 }
2352 let mut id_iter = elements
2353 .iter()
2354 .filter(|elem| is_named_element_with_id_attribute(elem));
2355 if let Some(first) = id_iter.next() {
2356 match names_with_first_named_element_map.entry(id.clone()) {
2357 Entry::Vacant(entry) => drop(entry.insert(first.as_rooted())),
2358 Entry::Occupied(mut entry) => {
2359 if first.upcast::<Node>().is_before(entry.get().upcast()) {
2360 *entry.get_mut() = first.as_rooted();
2361 }
2362 },
2363 }
2364 }
2365 });
2366
2367 let mut names_with_first_named_element_vec: Vec<_> =
2368 names_with_first_named_element_map.into_iter().collect();
2369 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
2370 if a.1 == b.1 {
2371 a.0.cmp(&b.0)
2374 } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
2375 cmp::Ordering::Less
2376 } else {
2377 cmp::Ordering::Greater
2378 }
2379 });
2380
2381 names_with_first_named_element_vec
2382 .iter()
2383 .map(|(k, _v)| DOMString::from(&**k))
2384 .collect()
2385 }
2386
2387 fn StructuredClone(
2389 &self,
2390 cx: &mut JSContext,
2391 value: HandleValue,
2392 options: RootedTraceableBox<StructuredSerializeOptions>,
2393 retval: MutableHandleValue,
2394 ) -> Fallible<()> {
2395 self.as_global_scope()
2396 .structured_clone(cx, value, options, retval)
2397 }
2398
2399 fn TrustedTypes(&self, cx: &mut JSContext) -> DomRoot<TrustedTypePolicyFactory> {
2400 self.trusted_types
2401 .or_init(|| TrustedTypePolicyFactory::new(cx, self.as_global_scope()))
2402 }
2403}
2404
2405impl Window {
2406 pub(crate) fn scroll_offset(&self) -> Vector2D<f32, LayoutPixel> {
2407 self.scroll_offset_query_with_external_scroll_id(self.pipeline_id().root_scroll_id())
2408 }
2409
2410 pub(crate) fn create_named_properties_object(
2413 cx: &mut JSContext,
2414 proto: HandleObject,
2415 object: MutableHandleObject,
2416 ) {
2417 window_named_properties::create(cx.into(), proto, object)
2418 }
2419
2420 pub(crate) fn current_event(&self) -> Option<DomRoot<Event>> {
2421 self.current_event
2422 .borrow()
2423 .as_ref()
2424 .map(|e| DomRoot::from_ref(&**e))
2425 }
2426
2427 pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
2428 let current = self.current_event();
2429 *self.current_event.borrow_mut() = event.map(Dom::from_ref);
2430 current
2431 }
2432
2433 fn post_message_impl(
2435 &self,
2436 target_origin: &USVString,
2437 source_origin: ImmutableOrigin,
2438 source: &Window,
2439 cx: &mut JSContext,
2440 message: HandleValue,
2441 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
2442 ) -> ErrorResult {
2443 let data = structuredclone::write(cx, message, Some(transfer))?;
2445
2446 let target_origin = match target_origin.0[..].as_ref() {
2448 "*" => None,
2449 "/" => Some(source_origin.clone()),
2450 url => match ServoUrl::parse(url) {
2451 Ok(url) => Some(url.origin()),
2452 Err(_) => return Err(Error::Syntax(None)),
2453 },
2454 };
2455
2456 self.post_message(target_origin, source_origin, &source.window_proxy(), data);
2458 Ok(())
2459 }
2460
2461 pub(crate) fn paint_worklet(&self, cx: &mut JSContext) -> DomRoot<Worklet> {
2463 self.paint_worklet.or_init(|| self.new_paint_worklet(cx))
2464 }
2465
2466 pub(crate) fn has_document(&self) -> bool {
2467 self.document.get().is_some()
2468 }
2469
2470 pub(crate) fn clear_js_runtime(&self) {
2471 self.as_global_scope()
2472 .remove_web_messaging_and_dedicated_workers_infra();
2473
2474 if let Some(custom_elements) = self.custom_element_registry.get() {
2477 custom_elements.teardown();
2478 }
2479
2480 self.current_state.set(WindowState::Zombie);
2481 *self.js_runtime.borrow_mut() = None;
2482
2483 if let Some(proxy) = self.window_proxy.get() {
2486 let pipeline_id = self.pipeline_id();
2487 if let Some(currently_active) = proxy.currently_active() &&
2488 currently_active == pipeline_id
2489 {
2490 self.window_proxy.set(None);
2491 }
2492 }
2493
2494 if let Some(performance) = self.performance.get() {
2495 performance.clear_and_disable_performance_entry_buffer();
2496 }
2497 self.as_global_scope()
2498 .task_manager()
2499 .cancel_all_tasks_and_ignore_future_tasks();
2500
2501 self.pending_image_callbacks.borrow_mut().clear();
2504 }
2505
2506 pub(crate) fn scroll(&self, cx: &mut JSContext, x: f32, y: f32, behavior: ScrollBehavior) {
2508 let xfinite = if x.is_finite() { x } else { 0.0 };
2510 let yfinite = if y.is_finite() { y } else { 0.0 };
2511
2512 let viewport = self.viewport_details.get().size;
2522
2523 let scrolling_area = self.scrolling_area_query(None).to_f32();
2542 let x = xfinite.clamp(0.0, 0.0f32.max(scrolling_area.width() - viewport.width));
2543 let y = yfinite.clamp(0.0, 0.0f32.max(scrolling_area.height() - viewport.height));
2544
2545 let scroll_offset = self.scroll_offset();
2548 if x == scroll_offset.x && y == scroll_offset.y {
2549 return;
2550 }
2551
2552 self.perform_a_scroll(
2557 cx,
2558 x,
2559 y,
2560 self.pipeline_id().root_scroll_id(),
2561 behavior,
2562 None,
2563 );
2564 }
2565
2566 pub(crate) fn perform_a_scroll(
2568 &self,
2569 cx: &mut JSContext,
2570 x: f32,
2571 y: f32,
2572 scroll_id: ExternalScrollId,
2573 _behavior: ScrollBehavior,
2574 element: Option<&Element>,
2575 ) {
2576 let (reflow_phases_run, _) = self.reflow(
2580 cx,
2581 ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)),
2582 );
2583 if reflow_phases_run.needs_frame() {
2584 self.paint_api()
2585 .generate_frame(vec![self.webview_id().into()]);
2586 }
2587
2588 if reflow_phases_run.contains(ReflowPhasesRun::UpdatedScrollNodeOffset) {
2593 match element {
2594 Some(element) if !scroll_id.is_root() => element.handle_scroll_event(),
2595 _ => self.Document().handle_viewport_scroll_event(),
2596 };
2597 }
2598 }
2599
2600 pub(crate) fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
2601 self.viewport_details.get().hidpi_scale_factor
2602 }
2603
2604 fn client_window(&self) -> DeviceIndependentIntRect {
2605 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel!");
2606
2607 self.send_to_embedder(EmbedderMsg::GetWindowRect(self.webview_id(), sender));
2608
2609 receiver.recv().unwrap_or_default()
2610 }
2611
2612 pub(crate) fn advance_animation_clock(&self, delta: TimeDuration) {
2615 self.Document()
2616 .advance_animation_timeline_for_testing(delta);
2617 ScriptThread::handle_tick_all_animations_for_testing(self.pipeline_id());
2618 }
2619
2620 pub(crate) fn reflow(
2628 &self,
2629 cx: &mut JSContext,
2630 reflow_goal: ReflowGoal,
2631 ) -> (ReflowPhasesRun, ReflowStatistics) {
2632 let document = self.Document();
2633
2634 if !document.is_fully_active() {
2636 return Default::default();
2637 }
2638
2639 self.Document().ensure_safe_to_run_script_or_layout();
2640
2641 let pipeline_id = self.pipeline_id();
2645 if reflow_goal == ReflowGoal::UpdateTheRendering &&
2646 self.layout_blocker.get().layout_blocked()
2647 {
2648 debug!("Suppressing pre-load-event reflow pipeline {pipeline_id}");
2649 return Default::default();
2650 }
2651
2652 debug!("script: performing reflow for goal {reflow_goal:?}");
2653 let marker = if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) {
2654 Some(TimelineMarker::start("Reflow".to_owned()))
2655 } else {
2656 None
2657 };
2658
2659 let restyle_reason = document.restyle_reason();
2660 document.clear_restyle_reasons();
2661 let restyle = if restyle_reason.needs_restyle() {
2662 debug!("Invalidating layout cache due to reflow condition {restyle_reason:?}",);
2663 self.layout_marker.borrow().set(false);
2665 *self.layout_marker.borrow_mut() = Rc::new(Cell::new(true));
2667
2668 let stylesheets_changed = document.flush_stylesheets_for_reflow();
2669 let pending_restyles = document.drain_pending_restyles();
2670 let dirty_root = document
2671 .take_dirty_root()
2672 .filter(|_| !stylesheets_changed)
2673 .or_else(|| document.GetDocumentElement())
2674 .map(|root| root.upcast::<Node>().to_trusted_node_address());
2675
2676 Some(ReflowRequestRestyle {
2677 reason: restyle_reason,
2678 dirty_root,
2679 stylesheets_changed,
2680 pending_restyles,
2681 })
2682 } else {
2683 None
2684 };
2685
2686 document.id_map().resolve_all(cx.no_gc(), document.upcast());
2689
2690 let document_context = self.web_font_context();
2691
2692 let reflow = ReflowRequest {
2694 document: document.upcast::<Node>().to_trusted_node_address(),
2695 epoch: document.current_rendering_epoch(),
2696 restyle,
2697 viewport_details: self.viewport_details.get(),
2698 origin: self.origin().immutable().clone(),
2699 reflow_goal,
2700 animation_timeline_value: document.current_animation_timeline_value(),
2701 animations: document.animations().sets.clone(),
2702 animating_images: document.image_animation_manager().animating_images(),
2703 highlighted_dom_node: document.highlighted_dom_node().map(|node| node.to_opaque()),
2704 document_context,
2705 };
2706
2707 let Some(reflow_result) = self.layout.borrow_mut().reflow(reflow) else {
2708 return Default::default();
2709 };
2710
2711 debug!("script: layout complete");
2712 if let Some(marker) = marker {
2713 self.emit_timeline_marker(marker.end());
2714 }
2715
2716 self.handle_pending_images_post_reflow(
2717 cx,
2718 reflow_result.pending_images,
2719 reflow_result.pending_rasterization_images,
2720 reflow_result.pending_svg_elements_for_serialization,
2721 );
2722
2723 if let Some(iframe_sizes) = reflow_result.iframe_sizes {
2724 document
2725 .iframes_mut()
2726 .handle_new_iframe_sizes_after_layout(self, iframe_sizes);
2727 }
2728
2729 document.update_animations_post_reflow();
2730
2731 document.accessibility_data_mut().unroot_all_removed_nodes();
2732
2733 (
2734 reflow_result.reflow_phases_run,
2735 reflow_result.reflow_statistics,
2736 )
2737 }
2738
2739 pub(crate) fn request_screenshot_readiness(&self, cx: &mut js::context::JSContext) {
2740 self.has_pending_screenshot_readiness_request.set(true);
2741 self.maybe_resolve_pending_screenshot_readiness_requests(cx);
2742 }
2743
2744 pub(crate) fn maybe_resolve_pending_screenshot_readiness_requests(
2745 &self,
2746 cx: &mut js::context::JSContext,
2747 ) {
2748 let pending_request = self.has_pending_screenshot_readiness_request.get();
2749 if !pending_request {
2750 return;
2751 }
2752
2753 let document = self.Document();
2754 if document.ReadyState() != DocumentReadyState::Complete {
2755 return;
2756 }
2757
2758 if document.render_blocking_element_count() > 0 {
2759 return;
2760 }
2761
2762 if document.GetDocumentElement().is_some_and(|elem| {
2766 elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
2767 elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
2768 }) {
2769 return;
2770 }
2771
2772 if self.font_context().web_fonts_still_loading() != 0 {
2773 return;
2774 }
2775
2776 if self.Document().Fonts(cx).waiting_to_fullfill_promise() {
2777 return;
2778 }
2779
2780 if !self.pending_layout_images.borrow().is_empty() ||
2781 !self.pending_images_for_rasterization.borrow().is_empty()
2782 {
2783 return;
2784 }
2785
2786 let document = self.Document();
2787 if document.needs_rendering_update() {
2788 return;
2789 }
2790
2791 let epoch = document.current_rendering_epoch();
2794 let pipeline_id = self.pipeline_id();
2795 debug!("Ready to take screenshot of {pipeline_id:?} at epoch={epoch:?}");
2796
2797 self.send_to_constellation(
2798 ScriptToConstellationMessage::RespondToScreenshotReadinessRequest(
2799 ScreenshotReadinessResponse::Ready(epoch),
2800 ),
2801 );
2802 self.has_pending_screenshot_readiness_request.set(false);
2803 }
2804
2805 pub(crate) fn reflow_if_reflow_timer_expired(&self, cx: &mut JSContext) {
2808 if !matches!(
2811 self.layout_blocker.get(),
2812 LayoutBlocker::Parsing(instant) if instant + INITIAL_REFLOW_DELAY < Instant::now()
2813 ) {
2814 return;
2815 }
2816 self.allow_layout_if_necessary(cx);
2817 }
2818
2819 pub(crate) fn prevent_layout_until_load_event(&self) {
2823 if !matches!(self.layout_blocker.get(), LayoutBlocker::WaitingForParse) {
2826 return;
2827 }
2828
2829 self.layout_blocker
2830 .set(LayoutBlocker::Parsing(Instant::now()));
2831 }
2832
2833 pub(crate) fn allow_layout_if_necessary(&self, cx: &mut JSContext) {
2836 if matches!(
2837 self.layout_blocker.get(),
2838 LayoutBlocker::FiredLoadEventOrParsingTimerExpired
2839 ) {
2840 return;
2841 }
2842
2843 self.layout_blocker
2844 .set(LayoutBlocker::FiredLoadEventOrParsingTimerExpired);
2845
2846 let document = self.Document();
2858 if !document.is_render_blocked() && document.update_the_rendering(cx).0.needs_frame() {
2859 self.paint_api()
2860 .generate_frame(vec![self.webview_id().into()]);
2861 }
2862 }
2863
2864 pub(crate) fn layout_blocked(&self) -> bool {
2865 self.layout_blocker.get().layout_blocked()
2866 }
2867
2868 #[expect(unsafe_code)]
2870 pub(crate) fn layout_reflow(&self, query_msg: QueryMsg) {
2871 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
2873
2874 self.reflow(&mut cx, ReflowGoal::LayoutQuery(query_msg));
2875 }
2876
2877 pub(crate) fn resolved_font_style_query(
2878 &self,
2879 node: &Node,
2880 value: String,
2881 ) -> Option<ServoArc<Font>> {
2882 self.layout_reflow(QueryMsg::ResolvedFontStyleQuery);
2883
2884 let document = self.Document();
2885 let animations = document.animations().sets.clone();
2886 self.layout.borrow().query_resolved_font_style(
2887 node.to_trusted_node_address(),
2888 &value,
2889 animations,
2890 document.current_animation_timeline_value(),
2891 )
2892 }
2893
2894 #[expect(unsafe_code)]
2897 pub(crate) fn containing_block_node_query_without_reflow(
2898 &self,
2899 node: &Node,
2900 ) -> Option<DomRoot<Node>> {
2901 self.layout
2902 .borrow()
2903 .query_containing_block(node.to_trusted_node_address())
2904 .map(|address| unsafe { from_untrusted_node_address(address) })
2905 }
2906
2907 pub(crate) fn padding_query_without_reflow(&self, node: &Node) -> Option<PhysicalSides> {
2912 let layout = self.layout.borrow();
2913 layout.query_padding(node.to_trusted_node_address())
2914 }
2915
2916 pub(crate) fn box_area_query_without_reflow(
2921 &self,
2922 node: &Node,
2923 area: BoxAreaType,
2924 exclude_transform_and_inline: bool,
2925 ) -> Option<Rect<Au, CSSPixel>> {
2926 let layout = self.layout.borrow();
2927 layout.ensure_stacking_context_tree(self.viewport_details.get());
2928 layout.query_box_area(
2929 node.to_trusted_node_address(),
2930 area,
2931 exclude_transform_and_inline,
2932 )
2933 }
2934
2935 pub(crate) fn box_area_query(
2936 &self,
2937 node: &Node,
2938 area: BoxAreaType,
2939 exclude_transform_and_inline: bool,
2940 ) -> Option<Rect<Au, CSSPixel>> {
2941 self.layout_reflow(QueryMsg::BoxArea);
2942 self.box_area_query_without_reflow(node, area, exclude_transform_and_inline)
2943 }
2944
2945 pub(crate) fn box_areas_query(&self, node: &Node, area: BoxAreaType) -> CSSPixelRectVec {
2946 self.layout_reflow(QueryMsg::BoxAreas);
2947 self.layout
2948 .borrow()
2949 .query_box_areas(node.to_trusted_node_address(), area)
2950 }
2951
2952 pub(crate) fn client_rect_query(&self, node: &Node) -> Rect<i32, CSSPixel> {
2953 self.layout_reflow(QueryMsg::ClientRectQuery);
2954 self.layout
2955 .borrow()
2956 .query_client_rect(node.to_trusted_node_address())
2957 }
2958
2959 pub(crate) fn current_css_zoom_query(&self, node: &Node) -> f32 {
2960 self.layout_reflow(QueryMsg::CurrentCSSZoomQuery);
2961 self.layout
2962 .borrow()
2963 .query_current_css_zoom(node.to_trusted_node_address())
2964 }
2965
2966 pub(crate) fn scrolling_area_query(&self, node: Option<&Node>) -> Rect<i32, CSSPixel> {
2969 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2970 self.layout
2971 .borrow()
2972 .query_scrolling_area(node.map(Node::to_trusted_node_address))
2973 }
2974
2975 pub(crate) fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32, LayoutPixel> {
2976 let external_scroll_id = ExternalScrollId(
2977 combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody),
2978 self.pipeline_id().into(),
2979 );
2980 self.scroll_offset_query_with_external_scroll_id(external_scroll_id)
2981 }
2982
2983 fn scroll_offset_query_with_external_scroll_id(
2984 &self,
2985 external_scroll_id: ExternalScrollId,
2986 ) -> Vector2D<f32, LayoutPixel> {
2987 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2988 self.scroll_offset_query_with_external_scroll_id_no_reflow(external_scroll_id)
2989 }
2990
2991 fn scroll_offset_query_with_external_scroll_id_no_reflow(
2992 &self,
2993 external_scroll_id: ExternalScrollId,
2994 ) -> Vector2D<f32, LayoutPixel> {
2995 self.layout
2996 .borrow()
2997 .scroll_offset(external_scroll_id)
2998 .unwrap_or_default()
2999 }
3000
3001 pub(crate) fn scroll_an_element(
3004 &self,
3005 cx: &mut JSContext,
3006 element: &Element,
3007 x: f32,
3008 y: f32,
3009 behavior: ScrollBehavior,
3010 ) {
3011 let scroll_id = ExternalScrollId(
3012 combine_id_with_fragment_type(
3013 element.upcast::<Node>().to_opaque().id(),
3014 FragmentType::FragmentBody,
3015 ),
3016 self.pipeline_id().into(),
3017 );
3018
3019 self.perform_a_scroll(cx, x, y, scroll_id, behavior, Some(element));
3023 }
3024
3025 pub(crate) fn resolved_style_query(
3026 &self,
3027 element: TrustedNodeAddress,
3028 pseudo: Option<PseudoElement>,
3029 property: PropertyId,
3030 ) -> DOMString {
3031 self.layout_reflow(QueryMsg::ResolvedStyleQuery);
3032
3033 let document = self.Document();
3034 let animations = document.animations().sets.clone();
3035 DOMString::from(self.layout.borrow().query_resolved_style(
3036 element,
3037 pseudo,
3038 property,
3039 animations,
3040 document.current_animation_timeline_value(),
3041 ))
3042 }
3043
3044 pub(crate) fn get_iframe_viewport_details_if_known(
3048 &self,
3049 browsing_context_id: BrowsingContextId,
3050 ) -> Option<ViewportDetails> {
3051 self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery);
3053 self.Document()
3054 .iframes()
3055 .get(browsing_context_id)
3056 .and_then(|iframe| iframe.size)
3057 }
3058
3059 #[expect(unsafe_code)]
3060 pub(crate) fn offset_parent_query(
3061 &self,
3062 node: &Node,
3063 ) -> (Option<DomRoot<Element>>, Rect<Au, CSSPixel>) {
3064 self.layout_reflow(QueryMsg::OffsetParentQuery);
3065 let response = self
3066 .layout
3067 .borrow()
3068 .query_offset_parent(node.to_trusted_node_address());
3069 let element = response.node_address.and_then(|parent_node_address| {
3070 let node = unsafe { from_untrusted_node_address(parent_node_address) };
3071 DomRoot::downcast(node)
3072 });
3073 (element, response.rect)
3074 }
3075
3076 pub(crate) fn scroll_container_query(
3077 &self,
3078 node: Option<&Node>,
3079 flags: ScrollContainerQueryFlags,
3080 ) -> Option<ScrollContainerResponse> {
3081 self.layout_reflow(QueryMsg::ScrollParentQuery);
3082 self.layout
3083 .borrow()
3084 .query_scroll_container(node.map(Node::to_trusted_node_address), flags)
3085 }
3086
3087 #[expect(unsafe_code)]
3088 pub(crate) fn scrolling_box_query(
3089 &self,
3090 node: Option<&Node>,
3091 flags: ScrollContainerQueryFlags,
3092 ) -> Option<ScrollingBox> {
3093 self.scroll_container_query(node, flags)
3094 .and_then(|response| {
3095 Some(match response {
3096 ScrollContainerResponse::Viewport(overflow) => {
3097 (ScrollingBoxSource::Viewport(self.Document()), overflow)
3098 },
3099 ScrollContainerResponse::Element(parent_node_address, overflow) => {
3100 let node = unsafe { from_untrusted_node_address(parent_node_address) };
3101 (
3102 ScrollingBoxSource::Element(DomRoot::downcast(node)?),
3103 overflow,
3104 )
3105 },
3106 })
3107 })
3108 .map(|(source, overflow)| ScrollingBox::new(source, overflow))
3109 }
3110
3111 pub(crate) fn text_index_query_on_node_for_event(
3112 &self,
3113 node: &Node,
3114 mouse_event: &MouseEvent,
3115 ) -> Option<usize> {
3116 let point_in_viewport = mouse_event.point_in_viewport()?.map(Au::from_f32_px);
3120
3121 self.layout_reflow(QueryMsg::TextIndexQuery);
3122 self.layout
3123 .borrow()
3124 .query_text_index(node.to_trusted_node_address(), point_in_viewport)
3125 }
3126
3127 pub(crate) fn elements_from_point_query(
3128 &self,
3129 point: LayoutPoint,
3130 ) -> Vec<ElementsFromPointResult> {
3131 self.layout_reflow(QueryMsg::ElementsFromPoint);
3132 self.layout().query_elements_from_point(point)
3133 }
3134
3135 pub(crate) fn query_effective_overflow(&self, node: &Node) -> Option<AxesOverflow> {
3136 self.layout_reflow(QueryMsg::EffectiveOverflow);
3137 self.query_effective_overflow_without_reflow(node)
3138 }
3139
3140 pub(crate) fn query_effective_overflow_without_reflow(
3141 &self,
3142 node: &Node,
3143 ) -> Option<AxesOverflow> {
3144 self.layout
3145 .borrow()
3146 .query_effective_overflow(node.to_trusted_node_address())
3147 }
3148
3149 pub(crate) fn hit_test_from_input_event(
3150 &self,
3151 input_event: &ConstellationInputEvent,
3152 ) -> Option<HitTestResult> {
3153 self.hit_test_from_point_in_viewport(
3154 input_event.hit_test_result.as_ref()?.point_in_viewport,
3155 )
3156 }
3157
3158 #[expect(unsafe_code)]
3159 pub(crate) fn hit_test_from_point_in_viewport(
3160 &self,
3161 point_in_frame: Point2D<f32, CSSPixel>,
3162 ) -> Option<HitTestResult> {
3163 let result = self
3164 .elements_from_point_query(point_in_frame.cast_unit())
3165 .into_iter()
3166 .nth(0)?;
3167
3168 let point_relative_to_initial_containing_block =
3169 point_in_frame + self.scroll_offset().cast_unit();
3170
3171 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
3174 Some(HitTestResult {
3175 node: unsafe { from_untrusted_node_address(address) },
3176 cursor: result.cursor,
3177 point_in_node: result.point_in_target,
3178 point_in_frame,
3179 point_relative_to_initial_containing_block,
3180 })
3181 }
3182
3183 pub(crate) fn init_window_proxy(&self, window_proxy: &WindowProxy) {
3184 assert!(self.window_proxy.get().is_none());
3185 self.window_proxy.set(Some(window_proxy));
3186 }
3187
3188 pub(crate) fn init_document(&self, document: &Document) {
3189 assert!(self.document.get().is_none());
3190 assert!(document.window() == self);
3191 self.document.set(Some(document));
3192 }
3193
3194 pub(crate) fn load_data_for_document(
3195 &self,
3196 url: ServoUrl,
3197 pipeline_id: PipelineId,
3198 ) -> LoadData {
3199 let source_document = self.Document();
3200 let secure_context = if self.is_top_level() {
3201 None
3202 } else {
3203 Some(self.IsSecureContext())
3204 };
3205 LoadData::new(
3206 LoadOrigin::Script(self.origin().snapshot()),
3207 url,
3208 source_document.about_base_url(),
3209 Some(pipeline_id),
3210 Referrer::ReferrerUrl(source_document.url()),
3211 source_document.get_referrer_policy(),
3212 secure_context,
3213 Some(source_document.insecure_requests_policy()),
3214 source_document.has_trustworthy_ancestor_origin(),
3215 source_document.creation_sandboxing_flag_set_considering_parent_iframe(),
3216 )
3217 }
3218
3219 pub(crate) fn set_viewport_details(&self, viewport_details: ViewportDetails) {
3222 self.viewport_details.set(viewport_details);
3223 if !self.layout_mut().set_viewport_details(viewport_details) {
3224 return;
3225 }
3226 self.Document()
3227 .add_restyle_reason(RestyleReason::ViewportChanged);
3228 }
3229
3230 pub(crate) fn viewport_details(&self) -> ViewportDetails {
3231 self.viewport_details.get()
3232 }
3233
3234 pub(crate) fn get_or_init_visual_viewport(&self, can_gc: CanGc) -> DomRoot<VisualViewport> {
3235 self.visual_viewport.or_init(|| {
3236 VisualViewport::new_from_layout_viewport(self, self.viewport_details().size, can_gc)
3237 })
3238 }
3239
3240 pub(crate) fn maybe_update_visual_viewport(
3242 &self,
3243 pinch_zoom_infos: PinchZoomInfos,
3244 can_gc: CanGc,
3245 ) {
3246 if pinch_zoom_infos.rect == Rect::from_size(self.viewport_details().size) &&
3249 self.visual_viewport.get().is_none()
3250 {
3251 return;
3252 }
3253
3254 let visual_viewport = self.get_or_init_visual_viewport(can_gc);
3255 let changes = visual_viewport.update_from_pinch_zoom_infos(pinch_zoom_infos);
3256
3257 if changes.intersects(VisualViewportChanges::DimensionChanged) {
3258 self.has_changed_visual_viewport_dimension.set(true);
3259 }
3260 if changes.intersects(VisualViewportChanges::OffsetChanged) {
3261 visual_viewport.handle_scroll_event();
3262 }
3263 }
3264
3265 pub(crate) fn theme(&self) -> Theme {
3267 self.theme.get()
3268 }
3269
3270 pub(crate) fn set_theme(&self, new_theme: Theme) {
3272 self.theme.set(new_theme);
3273 if !self.layout_mut().set_theme(new_theme) {
3274 return;
3275 }
3276 self.Document()
3277 .add_restyle_reason(RestyleReason::ThemeChanged);
3278 }
3279
3280 pub(crate) fn get_url(&self) -> ServoUrl {
3281 self.Document().url()
3282 }
3283
3284 pub(crate) fn windowproxy_handler(&self) -> &'static WindowProxyHandler {
3285 self.dom_static.windowproxy_handler
3286 }
3287
3288 pub(crate) fn add_resize_event(&self, event: ViewportDetails, event_type: WindowSizeType) {
3289 *self.unhandled_resize_event.borrow_mut() = Some((event, event_type))
3292 }
3293
3294 pub(crate) fn take_unhandled_resize_event(&self) -> Option<(ViewportDetails, WindowSizeType)> {
3295 self.unhandled_resize_event.borrow_mut().take()
3296 }
3297
3298 pub(crate) fn has_unhandled_resize_event(&self) -> bool {
3300 self.unhandled_resize_event.borrow().is_some()
3301 }
3302
3303 pub(crate) fn suspend(&self, cx: &mut js::context::JSContext) {
3304 self.as_global_scope().suspend();
3306
3307 if self.window_proxy().currently_active() == Some(self.global().pipeline_id()) {
3309 self.window_proxy().unset_currently_active(cx);
3310 }
3311
3312 self.gc();
3317 }
3318
3319 pub(crate) fn resume(&self, can_gc: CanGc) {
3320 self.as_global_scope().resume();
3322
3323 self.window_proxy().set_currently_active(self, can_gc);
3325
3326 self.Document().title_changed();
3329 }
3330
3331 pub(crate) fn need_emit_timeline_marker(&self, timeline_type: TimelineMarkerType) -> bool {
3332 let markers = self.devtools_markers.borrow();
3333 markers.contains(&timeline_type)
3334 }
3335
3336 pub(crate) fn emit_timeline_marker(&self, marker: TimelineMarker) {
3337 let sender = self.devtools_marker_sender.borrow();
3338 let sender = sender.as_ref().expect("There is no marker sender");
3339 sender.send(Some(marker)).unwrap();
3340 }
3341
3342 pub(crate) fn set_devtools_timeline_markers(
3343 &self,
3344 markers: Vec<TimelineMarkerType>,
3345 reply: GenericSender<Option<TimelineMarker>>,
3346 ) {
3347 *self.devtools_marker_sender.borrow_mut() = Some(reply);
3348 self.devtools_markers.borrow_mut().extend(markers);
3349 }
3350
3351 pub(crate) fn drop_devtools_timeline_markers(&self, markers: Vec<TimelineMarkerType>) {
3352 let mut devtools_markers = self.devtools_markers.borrow_mut();
3353 for marker in markers {
3354 devtools_markers.remove(&marker);
3355 }
3356 if devtools_markers.is_empty() {
3357 *self.devtools_marker_sender.borrow_mut() = None;
3358 }
3359 }
3360
3361 pub(crate) fn set_webdriver_script_chan(&self, chan: Option<GenericSender<WebDriverJSResult>>) {
3362 *self.webdriver_script_chan.borrow_mut() = chan;
3363 }
3364
3365 pub(crate) fn set_webdriver_load_status_sender(
3366 &self,
3367 sender: Option<GenericSender<WebDriverLoadStatus>>,
3368 ) {
3369 *self.webdriver_load_status_sender.borrow_mut() = sender;
3370 }
3371
3372 pub(crate) fn webdriver_load_status_sender(
3373 &self,
3374 ) -> Option<GenericSender<WebDriverLoadStatus>> {
3375 self.webdriver_load_status_sender.borrow().clone()
3376 }
3377
3378 pub(crate) fn is_alive(&self) -> bool {
3379 self.current_state.get() == WindowState::Alive
3380 }
3381
3382 pub(crate) fn is_top_level(&self) -> bool {
3384 self.parent_info.is_none()
3385 }
3386
3387 fn run_resize_steps_for_layout_viewport(&self, cx: &mut js::context::JSContext) -> bool {
3392 let Some((new_size, size_type)) = self.take_unhandled_resize_event() else {
3393 return false;
3394 };
3395
3396 if self.viewport_details() == new_size {
3397 return false;
3398 }
3399
3400 let _realm = enter_realm(self);
3401 debug!(
3402 "Resizing Window for pipeline {:?} from {:?} to {new_size:?}",
3403 self.pipeline_id(),
3404 self.viewport_details(),
3405 );
3406 self.set_viewport_details(new_size);
3407
3408 self.Document()
3412 .add_restyle_reason(RestyleReason::ViewportChanged);
3413
3414 if self.layout().device().used_viewport_size() {
3417 self.Document().dirty_all_nodes();
3418 }
3419
3420 if size_type == WindowSizeType::Resize {
3422 let uievent = UIEvent::new(
3423 self,
3424 atom!("resize"),
3425 EventBubbles::DoesNotBubble,
3426 EventCancelable::NotCancelable,
3427 Some(self),
3428 0i32,
3429 0u32,
3430 CanGc::from_cx(cx),
3431 );
3432 uievent.upcast::<Event>().fire(cx, self.upcast());
3433 }
3434
3435 true
3436 }
3437
3438 pub(crate) fn run_the_resize_steps(&self, cx: &mut js::context::JSContext) -> bool {
3443 let layout_viewport_resized = self.run_resize_steps_for_layout_viewport(cx);
3444
3445 if self.has_changed_visual_viewport_dimension.get() {
3446 let visual_viewport = self.get_or_init_visual_viewport(CanGc::from_cx(cx));
3447
3448 let uievent = UIEvent::new(
3449 self,
3450 atom!("resize"),
3451 EventBubbles::DoesNotBubble,
3452 EventCancelable::NotCancelable,
3453 Some(self),
3454 0i32,
3455 0u32,
3456 CanGc::from_cx(cx),
3457 );
3458 uievent.upcast::<Event>().fire(cx, visual_viewport.upcast());
3459
3460 self.has_changed_visual_viewport_dimension.set(false);
3461 }
3462
3463 layout_viewport_resized
3464 }
3465
3466 pub(crate) fn evaluate_media_queries_and_report_changes(
3469 &self,
3470 cx: &mut js::context::JSContext,
3471 ) {
3472 let mut realm = enter_auto_realm(cx, self);
3473 let cx = &mut realm.current_realm();
3474 rooted_vec!(let mut mql_list);
3475
3476 self.media_query_lists.for_each(|mql| {
3477 if let MediaQueryListMatchState::Changed = mql.evaluate_changes() {
3478 mql_list.push(Dom::from_ref(&*mql));
3480 }
3481 });
3482 for mql in mql_list.iter() {
3484 let event = MediaQueryListEvent::new(
3485 &mql.global(),
3486 atom!("change"),
3487 false,
3488 false,
3489 mql.Media(),
3490 mql.Matches(),
3491 CanGc::from_cx(cx),
3492 );
3493 event
3494 .upcast::<Event>()
3495 .fire(cx, mql.upcast::<EventTarget>());
3496 }
3497 }
3498
3499 pub(crate) fn set_throttled(&self, throttled: bool) {
3501 self.throttled.set(throttled);
3502 if throttled {
3503 self.as_global_scope().slow_down_timers();
3504 } else {
3505 self.as_global_scope().speed_up_timers();
3506 }
3507 }
3508
3509 pub(crate) fn throttled(&self) -> bool {
3510 self.throttled.get()
3511 }
3512
3513 pub(crate) fn unminified_css_dir(&self) -> Option<String> {
3514 self.unminified_css_dir.borrow().clone()
3515 }
3516
3517 pub(crate) fn local_script_source(&self) -> &Option<String> {
3518 &self.local_script_source
3519 }
3520
3521 pub(crate) fn set_navigation_start(&self) {
3522 self.navigation_start.set(CrossProcessInstant::now());
3523 }
3524
3525 pub(crate) fn navigation_start(&self) -> CrossProcessInstant {
3526 self.navigation_start.get()
3527 }
3528
3529 pub(crate) fn set_last_activation_timestamp(&self, time: UserActivationTimestamp) {
3530 self.last_activation_timestamp.set(time);
3531 }
3532
3533 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
3534 self.as_global_scope()
3535 .script_to_embedder_chan()
3536 .send(msg)
3537 .unwrap();
3538 }
3539
3540 pub(crate) fn send_to_constellation(&self, msg: ScriptToConstellationMessage) {
3541 self.as_global_scope()
3542 .script_to_constellation_chan()
3543 .send(msg)
3544 .unwrap();
3545 }
3546
3547 #[cfg(feature = "webxr")]
3548 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3549 self.navigator
3550 .get()
3551 .as_ref()
3552 .and_then(|nav| nav.xr())
3553 .is_some_and(|xr| xr.pending_or_active_session())
3554 }
3555
3556 #[cfg(not(feature = "webxr"))]
3557 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3558 false
3559 }
3560
3561 #[expect(unsafe_code)]
3562 fn handle_pending_images_post_reflow(
3563 &self,
3564 cx: &mut JSContext,
3565 pending_images: Vec<PendingImage>,
3566 pending_rasterization_images: Vec<PendingRasterizationImage>,
3567 pending_svg_element_for_serialization: Vec<UntrustedNodeAddress>,
3568 ) {
3569 let pipeline_id = self.pipeline_id();
3570 for image in pending_images {
3571 let id = image.id;
3572 let node = unsafe { from_untrusted_node_address(image.node) };
3573
3574 if let PendingImageState::Unrequested(ref url) = image.state {
3575 fetch_image_for_layout(
3576 url.clone(),
3577 &node,
3578 id,
3579 image.is_internal_request,
3580 self.image_cache.clone(),
3581 );
3582 }
3583
3584 let mut images = self.pending_layout_images.borrow_mut();
3585 if !images.contains_key(&id) {
3586 let trusted_node = Trusted::new(&*node);
3587 let sender = self.register_image_cache_listener(id, move |response, _| {
3588 trusted_node
3589 .root()
3590 .owner_window()
3591 .pending_layout_image_notification(response);
3592 });
3593
3594 self.image_cache
3595 .add_listener(ImageLoadListener::new(sender, pipeline_id, id));
3596 }
3597
3598 let nodes = images.entry(id).or_default();
3599 if !nodes.iter().any(|n| std::ptr::eq(&*(n.node), &*node)) {
3600 nodes.push(PendingLayoutImageAncillaryData {
3601 node: Dom::from_ref(&*node),
3602 destination: image.destination,
3603 });
3604 }
3605 }
3606
3607 for image in pending_rasterization_images {
3608 let node = unsafe { from_untrusted_node_address(image.node) };
3609
3610 let mut images = self.pending_images_for_rasterization.borrow_mut();
3611 if !images.contains_key(&(image.id, image.size)) {
3612 let image_cache_sender = self.image_cache_sender.clone();
3613 self.image_cache.add_rasterization_complete_listener(
3614 pipeline_id,
3615 image.id,
3616 image.size,
3617 Box::new(move |response| {
3618 let _ = image_cache_sender.send(response);
3619 }),
3620 );
3621 }
3622
3623 let nodes = images.entry((image.id, image.size)).or_default();
3624 if !nodes.iter().any(|n| std::ptr::eq(&**n, &*node)) {
3625 nodes.push(Dom::from_ref(&*node));
3626 }
3627 }
3628
3629 for node in pending_svg_element_for_serialization.into_iter() {
3630 let node = unsafe { from_untrusted_node_address(node) };
3631 let svg = node.downcast::<SVGSVGElement>().unwrap();
3632 svg.serialize_and_cache_subtree(cx);
3633 node.dirty(NodeDamage::Other);
3634 }
3635 }
3636
3637 pub(crate) fn has_sticky_activation(&self) -> bool {
3639 UserActivationTimestamp::TimeStamp(CrossProcessInstant::now()) >=
3641 self.last_activation_timestamp.get()
3642 }
3643
3644 pub(crate) fn has_transient_activation(&self) -> bool {
3646 let current_time = CrossProcessInstant::now();
3649 UserActivationTimestamp::TimeStamp(current_time) >= self.last_activation_timestamp.get() &&
3650 UserActivationTimestamp::TimeStamp(current_time) <
3651 self.last_activation_timestamp.get() +
3652 pref!(dom_transient_activation_duration_ms)
3653 }
3654
3655 pub(crate) fn consume_last_activation_timestamp(&self) {
3656 if self.last_activation_timestamp.get() != UserActivationTimestamp::PositiveInfinity {
3657 self.set_last_activation_timestamp(UserActivationTimestamp::NegativeInfinity);
3658 }
3659 }
3660
3661 pub(crate) fn consume_user_activation(&self) {
3663 if self.undiscarded_window_proxy().is_none() {
3666 return;
3667 }
3668
3669 let Some(top_level_document) = self.top_level_document_if_local() else {
3673 return;
3674 };
3675
3676 top_level_document
3684 .window()
3685 .consume_last_activation_timestamp();
3686 for document in SameOriginDescendantNavigablesIterator::new(top_level_document) {
3687 document.window().consume_last_activation_timestamp();
3688 }
3689 }
3690
3691 #[allow(clippy::too_many_arguments)]
3692 pub(crate) fn new(
3693 cx: &mut js::context::JSContext,
3694 webview_id: WebViewId,
3695 runtime: Rc<Runtime>,
3696 script_chan: Sender<MainThreadScriptMsg>,
3697 layout: Box<dyn Layout>,
3698 font_context: Arc<FontContext>,
3699 image_cache_sender: Sender<ImageCacheResponseMessage>,
3700 image_cache: Arc<dyn ImageCache>,
3701 resource_threads: ResourceThreads,
3702 storage_threads: StorageThreads,
3703 #[cfg(feature = "bluetooth")] bluetooth_thread: GenericSender<BluetoothRequest>,
3704 mem_profiler_chan: MemProfilerChan,
3705 time_profiler_chan: TimeProfilerChan,
3706 devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
3707 constellation_chan: ScriptToConstellationChan,
3708 embedder_chan: ScriptToEmbedderChan,
3709 control_chan: GenericSender<ScriptThreadMessage>,
3710 pipeline_id: PipelineId,
3711 parent_info: Option<PipelineId>,
3712 viewport_details: ViewportDetails,
3713 origin: MutableOrigin,
3714 creation_url: ServoUrl,
3715 top_level_creation_url: ServoUrl,
3716 navigation_start: CrossProcessInstant,
3717 webgl_chan: Option<WebGLChan>,
3718 #[cfg(feature = "webxr")] webxr_registry: Option<webxr_api::Registry>,
3719 paint_api: CrossProcessPaintApi,
3720 unminify_js: bool,
3721 unminify_css: bool,
3722 local_script_source: Option<String>,
3723 user_scripts: Rc<Vec<UserScript>>,
3724 player_context: WindowGLContext,
3725 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
3726 inherited_secure_context: Option<bool>,
3727 theme: Theme,
3728 weak_script_thread: Weak<ScriptThread>,
3729 ) -> DomRoot<Self> {
3730 let error_reporter = CSSErrorReporter {
3731 pipelineid: pipeline_id,
3732 script_chan: control_chan,
3733 };
3734
3735 let win = Box::new(Self {
3736 webview_id,
3737 globalscope: GlobalScope::new_inherited(
3738 pipeline_id,
3739 devtools_chan,
3740 mem_profiler_chan,
3741 time_profiler_chan,
3742 constellation_chan,
3743 embedder_chan,
3744 resource_threads,
3745 storage_threads,
3746 origin,
3747 creation_url,
3748 Some(top_level_creation_url),
3749 #[cfg(feature = "webgpu")]
3750 gpu_id_hub,
3751 inherited_secure_context,
3752 unminify_js,
3753 Some(font_context),
3754 ),
3755 ongoing_navigation: Default::default(),
3756 script_chan,
3757 layout: RefCell::new(layout),
3758 image_cache_sender,
3759 image_cache,
3760 navigator: Default::default(),
3761 crypto: Default::default(),
3762 location: Default::default(),
3763 history: Default::default(),
3764 custom_element_registry: Default::default(),
3765 window_proxy: Default::default(),
3766 document: Default::default(),
3767 performance: Default::default(),
3768 navigation_start: Cell::new(navigation_start),
3769 screen: Default::default(),
3770 session_storage: Default::default(),
3771 local_storage: Default::default(),
3772 cookie_store: Default::default(),
3773 status: DomRefCell::new(DOMString::new()),
3774 parent_info,
3775 dom_static: GlobalStaticData::new(),
3776 js_runtime: DomRefCell::new(Some(runtime)),
3777 #[cfg(feature = "bluetooth")]
3778 bluetooth_thread,
3779 #[cfg(feature = "bluetooth")]
3780 bluetooth_extra_permission_data: BluetoothExtraPermissionData::new(),
3781 unhandled_resize_event: Default::default(),
3782 viewport_details: Cell::new(viewport_details),
3783 layout_blocker: Cell::new(LayoutBlocker::WaitingForParse),
3784 current_state: Cell::new(WindowState::Alive),
3785 devtools_marker_sender: Default::default(),
3786 devtools_markers: Default::default(),
3787 webdriver_script_chan: Default::default(),
3788 webdriver_load_status_sender: Default::default(),
3789 error_reporter,
3790 media_query_lists: DOMTracker::new(),
3791 #[cfg(feature = "bluetooth")]
3792 test_runner: Default::default(),
3793 webgl_chan,
3794 #[cfg(feature = "webxr")]
3795 webxr_registry,
3796 pending_image_callbacks: Default::default(),
3797 pending_layout_images: Default::default(),
3798 pending_images_for_rasterization: Default::default(),
3799 unminified_css_dir: DomRefCell::new(if unminify_css {
3800 Some(unminified_path("unminified-css"))
3801 } else {
3802 None
3803 }),
3804 local_script_source,
3805 test_worklet: Default::default(),
3806 paint_worklet: Default::default(),
3807 exists_mut_observer: Cell::new(false),
3808 paint_api,
3809 user_scripts,
3810 player_context,
3811 throttled: Cell::new(false),
3812 layout_marker: DomRefCell::new(Rc::new(Cell::new(true))),
3813 current_event: DomRefCell::new(None),
3814 theme: Cell::new(theme),
3815 trusted_types: Default::default(),
3816 reporting_observer_list: Default::default(),
3817 report_list: Default::default(),
3818 endpoints_list: Default::default(),
3819 script_window_proxies: ScriptThread::window_proxies(),
3820 has_pending_screenshot_readiness_request: Default::default(),
3821 visual_viewport: Default::default(),
3822 weak_script_thread,
3823 has_changed_visual_viewport_dimension: Default::default(),
3824 last_activation_timestamp: Cell::new(UserActivationTimestamp::PositiveInfinity),
3825 devtools_wants_updates: Default::default(),
3826 });
3827
3828 WindowBinding::Wrap::<crate::DomTypeHolder>(cx, win)
3829 }
3830
3831 pub(crate) fn pipeline_id(&self) -> PipelineId {
3832 self.as_global_scope().pipeline_id()
3833 }
3834
3835 pub(crate) fn live_devtools_updates(&self) -> bool {
3836 self.devtools_wants_updates.get()
3837 }
3838
3839 pub(crate) fn set_devtools_wants_updates(&self, value: bool) {
3840 self.devtools_wants_updates.set(value);
3841 }
3842
3843 pub(crate) fn cache_layout_value<T>(&self, value: T) -> LayoutValue<T>
3845 where
3846 T: Copy + MallocSizeOf,
3847 {
3848 LayoutValue::new(self.layout_marker.borrow().clone(), value)
3849 }
3850}
3851
3852#[derive(MallocSizeOf)]
3857pub(crate) struct LayoutValue<T: MallocSizeOf> {
3858 #[conditional_malloc_size_of]
3859 is_valid: Rc<Cell<bool>>,
3860 value: T,
3861}
3862
3863#[expect(unsafe_code)]
3864unsafe impl<T: JSTraceable + MallocSizeOf> JSTraceable for LayoutValue<T> {
3865 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
3866 unsafe { self.value.trace(trc) };
3867 }
3868}
3869
3870impl<T: Copy + MallocSizeOf> LayoutValue<T> {
3871 fn new(marker: Rc<Cell<bool>>, value: T) -> Self {
3872 LayoutValue {
3873 is_valid: marker,
3874 value,
3875 }
3876 }
3877
3878 pub(crate) fn get(&self) -> Result<T, ()> {
3880 if self.is_valid.get() {
3881 return Ok(self.value);
3882 }
3883 Err(())
3884 }
3885}
3886
3887fn should_move_clip_rect(clip_rect: UntypedRect<Au>, new_viewport: UntypedRect<f32>) -> bool {
3888 let clip_rect = UntypedRect::new(
3889 Point2D::new(
3890 clip_rect.origin.x.to_f32_px(),
3891 clip_rect.origin.y.to_f32_px(),
3892 ),
3893 Size2D::new(
3894 clip_rect.size.width.to_f32_px(),
3895 clip_rect.size.height.to_f32_px(),
3896 ),
3897 );
3898
3899 static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5;
3903 let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE;
3904
3905 (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width ||
3906 (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width ||
3907 (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height ||
3908 (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height
3909}
3910
3911impl Window {
3912 pub(crate) fn post_message(
3914 &self,
3915 target_origin: Option<ImmutableOrigin>,
3916 source_origin: ImmutableOrigin,
3917 source: &WindowProxy,
3918 data: StructuredSerializedData,
3919 ) {
3920 let this = Trusted::new(self);
3921 let source = Trusted::new(source);
3922 let task = task!(post_serialised_message: move |cx| {
3923 let this = this.root();
3924 let source = source.root();
3925 let document = this.Document();
3926
3927 if let Some(ref target_origin) = target_origin
3929 && !target_origin.same_origin(&*document.origin()) {
3930 return;
3931 }
3932
3933 let obj = this.reflector().get_jsobject();
3935 let mut realm = AutoRealm::new(cx, NonNull::new(obj.get()).unwrap());
3936 let cx = &mut *realm;
3937 rooted!(&in(cx) let mut message_clone = UndefinedValue());
3938 if let Ok(ports) = structuredclone::read(cx, this.upcast(), data, message_clone.handle_mut()) {
3939 MessageEvent::dispatch_jsval(
3941 cx,
3942 this.upcast(),
3943 this.upcast(),
3944 message_clone.handle(),
3945 Some(&source_origin.ascii_serialization()),
3946 Some(&*source),
3947 ports,
3948 );
3949 } else {
3950 MessageEvent::dispatch_error(
3952 cx,
3953 this.upcast(),
3954 this.upcast(),
3955 );
3956 }
3957 });
3958 self.as_global_scope()
3960 .task_manager()
3961 .dom_manipulation_task_source()
3962 .queue(task);
3963 }
3964}
3965
3966#[derive(Clone, MallocSizeOf)]
3967pub(crate) struct CSSErrorReporter {
3968 pub(crate) pipelineid: PipelineId,
3969 pub(crate) script_chan: GenericSender<ScriptThreadMessage>,
3970}
3971unsafe_no_jsmanaged_fields!(CSSErrorReporter);
3972
3973impl ParseErrorReporter for CSSErrorReporter {
3974 fn report_error(
3975 &self,
3976 url: &UrlExtraData,
3977 location: SourceLocation,
3978 error: ContextualParseError,
3979 ) {
3980 if log_enabled!(log::Level::Info) {
3981 info!(
3982 "Url:\t{}\n{}:{} {}",
3983 url.0.as_str(),
3984 location.line,
3985 location.column,
3986 error
3987 )
3988 }
3989
3990 let _ = self.script_chan.send(ScriptThreadMessage::ReportCSSError(
3992 self.pipelineid,
3993 url.0.to_string(),
3994 location.line,
3995 location.column,
3996 error.to_string(),
3997 ));
3998 }
3999}
4000
4001fn is_named_element_with_name_attribute(elem: &Element) -> bool {
4002 let type_ = match elem.upcast::<Node>().type_id() {
4003 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
4004 _ => return false,
4005 };
4006 matches!(
4007 type_,
4008 HTMLElementTypeId::HTMLEmbedElement |
4009 HTMLElementTypeId::HTMLFormElement |
4010 HTMLElementTypeId::HTMLImageElement |
4011 HTMLElementTypeId::HTMLObjectElement
4012 )
4013}
4014
4015fn is_named_element_with_id_attribute(elem: &Element) -> bool {
4016 elem.is_html_element()
4017}
4018
4019#[expect(unsafe_code)]
4020#[unsafe(no_mangle)]
4021unsafe extern "C" fn dump_js_stack(cx: *mut RawJSContext) {
4023 unsafe {
4024 DumpJSStack(cx, true, false, false);
4025 }
4026}
4027
4028impl WindowHelpers for Window {
4029 fn create_named_properties_object(
4030 cx: &mut JSContext,
4031 proto: HandleObject,
4032 object: MutableHandleObject,
4033 ) {
4034 Self::create_named_properties_object(cx, proto, object)
4035 }
4036}