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::rc::{Rc, Weak};
14use std::sync::Arc;
15use std::time::{Duration, Instant};
16
17use app_units::Au;
18use base::cross_process_instant::CrossProcessInstant;
19use base::generic_channel::{self, GenericCallback, GenericSender};
20use base::id::{BrowsingContextId, PipelineId, WebViewId};
21use base64::Engine;
22#[cfg(feature = "bluetooth")]
23use bluetooth_traits::BluetoothRequest;
24use canvas_traits::webgl::WebGLChan;
25use constellation_traits::{
26 LoadData, LoadOrigin, NavigationHistoryBehavior, ScreenshotReadinessResponse,
27 ScriptToConstellationChan, ScriptToConstellationMessage, StructuredSerializedData,
28 WindowSizeType,
29};
30use content_security_policy::Violation;
31use content_security_policy::sandboxing_directive::SandboxingFlagSet;
32use crossbeam_channel::{Sender, unbounded};
33use cssparser::SourceLocation;
34use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
35use dom_struct::dom_struct;
36use embedder_traits::user_contents::UserScript;
37use embedder_traits::{
38 AlertResponse, ConfirmResponse, EmbedderMsg, JavaScriptEvaluationError, PromptResponse,
39 ScriptToEmbedderChan, SimpleDialogRequest, Theme, UntrustedNodeAddress, ViewportDetails,
40 WebDriverJSResult, WebDriverLoadStatus,
41};
42use euclid::default::Rect as UntypedRect;
43use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
44use fonts::{CspViolationHandler, FontContext, NetworkTimingHandler, WebFontDocumentContext};
45use js::context::JSContext;
46use js::glue::DumpJSStack;
47use js::jsapi::{
48 GCReason, Heap, JS_GC, JSAutoRealm, JSContext as RawJSContext, JSObject, JSPROP_ENUMERATE,
49};
50use js::jsval::{NullValue, UndefinedValue};
51use js::realm::CurrentRealm;
52use js::rust::wrappers::JS_DefineProperty;
53use js::rust::{
54 CustomAutoRooter, CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleObject,
55 MutableHandleValue,
56};
57use layout_api::{
58 BoxAreaType, CSSPixelRectIterator, ElementsFromPointFlags, ElementsFromPointResult,
59 FragmentType, Layout, LayoutImageDestination, PendingImage, PendingImageState,
60 PendingRasterizationImage, PhysicalSides, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest,
61 ReflowRequestRestyle, ReflowStatistics, RestyleReason, ScrollContainerQueryFlags,
62 ScrollContainerResponse, TrustedNodeAddress, combine_id_with_fragment_type,
63};
64use malloc_size_of::MallocSizeOf;
65use media::WindowGLContext;
66use net_traits::image_cache::{
67 ImageCache, ImageCacheResponseCallback, ImageCacheResponseMessage, ImageLoadListener,
68 ImageResponse, PendingImageId, PendingImageResponse, RasterizationCompleteResponse,
69};
70use net_traits::request::Referrer;
71use net_traits::{ResourceFetchTiming, ResourceThreads};
72use num_traits::ToPrimitive;
73use paint_api::{CrossProcessPaintApi, PinchZoomInfos};
74use profile_traits::generic_channel as ProfiledGenericChannel;
75use profile_traits::mem::ProfilerChan as MemProfilerChan;
76use profile_traits::time::ProfilerChan as TimeProfilerChan;
77use rustc_hash::{FxBuildHasher, FxHashMap};
78use script_bindings::codegen::GenericBindings::WindowBinding::ScrollToOptions;
79use script_bindings::conversions::SafeToJSValConvertible;
80use script_bindings::interfaces::WindowHelpers;
81use script_bindings::root::Root;
82use script_traits::{ConstellationInputEvent, ScriptThreadMessage};
83use selectors::attr::CaseSensitivity;
84use servo_arc::Arc as ServoArc;
85use servo_config::pref;
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 url::Position;
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::cell::{DomRefCell, Ref};
107use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
108 DocumentMethods, DocumentReadyState, NamedPropertyValue,
109};
110use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
111use crate::dom::bindings::codegen::Bindings::HistoryBinding::History_Binding::HistoryMethods;
112use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
113 ImageBitmapOptions, ImageBitmapSource,
114};
115use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryList_Binding::MediaQueryListMethods;
116use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
117use crate::dom::bindings::codegen::Bindings::RequestBinding::{RequestInfo, RequestInit};
118use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
119use crate::dom::bindings::codegen::Bindings::WindowBinding::{
120 self, DeferredRequestInit, FrameRequestCallback, ScrollBehavior, WindowMethods,
121 WindowPostMessageOptions,
122};
123use crate::dom::bindings::codegen::UnionTypes::{
124 RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
125};
126use crate::dom::bindings::error::{
127 Error, ErrorInfo, ErrorResult, Fallible, javascript_error_info_from_error_info,
128};
129use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
130use crate::dom::bindings::num::Finite;
131use crate::dom::bindings::refcounted::Trusted;
132use crate::dom::bindings::reflector::{DomGlobal, DomObject};
133use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
134use crate::dom::bindings::str::{DOMString, USVString};
135use crate::dom::bindings::structuredclone;
136use crate::dom::bindings::trace::{CustomTraceable, JSTraceable, RootedTraceableBox};
137use crate::dom::bindings::utils::GlobalStaticData;
138use crate::dom::bindings::weakref::DOMTracker;
139#[cfg(feature = "bluetooth")]
140use crate::dom::bluetooth::BluetoothExtraPermissionData;
141use crate::dom::cookiestore::CookieStore;
142use crate::dom::crypto::Crypto;
143use crate::dom::csp::GlobalCspReporting;
144use crate::dom::css::cssstyledeclaration::{
145 CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner,
146};
147use crate::dom::customelementregistry::CustomElementRegistry;
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::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
171use crate::dom::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::trustedtypepolicyfactory::TrustedTypePolicyFactory;
180use crate::dom::types::{ImageBitmap, MouseEvent, UIEvent};
181use crate::dom::useractivation::UserActivationTimestamp;
182use crate::dom::visualviewport::{VisualViewport, VisualViewportChanges};
183use crate::dom::webgl::webglrenderingcontext::WebGLCommandSender;
184#[cfg(feature = "webgpu")]
185use crate::dom::webgpu::identityhub::IdentityHub;
186use crate::dom::windowproxy::{WindowProxy, WindowProxyHandler};
187use crate::dom::worklet::Worklet;
188use crate::dom::workletglobalscope::WorkletGlobalScopeType;
189use crate::layout_image::fetch_image_for_layout;
190use crate::messaging::{MainThreadScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
191use crate::microtask::{Microtask, UserMicrotask};
192use crate::network_listener::{ResourceTimingListener, submit_timing};
193use crate::realms::{InRealm, enter_realm};
194use crate::script_runtime::{CanGc, JSContext as SafeJSContext, Runtime};
195use crate::script_thread::ScriptThread;
196use crate::script_window_proxies::ScriptWindowProxies;
197use crate::task_source::SendableTaskSource;
198use crate::timers::{IsInterval, TimerCallback};
199use crate::unminify::unminified_path;
200use crate::webdriver_handlers::{find_node_by_unique_id_in_document, jsval_to_webdriver};
201use crate::{fetch, window_named_properties};
202
203#[derive(MallocSizeOf)]
208pub struct PendingImageCallback(
209 #[ignore_malloc_size_of = "dyn Fn is currently impossible to measure"]
210 #[expect(clippy::type_complexity)]
211 Box<dyn Fn(PendingImageResponse, &mut js::context::JSContext) + 'static>,
212);
213
214#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
216enum WindowState {
217 Alive,
218 Zombie, }
220
221const INITIAL_REFLOW_DELAY: Duration = Duration::from_millis(200);
224
225#[derive(Clone, Copy, MallocSizeOf)]
236enum LayoutBlocker {
237 WaitingForParse,
239 Parsing(Instant),
241 FiredLoadEventOrParsingTimerExpired,
245}
246
247impl LayoutBlocker {
248 fn layout_blocked(&self) -> bool {
249 !matches!(self, Self::FiredLoadEventOrParsingTimerExpired)
250 }
251}
252
253#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf, PartialEq)]
256pub(crate) struct OngoingNavigation(u32);
257
258type PendingImageRasterizationKey = (PendingImageId, DeviceIntSize);
259
260#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
264#[derive(JSTraceable, MallocSizeOf)]
265struct PendingLayoutImageAncillaryData {
266 node: Dom<Node>,
267 #[no_trace]
268 destination: LayoutImageDestination,
269}
270
271#[dom_struct]
272pub(crate) struct Window {
273 globalscope: GlobalScope,
274
275 #[ignore_malloc_size_of = "Weak does not need to be accounted"]
279 #[no_trace]
280 weak_script_thread: Weak<ScriptThread>,
281
282 #[no_trace]
286 webview_id: WebViewId,
287 script_chan: Sender<MainThreadScriptMsg>,
288 #[no_trace]
289 #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"]
290 layout: RefCell<Box<dyn Layout>>,
291 navigator: MutNullableDom<Navigator>,
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 status: DomRefCell<DOMString>,
309 trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
310
311 ongoing_navigation: Cell<OngoingNavigation>,
314
315 #[no_trace]
318 devtools_markers: DomRefCell<HashSet<TimelineMarkerType>>,
319 #[no_trace]
320 devtools_marker_sender: DomRefCell<Option<GenericSender<Option<TimelineMarker>>>>,
321
322 #[no_trace]
324 unhandled_resize_event: DomRefCell<Option<(ViewportDetails, WindowSizeType)>>,
325
326 #[no_trace]
328 theme: Cell<Theme>,
329
330 #[no_trace]
332 parent_info: Option<PipelineId>,
333
334 dom_static: GlobalStaticData,
336
337 #[conditional_malloc_size_of]
339 js_runtime: DomRefCell<Option<Rc<Runtime>>>,
340
341 #[no_trace]
343 viewport_details: Cell<ViewportDetails>,
344
345 #[no_trace]
347 #[cfg(feature = "bluetooth")]
348 bluetooth_thread: GenericSender<BluetoothRequest>,
349
350 #[cfg(feature = "bluetooth")]
351 bluetooth_extra_permission_data: BluetoothExtraPermissionData,
352
353 #[no_trace]
357 layout_blocker: Cell<LayoutBlocker>,
358
359 #[no_trace]
361 webdriver_script_chan: DomRefCell<Option<GenericSender<WebDriverJSResult>>>,
362
363 #[no_trace]
365 webdriver_load_status_sender: RefCell<Option<GenericSender<WebDriverLoadStatus>>>,
366
367 current_state: Cell<WindowState>,
369
370 error_reporter: CSSErrorReporter,
371
372 media_query_lists: DOMTracker<MediaQueryList>,
374
375 #[cfg(feature = "bluetooth")]
376 test_runner: MutNullableDom<TestRunner>,
377
378 #[ignore_malloc_size_of = "channels are hard"]
380 #[no_trace]
381 webgl_chan: Option<WebGLChan>,
382
383 #[ignore_malloc_size_of = "defined in webxr"]
384 #[no_trace]
385 #[cfg(feature = "webxr")]
386 webxr_registry: Option<webxr_api::Registry>,
387
388 #[no_trace]
392 pending_image_callbacks: DomRefCell<FxHashMap<PendingImageId, Vec<PendingImageCallback>>>,
393
394 pending_layout_images: DomRefCell<
399 HashMapTracedValues<PendingImageId, Vec<PendingLayoutImageAncillaryData>, FxBuildHasher>,
400 >,
401
402 pending_images_for_rasterization: DomRefCell<
406 HashMapTracedValues<PendingImageRasterizationKey, Vec<Dom<Node>>, FxBuildHasher>,
407 >,
408
409 unminified_css_dir: DomRefCell<Option<String>>,
412
413 local_script_source: Option<String>,
415
416 test_worklet: MutNullableDom<Worklet>,
418 paint_worklet: MutNullableDom<Worklet>,
420
421 exists_mut_observer: Cell<bool>,
423
424 #[no_trace]
426 paint_api: CrossProcessPaintApi,
427
428 has_sent_idle_message: Cell<bool>,
431
432 unminify_css: bool,
434
435 #[no_trace]
438 #[conditional_malloc_size_of]
439 user_scripts: Rc<Vec<UserScript>>,
440
441 #[ignore_malloc_size_of = "defined in script_thread"]
443 #[no_trace]
444 player_context: WindowGLContext,
445
446 throttled: Cell<bool>,
447
448 #[conditional_malloc_size_of]
452 layout_marker: DomRefCell<Rc<Cell<bool>>>,
453
454 current_event: DomRefCell<Option<Dom<Event>>>,
456
457 reporting_observer_list: DomRefCell<Vec<DomRoot<ReportingObserver>>>,
459
460 report_list: DomRefCell<Vec<Report>>,
462
463 #[no_trace]
465 endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
466
467 #[conditional_malloc_size_of]
469 script_window_proxies: Rc<ScriptWindowProxies>,
470
471 has_pending_screenshot_readiness_request: Cell<bool>,
473
474 visual_viewport: MutNullableDom<VisualViewport>,
477
478 has_changed_visual_viewport_dimension: Cell<bool>,
480
481 #[no_trace]
483 last_activation_timestamp: Cell<UserActivationTimestamp>,
484}
485
486impl Window {
487 pub(crate) fn script_thread(&self) -> Rc<ScriptThread> {
488 Weak::upgrade(&self.weak_script_thread)
489 .expect("Weak reference should always be upgradable when a ScriptThread is running")
490 }
491
492 pub(crate) fn webview_id(&self) -> WebViewId {
493 self.webview_id
494 }
495
496 pub(crate) fn as_global_scope(&self) -> &GlobalScope {
497 self.upcast::<GlobalScope>()
498 }
499
500 pub(crate) fn layout(&self) -> Ref<'_, Box<dyn Layout>> {
501 self.layout.borrow()
502 }
503
504 pub(crate) fn layout_mut(&self) -> RefMut<'_, Box<dyn Layout>> {
505 self.layout.borrow_mut()
506 }
507
508 pub(crate) fn get_exists_mut_observer(&self) -> bool {
509 self.exists_mut_observer.get()
510 }
511
512 pub(crate) fn set_exists_mut_observer(&self) {
513 self.exists_mut_observer.set(true);
514 }
515
516 #[expect(unsafe_code)]
517 pub(crate) fn clear_js_runtime_for_script_deallocation(&self) {
518 self.as_global_scope()
519 .remove_web_messaging_and_dedicated_workers_infra();
520 unsafe {
521 *self.js_runtime.borrow_for_script_deallocation() = None;
522 self.window_proxy.set(None);
523 self.current_state.set(WindowState::Zombie);
524 self.as_global_scope()
525 .task_manager()
526 .cancel_all_tasks_and_ignore_future_tasks();
527 }
528 }
529
530 pub(crate) fn discard_browsing_context(&self) {
533 let proxy = match self.window_proxy.get() {
534 Some(proxy) => proxy,
535 None => panic!("Discarding a BC from a window that has none"),
536 };
537 proxy.discard_browsing_context();
538 self.as_global_scope()
542 .task_manager()
543 .cancel_all_tasks_and_ignore_future_tasks();
544 }
545
546 pub(crate) fn time_profiler_chan(&self) -> &TimeProfilerChan {
548 self.globalscope.time_profiler_chan()
549 }
550
551 pub(crate) fn origin(&self) -> &MutableOrigin {
552 self.globalscope.origin()
553 }
554
555 #[expect(unsafe_code)]
556 pub(crate) fn get_cx(&self) -> SafeJSContext {
557 unsafe { SafeJSContext::from_ptr(js::rust::Runtime::get().unwrap().as_ptr()) }
558 }
559
560 pub(crate) fn get_js_runtime(&self) -> Ref<'_, Option<Rc<Runtime>>> {
561 self.js_runtime.borrow()
562 }
563
564 pub(crate) fn main_thread_script_chan(&self) -> &Sender<MainThreadScriptMsg> {
565 &self.script_chan
566 }
567
568 pub(crate) fn parent_info(&self) -> Option<PipelineId> {
569 self.parent_info
570 }
571
572 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
573 let (sender, receiver) = unbounded();
574 (
575 ScriptEventLoopSender::MainThread(sender),
576 ScriptEventLoopReceiver::MainThread(receiver),
577 )
578 }
579
580 pub(crate) fn event_loop_sender(&self) -> ScriptEventLoopSender {
581 ScriptEventLoopSender::MainThread(self.script_chan.clone())
582 }
583
584 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
585 self.image_cache.clone()
586 }
587
588 pub(crate) fn window_proxy(&self) -> DomRoot<WindowProxy> {
590 self.window_proxy.get().unwrap()
591 }
592
593 pub(crate) fn append_reporting_observer(&self, reporting_observer: DomRoot<ReportingObserver>) {
594 self.reporting_observer_list
595 .borrow_mut()
596 .push(reporting_observer);
597 }
598
599 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
600 let index = {
601 let list = self.reporting_observer_list.borrow();
602 list.iter()
603 .position(|observer| &**observer == reporting_observer)
604 };
605
606 if let Some(index) = index {
607 self.reporting_observer_list.borrow_mut().remove(index);
608 }
609 }
610
611 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
612 self.reporting_observer_list.borrow().clone()
613 }
614
615 pub(crate) fn append_report(&self, report: Report) {
616 self.report_list.borrow_mut().push(report);
617 let trusted_window = Trusted::new(self);
618 self.upcast::<GlobalScope>()
619 .task_manager()
620 .dom_manipulation_task_source()
621 .queue(task!(send_to_reporting_endpoints: move || {
622 let window = trusted_window.root();
623 let reports = std::mem::take(&mut *window.report_list.borrow_mut());
624 window.upcast::<GlobalScope>().send_reports_to_endpoints(
625 reports,
626 window.endpoints_list.borrow().clone(),
627 );
628 }));
629 }
630
631 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
632 self.report_list.borrow().clone()
633 }
634
635 pub(crate) fn set_endpoints_list(&self, endpoints: Vec<ReportingEndpoint>) {
636 *self.endpoints_list.borrow_mut() = endpoints;
637 }
638
639 pub(crate) fn undiscarded_window_proxy(&self) -> Option<DomRoot<WindowProxy>> {
642 self.window_proxy.get().and_then(|window_proxy| {
643 if window_proxy.is_browsing_context_discarded() {
644 None
645 } else {
646 Some(window_proxy)
647 }
648 })
649 }
650
651 pub(crate) fn top_level_document_if_local(&self) -> Option<DomRoot<Document>> {
656 if self.is_top_level() {
657 return Some(self.Document());
658 }
659
660 let window_proxy = self.undiscarded_window_proxy()?;
661 self.script_window_proxies
662 .find_window_proxy(window_proxy.webview_id().into())?
663 .document()
664 }
665
666 #[cfg(feature = "bluetooth")]
667 pub(crate) fn bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
668 self.bluetooth_thread.clone()
669 }
670
671 #[cfg(feature = "bluetooth")]
672 pub(crate) fn bluetooth_extra_permission_data(&self) -> &BluetoothExtraPermissionData {
673 &self.bluetooth_extra_permission_data
674 }
675
676 pub(crate) fn css_error_reporter(&self) -> &CSSErrorReporter {
677 &self.error_reporter
678 }
679
680 pub(crate) fn webgl_chan(&self) -> Option<WebGLCommandSender> {
681 self.webgl_chan
682 .as_ref()
683 .map(|chan| WebGLCommandSender::new(chan.clone()))
684 }
685
686 #[cfg(feature = "webxr")]
687 pub(crate) fn webxr_registry(&self) -> Option<webxr_api::Registry> {
688 self.webxr_registry.clone()
689 }
690
691 fn new_paint_worklet(&self, can_gc: CanGc) -> DomRoot<Worklet> {
692 debug!("Creating new paint worklet.");
693 Worklet::new(self, WorkletGlobalScopeType::Paint, can_gc)
694 }
695
696 pub(crate) fn register_image_cache_listener(
697 &self,
698 id: PendingImageId,
699 callback: impl Fn(PendingImageResponse, &mut js::context::JSContext) + 'static,
700 ) -> ImageCacheResponseCallback {
701 self.pending_image_callbacks
702 .borrow_mut()
703 .entry(id)
704 .or_default()
705 .push(PendingImageCallback(Box::new(callback)));
706
707 let image_cache_sender = self.image_cache_sender.clone();
708 Box::new(move |message| {
709 let _ = image_cache_sender.send(message);
710 })
711 }
712
713 fn pending_layout_image_notification(&self, response: PendingImageResponse) {
714 let mut images = self.pending_layout_images.borrow_mut();
715 let nodes = images.entry(response.id);
716 let nodes = match nodes {
717 Entry::Occupied(nodes) => nodes,
718 Entry::Vacant(_) => return,
719 };
720 if matches!(
721 response.response,
722 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode
723 ) {
724 for ancillary_data in nodes.get() {
725 match ancillary_data.destination {
726 LayoutImageDestination::BoxTreeConstruction => {
727 ancillary_data.node.dirty(NodeDamage::Other);
728 },
729 LayoutImageDestination::DisplayListBuilding => {
730 self.layout().set_needs_new_display_list();
731 },
732 }
733 }
734 }
735
736 match response.response {
737 ImageResponse::MetadataLoaded(_) => {},
738 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode => {
739 nodes.remove();
740 },
741 }
742 }
743
744 pub(crate) fn handle_image_rasterization_complete_notification(
745 &self,
746 response: RasterizationCompleteResponse,
747 ) {
748 let mut images = self.pending_images_for_rasterization.borrow_mut();
749 let nodes = images.entry((response.image_id, response.requested_size));
750 let nodes = match nodes {
751 Entry::Occupied(nodes) => nodes,
752 Entry::Vacant(_) => return,
753 };
754 for node in nodes.get() {
755 node.dirty(NodeDamage::Other);
756 }
757 nodes.remove();
758 }
759
760 pub(crate) fn pending_image_notification(
761 &self,
762 response: PendingImageResponse,
763 cx: &mut js::context::JSContext,
764 ) {
765 let mut images = std::mem::take(&mut *self.pending_image_callbacks.borrow_mut());
770 let Entry::Occupied(callbacks) = images.entry(response.id) else {
771 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
772 return;
773 };
774
775 for callback in callbacks.get() {
776 callback.0(response.clone(), cx);
777 }
778
779 match response.response {
780 ImageResponse::MetadataLoaded(_) => {},
781 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode => {
782 callbacks.remove();
783 },
784 }
785
786 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
787 }
788
789 pub(crate) fn paint_api(&self) -> &CrossProcessPaintApi {
790 &self.paint_api
791 }
792
793 pub(crate) fn userscripts(&self) -> &[UserScript] {
794 &self.user_scripts
795 }
796
797 pub(crate) fn get_player_context(&self) -> WindowGLContext {
798 self.player_context.clone()
799 }
800
801 pub(crate) fn dispatch_event_with_target_override(&self, event: &Event, can_gc: CanGc) {
803 event.dispatch(self.upcast(), true, can_gc);
804 }
805
806 pub(crate) fn font_context(&self) -> &Arc<FontContext> {
807 self.as_global_scope()
808 .font_context()
809 .expect("A `Window` should always have a `FontContext`")
810 }
811
812 pub(crate) fn ongoing_navigation(&self) -> OngoingNavigation {
813 self.ongoing_navigation.get()
814 }
815
816 pub(crate) fn set_ongoing_navigation(&self) -> OngoingNavigation {
818 let new_value = self.ongoing_navigation.get().0.wrapping_add(1);
822
823 self.ongoing_navigation.set(OngoingNavigation(new_value));
830
831 OngoingNavigation(new_value)
833 }
834
835 fn stop_loading(&self, cx: &mut js::context::JSContext) {
837 let doc = self.Document();
839
840 self.set_ongoing_navigation();
850
851 doc.abort(cx);
853 }
854
855 fn destroy_top_level_traversable(&self, cx: &mut js::context::JSContext) {
857 let document = self.Document();
863 document.destroy_document_and_its_descendants(cx);
865 self.send_to_constellation(ScriptToConstellationMessage::DiscardTopLevelBrowsingContext);
867 }
868
869 fn definitely_close(&self, cx: &mut js::context::JSContext) {
871 let document = self.Document();
872 if !document.check_if_unloading_is_cancelled(false, CanGc::from_cx(cx)) {
877 return;
878 }
879 document.unload(false, CanGc::from_cx(cx));
883 self.destroy_top_level_traversable(cx);
885 }
886
887 fn cannot_show_simple_dialogs(&self) -> bool {
889 if self
892 .Document()
893 .has_active_sandboxing_flag(SandboxingFlagSet::SANDBOXED_MODALS_FLAG)
894 {
895 return true;
896 }
897
898 false
917 }
918
919 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
920 self.script_thread().perform_a_microtask_checkpoint(cx);
921 }
922
923 pub(crate) fn web_font_context(&self) -> WebFontDocumentContext {
924 let global = self.as_global_scope();
925 WebFontDocumentContext {
926 policy_container: global.policy_container(),
927 request_client: global.request_client(),
928 document_url: global.api_base_url(),
929 has_trustworthy_ancestor_origin: global.has_trustworthy_ancestor_origin(),
930 insecure_requests_policy: global.insecure_requests_policy(),
931 csp_handler: Box::new(FontCspHandler {
932 global: Trusted::new(global),
933 task_source: global
934 .task_manager()
935 .dom_manipulation_task_source()
936 .to_sendable(),
937 }),
938 network_timing_handler: Box::new(FontNetworkTimingHandler {
939 global: Trusted::new(global),
940 task_source: global
941 .task_manager()
942 .dom_manipulation_task_source()
943 .to_sendable(),
944 }),
945 }
946 }
947
948 #[expect(unsafe_code)]
949 pub(crate) fn gc(&self) {
950 unsafe {
951 JS_GC(*self.get_cx(), GCReason::API);
952 }
953 }
954}
955
956#[derive(Debug)]
957struct FontCspHandler {
958 global: Trusted<GlobalScope>,
959 task_source: SendableTaskSource,
960}
961
962impl CspViolationHandler for FontCspHandler {
963 fn process_violations(&self, violations: Vec<Violation>) {
964 let global = self.global.clone();
965 self.task_source.queue(task!(csp_violation: move || {
966 global.root().report_csp_violations(violations, None, None);
967 }));
968 }
969
970 fn clone(&self) -> Box<dyn CspViolationHandler> {
971 Box::new(Self {
972 global: self.global.clone(),
973 task_source: self.task_source.clone(),
974 })
975 }
976}
977
978#[derive(Debug)]
979struct FontNetworkTimingHandler {
980 global: Trusted<GlobalScope>,
981 task_source: SendableTaskSource,
982}
983
984impl NetworkTimingHandler for FontNetworkTimingHandler {
985 fn submit_timing(&self, url: ServoUrl, response: ResourceFetchTiming) {
986 let global = self.global.clone();
987 self.task_source.queue(task!(network_timing: move || {
988 submit_timing(
989 &FontFetchListener {
990 url,
991 global
992 },
993 &Ok(()),
994 &response,
995 CanGc::note(),
996 );
997 }));
998 }
999
1000 fn clone(&self) -> Box<dyn NetworkTimingHandler> {
1001 Box::new(Self {
1002 global: self.global.clone(),
1003 task_source: self.task_source.clone(),
1004 })
1005 }
1006}
1007
1008#[derive(Debug)]
1009struct FontFetchListener {
1010 global: Trusted<GlobalScope>,
1011 url: ServoUrl,
1012}
1013
1014impl ResourceTimingListener for FontFetchListener {
1015 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1016 (InitiatorType::Css, self.url.clone())
1017 }
1018
1019 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1020 self.global.root()
1021 }
1022}
1023
1024pub(crate) fn base64_btoa(input: DOMString) -> Fallible<DOMString> {
1026 if input.str().chars().any(|c: char| c > '\u{FF}') {
1030 Err(Error::InvalidCharacter(None))
1031 } else {
1032 let octets = input
1037 .str()
1038 .chars()
1039 .map(|c: char| c as u8)
1040 .collect::<Vec<u8>>();
1041
1042 let config =
1045 base64::engine::general_purpose::GeneralPurposeConfig::new().with_encode_padding(true);
1046 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
1047 Ok(DOMString::from(engine.encode(octets)))
1048 }
1049}
1050
1051pub(crate) fn base64_atob(input: DOMString) -> Fallible<DOMString> {
1053 fn is_html_space(c: char) -> bool {
1055 HTML_SPACE_CHARACTERS.contains(&c)
1056 }
1057 let without_spaces = input
1058 .str()
1059 .chars()
1060 .filter(|&c| !is_html_space(c))
1061 .collect::<String>();
1062 let mut input = &*without_spaces;
1063
1064 if input.len() % 4 == 0 {
1068 if input.ends_with("==") {
1069 input = &input[..input.len() - 2]
1070 } else if input.ends_with('=') {
1071 input = &input[..input.len() - 1]
1072 }
1073 }
1074
1075 if input.len() % 4 == 1 {
1078 return Err(Error::InvalidCharacter(None));
1079 }
1080
1081 if input
1089 .chars()
1090 .any(|c| c != '+' && c != '/' && !c.is_alphanumeric())
1091 {
1092 return Err(Error::InvalidCharacter(None));
1093 }
1094
1095 let config = base64::engine::general_purpose::GeneralPurposeConfig::new()
1096 .with_decode_padding_mode(base64::engine::DecodePaddingMode::RequireNone)
1097 .with_decode_allow_trailing_bits(true);
1098 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
1099
1100 let data = engine
1101 .decode(input)
1102 .map_err(|_| Error::InvalidCharacter(None))?;
1103 Ok(data.iter().map(|&b| b as char).collect::<String>().into())
1104}
1105
1106impl WindowMethods<crate::DomTypeHolder> for Window {
1107 fn Alert_(&self) {
1109 self.Alert(DOMString::new());
1112 }
1113
1114 fn Alert(&self, mut message: DOMString) {
1116 if self.cannot_show_simple_dialogs() {
1118 return;
1119 }
1120
1121 message.normalize_newlines();
1125
1126 {
1137 let stderr = stderr();
1141 let mut stderr = stderr.lock();
1142 let stdout = stdout();
1143 let mut stdout = stdout.lock();
1144 writeln!(&mut stdout, "\nALERT: {message}").unwrap();
1145 stdout.flush().unwrap();
1146 stderr.flush().unwrap();
1147 }
1148
1149 let (sender, receiver) =
1150 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1151 let dialog = SimpleDialogRequest::Alert {
1152 id: self.Document().embedder_controls().next_control_id(),
1153 message: message.to_string(),
1154 response_sender: sender,
1155 };
1156 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1157 receiver.recv().unwrap_or_else(|_| {
1158 debug!("Alert dialog was cancelled or failed to show.");
1160 AlertResponse::Ok
1161 });
1162
1163 }
1166
1167 fn Confirm(&self, mut message: DOMString) -> bool {
1169 if self.cannot_show_simple_dialogs() {
1171 return false;
1172 }
1173
1174 message.normalize_newlines();
1176
1177 let (sender, receiver) =
1183 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1184 let dialog = SimpleDialogRequest::Confirm {
1185 id: self.Document().embedder_controls().next_control_id(),
1186 message: message.to_string(),
1187 response_sender: sender,
1188 };
1189 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1190
1191 match receiver.recv() {
1207 Ok(ConfirmResponse::Ok) => true,
1208 Ok(ConfirmResponse::Cancel) => false,
1209 Err(_) => {
1210 warn!("Confirm dialog was cancelled or failed to show.");
1211 false
1212 },
1213 }
1214 }
1215
1216 fn Prompt(&self, mut message: DOMString, default: DOMString) -> Option<DOMString> {
1218 if self.cannot_show_simple_dialogs() {
1220 return None;
1221 }
1222
1223 message.normalize_newlines();
1225
1226 let (sender, receiver) =
1234 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1235 let dialog = SimpleDialogRequest::Prompt {
1236 id: self.Document().embedder_controls().next_control_id(),
1237 message: message.to_string(),
1238 default: default.to_string(),
1239 response_sender: sender,
1240 };
1241 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1242
1243 match receiver.recv() {
1262 Ok(PromptResponse::Ok(input)) => Some(input.into()),
1263 Ok(PromptResponse::Cancel) => None,
1264 Err(_) => {
1265 warn!("Prompt dialog was cancelled or failed to show.");
1266 None
1267 },
1268 }
1269 }
1270
1271 fn Stop(&self, cx: &mut js::context::JSContext) {
1273 self.stop_loading(cx);
1278 }
1279
1280 fn Focus(&self) {
1282 let current = match self.undiscarded_window_proxy() {
1286 Some(proxy) => proxy,
1287 None => return,
1288 };
1289
1290 current.focus();
1292
1293 }
1299
1300 fn Blur(&self) {
1302 }
1305
1306 fn Open(
1308 &self,
1309 url: USVString,
1310 target: DOMString,
1311 features: DOMString,
1312 can_gc: CanGc,
1313 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
1314 self.window_proxy().open(url, target, features, can_gc)
1315 }
1316
1317 fn GetOpener(&self, cx: &mut CurrentRealm, mut retval: MutableHandleValue) -> Fallible<()> {
1319 let current = match self.window_proxy.get() {
1321 Some(proxy) => proxy,
1322 None => {
1324 retval.set(NullValue());
1325 return Ok(());
1326 },
1327 };
1328 if current.is_browsing_context_discarded() {
1333 retval.set(NullValue());
1334 return Ok(());
1335 }
1336 current.opener(cx, retval);
1338 Ok(())
1339 }
1340
1341 #[expect(unsafe_code)]
1342 fn SetOpener(&self, cx: SafeJSContext, value: HandleValue) -> ErrorResult {
1344 if value.is_null() {
1346 if let Some(proxy) = self.window_proxy.get() {
1347 proxy.disown();
1348 }
1349 return Ok(());
1350 }
1351 let obj = self.reflector().get_jsobject();
1353 unsafe {
1354 let result =
1355 JS_DefineProperty(*cx, obj, c"opener".as_ptr(), value, JSPROP_ENUMERATE as u32);
1356
1357 if result { Ok(()) } else { Err(Error::JSFailed) }
1358 }
1359 }
1360
1361 fn Closed(&self) -> bool {
1363 self.window_proxy
1364 .get()
1365 .map(|ref proxy| proxy.is_browsing_context_discarded() || proxy.is_closing())
1366 .unwrap_or(true)
1367 }
1368
1369 fn Close(&self) {
1371 let window_proxy = match self.window_proxy.get() {
1373 Some(proxy) => proxy,
1374 None => return,
1376 };
1377 if window_proxy.is_closing() {
1379 return;
1380 }
1381 if let Ok(history_length) = self.History().GetLength() {
1384 let is_auxiliary = window_proxy.is_auxiliary();
1385
1386 let is_script_closable = (self.is_top_level() && history_length == 1) ||
1388 is_auxiliary ||
1389 pref!(dom_allow_scripts_to_close_windows);
1390
1391 if is_script_closable {
1395 window_proxy.close();
1397
1398 let this = Trusted::new(self);
1400 let task = task!(window_close_browsing_context: move |cx| {
1401 let window = this.root();
1402 window.definitely_close(cx);
1403 });
1404 self.as_global_scope()
1405 .task_manager()
1406 .dom_manipulation_task_source()
1407 .queue(task);
1408 }
1409 }
1410 }
1411
1412 fn Document(&self) -> DomRoot<Document> {
1414 self.document
1415 .get()
1416 .expect("Document accessed before initialization.")
1417 }
1418
1419 fn History(&self) -> DomRoot<History> {
1421 self.history.or_init(|| History::new(self, CanGc::note()))
1422 }
1423
1424 fn IndexedDB(&self) -> DomRoot<IDBFactory> {
1426 self.upcast::<GlobalScope>().get_indexeddb()
1427 }
1428
1429 fn CustomElements(&self) -> DomRoot<CustomElementRegistry> {
1431 self.custom_element_registry
1432 .or_init(|| CustomElementRegistry::new(self, CanGc::note()))
1433 }
1434
1435 fn Location(&self) -> DomRoot<Location> {
1437 self.location.or_init(|| Location::new(self, CanGc::note()))
1438 }
1439
1440 fn SessionStorage(&self) -> DomRoot<Storage> {
1442 self.session_storage
1443 .or_init(|| Storage::new(self, WebStorageType::Session, CanGc::note()))
1444 }
1445
1446 fn LocalStorage(&self) -> DomRoot<Storage> {
1448 self.local_storage
1449 .or_init(|| Storage::new(self, WebStorageType::Local, CanGc::note()))
1450 }
1451
1452 fn CookieStore(&self, can_gc: CanGc) -> DomRoot<CookieStore> {
1454 self.global().cookie_store(can_gc)
1455 }
1456
1457 fn Crypto(&self) -> DomRoot<Crypto> {
1459 self.as_global_scope().crypto(CanGc::note())
1460 }
1461
1462 fn GetFrameElement(&self) -> Option<DomRoot<Element>> {
1464 let window_proxy = self.window_proxy.get()?;
1466
1467 let container = window_proxy.frame_element()?;
1469
1470 let container_doc = container.owner_document();
1472 let current_doc = GlobalScope::current()
1473 .expect("No current global object")
1474 .as_window()
1475 .Document();
1476 if !current_doc
1477 .origin()
1478 .same_origin_domain(container_doc.origin())
1479 {
1480 return None;
1481 }
1482 Some(DomRoot::from_ref(container))
1484 }
1485
1486 fn ReportError(&self, cx: SafeJSContext, error: HandleValue, can_gc: CanGc) {
1488 self.as_global_scope()
1489 .report_an_exception(cx, error, can_gc);
1490 }
1491
1492 fn Navigator(&self) -> DomRoot<Navigator> {
1494 self.navigator
1495 .or_init(|| Navigator::new(self, CanGc::note()))
1496 }
1497
1498 fn ClientInformation(&self) -> DomRoot<Navigator> {
1500 self.Navigator()
1501 }
1502
1503 fn SetTimeout(
1505 &self,
1506 _cx: SafeJSContext,
1507 callback: TrustedScriptOrStringOrFunction,
1508 timeout: i32,
1509 args: Vec<HandleValue>,
1510 can_gc: CanGc,
1511 ) -> Fallible<i32> {
1512 let callback = match callback {
1513 TrustedScriptOrStringOrFunction::String(i) => {
1514 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1515 },
1516 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1517 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1518 },
1519 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1520 };
1521 self.as_global_scope().set_timeout_or_interval(
1522 callback,
1523 args,
1524 Duration::from_millis(timeout.max(0) as u64),
1525 IsInterval::NonInterval,
1526 can_gc,
1527 )
1528 }
1529
1530 fn ClearTimeout(&self, handle: i32) {
1532 self.as_global_scope().clear_timeout_or_interval(handle);
1533 }
1534
1535 fn SetInterval(
1537 &self,
1538 _cx: SafeJSContext,
1539 callback: TrustedScriptOrStringOrFunction,
1540 timeout: i32,
1541 args: Vec<HandleValue>,
1542 can_gc: CanGc,
1543 ) -> Fallible<i32> {
1544 let callback = match callback {
1545 TrustedScriptOrStringOrFunction::String(i) => {
1546 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1547 },
1548 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1549 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1550 },
1551 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1552 };
1553 self.as_global_scope().set_timeout_or_interval(
1554 callback,
1555 args,
1556 Duration::from_millis(timeout.max(0) as u64),
1557 IsInterval::Interval,
1558 can_gc,
1559 )
1560 }
1561
1562 fn ClearInterval(&self, handle: i32) {
1564 self.ClearTimeout(handle);
1565 }
1566
1567 fn QueueMicrotask(&self, callback: Rc<VoidFunction>) {
1569 ScriptThread::enqueue_microtask(Microtask::User(UserMicrotask {
1570 callback,
1571 pipeline: self.pipeline_id(),
1572 }));
1573 }
1574
1575 fn CreateImageBitmap(
1577 &self,
1578 realm: &mut CurrentRealm,
1579 image: ImageBitmapSource,
1580 options: &ImageBitmapOptions,
1581 ) -> Rc<Promise> {
1582 ImageBitmap::create_image_bitmap(
1583 self.as_global_scope(),
1584 image,
1585 0,
1586 0,
1587 None,
1588 None,
1589 options,
1590 realm,
1591 )
1592 }
1593
1594 fn CreateImageBitmap_(
1596 &self,
1597 realm: &mut CurrentRealm,
1598 image: ImageBitmapSource,
1599 sx: i32,
1600 sy: i32,
1601 sw: i32,
1602 sh: i32,
1603 options: &ImageBitmapOptions,
1604 ) -> Rc<Promise> {
1605 ImageBitmap::create_image_bitmap(
1606 self.as_global_scope(),
1607 image,
1608 sx,
1609 sy,
1610 Some(sw),
1611 Some(sh),
1612 options,
1613 realm,
1614 )
1615 }
1616
1617 fn Window(&self) -> DomRoot<WindowProxy> {
1619 self.window_proxy()
1620 }
1621
1622 fn Self_(&self) -> DomRoot<WindowProxy> {
1624 self.window_proxy()
1625 }
1626
1627 fn Frames(&self) -> DomRoot<WindowProxy> {
1629 self.window_proxy()
1630 }
1631
1632 fn Length(&self) -> u32 {
1634 self.Document().iframes().iter().count() as u32
1635 }
1636
1637 fn GetParent(&self) -> Option<DomRoot<WindowProxy>> {
1639 let window_proxy = self.undiscarded_window_proxy()?;
1641
1642 if let Some(parent) = window_proxy.parent() {
1644 return Some(DomRoot::from_ref(parent));
1645 }
1646 Some(window_proxy)
1648 }
1649
1650 fn GetTop(&self) -> Option<DomRoot<WindowProxy>> {
1652 let window_proxy = self.undiscarded_window_proxy()?;
1654
1655 Some(DomRoot::from_ref(window_proxy.top()))
1657 }
1658
1659 fn Performance(&self) -> DomRoot<Performance> {
1662 self.performance.or_init(|| {
1663 Performance::new(
1664 self.as_global_scope(),
1665 self.navigation_start.get(),
1666 CanGc::note(),
1667 )
1668 })
1669 }
1670
1671 global_event_handlers!();
1673
1674 window_event_handlers!();
1676
1677 fn Screen(&self, can_gc: CanGc) -> DomRoot<Screen> {
1679 self.screen.or_init(|| Screen::new(self, can_gc))
1680 }
1681
1682 fn GetVisualViewport(&self, can_gc: CanGc) -> Option<DomRoot<VisualViewport>> {
1684 if !self.Document().is_fully_active() {
1688 return None;
1689 }
1690
1691 Some(self.get_or_init_visual_viewport(can_gc))
1692 }
1693
1694 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
1696 base64_btoa(btoa)
1697 }
1698
1699 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
1701 base64_atob(atob)
1702 }
1703
1704 fn RequestAnimationFrame(&self, callback: Rc<FrameRequestCallback>) -> u32 {
1706 self.Document()
1707 .request_animation_frame(AnimationFrameCallback::FrameRequestCallback { callback })
1708 }
1709
1710 fn CancelAnimationFrame(&self, ident: u32) {
1712 let doc = self.Document();
1713 doc.cancel_animation_frame(ident);
1714 }
1715
1716 fn PostMessage(
1718 &self,
1719 cx: &mut JSContext,
1720 message: HandleValue,
1721 target_origin: USVString,
1722 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1723 ) -> ErrorResult {
1724 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1725 let source = incumbent.as_window();
1726 let source_origin = source.Document().origin().immutable().clone();
1727
1728 self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
1729 }
1730
1731 fn PostMessage_(
1733 &self,
1734 cx: &mut JSContext,
1735 message: HandleValue,
1736 options: RootedTraceableBox<WindowPostMessageOptions>,
1737 ) -> ErrorResult {
1738 let mut rooted = CustomAutoRooter::new(
1739 options
1740 .parent
1741 .transfer
1742 .iter()
1743 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
1744 .collect(),
1745 );
1746 #[expect(unsafe_code)]
1747 let transfer = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
1748
1749 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1750 let source = incumbent.as_window();
1751
1752 let source_origin = source.Document().origin().immutable().clone();
1753
1754 self.post_message_impl(
1755 &options.targetOrigin,
1756 source_origin,
1757 source,
1758 cx,
1759 message,
1760 transfer,
1761 )
1762 }
1763
1764 fn CaptureEvents(&self) {
1766 }
1768
1769 fn ReleaseEvents(&self) {
1771 }
1773
1774 fn WebdriverCallback(&self, realm: &mut CurrentRealm, value: HandleValue) {
1776 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1777 if let Some(webdriver_script_sender) = webdriver_script_sender {
1778 let result = jsval_to_webdriver(realm, &self.globalscope, value);
1779 let _ = webdriver_script_sender.send(result);
1780 }
1781 }
1782
1783 fn WebdriverException(&self, cx: SafeJSContext, value: HandleValue, can_gc: CanGc) {
1784 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1785 if let Some(webdriver_script_sender) = webdriver_script_sender {
1786 let _ =
1787 webdriver_script_sender.send(Err(JavaScriptEvaluationError::EvaluationFailure(
1788 Some(javascript_error_info_from_error_info(
1789 cx,
1790 &ErrorInfo::from_value(value, cx, can_gc),
1791 value,
1792 can_gc,
1793 )),
1794 )));
1795 }
1796 }
1797
1798 fn WebdriverElement(&self, id: DOMString) -> Option<DomRoot<Element>> {
1799 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1800 }
1801
1802 fn WebdriverFrame(&self, browsing_context_id: DOMString) -> Option<DomRoot<WindowProxy>> {
1803 self.Document()
1804 .iframes()
1805 .iter()
1806 .find(|iframe| {
1807 iframe
1808 .browsing_context_id()
1809 .as_ref()
1810 .map(BrowsingContextId::to_string) ==
1811 Some(browsing_context_id.to_string())
1812 })
1813 .and_then(|iframe| iframe.GetContentWindow())
1814 }
1815
1816 fn WebdriverWindow(&self, webview_id: DOMString) -> DomRoot<WindowProxy> {
1817 let window_proxy = &self
1818 .window_proxy
1819 .get()
1820 .expect("Should always have a WindowProxy when calling WebdriverWindow");
1821 assert!(
1822 self.is_top_level(),
1823 "Window must be top level browsing context."
1824 );
1825 assert!(self.webview_id().to_string() == webview_id);
1826 DomRoot::from_ref(window_proxy)
1827 }
1828
1829 fn WebdriverShadowRoot(&self, id: DOMString) -> Option<DomRoot<ShadowRoot>> {
1830 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1831 }
1832
1833 fn GetComputedStyle(
1835 &self,
1836 element: &Element,
1837 pseudo: Option<DOMString>,
1838 ) -> DomRoot<CSSStyleDeclaration> {
1839 let mut is_null = false;
1843
1844 let pseudo = pseudo.map(|mut s| {
1847 s.make_ascii_lowercase();
1848 s
1849 });
1850 let pseudo = match pseudo {
1851 Some(ref pseudo) if pseudo == ":before" || pseudo == "::before" => {
1852 Some(PseudoElement::Before)
1853 },
1854 Some(ref pseudo) if pseudo == ":after" || pseudo == "::after" => {
1855 Some(PseudoElement::After)
1856 },
1857 Some(ref pseudo) if pseudo == "::selection" => Some(PseudoElement::Selection),
1858 Some(ref pseudo) if pseudo == "::marker" => Some(PseudoElement::Marker),
1859 Some(ref pseudo) if pseudo.starts_with(':') => {
1860 is_null = true;
1863 None
1864 },
1865 _ => None,
1866 };
1867
1868 CSSStyleDeclaration::new(
1884 self,
1885 if is_null {
1886 CSSStyleOwner::Null
1887 } else {
1888 CSSStyleOwner::Element(Dom::from_ref(element))
1889 },
1890 pseudo,
1891 CSSModificationAccess::Readonly,
1892 CanGc::note(),
1893 )
1894 }
1895
1896 fn InnerHeight(&self) -> i32 {
1899 self.viewport_details
1900 .get()
1901 .size
1902 .height
1903 .to_i32()
1904 .unwrap_or(0)
1905 }
1906
1907 fn InnerWidth(&self) -> i32 {
1910 self.viewport_details.get().size.width.to_i32().unwrap_or(0)
1911 }
1912
1913 fn ScrollX(&self) -> i32 {
1915 self.scroll_offset().x as i32
1916 }
1917
1918 fn PageXOffset(&self) -> i32 {
1920 self.ScrollX()
1921 }
1922
1923 fn ScrollY(&self) -> i32 {
1925 self.scroll_offset().y as i32
1926 }
1927
1928 fn PageYOffset(&self) -> i32 {
1930 self.ScrollY()
1931 }
1932
1933 fn Scroll(&self, options: &ScrollToOptions) {
1935 let x = options.left.unwrap_or(0.0) as f32;
1940
1941 let y = options.top.unwrap_or(0.0) as f32;
1944
1945 self.scroll(x, y, options.parent.behavior);
1947 }
1948
1949 fn Scroll_(&self, x: f64, y: f64) {
1951 self.scroll(x as f32, y as f32, ScrollBehavior::Auto);
1955 }
1956
1957 fn ScrollTo(&self, options: &ScrollToOptions) {
1962 self.Scroll(options);
1963 }
1964
1965 fn ScrollTo_(&self, x: f64, y: f64) {
1970 self.Scroll_(x, y)
1971 }
1972
1973 fn ScrollBy(&self, options: &ScrollToOptions) {
1975 let mut options = options.clone();
1981 let x = options.left.unwrap_or(0.0);
1982 let x = if x.is_finite() { x } else { 0.0 };
1983 let y = options.top.unwrap_or(0.0);
1984 let y = if y.is_finite() { y } else { 0.0 };
1985
1986 options.left.replace(x + self.ScrollX() as f64);
1988
1989 options.top.replace(y + self.ScrollY() as f64);
1991
1992 self.Scroll(&options)
1994 }
1995
1996 fn ScrollBy_(&self, x: f64, y: f64) {
1998 let mut options = ScrollToOptions::empty();
2002
2003 options.left.replace(x);
2006
2007 options.top.replace(y);
2009
2010 self.ScrollBy(&options);
2012 }
2013
2014 fn ResizeTo(&self, width: i32, height: i32) {
2016 let window_proxy = match self.window_proxy.get() {
2018 Some(proxy) => proxy,
2019 None => return,
2020 };
2021
2022 if !window_proxy.is_auxiliary() {
2025 return;
2026 }
2027
2028 let dpr = self.device_pixel_ratio();
2029 let size = Size2D::new(width, height).to_f32() * dpr;
2030 self.send_to_embedder(EmbedderMsg::ResizeTo(self.webview_id(), size.to_i32()));
2031 }
2032
2033 fn ResizeBy(&self, x: i32, y: i32) {
2035 let size = self.client_window().size();
2036 self.ResizeTo(x + size.width, y + size.height)
2038 }
2039
2040 fn MoveTo(&self, x: i32, y: i32) {
2042 let dpr = self.device_pixel_ratio();
2045 let point = Point2D::new(x, y).to_f32() * dpr;
2046 let msg = EmbedderMsg::MoveTo(self.webview_id(), point.to_i32());
2047 self.send_to_embedder(msg);
2048 }
2049
2050 fn MoveBy(&self, x: i32, y: i32) {
2052 let origin = self.client_window().min;
2053 self.MoveTo(x + origin.x, y + origin.y)
2055 }
2056
2057 fn ScreenX(&self) -> i32 {
2059 self.client_window().min.x
2060 }
2061
2062 fn ScreenY(&self) -> i32 {
2064 self.client_window().min.y
2065 }
2066
2067 fn OuterHeight(&self) -> i32 {
2069 self.client_window().height()
2070 }
2071
2072 fn OuterWidth(&self) -> i32 {
2074 self.client_window().width()
2075 }
2076
2077 fn DevicePixelRatio(&self) -> Finite<f64> {
2079 Finite::wrap(self.device_pixel_ratio().get() as f64)
2080 }
2081
2082 fn Status(&self) -> DOMString {
2084 self.status.borrow().clone()
2085 }
2086
2087 fn SetStatus(&self, status: DOMString) {
2089 *self.status.borrow_mut() = status
2090 }
2091
2092 fn MatchMedia(&self, query: DOMString) -> DomRoot<MediaQueryList> {
2094 let media_query_list = MediaList::parse_media_list(&query.str(), self);
2095 let document = self.Document();
2096 let mql = MediaQueryList::new(&document, media_query_list, CanGc::note());
2097 self.media_query_lists.track(&*mql);
2098 mql
2099 }
2100
2101 fn Fetch(
2103 &self,
2104 input: RequestOrUSVString,
2105 init: RootedTraceableBox<RequestInit>,
2106 comp: InRealm,
2107 can_gc: CanGc,
2108 ) -> Rc<Promise> {
2109 fetch::Fetch(self.upcast(), input, init, comp, can_gc)
2110 }
2111
2112 fn FetchLater(
2114 &self,
2115 input: RequestInfo,
2116 init: RootedTraceableBox<DeferredRequestInit>,
2117 can_gc: CanGc,
2118 ) -> Fallible<DomRoot<FetchLaterResult>> {
2119 fetch::FetchLater(self, input, init, can_gc)
2120 }
2121
2122 #[cfg(feature = "bluetooth")]
2123 fn TestRunner(&self) -> DomRoot<TestRunner> {
2124 self.test_runner
2125 .or_init(|| TestRunner::new(self.upcast(), CanGc::note()))
2126 }
2127
2128 fn RunningAnimationCount(&self) -> u32 {
2129 self.document
2130 .get()
2131 .map_or(0, |d| d.animations().running_animation_count() as u32)
2132 }
2133
2134 fn SetName(&self, name: DOMString) {
2136 if let Some(proxy) = self.undiscarded_window_proxy() {
2137 proxy.set_name(name);
2138 }
2139 }
2140
2141 fn Name(&self) -> DOMString {
2143 match self.undiscarded_window_proxy() {
2144 Some(proxy) => proxy.get_name(),
2145 None => "".into(),
2146 }
2147 }
2148
2149 fn Origin(&self) -> USVString {
2151 USVString(self.origin().immutable().ascii_serialization())
2152 }
2153
2154 fn GetSelection(&self) -> Option<DomRoot<Selection>> {
2156 self.document
2157 .get()
2158 .and_then(|d| d.GetSelection(CanGc::note()))
2159 }
2160
2161 fn Event(&self, cx: SafeJSContext, rval: MutableHandleValue) {
2163 if let Some(ref event) = *self.current_event.borrow() {
2164 event
2165 .reflector()
2166 .get_jsobject()
2167 .safe_to_jsval(cx, rval, CanGc::note());
2168 }
2169 }
2170
2171 fn IsSecureContext(&self) -> bool {
2172 self.as_global_scope().is_secure_context()
2173 }
2174
2175 fn NamedGetter(&self, name: DOMString) -> Option<NamedPropertyValue> {
2177 if name.is_empty() {
2178 return None;
2179 }
2180 let document = self.Document();
2181
2182 let iframes: Vec<_> = document
2184 .iframes()
2185 .iter()
2186 .filter(|iframe| {
2187 if let Some(window) = iframe.GetContentWindow() {
2188 return window.get_name() == name;
2189 }
2190 false
2191 })
2192 .collect();
2193
2194 let iframe_iter = iframes.iter().map(|iframe| iframe.upcast::<Element>());
2195
2196 let name = Atom::from(name);
2197
2198 let elements_with_name = document.get_elements_with_name(&name);
2200 let name_iter = elements_with_name
2201 .iter()
2202 .map(|element| &**element)
2203 .filter(|elem| is_named_element_with_name_attribute(elem));
2204 let elements_with_id = document.get_elements_with_id(&name);
2205 let id_iter = elements_with_id
2206 .iter()
2207 .map(|element| &**element)
2208 .filter(|elem| is_named_element_with_id_attribute(elem));
2209
2210 for elem in iframe_iter.clone() {
2212 if let Some(nested_window_proxy) = elem
2213 .downcast::<HTMLIFrameElement>()
2214 .and_then(|iframe| iframe.GetContentWindow())
2215 {
2216 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
2217 }
2218 }
2219
2220 let mut elements = iframe_iter.chain(name_iter).chain(id_iter);
2221
2222 let first = elements.next()?;
2223
2224 if elements.next().is_none() {
2225 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
2227 }
2228
2229 #[derive(JSTraceable, MallocSizeOf)]
2231 struct WindowNamedGetter {
2232 #[no_trace]
2233 name: Atom,
2234 }
2235 impl CollectionFilter for WindowNamedGetter {
2236 fn filter(&self, elem: &Element, _root: &Node) -> bool {
2237 let type_ = match elem.upcast::<Node>().type_id() {
2238 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
2239 _ => return false,
2240 };
2241 if elem.get_id().as_ref() == Some(&self.name) {
2242 return true;
2243 }
2244 match type_ {
2245 HTMLElementTypeId::HTMLEmbedElement |
2246 HTMLElementTypeId::HTMLFormElement |
2247 HTMLElementTypeId::HTMLImageElement |
2248 HTMLElementTypeId::HTMLObjectElement => {
2249 elem.get_name().as_ref() == Some(&self.name)
2250 },
2251 _ => false,
2252 }
2253 }
2254 }
2255 let collection = HTMLCollection::create(
2256 self,
2257 document.upcast(),
2258 Box::new(WindowNamedGetter { name }),
2259 CanGc::note(),
2260 );
2261 Some(NamedPropertyValue::HTMLCollection(collection))
2262 }
2263
2264 fn SupportedPropertyNames(&self) -> Vec<DOMString> {
2266 let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
2267
2268 let document = self.Document();
2269 let name_map = document.name_map();
2270 for (name, elements) in &name_map.0 {
2271 if name.is_empty() {
2272 continue;
2273 }
2274 let mut name_iter = elements
2275 .iter()
2276 .filter(|elem| is_named_element_with_name_attribute(elem));
2277 if let Some(first) = name_iter.next() {
2278 names_with_first_named_element_map.insert(name, first);
2279 }
2280 }
2281 let id_map = document.id_map();
2282 for (id, elements) in &id_map.0 {
2283 if id.is_empty() {
2284 continue;
2285 }
2286 let mut id_iter = elements
2287 .iter()
2288 .filter(|elem| is_named_element_with_id_attribute(elem));
2289 if let Some(first) = id_iter.next() {
2290 match names_with_first_named_element_map.entry(id) {
2291 Entry::Vacant(entry) => drop(entry.insert(first)),
2292 Entry::Occupied(mut entry) => {
2293 if first.upcast::<Node>().is_before(entry.get().upcast()) {
2294 *entry.get_mut() = first;
2295 }
2296 },
2297 }
2298 }
2299 }
2300
2301 let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
2302 names_with_first_named_element_map
2303 .iter()
2304 .map(|(k, v)| (*k, *v))
2305 .collect();
2306 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
2307 if a.1 == b.1 {
2308 a.0.cmp(b.0)
2311 } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
2312 cmp::Ordering::Less
2313 } else {
2314 cmp::Ordering::Greater
2315 }
2316 });
2317
2318 names_with_first_named_element_vec
2319 .iter()
2320 .map(|(k, _v)| DOMString::from(&***k))
2321 .collect()
2322 }
2323
2324 fn StructuredClone(
2326 &self,
2327 cx: SafeJSContext,
2328 value: HandleValue,
2329 options: RootedTraceableBox<StructuredSerializeOptions>,
2330 can_gc: CanGc,
2331 retval: MutableHandleValue,
2332 ) -> Fallible<()> {
2333 self.as_global_scope()
2334 .structured_clone(cx, value, options, retval, can_gc)
2335 }
2336
2337 fn TrustedTypes(&self, can_gc: CanGc) -> DomRoot<TrustedTypePolicyFactory> {
2338 self.trusted_types
2339 .or_init(|| TrustedTypePolicyFactory::new(self.as_global_scope(), can_gc))
2340 }
2341}
2342
2343impl Window {
2344 pub(crate) fn scroll_offset(&self) -> Vector2D<f32, LayoutPixel> {
2345 self.scroll_offset_query_with_external_scroll_id(self.pipeline_id().root_scroll_id())
2346 }
2347
2348 pub(crate) fn create_named_properties_object(
2351 cx: SafeJSContext,
2352 proto: HandleObject,
2353 object: MutableHandleObject,
2354 ) {
2355 window_named_properties::create(cx, proto, object)
2356 }
2357
2358 pub(crate) fn current_event(&self) -> Option<DomRoot<Event>> {
2359 self.current_event
2360 .borrow()
2361 .as_ref()
2362 .map(|e| DomRoot::from_ref(&**e))
2363 }
2364
2365 pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
2366 let current = self.current_event();
2367 *self.current_event.borrow_mut() = event.map(Dom::from_ref);
2368 current
2369 }
2370
2371 fn post_message_impl(
2373 &self,
2374 target_origin: &USVString,
2375 source_origin: ImmutableOrigin,
2376 source: &Window,
2377 cx: &mut JSContext,
2378 message: HandleValue,
2379 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
2380 ) -> ErrorResult {
2381 let data = structuredclone::write(cx.into(), message, Some(transfer))?;
2383
2384 let target_origin = match target_origin.0[..].as_ref() {
2386 "*" => None,
2387 "/" => Some(source_origin.clone()),
2388 url => match ServoUrl::parse(url) {
2389 Ok(url) => Some(url.origin().clone()),
2390 Err(_) => return Err(Error::Syntax(None)),
2391 },
2392 };
2393
2394 self.post_message(target_origin, source_origin, &source.window_proxy(), data);
2396 Ok(())
2397 }
2398
2399 pub(crate) fn paint_worklet(&self) -> DomRoot<Worklet> {
2401 self.paint_worklet
2402 .or_init(|| self.new_paint_worklet(CanGc::note()))
2403 }
2404
2405 pub(crate) fn has_document(&self) -> bool {
2406 self.document.get().is_some()
2407 }
2408
2409 pub(crate) fn clear_js_runtime(&self) {
2410 self.as_global_scope()
2411 .remove_web_messaging_and_dedicated_workers_infra();
2412
2413 if let Some(custom_elements) = self.custom_element_registry.get() {
2416 custom_elements.teardown();
2417 }
2418
2419 self.current_state.set(WindowState::Zombie);
2420 *self.js_runtime.borrow_mut() = None;
2421
2422 if let Some(proxy) = self.window_proxy.get() {
2425 let pipeline_id = self.pipeline_id();
2426 if let Some(currently_active) = proxy.currently_active() {
2427 if currently_active == pipeline_id {
2428 self.window_proxy.set(None);
2429 }
2430 }
2431 }
2432
2433 if let Some(performance) = self.performance.get() {
2434 performance.clear_and_disable_performance_entry_buffer();
2435 }
2436 self.as_global_scope()
2437 .task_manager()
2438 .cancel_all_tasks_and_ignore_future_tasks();
2439 }
2440
2441 pub(crate) fn scroll(&self, x: f32, y: f32, behavior: ScrollBehavior) {
2443 let xfinite = if x.is_finite() { x } else { 0.0 };
2445 let yfinite = if y.is_finite() { y } else { 0.0 };
2446
2447 let viewport = self.viewport_details.get().size;
2457
2458 let scrolling_area = self.scrolling_area_query(None).to_f32();
2477 let x = xfinite.clamp(0.0, 0.0f32.max(scrolling_area.width() - viewport.width));
2478 let y = yfinite.clamp(0.0, 0.0f32.max(scrolling_area.height() - viewport.height));
2479
2480 let scroll_offset = self.scroll_offset();
2483 if x == scroll_offset.x && y == scroll_offset.y {
2484 return;
2485 }
2486
2487 self.perform_a_scroll(x, y, self.pipeline_id().root_scroll_id(), behavior, None);
2492 }
2493
2494 pub(crate) fn perform_a_scroll(
2496 &self,
2497 x: f32,
2498 y: f32,
2499 scroll_id: ExternalScrollId,
2500 _behavior: ScrollBehavior,
2501 element: Option<&Element>,
2502 ) {
2503 let (reflow_phases_run, _) =
2507 self.reflow(ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)));
2508 if reflow_phases_run.needs_frame() {
2509 self.paint_api()
2510 .generate_frame(vec![self.webview_id().into()]);
2511 }
2512
2513 if reflow_phases_run.contains(ReflowPhasesRun::UpdatedScrollNodeOffset) {
2518 match element {
2519 Some(el) => self.Document().handle_element_scroll_event(el),
2520 None => self.Document().handle_viewport_scroll_event(),
2521 };
2522 }
2523 }
2524
2525 pub(crate) fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
2526 self.viewport_details.get().hidpi_scale_factor
2527 }
2528
2529 fn client_window(&self) -> DeviceIndependentIntRect {
2530 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel!");
2531
2532 self.send_to_embedder(EmbedderMsg::GetWindowRect(self.webview_id(), sender));
2533
2534 receiver.recv().unwrap_or_default()
2535 }
2536
2537 pub(crate) fn advance_animation_clock(&self, delta_ms: i32) {
2540 self.Document()
2541 .advance_animation_timeline_for_testing(delta_ms as f64 / 1000.);
2542 ScriptThread::handle_tick_all_animations_for_testing(self.pipeline_id());
2543 }
2544
2545 pub(crate) fn reflow(&self, reflow_goal: ReflowGoal) -> (ReflowPhasesRun, ReflowStatistics) {
2553 let document = self.Document();
2554
2555 if !document.is_fully_active() {
2557 return Default::default();
2558 }
2559
2560 self.Document().ensure_safe_to_run_script_or_layout();
2561
2562 let pipeline_id = self.pipeline_id();
2566 if reflow_goal == ReflowGoal::UpdateTheRendering &&
2567 self.layout_blocker.get().layout_blocked()
2568 {
2569 debug!("Suppressing pre-load-event reflow pipeline {pipeline_id}");
2570 return Default::default();
2571 }
2572
2573 debug!("script: performing reflow for goal {reflow_goal:?}");
2574 let marker = if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) {
2575 Some(TimelineMarker::start("Reflow".to_owned()))
2576 } else {
2577 None
2578 };
2579
2580 let restyle_reason = document.restyle_reason();
2581 document.clear_restyle_reasons();
2582 let restyle = if restyle_reason.needs_restyle() {
2583 debug!("Invalidating layout cache due to reflow condition {restyle_reason:?}",);
2584 self.layout_marker.borrow().set(false);
2586 *self.layout_marker.borrow_mut() = Rc::new(Cell::new(true));
2588
2589 let stylesheets_changed = document.flush_stylesheets_for_reflow();
2590 let pending_restyles = document.drain_pending_restyles();
2591 let dirty_root = document
2592 .take_dirty_root()
2593 .filter(|_| !stylesheets_changed)
2594 .or_else(|| document.GetDocumentElement())
2595 .map(|root| root.upcast::<Node>().to_trusted_node_address());
2596
2597 Some(ReflowRequestRestyle {
2598 reason: restyle_reason,
2599 dirty_root,
2600 stylesheets_changed,
2601 pending_restyles,
2602 })
2603 } else {
2604 None
2605 };
2606
2607 let document_context = self.web_font_context();
2608
2609 let reflow = ReflowRequest {
2611 document: document.upcast::<Node>().to_trusted_node_address(),
2612 epoch: document.current_rendering_epoch(),
2613 restyle,
2614 viewport_details: self.viewport_details.get(),
2615 origin: self.origin().immutable().clone(),
2616 reflow_goal,
2617 dom_count: document.dom_count(),
2618 animation_timeline_value: document.current_animation_timeline_value(),
2619 animations: document.animations().sets.clone(),
2620 animating_images: document.image_animation_manager().animating_images(),
2621 highlighted_dom_node: document.highlighted_dom_node().map(|node| node.to_opaque()),
2622 document_context,
2623 };
2624
2625 let Some(reflow_result) = self.layout.borrow_mut().reflow(reflow) else {
2626 return Default::default();
2627 };
2628
2629 debug!("script: layout complete");
2630 if let Some(marker) = marker {
2631 self.emit_timeline_marker(marker.end());
2632 }
2633
2634 self.handle_pending_images_post_reflow(
2635 reflow_result.pending_images,
2636 reflow_result.pending_rasterization_images,
2637 reflow_result.pending_svg_elements_for_serialization,
2638 );
2639
2640 if let Some(iframe_sizes) = reflow_result.iframe_sizes {
2641 document
2642 .iframes_mut()
2643 .handle_new_iframe_sizes_after_layout(self, iframe_sizes);
2644 }
2645
2646 document.update_animations_post_reflow();
2647
2648 (
2649 reflow_result.reflow_phases_run,
2650 reflow_result.reflow_statistics,
2651 )
2652 }
2653
2654 pub(crate) fn request_screenshot_readiness(&self, can_gc: CanGc) {
2655 self.has_pending_screenshot_readiness_request.set(true);
2656 self.maybe_resolve_pending_screenshot_readiness_requests(can_gc);
2657 }
2658
2659 pub(crate) fn maybe_resolve_pending_screenshot_readiness_requests(&self, can_gc: CanGc) {
2660 let pending_request = self.has_pending_screenshot_readiness_request.get();
2661 if !pending_request {
2662 return;
2663 }
2664
2665 let document = self.Document();
2666 if document.ReadyState() != DocumentReadyState::Complete {
2667 return;
2668 }
2669
2670 if document.render_blocking_element_count() > 0 {
2671 return;
2672 }
2673
2674 if document.GetDocumentElement().is_some_and(|elem| {
2678 elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
2679 elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
2680 }) {
2681 return;
2682 }
2683
2684 if self.font_context().web_fonts_still_loading() != 0 {
2685 return;
2686 }
2687
2688 if self.Document().Fonts(can_gc).waiting_to_fullfill_promise() {
2689 return;
2690 }
2691
2692 if !self.pending_layout_images.borrow().is_empty() ||
2693 !self.pending_images_for_rasterization.borrow().is_empty()
2694 {
2695 return;
2696 }
2697
2698 let document = self.Document();
2699 if document.needs_rendering_update() {
2700 return;
2701 }
2702
2703 let epoch = document.current_rendering_epoch();
2706 let pipeline_id = self.pipeline_id();
2707 debug!("Ready to take screenshot of {pipeline_id:?} at epoch={epoch:?}");
2708
2709 self.send_to_constellation(
2710 ScriptToConstellationMessage::RespondToScreenshotReadinessRequest(
2711 ScreenshotReadinessResponse::Ready(epoch),
2712 ),
2713 );
2714 self.has_pending_screenshot_readiness_request.set(false);
2715 }
2716
2717 pub(crate) fn reflow_if_reflow_timer_expired(&self) {
2720 if !matches!(
2723 self.layout_blocker.get(),
2724 LayoutBlocker::Parsing(instant) if instant + INITIAL_REFLOW_DELAY < Instant::now()
2725 ) {
2726 return;
2727 }
2728 self.allow_layout_if_necessary();
2729 }
2730
2731 pub(crate) fn prevent_layout_until_load_event(&self) {
2735 if !matches!(self.layout_blocker.get(), LayoutBlocker::WaitingForParse) {
2738 return;
2739 }
2740
2741 self.layout_blocker
2742 .set(LayoutBlocker::Parsing(Instant::now()));
2743 }
2744
2745 pub(crate) fn allow_layout_if_necessary(&self) {
2748 if matches!(
2749 self.layout_blocker.get(),
2750 LayoutBlocker::FiredLoadEventOrParsingTimerExpired
2751 ) {
2752 return;
2753 }
2754
2755 self.layout_blocker
2756 .set(LayoutBlocker::FiredLoadEventOrParsingTimerExpired);
2757
2758 if self.Document().update_the_rendering().0.needs_frame() {
2770 self.paint_api()
2771 .generate_frame(vec![self.webview_id().into()]);
2772 }
2773 }
2774
2775 pub(crate) fn layout_blocked(&self) -> bool {
2776 self.layout_blocker.get().layout_blocked()
2777 }
2778
2779 pub(crate) fn layout_reflow(&self, query_msg: QueryMsg) {
2781 self.reflow(ReflowGoal::LayoutQuery(query_msg));
2782 }
2783
2784 pub(crate) fn resolved_font_style_query(
2785 &self,
2786 node: &Node,
2787 value: String,
2788 ) -> Option<ServoArc<Font>> {
2789 self.layout_reflow(QueryMsg::ResolvedFontStyleQuery);
2790
2791 let document = self.Document();
2792 let animations = document.animations().sets.clone();
2793 self.layout.borrow().query_resolved_font_style(
2794 node.to_trusted_node_address(),
2795 &value,
2796 animations,
2797 document.current_animation_timeline_value(),
2798 )
2799 }
2800
2801 pub(crate) fn padding_query_without_reflow(&self, node: &Node) -> Option<PhysicalSides> {
2806 let layout = self.layout.borrow();
2807 layout.query_padding(node.to_trusted_node_address())
2808 }
2809
2810 pub(crate) fn box_area_query_without_reflow(
2815 &self,
2816 node: &Node,
2817 area: BoxAreaType,
2818 exclude_transform_and_inline: bool,
2819 ) -> Option<Rect<Au, CSSPixel>> {
2820 let layout = self.layout.borrow();
2821 layout.ensure_stacking_context_tree(self.viewport_details.get());
2822 layout.query_box_area(
2823 node.to_trusted_node_address(),
2824 area,
2825 exclude_transform_and_inline,
2826 )
2827 }
2828
2829 pub(crate) fn box_area_query(
2830 &self,
2831 node: &Node,
2832 area: BoxAreaType,
2833 exclude_transform_and_inline: bool,
2834 ) -> Option<Rect<Au, CSSPixel>> {
2835 self.layout_reflow(QueryMsg::BoxArea);
2836 self.box_area_query_without_reflow(node, area, exclude_transform_and_inline)
2837 }
2838
2839 pub(crate) fn box_areas_query(&self, node: &Node, area: BoxAreaType) -> CSSPixelRectIterator {
2840 self.layout_reflow(QueryMsg::BoxAreas);
2841 self.layout
2842 .borrow()
2843 .query_box_areas(node.to_trusted_node_address(), area)
2844 }
2845
2846 pub(crate) fn client_rect_query(&self, node: &Node) -> Rect<i32, CSSPixel> {
2847 self.layout_reflow(QueryMsg::ClientRectQuery);
2848 self.layout
2849 .borrow()
2850 .query_client_rect(node.to_trusted_node_address())
2851 }
2852
2853 pub(crate) fn current_css_zoom_query(&self, node: &Node) -> f32 {
2854 self.layout_reflow(QueryMsg::CurrentCSSZoomQuery);
2855 self.layout
2856 .borrow()
2857 .query_current_css_zoom(node.to_trusted_node_address())
2858 }
2859
2860 pub(crate) fn scrolling_area_query(&self, node: Option<&Node>) -> Rect<i32, CSSPixel> {
2863 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2864 self.layout
2865 .borrow()
2866 .query_scrolling_area(node.map(Node::to_trusted_node_address))
2867 }
2868
2869 pub(crate) fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32, LayoutPixel> {
2870 let external_scroll_id = ExternalScrollId(
2871 combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody),
2872 self.pipeline_id().into(),
2873 );
2874 self.scroll_offset_query_with_external_scroll_id(external_scroll_id)
2875 }
2876
2877 fn scroll_offset_query_with_external_scroll_id(
2878 &self,
2879 external_scroll_id: ExternalScrollId,
2880 ) -> Vector2D<f32, LayoutPixel> {
2881 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2882 self.scroll_offset_query_with_external_scroll_id_no_reflow(external_scroll_id)
2883 }
2884
2885 fn scroll_offset_query_with_external_scroll_id_no_reflow(
2886 &self,
2887 external_scroll_id: ExternalScrollId,
2888 ) -> Vector2D<f32, LayoutPixel> {
2889 self.layout
2890 .borrow()
2891 .scroll_offset(external_scroll_id)
2892 .unwrap_or_default()
2893 }
2894
2895 pub(crate) fn scroll_an_element(
2898 &self,
2899 element: &Element,
2900 x: f32,
2901 y: f32,
2902 behavior: ScrollBehavior,
2903 ) {
2904 let scroll_id = ExternalScrollId(
2905 combine_id_with_fragment_type(
2906 element.upcast::<Node>().to_opaque().id(),
2907 FragmentType::FragmentBody,
2908 ),
2909 self.pipeline_id().into(),
2910 );
2911
2912 self.perform_a_scroll(x, y, scroll_id, behavior, Some(element));
2916 }
2917
2918 pub(crate) fn resolved_style_query(
2919 &self,
2920 element: TrustedNodeAddress,
2921 pseudo: Option<PseudoElement>,
2922 property: PropertyId,
2923 ) -> DOMString {
2924 self.layout_reflow(QueryMsg::ResolvedStyleQuery);
2925
2926 let document = self.Document();
2927 let animations = document.animations().sets.clone();
2928 DOMString::from(self.layout.borrow().query_resolved_style(
2929 element,
2930 pseudo,
2931 property,
2932 animations,
2933 document.current_animation_timeline_value(),
2934 ))
2935 }
2936
2937 pub(crate) fn get_iframe_viewport_details_if_known(
2941 &self,
2942 browsing_context_id: BrowsingContextId,
2943 ) -> Option<ViewportDetails> {
2944 self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery);
2946 self.Document()
2947 .iframes()
2948 .get(browsing_context_id)
2949 .and_then(|iframe| iframe.size)
2950 }
2951
2952 #[expect(unsafe_code)]
2953 pub(crate) fn offset_parent_query(
2954 &self,
2955 node: &Node,
2956 ) -> (Option<DomRoot<Element>>, Rect<Au, CSSPixel>) {
2957 self.layout_reflow(QueryMsg::OffsetParentQuery);
2958 let response = self
2959 .layout
2960 .borrow()
2961 .query_offset_parent(node.to_trusted_node_address());
2962 let element = response.node_address.and_then(|parent_node_address| {
2963 let node = unsafe { from_untrusted_node_address(parent_node_address) };
2964 DomRoot::downcast(node)
2965 });
2966 (element, response.rect)
2967 }
2968
2969 pub(crate) fn scroll_container_query(
2970 &self,
2971 node: Option<&Node>,
2972 flags: ScrollContainerQueryFlags,
2973 ) -> Option<ScrollContainerResponse> {
2974 self.layout_reflow(QueryMsg::ScrollParentQuery);
2975 self.layout
2976 .borrow()
2977 .query_scroll_container(node.map(Node::to_trusted_node_address), flags)
2978 }
2979
2980 #[expect(unsafe_code)]
2981 pub(crate) fn scrolling_box_query(
2982 &self,
2983 node: Option<&Node>,
2984 flags: ScrollContainerQueryFlags,
2985 ) -> Option<ScrollingBox> {
2986 self.scroll_container_query(node, flags)
2987 .and_then(|response| {
2988 Some(match response {
2989 ScrollContainerResponse::Viewport(overflow) => {
2990 (ScrollingBoxSource::Viewport(self.Document()), overflow)
2991 },
2992 ScrollContainerResponse::Element(parent_node_address, overflow) => {
2993 let node = unsafe { from_untrusted_node_address(parent_node_address) };
2994 (
2995 ScrollingBoxSource::Element(DomRoot::downcast(node)?),
2996 overflow,
2997 )
2998 },
2999 })
3000 })
3001 .map(|(source, overflow)| ScrollingBox::new(source, overflow))
3002 }
3003
3004 pub(crate) fn text_index_query_on_node_for_event(
3005 &self,
3006 node: &Node,
3007 mouse_event: &MouseEvent,
3008 ) -> Option<usize> {
3009 let point_in_viewport = mouse_event.point_in_viewport()?.map(Au::from_f32_px);
3013
3014 self.layout_reflow(QueryMsg::TextIndexQuery);
3015 self.layout
3016 .borrow()
3017 .query_text_index(node.to_trusted_node_address(), point_in_viewport)
3018 }
3019
3020 pub(crate) fn elements_from_point_query(
3021 &self,
3022 point: LayoutPoint,
3023 flags: ElementsFromPointFlags,
3024 ) -> Vec<ElementsFromPointResult> {
3025 self.layout_reflow(QueryMsg::ElementsFromPoint);
3026 self.layout().query_elements_from_point(point, flags)
3027 }
3028
3029 pub(crate) fn hit_test_from_input_event(
3030 &self,
3031 input_event: &ConstellationInputEvent,
3032 ) -> Option<HitTestResult> {
3033 self.hit_test_from_point_in_viewport(
3034 input_event.hit_test_result.as_ref()?.point_in_viewport,
3035 )
3036 }
3037
3038 #[expect(unsafe_code)]
3039 pub(crate) fn hit_test_from_point_in_viewport(
3040 &self,
3041 point_in_frame: Point2D<f32, CSSPixel>,
3042 ) -> Option<HitTestResult> {
3043 let result = self
3044 .elements_from_point_query(point_in_frame.cast_unit(), ElementsFromPointFlags::empty())
3045 .into_iter()
3046 .nth(0)?;
3047
3048 let point_relative_to_initial_containing_block =
3049 point_in_frame + self.scroll_offset().cast_unit();
3050
3051 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
3054 Some(HitTestResult {
3055 node: unsafe { from_untrusted_node_address(address) },
3056 cursor: result.cursor,
3057 point_in_node: result.point_in_target,
3058 point_in_frame,
3059 point_relative_to_initial_containing_block,
3060 })
3061 }
3062
3063 pub(crate) fn init_window_proxy(&self, window_proxy: &WindowProxy) {
3064 assert!(self.window_proxy.get().is_none());
3065 self.window_proxy.set(Some(window_proxy));
3066 }
3067
3068 pub(crate) fn init_document(&self, document: &Document) {
3069 assert!(self.document.get().is_none());
3070 assert!(document.window() == self);
3071 self.document.set(Some(document));
3072
3073 if self.unminify_css {
3074 *self.unminified_css_dir.borrow_mut() = Some(unminified_path("unminified-css"));
3075 }
3076 }
3077
3078 fn navigate_to_fragment(&self, url: &ServoUrl, history_handling: NavigationHistoryBehavior) {
3080 let doc = self.Document();
3081 self.send_to_constellation(ScriptToConstellationMessage::NavigatedToFragment(
3104 url.clone(),
3105 history_handling,
3106 ));
3107 let old_url = doc.url();
3109 doc.set_url(url.clone());
3110 doc.update_document_for_history_step_application(&old_url, url);
3113 let Some(fragment) = url.fragment() else {
3115 unreachable!("Must always have a fragment");
3116 };
3117 doc.scroll_to_the_fragment(fragment);
3118 }
3123
3124 pub(crate) fn load_data_for_document(
3125 &self,
3126 url: ServoUrl,
3127 pipeline_id: PipelineId,
3128 ) -> LoadData {
3129 let source_document = self.Document();
3130 let secure_context = if self.is_top_level() {
3131 None
3132 } else {
3133 Some(self.IsSecureContext())
3134 };
3135 LoadData::new(
3136 LoadOrigin::Script(self.origin().snapshot()),
3137 url,
3138 source_document.about_base_url(),
3139 Some(pipeline_id),
3140 Referrer::ReferrerUrl(source_document.url()),
3141 source_document.get_referrer_policy(),
3142 secure_context,
3143 Some(source_document.insecure_requests_policy()),
3144 source_document.has_trustworthy_ancestor_origin(),
3145 source_document.creation_sandboxing_flag_set_considering_parent_iframe(),
3146 )
3147 }
3148
3149 pub(crate) fn load_url(
3151 &self,
3152 history_handling: NavigationHistoryBehavior,
3153 force_reload: bool,
3154 load_data: LoadData,
3155 can_gc: CanGc,
3156 ) {
3157 let doc = self.Document();
3158
3159 let initiator_origin_snapshot = &load_data.load_origin;
3161
3162 let pipeline_id = self.pipeline_id();
3167 let window_proxy = self.window_proxy();
3168 if let Some(active) = window_proxy.currently_active() {
3169 if pipeline_id == active && doc.is_prompting_or_unloading() {
3170 return;
3171 }
3172 }
3173
3174 if doc.check_if_unloading_is_cancelled(false, can_gc) {
3177 let history_handling = if history_handling == NavigationHistoryBehavior::Auto {
3179 if let LoadOrigin::Script(initiator_origin) = initiator_origin_snapshot {
3186 if load_data.url == doc.url() && initiator_origin.same_origin(doc.origin()) {
3187 NavigationHistoryBehavior::Replace
3188 } else {
3189 NavigationHistoryBehavior::Push
3191 }
3192 } else {
3193 NavigationHistoryBehavior::Push
3195 }
3196 } else {
3197 history_handling
3198 };
3199 let history_handling =
3204 if load_data.url.scheme() == "javascript" || doc.is_initial_about_blank() {
3205 NavigationHistoryBehavior::Replace
3206 } else {
3207 history_handling
3208 };
3209
3210 if !force_reload
3214 && load_data.url.as_url()[..Position::AfterQuery] ==
3216 doc.url().as_url()[..Position::AfterQuery]
3217 && load_data.url.fragment().is_some()
3219 {
3220 let webdriver_sender = self.webdriver_load_status_sender.borrow().clone();
3223 if let Some(ref sender) = webdriver_sender {
3224 let _ = sender.send(WebDriverLoadStatus::NavigationStart);
3225 }
3226 self.navigate_to_fragment(&load_data.url, history_handling);
3227 if let Some(sender) = webdriver_sender {
3229 let _ = sender.send(WebDriverLoadStatus::NavigationStop);
3230 }
3231 return;
3232 }
3233
3234 let window_proxy = self.window_proxy();
3236 if window_proxy.parent().is_some() {
3237 window_proxy.start_delaying_load_events_mode();
3238 }
3239
3240 if let Some(sender) = self.webdriver_load_status_sender.borrow().as_ref() {
3241 let _ = sender.send(WebDriverLoadStatus::NavigationStart);
3242 }
3243
3244 ScriptThread::navigate(self.webview_id, pipeline_id, load_data, history_handling);
3246 };
3247 }
3248
3249 pub(crate) fn set_viewport_details(&self, viewport_details: ViewportDetails) {
3252 self.viewport_details.set(viewport_details);
3253 if !self.layout_mut().set_viewport_details(viewport_details) {
3254 return;
3255 }
3256 self.Document()
3257 .add_restyle_reason(RestyleReason::ViewportChanged);
3258 }
3259
3260 pub(crate) fn viewport_details(&self) -> ViewportDetails {
3261 self.viewport_details.get()
3262 }
3263
3264 pub(crate) fn get_or_init_visual_viewport(&self, can_gc: CanGc) -> DomRoot<VisualViewport> {
3265 self.visual_viewport.or_init(|| {
3266 VisualViewport::new_from_layout_viewport(self, self.viewport_details().size, can_gc)
3267 })
3268 }
3269
3270 pub(crate) fn maybe_update_visual_viewport(
3272 &self,
3273 pinch_zoom_infos: PinchZoomInfos,
3274 can_gc: CanGc,
3275 ) {
3276 if pinch_zoom_infos.rect == Rect::from_size(self.viewport_details().size) &&
3279 self.visual_viewport.get().is_none()
3280 {
3281 return;
3282 }
3283
3284 let visual_viewport = self.get_or_init_visual_viewport(can_gc);
3285 let changes = visual_viewport.update_from_pinch_zoom_infos(pinch_zoom_infos);
3286
3287 if changes.intersects(VisualViewportChanges::DimensionChanged) {
3288 self.has_changed_visual_viewport_dimension.set(true);
3289 }
3290 }
3292
3293 pub(crate) fn theme(&self) -> Theme {
3295 self.theme.get()
3296 }
3297
3298 pub(crate) fn set_theme(&self, new_theme: Theme) {
3300 self.theme.set(new_theme);
3301 if !self.layout_mut().set_theme(new_theme) {
3302 return;
3303 }
3304 self.Document()
3305 .add_restyle_reason(RestyleReason::ThemeChanged);
3306 }
3307
3308 pub(crate) fn get_url(&self) -> ServoUrl {
3309 self.Document().url()
3310 }
3311
3312 pub(crate) fn windowproxy_handler(&self) -> &'static WindowProxyHandler {
3313 self.dom_static.windowproxy_handler
3314 }
3315
3316 pub(crate) fn add_resize_event(&self, event: ViewportDetails, event_type: WindowSizeType) {
3317 *self.unhandled_resize_event.borrow_mut() = Some((event, event_type))
3320 }
3321
3322 pub(crate) fn take_unhandled_resize_event(&self) -> Option<(ViewportDetails, WindowSizeType)> {
3323 self.unhandled_resize_event.borrow_mut().take()
3324 }
3325
3326 pub(crate) fn has_unhandled_resize_event(&self) -> bool {
3328 self.unhandled_resize_event.borrow().is_some()
3329 }
3330
3331 pub(crate) fn suspend(&self, cx: &mut js::context::JSContext) {
3332 self.as_global_scope().suspend();
3334
3335 if self.window_proxy().currently_active() == Some(self.global().pipeline_id()) {
3337 self.window_proxy().unset_currently_active(cx);
3338 }
3339
3340 self.gc();
3345 }
3346
3347 pub(crate) fn resume(&self, can_gc: CanGc) {
3348 self.as_global_scope().resume();
3350
3351 self.window_proxy().set_currently_active(self, can_gc);
3353
3354 self.Document().title_changed();
3357 }
3358
3359 pub(crate) fn need_emit_timeline_marker(&self, timeline_type: TimelineMarkerType) -> bool {
3360 let markers = self.devtools_markers.borrow();
3361 markers.contains(&timeline_type)
3362 }
3363
3364 pub(crate) fn emit_timeline_marker(&self, marker: TimelineMarker) {
3365 let sender = self.devtools_marker_sender.borrow();
3366 let sender = sender.as_ref().expect("There is no marker sender");
3367 sender.send(Some(marker)).unwrap();
3368 }
3369
3370 pub(crate) fn set_devtools_timeline_markers(
3371 &self,
3372 markers: Vec<TimelineMarkerType>,
3373 reply: GenericSender<Option<TimelineMarker>>,
3374 ) {
3375 *self.devtools_marker_sender.borrow_mut() = Some(reply);
3376 self.devtools_markers.borrow_mut().extend(markers);
3377 }
3378
3379 pub(crate) fn drop_devtools_timeline_markers(&self, markers: Vec<TimelineMarkerType>) {
3380 let mut devtools_markers = self.devtools_markers.borrow_mut();
3381 for marker in markers {
3382 devtools_markers.remove(&marker);
3383 }
3384 if devtools_markers.is_empty() {
3385 *self.devtools_marker_sender.borrow_mut() = None;
3386 }
3387 }
3388
3389 pub(crate) fn set_webdriver_script_chan(&self, chan: Option<GenericSender<WebDriverJSResult>>) {
3390 *self.webdriver_script_chan.borrow_mut() = chan;
3391 }
3392
3393 pub(crate) fn set_webdriver_load_status_sender(
3394 &self,
3395 sender: Option<GenericSender<WebDriverLoadStatus>>,
3396 ) {
3397 *self.webdriver_load_status_sender.borrow_mut() = sender;
3398 }
3399
3400 pub(crate) fn is_alive(&self) -> bool {
3401 self.current_state.get() == WindowState::Alive
3402 }
3403
3404 pub(crate) fn is_top_level(&self) -> bool {
3406 self.parent_info.is_none()
3407 }
3408
3409 fn run_resize_steps_for_layout_viewport(&self, can_gc: CanGc) -> bool {
3414 let Some((new_size, size_type)) = self.take_unhandled_resize_event() else {
3415 return false;
3416 };
3417
3418 if self.viewport_details() == new_size {
3419 return false;
3420 }
3421
3422 let _realm = enter_realm(self);
3423 debug!(
3424 "Resizing Window for pipeline {:?} from {:?} to {new_size:?}",
3425 self.pipeline_id(),
3426 self.viewport_details(),
3427 );
3428 self.set_viewport_details(new_size);
3429
3430 self.Document()
3434 .add_restyle_reason(RestyleReason::ViewportChanged);
3435
3436 if self.layout().device().used_viewport_units() {
3439 self.Document().dirty_all_nodes();
3440 }
3441
3442 if size_type == WindowSizeType::Resize {
3444 let uievent = UIEvent::new(
3445 self,
3446 DOMString::from("resize"),
3447 EventBubbles::DoesNotBubble,
3448 EventCancelable::NotCancelable,
3449 Some(self),
3450 0i32,
3451 0u32,
3452 can_gc,
3453 );
3454 uievent.upcast::<Event>().fire(self.upcast(), can_gc);
3455 }
3456
3457 true
3458 }
3459
3460 pub(crate) fn run_the_resize_steps(&self, can_gc: CanGc) -> bool {
3465 let layout_viewport_resized = self.run_resize_steps_for_layout_viewport(can_gc);
3466
3467 if self.has_changed_visual_viewport_dimension.get() {
3468 let visual_viewport = self.get_or_init_visual_viewport(can_gc);
3469
3470 let uievent = UIEvent::new(
3471 self,
3472 DOMString::from("resize"),
3473 EventBubbles::DoesNotBubble,
3474 EventCancelable::NotCancelable,
3475 Some(self),
3476 0i32,
3477 0u32,
3478 can_gc,
3479 );
3480 uievent
3481 .upcast::<Event>()
3482 .fire(visual_viewport.upcast(), can_gc);
3483
3484 self.has_changed_visual_viewport_dimension.set(false);
3485 }
3486
3487 layout_viewport_resized
3488 }
3489
3490 pub(crate) fn evaluate_media_queries_and_report_changes(&self, can_gc: CanGc) {
3493 let _realm = enter_realm(self);
3494
3495 rooted_vec!(let mut mql_list);
3496 self.media_query_lists.for_each(|mql| {
3497 if let MediaQueryListMatchState::Changed = mql.evaluate_changes() {
3498 mql_list.push(Dom::from_ref(&*mql));
3500 }
3501 });
3502 for mql in mql_list.iter() {
3504 let event = MediaQueryListEvent::new(
3505 &mql.global(),
3506 atom!("change"),
3507 false,
3508 false,
3509 mql.Media(),
3510 mql.Matches(),
3511 can_gc,
3512 );
3513 event
3514 .upcast::<Event>()
3515 .fire(mql.upcast::<EventTarget>(), can_gc);
3516 }
3517 }
3518
3519 pub(crate) fn set_throttled(&self, throttled: bool) {
3521 self.throttled.set(throttled);
3522 if throttled {
3523 self.as_global_scope().slow_down_timers();
3524 } else {
3525 self.as_global_scope().speed_up_timers();
3526 }
3527 }
3528
3529 pub(crate) fn throttled(&self) -> bool {
3530 self.throttled.get()
3531 }
3532
3533 pub(crate) fn unminified_css_dir(&self) -> Option<String> {
3534 self.unminified_css_dir.borrow().clone()
3535 }
3536
3537 pub(crate) fn local_script_source(&self) -> &Option<String> {
3538 &self.local_script_source
3539 }
3540
3541 pub(crate) fn set_navigation_start(&self) {
3542 self.navigation_start.set(CrossProcessInstant::now());
3543 }
3544
3545 pub(crate) fn set_last_activation_timestamp(&self, time: UserActivationTimestamp) {
3546 self.last_activation_timestamp.set(time);
3547 }
3548
3549 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
3550 self.as_global_scope()
3551 .script_to_embedder_chan()
3552 .send(msg)
3553 .unwrap();
3554 }
3555
3556 pub(crate) fn send_to_constellation(&self, msg: ScriptToConstellationMessage) {
3557 self.as_global_scope()
3558 .script_to_constellation_chan()
3559 .send(msg)
3560 .unwrap();
3561 }
3562
3563 #[cfg(feature = "webxr")]
3564 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3565 self.navigator
3566 .get()
3567 .as_ref()
3568 .and_then(|nav| nav.xr())
3569 .is_some_and(|xr| xr.pending_or_active_session())
3570 }
3571
3572 #[cfg(not(feature = "webxr"))]
3573 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3574 false
3575 }
3576
3577 #[expect(unsafe_code)]
3578 fn handle_pending_images_post_reflow(
3579 &self,
3580 pending_images: Vec<PendingImage>,
3581 pending_rasterization_images: Vec<PendingRasterizationImage>,
3582 pending_svg_element_for_serialization: Vec<UntrustedNodeAddress>,
3583 ) {
3584 let pipeline_id = self.pipeline_id();
3585 for image in pending_images {
3586 let id = image.id;
3587 let node = unsafe { from_untrusted_node_address(image.node) };
3588
3589 if let PendingImageState::Unrequested(ref url) = image.state {
3590 fetch_image_for_layout(url.clone(), &node, id, self.image_cache.clone());
3591 }
3592
3593 let mut images = self.pending_layout_images.borrow_mut();
3594 if !images.contains_key(&id) {
3595 let trusted_node = Trusted::new(&*node);
3596 let sender = self.register_image_cache_listener(id, move |response, _| {
3597 trusted_node
3598 .root()
3599 .owner_window()
3600 .pending_layout_image_notification(response);
3601 });
3602
3603 self.image_cache
3604 .add_listener(ImageLoadListener::new(sender, pipeline_id, id));
3605 }
3606
3607 let nodes = images.entry(id).or_default();
3608 if !nodes.iter().any(|n| std::ptr::eq(&*(n.node), &*node)) {
3609 nodes.push(PendingLayoutImageAncillaryData {
3610 node: Dom::from_ref(&*node),
3611 destination: image.destination,
3612 });
3613 }
3614 }
3615
3616 for image in pending_rasterization_images {
3617 let node = unsafe { from_untrusted_node_address(image.node) };
3618
3619 let mut images = self.pending_images_for_rasterization.borrow_mut();
3620 if !images.contains_key(&(image.id, image.size)) {
3621 let image_cache_sender = self.image_cache_sender.clone();
3622 self.image_cache.add_rasterization_complete_listener(
3623 pipeline_id,
3624 image.id,
3625 image.size,
3626 Box::new(move |response| {
3627 let _ = image_cache_sender.send(response);
3628 }),
3629 );
3630 }
3631
3632 let nodes = images.entry((image.id, image.size)).or_default();
3633 if !nodes.iter().any(|n| std::ptr::eq(&**n, &*node)) {
3634 nodes.push(Dom::from_ref(&*node));
3635 }
3636 }
3637
3638 for node in pending_svg_element_for_serialization.into_iter() {
3639 let node = unsafe { from_untrusted_node_address(node) };
3640 let svg = node.downcast::<SVGSVGElement>().unwrap();
3641 svg.serialize_and_cache_subtree();
3642 node.dirty(NodeDamage::Other);
3643 }
3644 }
3645
3646 pub(crate) fn has_sticky_activation(&self) -> bool {
3648 UserActivationTimestamp::TimeStamp(CrossProcessInstant::now()) >=
3650 self.last_activation_timestamp.get()
3651 }
3652
3653 pub(crate) fn has_transient_activation(&self) -> bool {
3655 let current_time = CrossProcessInstant::now();
3658 UserActivationTimestamp::TimeStamp(current_time) >= self.last_activation_timestamp.get() &&
3659 UserActivationTimestamp::TimeStamp(current_time) <
3660 self.last_activation_timestamp.get() +
3661 pref!(dom_transient_activation_duration_ms)
3662 }
3663
3664 pub(crate) fn consume_last_activation_timestamp(&self) {
3665 if self.last_activation_timestamp.get() != UserActivationTimestamp::PositiveInfinity {
3666 self.set_last_activation_timestamp(UserActivationTimestamp::NegativeInfinity);
3667 }
3668 }
3669
3670 pub(crate) fn consume_user_activation(&self) {
3672 if self.undiscarded_window_proxy().is_none() {
3675 return;
3676 }
3677
3678 let Some(top_level_document) = self.top_level_document_if_local() else {
3682 return;
3683 };
3684
3685 top_level_document
3693 .window()
3694 .consume_last_activation_timestamp();
3695 for document in SameOriginDescendantNavigablesIterator::new(top_level_document) {
3696 document.window().consume_last_activation_timestamp();
3697 }
3698 }
3699
3700 #[allow(clippy::too_many_arguments)]
3701 pub(crate) fn new(
3702 cx: &mut js::context::JSContext,
3703 webview_id: WebViewId,
3704 runtime: Rc<Runtime>,
3705 script_chan: Sender<MainThreadScriptMsg>,
3706 layout: Box<dyn Layout>,
3707 font_context: Arc<FontContext>,
3708 image_cache_sender: Sender<ImageCacheResponseMessage>,
3709 image_cache: Arc<dyn ImageCache>,
3710 resource_threads: ResourceThreads,
3711 storage_threads: StorageThreads,
3712 #[cfg(feature = "bluetooth")] bluetooth_thread: GenericSender<BluetoothRequest>,
3713 mem_profiler_chan: MemProfilerChan,
3714 time_profiler_chan: TimeProfilerChan,
3715 devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
3716 constellation_chan: ScriptToConstellationChan,
3717 embedder_chan: ScriptToEmbedderChan,
3718 control_chan: GenericSender<ScriptThreadMessage>,
3719 pipeline_id: PipelineId,
3720 parent_info: Option<PipelineId>,
3721 viewport_details: ViewportDetails,
3722 origin: MutableOrigin,
3723 creation_url: ServoUrl,
3724 top_level_creation_url: ServoUrl,
3725 navigation_start: CrossProcessInstant,
3726 webgl_chan: Option<WebGLChan>,
3727 #[cfg(feature = "webxr")] webxr_registry: Option<webxr_api::Registry>,
3728 paint_api: CrossProcessPaintApi,
3729 unminify_js: bool,
3730 unminify_css: bool,
3731 local_script_source: Option<String>,
3732 user_scripts: Rc<Vec<UserScript>>,
3733 player_context: WindowGLContext,
3734 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
3735 inherited_secure_context: Option<bool>,
3736 theme: Theme,
3737 weak_script_thread: Weak<ScriptThread>,
3738 ) -> DomRoot<Self> {
3739 let error_reporter = CSSErrorReporter {
3740 pipelineid: pipeline_id,
3741 script_chan: control_chan,
3742 };
3743
3744 let win = Box::new(Self {
3745 webview_id,
3746 globalscope: GlobalScope::new_inherited(
3747 pipeline_id,
3748 devtools_chan,
3749 mem_profiler_chan,
3750 time_profiler_chan,
3751 constellation_chan,
3752 embedder_chan,
3753 resource_threads,
3754 storage_threads,
3755 origin,
3756 creation_url,
3757 Some(top_level_creation_url),
3758 #[cfg(feature = "webgpu")]
3759 gpu_id_hub,
3760 inherited_secure_context,
3761 unminify_js,
3762 Some(font_context.clone()),
3763 ),
3764 ongoing_navigation: Default::default(),
3765 script_chan,
3766 layout: RefCell::new(layout),
3767 image_cache_sender,
3768 image_cache,
3769 navigator: Default::default(),
3770 location: Default::default(),
3771 history: Default::default(),
3772 custom_element_registry: Default::default(),
3773 window_proxy: Default::default(),
3774 document: Default::default(),
3775 performance: Default::default(),
3776 navigation_start: Cell::new(navigation_start),
3777 screen: Default::default(),
3778 session_storage: Default::default(),
3779 local_storage: Default::default(),
3780 status: DomRefCell::new(DOMString::new()),
3781 parent_info,
3782 dom_static: GlobalStaticData::new(),
3783 js_runtime: DomRefCell::new(Some(runtime.clone())),
3784 #[cfg(feature = "bluetooth")]
3785 bluetooth_thread,
3786 #[cfg(feature = "bluetooth")]
3787 bluetooth_extra_permission_data: BluetoothExtraPermissionData::new(),
3788 unhandled_resize_event: Default::default(),
3789 viewport_details: Cell::new(viewport_details),
3790 layout_blocker: Cell::new(LayoutBlocker::WaitingForParse),
3791 current_state: Cell::new(WindowState::Alive),
3792 devtools_marker_sender: Default::default(),
3793 devtools_markers: Default::default(),
3794 webdriver_script_chan: Default::default(),
3795 webdriver_load_status_sender: Default::default(),
3796 error_reporter,
3797 media_query_lists: DOMTracker::new(),
3798 #[cfg(feature = "bluetooth")]
3799 test_runner: Default::default(),
3800 webgl_chan,
3801 #[cfg(feature = "webxr")]
3802 webxr_registry,
3803 pending_image_callbacks: Default::default(),
3804 pending_layout_images: Default::default(),
3805 pending_images_for_rasterization: Default::default(),
3806 unminified_css_dir: Default::default(),
3807 local_script_source,
3808 test_worklet: Default::default(),
3809 paint_worklet: Default::default(),
3810 exists_mut_observer: Cell::new(false),
3811 paint_api,
3812 has_sent_idle_message: Cell::new(false),
3813 unminify_css,
3814 user_scripts,
3815 player_context,
3816 throttled: Cell::new(false),
3817 layout_marker: DomRefCell::new(Rc::new(Cell::new(true))),
3818 current_event: DomRefCell::new(None),
3819 theme: Cell::new(theme),
3820 trusted_types: Default::default(),
3821 reporting_observer_list: Default::default(),
3822 report_list: Default::default(),
3823 endpoints_list: Default::default(),
3824 script_window_proxies: ScriptThread::window_proxies(),
3825 has_pending_screenshot_readiness_request: Default::default(),
3826 visual_viewport: Default::default(),
3827 weak_script_thread,
3828 has_changed_visual_viewport_dimension: Default::default(),
3829 last_activation_timestamp: Cell::new(UserActivationTimestamp::PositiveInfinity),
3830 });
3831
3832 WindowBinding::Wrap::<crate::DomTypeHolder>(cx, win)
3833 }
3834
3835 pub(crate) fn pipeline_id(&self) -> PipelineId {
3836 self.as_global_scope().pipeline_id()
3837 }
3838
3839 pub(crate) fn cache_layout_value<T>(&self, value: T) -> LayoutValue<T>
3841 where
3842 T: Copy + MallocSizeOf,
3843 {
3844 LayoutValue::new(self.layout_marker.borrow().clone(), value)
3845 }
3846}
3847
3848#[derive(MallocSizeOf)]
3853pub(crate) struct LayoutValue<T: MallocSizeOf> {
3854 #[conditional_malloc_size_of]
3855 is_valid: Rc<Cell<bool>>,
3856 value: T,
3857}
3858
3859#[expect(unsafe_code)]
3860unsafe impl<T: JSTraceable + MallocSizeOf> JSTraceable for LayoutValue<T> {
3861 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
3862 unsafe { self.value.trace(trc) };
3863 }
3864}
3865
3866impl<T: Copy + MallocSizeOf> LayoutValue<T> {
3867 fn new(marker: Rc<Cell<bool>>, value: T) -> Self {
3868 LayoutValue {
3869 is_valid: marker,
3870 value,
3871 }
3872 }
3873
3874 pub(crate) fn get(&self) -> Result<T, ()> {
3876 if self.is_valid.get() {
3877 return Ok(self.value);
3878 }
3879 Err(())
3880 }
3881}
3882
3883fn should_move_clip_rect(clip_rect: UntypedRect<Au>, new_viewport: UntypedRect<f32>) -> bool {
3884 let clip_rect = UntypedRect::new(
3885 Point2D::new(
3886 clip_rect.origin.x.to_f32_px(),
3887 clip_rect.origin.y.to_f32_px(),
3888 ),
3889 Size2D::new(
3890 clip_rect.size.width.to_f32_px(),
3891 clip_rect.size.height.to_f32_px(),
3892 ),
3893 );
3894
3895 static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5;
3899 let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE;
3900
3901 (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width ||
3902 (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width ||
3903 (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height ||
3904 (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height
3905}
3906
3907impl Window {
3908 pub(crate) fn post_message(
3910 &self,
3911 target_origin: Option<ImmutableOrigin>,
3912 source_origin: ImmutableOrigin,
3913 source: &WindowProxy,
3914 data: StructuredSerializedData,
3915 ) {
3916 let this = Trusted::new(self);
3917 let source = Trusted::new(source);
3918 let task = task!(post_serialised_message: move || {
3919 let this = this.root();
3920 let source = source.root();
3921 let document = this.Document();
3922
3923 if let Some(ref target_origin) = target_origin {
3925 if !target_origin.same_origin(document.origin()) {
3926 return;
3927 }
3928 }
3929
3930 let cx = this.get_cx();
3932 let obj = this.reflector().get_jsobject();
3933 let _ac = JSAutoRealm::new(*cx, obj.get());
3934 rooted!(in(*cx) let mut message_clone = UndefinedValue());
3935 if let Ok(ports) = structuredclone::read(this.upcast(), data, message_clone.handle_mut(), CanGc::note()) {
3936 MessageEvent::dispatch_jsval(
3938 this.upcast(),
3939 this.upcast(),
3940 message_clone.handle(),
3941 Some(&source_origin.ascii_serialization()),
3942 Some(&*source),
3943 ports,
3944 CanGc::note()
3945 );
3946 } else {
3947 MessageEvent::dispatch_error(
3949 this.upcast(),
3950 this.upcast(),
3951 CanGc::note()
3952 );
3953 }
3954 });
3955 self.as_global_scope()
3957 .task_manager()
3958 .dom_manipulation_task_source()
3959 .queue(task);
3960 }
3961}
3962
3963#[derive(Clone, MallocSizeOf)]
3964pub(crate) struct CSSErrorReporter {
3965 pub(crate) pipelineid: PipelineId,
3966 pub(crate) script_chan: GenericSender<ScriptThreadMessage>,
3967}
3968unsafe_no_jsmanaged_fields!(CSSErrorReporter);
3969
3970impl ParseErrorReporter for CSSErrorReporter {
3971 fn report_error(
3972 &self,
3973 url: &UrlExtraData,
3974 location: SourceLocation,
3975 error: ContextualParseError,
3976 ) {
3977 if log_enabled!(log::Level::Info) {
3978 info!(
3979 "Url:\t{}\n{}:{} {}",
3980 url.0.as_str(),
3981 location.line,
3982 location.column,
3983 error
3984 )
3985 }
3986
3987 let _ = self.script_chan.send(ScriptThreadMessage::ReportCSSError(
3989 self.pipelineid,
3990 url.0.to_string(),
3991 location.line,
3992 location.column,
3993 error.to_string(),
3994 ));
3995 }
3996}
3997
3998fn is_named_element_with_name_attribute(elem: &Element) -> bool {
3999 let type_ = match elem.upcast::<Node>().type_id() {
4000 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
4001 _ => return false,
4002 };
4003 matches!(
4004 type_,
4005 HTMLElementTypeId::HTMLEmbedElement |
4006 HTMLElementTypeId::HTMLFormElement |
4007 HTMLElementTypeId::HTMLImageElement |
4008 HTMLElementTypeId::HTMLObjectElement
4009 )
4010}
4011
4012fn is_named_element_with_id_attribute(elem: &Element) -> bool {
4013 elem.is_html_element()
4014}
4015
4016#[expect(unsafe_code)]
4017#[unsafe(no_mangle)]
4018unsafe extern "C" fn dump_js_stack(cx: *mut RawJSContext) {
4020 unsafe {
4021 DumpJSStack(cx, true, false, false);
4022 }
4023}
4024
4025impl WindowHelpers for Window {
4026 fn create_named_properties_object(
4027 cx: SafeJSContext,
4028 proto: HandleObject,
4029 object: MutableHandleObject,
4030 ) {
4031 Self::create_named_properties_object(cx, proto, object)
4032 }
4033}