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