1use std::borrow::ToOwned;
6use std::cell::{Cell, RefCell, RefMut};
7use std::collections::HashSet;
8use std::collections::hash_map::Entry;
9use std::default::Default;
10use std::ffi::c_void;
11use std::io::{Write, stderr, stdout};
12use std::ptr::NonNull;
13use std::rc::{Rc, Weak};
14use std::sync::Arc;
15use std::time::{Duration, Instant};
16
17use app_units::Au;
18use base64::Engine;
19use content_security_policy::Violation;
20use content_security_policy::sandboxing_directive::SandboxingFlagSet;
21use crossbeam_channel::{Sender, unbounded};
22use cssparser::SourceLocation;
23use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
24use dom_struct::dom_struct;
25use embedder_traits::user_contents::UserScript;
26use embedder_traits::{
27 AlertResponse, ConfirmResponse, EmbedderMsg, JavaScriptEvaluationError, PromptResponse,
28 ScriptToEmbedderChan, SimpleDialogRequest, Theme, UntrustedNodeAddress, ViewportDetails,
29 WebDriverJSResult, WebDriverLoadStatus,
30};
31use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
32use fonts::{CspViolationHandler, FontContext, NetworkTimingHandler, WebFontDocumentContext};
33use js::context::{JSContext, NoGC};
34use js::glue::DumpJSStack;
35use js::jsapi::{GCReason, Heap, JSContext as RawJSContext, JSObject, JSPROP_ENUMERATE};
36use js::jsval::{NullValue, UndefinedValue};
37use js::realm::{AutoRealm, CurrentRealm};
38use js::rust::wrappers2::{JS_DefineProperty, JS_GC};
39use js::rust::{
40 CustomAutoRooter, CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleObject,
41 MutableHandleValue,
42};
43use layout_api::{
44 AxesOverflow, BoxAreaType, CSSPixelRectVec, ElementsFromPointResult, FragmentType, Layout,
45 LayoutImageDestination, PendingImage, PendingImageState, PendingRasterizationImage,
46 PhysicalSides, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest, ReflowRequestRestyle,
47 ReflowStatistics, RestyleReason, ScrollContainerQueryFlags, ScrollContainerResponse,
48 TrustedNodeAddress, combine_id_with_fragment_type,
49};
50use malloc_size_of::MallocSizeOf;
51use media::WindowGLContext;
52use net_traits::image_cache::{
53 ImageCache, ImageCacheResponseCallback, ImageCacheResponseMessage, ImageLoadListener,
54 ImageResponse, PendingImageId, PendingImageResponse, RasterizationCompleteResponse,
55};
56use net_traits::request::Referrer;
57use net_traits::{ResourceFetchTiming, ResourceThreads};
58use num_traits::ToPrimitive;
59use paint_api::{CrossProcessPaintApi, PinchZoomInfos};
60use profile_traits::generic_channel as ProfiledGenericChannel;
61use profile_traits::mem::ProfilerChan as MemProfilerChan;
62use profile_traits::time::ProfilerChan as TimeProfilerChan;
63use rustc_hash::{FxBuildHasher, FxHashMap};
64use script_bindings::cell::{DomRefCell, Ref};
65use script_bindings::codegen::GenericBindings::WindowBinding::ScrollToOptions;
66use script_bindings::conversions::SafeToJSValConvertible;
67use script_bindings::interfaces::{HasOrigin, WindowHelpers};
68use script_bindings::reflector::DomObject;
69use script_bindings::root::Root;
70use script_traits::{ConstellationInputEvent, ScriptThreadMessage};
71use selectors::attr::CaseSensitivity;
72use servo_arc::Arc as ServoArc;
73use servo_base::cross_process_instant::CrossProcessInstant;
74use servo_base::generic_channel::{self, GenericCallback, GenericSender};
75use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
76#[cfg(feature = "bluetooth")]
77use servo_bluetooth_traits::BluetoothRequest;
78use servo_canvas_traits::webgl::WebGLChan;
79use servo_config::pref;
80use servo_constellation_traits::{
81 LoadData, LoadOrigin, ScreenshotReadinessResponse, ScriptToConstellationMessage,
82 ScriptToConstellationSender, StructuredSerializedData, WindowSizeType,
83};
84use servo_geometry::DeviceIndependentIntRect;
85use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
86use storage_traits::StorageThreads;
87use storage_traits::webstorage_thread::WebStorageType;
88use style::error_reporting::{ContextualParseError, ParseErrorReporter};
89use style::properties::PropertyId;
90use style::properties::style_structs::Font;
91use style::selector_parser::PseudoElement;
92use style::str::HTML_SPACE_CHARACTERS;
93use style::stylesheets::UrlExtraData;
94use style_traits::CSSPixel;
95use stylo_atoms::Atom;
96use time::Duration as TimeDuration;
97use webrender_api::ExternalScrollId;
98use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint};
99
100use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
101 DocumentMethods, DocumentReadyState, NamedPropertyValue,
102};
103use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
104use crate::dom::bindings::codegen::Bindings::HistoryBinding::History_Binding::HistoryMethods;
105use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
106 ImageBitmapOptions, ImageBitmapSource,
107};
108use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryList_Binding::MediaQueryListMethods;
109use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
110use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
111use crate::dom::bindings::codegen::Bindings::RequestBinding::{RequestInfo, RequestInit};
112use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
113use crate::dom::bindings::codegen::Bindings::WindowBinding::{
114 self, DeferredRequestInit, FrameRequestCallback, ScrollBehavior, WindowMethods,
115 WindowPostMessageOptions,
116};
117use crate::dom::bindings::codegen::UnionTypes::{
118 RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
119};
120use crate::dom::bindings::error::{
121 Error, ErrorInfo, ErrorResult, Fallible, javascript_error_info_from_error_info,
122};
123use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
124use crate::dom::bindings::num::Finite;
125use crate::dom::bindings::refcounted::Trusted;
126use crate::dom::bindings::reflector::DomGlobal;
127use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
128use crate::dom::bindings::str::{DOMString, USVString};
129use crate::dom::bindings::structuredclone;
130use crate::dom::bindings::trace::{
131 CustomTraceable, HashMapTracedValues, JSTraceable, RootedTraceableBox,
132};
133use crate::dom::bindings::utils::GlobalStaticData;
134use crate::dom::bindings::weakref::DOMTracker;
135#[cfg(feature = "bluetooth")]
136use crate::dom::bluetooth::BluetoothExtraPermissionData;
137use crate::dom::cookiestore::CookieStore;
138use crate::dom::crypto::Crypto;
139use crate::dom::csp::GlobalCspReporting;
140use crate::dom::css::cssstyledeclaration::{
141 CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner,
142};
143use crate::dom::customelementregistry::CustomElementRegistry;
144use crate::dom::document::focus::FocusableArea;
145use crate::dom::document::{
146 AnimationFrameCallback, Document, SameOriginDescendantNavigablesIterator,
147};
148use crate::dom::element::Element;
149use crate::dom::event::{Event, EventBubbles, EventCancelable};
150use crate::dom::eventtarget::EventTarget;
151use crate::dom::fetchlaterresult::FetchLaterResult;
152use crate::dom::globalscope::GlobalScope;
153use crate::dom::history::History;
154use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
155use crate::dom::html::htmliframeelement::HTMLIFrameElement;
156use crate::dom::idbfactory::IDBFactory;
157use crate::dom::inputevent::HitTestResult;
158use crate::dom::location::Location;
159use crate::dom::medialist::MediaList;
160use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
161use crate::dom::mediaquerylistevent::MediaQueryListEvent;
162use crate::dom::messageevent::MessageEvent;
163use crate::dom::navigator::Navigator;
164use crate::dom::node::{Node, NodeDamage, NodeTraits, from_untrusted_node_address};
165use crate::dom::performance::performance::Performance;
166use crate::dom::performanceresourcetiming::InitiatorType;
167use crate::dom::promise::Promise;
168use crate::dom::reporting::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
169use crate::dom::reporting::reportingobserver::ReportingObserver;
170use crate::dom::screen::Screen;
171use crate::dom::scrolling_box::{ScrollingBox, ScrollingBoxSource};
172use crate::dom::selection::Selection;
173use crate::dom::shadowroot::ShadowRoot;
174use crate::dom::storage::Storage;
175#[cfg(feature = "bluetooth")]
176use crate::dom::testrunner::TestRunner;
177use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
178use crate::dom::types::{ImageBitmap, MouseEvent, SVGSVGElement, UIEvent};
179use crate::dom::useractivation::UserActivationTimestamp;
180use crate::dom::visualviewport::{VisualViewport, VisualViewportChanges};
181#[cfg(feature = "webgpu")]
182use crate::dom::webgpu::identityhub::IdentityHub;
183use crate::dom::windowproxy::{WindowProxy, WindowProxyHandler};
184use crate::dom::worklet::Worklet;
185use crate::dom::workletglobalscope::WorkletGlobalScopeType;
186use crate::layout_image::fetch_image_for_layout;
187use crate::messaging::{MainThreadScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
188use crate::microtask::{Microtask, UserMicrotask};
189use crate::network_listener::{ResourceTimingListener, submit_timing};
190use crate::realms::enter_auto_realm;
191use crate::script_runtime::{CanGc, Runtime};
192use crate::script_thread::ScriptThread;
193use crate::script_window_proxies::ScriptWindowProxies;
194use crate::task_manager::TaskManager;
195use crate::task_source::SendableTaskSource;
196use crate::timers::{IsInterval, OneshotTimers, TimerCallback};
197use crate::unminify::unminified_path;
198use crate::webdriver_handlers::{find_node_by_unique_id_in_document, jsval_to_webdriver};
199use crate::{fetch, window_named_properties};
200
201#[derive(MallocSizeOf)]
206pub struct PendingImageCallback(
207 #[ignore_malloc_size_of = "dyn Fn is currently impossible to measure"]
208 #[expect(clippy::type_complexity)]
209 Box<dyn Fn(PendingImageResponse, &mut JSContext) + 'static>,
210);
211
212#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
214enum WindowState {
215 Alive,
216 Zombie, }
218
219const INITIAL_REFLOW_DELAY: Duration = Duration::from_millis(200);
222
223#[derive(Clone, Copy, MallocSizeOf)]
234enum LayoutBlocker {
235 WaitingForParse,
237 Parsing(Instant),
239 FiredLoadEventOrParsingTimerExpired,
243}
244
245impl LayoutBlocker {
246 fn layout_blocked(&self) -> bool {
247 !matches!(self, Self::FiredLoadEventOrParsingTimerExpired)
248 }
249}
250
251#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf, PartialEq)]
254pub(crate) struct OngoingNavigation(u32);
255
256type PendingImageRasterizationKey = (PendingImageId, DeviceIntSize);
257
258#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
262#[derive(JSTraceable, MallocSizeOf)]
263struct PendingLayoutImageAncillaryData {
264 node: Dom<Node>,
265 #[no_trace]
266 destination: LayoutImageDestination,
267}
268
269#[dom_struct]
270pub(crate) struct Window {
271 globalscope: GlobalScope,
272
273 #[ignore_malloc_size_of = "Weak does not need to be accounted"]
277 #[no_trace]
278 weak_script_thread: Weak<ScriptThread>,
279
280 #[no_trace]
284 webview_id: WebViewId,
285 script_chan: Sender<MainThreadScriptMsg>,
286 #[no_trace]
287 #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"]
288 layout: RefCell<Box<dyn Layout>>,
289 navigator: MutNullableDom<Navigator>,
290 crypto: MutNullableDom<Crypto>,
291 #[no_trace]
292 image_cache_sender: Sender<ImageCacheResponseMessage>,
293 window_proxy: MutNullableDom<WindowProxy>,
294 document: MutNullableDom<Document>,
295 location: MutNullableDom<Location>,
296 performance: MutNullableDom<Performance>,
297 #[no_trace]
298 navigation_start: Cell<CrossProcessInstant>,
299 screen: MutNullableDom<Screen>,
300 session_storage: MutNullableDom<Storage>,
301 local_storage: MutNullableDom<Storage>,
302 cookie_store: MutNullableDom<CookieStore>,
304 status: DomRefCell<DOMString>,
305 trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
306
307 ongoing_navigation: Cell<OngoingNavigation>,
310
311 #[no_trace]
314 devtools_markers: DomRefCell<HashSet<TimelineMarkerType>>,
315 #[no_trace]
316 devtools_marker_sender: DomRefCell<Option<GenericSender<Option<TimelineMarker>>>>,
317
318 #[no_trace]
320 unhandled_resize_event: DomRefCell<Option<(ViewportDetails, WindowSizeType)>>,
321
322 #[no_trace]
324 theme: Cell<Theme>,
325
326 #[no_trace]
328 parent_info: Option<PipelineId>,
329
330 dom_static: GlobalStaticData,
332
333 #[conditional_malloc_size_of]
335 js_runtime: DomRefCell<Option<Rc<Runtime>>>,
336
337 #[no_trace]
339 viewport_details: Cell<ViewportDetails>,
340
341 #[no_trace]
343 #[cfg(feature = "bluetooth")]
344 bluetooth_thread: GenericSender<BluetoothRequest>,
345
346 #[cfg(feature = "bluetooth")]
347 bluetooth_extra_permission_data: BluetoothExtraPermissionData,
348
349 #[no_trace]
353 layout_blocker: Cell<LayoutBlocker>,
354
355 #[no_trace]
357 webdriver_script_chan: DomRefCell<Option<GenericSender<WebDriverJSResult>>>,
358
359 #[no_trace]
361 webdriver_load_status_sender: RefCell<Option<GenericSender<WebDriverLoadStatus>>>,
362
363 current_state: Cell<WindowState>,
365
366 error_reporter: CSSErrorReporter,
367
368 media_query_lists: DOMTracker<MediaQueryList>,
370
371 #[cfg(feature = "bluetooth")]
372 test_runner: MutNullableDom<TestRunner>,
373
374 #[no_trace]
376 webgl_chan: Option<WebGLChan>,
377
378 #[ignore_malloc_size_of = "defined in webxr"]
379 #[no_trace]
380 #[cfg(feature = "webxr")]
381 webxr_registry: Option<webxr_api::Registry>,
382
383 #[no_trace]
387 pending_image_callbacks: DomRefCell<FxHashMap<PendingImageId, Vec<PendingImageCallback>>>,
388
389 pending_layout_images: DomRefCell<
394 HashMapTracedValues<PendingImageId, Vec<PendingLayoutImageAncillaryData>, FxBuildHasher>,
395 >,
396
397 pending_images_for_rasterization: DomRefCell<
401 HashMapTracedValues<PendingImageRasterizationKey, Vec<Dom<Node>>, FxBuildHasher>,
402 >,
403
404 unminified_css_dir: DomRefCell<Option<String>>,
407
408 local_script_source: Option<String>,
410
411 test_worklet: MutNullableDom<Worklet>,
413 paint_worklet: MutNullableDom<Worklet>,
415
416 exists_mut_observer: Cell<bool>,
418
419 #[no_trace]
421 paint_api: CrossProcessPaintApi,
422
423 #[no_trace]
426 #[conditional_malloc_size_of]
427 user_scripts: Rc<Vec<UserScript>>,
428
429 #[ignore_malloc_size_of = "defined in script_thread"]
431 #[no_trace]
432 player_context: WindowGLContext,
433
434 throttled: Cell<bool>,
435
436 #[conditional_malloc_size_of]
440 layout_marker: DomRefCell<Rc<Cell<bool>>>,
441
442 current_event: DomRefCell<Option<Dom<Event>>>,
444
445 reporting_observer_list: DomRefCell<Vec<DomRoot<ReportingObserver>>>,
447
448 report_list: DomRefCell<Vec<Report>>,
450
451 #[no_trace]
453 endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
454
455 #[conditional_malloc_size_of]
457 script_window_proxies: Rc<ScriptWindowProxies>,
458
459 has_pending_screenshot_readiness_request: Cell<bool>,
461
462 visual_viewport: MutNullableDom<VisualViewport>,
465
466 has_changed_visual_viewport_dimension: Cell<bool>,
468
469 pending_media_query_evaluation: Cell<bool>,
474
475 #[no_trace]
477 last_activation_timestamp: Cell<UserActivationTimestamp>,
478
479 devtools_wants_updates: Cell<bool>,
482}
483
484impl Window {
485 pub(crate) fn script_thread(&self) -> Rc<ScriptThread> {
486 Weak::upgrade(&self.weak_script_thread)
487 .expect("Weak reference should always be upgradable when a ScriptThread is running")
488 }
489
490 pub(crate) fn webview_id(&self) -> WebViewId {
491 self.webview_id
492 }
493
494 pub(crate) fn as_global_scope(&self) -> &GlobalScope {
495 self.upcast::<GlobalScope>()
496 }
497
498 pub(crate) fn layout(&self) -> Ref<'_, Box<dyn Layout>> {
499 self.layout.borrow()
500 }
501
502 pub(crate) fn layout_mut(&self) -> RefMut<'_, Box<dyn Layout>> {
503 self.layout.borrow_mut()
504 }
505
506 pub(crate) fn get_exists_mut_observer(&self) -> bool {
507 self.exists_mut_observer.get()
508 }
509
510 pub(crate) fn set_exists_mut_observer(&self) {
511 self.exists_mut_observer.set(true);
512 }
513
514 #[expect(unsafe_code)]
515 pub(crate) fn clear_js_runtime_for_script_deallocation(&self) {
516 self.as_global_scope()
517 .remove_web_messaging_and_dedicated_workers_infra();
518 unsafe {
519 *self.js_runtime.borrow_for_script_deallocation() = None;
520 self.window_proxy.set(None);
521 self.current_state.set(WindowState::Zombie);
522 self.as_global_scope()
523 .task_manager()
524 .cancel_all_tasks_and_ignore_future_tasks();
525 }
526 }
527
528 pub(crate) fn discard_browsing_context(&self) {
531 let proxy = match self.window_proxy.get() {
532 Some(proxy) => proxy,
533 None => panic!("Discarding a BC from a window that has none"),
534 };
535 proxy.discard_browsing_context();
536 self.as_global_scope()
540 .task_manager()
541 .cancel_all_tasks_and_ignore_future_tasks();
542 }
543
544 pub(crate) fn time_profiler_chan(&self) -> &TimeProfilerChan {
546 self.globalscope.time_profiler_chan()
547 }
548
549 pub(crate) fn origin(&self) -> MutableOrigin {
551 self.Document().origin().clone()
553 }
554
555 pub(crate) fn main_thread_script_chan(&self) -> &Sender<MainThreadScriptMsg> {
556 &self.script_chan
557 }
558
559 pub(crate) fn parent_info(&self) -> Option<PipelineId> {
560 self.parent_info
561 }
562
563 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
564 let (sender, receiver) = unbounded();
565 (
566 ScriptEventLoopSender::MainThread(sender),
567 ScriptEventLoopReceiver::MainThread(receiver),
568 )
569 }
570
571 pub(crate) fn event_loop_sender(&self) -> ScriptEventLoopSender {
572 ScriptEventLoopSender::MainThread(self.script_chan.clone())
573 }
574
575 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
576 self.Document().image_cache()
577 }
578
579 pub(crate) fn window_proxy(&self) -> DomRoot<WindowProxy> {
581 self.window_proxy.get().unwrap()
582 }
583
584 pub(crate) fn append_reporting_observer(&self, reporting_observer: DomRoot<ReportingObserver>) {
585 self.reporting_observer_list
586 .borrow_mut()
587 .push(reporting_observer);
588 }
589
590 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
591 let index = {
592 let list = self.reporting_observer_list.borrow();
593 list.iter()
594 .position(|observer| &**observer == reporting_observer)
595 };
596
597 if let Some(index) = index {
598 self.reporting_observer_list.borrow_mut().remove(index);
599 }
600 }
601
602 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
603 self.reporting_observer_list.borrow().clone()
604 }
605
606 pub(crate) fn append_report(&self, report: Report) {
607 self.report_list.borrow_mut().push(report);
608 let trusted_window = Trusted::new(self);
609 self.upcast::<GlobalScope>()
610 .task_manager()
611 .dom_manipulation_task_source()
612 .queue(task!(send_to_reporting_endpoints: move || {
613 let window = trusted_window.root();
614 let reports = std::mem::take(&mut *window.report_list.borrow_mut());
615 window.upcast::<GlobalScope>().send_reports_to_endpoints(
616 reports,
617 window.endpoints_list.borrow().clone(),
618 );
619 }));
620 }
621
622 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
623 self.report_list.borrow().clone()
624 }
625
626 pub(crate) fn set_endpoints_list(&self, endpoints: Vec<ReportingEndpoint>) {
627 *self.endpoints_list.borrow_mut() = endpoints;
628 }
629
630 pub(crate) fn undiscarded_window_proxy(&self) -> Option<DomRoot<WindowProxy>> {
633 self.window_proxy.get().and_then(|window_proxy| {
634 if window_proxy.is_browsing_context_discarded() {
635 None
636 } else {
637 Some(window_proxy)
638 }
639 })
640 }
641
642 pub(crate) fn top_level_document_if_local(&self) -> Option<DomRoot<Document>> {
647 if self.is_top_level() {
648 return Some(self.Document());
649 }
650
651 let window_proxy = self.undiscarded_window_proxy()?;
652 self.script_window_proxies
653 .find_window_proxy(window_proxy.webview_id().into())?
654 .document()
655 }
656
657 #[cfg(feature = "bluetooth")]
658 pub(crate) fn bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
659 self.bluetooth_thread.clone()
660 }
661
662 #[cfg(feature = "bluetooth")]
663 pub(crate) fn bluetooth_extra_permission_data(&self) -> &BluetoothExtraPermissionData {
664 &self.bluetooth_extra_permission_data
665 }
666
667 pub(crate) fn css_error_reporter(&self) -> &CSSErrorReporter {
668 &self.error_reporter
669 }
670
671 pub(crate) fn webgl_chan(&self) -> Option<WebGLChan> {
672 self.webgl_chan.clone()
673 }
674
675 pub(crate) fn webgl_chan_value(&self) -> Option<WebGLChan> {
677 self.webgl_chan.clone()
678 }
679
680 #[cfg(feature = "webxr")]
681 pub(crate) fn webxr_registry(&self) -> Option<webxr_api::Registry> {
682 self.webxr_registry.clone()
683 }
684
685 fn new_paint_worklet(&self, cx: &mut JSContext) -> DomRoot<Worklet> {
686 debug!("Creating new paint worklet.");
687 Worklet::new(cx, self, WorkletGlobalScopeType::Paint)
688 }
689
690 pub(crate) fn register_image_cache_listener(
691 &self,
692 id: PendingImageId,
693 callback: impl Fn(PendingImageResponse, &mut JSContext) + 'static,
694 ) -> ImageCacheResponseCallback {
695 self.pending_image_callbacks
696 .borrow_mut()
697 .entry(id)
698 .or_default()
699 .push(PendingImageCallback(Box::new(callback)));
700
701 let image_cache_sender = self.image_cache_sender.clone();
702 Box::new(move |message| {
703 let _ = image_cache_sender.send(message);
704 })
705 }
706
707 fn pending_layout_image_notification(&self, response: PendingImageResponse) {
708 let mut images = self.pending_layout_images.borrow_mut();
709 let nodes = images.entry(response.id);
710 let nodes = match nodes {
711 Entry::Occupied(nodes) => nodes,
712 Entry::Vacant(_) => return,
713 };
714 if matches!(
715 response.response,
716 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode
717 ) {
718 for ancillary_data in nodes.get() {
719 match ancillary_data.destination {
720 LayoutImageDestination::BoxTreeConstruction => {
721 ancillary_data.node.dirty(NodeDamage::Other);
722 },
723 LayoutImageDestination::DisplayListBuilding => {
724 self.layout().set_needs_new_display_list();
725 },
726 }
727 }
728 }
729
730 match response.response {
731 ImageResponse::MetadataLoaded(_) => {},
732 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode => {
733 nodes.remove();
734 },
735 }
736 }
737
738 pub(crate) fn handle_image_rasterization_complete_notification(
739 &self,
740 response: RasterizationCompleteResponse,
741 ) {
742 let mut images = self.pending_images_for_rasterization.borrow_mut();
743 let nodes = images.entry((response.image_id, response.requested_size));
744 let nodes = match nodes {
745 Entry::Occupied(nodes) => nodes,
746 Entry::Vacant(_) => return,
747 };
748 for node in nodes.get() {
749 node.dirty(NodeDamage::Other);
750 }
751 nodes.remove();
752 }
753
754 pub(crate) fn pending_image_notification(
755 &self,
756 response: PendingImageResponse,
757 cx: &mut JSContext,
758 ) {
759 let mut images = std::mem::take(&mut *self.pending_image_callbacks.borrow_mut());
764 let Entry::Occupied(callbacks) = images.entry(response.id) else {
765 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
766 return;
767 };
768
769 for callback in callbacks.get() {
770 callback.0(response.clone(), cx);
771 }
772
773 match response.response {
774 ImageResponse::MetadataLoaded(_) => {},
775 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode => {
776 callbacks.remove();
777 },
778 }
779
780 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
781 }
782
783 pub(crate) fn paint_api(&self) -> &CrossProcessPaintApi {
784 &self.paint_api
785 }
786
787 pub(crate) fn userscripts(&self) -> &[UserScript] {
788 &self.user_scripts
789 }
790
791 pub(crate) fn get_player_context(&self) -> WindowGLContext {
792 self.player_context.clone()
793 }
794
795 pub(crate) fn dispatch_event_with_target_override(&self, cx: &mut JSContext, event: &Event) {
797 event.dispatch(cx, self.upcast(), true);
798 }
799
800 pub(crate) fn font_context(&self) -> &Arc<FontContext> {
801 self.as_global_scope()
802 .font_context()
803 .expect("A `Window` should always have a `FontContext`")
804 }
805
806 pub(crate) fn ongoing_navigation(&self) -> OngoingNavigation {
807 self.ongoing_navigation.get()
808 }
809
810 pub(crate) fn set_ongoing_navigation(&self) -> OngoingNavigation {
812 let new_value = self.ongoing_navigation.get().0.wrapping_add(1);
816
817 self.ongoing_navigation.set(OngoingNavigation(new_value));
824
825 OngoingNavigation(new_value)
827 }
828
829 fn stop_loading(&self, cx: &mut JSContext) {
831 let doc = self.Document();
833
834 self.set_ongoing_navigation();
844
845 doc.abort_a_document_and_its_descendants(cx);
847 }
848
849 fn destroy_top_level_traversable(&self, cx: &mut JSContext) {
851 let document = self.Document();
857 document.destroy_document_and_its_descendants(cx);
859 self.send_to_constellation(ScriptToConstellationMessage::DiscardTopLevelBrowsingContext);
861 }
862
863 fn definitely_close(&self, cx: &mut JSContext) {
865 let document = self.Document();
866 if !document.check_if_unloading_is_cancelled(cx, false) {
871 return;
872 }
873 document.unload(cx, false);
877 self.destroy_top_level_traversable(cx);
879 }
880
881 fn cannot_show_simple_dialogs(&self) -> bool {
883 if self
886 .Document()
887 .has_active_sandboxing_flag(SandboxingFlagSet::SANDBOXED_MODALS_FLAG)
888 {
889 return true;
890 }
891
892 false
911 }
912
913 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut JSContext) {
914 self.script_thread().perform_a_microtask_checkpoint(cx);
915 }
916
917 pub(crate) fn web_font_context(&self) -> WebFontDocumentContext {
918 let global = self.as_global_scope();
919 WebFontDocumentContext {
920 policy_container: global.policy_container(),
921 request_client: global.request_client(),
922 document_url: global.api_base_url(),
923 has_trustworthy_ancestor_origin: global.has_trustworthy_ancestor_origin(),
924 insecure_requests_policy: global.insecure_requests_policy(),
925 csp_handler: Box::new(FontCspHandler {
926 global: Trusted::new(global),
927 task_source: global
928 .task_manager()
929 .dom_manipulation_task_source()
930 .to_sendable(),
931 }),
932 network_timing_handler: Box::new(FontNetworkTimingHandler {
933 global: Trusted::new(global),
934 task_source: global
935 .task_manager()
936 .dom_manipulation_task_source()
937 .to_sendable(),
938 }),
939 }
940 }
941
942 #[expect(unsafe_code)]
943 pub(crate) fn gc(&self, cx: &mut JSContext) {
944 unsafe { JS_GC(cx, GCReason::API) };
945 }
946
947 pub(crate) fn with_timers<T>(&self, f: impl FnOnce(&OneshotTimers) -> T) -> T {
948 let document = self.Document();
949 f(document.timers())
950 }
951}
952
953#[derive(Debug, MallocSizeOf)]
954struct FontCspHandler {
955 global: Trusted<GlobalScope>,
956 task_source: SendableTaskSource,
957}
958
959impl CspViolationHandler for FontCspHandler {
960 fn process_violations(&self, violations: Vec<Violation>) {
961 let global = self.global.clone();
962 self.task_source.queue(task!(csp_violation: move |cx| {
963 global.root().report_csp_violations(cx, violations, None, None);
964 }));
965 }
966
967 fn clone(&self) -> Box<dyn CspViolationHandler> {
968 Box::new(Self {
969 global: self.global.clone(),
970 task_source: self.task_source.clone(),
971 })
972 }
973}
974
975#[derive(Debug, MallocSizeOf)]
976struct FontNetworkTimingHandler {
977 global: Trusted<GlobalScope>,
978 task_source: SendableTaskSource,
979}
980
981impl NetworkTimingHandler for FontNetworkTimingHandler {
982 fn submit_timing(&self, url: ServoUrl, response: ResourceFetchTiming) {
983 let global = self.global.clone();
984 self.task_source.queue(task!(network_timing: move |cx| {
985 submit_timing(
986 cx,
987 &FontFetchListener {
988 url,
989 global
990 },
991 &Ok(()),
992 &response,
993 );
994 }));
995 }
996
997 fn clone(&self) -> Box<dyn NetworkTimingHandler> {
998 Box::new(Self {
999 global: self.global.clone(),
1000 task_source: self.task_source.clone(),
1001 })
1002 }
1003}
1004
1005#[derive(Debug)]
1006struct FontFetchListener {
1007 global: Trusted<GlobalScope>,
1008 url: ServoUrl,
1009}
1010
1011impl ResourceTimingListener for FontFetchListener {
1012 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1013 (InitiatorType::Css, self.url.clone())
1014 }
1015
1016 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1017 self.global.root()
1018 }
1019}
1020
1021pub(crate) fn base64_btoa(input: DOMString) -> Fallible<DOMString> {
1023 if input.str().chars().any(|c: char| c > '\u{FF}') {
1027 Err(Error::InvalidCharacter(None))
1028 } else {
1029 let octets = input
1034 .str()
1035 .chars()
1036 .map(|c: char| c as u8)
1037 .collect::<Vec<u8>>();
1038
1039 let config =
1042 base64::engine::general_purpose::GeneralPurposeConfig::new().with_encode_padding(true);
1043 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
1044 Ok(DOMString::from(engine.encode(octets)))
1045 }
1046}
1047
1048pub(crate) fn base64_atob(input: DOMString) -> Fallible<DOMString> {
1050 fn is_html_space(c: char) -> bool {
1052 HTML_SPACE_CHARACTERS.contains(&c)
1053 }
1054 let without_spaces = input
1055 .str()
1056 .chars()
1057 .filter(|&c| !is_html_space(c))
1058 .collect::<String>();
1059 let mut input = &*without_spaces;
1060
1061 if input.len() % 4 == 0 {
1065 if input.ends_with("==") {
1066 input = &input[..input.len() - 2]
1067 } else if input.ends_with('=') {
1068 input = &input[..input.len() - 1]
1069 }
1070 }
1071
1072 if input.len() % 4 == 1 {
1075 return Err(Error::InvalidCharacter(None));
1076 }
1077
1078 if input
1086 .chars()
1087 .any(|c| c != '+' && c != '/' && !c.is_alphanumeric())
1088 {
1089 return Err(Error::InvalidCharacter(None));
1090 }
1091
1092 let config = base64::engine::general_purpose::GeneralPurposeConfig::new()
1093 .with_decode_padding_mode(base64::engine::DecodePaddingMode::RequireNone)
1094 .with_decode_allow_trailing_bits(true);
1095 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
1096
1097 let data = engine
1098 .decode(input)
1099 .map_err(|_| Error::InvalidCharacter(None))?;
1100 Ok(data.iter().map(|&b| b as char).collect::<String>().into())
1101}
1102
1103impl WindowMethods<crate::DomTypeHolder> for Window {
1104 fn Alert_(&self) {
1106 self.Alert(DOMString::new());
1109 }
1110
1111 fn Alert(&self, mut message: DOMString) {
1113 if self.cannot_show_simple_dialogs() {
1115 return;
1116 }
1117
1118 message.normalize_newlines();
1122
1123 {
1134 let stderr = stderr();
1138 let mut stderr = stderr.lock();
1139 let stdout = stdout();
1140 let mut stdout = stdout.lock();
1141 writeln!(&mut stdout, "\nALERT: {message}").unwrap();
1142 stdout.flush().unwrap();
1143 stderr.flush().unwrap();
1144 }
1145
1146 let (sender, receiver) =
1147 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1148 let dialog = SimpleDialogRequest::Alert {
1149 id: self.Document().embedder_controls().next_control_id(),
1150 message: String::from(message),
1151 response_sender: sender,
1152 };
1153 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1154 receiver.recv().unwrap_or_else(|_| {
1155 debug!("Alert dialog was cancelled or failed to show.");
1157 AlertResponse::Ok
1158 });
1159
1160 }
1163
1164 fn Confirm(&self, mut message: DOMString) -> bool {
1166 if self.cannot_show_simple_dialogs() {
1168 return false;
1169 }
1170
1171 message.normalize_newlines();
1173
1174 let (sender, receiver) =
1180 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1181 let dialog = SimpleDialogRequest::Confirm {
1182 id: self.Document().embedder_controls().next_control_id(),
1183 message: String::from(message),
1184 response_sender: sender,
1185 };
1186 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1187
1188 match receiver.recv() {
1204 Ok(ConfirmResponse::Ok) => true,
1205 Ok(ConfirmResponse::Cancel) => false,
1206 Err(_) => {
1207 warn!("Confirm dialog was cancelled or failed to show.");
1208 false
1209 },
1210 }
1211 }
1212
1213 fn Prompt(&self, mut message: DOMString, default: DOMString) -> Option<DOMString> {
1215 if self.cannot_show_simple_dialogs() {
1217 return None;
1218 }
1219
1220 message.normalize_newlines();
1222
1223 let (sender, receiver) =
1231 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1232 let dialog = SimpleDialogRequest::Prompt {
1233 id: self.Document().embedder_controls().next_control_id(),
1234 message: String::from(message),
1235 default: String::from(default),
1236 response_sender: sender,
1237 };
1238 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1239
1240 match receiver.recv() {
1259 Ok(PromptResponse::Ok(input)) => Some(input.into()),
1260 Ok(PromptResponse::Cancel) => None,
1261 Err(_) => {
1262 warn!("Prompt dialog was cancelled or failed to show.");
1263 None
1264 },
1265 }
1266 }
1267
1268 fn Stop(&self, cx: &mut JSContext) {
1270 self.stop_loading(cx);
1275 }
1276
1277 fn Focus(&self, cx: &mut JSContext) {
1279 let document = self.Document();
1288 if !document.is_active() || self.undiscarded_window_proxy().is_none() {
1289 return;
1290 }
1291
1292 document.focus_handler().focus(cx, FocusableArea::Viewport);
1297
1298 }
1303
1304 fn Blur(&self) {
1306 }
1309
1310 fn Open(
1312 &self,
1313 cx: &mut JSContext,
1314 url: USVString,
1315 target: DOMString,
1316 features: DOMString,
1317 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
1318 self.window_proxy().open(cx, url, target, features)
1319 }
1320
1321 fn GetOpener(&self, cx: &mut CurrentRealm, mut retval: MutableHandleValue) -> Fallible<()> {
1323 let current = match self.window_proxy.get() {
1325 Some(proxy) => proxy,
1326 None => {
1328 retval.set(NullValue());
1329 return Ok(());
1330 },
1331 };
1332 if current.is_browsing_context_discarded() {
1337 retval.set(NullValue());
1338 return Ok(());
1339 }
1340 current.opener(cx, retval);
1342 Ok(())
1343 }
1344
1345 #[expect(unsafe_code)]
1346 fn SetOpener(&self, cx: &mut JSContext, value: HandleValue) -> ErrorResult {
1348 if value.is_null() {
1350 if let Some(proxy) = self.window_proxy.get() {
1351 proxy.disown();
1352 }
1353 return Ok(());
1354 }
1355
1356 let obj = self.reflector().get_jsobject();
1358 let result = unsafe {
1359 JS_DefineProperty(cx, obj, c"opener".as_ptr(), value, JSPROP_ENUMERATE as u32)
1360 };
1361
1362 if result { Ok(()) } else { Err(Error::JSFailed) }
1363 }
1364
1365 fn Closed(&self) -> bool {
1367 self.window_proxy
1368 .get()
1369 .map(|ref proxy| proxy.is_browsing_context_discarded() || proxy.is_closing())
1370 .unwrap_or(true)
1371 }
1372
1373 fn Close(&self, cx: &mut JSContext) {
1375 let window_proxy = match self.window_proxy.get() {
1377 Some(proxy) => proxy,
1378 None => return,
1380 };
1381 if window_proxy.is_closing() {
1383 return;
1384 }
1385 if let Ok(history_length) = self.History(cx).GetLength() {
1388 let is_auxiliary = window_proxy.is_auxiliary();
1389
1390 let is_script_closable = (self.is_top_level() && history_length == 1) ||
1392 is_auxiliary ||
1393 pref!(dom_allow_scripts_to_close_windows);
1394
1395 if is_script_closable {
1399 window_proxy.close();
1401
1402 let this = Trusted::new(self);
1404 let task = task!(window_close_browsing_context: move |cx| {
1405 let window = this.root();
1406 window.definitely_close(cx);
1407 });
1408 self.as_global_scope()
1409 .task_manager()
1410 .dom_manipulation_task_source()
1411 .queue(task);
1412 }
1413 }
1414 }
1415
1416 fn Document(&self) -> DomRoot<Document> {
1418 self.document
1419 .get()
1420 .expect("Document accessed before initialization.")
1421 }
1422
1423 fn History(&self, cx: &mut JSContext) -> DomRoot<History> {
1425 self.Document().history(cx)
1426 }
1427
1428 fn IndexedDB(&self, cx: &mut JSContext) -> DomRoot<IDBFactory> {
1430 self.upcast::<GlobalScope>().get_indexeddb(cx)
1431 }
1432
1433 fn CustomElements(&self, cx: &mut JSContext) -> DomRoot<CustomElementRegistry> {
1435 let document = self.Document();
1438 if let Some(registry) = document.custom_element_registry() {
1439 return registry;
1440 }
1441 let registry = CustomElementRegistry::new(cx, self);
1444 document.set_custom_element_registry(®istry);
1445 registry
1447 }
1448
1449 fn Location(&self, cx: &mut JSContext) -> DomRoot<Location> {
1451 self.location.or_init(|| Location::new(cx, self))
1452 }
1453
1454 fn GetSessionStorage(&self, cx: &mut JSContext) -> Fallible<DomRoot<Storage>> {
1456 if let Some(storage) = self.session_storage.get() {
1459 return Ok(storage);
1460 }
1461
1462 if !self.origin().is_tuple() {
1466 return Err(Error::Security(Some(
1467 "Cannot access sessionStorage from opaque origin.".to_string(),
1468 )));
1469 }
1470
1471 let storage = Storage::new(cx, self, WebStorageType::Session);
1473
1474 self.session_storage.set(Some(&storage));
1476
1477 Ok(storage)
1479 }
1480
1481 fn GetLocalStorage(&self, cx: &mut JSContext) -> Fallible<DomRoot<Storage>> {
1483 if let Some(storage) = self.local_storage.get() {
1486 return Ok(storage);
1487 }
1488
1489 if !self.origin().is_tuple() {
1493 return Err(Error::Security(Some(
1494 "Cannot access localStorage from opaque origin.".to_string(),
1495 )));
1496 }
1497
1498 let storage = Storage::new(cx, self, WebStorageType::Local);
1500
1501 self.local_storage.set(Some(&storage));
1503
1504 Ok(storage)
1506 }
1507
1508 fn CookieStore(&self, can_gc: CanGc) -> DomRoot<CookieStore> {
1510 self.cookie_store
1511 .or_init(|| CookieStore::new(self.upcast::<GlobalScope>(), can_gc))
1512 }
1513
1514 fn Crypto(&self) -> DomRoot<Crypto> {
1516 self.crypto
1517 .or_init(|| Crypto::new(self.as_global_scope(), CanGc::deprecated_note()))
1518 }
1519
1520 fn GetFrameElement(&self) -> Option<DomRoot<Element>> {
1522 let window_proxy = self.window_proxy.get()?;
1524
1525 let container = window_proxy.frame_element()?;
1527
1528 let container_doc = container.owner_document();
1530 let current_doc = GlobalScope::current()
1531 .expect("No current global object")
1532 .as_window()
1533 .Document();
1534 if !current_doc
1535 .origin()
1536 .same_origin_domain(&container_doc.origin())
1537 {
1538 return None;
1539 }
1540 Some(DomRoot::from_ref(container))
1542 }
1543
1544 fn ReportError(&self, cx: &mut JSContext, error: HandleValue) {
1546 self.as_global_scope().report_an_exception(cx, error);
1547 }
1548
1549 fn Navigator(&self) -> DomRoot<Navigator> {
1551 self.navigator
1552 .or_init(|| Navigator::new(self, CanGc::deprecated_note()))
1553 }
1554
1555 fn ClientInformation(&self) -> DomRoot<Navigator> {
1557 self.Navigator()
1558 }
1559
1560 fn SetTimeout(
1562 &self,
1563 cx: &mut JSContext,
1564 callback: TrustedScriptOrStringOrFunction,
1565 timeout: i32,
1566 args: Vec<HandleValue>,
1567 ) -> Fallible<i32> {
1568 let callback = match callback {
1569 TrustedScriptOrStringOrFunction::String(i) => {
1570 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1571 },
1572 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1573 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1574 },
1575 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1576 };
1577 self.as_global_scope().set_timeout_or_interval(
1578 cx,
1579 callback,
1580 args,
1581 Duration::from_millis(timeout.max(0) as u64),
1582 IsInterval::NonInterval,
1583 )
1584 }
1585
1586 fn ClearTimeout(&self, handle: i32) {
1588 self.as_global_scope().clear_timeout_or_interval(handle);
1589 }
1590
1591 fn SetInterval(
1593 &self,
1594 cx: &mut JSContext,
1595 callback: TrustedScriptOrStringOrFunction,
1596 timeout: i32,
1597 args: Vec<HandleValue>,
1598 ) -> Fallible<i32> {
1599 let callback = match callback {
1600 TrustedScriptOrStringOrFunction::String(i) => {
1601 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1602 },
1603 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1604 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1605 },
1606 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1607 };
1608 self.as_global_scope().set_timeout_or_interval(
1609 cx,
1610 callback,
1611 args,
1612 Duration::from_millis(timeout.max(0) as u64),
1613 IsInterval::Interval,
1614 )
1615 }
1616
1617 fn ClearInterval(&self, handle: i32) {
1619 self.ClearTimeout(handle);
1620 }
1621
1622 fn QueueMicrotask(&self, callback: Rc<VoidFunction>) {
1624 ScriptThread::enqueue_microtask(Microtask::User(UserMicrotask {
1625 callback,
1626 pipeline: self.pipeline_id(),
1627 }));
1628 }
1629
1630 fn CreateImageBitmap(
1632 &self,
1633 realm: &mut CurrentRealm,
1634 image: ImageBitmapSource,
1635 options: &ImageBitmapOptions,
1636 ) -> Rc<Promise> {
1637 ImageBitmap::create_image_bitmap(
1638 self.as_global_scope(),
1639 image,
1640 0,
1641 0,
1642 None,
1643 None,
1644 options,
1645 realm,
1646 )
1647 }
1648
1649 fn CreateImageBitmap_(
1651 &self,
1652 realm: &mut CurrentRealm,
1653 image: ImageBitmapSource,
1654 sx: i32,
1655 sy: i32,
1656 sw: i32,
1657 sh: i32,
1658 options: &ImageBitmapOptions,
1659 ) -> Rc<Promise> {
1660 ImageBitmap::create_image_bitmap(
1661 self.as_global_scope(),
1662 image,
1663 sx,
1664 sy,
1665 Some(sw),
1666 Some(sh),
1667 options,
1668 realm,
1669 )
1670 }
1671
1672 fn Window(&self) -> DomRoot<WindowProxy> {
1674 self.window_proxy()
1675 }
1676
1677 fn Self_(&self) -> DomRoot<WindowProxy> {
1679 self.window_proxy()
1680 }
1681
1682 fn Frames(&self) -> DomRoot<WindowProxy> {
1684 self.window_proxy()
1685 }
1686
1687 fn Length(&self) -> u32 {
1689 self.Document().iframes().iter().count() as u32
1690 }
1691
1692 fn GetParent(&self) -> Option<DomRoot<WindowProxy>> {
1694 let window_proxy = self.undiscarded_window_proxy()?;
1696
1697 if let Some(parent) = window_proxy.parent() {
1699 return Some(DomRoot::from_ref(parent));
1700 }
1701 Some(window_proxy)
1703 }
1704
1705 fn GetTop(&self) -> Option<DomRoot<WindowProxy>> {
1707 let window_proxy = self.undiscarded_window_proxy()?;
1709
1710 Some(DomRoot::from_ref(window_proxy.top()))
1712 }
1713
1714 fn Performance(&self) -> DomRoot<Performance> {
1717 self.performance.or_init(|| {
1718 Performance::new(
1719 self.as_global_scope(),
1720 self.navigation_start.get(),
1721 CanGc::deprecated_note(),
1722 )
1723 })
1724 }
1725
1726 global_event_handlers!();
1728
1729 window_event_handlers!();
1731
1732 fn Screen(&self, can_gc: CanGc) -> DomRoot<Screen> {
1734 self.screen.or_init(|| Screen::new(self, can_gc))
1735 }
1736
1737 fn GetVisualViewport(&self, can_gc: CanGc) -> Option<DomRoot<VisualViewport>> {
1739 if !self.Document().is_fully_active() {
1743 return None;
1744 }
1745
1746 Some(self.get_or_init_visual_viewport(can_gc))
1747 }
1748
1749 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
1751 base64_btoa(btoa)
1752 }
1753
1754 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
1756 base64_atob(atob)
1757 }
1758
1759 fn RequestAnimationFrame(&self, callback: Rc<FrameRequestCallback>) -> u32 {
1761 self.Document()
1762 .request_animation_frame(AnimationFrameCallback::FrameRequestCallback { callback })
1763 }
1764
1765 fn CancelAnimationFrame(&self, ident: u32) {
1767 let doc = self.Document();
1768 doc.cancel_animation_frame(ident);
1769 }
1770
1771 fn PostMessage(
1773 &self,
1774 cx: &mut JSContext,
1775 message: HandleValue,
1776 target_origin: USVString,
1777 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1778 ) -> ErrorResult {
1779 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1780 let source = incumbent.as_window();
1781 let source_origin = source.Document().origin().immutable().clone();
1782
1783 self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
1784 }
1785
1786 fn PostMessage_(
1788 &self,
1789 cx: &mut JSContext,
1790 message: HandleValue,
1791 options: RootedTraceableBox<WindowPostMessageOptions>,
1792 ) -> ErrorResult {
1793 let mut rooted = CustomAutoRooter::new(
1794 options
1795 .parent
1796 .transfer
1797 .iter()
1798 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
1799 .collect(),
1800 );
1801 #[expect(unsafe_code)]
1802 let transfer = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
1803
1804 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1805 let source = incumbent.as_window();
1806
1807 let source_origin = source.Document().origin().immutable().clone();
1808
1809 self.post_message_impl(
1810 &options.targetOrigin,
1811 source_origin,
1812 source,
1813 cx,
1814 message,
1815 transfer,
1816 )
1817 }
1818
1819 fn CaptureEvents(&self) {
1821 }
1823
1824 fn ReleaseEvents(&self) {
1826 }
1828
1829 fn WebdriverCallback(&self, realm: &mut CurrentRealm, value: HandleValue) {
1831 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1832 if let Some(webdriver_script_sender) = webdriver_script_sender {
1833 let result = jsval_to_webdriver(realm, &self.globalscope, value);
1834 let _ = webdriver_script_sender.send(result);
1835 }
1836 }
1837
1838 fn WebdriverException(&self, cx: &mut JSContext, value: HandleValue) {
1839 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1840 if let Some(webdriver_script_sender) = webdriver_script_sender {
1841 let error_info = ErrorInfo::from_value(cx, value);
1842 let _ = webdriver_script_sender.send(Err(
1843 JavaScriptEvaluationError::EvaluationFailure(Some(
1844 javascript_error_info_from_error_info(cx, &error_info, value),
1845 )),
1846 ));
1847 }
1848 }
1849
1850 fn WebdriverElement(&self, id: DOMString) -> Option<DomRoot<Element>> {
1851 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1852 }
1853
1854 fn WebdriverFrame(&self, browsing_context_id: DOMString) -> Option<DomRoot<WindowProxy>> {
1855 self.Document()
1856 .iframes()
1857 .iter()
1858 .find(|iframe| {
1859 iframe
1860 .browsing_context_id()
1861 .as_ref()
1862 .map(BrowsingContextId::to_string) ==
1863 Some(browsing_context_id.to_string())
1864 })
1865 .and_then(|iframe| iframe.GetContentWindow())
1866 }
1867
1868 fn WebdriverWindow(&self, webview_id: DOMString) -> DomRoot<WindowProxy> {
1869 let window_proxy = &self
1870 .window_proxy
1871 .get()
1872 .expect("Should always have a WindowProxy when calling WebdriverWindow");
1873 assert!(
1874 self.is_top_level(),
1875 "Window must be top level browsing context."
1876 );
1877 assert!(self.webview_id().to_string() == webview_id);
1878 DomRoot::from_ref(window_proxy)
1879 }
1880
1881 fn WebdriverShadowRoot(&self, id: DOMString) -> Option<DomRoot<ShadowRoot>> {
1882 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1883 }
1884
1885 fn GetComputedStyle(
1887 &self,
1888 cx: &mut JSContext,
1889 element: &Element,
1890 pseudo: Option<DOMString>,
1891 ) -> DomRoot<CSSStyleDeclaration> {
1892 let mut is_null = false;
1896
1897 let pseudo = pseudo.map(|mut s| {
1903 s.make_ascii_lowercase();
1904 s
1905 });
1906 let pseudo = match pseudo {
1907 Some(ref pseudo) if pseudo == ":before" || pseudo == "::before" => {
1908 Some(PseudoElement::Before)
1909 },
1910 Some(ref pseudo) if pseudo == ":after" || pseudo == "::after" => {
1911 Some(PseudoElement::After)
1912 },
1913 Some(ref pseudo) if pseudo == "::selection" => Some(PseudoElement::Selection),
1914 Some(ref pseudo) if pseudo == "::marker" => Some(PseudoElement::Marker),
1915 Some(ref pseudo) if pseudo == "::placeholder" => Some(PseudoElement::Placeholder),
1916 Some(ref pseudo) if pseudo.starts_with(':') => {
1917 is_null = true;
1920 None
1921 },
1922 _ => None,
1923 };
1924
1925 CSSStyleDeclaration::new(
1941 cx,
1942 self,
1943 if is_null {
1944 CSSStyleOwner::Null
1945 } else {
1946 CSSStyleOwner::Element(Dom::from_ref(element))
1947 },
1948 pseudo,
1949 CSSModificationAccess::Readonly,
1950 )
1951 }
1952
1953 fn InnerHeight(&self) -> i32 {
1956 self.viewport_details
1957 .get()
1958 .size
1959 .height
1960 .to_i32()
1961 .unwrap_or(0)
1962 }
1963
1964 fn InnerWidth(&self) -> i32 {
1967 self.viewport_details.get().size.width.to_i32().unwrap_or(0)
1968 }
1969
1970 fn ScrollX(&self) -> i32 {
1972 self.scroll_offset().x as i32
1973 }
1974
1975 fn PageXOffset(&self) -> i32 {
1977 self.ScrollX()
1978 }
1979
1980 fn ScrollY(&self) -> i32 {
1982 self.scroll_offset().y as i32
1983 }
1984
1985 fn PageYOffset(&self) -> i32 {
1987 self.ScrollY()
1988 }
1989
1990 fn Scroll(&self, cx: &mut JSContext, options: &ScrollToOptions) {
1992 let x = options.left.unwrap_or(0.0) as f32;
1997
1998 let y = options.top.unwrap_or(0.0) as f32;
2001
2002 self.scroll(cx, x, y, options.parent.behavior);
2004 }
2005
2006 fn Scroll_(&self, cx: &mut JSContext, x: f64, y: f64) {
2008 self.scroll(cx, x as f32, y as f32, ScrollBehavior::Auto);
2012 }
2013
2014 fn ScrollTo(&self, cx: &mut JSContext, options: &ScrollToOptions) {
2019 self.Scroll(cx, options);
2020 }
2021
2022 fn ScrollTo_(&self, cx: &mut JSContext, x: f64, y: f64) {
2027 self.Scroll_(cx, x, y)
2028 }
2029
2030 fn ScrollBy(&self, cx: &mut JSContext, options: &ScrollToOptions) {
2032 let mut options = options.clone();
2038 let x = options.left.unwrap_or(0.0);
2039 let x = if x.is_finite() { x } else { 0.0 };
2040 let y = options.top.unwrap_or(0.0);
2041 let y = if y.is_finite() { y } else { 0.0 };
2042
2043 options.left.replace(x + self.ScrollX() as f64);
2045
2046 options.top.replace(y + self.ScrollY() as f64);
2048
2049 self.Scroll(cx, &options)
2051 }
2052
2053 fn ScrollBy_(&self, cx: &mut JSContext, x: f64, y: f64) {
2055 let mut options = ScrollToOptions::empty();
2059
2060 options.left.replace(x);
2063
2064 options.top.replace(y);
2066
2067 self.ScrollBy(cx, &options);
2069 }
2070
2071 fn ResizeTo(&self, width: i32, height: i32) {
2073 let window_proxy = match self.window_proxy.get() {
2075 Some(proxy) => proxy,
2076 None => return,
2077 };
2078
2079 if !window_proxy.is_auxiliary() {
2082 return;
2083 }
2084
2085 let dpr = self.device_pixel_ratio();
2086 let size = Size2D::new(width, height).to_f32() * dpr;
2087 self.send_to_embedder(EmbedderMsg::ResizeTo(self.webview_id(), size.to_i32()));
2088 }
2089
2090 fn ResizeBy(&self, x: i32, y: i32) {
2092 let size = self.client_window().size();
2093 self.ResizeTo(x + size.width, y + size.height)
2095 }
2096
2097 fn MoveTo(&self, x: i32, y: i32) {
2099 let dpr = self.device_pixel_ratio();
2102 let point = Point2D::new(x, y).to_f32() * dpr;
2103 let msg = EmbedderMsg::MoveTo(self.webview_id(), point.to_i32());
2104 self.send_to_embedder(msg);
2105 }
2106
2107 fn MoveBy(&self, x: i32, y: i32) {
2109 let origin = self.client_window().min;
2110 self.MoveTo(x + origin.x, y + origin.y)
2112 }
2113
2114 fn ScreenX(&self) -> i32 {
2116 self.client_window().min.x
2117 }
2118
2119 fn ScreenLeft(&self) -> i32 {
2121 self.client_window().min.x
2122 }
2123
2124 fn ScreenY(&self) -> i32 {
2126 self.client_window().min.y
2127 }
2128
2129 fn ScreenTop(&self) -> i32 {
2131 self.client_window().min.y
2132 }
2133
2134 fn OuterHeight(&self) -> i32 {
2136 self.client_window().height()
2137 }
2138
2139 fn OuterWidth(&self) -> i32 {
2141 self.client_window().width()
2142 }
2143
2144 fn DevicePixelRatio(&self) -> Finite<f64> {
2146 Finite::wrap(self.device_pixel_ratio().get() as f64)
2147 }
2148
2149 fn Status(&self) -> DOMString {
2151 self.status.borrow().clone()
2152 }
2153
2154 fn SetStatus(&self, status: DOMString) {
2156 *self.status.borrow_mut() = status
2157 }
2158
2159 fn MatchMedia(&self, query: DOMString) -> DomRoot<MediaQueryList> {
2161 let media_query_list = MediaList::parse_media_list(&query.str(), self);
2162 let document = self.Document();
2163 let mql = MediaQueryList::new(&document, media_query_list, CanGc::deprecated_note());
2164 self.media_query_lists.track(&*mql);
2165 mql
2166 }
2167
2168 fn Fetch(
2170 &self,
2171 realm: &mut CurrentRealm,
2172 input: RequestOrUSVString,
2173 init: RootedTraceableBox<RequestInit>,
2174 ) -> Rc<Promise> {
2175 fetch::Fetch(self.upcast(), input, init, realm)
2176 }
2177
2178 fn FetchLater(
2180 &self,
2181 cx: &mut JSContext,
2182 input: RequestInfo,
2183 init: RootedTraceableBox<DeferredRequestInit>,
2184 ) -> Fallible<DomRoot<FetchLaterResult>> {
2185 fetch::FetchLater(cx, self, input, init)
2186 }
2187
2188 #[cfg(feature = "bluetooth")]
2189 fn TestRunner(&self, cx: &mut JSContext) -> DomRoot<TestRunner> {
2190 self.test_runner
2191 .or_init(|| TestRunner::new(cx, self.upcast()))
2192 }
2193
2194 fn RunningAnimationCount(&self) -> u32 {
2195 self.document
2196 .get()
2197 .map_or(0, |d| d.animations().running_animation_count() as u32)
2198 }
2199
2200 fn SetName(&self, name: DOMString) {
2202 if let Some(proxy) = self.undiscarded_window_proxy() {
2203 proxy.set_name(name);
2204 }
2205 }
2206
2207 fn Name(&self) -> DOMString {
2209 match self.undiscarded_window_proxy() {
2210 Some(proxy) => proxy.get_name(),
2211 None => "".into(),
2212 }
2213 }
2214
2215 fn Origin(&self) -> USVString {
2217 USVString(self.origin().immutable().ascii_serialization())
2218 }
2219
2220 fn GetSelection(&self, cx: &mut JSContext) -> Option<DomRoot<Selection>> {
2222 self.document.get().and_then(|d| d.GetSelection(cx))
2223 }
2224
2225 fn Event(&self, cx: &mut JSContext, rval: MutableHandleValue) {
2227 if let Some(ref event) = *self.current_event.borrow() {
2228 event.reflector().get_jsobject().safe_to_jsval(cx, rval);
2229 }
2230 }
2231
2232 fn IsSecureContext(&self) -> bool {
2233 self.as_global_scope().is_secure_context()
2234 }
2235
2236 fn NamedGetter(&self, cx: &mut JSContext, name: DOMString) -> Option<NamedPropertyValue> {
2238 if name.is_empty() {
2239 return None;
2240 }
2241 let document = self.Document();
2242
2243 let iframes: Vec<_> = document
2245 .iframes()
2246 .iter()
2247 .filter(|iframe| {
2248 if let Some(window) = iframe.GetContentWindow() {
2249 return window.get_name() == name;
2250 }
2251 false
2252 })
2253 .collect();
2254
2255 let iframe_iter = iframes.iter().map(|iframe| iframe.upcast::<Element>());
2256
2257 let name = Atom::from(name);
2258
2259 let elements_with_name = document.get_elements_with_name(cx, &name);
2261 let name_iter = elements_with_name
2262 .iter()
2263 .map(|element| &**element)
2264 .filter(|elem| is_named_element_with_name_attribute(elem));
2265
2266 let elements_with_id = document.get_elements_with_id(cx, &name);
2267 let id_iter = elements_with_id
2268 .iter()
2269 .map(|element| &**element)
2270 .filter(|elem| is_named_element_with_id_attribute(elem));
2271
2272 for elem in iframe_iter.clone() {
2274 if let Some(nested_window_proxy) = elem
2275 .downcast::<HTMLIFrameElement>()
2276 .and_then(|iframe| iframe.GetContentWindow())
2277 {
2278 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
2279 }
2280 }
2281
2282 let mut elements = iframe_iter.chain(name_iter).chain(id_iter);
2283
2284 let first = elements.next()?;
2285
2286 if elements.next().is_none() {
2287 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
2289 }
2290
2291 #[derive(JSTraceable, MallocSizeOf)]
2293 struct WindowNamedGetter {
2294 #[no_trace]
2295 name: Atom,
2296 }
2297 impl CollectionFilter for WindowNamedGetter {
2298 fn filter(&self, elem: &Element, _root: &Node) -> bool {
2299 let type_ = match elem.upcast::<Node>().type_id() {
2300 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
2301 _ => return false,
2302 };
2303 if elem.get_id().as_ref() == Some(&self.name) {
2304 return true;
2305 }
2306 match type_ {
2307 HTMLElementTypeId::HTMLEmbedElement |
2308 HTMLElementTypeId::HTMLFormElement |
2309 HTMLElementTypeId::HTMLImageElement |
2310 HTMLElementTypeId::HTMLObjectElement => {
2311 elem.get_name().as_ref() == Some(&self.name)
2312 },
2313 _ => false,
2314 }
2315 }
2316 }
2317 let collection = HTMLCollection::create(
2318 cx,
2319 self,
2320 document.upcast(),
2321 Box::new(WindowNamedGetter { name }),
2322 );
2323 Some(NamedPropertyValue::HTMLCollection(collection))
2324 }
2325
2326 fn SupportedPropertyNames(&self, no_gc: &NoGC) -> Vec<DOMString> {
2328 self.Document().SupportedPropertyNames(no_gc)
2329 }
2330
2331 fn StructuredClone(
2333 &self,
2334 cx: &mut JSContext,
2335 value: HandleValue,
2336 options: RootedTraceableBox<StructuredSerializeOptions>,
2337 retval: MutableHandleValue,
2338 ) -> Fallible<()> {
2339 self.as_global_scope()
2340 .structured_clone(cx, value, options, retval)
2341 }
2342
2343 fn TrustedTypes(&self, cx: &mut JSContext) -> DomRoot<TrustedTypePolicyFactory> {
2344 self.trusted_types
2345 .or_init(|| TrustedTypePolicyFactory::new(cx, self.as_global_scope()))
2346 }
2347}
2348
2349impl Window {
2350 pub(crate) fn scroll_offset(&self) -> Vector2D<f32, LayoutPixel> {
2351 self.scroll_offset_query_with_external_scroll_id(self.pipeline_id().root_scroll_id())
2352 }
2353
2354 pub(crate) fn create_named_properties_object(
2357 cx: &mut JSContext,
2358 proto: HandleObject,
2359 object: MutableHandleObject,
2360 ) {
2361 window_named_properties::create(cx.into(), proto, object)
2362 }
2363
2364 pub(crate) fn current_event(&self) -> Option<DomRoot<Event>> {
2365 self.current_event
2366 .borrow()
2367 .as_ref()
2368 .map(|e| DomRoot::from_ref(&**e))
2369 }
2370
2371 pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
2372 let current = self.current_event();
2373 *self.current_event.borrow_mut() = event.map(Dom::from_ref);
2374 current
2375 }
2376
2377 fn post_message_impl(
2379 &self,
2380 target_origin: &USVString,
2381 source_origin: ImmutableOrigin,
2382 source: &Window,
2383 cx: &mut JSContext,
2384 message: HandleValue,
2385 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
2386 ) -> ErrorResult {
2387 let data = structuredclone::write(cx, message, Some(transfer))?;
2389
2390 let target_origin = match target_origin.0[..].as_ref() {
2392 "*" => None,
2393 "/" => Some(source_origin.clone()),
2394 url => match ServoUrl::parse(url) {
2395 Ok(url) => Some(url.origin()),
2396 Err(_) => return Err(Error::Syntax(None)),
2397 },
2398 };
2399
2400 self.post_message(target_origin, source_origin, &source.window_proxy(), data);
2402 Ok(())
2403 }
2404
2405 pub(crate) fn paint_worklet(&self, cx: &mut JSContext) -> DomRoot<Worklet> {
2407 self.paint_worklet.or_init(|| self.new_paint_worklet(cx))
2408 }
2409
2410 pub(crate) fn clear_js_runtime(&self) {
2411 self.as_global_scope()
2412 .remove_web_messaging_and_dedicated_workers_infra();
2413
2414 self.Document().teardown_custom_element_registry();
2417
2418 self.current_state.set(WindowState::Zombie);
2419 *self.js_runtime.borrow_mut() = None;
2420
2421 if let Some(performance) = self.performance.get() {
2422 performance.clear_and_disable_performance_entry_buffer();
2423 }
2424 self.as_global_scope()
2425 .task_manager()
2426 .cancel_all_tasks_and_ignore_future_tasks();
2427
2428 self.pending_image_callbacks.borrow_mut().clear();
2431 }
2432
2433 pub(crate) fn scroll(&self, cx: &mut JSContext, x: f32, y: f32, behavior: ScrollBehavior) {
2435 let xfinite = if x.is_finite() { x } else { 0.0 };
2437 let yfinite = if y.is_finite() { y } else { 0.0 };
2438
2439 let viewport = self.viewport_details.get().size;
2449
2450 let scrolling_area = self.scrolling_area_query(None).to_f32();
2469 let x = xfinite.clamp(0.0, 0.0f32.max(scrolling_area.width() - viewport.width));
2470 let y = yfinite.clamp(0.0, 0.0f32.max(scrolling_area.height() - viewport.height));
2471
2472 let scroll_offset = self.scroll_offset();
2475 if x == scroll_offset.x && y == scroll_offset.y {
2476 return;
2477 }
2478
2479 self.perform_a_scroll(
2484 cx,
2485 x,
2486 y,
2487 self.pipeline_id().root_scroll_id(),
2488 behavior,
2489 None,
2490 );
2491 }
2492
2493 pub(crate) fn perform_a_scroll(
2495 &self,
2496 cx: &mut JSContext,
2497 x: f32,
2498 y: f32,
2499 scroll_id: ExternalScrollId,
2500 _behavior: ScrollBehavior,
2501 element: Option<&Element>,
2502 ) {
2503 let (reflow_phases_run, _) = self.reflow(
2507 cx,
2508 ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)),
2509 );
2510 if reflow_phases_run.needs_frame() {
2511 self.paint_api()
2512 .generate_frame(vec![self.webview_id().into()]);
2513 }
2514
2515 if reflow_phases_run.contains(ReflowPhasesRun::UpdatedScrollNodeOffset) {
2520 match element {
2521 Some(element) if !scroll_id.is_root() => element.handle_scroll_event(),
2522 _ => self.Document().handle_viewport_scroll_event(),
2523 };
2524 }
2525 }
2526
2527 pub(crate) fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
2528 self.viewport_details.get().hidpi_scale_factor
2529 }
2530
2531 fn client_window(&self) -> DeviceIndependentIntRect {
2532 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel!");
2533
2534 self.send_to_embedder(EmbedderMsg::GetWindowRect(self.webview_id(), sender));
2535
2536 receiver.recv().unwrap_or_default()
2537 }
2538
2539 pub(crate) fn advance_animation_clock(&self, delta: TimeDuration) {
2542 self.Document()
2543 .advance_animation_timeline_for_testing(delta);
2544 ScriptThread::handle_tick_all_animations_for_testing(self.pipeline_id());
2545 }
2546
2547 pub(crate) fn reflow(
2555 &self,
2556 cx: &mut JSContext,
2557 reflow_goal: ReflowGoal,
2558 ) -> (ReflowPhasesRun, ReflowStatistics) {
2559 let document = self.Document();
2560
2561 if !document.is_fully_active() {
2563 return Default::default();
2564 }
2565
2566 self.Document().ensure_safe_to_run_script_or_layout();
2567
2568 let pipeline_id = self.pipeline_id();
2572 if reflow_goal == ReflowGoal::UpdateTheRendering &&
2573 self.layout_blocker.get().layout_blocked()
2574 {
2575 debug!("Suppressing pre-load-event reflow pipeline {pipeline_id}");
2576 return Default::default();
2577 }
2578
2579 debug!("script: performing reflow for goal {reflow_goal:?}");
2580 let marker = if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) {
2581 Some(TimelineMarker::start("Reflow".to_owned()))
2582 } else {
2583 None
2584 };
2585
2586 let restyle_reason = document.restyle_reason();
2587 document.clear_restyle_reasons();
2588 let restyle = if restyle_reason.needs_restyle() {
2589 debug!("Invalidating layout cache due to reflow condition {restyle_reason:?}",);
2590 self.layout_marker.borrow().set(false);
2592 *self.layout_marker.borrow_mut() = Rc::new(Cell::new(true));
2594
2595 let stylesheets_changed = document.flush_stylesheets_for_reflow();
2596 let pending_restyles = document.drain_pending_restyles();
2597 let dirty_root = document
2598 .take_dirty_root()
2599 .filter(|_| !stylesheets_changed)
2600 .or_else(|| document.GetDocumentElement())
2601 .map(|root| root.upcast::<Node>().to_trusted_node_address());
2602
2603 Some(ReflowRequestRestyle {
2604 reason: restyle_reason,
2605 dirty_root,
2606 stylesheets_changed,
2607 pending_restyles,
2608 })
2609 } else {
2610 None
2611 };
2612
2613 document.id_map().resolve_all(cx.no_gc(), document.upcast());
2616
2617 let document_context = self.web_font_context();
2618
2619 let rooted_nodes_for_accessibility_integrity_check =
2620 document.rooted_nodes_for_accessibility_integrity_check();
2621
2622 let reflow = ReflowRequest {
2624 document: document.upcast::<Node>().to_trusted_node_address(),
2625 epoch: document.current_rendering_epoch(),
2626 restyle,
2627 viewport_details: self.viewport_details.get(),
2628 origin: self.origin().immutable().clone(),
2629 reflow_goal,
2630 animation_timeline_value: document.current_animation_timeline_value(),
2631 animations: document.animations().sets.clone(),
2632 animating_images: document.image_animation_manager().animating_images(),
2633 highlighted_dom_node: document.highlighted_dom_node().map(|node| node.to_opaque()),
2634 document_context,
2635 rooted_nodes_for_accessibility_integrity_check,
2636 };
2637
2638 let Some(reflow_result) = self.layout.borrow_mut().reflow(reflow) else {
2639 return Default::default();
2640 };
2641
2642 debug!("script: layout complete");
2643 if let Some(marker) = marker {
2644 self.emit_timeline_marker(marker.end());
2645 }
2646
2647 self.handle_pending_images_post_reflow(
2648 cx,
2649 reflow_result.pending_images,
2650 reflow_result.pending_rasterization_images,
2651 reflow_result.pending_svg_elements_for_serialization,
2652 );
2653
2654 if let Some(iframe_sizes) = reflow_result.iframe_sizes {
2655 document
2656 .iframes_mut()
2657 .handle_new_iframe_sizes_after_layout(self, iframe_sizes);
2658 }
2659
2660 document.update_animations_post_reflow();
2661
2662 (
2663 reflow_result.reflow_phases_run,
2664 reflow_result.reflow_statistics,
2665 )
2666 }
2667
2668 pub(crate) fn request_screenshot_readiness(&self, cx: &mut JSContext) {
2669 self.has_pending_screenshot_readiness_request.set(true);
2670 self.maybe_resolve_pending_screenshot_readiness_requests(cx);
2671 }
2672
2673 pub(crate) fn maybe_resolve_pending_screenshot_readiness_requests(&self, cx: &mut JSContext) {
2674 let pending_request = self.has_pending_screenshot_readiness_request.get();
2675 if !pending_request {
2676 return;
2677 }
2678
2679 let document = self.Document();
2680 if document.ReadyState() != DocumentReadyState::Complete {
2681 return;
2682 }
2683
2684 if document.render_blocking_element_count() > 0 {
2685 return;
2686 }
2687
2688 if document.GetDocumentElement().is_some_and(|elem| {
2692 elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
2693 elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
2694 }) {
2695 return;
2696 }
2697
2698 if self.font_context().web_fonts_still_loading() != 0 {
2699 return;
2700 }
2701
2702 if self.Document().Fonts(cx).waiting_to_fullfill_promise() {
2703 return;
2704 }
2705
2706 if !self.pending_layout_images.borrow().is_empty() ||
2707 !self.pending_images_for_rasterization.borrow().is_empty()
2708 {
2709 return;
2710 }
2711
2712 let document = self.Document();
2713 if document.needs_rendering_update() {
2714 return;
2715 }
2716
2717 let epoch = document.current_rendering_epoch();
2720 let pipeline_id = self.pipeline_id();
2721 debug!("Ready to take screenshot of {pipeline_id:?} at epoch={epoch:?}");
2722
2723 self.send_to_constellation(
2724 ScriptToConstellationMessage::RespondToScreenshotReadinessRequest(
2725 ScreenshotReadinessResponse::Ready(epoch),
2726 ),
2727 );
2728 self.has_pending_screenshot_readiness_request.set(false);
2729 }
2730
2731 pub(crate) fn reflow_if_reflow_timer_expired(&self, cx: &mut JSContext) {
2734 if !matches!(
2737 self.layout_blocker.get(),
2738 LayoutBlocker::Parsing(instant) if instant + INITIAL_REFLOW_DELAY < Instant::now()
2739 ) {
2740 return;
2741 }
2742 self.allow_layout_if_necessary(cx);
2743 }
2744
2745 pub(crate) fn prevent_layout_until_load_event(&self) {
2749 if !matches!(self.layout_blocker.get(), LayoutBlocker::WaitingForParse) {
2752 return;
2753 }
2754
2755 self.layout_blocker
2756 .set(LayoutBlocker::Parsing(Instant::now()));
2757 }
2758
2759 pub(crate) fn allow_layout_if_necessary(&self, cx: &mut JSContext) {
2762 if matches!(
2763 self.layout_blocker.get(),
2764 LayoutBlocker::FiredLoadEventOrParsingTimerExpired
2765 ) {
2766 return;
2767 }
2768
2769 self.layout_blocker
2770 .set(LayoutBlocker::FiredLoadEventOrParsingTimerExpired);
2771
2772 let document = self.Document();
2784 if !document.is_render_blocked() && document.update_the_rendering(cx).0.needs_frame() {
2785 self.paint_api()
2786 .generate_frame(vec![self.webview_id().into()]);
2787 }
2788 }
2789
2790 pub(crate) fn layout_blocked(&self) -> bool {
2791 self.layout_blocker.get().layout_blocked()
2792 }
2793
2794 #[expect(unsafe_code)]
2796 pub(crate) fn layout_reflow(&self, query_msg: QueryMsg) {
2797 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
2799
2800 self.reflow(&mut cx, ReflowGoal::LayoutQuery(query_msg));
2801 }
2802
2803 pub(crate) fn reflow_for_non_flushing_update_the_rendering_queries(&self, cx: &mut JSContext) {
2805 self.reflow(
2806 cx,
2807 ReflowGoal::LayoutQuery(QueryMsg::FlushForUpdateTheRenderingQuery),
2808 );
2809 }
2810
2811 pub(crate) fn resolved_font_style_query(
2812 &self,
2813 node: &Node,
2814 value: String,
2815 ) -> Option<ServoArc<Font>> {
2816 self.layout_reflow(QueryMsg::ResolvedFontStyleQuery);
2817
2818 let document = self.Document();
2819 let animations = document.animations().sets.clone();
2820 self.layout.borrow().query_resolved_font_style(
2821 node.to_trusted_node_address(),
2822 &value,
2823 animations,
2824 document.current_animation_timeline_value(),
2825 )
2826 }
2827
2828 #[expect(unsafe_code)]
2831 pub(crate) fn containing_block_node_query_without_reflow(
2832 &self,
2833 node: &Node,
2834 ) -> Option<DomRoot<Node>> {
2835 self.layout
2836 .borrow()
2837 .query_containing_block(node.to_trusted_node_address())
2838 .map(|address| unsafe { from_untrusted_node_address(address) })
2839 }
2840
2841 pub(crate) fn is_containing_block_descendant_query_without_reflow(
2844 &self,
2845 possible_ancestor: &Node,
2846 possible_descendant: &Node,
2847 ) -> bool {
2848 self.layout.borrow().query_containing_block_is_descendant(
2849 possible_ancestor.to_trusted_node_address(),
2850 possible_descendant.to_trusted_node_address(),
2851 )
2852 }
2853
2854 pub(crate) fn padding_query_without_reflow(&self, node: &Node) -> Option<PhysicalSides> {
2859 let layout = self.layout.borrow();
2860 layout.query_padding(node.to_trusted_node_address())
2861 }
2862
2863 pub(crate) fn box_area_query_without_reflow(
2868 &self,
2869 node: &Node,
2870 area: BoxAreaType,
2871 exclude_transform_and_inline: bool,
2872 ) -> Option<Rect<Au, CSSPixel>> {
2873 let layout = self.layout.borrow();
2874 layout.ensure_stacking_context_tree(self.viewport_details.get());
2875 layout.query_box_area(
2876 node.to_trusted_node_address(),
2877 area,
2878 exclude_transform_and_inline,
2879 )
2880 }
2881
2882 pub(crate) fn box_area_query(
2883 &self,
2884 node: &Node,
2885 area: BoxAreaType,
2886 exclude_transform_and_inline: bool,
2887 ) -> Option<Rect<Au, CSSPixel>> {
2888 self.layout_reflow(QueryMsg::BoxArea);
2889 self.box_area_query_without_reflow(node, area, exclude_transform_and_inline)
2890 }
2891
2892 pub(crate) fn box_areas_query(&self, node: &Node, area: BoxAreaType) -> CSSPixelRectVec {
2893 self.layout_reflow(QueryMsg::BoxAreas);
2894 self.layout
2895 .borrow()
2896 .query_box_areas(node.to_trusted_node_address(), area)
2897 }
2898
2899 pub(crate) fn client_rect_query(&self, node: &Node) -> Rect<i32, CSSPixel> {
2900 self.layout_reflow(QueryMsg::ClientRectQuery);
2901 self.layout
2902 .borrow()
2903 .query_client_rect(node.to_trusted_node_address())
2904 }
2905
2906 pub(crate) fn current_css_zoom_query(&self, node: &Node) -> f32 {
2907 self.layout_reflow(QueryMsg::CurrentCSSZoomQuery);
2908 self.layout
2909 .borrow()
2910 .query_current_css_zoom(node.to_trusted_node_address())
2911 }
2912
2913 pub(crate) fn scrolling_area_query(&self, node: Option<&Node>) -> Rect<i32, CSSPixel> {
2916 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2917 self.layout
2918 .borrow()
2919 .query_scrolling_area(node.map(Node::to_trusted_node_address))
2920 }
2921
2922 pub(crate) fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32, LayoutPixel> {
2923 let external_scroll_id = ExternalScrollId(
2924 combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody),
2925 self.pipeline_id().into(),
2926 );
2927 self.scroll_offset_query_with_external_scroll_id(external_scroll_id)
2928 }
2929
2930 fn scroll_offset_query_with_external_scroll_id(
2931 &self,
2932 external_scroll_id: ExternalScrollId,
2933 ) -> Vector2D<f32, LayoutPixel> {
2934 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2935 self.scroll_offset_query_with_external_scroll_id_no_reflow(external_scroll_id)
2936 }
2937
2938 fn scroll_offset_query_with_external_scroll_id_no_reflow(
2939 &self,
2940 external_scroll_id: ExternalScrollId,
2941 ) -> Vector2D<f32, LayoutPixel> {
2942 self.layout
2943 .borrow()
2944 .scroll_offset(external_scroll_id)
2945 .unwrap_or_default()
2946 }
2947
2948 pub(crate) fn scroll_an_element(
2951 &self,
2952 cx: &mut JSContext,
2953 element: &Element,
2954 x: f32,
2955 y: f32,
2956 behavior: ScrollBehavior,
2957 ) {
2958 let scroll_id = ExternalScrollId(
2959 combine_id_with_fragment_type(
2960 element.upcast::<Node>().to_opaque().id(),
2961 FragmentType::FragmentBody,
2962 ),
2963 self.pipeline_id().into(),
2964 );
2965
2966 self.perform_a_scroll(cx, x, y, scroll_id, behavior, Some(element));
2970 }
2971
2972 pub(crate) fn resolved_style_query(
2973 &self,
2974 element: TrustedNodeAddress,
2975 pseudo: Option<PseudoElement>,
2976 property: PropertyId,
2977 ) -> DOMString {
2978 self.layout_reflow(QueryMsg::ResolvedStyleQuery(property.clone()));
2979
2980 let document = self.Document();
2981 let animations = document.animations().sets.clone();
2982 DOMString::from(self.layout.borrow().query_resolved_style(
2983 element,
2984 pseudo,
2985 property,
2986 animations,
2987 document.current_animation_timeline_value(),
2988 ))
2989 }
2990
2991 pub(crate) fn get_iframe_viewport_details_if_known(
2995 &self,
2996 browsing_context_id: BrowsingContextId,
2997 ) -> Option<ViewportDetails> {
2998 self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery);
3000 self.Document()
3001 .iframes()
3002 .get(browsing_context_id)
3003 .and_then(|iframe| iframe.size)
3004 }
3005
3006 #[expect(unsafe_code)]
3007 pub(crate) fn offset_parent_query(
3008 &self,
3009 node: &Node,
3010 ) -> (Option<DomRoot<Element>>, Rect<Au, CSSPixel>) {
3011 self.layout_reflow(QueryMsg::OffsetParentQuery);
3012 let response = self
3013 .layout
3014 .borrow()
3015 .query_offset_parent(node.to_trusted_node_address());
3016 let element = response.node_address.and_then(|parent_node_address| {
3017 let node = unsafe { from_untrusted_node_address(parent_node_address) };
3018 DomRoot::downcast(node)
3019 });
3020 (element, response.rect)
3021 }
3022
3023 pub(crate) fn scroll_container_query(
3024 &self,
3025 node: Option<&Node>,
3026 flags: ScrollContainerQueryFlags,
3027 ) -> Option<ScrollContainerResponse> {
3028 self.layout_reflow(QueryMsg::ScrollParentQuery);
3029 self.layout
3030 .borrow()
3031 .query_scroll_container(node.map(Node::to_trusted_node_address), flags)
3032 }
3033
3034 #[expect(unsafe_code)]
3035 pub(crate) fn scrolling_box_query(
3036 &self,
3037 node: Option<&Node>,
3038 flags: ScrollContainerQueryFlags,
3039 ) -> Option<ScrollingBox> {
3040 self.scroll_container_query(node, flags)
3041 .and_then(|response| {
3042 Some(match response {
3043 ScrollContainerResponse::Viewport(overflow) => {
3044 (ScrollingBoxSource::Viewport(self.Document()), overflow)
3045 },
3046 ScrollContainerResponse::Element(parent_node_address, overflow) => {
3047 let node = unsafe { from_untrusted_node_address(parent_node_address) };
3048 (
3049 ScrollingBoxSource::Element(DomRoot::downcast(node)?),
3050 overflow,
3051 )
3052 },
3053 })
3054 })
3055 .map(|(source, overflow)| ScrollingBox::new(source, overflow))
3056 }
3057
3058 pub(crate) fn text_index_query_on_node_for_event(
3059 &self,
3060 node: &Node,
3061 mouse_event: &MouseEvent,
3062 ) -> Option<usize> {
3063 let point_in_viewport = mouse_event.point_in_viewport()?.map(Au::from_f32_px);
3067
3068 self.layout_reflow(QueryMsg::TextIndexQuery);
3069 self.layout
3070 .borrow()
3071 .query_text_index(node.to_trusted_node_address(), point_in_viewport)
3072 }
3073
3074 pub(crate) fn elements_from_point_query(
3075 &self,
3076 point: LayoutPoint,
3077 ) -> Vec<ElementsFromPointResult> {
3078 self.layout_reflow(QueryMsg::ElementsFromPoint);
3079 self.layout().query_elements_from_point(point)
3080 }
3081
3082 pub(crate) fn query_effective_overflow(&self, node: &Node) -> Option<AxesOverflow> {
3083 self.layout_reflow(QueryMsg::EffectiveOverflow);
3084 self.query_effective_overflow_without_reflow(node)
3085 }
3086
3087 pub(crate) fn query_effective_overflow_without_reflow(
3088 &self,
3089 node: &Node,
3090 ) -> Option<AxesOverflow> {
3091 self.layout
3092 .borrow()
3093 .query_effective_overflow(node.to_trusted_node_address())
3094 }
3095
3096 pub(crate) fn hit_test_from_input_event(
3097 &self,
3098 input_event: &ConstellationInputEvent,
3099 ) -> Option<HitTestResult> {
3100 self.hit_test_from_point_in_viewport(
3101 input_event.hit_test_result.as_ref()?.point_in_viewport,
3102 )
3103 }
3104
3105 #[expect(unsafe_code)]
3106 pub(crate) fn hit_test_from_point_in_viewport(
3107 &self,
3108 point_in_frame: Point2D<f32, CSSPixel>,
3109 ) -> Option<HitTestResult> {
3110 let result = self
3111 .elements_from_point_query(point_in_frame.cast_unit())
3112 .into_iter()
3113 .nth(0)?;
3114
3115 let point_relative_to_initial_containing_block =
3116 point_in_frame + self.scroll_offset().cast_unit();
3117
3118 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
3121 Some(HitTestResult {
3122 node: unsafe { from_untrusted_node_address(address) },
3123 cursor: result.cursor,
3124 point_in_node: result.point_in_target,
3125 point_in_frame,
3126 point_relative_to_initial_containing_block,
3127 })
3128 }
3129
3130 pub(crate) fn init_window_proxy(&self, window_proxy: &WindowProxy) {
3131 assert!(self.window_proxy.get().is_none());
3132 self.window_proxy.set(Some(window_proxy));
3133 }
3134
3135 pub(crate) fn init_document(&self, document: &Document) {
3136 assert!(self.document.get().is_none());
3137 assert!(document.window() == self);
3138 self.document.set(Some(document));
3139 }
3140
3141 pub(crate) fn load_data_for_document(
3142 &self,
3143 url: ServoUrl,
3144 pipeline_id: PipelineId,
3145 ) -> LoadData {
3146 let source_document = self.Document();
3147 let secure_context = if self.is_top_level() {
3148 None
3149 } else {
3150 Some(self.IsSecureContext())
3151 };
3152 LoadData::new(
3153 LoadOrigin::Script(self.origin().snapshot()),
3154 url,
3155 source_document.about_base_url(),
3156 Some(pipeline_id),
3157 Referrer::ReferrerUrl(source_document.url()),
3158 source_document.get_referrer_policy(),
3159 secure_context,
3160 Some(source_document.insecure_requests_policy()),
3161 source_document.has_trustworthy_ancestor_origin(),
3162 source_document.creation_sandboxing_flag_set_considering_parent_iframe(),
3163 )
3164 }
3165
3166 pub(crate) fn set_viewport_details(&self, viewport_details: ViewportDetails) {
3169 self.viewport_details.set(viewport_details);
3170 if !self.layout_mut().set_viewport_details(viewport_details) {
3171 return;
3172 }
3173 self.Document()
3174 .add_restyle_reason(RestyleReason::ViewportChanged);
3175 }
3176
3177 pub(crate) fn viewport_details(&self) -> ViewportDetails {
3178 self.viewport_details.get()
3179 }
3180
3181 pub(crate) fn get_or_init_visual_viewport(&self, can_gc: CanGc) -> DomRoot<VisualViewport> {
3182 self.visual_viewport.or_init(|| {
3183 VisualViewport::new_from_layout_viewport(self, self.viewport_details().size, can_gc)
3184 })
3185 }
3186
3187 pub(crate) fn maybe_update_visual_viewport(
3189 &self,
3190 pinch_zoom_infos: PinchZoomInfos,
3191 can_gc: CanGc,
3192 ) {
3193 if pinch_zoom_infos.rect == Rect::from_size(self.viewport_details().size) &&
3196 self.visual_viewport.get().is_none()
3197 {
3198 return;
3199 }
3200
3201 let visual_viewport = self.get_or_init_visual_viewport(can_gc);
3202 let changes = visual_viewport.update_from_pinch_zoom_infos(pinch_zoom_infos);
3203
3204 if changes.intersects(VisualViewportChanges::DimensionChanged) {
3205 self.has_changed_visual_viewport_dimension.set(true);
3206 }
3207 if changes.intersects(VisualViewportChanges::OffsetChanged) {
3208 visual_viewport.handle_scroll_event();
3209 }
3210 }
3211
3212 pub(crate) fn theme(&self) -> Theme {
3214 self.theme.get()
3215 }
3216
3217 pub(crate) fn set_theme(&self, new_theme: Theme) {
3219 self.theme.set(new_theme);
3220 if !self.layout_mut().set_theme(new_theme) {
3221 return;
3222 }
3223 self.Document()
3224 .add_restyle_reason(RestyleReason::ThemeChanged);
3225 self.pending_media_query_evaluation.set(true);
3228 }
3229
3230 pub(crate) fn take_pending_media_query_evaluation(&self) -> bool {
3233 self.pending_media_query_evaluation.replace(false)
3234 }
3235
3236 pub(crate) fn has_pending_media_query_evaluation(&self) -> bool {
3237 self.pending_media_query_evaluation.get()
3238 }
3239
3240 pub(crate) fn get_url(&self) -> ServoUrl {
3241 self.Document().url()
3242 }
3243
3244 pub(crate) fn windowproxy_handler(&self) -> &'static WindowProxyHandler {
3245 self.dom_static.windowproxy_handler
3246 }
3247
3248 pub(crate) fn add_resize_event(&self, event: ViewportDetails, event_type: WindowSizeType) {
3249 *self.unhandled_resize_event.borrow_mut() = Some((event, event_type))
3252 }
3253
3254 pub(crate) fn take_unhandled_resize_event(&self) -> Option<(ViewportDetails, WindowSizeType)> {
3255 self.unhandled_resize_event.borrow_mut().take()
3256 }
3257
3258 pub(crate) fn has_unhandled_resize_event(&self) -> bool {
3260 self.unhandled_resize_event.borrow().is_some()
3261 }
3262
3263 pub(crate) fn suspend(&self, cx: &mut JSContext) {
3264 self.as_global_scope().suspend();
3266
3267 if self.window_proxy().currently_active() == Some(self.global().pipeline_id()) {
3269 self.window_proxy().unset_currently_active(cx);
3270 }
3271
3272 self.gc(cx);
3277 }
3278
3279 pub(crate) fn resume(&self, cx: &mut JSContext) {
3280 self.as_global_scope().resume();
3282
3283 self.window_proxy().set_currently_active(cx, self);
3285
3286 self.Document().title_changed();
3289 }
3290
3291 pub(crate) fn need_emit_timeline_marker(&self, timeline_type: TimelineMarkerType) -> bool {
3292 let markers = self.devtools_markers.borrow();
3293 markers.contains(&timeline_type)
3294 }
3295
3296 pub(crate) fn emit_timeline_marker(&self, marker: TimelineMarker) {
3297 let sender = self.devtools_marker_sender.borrow();
3298 let sender = sender.as_ref().expect("There is no marker sender");
3299 sender.send(Some(marker)).unwrap();
3300 }
3301
3302 pub(crate) fn set_devtools_timeline_markers(
3303 &self,
3304 markers: Vec<TimelineMarkerType>,
3305 reply: GenericSender<Option<TimelineMarker>>,
3306 ) {
3307 *self.devtools_marker_sender.borrow_mut() = Some(reply);
3308 self.devtools_markers.borrow_mut().extend(markers);
3309 }
3310
3311 pub(crate) fn drop_devtools_timeline_markers(&self, markers: Vec<TimelineMarkerType>) {
3312 let mut devtools_markers = self.devtools_markers.borrow_mut();
3313 for marker in markers {
3314 devtools_markers.remove(&marker);
3315 }
3316 if devtools_markers.is_empty() {
3317 *self.devtools_marker_sender.borrow_mut() = None;
3318 }
3319 }
3320
3321 pub(crate) fn set_webdriver_script_chan(&self, chan: Option<GenericSender<WebDriverJSResult>>) {
3322 *self.webdriver_script_chan.borrow_mut() = chan;
3323 }
3324
3325 pub(crate) fn set_webdriver_load_status_sender(
3326 &self,
3327 sender: Option<GenericSender<WebDriverLoadStatus>>,
3328 ) {
3329 *self.webdriver_load_status_sender.borrow_mut() = sender;
3330 }
3331
3332 pub(crate) fn webdriver_load_status_sender(
3333 &self,
3334 ) -> Option<GenericSender<WebDriverLoadStatus>> {
3335 self.webdriver_load_status_sender.borrow().clone()
3336 }
3337
3338 pub(crate) fn is_alive(&self) -> bool {
3339 self.current_state.get() == WindowState::Alive
3340 }
3341
3342 pub(crate) fn is_top_level(&self) -> bool {
3344 self.parent_info.is_none()
3345 }
3346
3347 fn run_resize_steps_for_layout_viewport(&self, cx: &mut JSContext) -> bool {
3352 let Some((new_size, size_type)) = self.take_unhandled_resize_event() else {
3353 return false;
3354 };
3355
3356 if self.viewport_details() == new_size {
3357 return false;
3358 }
3359
3360 let mut realm = enter_auto_realm(cx, self);
3361 let cx = &mut realm.current_realm();
3362 debug!(
3363 "Resizing Window for pipeline {:?} from {:?} to {new_size:?}",
3364 self.pipeline_id(),
3365 self.viewport_details(),
3366 );
3367 self.set_viewport_details(new_size);
3368
3369 self.Document()
3373 .add_restyle_reason(RestyleReason::ViewportChanged);
3374
3375 if self.layout().device().used_viewport_size() {
3378 self.Document().dirty_all_nodes();
3379 }
3380
3381 if size_type == WindowSizeType::Resize {
3383 let uievent = UIEvent::new(
3384 cx,
3385 self,
3386 atom!("resize"),
3387 EventBubbles::DoesNotBubble,
3388 EventCancelable::NotCancelable,
3389 Some(self),
3390 0i32,
3391 0u32,
3392 );
3393 uievent.upcast::<Event>().fire(cx, self.upcast());
3394 }
3395
3396 true
3397 }
3398
3399 pub(crate) fn run_the_resize_steps(&self, cx: &mut JSContext) -> bool {
3404 let layout_viewport_resized = self.run_resize_steps_for_layout_viewport(cx);
3405
3406 if self.has_changed_visual_viewport_dimension.get() {
3407 let visual_viewport = self.get_or_init_visual_viewport(CanGc::from_cx(cx));
3408
3409 let uievent = UIEvent::new(
3410 cx,
3411 self,
3412 atom!("resize"),
3413 EventBubbles::DoesNotBubble,
3414 EventCancelable::NotCancelable,
3415 Some(self),
3416 0i32,
3417 0u32,
3418 );
3419 uievent.upcast::<Event>().fire(cx, visual_viewport.upcast());
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, cx: &mut JSContext) {
3430 let mut realm = enter_auto_realm(cx, self);
3431 let cx = &mut realm.current_realm();
3432 rooted_vec!(let mut mql_list);
3433
3434 self.media_query_lists.for_each(|mql| {
3435 if let MediaQueryListMatchState::Changed = mql.evaluate_changes() {
3436 mql_list.push(Dom::from_ref(&*mql));
3438 }
3439 });
3440 for mql in mql_list.iter() {
3442 let event = MediaQueryListEvent::new(
3443 cx,
3444 &mql.global(),
3445 atom!("change"),
3446 false,
3447 false,
3448 mql.Media(),
3449 mql.Matches(),
3450 );
3451 event
3452 .upcast::<Event>()
3453 .fire(cx, mql.upcast::<EventTarget>());
3454 }
3455 }
3456
3457 pub(crate) fn set_throttled(&self, throttled: bool) {
3459 self.throttled.set(throttled);
3460 if throttled {
3461 self.as_global_scope().slow_down_timers();
3462 } else {
3463 self.as_global_scope().speed_up_timers();
3464 }
3465 }
3466
3467 pub(crate) fn throttled(&self) -> bool {
3468 self.throttled.get()
3469 }
3470
3471 pub(crate) fn unminified_css_dir(&self) -> Option<String> {
3472 self.unminified_css_dir.borrow().clone()
3473 }
3474
3475 pub(crate) fn local_script_source(&self) -> &Option<String> {
3476 &self.local_script_source
3477 }
3478
3479 pub(crate) fn set_navigation_start(&self) {
3480 self.navigation_start.set(CrossProcessInstant::now());
3481 }
3482
3483 pub(crate) fn navigation_start(&self) -> CrossProcessInstant {
3484 self.navigation_start.get()
3485 }
3486
3487 pub(crate) fn set_last_activation_timestamp(&self, time: UserActivationTimestamp) {
3488 self.last_activation_timestamp.set(time);
3489 }
3490
3491 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
3492 self.as_global_scope()
3493 .script_to_embedder_chan()
3494 .send(msg)
3495 .unwrap();
3496 }
3497
3498 pub(crate) fn send_to_constellation(&self, msg: ScriptToConstellationMessage) {
3499 self.as_global_scope()
3500 .script_to_constellation_chan()
3501 .send(msg)
3502 .unwrap();
3503 }
3504
3505 #[cfg(feature = "webxr")]
3506 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3507 self.navigator
3508 .get()
3509 .as_ref()
3510 .and_then(|nav| nav.xr())
3511 .is_some_and(|xr| xr.pending_or_active_session())
3512 }
3513
3514 #[cfg(not(feature = "webxr"))]
3515 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3516 false
3517 }
3518
3519 #[expect(unsafe_code)]
3520 fn handle_pending_images_post_reflow(
3521 &self,
3522 cx: &mut JSContext,
3523 pending_images: Vec<PendingImage>,
3524 pending_rasterization_images: Vec<PendingRasterizationImage>,
3525 pending_svg_element_for_serialization: Vec<UntrustedNodeAddress>,
3526 ) {
3527 let pipeline_id = self.pipeline_id();
3528 let image_cache = self.image_cache();
3529 for image in pending_images {
3530 let id = image.id;
3531 let node = unsafe { from_untrusted_node_address(image.node) };
3532
3533 if let PendingImageState::Unrequested(ref url) = image.state {
3534 fetch_image_for_layout(
3535 url.clone(),
3536 &node,
3537 id,
3538 image.is_internal_request,
3539 image_cache.clone(),
3540 );
3541 }
3542
3543 let mut images = self.pending_layout_images.borrow_mut();
3544 if !images.contains_key(&id) {
3545 let trusted_node = Trusted::new(&*node);
3546 let sender = self.register_image_cache_listener(id, move |response, _| {
3547 trusted_node
3548 .root()
3549 .owner_window()
3550 .pending_layout_image_notification(response);
3551 });
3552
3553 image_cache.add_listener(ImageLoadListener::new(sender, pipeline_id, id));
3554 }
3555
3556 let nodes = images.entry(id).or_default();
3557 if !nodes.iter().any(|n| std::ptr::eq(&*(n.node), &*node)) {
3558 nodes.push(PendingLayoutImageAncillaryData {
3559 node: Dom::from_ref(&*node),
3560 destination: image.destination,
3561 });
3562 }
3563 }
3564
3565 for image in pending_rasterization_images {
3566 let node = unsafe { from_untrusted_node_address(image.node) };
3567
3568 let mut images = self.pending_images_for_rasterization.borrow_mut();
3569 if !images.contains_key(&(image.id, image.size)) {
3570 let image_cache_sender = self.image_cache_sender.clone();
3571 image_cache.add_rasterization_complete_listener(
3572 pipeline_id,
3573 image.id,
3574 image.size,
3575 Box::new(move |response| {
3576 let _ = image_cache_sender.send(response);
3577 }),
3578 );
3579 }
3580
3581 let nodes = images.entry((image.id, image.size)).or_default();
3582 if !nodes.iter().any(|n| std::ptr::eq(&**n, &*node)) {
3583 nodes.push(Dom::from_ref(&*node));
3584 }
3585 }
3586
3587 for node in pending_svg_element_for_serialization.into_iter() {
3588 let node = unsafe { from_untrusted_node_address(node) };
3589 let svg = node.downcast::<SVGSVGElement>().unwrap();
3590 svg.serialize_and_cache_subtree(cx);
3591 node.dirty(NodeDamage::Other);
3592 }
3593 }
3594
3595 pub(crate) fn has_sticky_activation(&self) -> bool {
3597 UserActivationTimestamp::TimeStamp(CrossProcessInstant::now()) >=
3599 self.last_activation_timestamp.get()
3600 }
3601
3602 pub(crate) fn has_transient_activation(&self) -> bool {
3604 let current_time = CrossProcessInstant::now();
3607 UserActivationTimestamp::TimeStamp(current_time) >= self.last_activation_timestamp.get() &&
3608 UserActivationTimestamp::TimeStamp(current_time) <
3609 self.last_activation_timestamp.get() +
3610 pref!(dom_transient_activation_duration_ms)
3611 }
3612
3613 pub(crate) fn consume_last_activation_timestamp(&self) {
3614 if self.last_activation_timestamp.get() != UserActivationTimestamp::PositiveInfinity {
3615 self.set_last_activation_timestamp(UserActivationTimestamp::NegativeInfinity);
3616 }
3617 }
3618
3619 pub(crate) fn consume_user_activation(&self) {
3621 if self.undiscarded_window_proxy().is_none() {
3624 return;
3625 }
3626
3627 let Some(top_level_document) = self.top_level_document_if_local() else {
3631 return;
3632 };
3633
3634 top_level_document
3642 .window()
3643 .consume_last_activation_timestamp();
3644 for document in SameOriginDescendantNavigablesIterator::new(top_level_document) {
3645 document.window().consume_last_activation_timestamp();
3646 }
3647 }
3648
3649 #[allow(clippy::too_many_arguments)]
3650 pub(crate) fn new(
3651 cx: &mut JSContext,
3652 webview_id: WebViewId,
3653 runtime: Rc<Runtime>,
3654 script_chan: Sender<MainThreadScriptMsg>,
3655 layout: Box<dyn Layout>,
3656 font_context: Arc<FontContext>,
3657 image_cache_sender: Sender<ImageCacheResponseMessage>,
3658 resource_threads: ResourceThreads,
3659 storage_threads: StorageThreads,
3660 #[cfg(feature = "bluetooth")] bluetooth_thread: GenericSender<BluetoothRequest>,
3661 mem_profiler_chan: MemProfilerChan,
3662 time_profiler_chan: TimeProfilerChan,
3663 devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
3664 script_to_constellation_sender: ScriptToConstellationSender,
3665 embedder_chan: ScriptToEmbedderChan,
3666 control_chan: GenericSender<ScriptThreadMessage>,
3667 pipeline_id: PipelineId,
3668 parent_info: Option<PipelineId>,
3669 viewport_details: ViewportDetails,
3670 origin: MutableOrigin,
3671 creation_url: ServoUrl,
3672 top_level_creation_url: ServoUrl,
3673 navigation_start: CrossProcessInstant,
3674 webgl_chan: Option<WebGLChan>,
3675 #[cfg(feature = "webxr")] webxr_registry: Option<webxr_api::Registry>,
3676 paint_api: CrossProcessPaintApi,
3677 unminify_js: bool,
3678 unminify_css: bool,
3679 local_script_source: Option<String>,
3680 user_scripts: Rc<Vec<UserScript>>,
3681 player_context: WindowGLContext,
3682 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
3683 inherited_secure_context: Option<bool>,
3684 theme: Theme,
3685 weak_script_thread: Weak<ScriptThread>,
3686 ) -> DomRoot<Self> {
3687 let error_reporter = CSSErrorReporter {
3688 pipelineid: pipeline_id,
3689 script_chan: control_chan,
3690 };
3691
3692 let win = Box::new(Self {
3693 webview_id,
3694 globalscope: GlobalScope::new_inherited(
3695 devtools_chan,
3696 mem_profiler_chan,
3697 time_profiler_chan,
3698 script_to_constellation_sender,
3699 embedder_chan,
3700 resource_threads,
3701 storage_threads,
3702 creation_url,
3703 Some(top_level_creation_url),
3704 #[cfg(feature = "webgpu")]
3705 gpu_id_hub,
3706 inherited_secure_context,
3707 unminify_js,
3708 Some(font_context),
3709 ),
3710 ongoing_navigation: Default::default(),
3711 script_chan,
3712 layout: RefCell::new(layout),
3713 image_cache_sender,
3714 navigator: Default::default(),
3715 crypto: Default::default(),
3716 location: 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 pending_media_query_evaluation: Default::default(),
3777 last_activation_timestamp: Cell::new(UserActivationTimestamp::PositiveInfinity),
3778 devtools_wants_updates: Default::default(),
3779 });
3780
3781 WindowBinding::Wrap::<crate::DomTypeHolder>(cx, &origin, win)
3782 }
3783
3784 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
3785 self.Document().task_manager()
3786 }
3787
3788 pub(crate) fn pipeline_id(&self) -> PipelineId {
3789 self.Document().pipeline_id()
3790 }
3791
3792 pub(crate) fn live_devtools_updates(&self) -> bool {
3793 self.devtools_wants_updates.get()
3794 }
3795
3796 pub(crate) fn set_devtools_wants_updates(&self, value: bool) {
3797 self.devtools_wants_updates.set(value);
3798 }
3799
3800 pub(crate) fn cache_layout_value<T>(&self, value: T) -> LayoutValue<T>
3802 where
3803 T: Copy + MallocSizeOf,
3804 {
3805 LayoutValue::new(self.layout_marker.borrow().clone(), value)
3806 }
3807}
3808
3809#[derive(MallocSizeOf)]
3814pub(crate) struct LayoutValue<T: MallocSizeOf> {
3815 #[conditional_malloc_size_of]
3816 is_valid: Rc<Cell<bool>>,
3817 value: T,
3818}
3819
3820#[expect(unsafe_code)]
3821unsafe impl<T: JSTraceable + MallocSizeOf> JSTraceable for LayoutValue<T> {
3822 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
3823 unsafe { self.value.trace(trc) };
3824 }
3825}
3826
3827impl<T: Copy + MallocSizeOf> LayoutValue<T> {
3828 fn new(marker: Rc<Cell<bool>>, value: T) -> Self {
3829 LayoutValue {
3830 is_valid: marker,
3831 value,
3832 }
3833 }
3834
3835 pub(crate) fn get(&self) -> Result<T, ()> {
3837 if self.is_valid.get() {
3838 return Ok(self.value);
3839 }
3840 Err(())
3841 }
3842}
3843
3844impl Window {
3845 pub(crate) fn post_message(
3847 &self,
3848 target_origin: Option<ImmutableOrigin>,
3849 source_origin: ImmutableOrigin,
3850 source: &WindowProxy,
3851 data: StructuredSerializedData,
3852 ) {
3853 let this = Trusted::new(self);
3854 let source = Trusted::new(source);
3855 let task = task!(post_serialised_message: move |cx| {
3856 let this = this.root();
3857 let source = source.root();
3858 let document = this.Document();
3859
3860 if let Some(ref target_origin) = target_origin
3862 && !target_origin.same_origin(&*document.origin()) {
3863 return;
3864 }
3865
3866 let obj = this.reflector().get_jsobject();
3868 let mut realm = AutoRealm::new(cx, NonNull::new(obj.get()).unwrap());
3869 let cx = &mut *realm;
3870 rooted!(&in(cx) let mut message_clone = UndefinedValue());
3871 if let Ok(ports) = structuredclone::read(cx, this.upcast(), data, message_clone.handle_mut()) {
3872 MessageEvent::dispatch_jsval(
3874 cx,
3875 this.upcast(),
3876 this.upcast(),
3877 message_clone.handle(),
3878 Some(&source_origin.ascii_serialization()),
3879 Some(&*source),
3880 ports,
3881 );
3882 } else {
3883 MessageEvent::dispatch_error(
3885 cx,
3886 this.upcast(),
3887 this.upcast(),
3888 );
3889 }
3890 });
3891 self.as_global_scope()
3893 .task_manager()
3894 .dom_manipulation_task_source()
3895 .queue(task);
3896 }
3897}
3898
3899#[derive(Clone, MallocSizeOf)]
3900pub(crate) struct CSSErrorReporter {
3901 pub(crate) pipelineid: PipelineId,
3902 pub(crate) script_chan: GenericSender<ScriptThreadMessage>,
3903}
3904unsafe_no_jsmanaged_fields!(CSSErrorReporter);
3905
3906impl ParseErrorReporter for CSSErrorReporter {
3907 fn report_error(
3908 &self,
3909 url: &UrlExtraData,
3910 location: SourceLocation,
3911 error: ContextualParseError,
3912 ) {
3913 if log_enabled!(log::Level::Info) {
3914 info!(
3915 "Url:\t{}\n{}:{} {}",
3916 url.0.as_str(),
3917 location.line,
3918 location.column,
3919 error
3920 )
3921 }
3922
3923 let _ = self.script_chan.send(ScriptThreadMessage::ReportCSSError(
3925 self.pipelineid,
3926 url.0.to_string(),
3927 location.line,
3928 location.column,
3929 error.to_string(),
3930 ));
3931 }
3932}
3933
3934fn is_named_element_with_name_attribute(elem: &Element) -> bool {
3935 let type_ = match elem.upcast::<Node>().type_id() {
3936 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
3937 _ => return false,
3938 };
3939 matches!(
3940 type_,
3941 HTMLElementTypeId::HTMLEmbedElement |
3942 HTMLElementTypeId::HTMLFormElement |
3943 HTMLElementTypeId::HTMLImageElement |
3944 HTMLElementTypeId::HTMLObjectElement
3945 )
3946}
3947
3948fn is_named_element_with_id_attribute(elem: &Element) -> bool {
3949 elem.is_html_element()
3950}
3951
3952#[expect(unsafe_code)]
3953#[unsafe(no_mangle)]
3954unsafe extern "C" fn dump_js_stack(cx: *mut RawJSContext) {
3956 unsafe {
3957 DumpJSStack(cx, true, false, false);
3958 }
3959}
3960
3961impl WindowHelpers for Window {
3962 fn create_named_properties_object(
3963 cx: &mut JSContext,
3964 proto: HandleObject,
3965 object: MutableHandleObject,
3966 ) {
3967 Self::create_named_properties_object(cx, proto, object)
3968 }
3969}
3970
3971impl HasOrigin for Window {
3972 fn origin(&self) -> MutableOrigin {
3973 Window::origin(self)
3974 }
3975}