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: &mut JSContext, value: HandleValue) {
1784 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1785 if let Some(webdriver_script_sender) = webdriver_script_sender {
1786 let error_info = ErrorInfo::from_value(value, cx.into(), CanGc::from_cx(cx));
1787 let _ = webdriver_script_sender.send(Err(
1788 JavaScriptEvaluationError::EvaluationFailure(Some(
1789 javascript_error_info_from_error_info(cx, &error_info, value),
1790 )),
1791 ));
1792 }
1793 }
1794
1795 fn WebdriverElement(&self, id: DOMString) -> Option<DomRoot<Element>> {
1796 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1797 }
1798
1799 fn WebdriverFrame(&self, browsing_context_id: DOMString) -> Option<DomRoot<WindowProxy>> {
1800 self.Document()
1801 .iframes()
1802 .iter()
1803 .find(|iframe| {
1804 iframe
1805 .browsing_context_id()
1806 .as_ref()
1807 .map(BrowsingContextId::to_string) ==
1808 Some(browsing_context_id.to_string())
1809 })
1810 .and_then(|iframe| iframe.GetContentWindow())
1811 }
1812
1813 fn WebdriverWindow(&self, webview_id: DOMString) -> DomRoot<WindowProxy> {
1814 let window_proxy = &self
1815 .window_proxy
1816 .get()
1817 .expect("Should always have a WindowProxy when calling WebdriverWindow");
1818 assert!(
1819 self.is_top_level(),
1820 "Window must be top level browsing context."
1821 );
1822 assert!(self.webview_id().to_string() == webview_id);
1823 DomRoot::from_ref(window_proxy)
1824 }
1825
1826 fn WebdriverShadowRoot(&self, id: DOMString) -> Option<DomRoot<ShadowRoot>> {
1827 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1828 }
1829
1830 fn GetComputedStyle(
1832 &self,
1833 element: &Element,
1834 pseudo: Option<DOMString>,
1835 ) -> DomRoot<CSSStyleDeclaration> {
1836 let mut is_null = false;
1840
1841 let pseudo = pseudo.map(|mut s| {
1844 s.make_ascii_lowercase();
1845 s
1846 });
1847 let pseudo = match pseudo {
1848 Some(ref pseudo) if pseudo == ":before" || pseudo == "::before" => {
1849 Some(PseudoElement::Before)
1850 },
1851 Some(ref pseudo) if pseudo == ":after" || pseudo == "::after" => {
1852 Some(PseudoElement::After)
1853 },
1854 Some(ref pseudo) if pseudo == "::selection" => Some(PseudoElement::Selection),
1855 Some(ref pseudo) if pseudo == "::marker" => Some(PseudoElement::Marker),
1856 Some(ref pseudo) if pseudo.starts_with(':') => {
1857 is_null = true;
1860 None
1861 },
1862 _ => None,
1863 };
1864
1865 CSSStyleDeclaration::new(
1881 self,
1882 if is_null {
1883 CSSStyleOwner::Null
1884 } else {
1885 CSSStyleOwner::Element(Dom::from_ref(element))
1886 },
1887 pseudo,
1888 CSSModificationAccess::Readonly,
1889 CanGc::note(),
1890 )
1891 }
1892
1893 fn InnerHeight(&self) -> i32 {
1896 self.viewport_details
1897 .get()
1898 .size
1899 .height
1900 .to_i32()
1901 .unwrap_or(0)
1902 }
1903
1904 fn InnerWidth(&self) -> i32 {
1907 self.viewport_details.get().size.width.to_i32().unwrap_or(0)
1908 }
1909
1910 fn ScrollX(&self) -> i32 {
1912 self.scroll_offset().x as i32
1913 }
1914
1915 fn PageXOffset(&self) -> i32 {
1917 self.ScrollX()
1918 }
1919
1920 fn ScrollY(&self) -> i32 {
1922 self.scroll_offset().y as i32
1923 }
1924
1925 fn PageYOffset(&self) -> i32 {
1927 self.ScrollY()
1928 }
1929
1930 fn Scroll(&self, options: &ScrollToOptions) {
1932 let x = options.left.unwrap_or(0.0) as f32;
1937
1938 let y = options.top.unwrap_or(0.0) as f32;
1941
1942 self.scroll(x, y, options.parent.behavior);
1944 }
1945
1946 fn Scroll_(&self, x: f64, y: f64) {
1948 self.scroll(x as f32, y as f32, ScrollBehavior::Auto);
1952 }
1953
1954 fn ScrollTo(&self, options: &ScrollToOptions) {
1959 self.Scroll(options);
1960 }
1961
1962 fn ScrollTo_(&self, x: f64, y: f64) {
1967 self.Scroll_(x, y)
1968 }
1969
1970 fn ScrollBy(&self, options: &ScrollToOptions) {
1972 let mut options = options.clone();
1978 let x = options.left.unwrap_or(0.0);
1979 let x = if x.is_finite() { x } else { 0.0 };
1980 let y = options.top.unwrap_or(0.0);
1981 let y = if y.is_finite() { y } else { 0.0 };
1982
1983 options.left.replace(x + self.ScrollX() as f64);
1985
1986 options.top.replace(y + self.ScrollY() as f64);
1988
1989 self.Scroll(&options)
1991 }
1992
1993 fn ScrollBy_(&self, x: f64, y: f64) {
1995 let mut options = ScrollToOptions::empty();
1999
2000 options.left.replace(x);
2003
2004 options.top.replace(y);
2006
2007 self.ScrollBy(&options);
2009 }
2010
2011 fn ResizeTo(&self, width: i32, height: i32) {
2013 let window_proxy = match self.window_proxy.get() {
2015 Some(proxy) => proxy,
2016 None => return,
2017 };
2018
2019 if !window_proxy.is_auxiliary() {
2022 return;
2023 }
2024
2025 let dpr = self.device_pixel_ratio();
2026 let size = Size2D::new(width, height).to_f32() * dpr;
2027 self.send_to_embedder(EmbedderMsg::ResizeTo(self.webview_id(), size.to_i32()));
2028 }
2029
2030 fn ResizeBy(&self, x: i32, y: i32) {
2032 let size = self.client_window().size();
2033 self.ResizeTo(x + size.width, y + size.height)
2035 }
2036
2037 fn MoveTo(&self, x: i32, y: i32) {
2039 let dpr = self.device_pixel_ratio();
2042 let point = Point2D::new(x, y).to_f32() * dpr;
2043 let msg = EmbedderMsg::MoveTo(self.webview_id(), point.to_i32());
2044 self.send_to_embedder(msg);
2045 }
2046
2047 fn MoveBy(&self, x: i32, y: i32) {
2049 let origin = self.client_window().min;
2050 self.MoveTo(x + origin.x, y + origin.y)
2052 }
2053
2054 fn ScreenX(&self) -> i32 {
2056 self.client_window().min.x
2057 }
2058
2059 fn ScreenY(&self) -> i32 {
2061 self.client_window().min.y
2062 }
2063
2064 fn OuterHeight(&self) -> i32 {
2066 self.client_window().height()
2067 }
2068
2069 fn OuterWidth(&self) -> i32 {
2071 self.client_window().width()
2072 }
2073
2074 fn DevicePixelRatio(&self) -> Finite<f64> {
2076 Finite::wrap(self.device_pixel_ratio().get() as f64)
2077 }
2078
2079 fn Status(&self) -> DOMString {
2081 self.status.borrow().clone()
2082 }
2083
2084 fn SetStatus(&self, status: DOMString) {
2086 *self.status.borrow_mut() = status
2087 }
2088
2089 fn MatchMedia(&self, query: DOMString) -> DomRoot<MediaQueryList> {
2091 let media_query_list = MediaList::parse_media_list(&query.str(), self);
2092 let document = self.Document();
2093 let mql = MediaQueryList::new(&document, media_query_list, CanGc::note());
2094 self.media_query_lists.track(&*mql);
2095 mql
2096 }
2097
2098 fn Fetch(
2100 &self,
2101 input: RequestOrUSVString,
2102 init: RootedTraceableBox<RequestInit>,
2103 comp: InRealm,
2104 can_gc: CanGc,
2105 ) -> Rc<Promise> {
2106 fetch::Fetch(self.upcast(), input, init, comp, can_gc)
2107 }
2108
2109 fn FetchLater(
2111 &self,
2112 input: RequestInfo,
2113 init: RootedTraceableBox<DeferredRequestInit>,
2114 can_gc: CanGc,
2115 ) -> Fallible<DomRoot<FetchLaterResult>> {
2116 fetch::FetchLater(self, input, init, can_gc)
2117 }
2118
2119 #[cfg(feature = "bluetooth")]
2120 fn TestRunner(&self) -> DomRoot<TestRunner> {
2121 self.test_runner
2122 .or_init(|| TestRunner::new(self.upcast(), CanGc::note()))
2123 }
2124
2125 fn RunningAnimationCount(&self) -> u32 {
2126 self.document
2127 .get()
2128 .map_or(0, |d| d.animations().running_animation_count() as u32)
2129 }
2130
2131 fn SetName(&self, name: DOMString) {
2133 if let Some(proxy) = self.undiscarded_window_proxy() {
2134 proxy.set_name(name);
2135 }
2136 }
2137
2138 fn Name(&self) -> DOMString {
2140 match self.undiscarded_window_proxy() {
2141 Some(proxy) => proxy.get_name(),
2142 None => "".into(),
2143 }
2144 }
2145
2146 fn Origin(&self) -> USVString {
2148 USVString(self.origin().immutable().ascii_serialization())
2149 }
2150
2151 fn GetSelection(&self) -> Option<DomRoot<Selection>> {
2153 self.document
2154 .get()
2155 .and_then(|d| d.GetSelection(CanGc::note()))
2156 }
2157
2158 fn Event(&self, cx: SafeJSContext, rval: MutableHandleValue) {
2160 if let Some(ref event) = *self.current_event.borrow() {
2161 event
2162 .reflector()
2163 .get_jsobject()
2164 .safe_to_jsval(cx, rval, CanGc::note());
2165 }
2166 }
2167
2168 fn IsSecureContext(&self) -> bool {
2169 self.as_global_scope().is_secure_context()
2170 }
2171
2172 fn NamedGetter(&self, name: DOMString) -> Option<NamedPropertyValue> {
2174 if name.is_empty() {
2175 return None;
2176 }
2177 let document = self.Document();
2178
2179 let iframes: Vec<_> = document
2181 .iframes()
2182 .iter()
2183 .filter(|iframe| {
2184 if let Some(window) = iframe.GetContentWindow() {
2185 return window.get_name() == name;
2186 }
2187 false
2188 })
2189 .collect();
2190
2191 let iframe_iter = iframes.iter().map(|iframe| iframe.upcast::<Element>());
2192
2193 let name = Atom::from(name);
2194
2195 let elements_with_name = document.get_elements_with_name(&name);
2197 let name_iter = elements_with_name
2198 .iter()
2199 .map(|element| &**element)
2200 .filter(|elem| is_named_element_with_name_attribute(elem));
2201 let elements_with_id = document.get_elements_with_id(&name);
2202 let id_iter = elements_with_id
2203 .iter()
2204 .map(|element| &**element)
2205 .filter(|elem| is_named_element_with_id_attribute(elem));
2206
2207 for elem in iframe_iter.clone() {
2209 if let Some(nested_window_proxy) = elem
2210 .downcast::<HTMLIFrameElement>()
2211 .and_then(|iframe| iframe.GetContentWindow())
2212 {
2213 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
2214 }
2215 }
2216
2217 let mut elements = iframe_iter.chain(name_iter).chain(id_iter);
2218
2219 let first = elements.next()?;
2220
2221 if elements.next().is_none() {
2222 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
2224 }
2225
2226 #[derive(JSTraceable, MallocSizeOf)]
2228 struct WindowNamedGetter {
2229 #[no_trace]
2230 name: Atom,
2231 }
2232 impl CollectionFilter for WindowNamedGetter {
2233 fn filter(&self, elem: &Element, _root: &Node) -> bool {
2234 let type_ = match elem.upcast::<Node>().type_id() {
2235 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
2236 _ => return false,
2237 };
2238 if elem.get_id().as_ref() == Some(&self.name) {
2239 return true;
2240 }
2241 match type_ {
2242 HTMLElementTypeId::HTMLEmbedElement |
2243 HTMLElementTypeId::HTMLFormElement |
2244 HTMLElementTypeId::HTMLImageElement |
2245 HTMLElementTypeId::HTMLObjectElement => {
2246 elem.get_name().as_ref() == Some(&self.name)
2247 },
2248 _ => false,
2249 }
2250 }
2251 }
2252 let collection = HTMLCollection::create(
2253 self,
2254 document.upcast(),
2255 Box::new(WindowNamedGetter { name }),
2256 CanGc::note(),
2257 );
2258 Some(NamedPropertyValue::HTMLCollection(collection))
2259 }
2260
2261 fn SupportedPropertyNames(&self) -> Vec<DOMString> {
2263 let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
2264
2265 let document = self.Document();
2266 let name_map = document.name_map();
2267 for (name, elements) in &name_map.0 {
2268 if name.is_empty() {
2269 continue;
2270 }
2271 let mut name_iter = elements
2272 .iter()
2273 .filter(|elem| is_named_element_with_name_attribute(elem));
2274 if let Some(first) = name_iter.next() {
2275 names_with_first_named_element_map.insert(name, first);
2276 }
2277 }
2278 let id_map = document.id_map();
2279 for (id, elements) in &id_map.0 {
2280 if id.is_empty() {
2281 continue;
2282 }
2283 let mut id_iter = elements
2284 .iter()
2285 .filter(|elem| is_named_element_with_id_attribute(elem));
2286 if let Some(first) = id_iter.next() {
2287 match names_with_first_named_element_map.entry(id) {
2288 Entry::Vacant(entry) => drop(entry.insert(first)),
2289 Entry::Occupied(mut entry) => {
2290 if first.upcast::<Node>().is_before(entry.get().upcast()) {
2291 *entry.get_mut() = first;
2292 }
2293 },
2294 }
2295 }
2296 }
2297
2298 let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
2299 names_with_first_named_element_map
2300 .iter()
2301 .map(|(k, v)| (*k, *v))
2302 .collect();
2303 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
2304 if a.1 == b.1 {
2305 a.0.cmp(b.0)
2308 } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
2309 cmp::Ordering::Less
2310 } else {
2311 cmp::Ordering::Greater
2312 }
2313 });
2314
2315 names_with_first_named_element_vec
2316 .iter()
2317 .map(|(k, _v)| DOMString::from(&***k))
2318 .collect()
2319 }
2320
2321 fn StructuredClone(
2323 &self,
2324 cx: SafeJSContext,
2325 value: HandleValue,
2326 options: RootedTraceableBox<StructuredSerializeOptions>,
2327 can_gc: CanGc,
2328 retval: MutableHandleValue,
2329 ) -> Fallible<()> {
2330 self.as_global_scope()
2331 .structured_clone(cx, value, options, retval, can_gc)
2332 }
2333
2334 fn TrustedTypes(&self, can_gc: CanGc) -> DomRoot<TrustedTypePolicyFactory> {
2335 self.trusted_types
2336 .or_init(|| TrustedTypePolicyFactory::new(self.as_global_scope(), can_gc))
2337 }
2338}
2339
2340impl Window {
2341 pub(crate) fn scroll_offset(&self) -> Vector2D<f32, LayoutPixel> {
2342 self.scroll_offset_query_with_external_scroll_id(self.pipeline_id().root_scroll_id())
2343 }
2344
2345 pub(crate) fn create_named_properties_object(
2348 cx: SafeJSContext,
2349 proto: HandleObject,
2350 object: MutableHandleObject,
2351 ) {
2352 window_named_properties::create(cx, proto, object)
2353 }
2354
2355 pub(crate) fn current_event(&self) -> Option<DomRoot<Event>> {
2356 self.current_event
2357 .borrow()
2358 .as_ref()
2359 .map(|e| DomRoot::from_ref(&**e))
2360 }
2361
2362 pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
2363 let current = self.current_event();
2364 *self.current_event.borrow_mut() = event.map(Dom::from_ref);
2365 current
2366 }
2367
2368 fn post_message_impl(
2370 &self,
2371 target_origin: &USVString,
2372 source_origin: ImmutableOrigin,
2373 source: &Window,
2374 cx: &mut JSContext,
2375 message: HandleValue,
2376 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
2377 ) -> ErrorResult {
2378 let data = structuredclone::write(cx.into(), message, Some(transfer))?;
2380
2381 let target_origin = match target_origin.0[..].as_ref() {
2383 "*" => None,
2384 "/" => Some(source_origin.clone()),
2385 url => match ServoUrl::parse(url) {
2386 Ok(url) => Some(url.origin().clone()),
2387 Err(_) => return Err(Error::Syntax(None)),
2388 },
2389 };
2390
2391 self.post_message(target_origin, source_origin, &source.window_proxy(), data);
2393 Ok(())
2394 }
2395
2396 pub(crate) fn paint_worklet(&self) -> DomRoot<Worklet> {
2398 self.paint_worklet
2399 .or_init(|| self.new_paint_worklet(CanGc::note()))
2400 }
2401
2402 pub(crate) fn has_document(&self) -> bool {
2403 self.document.get().is_some()
2404 }
2405
2406 pub(crate) fn clear_js_runtime(&self) {
2407 self.as_global_scope()
2408 .remove_web_messaging_and_dedicated_workers_infra();
2409
2410 if let Some(custom_elements) = self.custom_element_registry.get() {
2413 custom_elements.teardown();
2414 }
2415
2416 self.current_state.set(WindowState::Zombie);
2417 *self.js_runtime.borrow_mut() = None;
2418
2419 if let Some(proxy) = self.window_proxy.get() {
2422 let pipeline_id = self.pipeline_id();
2423 if let Some(currently_active) = proxy.currently_active() {
2424 if currently_active == pipeline_id {
2425 self.window_proxy.set(None);
2426 }
2427 }
2428 }
2429
2430 if let Some(performance) = self.performance.get() {
2431 performance.clear_and_disable_performance_entry_buffer();
2432 }
2433 self.as_global_scope()
2434 .task_manager()
2435 .cancel_all_tasks_and_ignore_future_tasks();
2436 }
2437
2438 pub(crate) fn scroll(&self, x: f32, y: f32, behavior: ScrollBehavior) {
2440 let xfinite = if x.is_finite() { x } else { 0.0 };
2442 let yfinite = if y.is_finite() { y } else { 0.0 };
2443
2444 let viewport = self.viewport_details.get().size;
2454
2455 let scrolling_area = self.scrolling_area_query(None).to_f32();
2474 let x = xfinite.clamp(0.0, 0.0f32.max(scrolling_area.width() - viewport.width));
2475 let y = yfinite.clamp(0.0, 0.0f32.max(scrolling_area.height() - viewport.height));
2476
2477 let scroll_offset = self.scroll_offset();
2480 if x == scroll_offset.x && y == scroll_offset.y {
2481 return;
2482 }
2483
2484 self.perform_a_scroll(x, y, self.pipeline_id().root_scroll_id(), behavior, None);
2489 }
2490
2491 pub(crate) fn perform_a_scroll(
2493 &self,
2494 x: f32,
2495 y: f32,
2496 scroll_id: ExternalScrollId,
2497 _behavior: ScrollBehavior,
2498 element: Option<&Element>,
2499 ) {
2500 let (reflow_phases_run, _) =
2504 self.reflow(ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)));
2505 if reflow_phases_run.needs_frame() {
2506 self.paint_api()
2507 .generate_frame(vec![self.webview_id().into()]);
2508 }
2509
2510 if reflow_phases_run.contains(ReflowPhasesRun::UpdatedScrollNodeOffset) {
2515 match element {
2516 Some(el) => self.Document().handle_element_scroll_event(el),
2517 None => self.Document().handle_viewport_scroll_event(),
2518 };
2519 }
2520 }
2521
2522 pub(crate) fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
2523 self.viewport_details.get().hidpi_scale_factor
2524 }
2525
2526 fn client_window(&self) -> DeviceIndependentIntRect {
2527 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel!");
2528
2529 self.send_to_embedder(EmbedderMsg::GetWindowRect(self.webview_id(), sender));
2530
2531 receiver.recv().unwrap_or_default()
2532 }
2533
2534 pub(crate) fn advance_animation_clock(&self, delta_ms: i32) {
2537 self.Document()
2538 .advance_animation_timeline_for_testing(delta_ms as f64 / 1000.);
2539 ScriptThread::handle_tick_all_animations_for_testing(self.pipeline_id());
2540 }
2541
2542 pub(crate) fn reflow(&self, reflow_goal: ReflowGoal) -> (ReflowPhasesRun, ReflowStatistics) {
2550 let document = self.Document();
2551
2552 if !document.is_fully_active() {
2554 return Default::default();
2555 }
2556
2557 self.Document().ensure_safe_to_run_script_or_layout();
2558
2559 let pipeline_id = self.pipeline_id();
2563 if reflow_goal == ReflowGoal::UpdateTheRendering &&
2564 self.layout_blocker.get().layout_blocked()
2565 {
2566 debug!("Suppressing pre-load-event reflow pipeline {pipeline_id}");
2567 return Default::default();
2568 }
2569
2570 debug!("script: performing reflow for goal {reflow_goal:?}");
2571 let marker = if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) {
2572 Some(TimelineMarker::start("Reflow".to_owned()))
2573 } else {
2574 None
2575 };
2576
2577 let restyle_reason = document.restyle_reason();
2578 document.clear_restyle_reasons();
2579 let restyle = if restyle_reason.needs_restyle() {
2580 debug!("Invalidating layout cache due to reflow condition {restyle_reason:?}",);
2581 self.layout_marker.borrow().set(false);
2583 *self.layout_marker.borrow_mut() = Rc::new(Cell::new(true));
2585
2586 let stylesheets_changed = document.flush_stylesheets_for_reflow();
2587 let pending_restyles = document.drain_pending_restyles();
2588 let dirty_root = document
2589 .take_dirty_root()
2590 .filter(|_| !stylesheets_changed)
2591 .or_else(|| document.GetDocumentElement())
2592 .map(|root| root.upcast::<Node>().to_trusted_node_address());
2593
2594 Some(ReflowRequestRestyle {
2595 reason: restyle_reason,
2596 dirty_root,
2597 stylesheets_changed,
2598 pending_restyles,
2599 })
2600 } else {
2601 None
2602 };
2603
2604 let document_context = self.web_font_context();
2605
2606 let reflow = ReflowRequest {
2608 document: document.upcast::<Node>().to_trusted_node_address(),
2609 epoch: document.current_rendering_epoch(),
2610 restyle,
2611 viewport_details: self.viewport_details.get(),
2612 origin: self.origin().immutable().clone(),
2613 reflow_goal,
2614 dom_count: document.dom_count(),
2615 animation_timeline_value: document.current_animation_timeline_value(),
2616 animations: document.animations().sets.clone(),
2617 animating_images: document.image_animation_manager().animating_images(),
2618 highlighted_dom_node: document.highlighted_dom_node().map(|node| node.to_opaque()),
2619 document_context,
2620 };
2621
2622 let Some(reflow_result) = self.layout.borrow_mut().reflow(reflow) else {
2623 return Default::default();
2624 };
2625
2626 debug!("script: layout complete");
2627 if let Some(marker) = marker {
2628 self.emit_timeline_marker(marker.end());
2629 }
2630
2631 self.handle_pending_images_post_reflow(
2632 reflow_result.pending_images,
2633 reflow_result.pending_rasterization_images,
2634 reflow_result.pending_svg_elements_for_serialization,
2635 );
2636
2637 if let Some(iframe_sizes) = reflow_result.iframe_sizes {
2638 document
2639 .iframes_mut()
2640 .handle_new_iframe_sizes_after_layout(self, iframe_sizes);
2641 }
2642
2643 document.update_animations_post_reflow();
2644
2645 (
2646 reflow_result.reflow_phases_run,
2647 reflow_result.reflow_statistics,
2648 )
2649 }
2650
2651 pub(crate) fn request_screenshot_readiness(&self, can_gc: CanGc) {
2652 self.has_pending_screenshot_readiness_request.set(true);
2653 self.maybe_resolve_pending_screenshot_readiness_requests(can_gc);
2654 }
2655
2656 pub(crate) fn maybe_resolve_pending_screenshot_readiness_requests(&self, can_gc: CanGc) {
2657 let pending_request = self.has_pending_screenshot_readiness_request.get();
2658 if !pending_request {
2659 return;
2660 }
2661
2662 let document = self.Document();
2663 if document.ReadyState() != DocumentReadyState::Complete {
2664 return;
2665 }
2666
2667 if document.render_blocking_element_count() > 0 {
2668 return;
2669 }
2670
2671 if document.GetDocumentElement().is_some_and(|elem| {
2675 elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
2676 elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
2677 }) {
2678 return;
2679 }
2680
2681 if self.font_context().web_fonts_still_loading() != 0 {
2682 return;
2683 }
2684
2685 if self.Document().Fonts(can_gc).waiting_to_fullfill_promise() {
2686 return;
2687 }
2688
2689 if !self.pending_layout_images.borrow().is_empty() ||
2690 !self.pending_images_for_rasterization.borrow().is_empty()
2691 {
2692 return;
2693 }
2694
2695 let document = self.Document();
2696 if document.needs_rendering_update() {
2697 return;
2698 }
2699
2700 let epoch = document.current_rendering_epoch();
2703 let pipeline_id = self.pipeline_id();
2704 debug!("Ready to take screenshot of {pipeline_id:?} at epoch={epoch:?}");
2705
2706 self.send_to_constellation(
2707 ScriptToConstellationMessage::RespondToScreenshotReadinessRequest(
2708 ScreenshotReadinessResponse::Ready(epoch),
2709 ),
2710 );
2711 self.has_pending_screenshot_readiness_request.set(false);
2712 }
2713
2714 pub(crate) fn reflow_if_reflow_timer_expired(&self) {
2717 if !matches!(
2720 self.layout_blocker.get(),
2721 LayoutBlocker::Parsing(instant) if instant + INITIAL_REFLOW_DELAY < Instant::now()
2722 ) {
2723 return;
2724 }
2725 self.allow_layout_if_necessary();
2726 }
2727
2728 pub(crate) fn prevent_layout_until_load_event(&self) {
2732 if !matches!(self.layout_blocker.get(), LayoutBlocker::WaitingForParse) {
2735 return;
2736 }
2737
2738 self.layout_blocker
2739 .set(LayoutBlocker::Parsing(Instant::now()));
2740 }
2741
2742 pub(crate) fn allow_layout_if_necessary(&self) {
2745 if matches!(
2746 self.layout_blocker.get(),
2747 LayoutBlocker::FiredLoadEventOrParsingTimerExpired
2748 ) {
2749 return;
2750 }
2751
2752 self.layout_blocker
2753 .set(LayoutBlocker::FiredLoadEventOrParsingTimerExpired);
2754
2755 if self.Document().update_the_rendering().0.needs_frame() {
2767 self.paint_api()
2768 .generate_frame(vec![self.webview_id().into()]);
2769 }
2770 }
2771
2772 pub(crate) fn layout_blocked(&self) -> bool {
2773 self.layout_blocker.get().layout_blocked()
2774 }
2775
2776 pub(crate) fn layout_reflow(&self, query_msg: QueryMsg) {
2778 self.reflow(ReflowGoal::LayoutQuery(query_msg));
2779 }
2780
2781 pub(crate) fn resolved_font_style_query(
2782 &self,
2783 node: &Node,
2784 value: String,
2785 ) -> Option<ServoArc<Font>> {
2786 self.layout_reflow(QueryMsg::ResolvedFontStyleQuery);
2787
2788 let document = self.Document();
2789 let animations = document.animations().sets.clone();
2790 self.layout.borrow().query_resolved_font_style(
2791 node.to_trusted_node_address(),
2792 &value,
2793 animations,
2794 document.current_animation_timeline_value(),
2795 )
2796 }
2797
2798 pub(crate) fn padding_query_without_reflow(&self, node: &Node) -> Option<PhysicalSides> {
2803 let layout = self.layout.borrow();
2804 layout.query_padding(node.to_trusted_node_address())
2805 }
2806
2807 pub(crate) fn box_area_query_without_reflow(
2812 &self,
2813 node: &Node,
2814 area: BoxAreaType,
2815 exclude_transform_and_inline: bool,
2816 ) -> Option<Rect<Au, CSSPixel>> {
2817 let layout = self.layout.borrow();
2818 layout.ensure_stacking_context_tree(self.viewport_details.get());
2819 layout.query_box_area(
2820 node.to_trusted_node_address(),
2821 area,
2822 exclude_transform_and_inline,
2823 )
2824 }
2825
2826 pub(crate) fn box_area_query(
2827 &self,
2828 node: &Node,
2829 area: BoxAreaType,
2830 exclude_transform_and_inline: bool,
2831 ) -> Option<Rect<Au, CSSPixel>> {
2832 self.layout_reflow(QueryMsg::BoxArea);
2833 self.box_area_query_without_reflow(node, area, exclude_transform_and_inline)
2834 }
2835
2836 pub(crate) fn box_areas_query(&self, node: &Node, area: BoxAreaType) -> CSSPixelRectIterator {
2837 self.layout_reflow(QueryMsg::BoxAreas);
2838 self.layout
2839 .borrow()
2840 .query_box_areas(node.to_trusted_node_address(), area)
2841 }
2842
2843 pub(crate) fn client_rect_query(&self, node: &Node) -> Rect<i32, CSSPixel> {
2844 self.layout_reflow(QueryMsg::ClientRectQuery);
2845 self.layout
2846 .borrow()
2847 .query_client_rect(node.to_trusted_node_address())
2848 }
2849
2850 pub(crate) fn current_css_zoom_query(&self, node: &Node) -> f32 {
2851 self.layout_reflow(QueryMsg::CurrentCSSZoomQuery);
2852 self.layout
2853 .borrow()
2854 .query_current_css_zoom(node.to_trusted_node_address())
2855 }
2856
2857 pub(crate) fn scrolling_area_query(&self, node: Option<&Node>) -> Rect<i32, CSSPixel> {
2860 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2861 self.layout
2862 .borrow()
2863 .query_scrolling_area(node.map(Node::to_trusted_node_address))
2864 }
2865
2866 pub(crate) fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32, LayoutPixel> {
2867 let external_scroll_id = ExternalScrollId(
2868 combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody),
2869 self.pipeline_id().into(),
2870 );
2871 self.scroll_offset_query_with_external_scroll_id(external_scroll_id)
2872 }
2873
2874 fn scroll_offset_query_with_external_scroll_id(
2875 &self,
2876 external_scroll_id: ExternalScrollId,
2877 ) -> Vector2D<f32, LayoutPixel> {
2878 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2879 self.scroll_offset_query_with_external_scroll_id_no_reflow(external_scroll_id)
2880 }
2881
2882 fn scroll_offset_query_with_external_scroll_id_no_reflow(
2883 &self,
2884 external_scroll_id: ExternalScrollId,
2885 ) -> Vector2D<f32, LayoutPixel> {
2886 self.layout
2887 .borrow()
2888 .scroll_offset(external_scroll_id)
2889 .unwrap_or_default()
2890 }
2891
2892 pub(crate) fn scroll_an_element(
2895 &self,
2896 element: &Element,
2897 x: f32,
2898 y: f32,
2899 behavior: ScrollBehavior,
2900 ) {
2901 let scroll_id = ExternalScrollId(
2902 combine_id_with_fragment_type(
2903 element.upcast::<Node>().to_opaque().id(),
2904 FragmentType::FragmentBody,
2905 ),
2906 self.pipeline_id().into(),
2907 );
2908
2909 self.perform_a_scroll(x, y, scroll_id, behavior, Some(element));
2913 }
2914
2915 pub(crate) fn resolved_style_query(
2916 &self,
2917 element: TrustedNodeAddress,
2918 pseudo: Option<PseudoElement>,
2919 property: PropertyId,
2920 ) -> DOMString {
2921 self.layout_reflow(QueryMsg::ResolvedStyleQuery);
2922
2923 let document = self.Document();
2924 let animations = document.animations().sets.clone();
2925 DOMString::from(self.layout.borrow().query_resolved_style(
2926 element,
2927 pseudo,
2928 property,
2929 animations,
2930 document.current_animation_timeline_value(),
2931 ))
2932 }
2933
2934 pub(crate) fn get_iframe_viewport_details_if_known(
2938 &self,
2939 browsing_context_id: BrowsingContextId,
2940 ) -> Option<ViewportDetails> {
2941 self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery);
2943 self.Document()
2944 .iframes()
2945 .get(browsing_context_id)
2946 .and_then(|iframe| iframe.size)
2947 }
2948
2949 #[expect(unsafe_code)]
2950 pub(crate) fn offset_parent_query(
2951 &self,
2952 node: &Node,
2953 ) -> (Option<DomRoot<Element>>, Rect<Au, CSSPixel>) {
2954 self.layout_reflow(QueryMsg::OffsetParentQuery);
2955 let response = self
2956 .layout
2957 .borrow()
2958 .query_offset_parent(node.to_trusted_node_address());
2959 let element = response.node_address.and_then(|parent_node_address| {
2960 let node = unsafe { from_untrusted_node_address(parent_node_address) };
2961 DomRoot::downcast(node)
2962 });
2963 (element, response.rect)
2964 }
2965
2966 pub(crate) fn scroll_container_query(
2967 &self,
2968 node: Option<&Node>,
2969 flags: ScrollContainerQueryFlags,
2970 ) -> Option<ScrollContainerResponse> {
2971 self.layout_reflow(QueryMsg::ScrollParentQuery);
2972 self.layout
2973 .borrow()
2974 .query_scroll_container(node.map(Node::to_trusted_node_address), flags)
2975 }
2976
2977 #[expect(unsafe_code)]
2978 pub(crate) fn scrolling_box_query(
2979 &self,
2980 node: Option<&Node>,
2981 flags: ScrollContainerQueryFlags,
2982 ) -> Option<ScrollingBox> {
2983 self.scroll_container_query(node, flags)
2984 .and_then(|response| {
2985 Some(match response {
2986 ScrollContainerResponse::Viewport(overflow) => {
2987 (ScrollingBoxSource::Viewport(self.Document()), overflow)
2988 },
2989 ScrollContainerResponse::Element(parent_node_address, overflow) => {
2990 let node = unsafe { from_untrusted_node_address(parent_node_address) };
2991 (
2992 ScrollingBoxSource::Element(DomRoot::downcast(node)?),
2993 overflow,
2994 )
2995 },
2996 })
2997 })
2998 .map(|(source, overflow)| ScrollingBox::new(source, overflow))
2999 }
3000
3001 pub(crate) fn text_index_query_on_node_for_event(
3002 &self,
3003 node: &Node,
3004 mouse_event: &MouseEvent,
3005 ) -> Option<usize> {
3006 let point_in_viewport = mouse_event.point_in_viewport()?.map(Au::from_f32_px);
3010
3011 self.layout_reflow(QueryMsg::TextIndexQuery);
3012 self.layout
3013 .borrow()
3014 .query_text_index(node.to_trusted_node_address(), point_in_viewport)
3015 }
3016
3017 pub(crate) fn elements_from_point_query(
3018 &self,
3019 point: LayoutPoint,
3020 flags: ElementsFromPointFlags,
3021 ) -> Vec<ElementsFromPointResult> {
3022 self.layout_reflow(QueryMsg::ElementsFromPoint);
3023 self.layout().query_elements_from_point(point, flags)
3024 }
3025
3026 pub(crate) fn hit_test_from_input_event(
3027 &self,
3028 input_event: &ConstellationInputEvent,
3029 ) -> Option<HitTestResult> {
3030 self.hit_test_from_point_in_viewport(
3031 input_event.hit_test_result.as_ref()?.point_in_viewport,
3032 )
3033 }
3034
3035 #[expect(unsafe_code)]
3036 pub(crate) fn hit_test_from_point_in_viewport(
3037 &self,
3038 point_in_frame: Point2D<f32, CSSPixel>,
3039 ) -> Option<HitTestResult> {
3040 let result = self
3041 .elements_from_point_query(point_in_frame.cast_unit(), ElementsFromPointFlags::empty())
3042 .into_iter()
3043 .nth(0)?;
3044
3045 let point_relative_to_initial_containing_block =
3046 point_in_frame + self.scroll_offset().cast_unit();
3047
3048 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
3051 Some(HitTestResult {
3052 node: unsafe { from_untrusted_node_address(address) },
3053 cursor: result.cursor,
3054 point_in_node: result.point_in_target,
3055 point_in_frame,
3056 point_relative_to_initial_containing_block,
3057 })
3058 }
3059
3060 pub(crate) fn init_window_proxy(&self, window_proxy: &WindowProxy) {
3061 assert!(self.window_proxy.get().is_none());
3062 self.window_proxy.set(Some(window_proxy));
3063 }
3064
3065 pub(crate) fn init_document(&self, document: &Document) {
3066 assert!(self.document.get().is_none());
3067 assert!(document.window() == self);
3068 self.document.set(Some(document));
3069
3070 if self.unminify_css {
3071 *self.unminified_css_dir.borrow_mut() = Some(unminified_path("unminified-css"));
3072 }
3073 }
3074
3075 fn navigate_to_fragment(&self, url: &ServoUrl, history_handling: NavigationHistoryBehavior) {
3077 let doc = self.Document();
3078 self.send_to_constellation(ScriptToConstellationMessage::NavigatedToFragment(
3101 url.clone(),
3102 history_handling,
3103 ));
3104 let old_url = doc.url();
3106 doc.set_url(url.clone());
3107 doc.update_document_for_history_step_application(&old_url, url);
3110 let Some(fragment) = url.fragment() else {
3112 unreachable!("Must always have a fragment");
3113 };
3114 doc.scroll_to_the_fragment(fragment);
3115 }
3120
3121 pub(crate) fn load_data_for_document(
3122 &self,
3123 url: ServoUrl,
3124 pipeline_id: PipelineId,
3125 ) -> LoadData {
3126 let source_document = self.Document();
3127 let secure_context = if self.is_top_level() {
3128 None
3129 } else {
3130 Some(self.IsSecureContext())
3131 };
3132 LoadData::new(
3133 LoadOrigin::Script(self.origin().snapshot()),
3134 url,
3135 source_document.about_base_url(),
3136 Some(pipeline_id),
3137 Referrer::ReferrerUrl(source_document.url()),
3138 source_document.get_referrer_policy(),
3139 secure_context,
3140 Some(source_document.insecure_requests_policy()),
3141 source_document.has_trustworthy_ancestor_origin(),
3142 source_document.creation_sandboxing_flag_set_considering_parent_iframe(),
3143 )
3144 }
3145
3146 pub(crate) fn load_url(
3148 &self,
3149 history_handling: NavigationHistoryBehavior,
3150 force_reload: bool,
3151 load_data: LoadData,
3152 can_gc: CanGc,
3153 ) {
3154 let doc = self.Document();
3155
3156 let initiator_origin_snapshot = &load_data.load_origin;
3158
3159 let pipeline_id = self.pipeline_id();
3164 let window_proxy = self.window_proxy();
3165 if let Some(active) = window_proxy.currently_active() {
3166 if pipeline_id == active && doc.is_prompting_or_unloading() {
3167 return;
3168 }
3169 }
3170
3171 if doc.check_if_unloading_is_cancelled(false, can_gc) {
3174 let history_handling = if history_handling == NavigationHistoryBehavior::Auto {
3176 if let LoadOrigin::Script(initiator_origin) = initiator_origin_snapshot {
3183 if load_data.url == doc.url() && initiator_origin.same_origin(doc.origin()) {
3184 NavigationHistoryBehavior::Replace
3185 } else {
3186 NavigationHistoryBehavior::Push
3188 }
3189 } else {
3190 NavigationHistoryBehavior::Push
3192 }
3193 } else {
3194 history_handling
3195 };
3196 let history_handling =
3201 if load_data.url.scheme() == "javascript" || doc.is_initial_about_blank() {
3202 NavigationHistoryBehavior::Replace
3203 } else {
3204 history_handling
3205 };
3206
3207 if !force_reload
3211 && load_data.url.as_url()[..Position::AfterQuery] ==
3213 doc.url().as_url()[..Position::AfterQuery]
3214 && load_data.url.fragment().is_some()
3216 {
3217 let webdriver_sender = self.webdriver_load_status_sender.borrow().clone();
3220 if let Some(ref sender) = webdriver_sender {
3221 let _ = sender.send(WebDriverLoadStatus::NavigationStart);
3222 }
3223 self.navigate_to_fragment(&load_data.url, history_handling);
3224 if let Some(sender) = webdriver_sender {
3226 let _ = sender.send(WebDriverLoadStatus::NavigationStop);
3227 }
3228 return;
3229 }
3230
3231 let window_proxy = self.window_proxy();
3233 if window_proxy.parent().is_some() {
3234 window_proxy.start_delaying_load_events_mode();
3235 }
3236
3237 if let Some(sender) = self.webdriver_load_status_sender.borrow().as_ref() {
3238 let _ = sender.send(WebDriverLoadStatus::NavigationStart);
3239 }
3240
3241 ScriptThread::navigate(self.webview_id, pipeline_id, load_data, history_handling);
3243 };
3244 }
3245
3246 pub(crate) fn set_viewport_details(&self, viewport_details: ViewportDetails) {
3249 self.viewport_details.set(viewport_details);
3250 if !self.layout_mut().set_viewport_details(viewport_details) {
3251 return;
3252 }
3253 self.Document()
3254 .add_restyle_reason(RestyleReason::ViewportChanged);
3255 }
3256
3257 pub(crate) fn viewport_details(&self) -> ViewportDetails {
3258 self.viewport_details.get()
3259 }
3260
3261 pub(crate) fn get_or_init_visual_viewport(&self, can_gc: CanGc) -> DomRoot<VisualViewport> {
3262 self.visual_viewport.or_init(|| {
3263 VisualViewport::new_from_layout_viewport(self, self.viewport_details().size, can_gc)
3264 })
3265 }
3266
3267 pub(crate) fn maybe_update_visual_viewport(
3269 &self,
3270 pinch_zoom_infos: PinchZoomInfos,
3271 can_gc: CanGc,
3272 ) {
3273 if pinch_zoom_infos.rect == Rect::from_size(self.viewport_details().size) &&
3276 self.visual_viewport.get().is_none()
3277 {
3278 return;
3279 }
3280
3281 let visual_viewport = self.get_or_init_visual_viewport(can_gc);
3282 let changes = visual_viewport.update_from_pinch_zoom_infos(pinch_zoom_infos);
3283
3284 if changes.intersects(VisualViewportChanges::DimensionChanged) {
3285 self.has_changed_visual_viewport_dimension.set(true);
3286 }
3287 }
3289
3290 pub(crate) fn theme(&self) -> Theme {
3292 self.theme.get()
3293 }
3294
3295 pub(crate) fn set_theme(&self, new_theme: Theme) {
3297 self.theme.set(new_theme);
3298 if !self.layout_mut().set_theme(new_theme) {
3299 return;
3300 }
3301 self.Document()
3302 .add_restyle_reason(RestyleReason::ThemeChanged);
3303 }
3304
3305 pub(crate) fn get_url(&self) -> ServoUrl {
3306 self.Document().url()
3307 }
3308
3309 pub(crate) fn windowproxy_handler(&self) -> &'static WindowProxyHandler {
3310 self.dom_static.windowproxy_handler
3311 }
3312
3313 pub(crate) fn add_resize_event(&self, event: ViewportDetails, event_type: WindowSizeType) {
3314 *self.unhandled_resize_event.borrow_mut() = Some((event, event_type))
3317 }
3318
3319 pub(crate) fn take_unhandled_resize_event(&self) -> Option<(ViewportDetails, WindowSizeType)> {
3320 self.unhandled_resize_event.borrow_mut().take()
3321 }
3322
3323 pub(crate) fn has_unhandled_resize_event(&self) -> bool {
3325 self.unhandled_resize_event.borrow().is_some()
3326 }
3327
3328 pub(crate) fn suspend(&self, cx: &mut js::context::JSContext) {
3329 self.as_global_scope().suspend();
3331
3332 if self.window_proxy().currently_active() == Some(self.global().pipeline_id()) {
3334 self.window_proxy().unset_currently_active(cx);
3335 }
3336
3337 self.gc();
3342 }
3343
3344 pub(crate) fn resume(&self, can_gc: CanGc) {
3345 self.as_global_scope().resume();
3347
3348 self.window_proxy().set_currently_active(self, can_gc);
3350
3351 self.Document().title_changed();
3354 }
3355
3356 pub(crate) fn need_emit_timeline_marker(&self, timeline_type: TimelineMarkerType) -> bool {
3357 let markers = self.devtools_markers.borrow();
3358 markers.contains(&timeline_type)
3359 }
3360
3361 pub(crate) fn emit_timeline_marker(&self, marker: TimelineMarker) {
3362 let sender = self.devtools_marker_sender.borrow();
3363 let sender = sender.as_ref().expect("There is no marker sender");
3364 sender.send(Some(marker)).unwrap();
3365 }
3366
3367 pub(crate) fn set_devtools_timeline_markers(
3368 &self,
3369 markers: Vec<TimelineMarkerType>,
3370 reply: GenericSender<Option<TimelineMarker>>,
3371 ) {
3372 *self.devtools_marker_sender.borrow_mut() = Some(reply);
3373 self.devtools_markers.borrow_mut().extend(markers);
3374 }
3375
3376 pub(crate) fn drop_devtools_timeline_markers(&self, markers: Vec<TimelineMarkerType>) {
3377 let mut devtools_markers = self.devtools_markers.borrow_mut();
3378 for marker in markers {
3379 devtools_markers.remove(&marker);
3380 }
3381 if devtools_markers.is_empty() {
3382 *self.devtools_marker_sender.borrow_mut() = None;
3383 }
3384 }
3385
3386 pub(crate) fn set_webdriver_script_chan(&self, chan: Option<GenericSender<WebDriverJSResult>>) {
3387 *self.webdriver_script_chan.borrow_mut() = chan;
3388 }
3389
3390 pub(crate) fn set_webdriver_load_status_sender(
3391 &self,
3392 sender: Option<GenericSender<WebDriverLoadStatus>>,
3393 ) {
3394 *self.webdriver_load_status_sender.borrow_mut() = sender;
3395 }
3396
3397 pub(crate) fn is_alive(&self) -> bool {
3398 self.current_state.get() == WindowState::Alive
3399 }
3400
3401 pub(crate) fn is_top_level(&self) -> bool {
3403 self.parent_info.is_none()
3404 }
3405
3406 fn run_resize_steps_for_layout_viewport(&self, can_gc: CanGc) -> bool {
3411 let Some((new_size, size_type)) = self.take_unhandled_resize_event() else {
3412 return false;
3413 };
3414
3415 if self.viewport_details() == new_size {
3416 return false;
3417 }
3418
3419 let _realm = enter_realm(self);
3420 debug!(
3421 "Resizing Window for pipeline {:?} from {:?} to {new_size:?}",
3422 self.pipeline_id(),
3423 self.viewport_details(),
3424 );
3425 self.set_viewport_details(new_size);
3426
3427 self.Document()
3431 .add_restyle_reason(RestyleReason::ViewportChanged);
3432
3433 if self.layout().device().used_viewport_units() {
3436 self.Document().dirty_all_nodes();
3437 }
3438
3439 if size_type == WindowSizeType::Resize {
3441 let uievent = UIEvent::new(
3442 self,
3443 DOMString::from("resize"),
3444 EventBubbles::DoesNotBubble,
3445 EventCancelable::NotCancelable,
3446 Some(self),
3447 0i32,
3448 0u32,
3449 can_gc,
3450 );
3451 uievent.upcast::<Event>().fire(self.upcast(), can_gc);
3452 }
3453
3454 true
3455 }
3456
3457 pub(crate) fn run_the_resize_steps(&self, can_gc: CanGc) -> bool {
3462 let layout_viewport_resized = self.run_resize_steps_for_layout_viewport(can_gc);
3463
3464 if self.has_changed_visual_viewport_dimension.get() {
3465 let visual_viewport = self.get_or_init_visual_viewport(can_gc);
3466
3467 let uievent = UIEvent::new(
3468 self,
3469 DOMString::from("resize"),
3470 EventBubbles::DoesNotBubble,
3471 EventCancelable::NotCancelable,
3472 Some(self),
3473 0i32,
3474 0u32,
3475 can_gc,
3476 );
3477 uievent
3478 .upcast::<Event>()
3479 .fire(visual_viewport.upcast(), can_gc);
3480
3481 self.has_changed_visual_viewport_dimension.set(false);
3482 }
3483
3484 layout_viewport_resized
3485 }
3486
3487 pub(crate) fn evaluate_media_queries_and_report_changes(&self, can_gc: CanGc) {
3490 let _realm = enter_realm(self);
3491
3492 rooted_vec!(let mut mql_list);
3493 self.media_query_lists.for_each(|mql| {
3494 if let MediaQueryListMatchState::Changed = mql.evaluate_changes() {
3495 mql_list.push(Dom::from_ref(&*mql));
3497 }
3498 });
3499 for mql in mql_list.iter() {
3501 let event = MediaQueryListEvent::new(
3502 &mql.global(),
3503 atom!("change"),
3504 false,
3505 false,
3506 mql.Media(),
3507 mql.Matches(),
3508 can_gc,
3509 );
3510 event
3511 .upcast::<Event>()
3512 .fire(mql.upcast::<EventTarget>(), can_gc);
3513 }
3514 }
3515
3516 pub(crate) fn set_throttled(&self, throttled: bool) {
3518 self.throttled.set(throttled);
3519 if throttled {
3520 self.as_global_scope().slow_down_timers();
3521 } else {
3522 self.as_global_scope().speed_up_timers();
3523 }
3524 }
3525
3526 pub(crate) fn throttled(&self) -> bool {
3527 self.throttled.get()
3528 }
3529
3530 pub(crate) fn unminified_css_dir(&self) -> Option<String> {
3531 self.unminified_css_dir.borrow().clone()
3532 }
3533
3534 pub(crate) fn local_script_source(&self) -> &Option<String> {
3535 &self.local_script_source
3536 }
3537
3538 pub(crate) fn set_navigation_start(&self) {
3539 self.navigation_start.set(CrossProcessInstant::now());
3540 }
3541
3542 pub(crate) fn set_last_activation_timestamp(&self, time: UserActivationTimestamp) {
3543 self.last_activation_timestamp.set(time);
3544 }
3545
3546 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
3547 self.as_global_scope()
3548 .script_to_embedder_chan()
3549 .send(msg)
3550 .unwrap();
3551 }
3552
3553 pub(crate) fn send_to_constellation(&self, msg: ScriptToConstellationMessage) {
3554 self.as_global_scope()
3555 .script_to_constellation_chan()
3556 .send(msg)
3557 .unwrap();
3558 }
3559
3560 #[cfg(feature = "webxr")]
3561 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3562 self.navigator
3563 .get()
3564 .as_ref()
3565 .and_then(|nav| nav.xr())
3566 .is_some_and(|xr| xr.pending_or_active_session())
3567 }
3568
3569 #[cfg(not(feature = "webxr"))]
3570 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3571 false
3572 }
3573
3574 #[expect(unsafe_code)]
3575 fn handle_pending_images_post_reflow(
3576 &self,
3577 pending_images: Vec<PendingImage>,
3578 pending_rasterization_images: Vec<PendingRasterizationImage>,
3579 pending_svg_element_for_serialization: Vec<UntrustedNodeAddress>,
3580 ) {
3581 let pipeline_id = self.pipeline_id();
3582 for image in pending_images {
3583 let id = image.id;
3584 let node = unsafe { from_untrusted_node_address(image.node) };
3585
3586 if let PendingImageState::Unrequested(ref url) = image.state {
3587 fetch_image_for_layout(url.clone(), &node, id, self.image_cache.clone());
3588 }
3589
3590 let mut images = self.pending_layout_images.borrow_mut();
3591 if !images.contains_key(&id) {
3592 let trusted_node = Trusted::new(&*node);
3593 let sender = self.register_image_cache_listener(id, move |response, _| {
3594 trusted_node
3595 .root()
3596 .owner_window()
3597 .pending_layout_image_notification(response);
3598 });
3599
3600 self.image_cache
3601 .add_listener(ImageLoadListener::new(sender, pipeline_id, id));
3602 }
3603
3604 let nodes = images.entry(id).or_default();
3605 if !nodes.iter().any(|n| std::ptr::eq(&*(n.node), &*node)) {
3606 nodes.push(PendingLayoutImageAncillaryData {
3607 node: Dom::from_ref(&*node),
3608 destination: image.destination,
3609 });
3610 }
3611 }
3612
3613 for image in pending_rasterization_images {
3614 let node = unsafe { from_untrusted_node_address(image.node) };
3615
3616 let mut images = self.pending_images_for_rasterization.borrow_mut();
3617 if !images.contains_key(&(image.id, image.size)) {
3618 let image_cache_sender = self.image_cache_sender.clone();
3619 self.image_cache.add_rasterization_complete_listener(
3620 pipeline_id,
3621 image.id,
3622 image.size,
3623 Box::new(move |response| {
3624 let _ = image_cache_sender.send(response);
3625 }),
3626 );
3627 }
3628
3629 let nodes = images.entry((image.id, image.size)).or_default();
3630 if !nodes.iter().any(|n| std::ptr::eq(&**n, &*node)) {
3631 nodes.push(Dom::from_ref(&*node));
3632 }
3633 }
3634
3635 for node in pending_svg_element_for_serialization.into_iter() {
3636 let node = unsafe { from_untrusted_node_address(node) };
3637 let svg = node.downcast::<SVGSVGElement>().unwrap();
3638 svg.serialize_and_cache_subtree();
3639 node.dirty(NodeDamage::Other);
3640 }
3641 }
3642
3643 pub(crate) fn has_sticky_activation(&self) -> bool {
3645 UserActivationTimestamp::TimeStamp(CrossProcessInstant::now()) >=
3647 self.last_activation_timestamp.get()
3648 }
3649
3650 pub(crate) fn has_transient_activation(&self) -> bool {
3652 let current_time = CrossProcessInstant::now();
3655 UserActivationTimestamp::TimeStamp(current_time) >= self.last_activation_timestamp.get() &&
3656 UserActivationTimestamp::TimeStamp(current_time) <
3657 self.last_activation_timestamp.get() +
3658 pref!(dom_transient_activation_duration_ms)
3659 }
3660
3661 pub(crate) fn consume_last_activation_timestamp(&self) {
3662 if self.last_activation_timestamp.get() != UserActivationTimestamp::PositiveInfinity {
3663 self.set_last_activation_timestamp(UserActivationTimestamp::NegativeInfinity);
3664 }
3665 }
3666
3667 pub(crate) fn consume_user_activation(&self) {
3669 if self.undiscarded_window_proxy().is_none() {
3672 return;
3673 }
3674
3675 let Some(top_level_document) = self.top_level_document_if_local() else {
3679 return;
3680 };
3681
3682 top_level_document
3690 .window()
3691 .consume_last_activation_timestamp();
3692 for document in SameOriginDescendantNavigablesIterator::new(top_level_document) {
3693 document.window().consume_last_activation_timestamp();
3694 }
3695 }
3696
3697 #[allow(clippy::too_many_arguments)]
3698 pub(crate) fn new(
3699 cx: &mut js::context::JSContext,
3700 webview_id: WebViewId,
3701 runtime: Rc<Runtime>,
3702 script_chan: Sender<MainThreadScriptMsg>,
3703 layout: Box<dyn Layout>,
3704 font_context: Arc<FontContext>,
3705 image_cache_sender: Sender<ImageCacheResponseMessage>,
3706 image_cache: Arc<dyn ImageCache>,
3707 resource_threads: ResourceThreads,
3708 storage_threads: StorageThreads,
3709 #[cfg(feature = "bluetooth")] bluetooth_thread: GenericSender<BluetoothRequest>,
3710 mem_profiler_chan: MemProfilerChan,
3711 time_profiler_chan: TimeProfilerChan,
3712 devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
3713 constellation_chan: ScriptToConstellationChan,
3714 embedder_chan: ScriptToEmbedderChan,
3715 control_chan: GenericSender<ScriptThreadMessage>,
3716 pipeline_id: PipelineId,
3717 parent_info: Option<PipelineId>,
3718 viewport_details: ViewportDetails,
3719 origin: MutableOrigin,
3720 creation_url: ServoUrl,
3721 top_level_creation_url: ServoUrl,
3722 navigation_start: CrossProcessInstant,
3723 webgl_chan: Option<WebGLChan>,
3724 #[cfg(feature = "webxr")] webxr_registry: Option<webxr_api::Registry>,
3725 paint_api: CrossProcessPaintApi,
3726 unminify_js: bool,
3727 unminify_css: bool,
3728 local_script_source: Option<String>,
3729 user_scripts: Rc<Vec<UserScript>>,
3730 player_context: WindowGLContext,
3731 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
3732 inherited_secure_context: Option<bool>,
3733 theme: Theme,
3734 weak_script_thread: Weak<ScriptThread>,
3735 ) -> DomRoot<Self> {
3736 let error_reporter = CSSErrorReporter {
3737 pipelineid: pipeline_id,
3738 script_chan: control_chan,
3739 };
3740
3741 let win = Box::new(Self {
3742 webview_id,
3743 globalscope: GlobalScope::new_inherited(
3744 pipeline_id,
3745 devtools_chan,
3746 mem_profiler_chan,
3747 time_profiler_chan,
3748 constellation_chan,
3749 embedder_chan,
3750 resource_threads,
3751 storage_threads,
3752 origin,
3753 creation_url,
3754 Some(top_level_creation_url),
3755 #[cfg(feature = "webgpu")]
3756 gpu_id_hub,
3757 inherited_secure_context,
3758 unminify_js,
3759 Some(font_context.clone()),
3760 ),
3761 ongoing_navigation: Default::default(),
3762 script_chan,
3763 layout: RefCell::new(layout),
3764 image_cache_sender,
3765 image_cache,
3766 navigator: Default::default(),
3767 location: Default::default(),
3768 history: Default::default(),
3769 custom_element_registry: Default::default(),
3770 window_proxy: Default::default(),
3771 document: Default::default(),
3772 performance: Default::default(),
3773 navigation_start: Cell::new(navigation_start),
3774 screen: Default::default(),
3775 session_storage: Default::default(),
3776 local_storage: Default::default(),
3777 status: DomRefCell::new(DOMString::new()),
3778 parent_info,
3779 dom_static: GlobalStaticData::new(),
3780 js_runtime: DomRefCell::new(Some(runtime.clone())),
3781 #[cfg(feature = "bluetooth")]
3782 bluetooth_thread,
3783 #[cfg(feature = "bluetooth")]
3784 bluetooth_extra_permission_data: BluetoothExtraPermissionData::new(),
3785 unhandled_resize_event: Default::default(),
3786 viewport_details: Cell::new(viewport_details),
3787 layout_blocker: Cell::new(LayoutBlocker::WaitingForParse),
3788 current_state: Cell::new(WindowState::Alive),
3789 devtools_marker_sender: Default::default(),
3790 devtools_markers: Default::default(),
3791 webdriver_script_chan: Default::default(),
3792 webdriver_load_status_sender: Default::default(),
3793 error_reporter,
3794 media_query_lists: DOMTracker::new(),
3795 #[cfg(feature = "bluetooth")]
3796 test_runner: Default::default(),
3797 webgl_chan,
3798 #[cfg(feature = "webxr")]
3799 webxr_registry,
3800 pending_image_callbacks: Default::default(),
3801 pending_layout_images: Default::default(),
3802 pending_images_for_rasterization: Default::default(),
3803 unminified_css_dir: Default::default(),
3804 local_script_source,
3805 test_worklet: Default::default(),
3806 paint_worklet: Default::default(),
3807 exists_mut_observer: Cell::new(false),
3808 paint_api,
3809 has_sent_idle_message: Cell::new(false),
3810 unminify_css,
3811 user_scripts,
3812 player_context,
3813 throttled: Cell::new(false),
3814 layout_marker: DomRefCell::new(Rc::new(Cell::new(true))),
3815 current_event: DomRefCell::new(None),
3816 theme: Cell::new(theme),
3817 trusted_types: Default::default(),
3818 reporting_observer_list: Default::default(),
3819 report_list: Default::default(),
3820 endpoints_list: Default::default(),
3821 script_window_proxies: ScriptThread::window_proxies(),
3822 has_pending_screenshot_readiness_request: Default::default(),
3823 visual_viewport: Default::default(),
3824 weak_script_thread,
3825 has_changed_visual_viewport_dimension: Default::default(),
3826 last_activation_timestamp: Cell::new(UserActivationTimestamp::PositiveInfinity),
3827 });
3828
3829 WindowBinding::Wrap::<crate::DomTypeHolder>(cx, win)
3830 }
3831
3832 pub(crate) fn pipeline_id(&self) -> PipelineId {
3833 self.as_global_scope().pipeline_id()
3834 }
3835
3836 pub(crate) fn cache_layout_value<T>(&self, value: T) -> LayoutValue<T>
3838 where
3839 T: Copy + MallocSizeOf,
3840 {
3841 LayoutValue::new(self.layout_marker.borrow().clone(), value)
3842 }
3843}
3844
3845#[derive(MallocSizeOf)]
3850pub(crate) struct LayoutValue<T: MallocSizeOf> {
3851 #[conditional_malloc_size_of]
3852 is_valid: Rc<Cell<bool>>,
3853 value: T,
3854}
3855
3856#[expect(unsafe_code)]
3857unsafe impl<T: JSTraceable + MallocSizeOf> JSTraceable for LayoutValue<T> {
3858 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
3859 unsafe { self.value.trace(trc) };
3860 }
3861}
3862
3863impl<T: Copy + MallocSizeOf> LayoutValue<T> {
3864 fn new(marker: Rc<Cell<bool>>, value: T) -> Self {
3865 LayoutValue {
3866 is_valid: marker,
3867 value,
3868 }
3869 }
3870
3871 pub(crate) fn get(&self) -> Result<T, ()> {
3873 if self.is_valid.get() {
3874 return Ok(self.value);
3875 }
3876 Err(())
3877 }
3878}
3879
3880fn should_move_clip_rect(clip_rect: UntypedRect<Au>, new_viewport: UntypedRect<f32>) -> bool {
3881 let clip_rect = UntypedRect::new(
3882 Point2D::new(
3883 clip_rect.origin.x.to_f32_px(),
3884 clip_rect.origin.y.to_f32_px(),
3885 ),
3886 Size2D::new(
3887 clip_rect.size.width.to_f32_px(),
3888 clip_rect.size.height.to_f32_px(),
3889 ),
3890 );
3891
3892 static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5;
3896 let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE;
3897
3898 (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width ||
3899 (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width ||
3900 (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height ||
3901 (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height
3902}
3903
3904impl Window {
3905 pub(crate) fn post_message(
3907 &self,
3908 target_origin: Option<ImmutableOrigin>,
3909 source_origin: ImmutableOrigin,
3910 source: &WindowProxy,
3911 data: StructuredSerializedData,
3912 ) {
3913 let this = Trusted::new(self);
3914 let source = Trusted::new(source);
3915 let task = task!(post_serialised_message: move || {
3916 let this = this.root();
3917 let source = source.root();
3918 let document = this.Document();
3919
3920 if let Some(ref target_origin) = target_origin {
3922 if !target_origin.same_origin(document.origin()) {
3923 return;
3924 }
3925 }
3926
3927 let cx = this.get_cx();
3929 let obj = this.reflector().get_jsobject();
3930 let _ac = JSAutoRealm::new(*cx, obj.get());
3931 rooted!(in(*cx) let mut message_clone = UndefinedValue());
3932 if let Ok(ports) = structuredclone::read(this.upcast(), data, message_clone.handle_mut(), CanGc::note()) {
3933 MessageEvent::dispatch_jsval(
3935 this.upcast(),
3936 this.upcast(),
3937 message_clone.handle(),
3938 Some(&source_origin.ascii_serialization()),
3939 Some(&*source),
3940 ports,
3941 CanGc::note()
3942 );
3943 } else {
3944 MessageEvent::dispatch_error(
3946 this.upcast(),
3947 this.upcast(),
3948 CanGc::note()
3949 );
3950 }
3951 });
3952 self.as_global_scope()
3954 .task_manager()
3955 .dom_manipulation_task_source()
3956 .queue(task);
3957 }
3958}
3959
3960#[derive(Clone, MallocSizeOf)]
3961pub(crate) struct CSSErrorReporter {
3962 pub(crate) pipelineid: PipelineId,
3963 pub(crate) script_chan: GenericSender<ScriptThreadMessage>,
3964}
3965unsafe_no_jsmanaged_fields!(CSSErrorReporter);
3966
3967impl ParseErrorReporter for CSSErrorReporter {
3968 fn report_error(
3969 &self,
3970 url: &UrlExtraData,
3971 location: SourceLocation,
3972 error: ContextualParseError,
3973 ) {
3974 if log_enabled!(log::Level::Info) {
3975 info!(
3976 "Url:\t{}\n{}:{} {}",
3977 url.0.as_str(),
3978 location.line,
3979 location.column,
3980 error
3981 )
3982 }
3983
3984 let _ = self.script_chan.send(ScriptThreadMessage::ReportCSSError(
3986 self.pipelineid,
3987 url.0.to_string(),
3988 location.line,
3989 location.column,
3990 error.to_string(),
3991 ));
3992 }
3993}
3994
3995fn is_named_element_with_name_attribute(elem: &Element) -> bool {
3996 let type_ = match elem.upcast::<Node>().type_id() {
3997 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
3998 _ => return false,
3999 };
4000 matches!(
4001 type_,
4002 HTMLElementTypeId::HTMLEmbedElement |
4003 HTMLElementTypeId::HTMLFormElement |
4004 HTMLElementTypeId::HTMLImageElement |
4005 HTMLElementTypeId::HTMLObjectElement
4006 )
4007}
4008
4009fn is_named_element_with_id_attribute(elem: &Element) -> bool {
4010 elem.is_html_element()
4011}
4012
4013#[expect(unsafe_code)]
4014#[unsafe(no_mangle)]
4015unsafe extern "C" fn dump_js_stack(cx: *mut RawJSContext) {
4017 unsafe {
4018 DumpJSStack(cx, true, false, false);
4019 }
4020}
4021
4022impl WindowHelpers for Window {
4023 fn create_named_properties_object(
4024 cx: SafeJSContext,
4025 proto: HandleObject,
4026 object: MutableHandleObject,
4027 ) {
4028 Self::create_named_properties_object(cx, proto, object)
4029 }
4030}