1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::borrow::ToOwned;
8use std::cell::{Cell, RefCell, RefMut};
9use std::collections::hash_map::Entry;
10use std::default::Default;
11use std::ffi::c_void;
12use std::io::{Write, stderr, stdout};
13use std::ptr::NonNull;
14use std::rc::{Rc, Weak};
15use std::sync::Arc;
16use std::time::{Duration, Instant};
17
18use accesskit::Action;
19use app_units::Au;
20use base64::Engine;
21use content_security_policy::Violation;
22use content_security_policy::sandboxing_directive::SandboxingFlagSet;
23use crossbeam_channel::{Sender, unbounded};
24use cssparser::SourceLocation;
25use dom_struct::dom_struct;
26use embedder_traits::user_contents::UserScript;
27use embedder_traits::{
28 AlertResponse, ConfirmResponse, EmbedderMsg, LoadStatus, PromptResponse, ScriptToEmbedderChan,
29 SimpleDialogRequest, Theme, UntrustedNodeAddress, ViewportDetails, WebDriverLoadStatus,
30};
31use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
32use fonts::{
33 CspViolationHandler, FontContext, NetworkTimingHandler, WebFontDocumentContext,
34 WebFontSetDifference,
35};
36use html5ever::LocalName;
37use js::context::{JSContext, NoGC};
38use js::conversions::ToJSValConvertible;
39use js::glue::DumpJSStack;
40use js::jsapi::{
41 GCReason, GetObjectRealmOrNull, JSContext as RawJSContext, JSObject, SetRealmPrincipals,
42};
43use js::jsval::{NullValue, UndefinedValue};
44use js::realm::{AutoRealm, CurrentRealm};
45use js::rust::wrappers2::{JS_GC, JS_MaybeGC};
46use js::rust::{
47 CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleObject, MutableHandleValue,
48};
49use layout_api::{
50 AccessibilityActionRequest, AxesOverflow, BoxAreaType, CSSPixelRectVec, ContainerTimingRecord,
51 FragmentType, HitTestFlags, LCPCandidate, Layout, LayoutImageDestination, PendingImage,
52 PendingImageState, PendingRasterizationImage, PhysicalSides, QueryMsg, ReflowGoal,
53 ReflowPhasesRun, ReflowRequest, ReflowRequestAccessibility, ReflowRequestRestyle,
54 ReflowStatistics, RestyleReason, ScrollContainerQueryFlags, ScrollContainerResponse,
55 SegmentGranularity, TrustedNodeAddress, combine_id_with_fragment_type,
56};
57use malloc_size_of::MallocSizeOf;
58use media::WindowGLContext;
59use net_traits::image_cache::{
60 ImageCache, ImageCacheResponseCallback, ImageCacheResponseMessage, ImageLoadListener,
61 ImageResponse, PendingImageId, PendingImageResponse, RasterizationCompleteResponse,
62};
63use net_traits::request::{Origin, Referrer, RequestClient};
64use net_traits::{ResourceFetchTiming, ResourceThreads};
65use num_traits::ToPrimitive;
66use paint_api::{CrossProcessPaintApi, PinchZoomInfos};
67use profile_traits::generic_channel as ProfiledGenericChannel;
68use profile_traits::mem::ProfilerChan as MemProfilerChan;
69use profile_traits::time::ProfilerChan as TimeProfilerChan;
70use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
71use script_bindings::cell::{DomRefCell, Ref};
72use script_bindings::dom::UnrootedDom;
73use script_bindings::domstring::TracedDOMString;
74use script_bindings::interfaces::{HasOrigin, WindowHelpers};
75use script_bindings::js::{JS_DefineProperty, PropertyFlags};
76use script_bindings::like::Setlike;
77use script_bindings::principals::ServoJSPrincipals;
78use script_bindings::reflector::DomObject;
79use script_bindings::root::Root;
80use script_traits::{ConstellationInputEvent, ScriptThreadMessage, WebViewState};
81use selectors::attr::CaseSensitivity;
82use servo_arc::Arc as ServoArc;
83use servo_base::cross_process_instant::CrossProcessInstant;
84use servo_base::generic_channel::{self, GenericCallback, GenericSender};
85use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
86use servo_base::text::{Utf32CodeUnits, Utf32CodeUnitsOrNodeOffset};
87#[cfg(feature = "bluetooth")]
88use servo_bluetooth_traits::BluetoothRequest;
89#[cfg(feature = "webgl")]
90use servo_canvas_traits::webgl::WebGLChan;
91use servo_config::pref;
92use servo_constellation_traits::{
93 LoadData, LoadOrigin, ScreenshotReadinessResponse, ScriptToConstellationMessage,
94 ScriptToConstellationSender, StructuredSerializedData, WindowSizeType,
95};
96use servo_geometry::DeviceIndependentIntRect;
97use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
98use storage_traits::StorageThreads;
99use storage_traits::webstorage_thread::WebStorageType;
100use style::dom::OpaqueNode;
101use style::error_reporting::{ContextualParseError, ParseErrorReporter};
102use style::properties::PropertyId;
103use style::properties::style_structs::Font;
104use style::selector_parser::PseudoElement;
105use style::str::HTML_SPACE_CHARACTERS;
106use style::stylesheets::UrlExtraData;
107use style_traits::CSSPixel;
108use stylo_atoms::Atom;
109use time::Duration as TimeDuration;
110use webrender_api::ExternalScrollId;
111use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint};
112
113use crate::dom::StatelessWorkletThreadPool;
114use crate::dom::bindings::callback::RootedCallback;
115use crate::dom::bindings::codegen::Bindings::AnimationFrameProviderBinding::FrameRequestCallback;
116use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
117 DocumentMethods, DocumentReadyState, NamedPropertyValue,
118};
119use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
120use crate::dom::bindings::codegen::Bindings::HistoryBinding::History_Binding::HistoryMethods;
121use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
122 ImageBitmapOptions, ImageBitmapSource,
123};
124use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryList_Binding::MediaQueryListMethods;
125use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
126use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
127use crate::dom::bindings::codegen::Bindings::RequestBinding::{RequestInfo, RequestInit};
128use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
129use crate::dom::bindings::codegen::Bindings::WindowBinding::{
130 self, DeferredRequestInit, ScrollBehavior, ScrollToOptions, WindowMethods,
131 WindowPostMessageOptions,
132};
133use crate::dom::bindings::codegen::UnionTypes::{
134 RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
135};
136use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
137use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
138use crate::dom::bindings::num::Finite;
139use crate::dom::bindings::refcounted::Trusted;
140use crate::dom::bindings::reflector::DomGlobal;
141use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
142use crate::dom::bindings::str::{DOMString, USVString};
143use crate::dom::bindings::structuredclone;
144use crate::dom::bindings::trace::{CustomTraceable, HashMapTracedValues, JSTraceable};
145use crate::dom::bindings::utils::GlobalStaticData;
146use crate::dom::bindings::weakref::DOMTracker;
147#[cfg(feature = "bluetooth")]
148use crate::dom::bluetooth::BluetoothExtraPermissionData;
149use crate::dom::closewatchermanager::CloseWatcherManager;
150use crate::dom::cookiestore::cookiestore::CookieStore;
151use crate::dom::csp::GlobalCspReporting;
152use crate::dom::css::cssstyledeclaration::{
153 CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner,
154};
155use crate::dom::customelementregistry::CustomElementRegistry;
156use crate::dom::document::focus::FocusableArea;
157use crate::dom::document::{
158 AbortReason, AnimationFrameCallback, Document, SameOriginDescendantNavigablesIterator,
159};
160use crate::dom::element::Element;
161use crate::dom::event::{Event, EventBubbles, EventCancelable};
162use crate::dom::eventtarget::EventTarget;
163use crate::dom::fetchlaterresult::FetchLaterResult;
164use crate::dom::geolocation::Geolocation;
165use crate::dom::globalscope::GlobalScope;
166use crate::dom::history::History;
167use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
168use crate::dom::html::htmliframeelement::HTMLIFrameElement;
169use crate::dom::idbfactory::IDBFactory;
170use crate::dom::inputevent::HitTestResult;
171use crate::dom::location::Location;
172use crate::dom::medialist::MediaList;
173use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
174use crate::dom::mediaquerylistevent::MediaQueryListEvent;
175use crate::dom::messageevent::MessageEvent;
176use crate::dom::navigator::Navigator;
177use crate::dom::node::focus::FocusTrigger;
178use crate::dom::node::{Node, NodeDamage, NodeTraits, from_untrusted_node_address};
179use crate::dom::performance::performance::Performance;
180use crate::dom::performanceresourcetiming::InitiatorType;
181use crate::dom::promise::RootedPromise;
182use crate::dom::reporting::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
183use crate::dom::reporting::reportingobserver::ReportingObserver;
184use crate::dom::selection::Selection;
185use crate::dom::selection_range::RootedSelectionBoundary;
186use crate::dom::serviceworker::cachestorage::CacheStorage;
187use crate::dom::shadowroot::shadowroot::ShadowRoot;
188use crate::dom::storage::Storage;
189#[cfg(feature = "bluetooth")]
190use crate::dom::testrunner::TestRunner;
191use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
192use crate::dom::types::{FontFace, ImageBitmap, SVGSVGElement, UIEvent};
193use crate::dom::visualviewport::{VisualViewport, VisualViewportChanges};
194#[cfg(feature = "webgpu")]
195use crate::dom::webgpu::identityhub::IdentityHub;
196use crate::dom::window::layout_image::fetch_image_for_layout;
197use crate::dom::window::screen::Screen;
198use crate::dom::window::scrolling_box::{ScrollingBox, ScrollingBoxSource};
199use crate::dom::window::useractivation::UserActivationTimestamp;
200use crate::dom::windowproxy::{WindowProxy, WindowProxyHandler};
201use crate::dom::worklet::Worklet;
202use crate::dom::workletglobalscope::WorkletGlobalScopeType;
203use crate::event_loop::script_thread::ScriptThread;
204use crate::event_loop::script_window_proxies::ScriptWindowProxies;
205use crate::event_loop::timers::{IsInterval, OneshotTimers, TimerCallback};
206use crate::event_loop::webdriver_handlers::find_node_by_unique_id_in_document;
207use crate::fetch::fetch;
208use crate::fetch::network_listener::{ResourceTimingListener, submit_timing};
209use crate::messaging::{MainThreadScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
210use crate::realms::enter_auto_realm;
211use crate::runtime::job_queue::UserMicrotask;
212use crate::runtime::script_runtime::Runtime;
213use crate::tasks::task_manager::TaskManager;
214use crate::tasks::task_source::SendableTaskSource;
215use crate::unminify::unminified_path;
216use crate::window_named_properties;
217
218#[derive(MallocSizeOf)]
223pub struct PendingImageCallback(
224 #[ignore_malloc_size_of = "dyn Fn is currently impossible to measure"]
225 #[expect(clippy::type_complexity)]
226 Box<dyn Fn(PendingImageResponse, &mut JSContext) + 'static>,
227);
228
229#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
231enum WindowState {
232 Alive,
233 Zombie, }
235
236const INITIAL_REFLOW_DELAY: Duration = Duration::from_millis(200);
239
240#[derive(Clone, Copy, MallocSizeOf)]
252enum LayoutBlocker {
253 WaitingForParse,
255 Parsing(Instant),
257 NotBlocked,
267}
268
269impl LayoutBlocker {
270 fn layout_blocked(&self) -> bool {
271 !matches!(self, Self::NotBlocked)
272 }
273}
274
275#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf, PartialEq)]
278pub(crate) struct OngoingNavigation(u32);
279
280type PendingImageRasterizationKey = (PendingImageId, DeviceIntSize);
281
282#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
286#[derive(JSTraceable, MallocSizeOf)]
287struct PendingLayoutImageAncillaryData {
288 node: Dom<Node>,
289 #[no_trace]
290 destination: LayoutImageDestination,
291}
292
293#[dom_struct]
294pub(crate) struct Window {
295 globalscope: GlobalScope,
296 #[ignore_malloc_size_of = "Weak does not need to be accounted"]
300 #[no_trace]
301 weak_script_thread: Weak<ScriptThread>,
302 #[no_trace]
305 #[conditional_malloc_size_of]
306 webview_state: Rc<WebViewState>,
307 script_chan: Sender<MainThreadScriptMsg>,
308 #[no_trace]
309 #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"]
310 layout: RefCell<Box<dyn Layout>>,
311 navigator: MutNullableDom<Navigator>,
312 crypto: MutNullableDom<crate::dom::crypto::Crypto>,
313 #[no_trace]
314 image_cache_sender: Sender<ImageCacheResponseMessage>,
315 window_proxy: MutNullableDom<WindowProxy>,
316 document: MutNullableDom<Document>,
317 location: MutNullableDom<Location>,
318 performance: MutNullableDom<Performance>,
319 #[no_trace]
320 navigation_start: Cell<CrossProcessInstant>,
321 screen: MutNullableDom<Screen>,
322 session_storage: MutNullableDom<Storage>,
323 local_storage: MutNullableDom<Storage>,
324 cookie_store: MutNullableDom<CookieStore>,
326 status: DomRefCell<TracedDOMString>,
327 trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
328
329 ongoing_navigation: Cell<OngoingNavigation>,
332
333 caches: MutNullableDom<CacheStorage>,
335
336 #[no_trace]
339 #[cfg(feature = "devtools")]
340 devtools_markers: DomRefCell<FxHashSet<devtools_traits::TimelineMarkerType>>,
341 #[no_trace]
342 #[cfg(feature = "devtools")]
343 devtools_marker_sender:
344 DomRefCell<Option<GenericSender<Option<devtools_traits::TimelineMarker>>>>,
345
346 #[no_trace]
348 unhandled_resize_event: DomRefCell<Option<(ViewportDetails, WindowSizeType)>>,
349
350 #[no_trace]
354 viewport_details_at_last_resize_steps: Cell<ViewportDetails>,
355
356 #[no_trace]
358 parent_info: Option<PipelineId>,
359
360 dom_static: GlobalStaticData,
362
363 #[conditional_malloc_size_of]
365 js_runtime: DomRefCell<Option<Rc<Runtime>>>,
366
367 #[no_trace]
369 viewport_details: Cell<ViewportDetails>,
370
371 #[no_trace]
373 #[cfg(feature = "bluetooth")]
374 bluetooth_thread: GenericSender<BluetoothRequest>,
375
376 #[cfg(feature = "bluetooth")]
377 bluetooth_extra_permission_data: BluetoothExtraPermissionData,
378
379 #[no_trace]
383 layout_blocker: Cell<LayoutBlocker>,
384
385 #[no_trace]
387 webdriver_load_status_sender: RefCell<Option<GenericSender<WebDriverLoadStatus>>>,
388
389 current_state: Cell<WindowState>,
391
392 error_reporter: CSSErrorReporter,
393
394 media_query_lists: DOMTracker<MediaQueryList>,
396
397 #[cfg(feature = "bluetooth")]
398 test_runner: MutNullableDom<TestRunner>,
399
400 #[no_trace]
402 #[cfg(feature = "webgl")]
403 webgl_chan: Option<WebGLChan>,
404
405 #[ignore_malloc_size_of = "defined in webxr"]
406 #[no_trace]
407 #[cfg(feature = "webxr")]
408 webxr_registry: Option<webxr_api::Registry>,
409
410 #[no_trace]
414 pending_image_callbacks: DomRefCell<FxHashMap<PendingImageId, Vec<PendingImageCallback>>>,
415
416 pending_layout_images: DomRefCell<
421 HashMapTracedValues<PendingImageId, Vec<PendingLayoutImageAncillaryData>, FxBuildHasher>,
422 >,
423
424 pending_images_for_rasterization: DomRefCell<
428 HashMapTracedValues<PendingImageRasterizationKey, Vec<Dom<Node>>, FxBuildHasher>,
429 >,
430
431 unminified_css_dir: DomRefCell<Option<String>>,
434
435 local_script_source: Option<String>,
437
438 test_worklet: MutNullableDom<Worklet>,
440 paint_worklet: MutNullableDom<Worklet>,
442
443 exists_mut_observer: Cell<bool>,
445
446 #[no_trace]
448 paint_api: CrossProcessPaintApi,
449
450 #[no_trace]
453 #[conditional_malloc_size_of]
454 user_scripts: Rc<Vec<UserScript>>,
455
456 #[no_trace]
458 player_context: WindowGLContext,
459
460 throttled: Cell<bool>,
464
465 #[conditional_malloc_size_of]
469 layout_marker: DomRefCell<Rc<Cell<bool>>>,
470
471 current_event: MutNullableDom<Event>,
473
474 reporting_observer_list: DomRefCell<Vec<Dom<ReportingObserver>>>,
476
477 report_list: DomRefCell<Vec<Report>>,
479
480 #[no_trace]
482 endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
483
484 #[conditional_malloc_size_of]
486 script_window_proxies: Rc<ScriptWindowProxies>,
487
488 has_pending_screenshot_readiness_request: Cell<bool>,
490
491 visual_viewport: MutNullableDom<VisualViewport>,
494
495 has_changed_visual_viewport_dimension: Cell<bool>,
497
498 pending_media_query_evaluation: Cell<bool>,
503
504 #[no_trace]
506 last_activation_timestamp: Cell<UserActivationTimestamp>,
507
508 devtools_wants_updates: Cell<bool>,
511
512 has_dispatched_scroll_event: Cell<bool>,
514
515 has_dispatched_input_event: Cell<bool>,
517
518 #[no_trace]
520 last_history_action_activation_timestamp: Cell<UserActivationTimestamp>,
521
522 close_watcher_manager: CloseWatcherManager,
524}
525
526impl Window {
527 pub(crate) fn script_thread(&self) -> Rc<ScriptThread> {
528 Weak::upgrade(&self.weak_script_thread)
529 .expect("Weak reference should always be upgradable when a ScriptThread is running")
530 }
531
532 pub(crate) fn webview_id(&self) -> WebViewId {
533 self.webview_state.id
534 }
535
536 pub(crate) fn webview_state(&self) -> Rc<WebViewState> {
537 self.webview_state.clone()
538 }
539
540 pub(crate) fn as_global_scope(&self) -> &GlobalScope {
541 self.upcast::<GlobalScope>()
542 }
543
544 pub(crate) fn mark_has_dispatched_scroll_event(&self) {
546 self.has_dispatched_scroll_event.set(true);
547 }
548
549 pub(crate) fn mark_has_dispatched_input_event(&self) {
551 self.has_dispatched_input_event.set(true);
552 }
553
554 pub(crate) fn layout(&self) -> Ref<'_, Box<dyn Layout>> {
555 self.layout.borrow()
556 }
557
558 pub(crate) fn layout_mut(&self) -> RefMut<'_, Box<dyn Layout>> {
559 self.layout.borrow_mut()
560 }
561
562 pub(crate) fn get_exists_mut_observer(&self) -> bool {
563 self.exists_mut_observer.get()
564 }
565
566 pub(crate) fn set_exists_mut_observer(&self) {
567 self.exists_mut_observer.set(true);
568 }
569
570 fn clear_js_runtime_common(&self) {
571 self.stop_geolocation_watches();
572 self.as_global_scope()
573 .remove_web_messaging_and_dedicated_workers_infra();
574 self.current_state.set(WindowState::Zombie);
575 self.as_global_scope()
576 .task_manager()
577 .cancel_all_tasks_and_ignore_future_tasks();
578 }
579
580 #[expect(unsafe_code)]
581 pub(crate) fn clear_js_runtime_for_script_deallocation(&self) {
582 self.clear_js_runtime_common();
583 unsafe {
584 *self.js_runtime.borrow_for_script_deallocation() = None;
585 }
586 self.window_proxy.set(None);
587 }
588
589 fn stop_geolocation_watches(&self) {
592 if let Some(geolocation) = self.maybe_geolocation() {
593 geolocation.stop_all_watches();
594 }
595 }
596
597 pub(crate) fn maybe_geolocation(&self) -> Option<DomRoot<Geolocation>> {
599 self.navigator.get()?.maybe_geolocation()
600 }
601
602 pub(crate) fn discard_browsing_context(&self) {
605 let proxy = self
606 .window_proxy
607 .get()
608 .expect("Discarding a BC from a window that has none");
609 proxy.discard_browsing_context();
610
611 self.as_global_scope()
615 .task_manager()
616 .cancel_all_tasks_and_ignore_future_tasks();
617 }
618
619 pub(crate) fn time_profiler_chan(&self) -> &TimeProfilerChan {
621 self.globalscope.time_profiler_chan()
622 }
623
624 pub(crate) fn origin(&self) -> MutableOrigin {
626 self.Document().origin().clone()
628 }
629
630 pub(crate) fn main_thread_script_chan(&self) -> &Sender<MainThreadScriptMsg> {
631 &self.script_chan
632 }
633
634 pub(crate) fn parent_info(&self) -> Option<PipelineId> {
635 self.parent_info
636 }
637
638 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
639 let (sender, receiver) = unbounded();
640 (
641 ScriptEventLoopSender::MainThread(sender),
642 ScriptEventLoopReceiver::MainThread(receiver),
643 )
644 }
645
646 pub(crate) fn event_loop_sender(&self) -> ScriptEventLoopSender {
647 ScriptEventLoopSender::MainThread(self.script_chan.clone())
648 }
649
650 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
651 self.Document().image_cache()
652 }
653
654 pub(crate) fn window_proxy(&self) -> DomRoot<WindowProxy> {
656 self.window_proxy.get().unwrap()
657 }
658
659 pub(crate) fn append_reporting_observer(&self, reporting_observer: &ReportingObserver) {
660 self.reporting_observer_list
661 .borrow_mut()
662 .push(Dom::from_ref(reporting_observer));
663 }
664
665 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
666 let index = {
667 let list = self.reporting_observer_list.borrow();
668 list.iter()
669 .position(|observer| &**observer == reporting_observer)
670 };
671
672 if let Some(index) = index {
673 self.reporting_observer_list.borrow_mut().remove(index);
674 }
675 }
676
677 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
678 self.reporting_observer_list
679 .borrow()
680 .iter()
681 .map(|observer| DomRoot::from_ref(&**observer))
682 .collect()
683 }
684
685 pub(crate) fn append_report(&self, report: Report) {
686 self.report_list.borrow_mut().push(report);
687 let trusted_window = Trusted::new(self);
688 self.upcast::<GlobalScope>()
689 .task_manager()
690 .dom_manipulation_task_source()
691 .queue(task!(send_to_reporting_endpoints: move || {
692 let window = trusted_window.root();
693 let reports = std::mem::take(&mut *window.report_list.borrow_mut());
694 window.upcast::<GlobalScope>().send_reports_to_endpoints(
695 reports,
696 window.endpoints_list.borrow().clone(),
697 );
698 }));
699 }
700
701 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
702 self.report_list.borrow().clone()
703 }
704
705 pub(crate) fn set_endpoints_list(&self, endpoints: Vec<ReportingEndpoint>) {
706 *self.endpoints_list.borrow_mut() = endpoints;
707 }
708
709 pub(crate) fn undiscarded_window_proxy(&self) -> Option<DomRoot<WindowProxy>> {
712 self.window_proxy
713 .get()
714 .filter(|window_proxy| !window_proxy.is_browsing_context_discarded())
715 }
716
717 pub(crate) fn top_level_document_if_local(&self) -> Option<DomRoot<Document>> {
722 if self.is_top_level() {
723 return Some(self.Document());
724 }
725
726 let window_proxy = self.undiscarded_window_proxy()?;
727 self.script_window_proxies
728 .find_window_proxy(window_proxy.webview_id().into())?
729 .document()
730 }
731
732 #[cfg(feature = "bluetooth")]
733 pub(crate) fn bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
734 self.bluetooth_thread.clone()
735 }
736
737 #[cfg(feature = "bluetooth")]
738 pub(crate) fn bluetooth_extra_permission_data(&self) -> &BluetoothExtraPermissionData {
739 &self.bluetooth_extra_permission_data
740 }
741
742 pub(crate) fn css_error_reporter(&self) -> &CSSErrorReporter {
743 &self.error_reporter
744 }
745
746 #[cfg(feature = "webgl")]
747 pub(crate) fn webgl_chan(&self) -> Option<WebGLChan> {
748 self.webgl_chan.clone()
749 }
750
751 #[cfg(feature = "webxr")]
752 pub(crate) fn webxr_registry(&self) -> Option<webxr_api::Registry> {
753 self.webxr_registry.clone()
754 }
755
756 fn new_paint_worklet(&self, cx: &mut JSContext) -> DomRoot<Worklet> {
757 debug!("Creating new paint worklet.");
758
759 let worklet_global_scope_init = self.into();
760 Worklet::new(
761 cx,
762 self,
763 WorkletGlobalScopeType::Paint,
764 Box::new(|| Rc::new(StatelessWorkletThreadPool::spawn(worklet_global_scope_init))),
765 )
766 }
767
768 pub(crate) fn register_image_cache_listener(
769 &self,
770 id: PendingImageId,
771 callback: impl Fn(PendingImageResponse, &mut JSContext) + 'static,
772 ) -> ImageCacheResponseCallback {
773 self.pending_image_callbacks
774 .borrow_mut()
775 .entry(id)
776 .or_default()
777 .push(PendingImageCallback(Box::new(callback)));
778
779 let image_cache_sender = self.image_cache_sender.clone();
780 Box::new(move |message| {
781 let _ = image_cache_sender.send(message);
782 })
783 }
784
785 fn pending_layout_image_notification(&self, no_gc: &NoGC, response: PendingImageResponse) {
786 let mut images = self.pending_layout_images.borrow_mut();
787 let nodes = images.entry(response.id);
788 let nodes = match nodes {
789 Entry::Occupied(nodes) => nodes,
790 Entry::Vacant(_) => return,
791 };
792 if matches!(
793 response.response,
794 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode
795 ) {
796 for ancillary_data in nodes.get() {
797 match ancillary_data.destination {
798 LayoutImageDestination::BoxTreeConstruction => {
799 ancillary_data.node.dirty(no_gc, NodeDamage::Other);
800 },
801 LayoutImageDestination::DisplayListBuilding => {
802 self.layout().set_needs_new_display_list();
803 },
804 }
805 }
806 }
807
808 match response.response {
809 ImageResponse::MetadataLoaded(_) => {},
810 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode => {
811 nodes.remove();
812 },
813 }
814 }
815
816 pub(crate) fn handle_image_rasterization_complete_notification(
817 &self,
818 no_gc: &NoGC,
819 response: RasterizationCompleteResponse,
820 ) {
821 let mut images = self.pending_images_for_rasterization.borrow_mut();
822 let nodes = images.entry((response.image_id, response.requested_size));
823 let nodes = match nodes {
824 Entry::Occupied(nodes) => nodes,
825 Entry::Vacant(_) => return,
826 };
827 for node in nodes.get() {
828 node.dirty(no_gc, NodeDamage::Other);
829 }
830 nodes.remove();
831 }
832
833 pub(crate) fn pending_image_notification(
834 &self,
835 response: PendingImageResponse,
836 cx: &mut JSContext,
837 ) {
838 let mut images = std::mem::take(&mut *self.pending_image_callbacks.borrow_mut());
843 let Entry::Occupied(callbacks) = images.entry(response.id) else {
844 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
845 return;
846 };
847
848 for callback in callbacks.get() {
849 callback.0(response.clone(), cx);
850 }
851
852 match response.response {
853 ImageResponse::MetadataLoaded(_) => {},
854 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode => {
855 callbacks.remove();
856 },
857 }
858
859 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
860 }
861
862 pub(crate) fn paint_api(&self) -> &CrossProcessPaintApi {
863 &self.paint_api
864 }
865
866 pub(crate) fn userscripts(&self) -> &[UserScript] {
867 &self.user_scripts
868 }
869
870 pub(crate) fn get_player_context(&self) -> WindowGLContext {
871 self.player_context.clone()
872 }
873
874 pub(crate) fn dispatch_event_with_target_override(&self, cx: &mut JSContext, event: &Event) {
876 event.dispatch(cx, self.upcast(), true);
877 }
878
879 pub(crate) fn font_context(&self) -> Arc<FontContext> {
880 self.layout().font_context().clone()
881 }
882
883 pub(crate) fn ongoing_navigation(&self) -> OngoingNavigation {
884 self.ongoing_navigation.get()
885 }
886
887 pub(crate) fn set_ongoing_navigation(&self) -> OngoingNavigation {
889 let new_value = self.ongoing_navigation.get().0.wrapping_add(1);
893
894 self.ongoing_navigation.set(OngoingNavigation(new_value));
901
902 OngoingNavigation(new_value)
904 }
905
906 fn stop_loading(&self, cx: &mut JSContext) {
908 let document = self.Document();
910 let was_loading = document.ReadyState() != DocumentReadyState::Complete;
911
912 self.set_ongoing_navigation();
921
922 document.abort_a_document_and_its_descendants(cx, AbortReason::StopLoading);
924
925 document.notify_constellation_that_load_finished(true );
929
930 if self.is_top_level() && was_loading {
931 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
932 self.webview_id(),
933 LoadStatus::Stopped,
934 ));
935 }
936 }
937
938 fn destroy_top_level_traversable(&self, cx: &mut JSContext) {
940 let document = self.Document();
946 document.destroy_document_and_its_descendants(cx);
948 self.send_to_constellation(ScriptToConstellationMessage::DiscardTopLevelBrowsingContext);
950 }
951
952 fn definitely_close(&self, cx: &mut JSContext) {
954 let document = self.Document();
955 if !document.check_if_unloading_is_cancelled(cx, false) {
960 return;
961 }
962 document.unload(cx, false);
966 self.destroy_top_level_traversable(cx);
968 }
969
970 fn cannot_show_simple_dialogs(&self) -> bool {
972 if self
975 .Document()
976 .has_active_sandboxing_flag(SandboxingFlagSet::SANDBOXED_MODALS_FLAG)
977 {
978 return true;
979 }
980
981 false
1000 }
1001
1002 pub(crate) fn notify_the_close_watcher_manager_about_user_activation(&self) {
1004 let manager = self.close_watcher_manager();
1006
1007 if manager.next_user_interaction_allows_a_new_group() {
1010 manager.increment_allowed_number_of_groups();
1011 }
1012
1013 manager.set_next_user_interaction_allows_a_new_group(false);
1015 }
1016
1017 pub(crate) fn close_watcher_manager(&self) -> &CloseWatcherManager {
1018 &self.close_watcher_manager
1019 }
1020
1021 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut JSContext) {
1022 self.script_thread().perform_a_microtask_checkpoint(cx);
1023 }
1024
1025 pub(crate) fn web_font_context(&self, no_gc: &NoGC) -> WebFontDocumentContext {
1026 let global = self.as_global_scope();
1027 let task_source = global
1028 .task_manager()
1029 .dom_manipulation_task_source()
1030 .to_sendable();
1031 let document = self.document_unrooted(no_gc);
1032 WebFontDocumentContext {
1033 policy_container: document.policy_container().clone(),
1034 request_client: self.request_client(Some(no_gc)),
1035 document_url: document.base_url(),
1036 csp_handler: Box::new(FontCspHandler {
1037 task_source: task_source.clone(),
1038 }),
1039 network_timing_handler: Box::new(FontNetworkTimingHandler { task_source }),
1040 }
1041 }
1042
1043 pub(crate) fn request_client(&self, no_gc: Option<&NoGC>) -> RequestClient {
1045 let (
1048 preloaded_resources,
1049 insecure_requests_policy,
1050 has_trustworthy_ancestor_origin,
1051 policy_container,
1052 origin,
1053 network_partition_key,
1054 ) = if let Some(no_gc) = no_gc {
1055 let document = self.document_unrooted(no_gc);
1056 (
1057 document.preloaded_resources().clone(),
1058 document.insecure_requests_policy(),
1059 document.has_trustworthy_ancestor_or_current_origin(),
1060 document.policy_container().clone(),
1061 document.origin().clone(),
1062 document.determine_the_network_partition_key(),
1063 )
1064 } else {
1065 let document = self.Document();
1066 (
1067 document.preloaded_resources().clone(),
1068 document.insecure_requests_policy(),
1069 document.has_trustworthy_ancestor_or_current_origin(),
1070 document.policy_container().clone(),
1071 document.origin().clone(),
1072 document.determine_the_network_partition_key(),
1073 )
1074 };
1075 RequestClient {
1076 preloaded_resources,
1077 policy_container,
1078 origin: Origin::Origin(origin.immutable().clone()),
1079 is_nested_browsing_context: !self.is_top_level(),
1080 insecure_requests_policy,
1081 has_trustworthy_ancestor_origin,
1082 network_partition_key,
1083 }
1084 }
1085
1086 #[expect(unsafe_code)]
1087 pub(crate) fn gc(&self, cx: &mut JSContext) {
1088 unsafe { JS_GC(cx, GCReason::API) };
1089 }
1090
1091 #[expect(unsafe_code)]
1092 pub(crate) fn maybe_gc(&self, cx: &mut JSContext) {
1093 unsafe { JS_MaybeGC(cx) };
1094 }
1095
1096 pub(crate) fn with_timers<T>(&self, f: impl FnOnce(&OneshotTimers) -> T) -> T {
1097 let document = self.Document();
1098 f(document.timers())
1099 }
1100}
1101
1102#[derive(Debug, MallocSizeOf)]
1103struct FontCspHandler {
1104 task_source: SendableTaskSource,
1105}
1106
1107impl CspViolationHandler for FontCspHandler {
1108 fn process_violations(&self, violations: Vec<Violation>) {
1109 self.task_source.queue(task!(csp_violation: move |cx| {
1110 if let Some(global) = GlobalScope::current() {
1111 global.report_csp_violations(cx, violations, None, None);
1112 }
1113 }));
1114 }
1115
1116 fn clone(&self) -> Box<dyn CspViolationHandler> {
1117 Box::new(Self {
1118 task_source: self.task_source.clone(),
1119 })
1120 }
1121}
1122
1123#[derive(Debug, MallocSizeOf)]
1124struct FontNetworkTimingHandler {
1125 task_source: SendableTaskSource,
1126}
1127
1128impl NetworkTimingHandler for FontNetworkTimingHandler {
1129 fn submit_timing(&self, url: ServoUrl, response: ResourceFetchTiming) {
1130 self.task_source.queue(task!(network_timing: move |cx| {
1131 let Some(global) = GlobalScope::current() else {
1132 return
1133 };
1134
1135 submit_timing(
1136 cx,
1137 &global,
1138 &FontFetchListener {
1139 url,
1140 },
1141 &Ok(()),
1142 &response,
1143 );
1144 }));
1145 }
1146
1147 fn clone(&self) -> Box<dyn NetworkTimingHandler> {
1148 Box::new(Self {
1149 task_source: self.task_source.clone(),
1150 })
1151 }
1152}
1153
1154#[derive(Debug)]
1155struct FontFetchListener {
1156 url: ServoUrl,
1157}
1158
1159impl ResourceTimingListener for FontFetchListener {
1160 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1161 (InitiatorType::Css, self.url.clone())
1162 }
1163}
1164
1165pub(crate) fn base64_btoa(input: DOMString) -> Fallible<DOMString> {
1167 if input.str().chars().any(|c: char| c > '\u{FF}') {
1171 Err(Error::InvalidCharacter(None))
1172 } else {
1173 let octets = input
1178 .str()
1179 .chars()
1180 .map(|c: char| c as u8)
1181 .collect::<Vec<u8>>();
1182
1183 let config =
1186 base64::engine::general_purpose::GeneralPurposeConfig::new().with_encode_padding(true);
1187 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
1188 Ok(DOMString::from(engine.encode(octets)))
1189 }
1190}
1191
1192pub(crate) fn base64_atob(input: DOMString) -> Fallible<DOMString> {
1194 fn is_html_space(c: char) -> bool {
1196 HTML_SPACE_CHARACTERS.contains(&c)
1197 }
1198 let without_spaces = input
1199 .str()
1200 .chars()
1201 .filter(|&c| !is_html_space(c))
1202 .collect::<String>();
1203 let mut input = &*without_spaces;
1204
1205 if input.len() % 4 == 0 {
1209 if input.ends_with("==") {
1210 input = &input[..input.len() - 2]
1211 } else if input.ends_with('=') {
1212 input = &input[..input.len() - 1]
1213 }
1214 }
1215
1216 if input.len() % 4 == 1 {
1219 return Err(Error::InvalidCharacter(None));
1220 }
1221
1222 if input
1230 .chars()
1231 .any(|c| c != '+' && c != '/' && !c.is_alphanumeric())
1232 {
1233 return Err(Error::InvalidCharacter(None));
1234 }
1235
1236 let config = base64::engine::general_purpose::GeneralPurposeConfig::new()
1237 .with_decode_padding_mode(base64::engine::DecodePaddingMode::RequireNone)
1238 .with_decode_allow_trailing_bits(true);
1239 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
1240
1241 let data = engine
1242 .decode(input)
1243 .map_err(|_| Error::InvalidCharacter(None))?;
1244 Ok(data.iter().map(|&b| b as char).collect::<String>().into())
1245}
1246
1247impl WindowMethods<crate::DomTypeHolder> for Window {
1248 fn Alert_(&self) {
1250 self.Alert(DOMString::new());
1253 }
1254
1255 fn Alert(&self, mut message: DOMString) {
1257 if self.cannot_show_simple_dialogs() {
1259 return;
1260 }
1261
1262 message.normalize_newlines();
1266
1267 {
1278 let stderr = stderr();
1282 let mut stderr = stderr.lock();
1283 let stdout = stdout();
1284 let mut stdout = stdout.lock();
1285 writeln!(&mut stdout, "\nALERT: {message}").unwrap();
1286 stdout.flush().unwrap();
1287 stderr.flush().unwrap();
1288 }
1289
1290 let (sender, receiver) =
1291 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1292 let dialog = SimpleDialogRequest::Alert {
1293 id: self.Document().embedder_controls().next_control_id(),
1294 message: String::from(message),
1295 response_sender: sender,
1296 };
1297 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1298 receiver.recv().unwrap_or_else(|_| {
1299 debug!("Alert dialog was cancelled or failed to show.");
1301 AlertResponse::Ok
1302 });
1303
1304 }
1307
1308 fn Caches(&self, cx: &mut JSContext) -> DomRoot<CacheStorage> {
1310 self.caches
1311 .or_init(|| CacheStorage::new(cx, self.as_global_scope()))
1312 }
1313
1314 fn Confirm(&self, mut message: DOMString) -> bool {
1316 if self.cannot_show_simple_dialogs() {
1318 return false;
1319 }
1320
1321 message.normalize_newlines();
1323
1324 let (sender, receiver) =
1330 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1331 let dialog = SimpleDialogRequest::Confirm {
1332 id: self.Document().embedder_controls().next_control_id(),
1333 message: String::from(message),
1334 response_sender: sender,
1335 };
1336 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1337
1338 match receiver.recv() {
1354 Ok(ConfirmResponse::Ok) => true,
1355 Ok(ConfirmResponse::Cancel) => false,
1356 Err(_) => {
1357 warn!("Confirm dialog was cancelled or failed to show.");
1358 false
1359 },
1360 }
1361 }
1362
1363 fn Prompt(&self, mut message: DOMString, default: DOMString) -> Option<DOMString> {
1365 if self.cannot_show_simple_dialogs() {
1367 return None;
1368 }
1369
1370 message.normalize_newlines();
1372
1373 let (sender, receiver) =
1381 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1382 let dialog = SimpleDialogRequest::Prompt {
1383 id: self.Document().embedder_controls().next_control_id(),
1384 message: String::from(message),
1385 default: String::from(default),
1386 response_sender: sender,
1387 };
1388 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1389
1390 match receiver.recv() {
1409 Ok(PromptResponse::Ok(input)) => Some(input.into()),
1410 Ok(PromptResponse::Cancel) => None,
1411 Err(_) => {
1412 warn!("Prompt dialog was cancelled or failed to show.");
1413 None
1414 },
1415 }
1416 }
1417
1418 fn Stop(&self, cx: &mut JSContext) {
1420 self.stop_loading(cx);
1425 }
1426
1427 fn Focus(&self, cx: &mut JSContext) {
1429 let document = self.Document();
1437 if !document.is_active() {
1438 return;
1439 }
1440
1441 document
1446 .focus_handler()
1447 .focus(cx, &FocusableArea::Viewport, FocusTrigger::Script);
1448
1449 }
1454
1455 fn Blur(&self) {
1457 }
1460
1461 fn Open(
1463 &self,
1464 cx: &mut JSContext,
1465 url: USVString,
1466 target: DOMString,
1467 features: DOMString,
1468 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
1469 self.window_proxy().open(cx, url, target, features)
1470 }
1471
1472 fn GetOpener(&self, cx: &mut CurrentRealm, mut retval: MutableHandleValue) -> Fallible<()> {
1474 let current = match self.window_proxy.get() {
1476 Some(proxy) => proxy,
1477 None => {
1479 retval.set(NullValue());
1480 return Ok(());
1481 },
1482 };
1483 if current.is_browsing_context_discarded() {
1488 retval.set(NullValue());
1489 return Ok(());
1490 }
1491 current.opener(cx, retval);
1493 Ok(())
1494 }
1495
1496 fn SetOpener(&self, cx: &mut JSContext, value: HandleValue) -> ErrorResult {
1498 if value.is_null() {
1500 if let Some(proxy) = self.window_proxy.get() {
1501 proxy.disown();
1502 }
1503 return Ok(());
1504 }
1505
1506 let obj = self.reflector().get_jsobject();
1508 let result = JS_DefineProperty(
1509 cx,
1510 obj,
1511 c"opener",
1512 value,
1513 PropertyFlags::ENUMERABLE | PropertyFlags::WRITABLE | PropertyFlags::CONFIGURABLE,
1514 );
1515
1516 if result { Ok(()) } else { Err(Error::JSFailed) }
1517 }
1518
1519 fn Closed(&self) -> bool {
1521 self.window_proxy
1522 .get()
1523 .map(|ref proxy| proxy.is_browsing_context_discarded() || proxy.is_closing())
1524 .unwrap_or(true)
1525 }
1526
1527 fn Close(&self, cx: &mut JSContext) {
1529 let window_proxy = match self.window_proxy.get() {
1531 Some(proxy) => proxy,
1532 None => return,
1534 };
1535 if window_proxy.is_closing() {
1537 return;
1538 }
1539 if let Ok(history_length) = self.History(cx).GetLength() {
1542 let is_auxiliary = window_proxy.is_auxiliary();
1543
1544 let is_script_closable = (self.is_top_level() && history_length == 1) ||
1546 is_auxiliary ||
1547 pref!(dom_allow_scripts_to_close_windows);
1548
1549 if is_script_closable {
1553 window_proxy.close();
1555
1556 let this = Trusted::new(self);
1558 let task = task!(window_close_browsing_context: move |cx| {
1559 let window = this.root();
1560 window.definitely_close(cx);
1561 });
1562 self.as_global_scope()
1563 .task_manager()
1564 .dom_manipulation_task_source()
1565 .queue(task);
1566 }
1567 }
1568 }
1569
1570 fn Document(&self) -> DomRoot<Document> {
1572 self.document
1573 .get()
1574 .expect("Document accessed before initialization.")
1575 }
1576
1577 fn History(&self, cx: &mut JSContext) -> DomRoot<History> {
1579 self.Document().history(cx)
1580 }
1581
1582 fn IndexedDB(&self, cx: &mut JSContext) -> DomRoot<IDBFactory> {
1584 self.upcast::<GlobalScope>().ensure_indexeddb_factory(cx)
1585 }
1586
1587 fn CustomElements(&self, cx: &mut JSContext) -> DomRoot<CustomElementRegistry> {
1589 let document = self.Document();
1592 if let Some(registry) = document.custom_element_registry() {
1593 return registry;
1594 }
1595 let registry = CustomElementRegistry::new(cx, self);
1598 document.set_custom_element_registry(®istry);
1599 registry
1601 }
1602
1603 fn Location(&self, cx: &mut JSContext) -> DomRoot<Location> {
1605 self.location.or_init(|| Location::new(cx, self))
1606 }
1607
1608 fn GetSessionStorage(&self, cx: &mut JSContext) -> Fallible<DomRoot<Storage>> {
1610 if let Some(storage) = self.session_storage.get() {
1613 return Ok(storage);
1614 }
1615
1616 if !self.origin().is_tuple() {
1620 return Err(Error::Security(Some(
1621 "Cannot access sessionStorage from opaque origin.".to_string(),
1622 )));
1623 }
1624
1625 let storage = Storage::new(cx, self, WebStorageType::Session);
1627
1628 self.session_storage.set(Some(&storage));
1630
1631 Ok(storage)
1633 }
1634
1635 fn GetLocalStorage(&self, cx: &mut JSContext) -> Fallible<DomRoot<Storage>> {
1637 if let Some(storage) = self.local_storage.get() {
1640 return Ok(storage);
1641 }
1642
1643 if !self.origin().is_tuple() {
1647 return Err(Error::Security(Some(
1648 "Cannot access localStorage from opaque origin.".to_string(),
1649 )));
1650 }
1651
1652 let storage = Storage::new(cx, self, WebStorageType::Local);
1654
1655 self.local_storage.set(Some(&storage));
1657
1658 Ok(storage)
1660 }
1661
1662 fn CookieStore(&self, cx: &mut JSContext) -> DomRoot<CookieStore> {
1664 self.cookie_store
1665 .or_init(|| CookieStore::new(cx, self.upcast::<GlobalScope>()))
1666 }
1667
1668 fn Crypto(&self, cx: &mut JSContext) -> DomRoot<crate::dom::crypto::Crypto> {
1670 self.crypto
1671 .or_init(|| crate::dom::crypto::Crypto::new(cx, self.as_global_scope()))
1672 }
1673
1674 fn GetFrameElement(&self) -> Option<DomRoot<Element>> {
1676 let window_proxy = self.window_proxy.get()?;
1678
1679 let container = window_proxy.frame_element()?;
1681
1682 let container_doc = container.owner_document();
1684 let current_doc = GlobalScope::current()
1685 .expect("No current global object")
1686 .as_window()
1687 .Document();
1688 if !current_doc
1689 .origin()
1690 .same_origin_domain(&container_doc.origin())
1691 {
1692 return None;
1693 }
1694 Some(DomRoot::from_ref(container))
1696 }
1697
1698 fn ReportError(&self, cx: &mut JSContext, error: HandleValue) {
1700 self.as_global_scope().report_an_exception(cx, error);
1701 }
1702
1703 fn Navigator(&self, cx: &mut JSContext) -> DomRoot<Navigator> {
1705 self.navigator.or_init(|| Navigator::new(cx, self))
1706 }
1707
1708 fn ClientInformation(&self, cx: &mut JSContext) -> DomRoot<Navigator> {
1710 self.Navigator(cx)
1711 }
1712
1713 fn SetTimeout(
1715 &self,
1716 cx: &mut JSContext,
1717 callback: TrustedScriptOrStringOrFunction,
1718 timeout: i32,
1719 args: Vec<HandleValue>,
1720 ) -> Fallible<i32> {
1721 let callback = match callback {
1722 TrustedScriptOrStringOrFunction::String(i) => {
1723 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1724 },
1725 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1726 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1727 },
1728 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1729 };
1730 self.as_global_scope().set_timeout_or_interval(
1731 cx,
1732 callback,
1733 args,
1734 Duration::from_millis(timeout.max(0) as u64),
1735 IsInterval::NonInterval,
1736 )
1737 }
1738
1739 fn ClearTimeout(&self, handle: i32) {
1741 self.as_global_scope().clear_timeout_or_interval(handle);
1742 }
1743
1744 fn SetInterval(
1746 &self,
1747 cx: &mut JSContext,
1748 callback: TrustedScriptOrStringOrFunction,
1749 timeout: i32,
1750 args: Vec<HandleValue>,
1751 ) -> Fallible<i32> {
1752 let callback = match callback {
1753 TrustedScriptOrStringOrFunction::String(i) => {
1754 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1755 },
1756 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1757 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1758 },
1759 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1760 };
1761 self.as_global_scope().set_timeout_or_interval(
1762 cx,
1763 callback,
1764 args,
1765 Duration::from_millis(timeout.max(0) as u64),
1766 IsInterval::Interval,
1767 )
1768 }
1769
1770 fn ClearInterval(&self, handle: i32) {
1772 self.ClearTimeout(handle);
1773 }
1774
1775 fn QueueMicrotask(&self, cx: &JSContext, callback: RootedCallback<VoidFunction>) {
1777 ScriptThread::enqueue_microtask(
1778 cx,
1779 Box::new(UserMicrotask {
1780 callback: callback.to_traced(),
1781 global: Dom::from_ref(&self.globalscope),
1782 }),
1783 );
1784 }
1785
1786 fn CreateImageBitmap(
1788 &self,
1789 realm: &mut CurrentRealm,
1790 image: ImageBitmapSource,
1791 options: &ImageBitmapOptions,
1792 ) -> RootedPromise {
1793 ImageBitmap::create_image_bitmap(
1794 self.as_global_scope(),
1795 image,
1796 0,
1797 0,
1798 None,
1799 None,
1800 options,
1801 realm,
1802 )
1803 }
1804
1805 fn CreateImageBitmap_(
1807 &self,
1808 realm: &mut CurrentRealm,
1809 image: ImageBitmapSource,
1810 sx: i32,
1811 sy: i32,
1812 sw: i32,
1813 sh: i32,
1814 options: &ImageBitmapOptions,
1815 ) -> RootedPromise {
1816 ImageBitmap::create_image_bitmap(
1817 self.as_global_scope(),
1818 image,
1819 sx,
1820 sy,
1821 Some(sw),
1822 Some(sh),
1823 options,
1824 realm,
1825 )
1826 }
1827
1828 fn Window(&self) -> DomRoot<WindowProxy> {
1830 self.window_proxy()
1831 }
1832
1833 fn Self_(&self) -> DomRoot<WindowProxy> {
1835 self.window_proxy()
1836 }
1837
1838 fn Frames(&self) -> DomRoot<WindowProxy> {
1840 self.window_proxy()
1841 }
1842
1843 fn Length(&self) -> u32 {
1845 self.Document().iframes().active_iframe_count() as u32
1846 }
1847
1848 fn GetParent(&self) -> Option<DomRoot<WindowProxy>> {
1850 let window_proxy = self.undiscarded_window_proxy()?;
1852
1853 if let Some(parent) = window_proxy.parent() {
1855 return Some(DomRoot::from_ref(parent));
1856 }
1857 Some(window_proxy)
1859 }
1860
1861 fn GetTop(&self) -> Option<DomRoot<WindowProxy>> {
1863 let window_proxy = self.undiscarded_window_proxy()?;
1865
1866 Some(DomRoot::from_ref(window_proxy.top()))
1868 }
1869
1870 fn Performance(&self, cx: &mut JSContext) -> DomRoot<Performance> {
1873 self.performance
1874 .or_init(|| Performance::new(cx, self.as_global_scope(), self.navigation_start.get()))
1875 }
1876
1877 global_event_handlers!();
1879
1880 window_event_handlers!();
1882
1883 fn Screen(&self, cx: &mut JSContext) -> DomRoot<Screen> {
1885 self.screen.or_init(|| Screen::new(cx, self))
1886 }
1887
1888 fn GetVisualViewport(&self, cx: &mut JSContext) -> Option<DomRoot<VisualViewport>> {
1890 if !self.Document().is_fully_active() {
1894 return None;
1895 }
1896
1897 Some(self.get_or_init_visual_viewport(cx))
1898 }
1899
1900 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
1902 base64_btoa(btoa)
1903 }
1904
1905 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
1907 base64_atob(atob)
1908 }
1909
1910 fn RequestAnimationFrame(
1912 &self,
1913 callback: RootedCallback<FrameRequestCallback>,
1914 ) -> Fallible<u32> {
1915 Ok(self
1916 .Document()
1917 .request_animation_frame(AnimationFrameCallback::FrameRequestCallback {
1918 callback: callback.to_traced(),
1919 }))
1920 }
1921
1922 fn CancelAnimationFrame(&self, ident: u32) -> ErrorResult {
1924 let doc = self.Document();
1925 doc.cancel_animation_frame(ident);
1926 Ok(())
1927 }
1928
1929 fn PostMessage(
1931 &self,
1932 cx: &mut JSContext,
1933 message: HandleValue,
1934 target_origin: USVString,
1935 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1936 ) -> ErrorResult {
1937 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1938 let source = incumbent.as_window();
1939 let source_origin = source.Document().origin().immutable().clone();
1940
1941 self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
1942 }
1943
1944 fn PostMessage_(
1946 &self,
1947 cx: &mut JSContext,
1948 message: HandleValue,
1949 options: &WindowPostMessageOptions,
1950 ) -> ErrorResult {
1951 auto_root!(&in(cx) let transfer =
1952 options
1953 .parent
1954 .transfer
1955 .iter()
1956 .map(|js| js.get())
1957 .collect::<Vec<_>>());
1958
1959 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1960 let source = incumbent.as_window();
1961
1962 let source_origin = source.Document().origin().immutable().clone();
1963
1964 self.post_message_impl(
1965 &options.targetOrigin,
1966 source_origin,
1967 source,
1968 cx,
1969 message,
1970 transfer,
1971 )
1972 }
1973
1974 fn CaptureEvents(&self) {
1976 }
1978
1979 fn ReleaseEvents(&self) {
1981 }
1983
1984 fn WebdriverElement(&self, id: DOMString) -> Option<DomRoot<Element>> {
1985 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1986 }
1987
1988 fn WebdriverFrame(&self, browsing_context_id: DOMString) -> Option<DomRoot<WindowProxy>> {
1989 self.Document()
1990 .iframes()
1991 .iter()
1992 .find(|iframe| {
1993 iframe
1994 .browsing_context_id()
1995 .as_ref()
1996 .map(BrowsingContextId::to_string) ==
1997 Some(browsing_context_id.to_string())
1998 })
1999 .and_then(|iframe| iframe.GetContentWindow())
2000 }
2001
2002 fn WebdriverWindow(&self, webview_id: DOMString) -> DomRoot<WindowProxy> {
2003 let window_proxy = &self
2004 .window_proxy
2005 .get()
2006 .expect("Should always have a WindowProxy when calling WebdriverWindow");
2007 assert!(
2008 self.is_top_level(),
2009 "Window must be top level browsing context."
2010 );
2011 assert!(self.webview_id().to_string() == webview_id);
2012 DomRoot::from_ref(window_proxy)
2013 }
2014
2015 fn WebdriverShadowRoot(&self, id: DOMString) -> Option<DomRoot<ShadowRoot>> {
2016 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
2017 }
2018
2019 fn GetComputedStyle(
2021 &self,
2022 cx: &mut JSContext,
2023 element: &Element,
2024 pseudo: Option<DOMString>,
2025 ) -> DomRoot<CSSStyleDeclaration> {
2026 let mut is_null = false;
2030
2031 let pseudo = pseudo.map(|mut s| {
2037 s.make_ascii_lowercase();
2038 s
2039 });
2040 let pseudo = match pseudo {
2041 Some(ref pseudo) if pseudo == ":before" || pseudo == "::before" => {
2042 Some(PseudoElement::Before)
2043 },
2044 Some(ref pseudo) if pseudo == ":after" || pseudo == "::after" => {
2045 Some(PseudoElement::After)
2046 },
2047 Some(ref pseudo) if pseudo == "::selection" => Some(PseudoElement::Selection),
2048 Some(ref pseudo) if pseudo == "::marker" => Some(PseudoElement::Marker),
2049 Some(ref pseudo) if pseudo == "::placeholder" => Some(PseudoElement::Placeholder),
2050 Some(ref pseudo) if pseudo.starts_with(':') => {
2051 is_null = true;
2054 None
2055 },
2056 _ => None,
2057 };
2058
2059 CSSStyleDeclaration::new(
2075 cx,
2076 self,
2077 if is_null {
2078 CSSStyleOwner::Null
2079 } else {
2080 CSSStyleOwner::Element(Dom::from_ref(element))
2081 },
2082 pseudo,
2083 CSSModificationAccess::Readonly,
2084 )
2085 }
2086
2087 fn InnerHeight(&self) -> i32 {
2090 self.viewport_details
2091 .get()
2092 .size
2093 .height
2094 .to_i32()
2095 .unwrap_or(0)
2096 }
2097
2098 fn InnerWidth(&self) -> i32 {
2101 self.viewport_details.get().size.width.to_i32().unwrap_or(0)
2102 }
2103
2104 fn ScrollX(&self, cx: &mut JSContext) -> i32 {
2106 self.scroll_offset(cx).x as i32
2107 }
2108
2109 fn PageXOffset(&self, cx: &mut JSContext) -> i32 {
2111 self.ScrollX(cx)
2112 }
2113
2114 fn ScrollY(&self, cx: &mut JSContext) -> i32 {
2116 self.scroll_offset(cx).y as i32
2117 }
2118
2119 fn PageYOffset(&self, cx: &mut JSContext) -> i32 {
2121 self.ScrollY(cx)
2122 }
2123
2124 fn Scroll(&self, cx: &mut JSContext, options: &ScrollToOptions) {
2126 let x = options.left.unwrap_or(0.0) as f32;
2131
2132 let y = options.top.unwrap_or(0.0) as f32;
2135
2136 self.scroll(cx, x, y, options.parent.behavior);
2138 }
2139
2140 fn Scroll_(&self, cx: &mut JSContext, x: f64, y: f64) {
2142 self.scroll(cx, x as f32, y as f32, ScrollBehavior::Auto);
2146 }
2147
2148 fn ScrollTo(&self, cx: &mut JSContext, options: &ScrollToOptions) {
2153 self.Scroll(cx, options);
2154 }
2155
2156 fn ScrollTo_(&self, cx: &mut JSContext, x: f64, y: f64) {
2161 self.Scroll_(cx, x, y)
2162 }
2163
2164 fn ScrollBy(&self, cx: &mut JSContext, options: &ScrollToOptions) {
2166 let mut options = options.clone();
2172 let x = options.left.unwrap_or(0.0);
2173 let x = if x.is_finite() { x } else { 0.0 };
2174 let y = options.top.unwrap_or(0.0);
2175 let y = if y.is_finite() { y } else { 0.0 };
2176
2177 options.left.replace(x + self.ScrollX(cx) as f64);
2179
2180 options.top.replace(y + self.ScrollY(cx) as f64);
2182
2183 self.Scroll(cx, &options)
2185 }
2186
2187 fn ScrollBy_(&self, cx: &mut JSContext, x: f64, y: f64) {
2189 let mut options = ScrollToOptions::empty();
2193
2194 options.left.replace(x);
2197
2198 options.top.replace(y);
2200
2201 self.ScrollBy(cx, &options);
2203 }
2204
2205 fn ResizeTo(&self, width: i32, height: i32) {
2207 let window_proxy = match self.window_proxy.get() {
2209 Some(proxy) => proxy,
2210 None => return,
2211 };
2212
2213 if !window_proxy.is_auxiliary() {
2216 return;
2217 }
2218
2219 let dpr = self.device_pixel_ratio();
2220 let size = Size2D::new(width, height).to_f32() * dpr;
2221 self.send_to_embedder(EmbedderMsg::ResizeTo(self.webview_id(), size.to_i32()));
2222 }
2223
2224 fn ResizeBy(&self, x: i32, y: i32) {
2226 let size = self.client_window().size();
2227 self.ResizeTo(x + size.width, y + size.height)
2229 }
2230
2231 fn MoveTo(&self, x: i32, y: i32) {
2233 let dpr = self.device_pixel_ratio();
2236 let point = Point2D::new(x, y).to_f32() * dpr;
2237 let msg = EmbedderMsg::MoveTo(self.webview_id(), point.to_i32());
2238 self.send_to_embedder(msg);
2239 }
2240
2241 fn MoveBy(&self, x: i32, y: i32) {
2243 let origin = self.client_window().min;
2244 self.MoveTo(x + origin.x, y + origin.y)
2246 }
2247
2248 fn ScreenX(&self) -> i32 {
2250 self.client_window().min.x
2251 }
2252
2253 fn ScreenLeft(&self) -> i32 {
2255 self.client_window().min.x
2256 }
2257
2258 fn ScreenY(&self) -> i32 {
2260 self.client_window().min.y
2261 }
2262
2263 fn ScreenTop(&self) -> i32 {
2265 self.client_window().min.y
2266 }
2267
2268 fn OuterHeight(&self) -> i32 {
2270 self.client_window().height()
2271 }
2272
2273 fn OuterWidth(&self) -> i32 {
2275 self.client_window().width()
2276 }
2277
2278 fn DevicePixelRatio(&self) -> Finite<f64> {
2280 Finite::wrap(self.device_pixel_ratio().get() as f64)
2281 }
2282
2283 fn Status(&self) -> DOMString {
2285 self.status.borrow().clone().root()
2286 }
2287
2288 fn SetStatus(&self, status: DOMString) {
2290 *self.status.borrow_mut() = status.traced()
2291 }
2292
2293 fn MatchMedia(&self, cx: &mut JSContext, query: DOMString) -> DomRoot<MediaQueryList> {
2295 let media_query_list = MediaList::parse_media_list(&query.str(), self);
2296 let document = self.Document();
2297 let mql = MediaQueryList::new(cx, &document, media_query_list);
2298 self.media_query_lists.track(&*mql);
2299 mql
2300 }
2301
2302 fn Fetch(
2304 &self,
2305 realm: &mut CurrentRealm,
2306 input: RequestOrUSVString,
2307 init: &RequestInit,
2308 ) -> RootedPromise {
2309 fetch::Fetch(self.upcast(), input, init, realm)
2310 }
2311
2312 fn FetchLater(
2314 &self,
2315 cx: &mut JSContext,
2316 input: RequestInfo,
2317 init: &DeferredRequestInit,
2318 ) -> Fallible<DomRoot<FetchLaterResult>> {
2319 fetch::FetchLater(cx, self, input, init)
2320 }
2321
2322 #[cfg(feature = "bluetooth")]
2323 fn TestRunner(&self, cx: &mut JSContext) -> DomRoot<TestRunner> {
2324 self.test_runner
2325 .or_init(|| TestRunner::new(cx, self.upcast()))
2326 }
2327
2328 fn RunningAnimationCount(&self) -> u32 {
2329 self.document.get().map_or(0, |document| {
2330 document.animation_manager().running_animation_count() as u32
2331 })
2332 }
2333
2334 fn SetName(&self, name: DOMString) {
2336 if let Some(proxy) = self.undiscarded_window_proxy() {
2337 proxy.set_name(name);
2338 }
2339 }
2340
2341 fn Name(&self) -> DOMString {
2343 match self.undiscarded_window_proxy() {
2344 Some(proxy) => proxy.get_name(),
2345 None => DOMString::new(),
2346 }
2347 }
2348
2349 fn Origin(&self) -> USVString {
2351 USVString(self.origin().immutable().ascii_serialization().into_owned())
2352 }
2353
2354 fn GetSelection(&self, cx: &mut JSContext) -> Option<DomRoot<Selection>> {
2356 self.document.get().and_then(|d| d.GetSelection(cx))
2357 }
2358
2359 fn Event(&self, cx: &mut JSContext, rval: MutableHandleValue) {
2361 if let Some(ref event) = self.current_event.get() {
2362 event.reflector().get_jsobject().to_jsval(cx, rval);
2363 }
2364 }
2365
2366 fn IsSecureContext(&self) -> bool {
2367 self.as_global_scope().is_secure_context()
2368 }
2369
2370 fn NamedGetter(&self, cx: &mut JSContext, name: DOMString) -> Option<NamedPropertyValue> {
2372 if name.is_empty() {
2373 return None;
2374 }
2375 let document = self.Document();
2376
2377 let iframes: Vec<_> = document
2379 .iframes()
2380 .iter()
2381 .filter(|iframe| {
2382 if let Some(window) = iframe.GetContentWindow() {
2383 return window.get_name() == name;
2384 }
2385 false
2386 })
2387 .collect();
2388
2389 let iframe_iter = iframes.iter().map(|iframe| iframe.upcast::<Element>());
2390
2391 let name = Atom::from(name);
2392
2393 let elements_with_name = document.get_elements_with_name(cx, &name);
2395 let name_iter = elements_with_name
2396 .iter()
2397 .map(|element| &**element)
2398 .filter(|elem| is_named_element_with_name_attribute(elem));
2399
2400 let elements_with_id = document.get_elements_with_id(cx, &name);
2401 let id_iter = elements_with_id
2402 .iter()
2403 .map(|element| &**element)
2404 .filter(|elem| is_named_element_with_id_attribute(elem));
2405
2406 for elem in iframe_iter.clone() {
2408 if let Some(nested_window_proxy) = elem
2409 .downcast::<HTMLIFrameElement>()
2410 .and_then(|iframe| iframe.GetContentWindow())
2411 {
2412 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
2413 }
2414 }
2415
2416 let mut elements = iframe_iter.chain(name_iter).chain(id_iter);
2417
2418 let first = elements.next()?;
2419
2420 if elements.next().is_none() {
2421 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
2423 }
2424
2425 #[derive(JSTraceable, MallocSizeOf)]
2427 struct WindowNamedGetter {
2428 #[no_trace]
2429 name: Atom,
2430 }
2431 impl CollectionFilter for WindowNamedGetter {
2432 fn filter(&self, elem: &Element, _root: &Node) -> bool {
2433 let type_ = match elem.upcast::<Node>().type_id() {
2434 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
2435 _ => return false,
2436 };
2437 if elem.get_id().as_ref() == Some(&self.name) {
2438 return true;
2439 }
2440 match type_ {
2441 HTMLElementTypeId::HTMLEmbedElement |
2442 HTMLElementTypeId::HTMLFormElement |
2443 HTMLElementTypeId::HTMLImageElement |
2444 HTMLElementTypeId::HTMLObjectElement => {
2445 elem.get_name().as_ref() == Some(&self.name)
2446 },
2447 _ => false,
2448 }
2449 }
2450 }
2451 let collection = HTMLCollection::create(
2452 cx,
2453 self,
2454 document.upcast(),
2455 Box::new(WindowNamedGetter { name }),
2456 );
2457 Some(NamedPropertyValue::HTMLCollection(collection))
2458 }
2459
2460 fn SupportedPropertyNames(&self, no_gc: &NoGC) -> Vec<DOMString> {
2462 self.Document().SupportedPropertyNames(no_gc)
2463 }
2464
2465 fn StructuredClone(
2467 &self,
2468 cx: &mut JSContext,
2469 value: HandleValue,
2470 options: &StructuredSerializeOptions,
2471 retval: MutableHandleValue,
2472 ) -> Fallible<()> {
2473 self.as_global_scope()
2474 .structured_clone(cx, value, options, retval)
2475 }
2476
2477 fn TrustedTypes(&self, cx: &mut JSContext) -> DomRoot<TrustedTypePolicyFactory> {
2478 self.trusted_types
2479 .or_init(|| TrustedTypePolicyFactory::new(cx, self.as_global_scope()))
2480 }
2481}
2482
2483impl Window {
2484 pub(crate) fn scroll_offset(&self, cx: &mut JSContext) -> Vector2D<f32, LayoutPixel> {
2485 self.scroll_offset_query_with_external_scroll_id(cx, self.pipeline_id().root_scroll_id())
2486 }
2487
2488 pub(crate) fn create_named_properties_object(
2491 cx: &mut JSContext,
2492 proto: HandleObject,
2493 object: MutableHandleObject,
2494 ) {
2495 window_named_properties::create(cx, proto, object)
2496 }
2497
2498 pub(crate) fn current_event(&self) -> Option<DomRoot<Event>> {
2499 self.current_event.get()
2500 }
2501
2502 pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
2503 let current = self.current_event();
2504 self.current_event.set(event);
2505 current
2506 }
2507
2508 fn post_message_impl(
2510 &self,
2511 target_origin: &USVString,
2512 source_origin: ImmutableOrigin,
2513 source: &Window,
2514 cx: &mut JSContext,
2515 message: HandleValue,
2516 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
2517 ) -> ErrorResult {
2518 let data = structuredclone::write(cx, message, Some(transfer))?;
2520
2521 let target_origin = match target_origin.0[..].as_ref() {
2523 "*" => None,
2524 "/" => Some(source_origin.clone()),
2525 url => match ServoUrl::parse(url) {
2526 Ok(url) => Some(url.origin()),
2527 Err(_) => return Err(Error::Syntax(None)),
2528 },
2529 };
2530
2531 self.post_message(target_origin, source_origin, &source.window_proxy(), data);
2533 Ok(())
2534 }
2535
2536 pub(crate) fn paint_worklet(&self, cx: &mut JSContext) -> DomRoot<Worklet> {
2538 self.paint_worklet.or_init(|| self.new_paint_worklet(cx))
2539 }
2540
2541 pub(crate) fn clear_js_runtime(&self) {
2542 self.clear_js_runtime_common();
2543
2544 self.Document().teardown_custom_element_registry();
2547
2548 *self.js_runtime.borrow_mut() = None;
2549
2550 if let Some(performance) = self.performance.get() {
2551 performance.clear_and_disable_performance_entry_buffer();
2552 }
2553
2554 if let Some(factory) = self.upcast::<GlobalScope>().indexeddb_factory() {
2559 factory.abort_pending_upgrades_and_close_databases();
2560 }
2561
2562 self.pending_image_callbacks.borrow_mut().clear();
2565 }
2566
2567 pub(crate) fn scroll(&self, cx: &mut JSContext, x: f32, y: f32, behavior: ScrollBehavior) {
2569 let xfinite = if x.is_finite() { x } else { 0.0 };
2571 let yfinite = if y.is_finite() { y } else { 0.0 };
2572
2573 let viewport = self.viewport_details.get().size;
2583
2584 let scrolling_area = self.scrolling_area_query(cx, None).to_f32();
2603 let x = xfinite.clamp(0.0, 0.0f32.max(scrolling_area.width() - viewport.width));
2604 let y = yfinite.clamp(0.0, 0.0f32.max(scrolling_area.height() - viewport.height));
2605
2606 let scroll_offset = self.scroll_offset(cx);
2609 if x == scroll_offset.x && y == scroll_offset.y {
2610 return;
2611 }
2612
2613 self.perform_a_scroll(
2618 cx,
2619 x,
2620 y,
2621 self.pipeline_id().root_scroll_id(),
2622 behavior,
2623 None,
2624 );
2625 }
2626
2627 pub(crate) fn perform_a_scroll(
2629 &self,
2630 cx: &mut JSContext,
2631 x: f32,
2632 y: f32,
2633 scroll_id: ExternalScrollId,
2634 _behavior: ScrollBehavior,
2635 element: Option<&Element>,
2636 ) {
2637 let (reflow_phases_run, _) = self.reflow(
2661 cx,
2662 ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)),
2663 );
2664 if reflow_phases_run.needs_frame() {
2665 self.paint_api()
2666 .generate_frame(vec![self.webview_id().into()]);
2667 }
2668
2669 if reflow_phases_run.contains(ReflowPhasesRun::UpdatedScrollNodeOffset) {
2677 match element {
2678 Some(element) if !scroll_id.is_root() => element.handle_scroll_event(),
2679 _ => self.Document().handle_viewport_scroll_event(),
2680 };
2681 }
2682
2683 }
2686
2687 pub(crate) fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
2688 self.viewport_details.get().hidpi_scale_factor
2689 }
2690
2691 fn client_window(&self) -> DeviceIndependentIntRect {
2692 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel!");
2693
2694 self.send_to_embedder(EmbedderMsg::GetWindowRect(self.webview_id(), sender));
2695
2696 receiver.recv().unwrap_or_default()
2697 }
2698
2699 pub(crate) fn advance_animation_clock(&self, no_gc: &NoGC, delta: TimeDuration) {
2702 self.Document()
2703 .advance_animation_timeline_for_testing(delta);
2704 ScriptThread::handle_tick_all_animations_for_testing(no_gc, self.pipeline_id());
2705 }
2706
2707 pub(crate) fn reflow(
2715 &self,
2716 cx: &mut JSContext,
2717 reflow_goal: ReflowGoal,
2718 ) -> (ReflowPhasesRun, ReflowStatistics) {
2719 let document = self.Document();
2720
2721 if !document.is_fully_active() {
2723 return Default::default();
2724 }
2725
2726 self.document_unrooted(cx.no_gc())
2727 .ensure_safe_to_run_script_or_layout();
2728
2729 match reflow_goal {
2733 ReflowGoal::LayoutQuery(_) | ReflowGoal::UpdateScrollNode(..) => {
2734 self.flush_ancestor_layouts_if_necessary(cx);
2735 },
2736 ReflowGoal::UpdateTheRendering => { },
2737 }
2738
2739 let pipeline_id = self.pipeline_id();
2743 if reflow_goal == ReflowGoal::UpdateTheRendering &&
2744 self.layout_blocker.get().layout_blocked()
2745 {
2746 debug!("Suppressing pre-load-event reflow pipeline {pipeline_id}");
2747 return Default::default();
2748 }
2749
2750 debug!("script: performing reflow for goal {reflow_goal:?}");
2751 #[cfg(feature = "devtools")]
2752 let marker = if self.need_emit_timeline_marker(devtools_traits::TimelineMarkerType::Reflow)
2753 {
2754 Some(devtools_traits::TimelineMarker::start("Reflow".to_owned()))
2755 } else {
2756 None
2757 };
2758
2759 if let Some(selection) = document.selection() {
2760 selection.update_overlaps_document_selection_flags(cx.no_gc());
2761 }
2762
2763 let restyle_reason = document.restyle_reason(cx.no_gc());
2764 document.clear_restyle_reasons();
2765 let restyle = if restyle_reason.needs_restyle() {
2766 debug!("Invalidating layout cache due to reflow condition {restyle_reason:?}",);
2767 self.layout_marker.borrow().set(false);
2769 *self.layout_marker.borrow_mut() = Rc::new(Cell::new(true));
2771
2772 if restyle_reason.contains(RestyleReason::ViewportChanged) &&
2776 self.layout().device().used_viewport_size()
2777 {
2778 document.dirty_all_nodes(cx.no_gc());
2779 }
2780
2781 let stylesheets_changed = document.flush_stylesheets_for_reflow();
2782 let pending_restyles = document.drain_pending_restyles(cx.no_gc());
2783 let dirty_root = document
2784 .take_dirty_root()
2785 .filter(|_| !stylesheets_changed)
2786 .or_else(|| document.GetDocumentElement())
2787 .map(|root| root.upcast::<Node>().to_trusted_node_address());
2788
2789 Some(ReflowRequestRestyle {
2790 reason: restyle_reason,
2791 dirty_root,
2792 stylesheets_changed,
2793 pending_restyles,
2794 })
2795 } else {
2796 None
2797 };
2798
2799 document.id_map().resolve_all(cx.no_gc(), document.upcast());
2802
2803 let document_context = self.web_font_context(cx.no_gc());
2804 let should_update_accessibility_tree = matches!(
2805 reflow_goal,
2806 ReflowGoal::UpdateTheRendering | ReflowGoal::LayoutQuery(QueryMsg::AccessKitNodeQuery)
2807 );
2808 let accessibility =
2809 if should_update_accessibility_tree && self.layout().accessibility_active() {
2810 let rooted_nodes_for_integrity_check =
2811 document.rooted_nodes_for_accessibility_integrity_check();
2812 let focused_element = document
2813 .focus_handler()
2814 .focused_area()
2815 .element()
2816 .map(|element| element.upcast::<Node>().to_opaque());
2817 let damage = document
2818 .accessibility_data_mut()
2819 .drain_pending_accessibility_damage();
2820 Some(ReflowRequestAccessibility {
2821 damage,
2822 focused_element,
2823 rooted_nodes_for_integrity_check,
2824 })
2825 } else {
2826 None
2827 };
2828
2829 let reflow = ReflowRequest {
2831 document: document.upcast::<Node>().to_trusted_node_address(),
2832 epoch: document.current_rendering_epoch(),
2833 restyle,
2834 viewport_details: self.viewport_details.get(),
2835 origin: self.origin().immutable().clone(),
2836 reflow_goal,
2837 animation_timeline_value: document.current_animation_timeline_value(),
2838 animations: document.animation_manager().sets(),
2839 animating_images: document.animation_manager().animating_images(),
2840 highlighted_dom_node: document.highlighted_dom_node().map(|node| node.to_opaque()),
2841 frame_focused: self.webview_state().has_system_focus.get() &&
2842 document.focus_handler().has_focus(),
2843 halt_lcp: self.has_dispatched_scroll_event.get() ||
2844 self.has_dispatched_input_event.get(),
2845 paint_timing_eligible: document.paint_timing_eligible(),
2846 paint_timing_info: document.paint_timing_info(),
2847 document_context,
2848 accessibility,
2849 };
2850
2851 let Some(reflow_result) = self.layout.borrow_mut().reflow(reflow) else {
2852 return Default::default();
2853 };
2854
2855 self.handle_accessibility_actions(reflow_result.pending_accessibility_actions, cx);
2856
2857 debug!("script: layout complete");
2858 #[cfg(feature = "devtools")]
2859 if let Some(marker) = marker {
2860 self.emit_timeline_marker(marker.end());
2861 }
2862
2863 self.handle_new_or_removed_web_fonts_post_reflow(cx, reflow_result.changed_web_fonts);
2864
2865 self.handle_pending_images_post_reflow(
2866 cx,
2867 reflow_result.pending_images,
2868 reflow_result.pending_rasterization_images,
2869 reflow_result.pending_svg_elements_for_serialization,
2870 );
2871
2872 if let Some(candidate) = reflow_result.lcp_candidate {
2873 self.process_lcp_candidate_post_reflow(candidate, &document);
2874 }
2875
2876 if !reflow_result.container_timing_records.is_empty() {
2877 self.process_container_timing_records_post_reflow(
2878 reflow_result.container_timing_records,
2879 &document,
2880 );
2881 }
2882
2883 if let Some(iframe_sizes) = reflow_result.iframe_sizes {
2884 document
2885 .iframes()
2886 .handle_new_iframe_sizes_after_layout(cx, self, iframe_sizes);
2887 }
2888
2889 document.update_animations_post_reflow();
2890
2891 (
2892 reflow_result.reflow_phases_run,
2893 reflow_result.reflow_statistics,
2894 )
2895 }
2896
2897 pub(crate) fn request_screenshot_readiness(&self, cx: &mut JSContext) {
2898 self.has_pending_screenshot_readiness_request.set(true);
2899 self.maybe_resolve_pending_screenshot_readiness_requests(cx);
2900 }
2901
2902 pub(crate) fn maybe_resolve_pending_screenshot_readiness_requests(&self, cx: &mut JSContext) {
2903 let pending_request = self.has_pending_screenshot_readiness_request.get();
2904 if !pending_request {
2905 return;
2906 }
2907
2908 let document = self.Document();
2909 if document.ReadyState() != DocumentReadyState::Complete {
2910 return;
2911 }
2912
2913 if document.render_blocking_element_count() > 0 {
2914 return;
2915 }
2916
2917 if document.GetDocumentElement().is_some_and(|elem| {
2921 elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
2922 elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
2923 }) {
2924 return;
2925 }
2926
2927 if self.font_context().web_fonts_still_loading() != 0 {
2928 return;
2929 }
2930
2931 if self.Document().Fonts(cx).waiting_to_fullfill_promise() {
2932 return;
2933 }
2934
2935 if !self.pending_layout_images.borrow().is_empty() ||
2936 !self.pending_images_for_rasterization.borrow().is_empty()
2937 {
2938 return;
2939 }
2940
2941 let document = self.Document();
2942 if document.needs_rendering_update(cx.no_gc()) {
2943 return;
2944 }
2945
2946 let epoch = document.current_rendering_epoch();
2949 let pipeline_id = self.pipeline_id();
2950 debug!("Ready to take screenshot of {pipeline_id:?} at epoch={epoch:?}");
2951
2952 self.send_to_constellation(
2953 ScriptToConstellationMessage::RespondToScreenshotReadinessRequest(
2954 ScreenshotReadinessResponse::Ready(epoch),
2955 ),
2956 );
2957 self.has_pending_screenshot_readiness_request.set(false);
2958 }
2959
2960 pub(crate) fn reflow_if_reflow_timer_expired(&self, cx: &mut JSContext) {
2963 if !matches!(
2966 self.layout_blocker.get(),
2967 LayoutBlocker::Parsing(instant) if instant + INITIAL_REFLOW_DELAY < Instant::now()
2968 ) {
2969 return;
2970 }
2971 self.allow_layout_if_necessary(cx);
2972 }
2973
2974 pub(crate) fn prevent_layout_until_load_event(&self) {
2978 if !matches!(self.layout_blocker.get(), LayoutBlocker::WaitingForParse) {
2981 return;
2982 }
2983
2984 self.layout_blocker
2985 .set(LayoutBlocker::Parsing(Instant::now()));
2986 }
2987
2988 pub(crate) fn allow_layout_if_necessary(&self, cx: &mut JSContext) {
2991 if matches!(self.layout_blocker.get(), LayoutBlocker::NotBlocked) {
2992 return;
2993 }
2994
2995 self.layout_blocker.set(LayoutBlocker::NotBlocked);
2996
2997 let document = self.Document();
3009 if !document.is_render_blocked() && document.update_the_rendering(cx).0.needs_frame() {
3010 self.paint_api()
3011 .generate_frame(vec![self.webview_id().into()]);
3012 }
3013 }
3014
3015 pub(crate) fn layout_blocked(&self) -> bool {
3016 self.layout_blocker.get().layout_blocked()
3017 }
3018
3019 fn flush_ancestor_layouts_if_necessary(&self, cx: &mut JSContext) {
3020 let Some(parent_pipeline_id) = self.parent_info else {
3021 return;
3022 };
3023 let Some(parent_window) = ScriptThread::find_window(parent_pipeline_id) else {
3024 return;
3025 };
3026 if !parent_window.Document().is_safe_to_run_script_or_layout() {
3029 return;
3030 }
3031 if parent_window.Document().is_render_blocked() {
3034 return;
3035 }
3036 parent_window.flush_ancestor_layouts_if_necessary(cx);
3037 if parent_window
3038 .document_unrooted(cx.no_gc())
3039 .restyle_reason(cx.no_gc())
3040 .needs_restyle()
3041 {
3042 parent_window.reflow(
3043 cx,
3044 ReflowGoal::LayoutQuery(QueryMsg::FlushForUpdateTheRenderingQuery),
3045 );
3046 }
3047 }
3048
3049 pub(crate) fn layout_reflow(&self, cx: &mut JSContext, query_msg: QueryMsg) {
3051 self.reflow(cx, ReflowGoal::LayoutQuery(query_msg));
3052 }
3053
3054 #[expect(unsafe_code)]
3056 pub(crate) fn layout_reflow_deprecated(&self, query_msg: QueryMsg) {
3057 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
3059 self.reflow(&mut cx, ReflowGoal::LayoutQuery(query_msg));
3060 }
3061
3062 pub(crate) fn reflow_for_non_flushing_update_the_rendering_queries(&self, cx: &mut JSContext) {
3064 self.reflow(
3065 cx,
3066 ReflowGoal::LayoutQuery(QueryMsg::FlushForUpdateTheRenderingQuery),
3067 );
3068 }
3069
3070 pub(crate) fn resolved_font_style_query(
3071 &self,
3072 cx: &mut JSContext,
3073 node: &Node,
3074 value: String,
3075 ) -> Option<ServoArc<Font>> {
3076 self.layout_reflow(cx, QueryMsg::ResolvedFontStyleQuery);
3077
3078 let document = self.Document();
3079 let animations = document.animation_manager().sets();
3080 self.layout.borrow().query_resolved_font_style(
3081 node.to_trusted_node_address(),
3082 &value,
3083 animations,
3084 document.current_animation_timeline_value(),
3085 )
3086 }
3087
3088 #[expect(unsafe_code)]
3091 pub(crate) fn containing_block_node_query_without_reflow(
3092 &self,
3093 node: &Node,
3094 ) -> Option<DomRoot<Node>> {
3095 self.layout
3096 .borrow()
3097 .query_containing_block(node.to_trusted_node_address())
3098 .map(|address| unsafe { from_untrusted_node_address(address) })
3099 }
3100
3101 pub(crate) fn is_containing_block_descendant_query_without_reflow(
3104 &self,
3105 possible_ancestor: &Node,
3106 possible_descendant: &Node,
3107 ) -> bool {
3108 self.layout.borrow().query_containing_block_is_descendant(
3109 possible_ancestor.to_trusted_node_address(),
3110 possible_descendant.to_trusted_node_address(),
3111 )
3112 }
3113
3114 pub(crate) fn padding_query_without_reflow(&self, node: &Node) -> Option<PhysicalSides<Au>> {
3119 let layout = self.layout.borrow();
3120 layout.query_padding(node.to_trusted_node_address())
3121 }
3122
3123 pub(crate) fn box_area_query_without_reflow(
3128 &self,
3129 node: &Node,
3130 area: BoxAreaType,
3131 exclude_transform_and_inline: bool,
3132 ) -> Option<Rect<Au, CSSPixel>> {
3133 let layout = self.layout.borrow();
3134 layout.ensure_stacking_context_tree(self.viewport_details.get());
3135 layout.query_box_area(
3136 node.to_trusted_node_address(),
3137 area,
3138 exclude_transform_and_inline,
3139 )
3140 }
3141
3142 pub(crate) fn box_area_query(
3143 &self,
3144 cx: &mut JSContext,
3145 node: &Node,
3146 area: BoxAreaType,
3147 exclude_transform_and_inline: bool,
3148 ) -> Option<Rect<Au, CSSPixel>> {
3149 self.layout_reflow(cx, QueryMsg::BoxArea);
3150 self.box_area_query_without_reflow(node, area, exclude_transform_and_inline)
3151 }
3152
3153 pub(crate) fn box_areas_query(
3154 &self,
3155 cx: &mut JSContext,
3156 node: &Node,
3157 area: BoxAreaType,
3158 ) -> CSSPixelRectVec {
3159 self.layout_reflow(cx, QueryMsg::BoxAreas);
3160 self.layout
3161 .borrow()
3162 .query_box_areas(node.to_trusted_node_address(), area)
3163 }
3164
3165 pub(crate) fn client_rect_query(&self, cx: &mut JSContext, node: &Node) -> Rect<i32, CSSPixel> {
3166 self.layout_reflow(cx, QueryMsg::ClientRectQuery);
3167 self.layout
3168 .borrow()
3169 .query_client_rect(node.to_trusted_node_address())
3170 }
3171
3172 pub(crate) fn current_css_zoom_query(&self, cx: &mut JSContext, node: &Node) -> f32 {
3173 self.layout_reflow(cx, QueryMsg::CurrentCSSZoomQuery);
3174 self.layout
3175 .borrow()
3176 .query_current_css_zoom(node.to_trusted_node_address())
3177 }
3178
3179 pub(crate) fn document_unrooted<'a>(&self, no_gc: &'a NoGC) -> UnrootedDom<'a, Document> {
3181 self.document
3182 .get_unrooted(no_gc)
3183 .expect("Document accessed before initialization.")
3184 }
3185
3186 pub(crate) fn scrolling_area_query(
3189 &self,
3190 cx: &mut JSContext,
3191 node: Option<&Node>,
3192 ) -> Rect<i32, CSSPixel> {
3193 self.layout_reflow(cx, QueryMsg::ScrollingAreaOrOffsetQuery);
3194 self.layout
3195 .borrow()
3196 .query_scrolling_area(node.map(Node::to_trusted_node_address))
3197 }
3198
3199 pub(crate) fn scroll_offset_query(
3200 &self,
3201 cx: &mut JSContext,
3202 node: &Node,
3203 ) -> Vector2D<f32, LayoutPixel> {
3204 let external_scroll_id = ExternalScrollId(
3205 combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody),
3206 self.pipeline_id().into(),
3207 );
3208 self.scroll_offset_query_with_external_scroll_id(cx, external_scroll_id)
3209 }
3210
3211 fn scroll_offset_query_with_external_scroll_id(
3212 &self,
3213 cx: &mut JSContext,
3214 external_scroll_id: ExternalScrollId,
3215 ) -> Vector2D<f32, LayoutPixel> {
3216 self.layout_reflow(cx, QueryMsg::ScrollingAreaOrOffsetQuery);
3217 self.scroll_offset_query_with_external_scroll_id_no_reflow(external_scroll_id)
3218 }
3219
3220 fn scroll_offset_query_with_external_scroll_id_no_reflow(
3221 &self,
3222 external_scroll_id: ExternalScrollId,
3223 ) -> Vector2D<f32, LayoutPixel> {
3224 self.layout
3225 .borrow()
3226 .scroll_offset(external_scroll_id)
3227 .unwrap_or_default()
3228 }
3229
3230 pub(crate) fn scroll_an_element(
3233 &self,
3234 cx: &mut JSContext,
3235 element: &Element,
3236 x: f32,
3237 y: f32,
3238 behavior: ScrollBehavior,
3239 ) {
3240 let scroll_id = ExternalScrollId(
3241 combine_id_with_fragment_type(
3242 element.upcast::<Node>().to_opaque().id(),
3243 FragmentType::FragmentBody,
3244 ),
3245 self.pipeline_id().into(),
3246 );
3247
3248 self.perform_a_scroll(cx, x, y, scroll_id, behavior, Some(element));
3252 }
3253
3254 pub(crate) fn resolved_style_query(
3255 &self,
3256 element: TrustedNodeAddress,
3257 pseudo: Option<PseudoElement>,
3258 property: PropertyId,
3259 ) -> DOMString {
3260 self.layout_reflow_deprecated(QueryMsg::ResolvedStyleQuery(property.clone()));
3261
3262 let document = self.Document();
3263 let animations = document.animation_manager().sets();
3264 DOMString::from(self.layout.borrow().query_resolved_style(
3265 element,
3266 pseudo,
3267 property,
3268 animations,
3269 document.current_animation_timeline_value(),
3270 ))
3271 }
3272
3273 pub(crate) fn accesskit_node_query(
3274 &self,
3275 cx: &mut JSContext,
3276 element: TrustedNodeAddress,
3277 ) -> Option<accesskit::Node> {
3278 self.layout_reflow(cx, QueryMsg::AccessKitNodeQuery);
3279 self.layout.borrow().query_accesskit_node(element)
3280 }
3281
3282 pub(crate) fn get_iframe_viewport_details_if_known(
3286 &self,
3287 cx: &mut JSContext,
3288 browsing_context_id: BrowsingContextId,
3289 ) -> Option<ViewportDetails> {
3290 self.layout_reflow(cx, QueryMsg::InnerWindowDimensionsQuery);
3292 self.Document()
3293 .iframes()
3294 .viewport_details(browsing_context_id)
3295 }
3296
3297 #[expect(unsafe_code)]
3298 pub(crate) fn offset_parent_query(
3299 &self,
3300 cx: &mut JSContext,
3301 node: &Node,
3302 ) -> (Option<DomRoot<Element>>, Rect<Au, CSSPixel>) {
3303 self.layout_reflow(cx, QueryMsg::OffsetParentQuery);
3304 let response = self
3305 .layout
3306 .borrow()
3307 .query_offset_parent(node.to_trusted_node_address());
3308 let element = response.node_address.and_then(|parent_node_address| {
3309 let node = unsafe { from_untrusted_node_address(parent_node_address) };
3310 DomRoot::downcast(node)
3311 });
3312 (element, response.rect)
3313 }
3314
3315 pub(crate) fn scroll_container_query(
3316 &self,
3317 node: Option<&Node>,
3318 flags: ScrollContainerQueryFlags,
3319 ) -> Option<ScrollContainerResponse> {
3320 self.layout_reflow_deprecated(QueryMsg::ScrollParentQuery);
3321 self.layout
3322 .borrow()
3323 .query_scroll_container(node.map(Node::to_trusted_node_address), flags)
3324 }
3325
3326 #[expect(unsafe_code)]
3327 pub(crate) fn scrolling_box_query(
3328 &self,
3329 node: Option<&Node>,
3330 flags: ScrollContainerQueryFlags,
3331 ) -> Option<ScrollingBox> {
3332 self.scroll_container_query(node, flags)
3333 .and_then(|response| {
3334 Some(match response {
3335 ScrollContainerResponse::Viewport(overflow) => {
3336 (ScrollingBoxSource::Viewport(self.Document()), overflow)
3337 },
3338 ScrollContainerResponse::Element(parent_node_address, overflow) => {
3339 let node = unsafe { from_untrusted_node_address(parent_node_address) };
3340 (
3341 ScrollingBoxSource::Element(DomRoot::downcast(node)?),
3342 overflow,
3343 )
3344 },
3345 })
3346 })
3347 .map(|(source, overflow)| ScrollingBox::new(source, overflow))
3348 }
3349
3350 #[expect(unsafe_code)]
3351 pub(crate) fn text_index_query_on_node_for_event(
3352 &self,
3353 cx: &mut JSContext,
3354 node: &Node,
3355 point_in_viewport: Point2D<Au, CSSPixel>,
3356 ) -> Option<(DomRoot<Node>, Utf32CodeUnits)> {
3357 self.layout_reflow(cx, QueryMsg::TextIndexOrSegmentQuery);
3358 let result = self
3359 .layout
3360 .borrow()
3361 .query_text_index(node.to_trusted_node_address(), point_in_viewport)?;
3362 let node = unsafe { from_untrusted_node_address(result.0.into()) };
3363 Some((node, result.1))
3364 }
3365
3366 #[expect(unsafe_code)]
3371 pub(crate) fn query_text_segment(
3372 &self,
3373 cx: &mut JSContext,
3374 node: &Node,
3375 offset: Utf32CodeUnitsOrNodeOffset,
3376 granularity: SegmentGranularity,
3377 ) -> Option<(RootedSelectionBoundary, RootedSelectionBoundary)> {
3378 self.layout_reflow(cx, QueryMsg::TextIndexOrSegmentQuery);
3379 let ((start_node, start_offset), (end_node, end_offset)) = self
3380 .layout()
3381 .query_text_segment(node.to_trusted_node_address(), offset, granularity)?;
3382 let start = RootedSelectionBoundary::new_with_utf32_offset(
3383 unsafe { from_untrusted_node_address(start_node.into()) },
3384 start_offset,
3385 );
3386 let end = RootedSelectionBoundary::new_with_utf32_offset(
3387 unsafe { from_untrusted_node_address(end_node.into()) },
3388 end_offset,
3389 );
3390 Some((start, end))
3391 }
3392
3393 pub(crate) fn elements_from_point_query(
3394 &self,
3395 cx: &mut JSContext,
3396 flags: HitTestFlags,
3397 point: LayoutPoint,
3398 ) -> layout_api::HitTestResult {
3399 self.layout_reflow(cx, QueryMsg::ElementsFromPoint);
3400 self.layout().hit_test(flags, point)
3401 }
3402
3403 pub(crate) fn query_effective_overflow(
3404 &self,
3405 cx: &mut JSContext,
3406 node: &Node,
3407 ) -> Option<AxesOverflow> {
3408 self.layout_reflow(cx, QueryMsg::EffectiveOverflow);
3409 self.query_effective_overflow_without_reflow(node)
3410 }
3411
3412 pub(crate) fn query_effective_overflow_without_reflow(
3413 &self,
3414 node: &Node,
3415 ) -> Option<AxesOverflow> {
3416 self.layout
3417 .borrow()
3418 .query_effective_overflow(node.to_trusted_node_address())
3419 }
3420
3421 pub(crate) fn hit_test_from_input_event(
3422 &self,
3423 cx: &mut JSContext,
3424 flags: HitTestFlags,
3425 input_event: &ConstellationInputEvent,
3426 ) -> Option<HitTestResult> {
3427 self.hit_test_from_point_in_viewport(
3428 cx,
3429 flags,
3430 input_event.hit_test_result.as_ref()?.point_in_viewport,
3431 )
3432 }
3433
3434 #[expect(unsafe_code)]
3435 pub(crate) fn hit_test_from_point_in_viewport(
3436 &self,
3437 cx: &mut JSContext,
3438 flags: HitTestFlags,
3439 point_in_frame: Point2D<f32, CSSPixel>,
3440 ) -> Option<HitTestResult> {
3441 let result = self.elements_from_point_query(cx, flags, point_in_frame.cast_unit());
3442 let item = result.items.into_iter().next()?;
3443
3444 let point_relative_to_initial_containing_block =
3445 point_in_frame + self.scroll_offset(cx).cast_unit();
3446
3447 let from_opaque_node = |node: OpaqueNode| {
3450 let address = UntrustedNodeAddress(node.0 as *const c_void);
3451 unsafe { from_untrusted_node_address(address) }
3452 };
3453 Some(HitTestResult {
3454 node: from_opaque_node(item.node),
3455 dom_position_for_selection: result.dom_position_for_selection.map(|(node, offset)| {
3456 RootedSelectionBoundary::new_with_utf32_offset(from_opaque_node(node), offset)
3457 }),
3458 cursor: item.cursor,
3459 point_in_node: item.point_in_target,
3460 point_in_frame,
3461 point_relative_to_initial_containing_block,
3462 })
3463 }
3464
3465 pub(crate) fn init_window_proxy(&self, window_proxy: &WindowProxy) {
3466 assert!(
3467 self.window_proxy
3468 .get()
3469 .is_none_or(|current_proxy| &*current_proxy as *const WindowProxy == window_proxy)
3470 );
3471 self.window_proxy.set(Some(window_proxy));
3472 }
3473
3474 pub(crate) fn init_document(&self, document: &Document) {
3475 assert!(
3476 self.document
3477 .get()
3478 .is_none_or(|document| document.is_initial_about_blank())
3479 );
3480 assert!(document.window() == self);
3481 self.document.set(Some(document));
3482 self.update_jsprincipals_from_document(document);
3483 }
3484
3485 #[expect(unsafe_code)]
3492 pub(crate) fn update_jsprincipals_from_document(&self, document: &Document) {
3493 let realm = unsafe { GetObjectRealmOrNull(self.reflector().get_jsobject().get()) };
3494 let new_principals = ServoJSPrincipals::new::<crate::DomTypeHolder>(&document.origin());
3495 unsafe { SetRealmPrincipals(realm, new_principals.as_raw()) };
3496 }
3497
3498 pub(crate) fn load_data_for_document(
3499 &self,
3500 url: ServoUrl,
3501 pipeline_id: PipelineId,
3502 ) -> LoadData {
3503 let source_document = self.Document();
3504 let secure_context = if self.is_top_level() {
3505 None
3506 } else {
3507 Some(self.IsSecureContext())
3508 };
3509 LoadData::new(
3510 LoadOrigin::Script(self.origin().snapshot()),
3511 url,
3512 source_document.about_base_url(),
3513 Some(pipeline_id),
3514 Referrer::ReferrerUrl(source_document.url()),
3515 source_document.get_referrer_policy(),
3516 secure_context,
3517 Some(source_document.insecure_requests_policy()),
3518 source_document.has_trustworthy_ancestor_origin(),
3519 source_document.creation_sandboxing_flag_set_considering_parent_iframe(),
3520 )
3521 }
3522
3523 pub(crate) fn set_viewport_details(&self, viewport_details: ViewportDetails) {
3526 self.viewport_details.set(viewport_details);
3527 if !self.layout_mut().set_viewport_details(viewport_details) {
3528 return;
3529 }
3530 self.Document()
3531 .add_restyle_reason(RestyleReason::ViewportChanged);
3532 }
3533
3534 pub(crate) fn viewport_details(&self) -> ViewportDetails {
3535 self.viewport_details.get()
3536 }
3537
3538 pub(crate) fn get_or_init_visual_viewport(
3539 &self,
3540 cx: &mut JSContext,
3541 ) -> DomRoot<VisualViewport> {
3542 self.visual_viewport.or_init(|| {
3543 VisualViewport::new_from_layout_viewport(cx, self, self.viewport_details().size)
3544 })
3545 }
3546
3547 pub(crate) fn maybe_update_visual_viewport(
3549 &self,
3550 cx: &mut JSContext,
3551 pinch_zoom_infos: PinchZoomInfos,
3552 ) {
3553 if pinch_zoom_infos.rect == Rect::from_size(self.viewport_details().size) &&
3556 self.visual_viewport.get().is_none()
3557 {
3558 return;
3559 }
3560
3561 let visual_viewport = self.get_or_init_visual_viewport(cx);
3562 let changes = visual_viewport.update_from_pinch_zoom_infos(pinch_zoom_infos);
3563
3564 if changes.intersects(VisualViewportChanges::DimensionChanged) {
3565 self.has_changed_visual_viewport_dimension.set(true);
3566 }
3567 if changes.intersects(VisualViewportChanges::OffsetChanged) {
3568 visual_viewport.handle_scroll_event();
3569 }
3570 }
3571
3572 pub(crate) fn webview_theme(&self) -> Theme {
3573 self.webview_state.theme.get()
3574 }
3575
3576 pub(crate) fn refresh_theme(&self) {
3577 let document = self.Document();
3582 let new_theme = document
3583 .theme_override()
3584 .or(document.theme())
3585 .unwrap_or(self.webview_theme());
3586 if !self.layout_mut().set_theme(new_theme) {
3587 return;
3588 }
3589 document.add_restyle_reason(RestyleReason::ThemeChanged);
3590 self.pending_media_query_evaluation.set(true);
3593 }
3594
3595 pub(crate) fn take_pending_media_query_evaluation(&self) -> bool {
3598 self.pending_media_query_evaluation.replace(false)
3599 }
3600
3601 pub(crate) fn has_pending_media_query_evaluation(&self) -> bool {
3602 self.pending_media_query_evaluation.get()
3603 }
3604
3605 pub(crate) fn get_url(&self) -> ServoUrl {
3606 self.Document().url()
3607 }
3608
3609 pub(crate) fn windowproxy_handler(&self) -> &'static WindowProxyHandler {
3610 self.dom_static.windowproxy_handler
3611 }
3612
3613 pub(crate) fn add_resize_event(&self, event: ViewportDetails, event_type: WindowSizeType) {
3614 if self.viewport_details() == event {
3615 return;
3616 }
3617
3618 self.set_viewport_details(event);
3620
3621 *self.unhandled_resize_event.borrow_mut() = Some((event, event_type))
3624 }
3625
3626 pub(crate) fn take_unhandled_resize_event(&self) -> Option<(ViewportDetails, WindowSizeType)> {
3627 self.unhandled_resize_event.borrow_mut().take()
3628 }
3629
3630 pub(crate) fn has_unhandled_resize_event(&self) -> bool {
3632 self.unhandled_resize_event.borrow().is_some()
3633 }
3634
3635 pub(crate) fn suspend(&self, cx: &mut JSContext) {
3636 self.as_global_scope().suspend();
3638
3639 if self.window_proxy().currently_active() == Some(self.global().pipeline_id()) {
3641 self.window_proxy().unset_currently_active(cx);
3642 }
3643
3644 self.maybe_gc(cx);
3649 }
3650
3651 pub(crate) fn resume(&self, cx: &mut JSContext) {
3652 self.as_global_scope().resume();
3654
3655 self.window_proxy().set_currently_active(cx, self);
3657
3658 self.Document().title_changed();
3661 }
3662 pub(crate) fn set_webdriver_load_status_sender(
3663 &self,
3664 sender: Option<GenericSender<WebDriverLoadStatus>>,
3665 ) {
3666 *self.webdriver_load_status_sender.borrow_mut() = sender;
3667 }
3668
3669 pub(crate) fn webdriver_load_status_sender(
3670 &self,
3671 ) -> Option<GenericSender<WebDriverLoadStatus>> {
3672 self.webdriver_load_status_sender.borrow().clone()
3673 }
3674
3675 pub(crate) fn is_alive(&self) -> bool {
3676 self.current_state.get() == WindowState::Alive
3677 }
3678
3679 pub(crate) fn is_top_level(&self) -> bool {
3681 self.parent_info.is_none()
3682 }
3683
3684 fn run_resize_steps_for_layout_viewport(&self, cx: &mut JSContext) -> bool {
3689 let Some((new_size, size_type)) = self.take_unhandled_resize_event() else {
3690 return false;
3691 };
3692
3693 let current_viewport = self.viewport_details();
3696 if current_viewport == self.viewport_details_at_last_resize_steps.get() {
3697 return false;
3698 }
3699 self.viewport_details_at_last_resize_steps
3700 .set(current_viewport);
3701
3702 debug!(
3703 "Running resize steps for pipeline {:?} with viewport {new_size:?}",
3704 self.pipeline_id(),
3705 );
3706
3707 if size_type == WindowSizeType::Resize {
3709 let mut realm = enter_auto_realm(cx, self);
3710 let cx = &mut realm.current_realm();
3711 let uievent = UIEvent::new(
3712 cx,
3713 self,
3714 atom!("resize"),
3715 EventBubbles::DoesNotBubble,
3716 EventCancelable::NotCancelable,
3717 Some(self),
3718 0i32,
3719 0u32,
3720 );
3721 uievent.upcast::<Event>().fire(cx, self.upcast());
3722 }
3723
3724 true
3725 }
3726
3727 pub(crate) fn run_the_resize_steps(&self, cx: &mut JSContext) -> bool {
3732 let layout_viewport_resized = self.run_resize_steps_for_layout_viewport(cx);
3733
3734 if self.has_changed_visual_viewport_dimension.get() {
3735 let visual_viewport = self.get_or_init_visual_viewport(cx);
3736
3737 let uievent = UIEvent::new(
3738 cx,
3739 self,
3740 atom!("resize"),
3741 EventBubbles::DoesNotBubble,
3742 EventCancelable::NotCancelable,
3743 Some(self),
3744 0i32,
3745 0u32,
3746 );
3747 uievent.upcast::<Event>().fire(cx, visual_viewport.upcast());
3748
3749 self.has_changed_visual_viewport_dimension.set(false);
3750 }
3751
3752 layout_viewport_resized
3753 }
3754
3755 pub(crate) fn evaluate_media_queries_and_report_changes(&self, cx: &mut JSContext) {
3758 let mut realm = enter_auto_realm(cx, self);
3759 let cx = &mut realm.current_realm();
3760 rooted_vec!(let mut mql_list);
3761
3762 self.media_query_lists.for_each(|mql| {
3763 if let MediaQueryListMatchState::Changed = mql.evaluate_changes() {
3764 mql_list.push(Dom::from_ref(&*mql));
3766 }
3767 });
3768 for mql in mql_list.iter() {
3770 let event = MediaQueryListEvent::new(
3771 cx,
3772 &mql.global(),
3773 atom!("change"),
3774 false,
3775 false,
3776 mql.Media(),
3777 mql.Matches(),
3778 );
3779 event
3780 .upcast::<Event>()
3781 .fire(cx, mql.upcast::<EventTarget>());
3782 }
3783 }
3784
3785 pub(crate) fn set_throttled(&self, throttled: bool) {
3787 self.throttled.set(throttled);
3788 if throttled {
3789 self.as_global_scope().slow_down_timers();
3790 } else {
3791 self.as_global_scope().speed_up_timers();
3792 }
3793 }
3794
3795 pub(crate) fn throttled(&self) -> bool {
3796 self.throttled.get()
3797 }
3798
3799 pub(crate) fn unminified_css_dir(&self) -> Option<String> {
3800 self.unminified_css_dir.borrow().clone()
3801 }
3802
3803 pub(crate) fn local_script_source(&self) -> &Option<String> {
3804 &self.local_script_source
3805 }
3806
3807 pub(crate) fn set_navigation_start(&self) {
3808 self.navigation_start.set(CrossProcessInstant::now());
3809 }
3810
3811 pub(crate) fn navigation_start(&self) -> CrossProcessInstant {
3812 self.navigation_start.get()
3813 }
3814
3815 pub(crate) fn set_last_activation_timestamp(&self, time: UserActivationTimestamp) {
3816 self.last_activation_timestamp.set(time);
3817 }
3818
3819 pub(crate) fn set_last_history_action_activation_timestamp(
3820 &self,
3821 time: UserActivationTimestamp,
3822 ) {
3823 self.last_history_action_activation_timestamp.set(time);
3824 }
3825
3826 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
3827 self.as_global_scope()
3828 .script_to_embedder_chan()
3829 .send(msg)
3830 .unwrap();
3831 }
3832
3833 pub(crate) fn send_to_constellation(&self, msg: ScriptToConstellationMessage) {
3834 self.as_global_scope()
3835 .script_to_constellation_chan()
3836 .send(msg)
3837 .unwrap();
3838 }
3839
3840 #[cfg(feature = "webxr")]
3841 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3842 self.navigator
3843 .get()
3844 .as_ref()
3845 .and_then(|nav| nav.xr())
3846 .is_some_and(|xr| xr.pending_or_active_session())
3847 }
3848
3849 #[cfg(all(feature = "webgl", not(feature = "webxr")))]
3850 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3851 false
3852 }
3853
3854 fn handle_new_or_removed_web_fonts_post_reflow(
3856 &self,
3857 cx: &mut JSContext,
3858 changed_web_fonts: WebFontSetDifference,
3859 ) {
3860 if changed_web_fonts.is_empty() {
3861 return;
3862 }
3863
3864 let document = self.Document();
3865 let fonts = document.Fonts(cx);
3866 if !changed_web_fonts.removed_font_faces.is_empty() {
3867 fonts.notify_font_face_rules_removed(&changed_web_fonts.removed_font_faces);
3868 }
3869
3870 if !changed_web_fonts.removed_font_faces.is_empty() ||
3871 changed_web_fonts.cascade_index_of_any_rule_changed
3872 {
3873 document.dirty_all_nodes(cx.no_gc());
3876 }
3877
3878 if !changed_web_fonts.added_font_faces.is_empty() {
3879 fonts.switch_to_loading(cx);
3880
3881 for new_web_font in changed_web_fonts.added_font_faces {
3882 if let Some(font_face) = FontFace::new_for_web_font(cx, self.upcast(), new_web_font)
3883 {
3884 fonts.add(cx, font_face);
3885 }
3886 }
3887 }
3888 }
3889
3890 #[expect(unsafe_code)]
3892 fn process_lcp_candidate_post_reflow(&self, candidate: LCPCandidate, document: &Document) {
3893 let element = candidate.node.and_then(|node| {
3894 let node_address = UntrustedNodeAddress(node.id() as *const c_void);
3895 let node = unsafe { from_untrusted_node_address(node_address) };
3896 DomRoot::downcast::<Element>(node)
3897 });
3898 document.store_lcp_candidate(candidate, element.as_deref());
3899 }
3900
3901 #[expect(unsafe_code)]
3907 fn process_container_timing_records_post_reflow(
3908 &self,
3909 records: Vec<ContainerTimingRecord>,
3910 document: &Document,
3911 ) {
3912 let resolve = |node: OpaqueNode| unsafe {
3916 let node_address = UntrustedNodeAddress(node.id() as *const c_void);
3917 DomRoot::downcast::<Element>(from_untrusted_node_address(node_address))
3918 };
3919
3920 for record in records {
3921 let Some(root_element) = resolve(record.root_element) else {
3922 continue;
3923 };
3924 let Some(identifier) =
3928 root_element.get_attribute_string_value(&LocalName::from("containertiming"))
3929 else {
3930 continue;
3931 };
3932 let last_painted_element = record.last_painted_element.and_then(resolve);
3933 document.store_container_timing_record(
3934 record,
3935 identifier.into(),
3936 &root_element,
3937 last_painted_element.as_deref(),
3938 );
3939 }
3940 }
3941
3942 #[expect(unsafe_code)]
3943 fn handle_pending_images_post_reflow(
3944 &self,
3945 cx: &mut JSContext,
3946 pending_images: Vec<PendingImage>,
3947 pending_rasterization_images: Vec<PendingRasterizationImage>,
3948 pending_svg_element_for_serialization: Vec<UntrustedNodeAddress>,
3949 ) {
3950 let pipeline_id = self.pipeline_id();
3951 let image_cache = self.image_cache();
3952 for image in pending_images {
3953 let id = image.id;
3954 let node = unsafe { from_untrusted_node_address(image.node) };
3955
3956 if let PendingImageState::Unrequested(ref url) = image.state {
3957 fetch_image_for_layout(
3958 url.clone(),
3959 &node,
3960 id,
3961 image.is_internal_request,
3962 image_cache.clone(),
3963 );
3964 }
3965
3966 let mut images = self.pending_layout_images.borrow_mut();
3967 if !images.contains_key(&id) {
3968 let trusted_node = Trusted::new(&*node);
3969 let sender = self.register_image_cache_listener(id, move |response, cx| {
3970 trusted_node
3971 .root()
3972 .owner_window()
3973 .pending_layout_image_notification(cx.no_gc(), response);
3974 });
3975
3976 image_cache.add_listener(ImageLoadListener::new(sender, pipeline_id, id));
3977 }
3978
3979 let nodes = images.entry(id).or_default();
3980 if !nodes.iter().any(|n| *n.node == *node) {
3981 nodes.push(PendingLayoutImageAncillaryData {
3982 node: Dom::from_ref(&*node),
3983 destination: image.destination,
3984 });
3985 }
3986 }
3987
3988 for image in pending_rasterization_images {
3989 let node = unsafe { from_untrusted_node_address(image.node) };
3990
3991 let mut images = self.pending_images_for_rasterization.borrow_mut();
3992 if !images.contains_key(&(image.id, image.size)) {
3993 let image_cache_sender = self.image_cache_sender.clone();
3994 image_cache.add_rasterization_complete_listener(
3995 pipeline_id,
3996 image.id,
3997 image.size,
3998 Box::new(move |response| {
3999 let _ = image_cache_sender.send(response);
4000 }),
4001 );
4002 }
4003
4004 let nodes = images.entry((image.id, image.size)).or_default();
4005 if !nodes.iter().any(|n| **n == *node) {
4006 nodes.push(Dom::from_ref(&*node));
4007 }
4008 }
4009
4010 for node in pending_svg_element_for_serialization.into_iter() {
4011 let node = unsafe { from_untrusted_node_address(node) };
4012 let svg = node.downcast::<SVGSVGElement>().unwrap();
4013 svg.serialize_and_cache_subtree(cx);
4014 node.dirty(cx.no_gc(), NodeDamage::Other);
4015 }
4016 }
4017
4018 #[expect(unsafe_code)]
4019 fn handle_accessibility_actions(
4020 &self,
4021 actions: Vec<AccessibilityActionRequest>,
4022 cx: &mut JSContext,
4023 ) {
4024 for action_request in actions {
4025 let target_opaque = action_request.target;
4026 let target = unsafe { from_untrusted_node_address(target_opaque.into()) };
4027 if action_request.action == Action::Click {
4028 target.fire_synthetic_pointer_event_not_trusted(cx, atom!("click"));
4029 }
4030 }
4031 }
4032
4033 pub(crate) fn has_sticky_activation(&self) -> bool {
4035 UserActivationTimestamp::TimeStamp(CrossProcessInstant::now()) >=
4037 self.last_activation_timestamp.get()
4038 }
4039
4040 pub(crate) fn has_history_action_activation(&self) -> bool {
4042 self.last_history_action_activation_timestamp.get() != self.last_activation_timestamp.get()
4046 }
4047
4048 pub(crate) fn has_transient_activation(&self) -> bool {
4050 let current_time = CrossProcessInstant::now();
4053 UserActivationTimestamp::TimeStamp(current_time) >= self.last_activation_timestamp.get() &&
4054 UserActivationTimestamp::TimeStamp(current_time) <
4055 self.last_activation_timestamp.get() +
4056 pref!(dom_transient_activation_duration_ms)
4057 }
4058
4059 pub(crate) fn consume_last_activation_timestamp(&self) {
4060 if self.last_activation_timestamp.get() != UserActivationTimestamp::PositiveInfinity {
4061 self.set_last_activation_timestamp(UserActivationTimestamp::NegativeInfinity);
4062 }
4063 }
4064
4065 pub(crate) fn consume_user_activation(&self) {
4067 if self.undiscarded_window_proxy().is_none() {
4070 return;
4071 }
4072
4073 let Some(top_level_document) = self.top_level_document_if_local() else {
4077 return;
4078 };
4079
4080 top_level_document
4088 .window()
4089 .consume_last_activation_timestamp();
4090 for document in SameOriginDescendantNavigablesIterator::new(&top_level_document) {
4091 document.window().consume_last_activation_timestamp();
4092 }
4093 }
4094
4095 pub(crate) fn consume_history_action_user_activation(&self) {
4097 if self.undiscarded_window_proxy().is_none() {
4099 return;
4100 }
4101
4102 let Some(top_level_document) = self.top_level_document_if_local() else {
4105 return;
4106 };
4107
4108 top_level_document
4115 .window()
4116 .set_last_history_action_activation_timestamp(
4117 top_level_document.window().last_activation_timestamp.get(),
4118 );
4119 for document in SameOriginDescendantNavigablesIterator::new(&top_level_document) {
4120 document
4121 .window()
4122 .set_last_history_action_activation_timestamp(
4123 document.window().last_activation_timestamp.get(),
4124 );
4125 }
4126 }
4127
4128 #[allow(clippy::too_many_arguments)]
4129 pub(crate) fn new(
4130 cx: &mut JSContext,
4131 webview_state: Rc<WebViewState>,
4132 runtime: Rc<Runtime>,
4133 script_chan: Sender<MainThreadScriptMsg>,
4134 layout: Box<dyn Layout>,
4135 image_cache_sender: Sender<ImageCacheResponseMessage>,
4136 resource_threads: ResourceThreads,
4137 storage_threads: StorageThreads,
4138 #[cfg(feature = "bluetooth")] bluetooth_thread: GenericSender<BluetoothRequest>,
4139 mem_profiler_chan: MemProfilerChan,
4140 time_profiler_chan: TimeProfilerChan,
4141 #[cfg(feature = "devtools")] devtools_chan: Option<
4142 GenericCallback<devtools_traits::ScriptToDevtoolsControlMsg>,
4143 >,
4144 script_to_constellation_sender: ScriptToConstellationSender,
4145 embedder_chan: ScriptToEmbedderChan,
4146 control_chan: GenericSender<ScriptThreadMessage>,
4147 pipeline_id: PipelineId,
4148 parent_info: Option<PipelineId>,
4149 viewport_details: ViewportDetails,
4150 origin: MutableOrigin,
4151 creation_url: ServoUrl,
4152 top_level_creation_url: ServoUrl,
4153 navigation_start: CrossProcessInstant,
4154 #[cfg(feature = "webgl")] webgl_chan: Option<WebGLChan>,
4155 #[cfg(feature = "webxr")] webxr_registry: Option<webxr_api::Registry>,
4156 paint_api: CrossProcessPaintApi,
4157 unminify_js: bool,
4158 unminify_css: bool,
4159 local_script_source: Option<String>,
4160 user_scripts: Rc<Vec<UserScript>>,
4161 player_context: WindowGLContext,
4162 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
4163 inherited_secure_context: Option<bool>,
4164 weak_script_thread: Weak<ScriptThread>,
4165 ) -> DomRoot<Self> {
4166 let error_reporter = CSSErrorReporter {
4167 pipelineid: pipeline_id,
4168 script_chan: control_chan,
4169 };
4170
4171 let win = Box::new(Self {
4172 webview_state,
4173 globalscope: GlobalScope::new_inherited(
4174 #[cfg(feature = "devtools")]
4175 devtools_chan,
4176 mem_profiler_chan,
4177 time_profiler_chan,
4178 script_to_constellation_sender,
4179 embedder_chan,
4180 resource_threads,
4181 storage_threads,
4182 creation_url,
4183 Some(top_level_creation_url),
4184 #[cfg(feature = "webgpu")]
4185 gpu_id_hub,
4186 inherited_secure_context,
4187 unminify_js,
4188 ),
4189 caches: Default::default(),
4190 ongoing_navigation: Default::default(),
4191 script_chan,
4192 layout: RefCell::new(layout),
4193 image_cache_sender,
4194 navigator: Default::default(),
4195 crypto: Default::default(),
4196 location: Default::default(),
4197 window_proxy: Default::default(),
4198 document: Default::default(),
4199 performance: Default::default(),
4200 navigation_start: Cell::new(navigation_start),
4201 screen: Default::default(),
4202 session_storage: Default::default(),
4203 local_storage: Default::default(),
4204 cookie_store: Default::default(),
4205 status: DomRefCell::new(DOMString::new().traced()),
4206 parent_info,
4207 dom_static: GlobalStaticData::new(),
4208 js_runtime: DomRefCell::new(Some(runtime)),
4209 #[cfg(feature = "bluetooth")]
4210 bluetooth_thread,
4211 #[cfg(feature = "bluetooth")]
4212 bluetooth_extra_permission_data: BluetoothExtraPermissionData::new(),
4213 unhandled_resize_event: Default::default(),
4214 viewport_details_at_last_resize_steps: Cell::new(viewport_details),
4215 viewport_details: Cell::new(viewport_details),
4216 layout_blocker: Cell::new(LayoutBlocker::WaitingForParse),
4217 current_state: Cell::new(WindowState::Alive),
4218 #[cfg(feature = "devtools")]
4219 devtools_marker_sender: Default::default(),
4220 #[cfg(feature = "devtools")]
4221 devtools_markers: Default::default(),
4222 webdriver_load_status_sender: Default::default(),
4223 error_reporter,
4224 media_query_lists: DOMTracker::new(),
4225 #[cfg(feature = "bluetooth")]
4226 test_runner: Default::default(),
4227 #[cfg(feature = "webgl")]
4228 webgl_chan,
4229 #[cfg(feature = "webxr")]
4230 webxr_registry,
4231 pending_image_callbacks: Default::default(),
4232 pending_layout_images: Default::default(),
4233 pending_images_for_rasterization: Default::default(),
4234 unminified_css_dir: DomRefCell::new(if unminify_css {
4235 Some(unminified_path("unminified-css"))
4236 } else {
4237 None
4238 }),
4239 local_script_source,
4240 test_worklet: Default::default(),
4241 paint_worklet: Default::default(),
4242 exists_mut_observer: Cell::new(false),
4243 paint_api,
4244 user_scripts,
4245 player_context,
4246 throttled: Cell::new(false),
4247 layout_marker: DomRefCell::new(Rc::new(Cell::new(true))),
4248 current_event: MutNullableDom::new(None),
4249 trusted_types: Default::default(),
4250 reporting_observer_list: Default::default(),
4251 report_list: Default::default(),
4252 endpoints_list: Default::default(),
4253 script_window_proxies: ScriptThread::window_proxies(),
4254 has_pending_screenshot_readiness_request: Default::default(),
4255 visual_viewport: Default::default(),
4256 weak_script_thread,
4257 has_changed_visual_viewport_dimension: Default::default(),
4258 pending_media_query_evaluation: Default::default(),
4259 last_activation_timestamp: Cell::new(UserActivationTimestamp::PositiveInfinity),
4260 devtools_wants_updates: Default::default(),
4261 has_dispatched_scroll_event: Cell::new(false),
4262 has_dispatched_input_event: Cell::new(false),
4263 last_history_action_activation_timestamp: Cell::new(
4267 UserActivationTimestamp::PositiveInfinity,
4268 ),
4269 close_watcher_manager: CloseWatcherManager::new(),
4270 });
4271
4272 WindowBinding::Wrap::<crate::DomTypeHolder>(cx, &origin, win)
4273 }
4274
4275 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
4276 self.Document().task_manager()
4277 }
4278
4279 pub(crate) fn pipeline_id(&self) -> PipelineId {
4280 self.Document().pipeline_id()
4281 }
4282
4283 pub(crate) fn live_devtools_updates(&self) -> bool {
4284 self.devtools_wants_updates.get()
4285 }
4286
4287 pub(crate) fn set_devtools_wants_updates(&self, value: bool) {
4288 self.devtools_wants_updates.set(value);
4289 }
4290
4291 pub(crate) fn cache_layout_value<T>(&self, value: T) -> LayoutValue<T>
4293 where
4294 T: Copy + MallocSizeOf,
4295 {
4296 LayoutValue::new(self.layout_marker.borrow().clone(), value)
4297 }
4298
4299 pub(crate) fn set_up_a_window_environment_settings_object(
4308 &self,
4309 layout: Box<dyn Layout>,
4310 creation_url: ServoUrl,
4311 top_level_creation_url: ServoUrl,
4312 navigation_start: CrossProcessInstant,
4313 viewport_details: ViewportDetails,
4314 ) {
4315 *self.layout.borrow_mut() = layout;
4316 self.set_viewport_details(viewport_details);
4317 self.navigation_start.set(navigation_start);
4318
4319 let global = self.upcast::<GlobalScope>();
4322 global.set_creation_url(creation_url);
4323 global.set_top_level_creation_url(top_level_creation_url);
4324
4325 self.Document().detach_window();
4326 }
4327}
4328
4329#[cfg(feature = "devtools")]
4330impl Window {
4331 pub(crate) fn need_emit_timeline_marker(
4332 &self,
4333 timeline_type: devtools_traits::TimelineMarkerType,
4334 ) -> bool {
4335 let markers = self.devtools_markers.borrow();
4336 markers.contains(&timeline_type)
4337 }
4338
4339 pub(crate) fn emit_timeline_marker(&self, marker: devtools_traits::TimelineMarker) {
4340 let sender = self.devtools_marker_sender.borrow();
4341 let sender = sender.as_ref().expect("There is no marker sender");
4342 sender.send(Some(marker)).unwrap();
4343 }
4344
4345 pub(crate) fn set_devtools_timeline_markers(
4346 &self,
4347 markers: Vec<devtools_traits::TimelineMarkerType>,
4348 reply: GenericSender<Option<devtools_traits::TimelineMarker>>,
4349 ) {
4350 *self.devtools_marker_sender.borrow_mut() = Some(reply);
4351 self.devtools_markers.borrow_mut().extend(markers);
4352 }
4353
4354 pub(crate) fn drop_devtools_timeline_markers(
4355 &self,
4356 markers: Vec<devtools_traits::TimelineMarkerType>,
4357 ) {
4358 let mut devtools_markers = self.devtools_markers.borrow_mut();
4359 for marker in markers {
4360 devtools_markers.remove(&marker);
4361 }
4362 if devtools_markers.is_empty() {
4363 *self.devtools_marker_sender.borrow_mut() = None;
4364 }
4365 }
4366}
4367#[derive(MallocSizeOf)]
4372pub(crate) struct LayoutValue<T: MallocSizeOf> {
4373 #[conditional_malloc_size_of]
4374 is_valid: Rc<Cell<bool>>,
4375 value: T,
4376}
4377
4378#[expect(unsafe_code)]
4379unsafe impl<T: JSTraceable + MallocSizeOf> JSTraceable for LayoutValue<T> {
4380 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
4381 unsafe { self.value.trace(trc) };
4382 }
4383}
4384
4385impl<T: Copy + MallocSizeOf> LayoutValue<T> {
4386 fn new(marker: Rc<Cell<bool>>, value: T) -> Self {
4387 LayoutValue {
4388 is_valid: marker,
4389 value,
4390 }
4391 }
4392
4393 pub(crate) fn get(&self) -> Result<T, ()> {
4395 if self.is_valid.get() {
4396 return Ok(self.value);
4397 }
4398 Err(())
4399 }
4400}
4401
4402impl Window {
4403 pub(crate) fn post_message(
4405 &self,
4406 target_origin: Option<ImmutableOrigin>,
4407 source_origin: ImmutableOrigin,
4408 source: &WindowProxy,
4409 data: StructuredSerializedData,
4410 ) {
4411 let this = Trusted::new(self);
4412 let source = Trusted::new(source);
4413 let task = task!(post_serialised_message: move |cx| {
4414 let this = this.root();
4415 let source = source.root();
4416 let document = this.Document();
4417
4418 if let Some(ref target_origin) = target_origin
4420 && !target_origin.same_origin(&*document.origin()) {
4421 return;
4422 }
4423
4424 let obj = this.reflector().get_jsobject();
4426 let mut realm = AutoRealm::new(cx, NonNull::new(obj.get()).unwrap());
4427 let cx = &mut *realm;
4428 rooted!(&in(cx) let mut message_clone = UndefinedValue());
4429 if let Ok(ports) = structuredclone::read(cx, this.upcast(), data, message_clone.handle_mut()) {
4430 MessageEvent::dispatch_jsval(
4432 cx,
4433 this.upcast(),
4434 this.upcast(),
4435 message_clone.handle(),
4436 Some(source_origin.ascii_serialization().as_ref()),
4437 Some(&*source),
4438 ports,
4439 );
4440 } else {
4441 MessageEvent::dispatch_error(
4443 cx,
4444 this.upcast(),
4445 this.upcast(),
4446 );
4447 }
4448 });
4449 self.as_global_scope()
4451 .task_manager()
4452 .dom_manipulation_task_source()
4453 .queue(task);
4454 }
4455}
4456
4457#[derive(Clone, MallocSizeOf)]
4458pub(crate) struct CSSErrorReporter {
4459 pub(crate) pipelineid: PipelineId,
4460 pub(crate) script_chan: GenericSender<ScriptThreadMessage>,
4461}
4462unsafe_no_jsmanaged_fields!(CSSErrorReporter);
4463
4464impl ParseErrorReporter for CSSErrorReporter {
4465 fn report_error(
4466 &self,
4467 url: &UrlExtraData,
4468 location: SourceLocation,
4469 error: ContextualParseError,
4470 ) {
4471 if log_enabled!(log::Level::Info) {
4472 info!(
4473 "Url:\t{}\n{}:{} {}",
4474 url.0.as_str(),
4475 location.line,
4476 location.column,
4477 error
4478 )
4479 }
4480
4481 #[cfg(feature = "devtools")]
4483 let _ = self.script_chan.send(ScriptThreadMessage::ReportCSSError(
4484 self.pipelineid,
4485 url.0.to_string(),
4486 location.line,
4487 location.column,
4488 error.to_string(),
4489 ));
4490 }
4491}
4492
4493fn is_named_element_with_name_attribute(elem: &Element) -> bool {
4494 let type_ = match elem.upcast::<Node>().type_id() {
4495 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
4496 _ => return false,
4497 };
4498 matches!(
4499 type_,
4500 HTMLElementTypeId::HTMLEmbedElement |
4501 HTMLElementTypeId::HTMLFormElement |
4502 HTMLElementTypeId::HTMLImageElement |
4503 HTMLElementTypeId::HTMLObjectElement
4504 )
4505}
4506
4507fn is_named_element_with_id_attribute(elem: &Element) -> bool {
4508 elem.is_html_element() || elem.is_svg_element()
4509}
4510
4511#[expect(unsafe_code)]
4512#[unsafe(no_mangle)]
4513unsafe extern "C" fn dump_js_stack(cx: *mut RawJSContext) {
4515 unsafe {
4516 DumpJSStack(cx, true, false, false);
4517 }
4518}
4519
4520impl WindowHelpers for Window {
4521 fn create_named_properties_object(
4522 cx: &mut JSContext,
4523 proto: HandleObject,
4524 object: MutableHandleObject,
4525 ) {
4526 Self::create_named_properties_object(cx, proto, object)
4527 }
4528}
4529
4530impl HasOrigin for Window {
4531 fn origin(&self) -> MutableOrigin {
4532 Window::origin(self)
4533 }
4534}