1use std::borrow::ToOwned;
6use std::cell::{Cell, RefCell, RefMut};
7use std::cmp;
8use std::collections::hash_map::Entry;
9use std::collections::{HashMap, HashSet};
10use std::default::Default;
11use std::ffi::c_void;
12use std::io::{Write, stderr, stdout};
13use std::rc::Rc;
14use std::sync::Arc;
15use std::time::{Duration, Instant};
16
17use app_units::Au;
18use backtrace::Backtrace;
19use base::cross_process_instant::CrossProcessInstant;
20use base::generic_channel;
21use base::generic_channel::GenericSender;
22use base::id::{BrowsingContextId, PipelineId, WebViewId};
23use base64::Engine;
24#[cfg(feature = "bluetooth")]
25use bluetooth_traits::BluetoothRequest;
26use canvas_traits::webgl::WebGLChan;
27use compositing_traits::CrossProcessCompositorApi;
28use constellation_traits::{
29 LoadData, LoadOrigin, NavigationHistoryBehavior, ScreenshotReadinessResponse,
30 ScriptToConstellationChan, ScriptToConstellationMessage, StructuredSerializedData,
31 WindowSizeType,
32};
33use content_security_policy::Violation;
34use content_security_policy::sandboxing_directive::SandboxingFlagSet;
35use crossbeam_channel::{Sender, unbounded};
36use cssparser::SourceLocation;
37use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
38use dom_struct::dom_struct;
39use embedder_traits::user_content_manager::{UserContentManager, UserScript};
40use embedder_traits::{
41 AlertResponse, ConfirmResponse, EmbedderMsg, JavaScriptEvaluationError, PromptResponse,
42 ScriptToEmbedderChan, SimpleDialogRequest, Theme, UntrustedNodeAddress, ViewportDetails,
43 WebDriverJSResult, WebDriverLoadStatus,
44};
45use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
46use euclid::{Point2D, Scale, Size2D, Vector2D};
47use fonts::{CspViolationHandler, FontContext, WebFontDocumentContext};
48use ipc_channel::ipc::IpcSender;
49use js::glue::DumpJSStack;
50use js::jsapi::{
51 GCReason, Heap, JS_GC, JSAutoRealm, JSContext as RawJSContext, JSObject, JSPROP_ENUMERATE,
52};
53use js::jsval::{NullValue, UndefinedValue};
54use js::rust::wrappers::JS_DefineProperty;
55use js::rust::{
56 CustomAutoRooter, CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleObject,
57 MutableHandleValue,
58};
59use layout_api::{
60 BoxAreaType, ElementsFromPointFlags, ElementsFromPointResult, FragmentType, Layout,
61 LayoutImageDestination, PendingImage, PendingImageState, PendingRasterizationImage,
62 PhysicalSides, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest, ReflowRequestRestyle,
63 RestyleReason, ScrollContainerQueryFlags, ScrollContainerResponse, TrustedNodeAddress,
64 combine_id_with_fragment_type,
65};
66use malloc_size_of::MallocSizeOf;
67use media::WindowGLContext;
68use net_traits::ResourceThreads;
69use net_traits::image_cache::{
70 ImageCache, ImageCacheResponseCallback, ImageCacheResponseMessage, ImageLoadListener,
71 ImageResponse, PendingImageId, PendingImageResponse, RasterizationCompleteResponse,
72};
73use num_traits::ToPrimitive;
74use profile_traits::generic_channel as ProfiledGenericChannel;
75use profile_traits::mem::ProfilerChan as MemProfilerChan;
76use profile_traits::time::ProfilerChan as TimeProfilerChan;
77use rustc_hash::{FxBuildHasher, FxHashMap};
78use script_bindings::codegen::GenericBindings::WindowBinding::ScrollToOptions;
79use script_bindings::conversions::SafeToJSValConvertible;
80use script_bindings::interfaces::WindowHelpers;
81use script_bindings::root::Root;
82use script_traits::{ConstellationInputEvent, ScriptThreadMessage};
83use selectors::attr::CaseSensitivity;
84use servo_arc::Arc as ServoArc;
85use servo_config::pref;
86use servo_geometry::DeviceIndependentIntRect;
87use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
88use storage_traits::StorageThreads;
89use storage_traits::webstorage_thread::StorageType;
90use style::error_reporting::{ContextualParseError, ParseErrorReporter};
91use style::properties::PropertyId;
92use style::properties::style_structs::Font;
93use style::selector_parser::PseudoElement;
94use style::str::HTML_SPACE_CHARACTERS;
95use style::stylesheets::UrlExtraData;
96use style_traits::CSSPixel;
97use stylo_atoms::Atom;
98use url::Position;
99use webrender_api::ExternalScrollId;
100use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint};
101
102use super::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
103use super::bindings::trace::HashMapTracedValues;
104use super::types::SVGSVGElement;
105use crate::dom::bindings::cell::{DomRefCell, Ref};
106use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
107 DocumentMethods, DocumentReadyState, NamedPropertyValue,
108};
109use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
110use crate::dom::bindings::codegen::Bindings::HistoryBinding::History_Binding::HistoryMethods;
111use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
112 ImageBitmapOptions, ImageBitmapSource,
113};
114use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryList_Binding::MediaQueryListMethods;
115use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
116use crate::dom::bindings::codegen::Bindings::RequestBinding::{RequestInfo, RequestInit};
117use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
118use crate::dom::bindings::codegen::Bindings::WindowBinding::{
119 self, DeferredRequestInit, FrameRequestCallback, ScrollBehavior, WindowMethods,
120 WindowPostMessageOptions,
121};
122use crate::dom::bindings::codegen::UnionTypes::{
123 RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
124};
125use crate::dom::bindings::error::{
126 Error, ErrorInfo, ErrorResult, Fallible, javascript_error_info_from_error_info,
127};
128use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
129use crate::dom::bindings::num::Finite;
130use crate::dom::bindings::refcounted::Trusted;
131use crate::dom::bindings::reflector::{DomGlobal, DomObject};
132use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
133use crate::dom::bindings::str::{DOMString, USVString};
134use crate::dom::bindings::structuredclone;
135use crate::dom::bindings::trace::{CustomTraceable, JSTraceable, RootedTraceableBox};
136use crate::dom::bindings::utils::GlobalStaticData;
137use crate::dom::bindings::weakref::DOMTracker;
138#[cfg(feature = "bluetooth")]
139use crate::dom::bluetooth::BluetoothExtraPermissionData;
140use crate::dom::cookiestore::CookieStore;
141use crate::dom::crypto::Crypto;
142use crate::dom::csp::GlobalCspReporting;
143use crate::dom::css::cssstyledeclaration::{
144 CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner,
145};
146use crate::dom::customelementregistry::CustomElementRegistry;
147use crate::dom::document::{AnimationFrameCallback, Document};
148use crate::dom::element::Element;
149use crate::dom::event::{Event, EventBubbles, EventCancelable};
150use crate::dom::eventtarget::EventTarget;
151use crate::dom::fetchlaterresult::FetchLaterResult;
152use crate::dom::globalscope::GlobalScope;
153use crate::dom::hashchangeevent::HashChangeEvent;
154use crate::dom::history::History;
155use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
156use crate::dom::html::htmliframeelement::HTMLIFrameElement;
157use crate::dom::idbfactory::IDBFactory;
158use crate::dom::inputevent::HitTestResult;
159use crate::dom::location::Location;
160use crate::dom::medialist::MediaList;
161use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
162use crate::dom::mediaquerylistevent::MediaQueryListEvent;
163use crate::dom::messageevent::MessageEvent;
164use crate::dom::navigator::Navigator;
165use crate::dom::node::{Node, NodeDamage, NodeTraits, from_untrusted_node_address};
166use crate::dom::performance::performance::Performance;
167use crate::dom::promise::Promise;
168use crate::dom::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
169use crate::dom::reportingobserver::ReportingObserver;
170use crate::dom::screen::Screen;
171use crate::dom::scrolling_box::{ScrollingBox, ScrollingBoxSource};
172use crate::dom::selection::Selection;
173use crate::dom::shadowroot::ShadowRoot;
174use crate::dom::storage::Storage;
175#[cfg(feature = "bluetooth")]
176use crate::dom::testrunner::TestRunner;
177use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
178use crate::dom::types::{ImageBitmap, UIEvent};
179use crate::dom::webgl::webglrenderingcontext::WebGLCommandSender;
180#[cfg(feature = "webgpu")]
181use crate::dom::webgpu::identityhub::IdentityHub;
182use crate::dom::windowproxy::{WindowProxy, WindowProxyHandler};
183use crate::dom::worklet::Worklet;
184use crate::dom::workletglobalscope::WorkletGlobalScopeType;
185use crate::layout_image::fetch_image_for_layout;
186use crate::messaging::{MainThreadScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
187use crate::microtask::{Microtask, UserMicrotask};
188use crate::realms::{InRealm, enter_realm};
189use crate::script_runtime::{CanGc, JSContext, Runtime};
190use crate::script_thread::{ScriptThread, with_script_thread};
191use crate::script_window_proxies::ScriptWindowProxies;
192use crate::task_source::SendableTaskSource;
193use crate::timers::{IsInterval, TimerCallback};
194use crate::unminify::unminified_path;
195use crate::webdriver_handlers::{find_node_by_unique_id_in_document, jsval_to_webdriver};
196use crate::{fetch, window_named_properties};
197
198#[derive(MallocSizeOf)]
203pub struct PendingImageCallback(
204 #[ignore_malloc_size_of = "dyn Fn is currently impossible to measure"]
205 Box<dyn Fn(PendingImageResponse) + 'static>,
206);
207
208#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
210enum WindowState {
211 Alive,
212 Zombie, }
214
215const INITIAL_REFLOW_DELAY: Duration = Duration::from_millis(200);
218
219#[derive(Clone, Copy, MallocSizeOf)]
230enum LayoutBlocker {
231 WaitingForParse,
233 Parsing(Instant),
235 FiredLoadEventOrParsingTimerExpired,
239}
240
241impl LayoutBlocker {
242 fn layout_blocked(&self) -> bool {
243 !matches!(self, Self::FiredLoadEventOrParsingTimerExpired)
244 }
245}
246
247#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf, PartialEq)]
250pub(crate) struct OngoingNavigation(u32);
251
252type PendingImageRasterizationKey = (PendingImageId, DeviceIntSize);
253
254#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
258#[derive(JSTraceable, MallocSizeOf)]
259struct PendingLayoutImageAncillaryData {
260 node: Dom<Node>,
261 #[no_trace]
262 destination: LayoutImageDestination,
263}
264
265#[dom_struct]
266pub(crate) struct Window {
267 globalscope: GlobalScope,
268 #[no_trace]
272 webview_id: WebViewId,
273 script_chan: Sender<MainThreadScriptMsg>,
274 #[no_trace]
275 #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"]
276 layout: RefCell<Box<dyn Layout>>,
277 navigator: MutNullableDom<Navigator>,
278 #[ignore_malloc_size_of = "ImageCache"]
279 #[no_trace]
280 image_cache: Arc<dyn ImageCache>,
281 #[no_trace]
282 image_cache_sender: Sender<ImageCacheResponseMessage>,
283 window_proxy: MutNullableDom<WindowProxy>,
284 document: MutNullableDom<Document>,
285 location: MutNullableDom<Location>,
286 history: MutNullableDom<History>,
287 indexeddb: MutNullableDom<IDBFactory>,
288 custom_element_registry: MutNullableDom<CustomElementRegistry>,
289 performance: MutNullableDom<Performance>,
290 #[no_trace]
291 navigation_start: Cell<CrossProcessInstant>,
292 screen: MutNullableDom<Screen>,
293 session_storage: MutNullableDom<Storage>,
294 local_storage: MutNullableDom<Storage>,
295 status: DomRefCell<DOMString>,
296 trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
297
298 ongoing_navigation: Cell<OngoingNavigation>,
301
302 #[no_trace]
305 devtools_markers: DomRefCell<HashSet<TimelineMarkerType>>,
306 #[no_trace]
307 devtools_marker_sender: DomRefCell<Option<IpcSender<Option<TimelineMarker>>>>,
308
309 #[no_trace]
311 unhandled_resize_event: DomRefCell<Option<(ViewportDetails, WindowSizeType)>>,
312
313 #[no_trace]
315 theme: Cell<Theme>,
316
317 #[no_trace]
319 parent_info: Option<PipelineId>,
320
321 dom_static: GlobalStaticData,
323
324 #[conditional_malloc_size_of]
326 js_runtime: DomRefCell<Option<Rc<Runtime>>>,
327
328 #[no_trace]
330 viewport_details: Cell<ViewportDetails>,
331
332 #[no_trace]
334 #[cfg(feature = "bluetooth")]
335 bluetooth_thread: GenericSender<BluetoothRequest>,
336
337 #[cfg(feature = "bluetooth")]
338 bluetooth_extra_permission_data: BluetoothExtraPermissionData,
339
340 #[no_trace]
344 layout_blocker: Cell<LayoutBlocker>,
345
346 #[no_trace]
348 webdriver_script_chan: DomRefCell<Option<IpcSender<WebDriverJSResult>>>,
349
350 #[no_trace]
352 webdriver_load_status_sender: RefCell<Option<GenericSender<WebDriverLoadStatus>>>,
353
354 current_state: Cell<WindowState>,
356
357 error_reporter: CSSErrorReporter,
358
359 media_query_lists: DOMTracker<MediaQueryList>,
361
362 #[cfg(feature = "bluetooth")]
363 test_runner: MutNullableDom<TestRunner>,
364
365 #[ignore_malloc_size_of = "channels are hard"]
367 #[no_trace]
368 webgl_chan: Option<WebGLChan>,
369
370 #[ignore_malloc_size_of = "defined in webxr"]
371 #[no_trace]
372 #[cfg(feature = "webxr")]
373 webxr_registry: Option<webxr_api::Registry>,
374
375 #[no_trace]
379 pending_image_callbacks: DomRefCell<FxHashMap<PendingImageId, Vec<PendingImageCallback>>>,
380
381 pending_layout_images: DomRefCell<
386 HashMapTracedValues<PendingImageId, Vec<PendingLayoutImageAncillaryData>, FxBuildHasher>,
387 >,
388
389 pending_images_for_rasterization: DomRefCell<
393 HashMapTracedValues<PendingImageRasterizationKey, Vec<Dom<Node>>, FxBuildHasher>,
394 >,
395
396 unminified_css_dir: DomRefCell<Option<String>>,
399
400 local_script_source: Option<String>,
402
403 test_worklet: MutNullableDom<Worklet>,
405 paint_worklet: MutNullableDom<Worklet>,
407
408 exists_mut_observer: Cell<bool>,
410
411 #[ignore_malloc_size_of = "Wraps an IpcSender"]
413 #[no_trace]
414 compositor_api: CrossProcessCompositorApi,
415
416 has_sent_idle_message: Cell<bool>,
419
420 unminify_css: bool,
422
423 #[no_trace]
425 user_content_manager: UserContentManager,
426
427 #[ignore_malloc_size_of = "defined in script_thread"]
429 #[no_trace]
430 player_context: WindowGLContext,
431
432 throttled: Cell<bool>,
433
434 #[conditional_malloc_size_of]
438 layout_marker: DomRefCell<Rc<Cell<bool>>>,
439
440 current_event: DomRefCell<Option<Dom<Event>>>,
442
443 reporting_observer_list: DomRefCell<Vec<DomRoot<ReportingObserver>>>,
445
446 report_list: DomRefCell<Vec<Report>>,
448
449 #[no_trace]
451 endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
452
453 #[conditional_malloc_size_of]
455 script_window_proxies: Rc<ScriptWindowProxies>,
456
457 has_pending_screenshot_readiness_request: Cell<bool>,
459}
460
461impl Window {
462 pub(crate) fn webview_id(&self) -> WebViewId {
463 self.webview_id
464 }
465
466 pub(crate) fn as_global_scope(&self) -> &GlobalScope {
467 self.upcast::<GlobalScope>()
468 }
469
470 pub(crate) fn layout(&self) -> Ref<'_, Box<dyn Layout>> {
471 self.layout.borrow()
472 }
473
474 pub(crate) fn layout_mut(&self) -> RefMut<'_, Box<dyn Layout>> {
475 self.layout.borrow_mut()
476 }
477
478 pub(crate) fn get_exists_mut_observer(&self) -> bool {
479 self.exists_mut_observer.get()
480 }
481
482 pub(crate) fn set_exists_mut_observer(&self) {
483 self.exists_mut_observer.set(true);
484 }
485
486 #[expect(unsafe_code)]
487 pub(crate) fn clear_js_runtime_for_script_deallocation(&self) {
488 self.as_global_scope()
489 .remove_web_messaging_and_dedicated_workers_infra();
490 unsafe {
491 *self.js_runtime.borrow_for_script_deallocation() = None;
492 self.window_proxy.set(None);
493 self.current_state.set(WindowState::Zombie);
494 self.as_global_scope()
495 .task_manager()
496 .cancel_all_tasks_and_ignore_future_tasks();
497 }
498 }
499
500 pub(crate) fn discard_browsing_context(&self) {
503 let proxy = match self.window_proxy.get() {
504 Some(proxy) => proxy,
505 None => panic!("Discarding a BC from a window that has none"),
506 };
507 proxy.discard_browsing_context();
508 self.as_global_scope()
512 .task_manager()
513 .cancel_all_tasks_and_ignore_future_tasks();
514 }
515
516 pub(crate) fn time_profiler_chan(&self) -> &TimeProfilerChan {
518 self.globalscope.time_profiler_chan()
519 }
520
521 pub(crate) fn origin(&self) -> &MutableOrigin {
522 self.globalscope.origin()
523 }
524
525 #[expect(unsafe_code)]
526 pub(crate) fn get_cx(&self) -> JSContext {
527 unsafe { JSContext::from_ptr(js::rust::Runtime::get().unwrap().as_ptr()) }
528 }
529
530 pub(crate) fn get_js_runtime(&self) -> Ref<'_, Option<Rc<Runtime>>> {
531 self.js_runtime.borrow()
532 }
533
534 pub(crate) fn main_thread_script_chan(&self) -> &Sender<MainThreadScriptMsg> {
535 &self.script_chan
536 }
537
538 pub(crate) fn parent_info(&self) -> Option<PipelineId> {
539 self.parent_info
540 }
541
542 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
543 let (sender, receiver) = unbounded();
544 (
545 ScriptEventLoopSender::MainThread(sender),
546 ScriptEventLoopReceiver::MainThread(receiver),
547 )
548 }
549
550 pub(crate) fn event_loop_sender(&self) -> ScriptEventLoopSender {
551 ScriptEventLoopSender::MainThread(self.script_chan.clone())
552 }
553
554 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
555 self.image_cache.clone()
556 }
557
558 pub(crate) fn window_proxy(&self) -> DomRoot<WindowProxy> {
560 self.window_proxy.get().unwrap()
561 }
562
563 pub(crate) fn append_reporting_observer(&self, reporting_observer: DomRoot<ReportingObserver>) {
564 self.reporting_observer_list
565 .borrow_mut()
566 .push(reporting_observer);
567 }
568
569 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
570 if let Some(index) = self
571 .reporting_observer_list
572 .borrow()
573 .iter()
574 .position(|observer| &**observer == reporting_observer)
575 {
576 self.reporting_observer_list.borrow_mut().remove(index);
577 }
578 }
579
580 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
581 self.reporting_observer_list.borrow().clone()
582 }
583
584 pub(crate) fn append_report(&self, report: Report) {
585 self.report_list.borrow_mut().push(report);
586 let trusted_window = Trusted::new(self);
587 self.upcast::<GlobalScope>()
588 .task_manager()
589 .dom_manipulation_task_source()
590 .queue(task!(send_to_reporting_endpoints: move || {
591 let window = trusted_window.root();
592 let reports = std::mem::take(&mut *window.report_list.borrow_mut());
593 window.upcast::<GlobalScope>().send_reports_to_endpoints(
594 reports,
595 window.endpoints_list.borrow().clone(),
596 );
597 }));
598 }
599
600 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
601 self.report_list.borrow().clone()
602 }
603
604 pub(crate) fn set_endpoints_list(&self, endpoints: Vec<ReportingEndpoint>) {
605 *self.endpoints_list.borrow_mut() = endpoints;
606 }
607
608 pub(crate) fn undiscarded_window_proxy(&self) -> Option<DomRoot<WindowProxy>> {
611 self.window_proxy.get().and_then(|window_proxy| {
612 if window_proxy.is_browsing_context_discarded() {
613 None
614 } else {
615 Some(window_proxy)
616 }
617 })
618 }
619
620 pub(crate) fn webview_window_proxy(&self) -> Option<DomRoot<WindowProxy>> {
623 self.undiscarded_window_proxy().and_then(|window_proxy| {
624 self.script_window_proxies
625 .find_window_proxy(window_proxy.webview_id().into())
626 })
627 }
628
629 #[cfg(feature = "bluetooth")]
630 pub(crate) fn bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
631 self.bluetooth_thread.clone()
632 }
633
634 #[cfg(feature = "bluetooth")]
635 pub(crate) fn bluetooth_extra_permission_data(&self) -> &BluetoothExtraPermissionData {
636 &self.bluetooth_extra_permission_data
637 }
638
639 pub(crate) fn css_error_reporter(&self) -> &CSSErrorReporter {
640 &self.error_reporter
641 }
642
643 pub(crate) fn webgl_chan(&self) -> Option<WebGLCommandSender> {
644 self.webgl_chan
645 .as_ref()
646 .map(|chan| WebGLCommandSender::new(chan.clone()))
647 }
648
649 #[cfg(feature = "webxr")]
650 pub(crate) fn webxr_registry(&self) -> Option<webxr_api::Registry> {
651 self.webxr_registry.clone()
652 }
653
654 fn new_paint_worklet(&self, can_gc: CanGc) -> DomRoot<Worklet> {
655 debug!("Creating new paint worklet.");
656 Worklet::new(self, WorkletGlobalScopeType::Paint, can_gc)
657 }
658
659 pub(crate) fn register_image_cache_listener(
660 &self,
661 id: PendingImageId,
662 callback: impl Fn(PendingImageResponse) + 'static,
663 ) -> ImageCacheResponseCallback {
664 self.pending_image_callbacks
665 .borrow_mut()
666 .entry(id)
667 .or_default()
668 .push(PendingImageCallback(Box::new(callback)));
669
670 let image_cache_sender = self.image_cache_sender.clone();
671 Box::new(move |message| {
672 let _ = image_cache_sender.send(message);
673 })
674 }
675
676 fn pending_layout_image_notification(&self, response: PendingImageResponse) {
677 let mut images = self.pending_layout_images.borrow_mut();
678 let nodes = images.entry(response.id);
679 let nodes = match nodes {
680 Entry::Occupied(nodes) => nodes,
681 Entry::Vacant(_) => return,
682 };
683 if matches!(
684 response.response,
685 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode
686 ) {
687 for ancillary_data in nodes.get() {
688 match ancillary_data.destination {
689 LayoutImageDestination::BoxTreeConstruction => {
690 ancillary_data.node.dirty(NodeDamage::Other);
691 },
692 LayoutImageDestination::DisplayListBuilding => {
693 self.layout().set_needs_new_display_list();
694 },
695 }
696 }
697 }
698
699 match response.response {
700 ImageResponse::MetadataLoaded(_) => {},
701 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode => {
702 nodes.remove();
703 },
704 }
705 }
706
707 pub(crate) fn handle_image_rasterization_complete_notification(
708 &self,
709 response: RasterizationCompleteResponse,
710 ) {
711 let mut images = self.pending_images_for_rasterization.borrow_mut();
712 let nodes = images.entry((response.image_id, response.requested_size));
713 let nodes = match nodes {
714 Entry::Occupied(nodes) => nodes,
715 Entry::Vacant(_) => return,
716 };
717 for node in nodes.get() {
718 node.dirty(NodeDamage::Other);
719 }
720 nodes.remove();
721 }
722
723 pub(crate) fn pending_image_notification(&self, response: PendingImageResponse) {
724 let mut images = std::mem::take(&mut *self.pending_image_callbacks.borrow_mut());
729 let Entry::Occupied(callbacks) = images.entry(response.id) else {
730 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
731 return;
732 };
733
734 for callback in callbacks.get() {
735 callback.0(response.clone());
736 }
737
738 match response.response {
739 ImageResponse::MetadataLoaded(_) => {},
740 ImageResponse::Loaded(_, _) | ImageResponse::FailedToLoadOrDecode => {
741 callbacks.remove();
742 },
743 }
744
745 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
746 }
747
748 pub(crate) fn compositor_api(&self) -> &CrossProcessCompositorApi {
749 &self.compositor_api
750 }
751
752 pub(crate) fn userscripts(&self) -> &[UserScript] {
753 self.user_content_manager.scripts()
754 }
755
756 pub(crate) fn get_player_context(&self) -> WindowGLContext {
757 self.player_context.clone()
758 }
759
760 pub(crate) fn dispatch_event_with_target_override(&self, event: &Event, can_gc: CanGc) {
762 event.dispatch(self.upcast(), true, can_gc);
763 }
764
765 pub(crate) fn font_context(&self) -> &Arc<FontContext> {
766 self.as_global_scope()
767 .font_context()
768 .expect("A `Window` should always have a `FontContext`")
769 }
770
771 pub(crate) fn ongoing_navigation(&self) -> OngoingNavigation {
772 self.ongoing_navigation.get()
773 }
774
775 pub(crate) fn set_ongoing_navigation(&self) -> OngoingNavigation {
777 let new_value = self.ongoing_navigation.get().0.wrapping_add(1);
781
782 self.ongoing_navigation.set(OngoingNavigation(new_value));
789
790 OngoingNavigation(new_value)
792 }
793
794 fn stop_loading(&self, can_gc: CanGc) {
796 let doc = self.Document();
798
799 self.set_ongoing_navigation();
809
810 doc.abort(can_gc);
812 }
813
814 fn cannot_show_simple_dialogs(&self) -> bool {
816 if self
819 .Document()
820 .has_active_sandboxing_flag(SandboxingFlagSet::SANDBOXED_MODALS_FLAG)
821 {
822 return true;
823 }
824
825 false
844 }
845
846 pub(crate) fn perform_a_microtask_checkpoint(&self, can_gc: CanGc) {
847 with_script_thread(|script_thread| script_thread.perform_a_microtask_checkpoint(can_gc));
848 }
849
850 pub(crate) fn web_font_context(&self) -> WebFontDocumentContext {
851 let global = self.as_global_scope();
852 WebFontDocumentContext {
853 policy_container: global.policy_container(),
854 document_url: global.api_base_url(),
855 has_trustworthy_ancestor_origin: global.has_trustworthy_ancestor_origin(),
856 insecure_requests_policy: global.insecure_requests_policy(),
857 csp_handler: Box::new(FontCspHandler {
858 global: Trusted::new(global),
859 task_source: global
860 .task_manager()
861 .dom_manipulation_task_source()
862 .to_sendable(),
863 }),
864 }
865 }
866}
867
868#[derive(Debug)]
869struct FontCspHandler {
870 global: Trusted<GlobalScope>,
871 task_source: SendableTaskSource,
872}
873
874impl CspViolationHandler for FontCspHandler {
875 fn process_violations(&self, violations: Vec<Violation>) {
876 let global = self.global.clone();
877 self.task_source.queue(task!(csp_violation: move || {
878 global.root().report_csp_violations(violations, None, None);
879 }));
880 }
881
882 fn clone(&self) -> Box<dyn CspViolationHandler> {
883 Box::new(Self {
884 global: self.global.clone(),
885 task_source: self.task_source.clone(),
886 })
887 }
888}
889
890pub(crate) fn base64_btoa(input: DOMString) -> Fallible<DOMString> {
892 if input.str().chars().any(|c: char| c > '\u{FF}') {
896 Err(Error::InvalidCharacter(None))
897 } else {
898 let octets = input
903 .str()
904 .chars()
905 .map(|c: char| c as u8)
906 .collect::<Vec<u8>>();
907
908 let config =
911 base64::engine::general_purpose::GeneralPurposeConfig::new().with_encode_padding(true);
912 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
913 Ok(DOMString::from(engine.encode(octets)))
914 }
915}
916
917pub(crate) fn base64_atob(input: DOMString) -> Fallible<DOMString> {
919 fn is_html_space(c: char) -> bool {
921 HTML_SPACE_CHARACTERS.contains(&c)
922 }
923 let without_spaces = input
924 .str()
925 .chars()
926 .filter(|&c| !is_html_space(c))
927 .collect::<String>();
928 let mut input = &*without_spaces;
929
930 if input.len() % 4 == 0 {
934 if input.ends_with("==") {
935 input = &input[..input.len() - 2]
936 } else if input.ends_with('=') {
937 input = &input[..input.len() - 1]
938 }
939 }
940
941 if input.len() % 4 == 1 {
944 return Err(Error::InvalidCharacter(None));
945 }
946
947 if input
955 .chars()
956 .any(|c| c != '+' && c != '/' && !c.is_alphanumeric())
957 {
958 return Err(Error::InvalidCharacter(None));
959 }
960
961 let config = base64::engine::general_purpose::GeneralPurposeConfig::new()
962 .with_decode_padding_mode(base64::engine::DecodePaddingMode::RequireNone)
963 .with_decode_allow_trailing_bits(true);
964 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
965
966 let data = engine
967 .decode(input)
968 .map_err(|_| Error::InvalidCharacter(None))?;
969 Ok(data.iter().map(|&b| b as char).collect::<String>().into())
970}
971
972impl WindowMethods<crate::DomTypeHolder> for Window {
973 fn Alert_(&self) {
975 self.Alert(DOMString::new());
978 }
979
980 fn Alert(&self, mut message: DOMString) {
982 if self.cannot_show_simple_dialogs() {
984 return;
985 }
986
987 message.normalize_newlines();
991
992 {
1003 let stderr = stderr();
1007 let mut stderr = stderr.lock();
1008 let stdout = stdout();
1009 let mut stdout = stdout.lock();
1010 writeln!(&mut stdout, "\nALERT: {message}").unwrap();
1011 stdout.flush().unwrap();
1012 stderr.flush().unwrap();
1013 }
1014
1015 let (sender, receiver) =
1016 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1017 let dialog = SimpleDialogRequest::Alert {
1018 id: self.Document().embedder_controls().next_control_id(),
1019 message: message.to_string(),
1020 response_sender: sender,
1021 };
1022 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1023 receiver.recv().unwrap_or_else(|_| {
1024 debug!("Alert dialog was cancelled or failed to show.");
1026 AlertResponse::Ok
1027 });
1028
1029 }
1032
1033 fn Confirm(&self, mut message: DOMString) -> bool {
1035 if self.cannot_show_simple_dialogs() {
1037 return false;
1038 }
1039
1040 message.normalize_newlines();
1042
1043 let (sender, receiver) =
1049 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1050 let dialog = SimpleDialogRequest::Confirm {
1051 id: self.Document().embedder_controls().next_control_id(),
1052 message: message.to_string(),
1053 response_sender: sender,
1054 };
1055 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1056
1057 match receiver.recv() {
1073 Ok(ConfirmResponse::Ok) => true,
1074 Ok(ConfirmResponse::Cancel) => false,
1075 Err(_) => {
1076 warn!("Confirm dialog was cancelled or failed to show.");
1077 false
1078 },
1079 }
1080 }
1081
1082 fn Prompt(&self, mut message: DOMString, default: DOMString) -> Option<DOMString> {
1084 if self.cannot_show_simple_dialogs() {
1086 return None;
1087 }
1088
1089 message.normalize_newlines();
1091
1092 let (sender, receiver) =
1100 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
1101 let dialog = SimpleDialogRequest::Prompt {
1102 id: self.Document().embedder_controls().next_control_id(),
1103 message: message.to_string(),
1104 default: default.to_string(),
1105 response_sender: sender,
1106 };
1107 self.send_to_embedder(EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog));
1108
1109 match receiver.recv() {
1128 Ok(PromptResponse::Ok(input)) => Some(input.into()),
1129 Ok(PromptResponse::Cancel) => None,
1130 Err(_) => {
1131 warn!("Prompt dialog was cancelled or failed to show.");
1132 None
1133 },
1134 }
1135 }
1136
1137 fn Stop(&self, can_gc: CanGc) {
1139 self.stop_loading(can_gc);
1144 }
1145
1146 fn Focus(&self) {
1148 let current = match self.undiscarded_window_proxy() {
1152 Some(proxy) => proxy,
1153 None => return,
1154 };
1155
1156 current.focus();
1158
1159 }
1165
1166 fn Blur(&self) {
1168 }
1171
1172 fn Open(
1174 &self,
1175 url: USVString,
1176 target: DOMString,
1177 features: DOMString,
1178 can_gc: CanGc,
1179 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
1180 self.window_proxy().open(url, target, features, can_gc)
1181 }
1182
1183 fn GetOpener(
1185 &self,
1186 cx: JSContext,
1187 in_realm_proof: InRealm,
1188 mut retval: MutableHandleValue,
1189 ) -> Fallible<()> {
1190 let current = match self.window_proxy.get() {
1192 Some(proxy) => proxy,
1193 None => {
1195 retval.set(NullValue());
1196 return Ok(());
1197 },
1198 };
1199 if current.is_browsing_context_discarded() {
1204 retval.set(NullValue());
1205 return Ok(());
1206 }
1207 current.opener(*cx, in_realm_proof, retval);
1209 Ok(())
1210 }
1211
1212 #[expect(unsafe_code)]
1213 fn SetOpener(&self, cx: JSContext, value: HandleValue) -> ErrorResult {
1215 if value.is_null() {
1217 if let Some(proxy) = self.window_proxy.get() {
1218 proxy.disown();
1219 }
1220 return Ok(());
1221 }
1222 let obj = self.reflector().get_jsobject();
1224 unsafe {
1225 let result =
1226 JS_DefineProperty(*cx, obj, c"opener".as_ptr(), value, JSPROP_ENUMERATE as u32);
1227
1228 if result { Ok(()) } else { Err(Error::JSFailed) }
1229 }
1230 }
1231
1232 fn Closed(&self) -> bool {
1234 self.window_proxy
1235 .get()
1236 .map(|ref proxy| proxy.is_browsing_context_discarded() || proxy.is_closing())
1237 .unwrap_or(true)
1238 }
1239
1240 fn Close(&self) {
1242 let window_proxy = match self.window_proxy.get() {
1245 Some(proxy) => proxy,
1246 None => return,
1247 };
1248 if window_proxy.is_closing() {
1249 return;
1250 }
1251 if let Ok(history_length) = self.History().GetLength() {
1254 let is_auxiliary = window_proxy.is_auxiliary();
1255
1256 let is_script_closable = (self.is_top_level() && history_length == 1) ||
1258 is_auxiliary ||
1259 pref!(dom_allow_scripts_to_close_windows);
1260
1261 if is_script_closable {
1265 window_proxy.close();
1267
1268 let this = Trusted::new(self);
1270 let task = task!(window_close_browsing_context: move || {
1271 let window = this.root();
1272 let document = window.Document();
1273 if document.prompt_to_unload(false, CanGc::note()) {
1279 document.unload(false, CanGc::note());
1281
1282 window.discard_browsing_context();
1285
1286 window.send_to_constellation(ScriptToConstellationMessage::DiscardTopLevelBrowsingContext);
1287 }
1288 });
1289 self.as_global_scope()
1290 .task_manager()
1291 .dom_manipulation_task_source()
1292 .queue(task);
1293 }
1294 }
1295 }
1296
1297 fn Document(&self) -> DomRoot<Document> {
1299 self.document
1300 .get()
1301 .expect("Document accessed before initialization.")
1302 }
1303
1304 fn History(&self) -> DomRoot<History> {
1306 self.history.or_init(|| History::new(self, CanGc::note()))
1307 }
1308
1309 fn IndexedDB(&self) -> DomRoot<IDBFactory> {
1311 self.indexeddb.or_init(|| {
1312 let global_scope = self.upcast::<GlobalScope>();
1313 IDBFactory::new(global_scope, CanGc::note())
1314 })
1315 }
1316
1317 fn CustomElements(&self) -> DomRoot<CustomElementRegistry> {
1319 self.custom_element_registry
1320 .or_init(|| CustomElementRegistry::new(self, CanGc::note()))
1321 }
1322
1323 fn Location(&self) -> DomRoot<Location> {
1325 self.location.or_init(|| Location::new(self, CanGc::note()))
1326 }
1327
1328 fn SessionStorage(&self) -> DomRoot<Storage> {
1330 self.session_storage
1331 .or_init(|| Storage::new(self, StorageType::Session, CanGc::note()))
1332 }
1333
1334 fn LocalStorage(&self) -> DomRoot<Storage> {
1336 self.local_storage
1337 .or_init(|| Storage::new(self, StorageType::Local, CanGc::note()))
1338 }
1339
1340 fn CookieStore(&self, can_gc: CanGc) -> DomRoot<CookieStore> {
1342 self.global().cookie_store(can_gc)
1343 }
1344
1345 fn Crypto(&self) -> DomRoot<Crypto> {
1347 self.as_global_scope().crypto(CanGc::note())
1348 }
1349
1350 fn GetFrameElement(&self) -> Option<DomRoot<Element>> {
1352 let window_proxy = self.window_proxy.get()?;
1354
1355 let container = window_proxy.frame_element()?;
1357
1358 let container_doc = container.owner_document();
1360 let current_doc = GlobalScope::current()
1361 .expect("No current global object")
1362 .as_window()
1363 .Document();
1364 if !current_doc
1365 .origin()
1366 .same_origin_domain(container_doc.origin())
1367 {
1368 return None;
1369 }
1370 Some(DomRoot::from_ref(container))
1372 }
1373
1374 fn ReportError(&self, cx: JSContext, error: HandleValue, can_gc: CanGc) {
1376 self.as_global_scope()
1377 .report_an_exception(cx, error, can_gc);
1378 }
1379
1380 fn Navigator(&self) -> DomRoot<Navigator> {
1382 self.navigator
1383 .or_init(|| Navigator::new(self, CanGc::note()))
1384 }
1385
1386 fn SetTimeout(
1388 &self,
1389 _cx: JSContext,
1390 callback: TrustedScriptOrStringOrFunction,
1391 timeout: i32,
1392 args: Vec<HandleValue>,
1393 can_gc: CanGc,
1394 ) -> Fallible<i32> {
1395 let callback = match callback {
1396 TrustedScriptOrStringOrFunction::String(i) => {
1397 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1398 },
1399 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1400 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1401 },
1402 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1403 };
1404 self.as_global_scope().set_timeout_or_interval(
1405 callback,
1406 args,
1407 Duration::from_millis(timeout.max(0) as u64),
1408 IsInterval::NonInterval,
1409 can_gc,
1410 )
1411 }
1412
1413 fn ClearTimeout(&self, handle: i32) {
1415 self.as_global_scope().clear_timeout_or_interval(handle);
1416 }
1417
1418 fn SetInterval(
1420 &self,
1421 _cx: JSContext,
1422 callback: TrustedScriptOrStringOrFunction,
1423 timeout: i32,
1424 args: Vec<HandleValue>,
1425 can_gc: CanGc,
1426 ) -> Fallible<i32> {
1427 let callback = match callback {
1428 TrustedScriptOrStringOrFunction::String(i) => {
1429 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1430 },
1431 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1432 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1433 },
1434 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1435 };
1436 self.as_global_scope().set_timeout_or_interval(
1437 callback,
1438 args,
1439 Duration::from_millis(timeout.max(0) as u64),
1440 IsInterval::Interval,
1441 can_gc,
1442 )
1443 }
1444
1445 fn ClearInterval(&self, handle: i32) {
1447 self.ClearTimeout(handle);
1448 }
1449
1450 fn QueueMicrotask(&self, callback: Rc<VoidFunction>) {
1452 ScriptThread::enqueue_microtask(Microtask::User(UserMicrotask {
1453 callback,
1454 pipeline: self.pipeline_id(),
1455 }));
1456 }
1457
1458 fn CreateImageBitmap(
1460 &self,
1461 image: ImageBitmapSource,
1462 options: &ImageBitmapOptions,
1463 can_gc: CanGc,
1464 ) -> Rc<Promise> {
1465 ImageBitmap::create_image_bitmap(
1466 self.as_global_scope(),
1467 image,
1468 0,
1469 0,
1470 None,
1471 None,
1472 options,
1473 can_gc,
1474 )
1475 }
1476
1477 fn CreateImageBitmap_(
1479 &self,
1480 image: ImageBitmapSource,
1481 sx: i32,
1482 sy: i32,
1483 sw: i32,
1484 sh: i32,
1485 options: &ImageBitmapOptions,
1486 can_gc: CanGc,
1487 ) -> Rc<Promise> {
1488 ImageBitmap::create_image_bitmap(
1489 self.as_global_scope(),
1490 image,
1491 sx,
1492 sy,
1493 Some(sw),
1494 Some(sh),
1495 options,
1496 can_gc,
1497 )
1498 }
1499
1500 fn Window(&self) -> DomRoot<WindowProxy> {
1502 self.window_proxy()
1503 }
1504
1505 fn Self_(&self) -> DomRoot<WindowProxy> {
1507 self.window_proxy()
1508 }
1509
1510 fn Frames(&self) -> DomRoot<WindowProxy> {
1512 self.window_proxy()
1513 }
1514
1515 fn Length(&self) -> u32 {
1517 self.Document().iframes().iter().count() as u32
1518 }
1519
1520 fn GetParent(&self) -> Option<DomRoot<WindowProxy>> {
1522 let window_proxy = self.undiscarded_window_proxy()?;
1524
1525 if let Some(parent) = window_proxy.parent() {
1527 return Some(DomRoot::from_ref(parent));
1528 }
1529 Some(window_proxy)
1531 }
1532
1533 fn GetTop(&self) -> Option<DomRoot<WindowProxy>> {
1535 let window_proxy = self.undiscarded_window_proxy()?;
1537
1538 Some(DomRoot::from_ref(window_proxy.top()))
1540 }
1541
1542 fn Performance(&self) -> DomRoot<Performance> {
1545 self.performance.or_init(|| {
1546 Performance::new(
1547 self.as_global_scope(),
1548 self.navigation_start.get(),
1549 CanGc::note(),
1550 )
1551 })
1552 }
1553
1554 global_event_handlers!();
1556
1557 window_event_handlers!();
1559
1560 fn Screen(&self) -> DomRoot<Screen> {
1562 self.screen.or_init(|| Screen::new(self, CanGc::note()))
1563 }
1564
1565 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
1567 base64_btoa(btoa)
1568 }
1569
1570 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
1572 base64_atob(atob)
1573 }
1574
1575 fn RequestAnimationFrame(&self, callback: Rc<FrameRequestCallback>) -> u32 {
1577 self.Document()
1578 .request_animation_frame(AnimationFrameCallback::FrameRequestCallback { callback })
1579 }
1580
1581 fn CancelAnimationFrame(&self, ident: u32) {
1583 let doc = self.Document();
1584 doc.cancel_animation_frame(ident);
1585 }
1586
1587 fn PostMessage(
1589 &self,
1590 cx: JSContext,
1591 message: HandleValue,
1592 target_origin: USVString,
1593 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1594 ) -> ErrorResult {
1595 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1596 let source = incumbent.as_window();
1597 let source_origin = source.Document().origin().immutable().clone();
1598
1599 self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
1600 }
1601
1602 fn PostMessage_(
1604 &self,
1605 cx: JSContext,
1606 message: HandleValue,
1607 options: RootedTraceableBox<WindowPostMessageOptions>,
1608 ) -> ErrorResult {
1609 let mut rooted = CustomAutoRooter::new(
1610 options
1611 .parent
1612 .transfer
1613 .iter()
1614 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
1615 .collect(),
1616 );
1617 let transfer = CustomAutoRooterGuard::new(*cx, &mut rooted);
1618
1619 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1620 let source = incumbent.as_window();
1621
1622 let source_origin = source.Document().origin().immutable().clone();
1623
1624 self.post_message_impl(
1625 &options.targetOrigin,
1626 source_origin,
1627 source,
1628 cx,
1629 message,
1630 transfer,
1631 )
1632 }
1633
1634 fn CaptureEvents(&self) {
1636 }
1638
1639 fn ReleaseEvents(&self) {
1641 }
1643
1644 fn Debug(&self, message: DOMString) {
1646 debug!("{}", message);
1647 }
1648
1649 #[expect(unsafe_code)]
1650 fn Gc(&self) {
1651 unsafe {
1652 JS_GC(*self.get_cx(), GCReason::API);
1653 }
1654 }
1655
1656 #[expect(unsafe_code)]
1657 fn Js_backtrace(&self) {
1658 unsafe {
1659 println!("Current JS stack:");
1660 dump_js_stack(*self.get_cx());
1661 let rust_stack = Backtrace::new();
1662 println!("Current Rust stack:\n{:?}", rust_stack);
1663 }
1664 }
1665
1666 fn WebdriverCallback(&self, cx: JSContext, value: HandleValue, realm: InRealm, can_gc: CanGc) {
1667 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1668 if let Some(webdriver_script_sender) = webdriver_script_sender {
1669 let result = jsval_to_webdriver(cx, &self.globalscope, value, realm, can_gc);
1670 let _ = webdriver_script_sender.send(result);
1671 }
1672 }
1673
1674 fn WebdriverException(&self, cx: JSContext, value: HandleValue, can_gc: CanGc) {
1675 let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
1676 if let Some(webdriver_script_sender) = webdriver_script_sender {
1677 let _ =
1678 webdriver_script_sender.send(Err(JavaScriptEvaluationError::EvaluationFailure(
1679 Some(javascript_error_info_from_error_info(
1680 cx,
1681 &ErrorInfo::from_value(value, cx, can_gc),
1682 value,
1683 can_gc,
1684 )),
1685 )));
1686 }
1687 }
1688
1689 fn WebdriverElement(&self, id: DOMString) -> Option<DomRoot<Element>> {
1690 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1691 }
1692
1693 fn WebdriverFrame(&self, browsing_context_id: DOMString) -> Option<DomRoot<WindowProxy>> {
1694 self.Document()
1695 .iframes()
1696 .iter()
1697 .find(|iframe| {
1698 iframe
1699 .browsing_context_id()
1700 .as_ref()
1701 .map(BrowsingContextId::to_string) ==
1702 Some(browsing_context_id.to_string())
1703 })
1704 .and_then(|iframe| iframe.GetContentWindow())
1705 }
1706
1707 fn WebdriverWindow(&self, webview_id: DOMString) -> DomRoot<WindowProxy> {
1708 let window_proxy = &self
1709 .window_proxy
1710 .get()
1711 .expect("Should always have a WindowProxy when calling WebdriverWindow");
1712 assert!(window_proxy.browsing_context_id() == window_proxy.webview_id());
1714 assert!(self.webview_id().to_string() == webview_id);
1715 DomRoot::from_ref(window_proxy)
1716 }
1717
1718 fn WebdriverShadowRoot(&self, id: DOMString) -> Option<DomRoot<ShadowRoot>> {
1719 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1720 }
1721
1722 fn GetComputedStyle(
1724 &self,
1725 element: &Element,
1726 pseudo: Option<DOMString>,
1727 ) -> DomRoot<CSSStyleDeclaration> {
1728 let mut is_null = false;
1732
1733 let pseudo = pseudo.map(|mut s| {
1736 s.make_ascii_lowercase();
1737 s
1738 });
1739 let pseudo = match pseudo {
1740 Some(ref pseudo) if pseudo == ":before" || pseudo == "::before" => {
1741 Some(PseudoElement::Before)
1742 },
1743 Some(ref pseudo) if pseudo == ":after" || pseudo == "::after" => {
1744 Some(PseudoElement::After)
1745 },
1746 Some(ref pseudo) if pseudo == "::selection" => Some(PseudoElement::Selection),
1747 Some(ref pseudo) if pseudo == "::marker" => Some(PseudoElement::Marker),
1748 Some(ref pseudo) if pseudo.starts_with(':') => {
1749 is_null = true;
1752 None
1753 },
1754 _ => None,
1755 };
1756
1757 CSSStyleDeclaration::new(
1773 self,
1774 if is_null {
1775 CSSStyleOwner::Null
1776 } else {
1777 CSSStyleOwner::Element(Dom::from_ref(element))
1778 },
1779 pseudo,
1780 CSSModificationAccess::Readonly,
1781 CanGc::note(),
1782 )
1783 }
1784
1785 fn InnerHeight(&self) -> i32 {
1788 self.viewport_details
1789 .get()
1790 .size
1791 .height
1792 .to_i32()
1793 .unwrap_or(0)
1794 }
1795
1796 fn InnerWidth(&self) -> i32 {
1799 self.viewport_details.get().size.width.to_i32().unwrap_or(0)
1800 }
1801
1802 fn ScrollX(&self) -> i32 {
1804 self.scroll_offset().x as i32
1805 }
1806
1807 fn PageXOffset(&self) -> i32 {
1809 self.ScrollX()
1810 }
1811
1812 fn ScrollY(&self) -> i32 {
1814 self.scroll_offset().y as i32
1815 }
1816
1817 fn PageYOffset(&self) -> i32 {
1819 self.ScrollY()
1820 }
1821
1822 fn Scroll(&self, options: &ScrollToOptions) {
1824 let x = options.left.unwrap_or(0.0) as f32;
1829
1830 let y = options.top.unwrap_or(0.0) as f32;
1833
1834 self.scroll(x, y, options.parent.behavior);
1836 }
1837
1838 fn Scroll_(&self, x: f64, y: f64) {
1840 self.scroll(x as f32, y as f32, ScrollBehavior::Auto);
1844 }
1845
1846 fn ScrollTo(&self, options: &ScrollToOptions) {
1851 self.Scroll(options);
1852 }
1853
1854 fn ScrollTo_(&self, x: f64, y: f64) {
1859 self.Scroll_(x, y)
1860 }
1861
1862 fn ScrollBy(&self, options: &ScrollToOptions) {
1864 let mut options = options.clone();
1870 let x = options.left.unwrap_or(0.0);
1871 let x = if x.is_finite() { x } else { 0.0 };
1872 let y = options.top.unwrap_or(0.0);
1873 let y = if y.is_finite() { y } else { 0.0 };
1874
1875 options.left.replace(x + self.ScrollX() as f64);
1877
1878 options.top.replace(y + self.ScrollY() as f64);
1880
1881 self.Scroll(&options)
1883 }
1884
1885 fn ScrollBy_(&self, x: f64, y: f64) {
1887 let mut options = ScrollToOptions::empty();
1891
1892 options.left.replace(x);
1895
1896 options.top.replace(y);
1898
1899 self.ScrollBy(&options);
1901 }
1902
1903 fn ResizeTo(&self, width: i32, height: i32) {
1905 let window_proxy = match self.window_proxy.get() {
1907 Some(proxy) => proxy,
1908 None => return,
1909 };
1910
1911 if !window_proxy.is_auxiliary() {
1914 return;
1915 }
1916
1917 let dpr = self.device_pixel_ratio();
1918 let size = Size2D::new(width, height).to_f32() * dpr;
1919 self.send_to_embedder(EmbedderMsg::ResizeTo(self.webview_id(), size.to_i32()));
1920 }
1921
1922 fn ResizeBy(&self, x: i32, y: i32) {
1924 let size = self.client_window().size();
1925 self.ResizeTo(x + size.width, y + size.height)
1927 }
1928
1929 fn MoveTo(&self, x: i32, y: i32) {
1931 let dpr = self.device_pixel_ratio();
1934 let point = Point2D::new(x, y).to_f32() * dpr;
1935 let msg = EmbedderMsg::MoveTo(self.webview_id(), point.to_i32());
1936 self.send_to_embedder(msg);
1937 }
1938
1939 fn MoveBy(&self, x: i32, y: i32) {
1941 let origin = self.client_window().min;
1942 self.MoveTo(x + origin.x, y + origin.y)
1944 }
1945
1946 fn ScreenX(&self) -> i32 {
1948 self.client_window().min.x
1949 }
1950
1951 fn ScreenY(&self) -> i32 {
1953 self.client_window().min.y
1954 }
1955
1956 fn OuterHeight(&self) -> i32 {
1958 self.client_window().height()
1959 }
1960
1961 fn OuterWidth(&self) -> i32 {
1963 self.client_window().width()
1964 }
1965
1966 fn DevicePixelRatio(&self) -> Finite<f64> {
1968 Finite::wrap(self.device_pixel_ratio().get() as f64)
1969 }
1970
1971 fn Status(&self) -> DOMString {
1973 self.status.borrow().clone()
1974 }
1975
1976 fn SetStatus(&self, status: DOMString) {
1978 *self.status.borrow_mut() = status
1979 }
1980
1981 fn MatchMedia(&self, query: DOMString) -> DomRoot<MediaQueryList> {
1983 let media_query_list = MediaList::parse_media_list(&query.str(), self);
1984 let document = self.Document();
1985 let mql = MediaQueryList::new(&document, media_query_list, CanGc::note());
1986 self.media_query_lists.track(&*mql);
1987 mql
1988 }
1989
1990 fn Fetch(
1992 &self,
1993 input: RequestOrUSVString,
1994 init: RootedTraceableBox<RequestInit>,
1995 comp: InRealm,
1996 can_gc: CanGc,
1997 ) -> Rc<Promise> {
1998 fetch::Fetch(self.upcast(), input, init, comp, can_gc)
1999 }
2000
2001 fn FetchLater(
2003 &self,
2004 input: RequestInfo,
2005 init: RootedTraceableBox<DeferredRequestInit>,
2006 can_gc: CanGc,
2007 ) -> Fallible<DomRoot<FetchLaterResult>> {
2008 fetch::FetchLater(self, input, init, can_gc)
2009 }
2010
2011 #[cfg(feature = "bluetooth")]
2012 fn TestRunner(&self) -> DomRoot<TestRunner> {
2013 self.test_runner
2014 .or_init(|| TestRunner::new(self.upcast(), CanGc::note()))
2015 }
2016
2017 fn RunningAnimationCount(&self) -> u32 {
2018 self.document
2019 .get()
2020 .map_or(0, |d| d.animations().running_animation_count() as u32)
2021 }
2022
2023 fn SetName(&self, name: DOMString) {
2025 if let Some(proxy) = self.undiscarded_window_proxy() {
2026 proxy.set_name(name);
2027 }
2028 }
2029
2030 fn Name(&self) -> DOMString {
2032 match self.undiscarded_window_proxy() {
2033 Some(proxy) => proxy.get_name(),
2034 None => "".into(),
2035 }
2036 }
2037
2038 fn Origin(&self) -> USVString {
2040 USVString(self.origin().immutable().ascii_serialization())
2041 }
2042
2043 fn GetSelection(&self) -> Option<DomRoot<Selection>> {
2045 self.document
2046 .get()
2047 .and_then(|d| d.GetSelection(CanGc::note()))
2048 }
2049
2050 fn Event(&self, cx: JSContext, rval: MutableHandleValue) {
2052 if let Some(ref event) = *self.current_event.borrow() {
2053 event
2054 .reflector()
2055 .get_jsobject()
2056 .safe_to_jsval(cx, rval, CanGc::note());
2057 }
2058 }
2059
2060 fn IsSecureContext(&self) -> bool {
2061 self.as_global_scope().is_secure_context()
2062 }
2063
2064 fn NamedGetter(&self, name: DOMString) -> Option<NamedPropertyValue> {
2066 if name.is_empty() {
2067 return None;
2068 }
2069 let document = self.Document();
2070
2071 let iframes: Vec<_> = document
2073 .iframes()
2074 .iter()
2075 .filter(|iframe| {
2076 if let Some(window) = iframe.GetContentWindow() {
2077 return window.get_name() == name;
2078 }
2079 false
2080 })
2081 .collect();
2082
2083 let iframe_iter = iframes.iter().map(|iframe| iframe.upcast::<Element>());
2084
2085 let name = Atom::from(name);
2086
2087 let elements_with_name = document.get_elements_with_name(&name);
2089 let name_iter = elements_with_name
2090 .iter()
2091 .map(|element| &**element)
2092 .filter(|elem| is_named_element_with_name_attribute(elem));
2093 let elements_with_id = document.get_elements_with_id(&name);
2094 let id_iter = elements_with_id
2095 .iter()
2096 .map(|element| &**element)
2097 .filter(|elem| is_named_element_with_id_attribute(elem));
2098
2099 for elem in iframe_iter.clone() {
2101 if let Some(nested_window_proxy) = elem
2102 .downcast::<HTMLIFrameElement>()
2103 .and_then(|iframe| iframe.GetContentWindow())
2104 {
2105 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
2106 }
2107 }
2108
2109 let mut elements = iframe_iter.chain(name_iter).chain(id_iter);
2110
2111 let first = elements.next()?;
2112
2113 if elements.next().is_none() {
2114 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
2116 }
2117
2118 #[derive(JSTraceable, MallocSizeOf)]
2120 struct WindowNamedGetter {
2121 #[no_trace]
2122 name: Atom,
2123 }
2124 impl CollectionFilter for WindowNamedGetter {
2125 fn filter(&self, elem: &Element, _root: &Node) -> bool {
2126 let type_ = match elem.upcast::<Node>().type_id() {
2127 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
2128 _ => return false,
2129 };
2130 if elem.get_id().as_ref() == Some(&self.name) {
2131 return true;
2132 }
2133 match type_ {
2134 HTMLElementTypeId::HTMLEmbedElement |
2135 HTMLElementTypeId::HTMLFormElement |
2136 HTMLElementTypeId::HTMLImageElement |
2137 HTMLElementTypeId::HTMLObjectElement => {
2138 elem.get_name().as_ref() == Some(&self.name)
2139 },
2140 _ => false,
2141 }
2142 }
2143 }
2144 let collection = HTMLCollection::create(
2145 self,
2146 document.upcast(),
2147 Box::new(WindowNamedGetter { name }),
2148 CanGc::note(),
2149 );
2150 Some(NamedPropertyValue::HTMLCollection(collection))
2151 }
2152
2153 fn SupportedPropertyNames(&self) -> Vec<DOMString> {
2155 let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
2156
2157 let document = self.Document();
2158 let name_map = document.name_map();
2159 for (name, elements) in &name_map.0 {
2160 if name.is_empty() {
2161 continue;
2162 }
2163 let mut name_iter = elements
2164 .iter()
2165 .filter(|elem| is_named_element_with_name_attribute(elem));
2166 if let Some(first) = name_iter.next() {
2167 names_with_first_named_element_map.insert(name, first);
2168 }
2169 }
2170 let id_map = document.id_map();
2171 for (id, elements) in &id_map.0 {
2172 if id.is_empty() {
2173 continue;
2174 }
2175 let mut id_iter = elements
2176 .iter()
2177 .filter(|elem| is_named_element_with_id_attribute(elem));
2178 if let Some(first) = id_iter.next() {
2179 match names_with_first_named_element_map.entry(id) {
2180 Entry::Vacant(entry) => drop(entry.insert(first)),
2181 Entry::Occupied(mut entry) => {
2182 if first.upcast::<Node>().is_before(entry.get().upcast()) {
2183 *entry.get_mut() = first;
2184 }
2185 },
2186 }
2187 }
2188 }
2189
2190 let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
2191 names_with_first_named_element_map
2192 .iter()
2193 .map(|(k, v)| (*k, *v))
2194 .collect();
2195 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
2196 if a.1 == b.1 {
2197 a.0.cmp(b.0)
2200 } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
2201 cmp::Ordering::Less
2202 } else {
2203 cmp::Ordering::Greater
2204 }
2205 });
2206
2207 names_with_first_named_element_vec
2208 .iter()
2209 .map(|(k, _v)| DOMString::from(&***k))
2210 .collect()
2211 }
2212
2213 fn StructuredClone(
2215 &self,
2216 cx: JSContext,
2217 value: HandleValue,
2218 options: RootedTraceableBox<StructuredSerializeOptions>,
2219 can_gc: CanGc,
2220 retval: MutableHandleValue,
2221 ) -> Fallible<()> {
2222 self.as_global_scope()
2223 .structured_clone(cx, value, options, retval, can_gc)
2224 }
2225
2226 fn TrustedTypes(&self, can_gc: CanGc) -> DomRoot<TrustedTypePolicyFactory> {
2227 self.trusted_types
2228 .or_init(|| TrustedTypePolicyFactory::new(self.as_global_scope(), can_gc))
2229 }
2230}
2231
2232impl Window {
2233 pub(crate) fn scroll_offset(&self) -> Vector2D<f32, LayoutPixel> {
2234 self.scroll_offset_query_with_external_scroll_id(self.pipeline_id().root_scroll_id())
2235 }
2236
2237 pub(crate) fn create_named_properties_object(
2240 cx: JSContext,
2241 proto: HandleObject,
2242 object: MutableHandleObject,
2243 ) {
2244 window_named_properties::create(cx, proto, object)
2245 }
2246
2247 pub(crate) fn current_event(&self) -> Option<DomRoot<Event>> {
2248 self.current_event
2249 .borrow()
2250 .as_ref()
2251 .map(|e| DomRoot::from_ref(&**e))
2252 }
2253
2254 pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
2255 let current = self.current_event();
2256 *self.current_event.borrow_mut() = event.map(Dom::from_ref);
2257 current
2258 }
2259
2260 fn post_message_impl(
2262 &self,
2263 target_origin: &USVString,
2264 source_origin: ImmutableOrigin,
2265 source: &Window,
2266 cx: JSContext,
2267 message: HandleValue,
2268 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
2269 ) -> ErrorResult {
2270 let data = structuredclone::write(cx, message, Some(transfer))?;
2272
2273 let target_origin = match target_origin.0[..].as_ref() {
2275 "*" => None,
2276 "/" => Some(source_origin.clone()),
2277 url => match ServoUrl::parse(url) {
2278 Ok(url) => Some(url.origin().clone()),
2279 Err(_) => return Err(Error::Syntax(None)),
2280 },
2281 };
2282
2283 self.post_message(target_origin, source_origin, &source.window_proxy(), data);
2285 Ok(())
2286 }
2287
2288 pub(crate) fn paint_worklet(&self) -> DomRoot<Worklet> {
2290 self.paint_worklet
2291 .or_init(|| self.new_paint_worklet(CanGc::note()))
2292 }
2293
2294 pub(crate) fn has_document(&self) -> bool {
2295 self.document.get().is_some()
2296 }
2297
2298 pub(crate) fn clear_js_runtime(&self) {
2299 self.as_global_scope()
2300 .remove_web_messaging_and_dedicated_workers_infra();
2301
2302 if let Some(custom_elements) = self.custom_element_registry.get() {
2305 custom_elements.teardown();
2306 }
2307
2308 self.current_state.set(WindowState::Zombie);
2309 *self.js_runtime.borrow_mut() = None;
2310
2311 if let Some(proxy) = self.window_proxy.get() {
2314 let pipeline_id = self.pipeline_id();
2315 if let Some(currently_active) = proxy.currently_active() {
2316 if currently_active == pipeline_id {
2317 self.window_proxy.set(None);
2318 }
2319 }
2320 }
2321
2322 if let Some(performance) = self.performance.get() {
2323 performance.clear_and_disable_performance_entry_buffer();
2324 }
2325 self.as_global_scope()
2326 .task_manager()
2327 .cancel_all_tasks_and_ignore_future_tasks();
2328 }
2329
2330 pub(crate) fn scroll(&self, x: f32, y: f32, behavior: ScrollBehavior) {
2332 let xfinite = if x.is_finite() { x } else { 0.0 };
2334 let yfinite = if y.is_finite() { y } else { 0.0 };
2335
2336 let viewport = self.viewport_details.get().size;
2346
2347 let scrolling_area = self.scrolling_area_query(None).to_f32();
2366 let x = xfinite.clamp(0.0, 0.0f32.max(scrolling_area.width() - viewport.width));
2367 let y = yfinite.clamp(0.0, 0.0f32.max(scrolling_area.height() - viewport.height));
2368
2369 let scroll_offset = self.scroll_offset();
2372 if x == scroll_offset.x && y == scroll_offset.y {
2373 return;
2374 }
2375
2376 self.perform_a_scroll(x, y, self.pipeline_id().root_scroll_id(), behavior, None);
2381 }
2382
2383 pub(crate) fn perform_a_scroll(
2385 &self,
2386 x: f32,
2387 y: f32,
2388 scroll_id: ExternalScrollId,
2389 _behavior: ScrollBehavior,
2390 element: Option<&Element>,
2391 ) {
2392 let reflow_phases_run =
2396 self.reflow(ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)));
2397 if reflow_phases_run.needs_frame() {
2398 self.compositor_api()
2399 .generate_frame(vec![self.webview_id().into()]);
2400 }
2401
2402 if reflow_phases_run.contains(ReflowPhasesRun::UpdatedScrollNodeOffset) {
2407 match element {
2408 Some(el) => self.Document().handle_element_scroll_event(el),
2409 None => self.Document().handle_viewport_scroll_event(),
2410 };
2411 }
2412 }
2413
2414 pub(crate) fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
2415 self.viewport_details.get().hidpi_scale_factor
2416 }
2417
2418 fn client_window(&self) -> DeviceIndependentIntRect {
2419 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel!");
2420
2421 self.send_to_embedder(EmbedderMsg::GetWindowRect(self.webview_id(), sender));
2422
2423 receiver.recv().unwrap_or_default()
2424 }
2425
2426 pub(crate) fn advance_animation_clock(&self, delta_ms: i32) {
2429 self.Document()
2430 .advance_animation_timeline_for_testing(delta_ms as f64 / 1000.);
2431 ScriptThread::handle_tick_all_animations_for_testing(self.pipeline_id());
2432 }
2433
2434 pub(crate) fn reflow(&self, reflow_goal: ReflowGoal) -> ReflowPhasesRun {
2442 let document = self.Document();
2443
2444 if !document.is_fully_active() {
2446 return ReflowPhasesRun::empty();
2447 }
2448
2449 self.Document().ensure_safe_to_run_script_or_layout();
2450
2451 let pipeline_id = self.pipeline_id();
2455 if reflow_goal == ReflowGoal::UpdateTheRendering &&
2456 self.layout_blocker.get().layout_blocked()
2457 {
2458 debug!("Suppressing pre-load-event reflow pipeline {pipeline_id}");
2459 return ReflowPhasesRun::empty();
2460 }
2461
2462 debug!("script: performing reflow for goal {reflow_goal:?}");
2463 let marker = if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) {
2464 Some(TimelineMarker::start("Reflow".to_owned()))
2465 } else {
2466 None
2467 };
2468
2469 let restyle_reason = document.restyle_reason();
2470 document.clear_restyle_reasons();
2471 let restyle = if restyle_reason.needs_restyle() {
2472 debug!("Invalidating layout cache due to reflow condition {restyle_reason:?}",);
2473 self.layout_marker.borrow().set(false);
2475 *self.layout_marker.borrow_mut() = Rc::new(Cell::new(true));
2477
2478 let stylesheets_changed = document.flush_stylesheets_for_reflow();
2479 let pending_restyles = document.drain_pending_restyles();
2480 let dirty_root = document
2481 .take_dirty_root()
2482 .filter(|_| !stylesheets_changed)
2483 .or_else(|| document.GetDocumentElement())
2484 .map(|root| root.upcast::<Node>().to_trusted_node_address());
2485
2486 Some(ReflowRequestRestyle {
2487 reason: restyle_reason,
2488 dirty_root,
2489 stylesheets_changed,
2490 pending_restyles,
2491 })
2492 } else {
2493 None
2494 };
2495
2496 let document_context = self.web_font_context();
2497
2498 let reflow = ReflowRequest {
2500 document: document.upcast::<Node>().to_trusted_node_address(),
2501 epoch: document.current_rendering_epoch(),
2502 restyle,
2503 viewport_details: self.viewport_details.get(),
2504 origin: self.origin().immutable().clone(),
2505 reflow_goal,
2506 dom_count: document.dom_count(),
2507 animation_timeline_value: document.current_animation_timeline_value(),
2508 animations: document.animations().sets.clone(),
2509 animating_images: document.image_animation_manager().animating_images(),
2510 highlighted_dom_node: document.highlighted_dom_node().map(|node| node.to_opaque()),
2511 document_context,
2512 };
2513
2514 let Some(reflow_result) = self.layout.borrow_mut().reflow(reflow) else {
2515 return ReflowPhasesRun::empty();
2516 };
2517
2518 debug!("script: layout complete");
2519 if let Some(marker) = marker {
2520 self.emit_timeline_marker(marker.end());
2521 }
2522
2523 self.handle_pending_images_post_reflow(
2524 reflow_result.pending_images,
2525 reflow_result.pending_rasterization_images,
2526 reflow_result.pending_svg_elements_for_serialization,
2527 );
2528
2529 if let Some(iframe_sizes) = reflow_result.iframe_sizes {
2530 document
2531 .iframes_mut()
2532 .handle_new_iframe_sizes_after_layout(self, iframe_sizes);
2533 }
2534
2535 document.update_animations_post_reflow();
2536
2537 reflow_result.reflow_phases_run
2538 }
2539
2540 pub(crate) fn request_screenshot_readiness(&self, can_gc: CanGc) {
2541 self.has_pending_screenshot_readiness_request.set(true);
2542 self.maybe_resolve_pending_screenshot_readiness_requests(can_gc);
2543 }
2544
2545 pub(crate) fn maybe_resolve_pending_screenshot_readiness_requests(&self, can_gc: CanGc) {
2546 let pending_request = self.has_pending_screenshot_readiness_request.get();
2547 if !pending_request {
2548 return;
2549 }
2550
2551 let document = self.Document();
2552 if document.ReadyState() != DocumentReadyState::Complete {
2553 return;
2554 }
2555
2556 if document.render_blocking_element_count() > 0 {
2557 return;
2558 }
2559
2560 if document.GetDocumentElement().is_some_and(|elem| {
2564 elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
2565 elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
2566 }) {
2567 return;
2568 }
2569
2570 if self.font_context().web_fonts_still_loading() != 0 {
2571 return;
2572 }
2573
2574 if self.Document().Fonts(can_gc).waiting_to_fullfill_promise() {
2575 return;
2576 }
2577
2578 if !self.pending_layout_images.borrow().is_empty() ||
2579 !self.pending_images_for_rasterization.borrow().is_empty()
2580 {
2581 return;
2582 }
2583
2584 let document = self.Document();
2585 if document.needs_rendering_update() {
2586 return;
2587 }
2588
2589 let epoch = document.current_rendering_epoch();
2592 let pipeline_id = self.pipeline_id();
2593 debug!("Ready to take screenshot of {pipeline_id:?} at epoch={epoch:?}");
2594
2595 self.send_to_constellation(
2596 ScriptToConstellationMessage::RespondToScreenshotReadinessRequest(
2597 ScreenshotReadinessResponse::Ready(epoch),
2598 ),
2599 );
2600 self.has_pending_screenshot_readiness_request.set(false);
2601 }
2602
2603 pub(crate) fn reflow_if_reflow_timer_expired(&self) {
2606 if !matches!(
2609 self.layout_blocker.get(),
2610 LayoutBlocker::Parsing(instant) if instant + INITIAL_REFLOW_DELAY < Instant::now()
2611 ) {
2612 return;
2613 }
2614 self.allow_layout_if_necessary();
2615 }
2616
2617 pub(crate) fn prevent_layout_until_load_event(&self) {
2621 if !matches!(self.layout_blocker.get(), LayoutBlocker::WaitingForParse) {
2624 return;
2625 }
2626
2627 self.layout_blocker
2628 .set(LayoutBlocker::Parsing(Instant::now()));
2629 }
2630
2631 pub(crate) fn allow_layout_if_necessary(&self) {
2634 if matches!(
2635 self.layout_blocker.get(),
2636 LayoutBlocker::FiredLoadEventOrParsingTimerExpired
2637 ) {
2638 return;
2639 }
2640
2641 self.layout_blocker
2642 .set(LayoutBlocker::FiredLoadEventOrParsingTimerExpired);
2643
2644 if self.Document().update_the_rendering().needs_frame() {
2656 self.compositor_api()
2657 .generate_frame(vec![self.webview_id().into()]);
2658 }
2659 }
2660
2661 pub(crate) fn layout_blocked(&self) -> bool {
2662 self.layout_blocker.get().layout_blocked()
2663 }
2664
2665 pub(crate) fn layout_reflow(&self, query_msg: QueryMsg) {
2667 self.reflow(ReflowGoal::LayoutQuery(query_msg));
2668 }
2669
2670 pub(crate) fn resolved_font_style_query(
2671 &self,
2672 node: &Node,
2673 value: String,
2674 ) -> Option<ServoArc<Font>> {
2675 self.layout_reflow(QueryMsg::ResolvedFontStyleQuery);
2676
2677 let document = self.Document();
2678 let animations = document.animations().sets.clone();
2679 self.layout.borrow().query_resolved_font_style(
2680 node.to_trusted_node_address(),
2681 &value,
2682 animations,
2683 document.current_animation_timeline_value(),
2684 )
2685 }
2686
2687 pub(crate) fn padding_query_without_reflow(&self, node: &Node) -> Option<PhysicalSides> {
2692 let layout = self.layout.borrow();
2693 layout.query_padding(node.to_trusted_node_address())
2694 }
2695
2696 pub(crate) fn box_area_query_without_reflow(
2701 &self,
2702 node: &Node,
2703 area: BoxAreaType,
2704 exclude_transform_and_inline: bool,
2705 ) -> Option<UntypedRect<Au>> {
2706 let layout = self.layout.borrow();
2707 layout.ensure_stacking_context_tree(self.viewport_details.get());
2708 layout.query_box_area(
2709 node.to_trusted_node_address(),
2710 area,
2711 exclude_transform_and_inline,
2712 )
2713 }
2714
2715 pub(crate) fn box_area_query(
2716 &self,
2717 node: &Node,
2718 area: BoxAreaType,
2719 exclude_transform_and_inline: bool,
2720 ) -> Option<UntypedRect<Au>> {
2721 self.layout_reflow(QueryMsg::BoxArea);
2722 self.box_area_query_without_reflow(node, area, exclude_transform_and_inline)
2723 }
2724
2725 pub(crate) fn box_areas_query(&self, node: &Node, area: BoxAreaType) -> Vec<UntypedRect<Au>> {
2726 self.layout_reflow(QueryMsg::BoxAreas);
2727 self.layout
2728 .borrow()
2729 .query_box_areas(node.to_trusted_node_address(), area)
2730 }
2731
2732 pub(crate) fn client_rect_query(&self, node: &Node) -> UntypedRect<i32> {
2733 self.layout_reflow(QueryMsg::ClientRectQuery);
2734 self.layout
2735 .borrow()
2736 .query_client_rect(node.to_trusted_node_address())
2737 }
2738
2739 pub(crate) fn current_css_zoom_query(&self, node: &Node) -> f32 {
2740 self.layout_reflow(QueryMsg::CurrentCSSZoomQuery);
2741 self.layout
2742 .borrow()
2743 .query_current_css_zoom(node.to_trusted_node_address())
2744 }
2745
2746 pub(crate) fn scrolling_area_query(&self, node: Option<&Node>) -> UntypedRect<i32> {
2749 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2750 self.layout
2751 .borrow()
2752 .query_scrolling_area(node.map(Node::to_trusted_node_address))
2753 }
2754
2755 pub(crate) fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32, LayoutPixel> {
2756 let external_scroll_id = ExternalScrollId(
2757 combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody),
2758 self.pipeline_id().into(),
2759 );
2760 self.scroll_offset_query_with_external_scroll_id(external_scroll_id)
2761 }
2762
2763 fn scroll_offset_query_with_external_scroll_id(
2764 &self,
2765 external_scroll_id: ExternalScrollId,
2766 ) -> Vector2D<f32, LayoutPixel> {
2767 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2768 self.scroll_offset_query_with_external_scroll_id_no_reflow(external_scroll_id)
2769 }
2770
2771 fn scroll_offset_query_with_external_scroll_id_no_reflow(
2772 &self,
2773 external_scroll_id: ExternalScrollId,
2774 ) -> Vector2D<f32, LayoutPixel> {
2775 self.layout
2776 .borrow()
2777 .scroll_offset(external_scroll_id)
2778 .unwrap_or_default()
2779 }
2780
2781 pub(crate) fn scroll_an_element(
2784 &self,
2785 element: &Element,
2786 x: f32,
2787 y: f32,
2788 behavior: ScrollBehavior,
2789 ) {
2790 let scroll_id = ExternalScrollId(
2791 combine_id_with_fragment_type(
2792 element.upcast::<Node>().to_opaque().id(),
2793 FragmentType::FragmentBody,
2794 ),
2795 self.pipeline_id().into(),
2796 );
2797
2798 self.perform_a_scroll(x, y, scroll_id, behavior, Some(element));
2802 }
2803
2804 pub(crate) fn resolved_style_query(
2805 &self,
2806 element: TrustedNodeAddress,
2807 pseudo: Option<PseudoElement>,
2808 property: PropertyId,
2809 ) -> DOMString {
2810 self.layout_reflow(QueryMsg::ResolvedStyleQuery);
2811
2812 let document = self.Document();
2813 let animations = document.animations().sets.clone();
2814 DOMString::from(self.layout.borrow().query_resolved_style(
2815 element,
2816 pseudo,
2817 property,
2818 animations,
2819 document.current_animation_timeline_value(),
2820 ))
2821 }
2822
2823 pub(crate) fn get_iframe_viewport_details_if_known(
2827 &self,
2828 browsing_context_id: BrowsingContextId,
2829 ) -> Option<ViewportDetails> {
2830 self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery);
2832 self.Document()
2833 .iframes()
2834 .get(browsing_context_id)
2835 .and_then(|iframe| iframe.size)
2836 }
2837
2838 #[expect(unsafe_code)]
2839 pub(crate) fn offset_parent_query(
2840 &self,
2841 node: &Node,
2842 ) -> (Option<DomRoot<Element>>, UntypedRect<Au>) {
2843 self.layout_reflow(QueryMsg::OffsetParentQuery);
2844 let response = self
2845 .layout
2846 .borrow()
2847 .query_offset_parent(node.to_trusted_node_address());
2848 let element = response.node_address.and_then(|parent_node_address| {
2849 let node = unsafe { from_untrusted_node_address(parent_node_address) };
2850 DomRoot::downcast(node)
2851 });
2852 (element, response.rect)
2853 }
2854
2855 pub(crate) fn scroll_container_query(
2856 &self,
2857 node: Option<&Node>,
2858 flags: ScrollContainerQueryFlags,
2859 ) -> Option<ScrollContainerResponse> {
2860 self.layout_reflow(QueryMsg::ScrollParentQuery);
2861 self.layout
2862 .borrow()
2863 .query_scroll_container(node.map(Node::to_trusted_node_address), flags)
2864 }
2865
2866 #[expect(unsafe_code)]
2867 pub(crate) fn scrolling_box_query(
2868 &self,
2869 node: Option<&Node>,
2870 flags: ScrollContainerQueryFlags,
2871 ) -> Option<ScrollingBox> {
2872 self.scroll_container_query(node, flags)
2873 .and_then(|response| {
2874 Some(match response {
2875 ScrollContainerResponse::Viewport(overflow) => {
2876 (ScrollingBoxSource::Viewport(self.Document()), overflow)
2877 },
2878 ScrollContainerResponse::Element(parent_node_address, overflow) => {
2879 let node = unsafe { from_untrusted_node_address(parent_node_address) };
2880 (
2881 ScrollingBoxSource::Element(DomRoot::downcast(node)?),
2882 overflow,
2883 )
2884 },
2885 })
2886 })
2887 .map(|(source, overflow)| ScrollingBox::new(source, overflow))
2888 }
2889
2890 pub(crate) fn text_index_query(
2891 &self,
2892 node: &Node,
2893 point_in_node: UntypedPoint2D<f32>,
2894 ) -> Option<usize> {
2895 self.layout_reflow(QueryMsg::TextIndexQuery);
2896 self.layout
2897 .borrow()
2898 .query_text_indext(node.to_opaque(), point_in_node)
2899 }
2900
2901 pub(crate) fn elements_from_point_query(
2902 &self,
2903 point: LayoutPoint,
2904 flags: ElementsFromPointFlags,
2905 ) -> Vec<ElementsFromPointResult> {
2906 self.layout_reflow(QueryMsg::ElementsFromPoint);
2907 self.layout().query_elements_from_point(point, flags)
2908 }
2909
2910 pub(crate) fn hit_test_from_input_event(
2911 &self,
2912 input_event: &ConstellationInputEvent,
2913 ) -> Option<HitTestResult> {
2914 self.hit_test_from_point_in_viewport(
2915 input_event.hit_test_result.as_ref()?.point_in_viewport,
2916 )
2917 }
2918
2919 #[expect(unsafe_code)]
2920 pub(crate) fn hit_test_from_point_in_viewport(
2921 &self,
2922 point_in_frame: Point2D<f32, CSSPixel>,
2923 ) -> Option<HitTestResult> {
2924 let result = self
2925 .elements_from_point_query(point_in_frame.cast_unit(), ElementsFromPointFlags::empty())
2926 .into_iter()
2927 .nth(0)?;
2928
2929 let point_relative_to_initial_containing_block =
2930 point_in_frame + self.scroll_offset().cast_unit();
2931
2932 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
2935 Some(HitTestResult {
2936 node: unsafe { from_untrusted_node_address(address) },
2937 cursor: result.cursor,
2938 point_in_node: result.point_in_target,
2939 point_in_frame,
2940 point_relative_to_initial_containing_block,
2941 })
2942 }
2943
2944 pub(crate) fn init_window_proxy(&self, window_proxy: &WindowProxy) {
2945 assert!(self.window_proxy.get().is_none());
2946 self.window_proxy.set(Some(window_proxy));
2947 }
2948
2949 pub(crate) fn init_document(&self, document: &Document) {
2950 assert!(self.document.get().is_none());
2951 assert!(document.window() == self);
2952 self.document.set(Some(document));
2953
2954 if self.unminify_css {
2955 *self.unminified_css_dir.borrow_mut() = Some(unminified_path("unminified-css"));
2956 }
2957 }
2958
2959 pub(crate) fn load_url(
2963 &self,
2964 history_handling: NavigationHistoryBehavior,
2965 force_reload: bool,
2966 load_data: LoadData,
2967 can_gc: CanGc,
2968 ) {
2969 let doc = self.Document();
2970
2971 let initiator_origin_snapshot = &load_data.load_origin;
2973
2974 if !force_reload &&
2977 load_data.url.as_url()[..Position::AfterQuery] ==
2978 doc.url().as_url()[..Position::AfterQuery]
2979 {
2980 if let Some(fragment) = load_data.url.fragment() {
2983 let webdriver_sender = self.webdriver_load_status_sender.borrow().clone();
2984 if let Some(ref sender) = webdriver_sender {
2985 let _ = sender.send(WebDriverLoadStatus::NavigationStart);
2986 }
2987
2988 self.send_to_constellation(ScriptToConstellationMessage::NavigatedToFragment(
2989 load_data.url.clone(),
2990 history_handling,
2991 ));
2992 doc.check_and_scroll_fragment(fragment);
2993 let this = Trusted::new(self);
2994 let old_url = doc.url().into_string();
2995 let new_url = load_data.url.clone().into_string();
2996 let task = task!(hashchange_event: move || {
2997 let this = this.root();
2998 let event = HashChangeEvent::new(
2999 &this,
3000 atom!("hashchange"),
3001 false,
3002 false,
3003 old_url,
3004 new_url,
3005 CanGc::note());
3006 event.upcast::<Event>().fire(this.upcast::<EventTarget>(), CanGc::note());
3007 if let Some(sender) = webdriver_sender {
3008 let _ = sender.send(WebDriverLoadStatus::NavigationStop);
3009 }
3010 });
3011 self.as_global_scope()
3012 .task_manager()
3013 .dom_manipulation_task_source()
3014 .queue(task);
3015 doc.set_url(load_data.url.clone());
3016 return;
3017 }
3018 }
3019
3020 let pipeline_id = self.pipeline_id();
3022 let window_proxy = self.window_proxy();
3023 if let Some(active) = window_proxy.currently_active() {
3024 if pipeline_id == active && doc.is_prompting_or_unloading() {
3025 return;
3026 }
3027 }
3028
3029 if doc.prompt_to_unload(false, can_gc) {
3031 let window_proxy = self.window_proxy();
3032 if window_proxy.parent().is_some() {
3033 window_proxy.start_delaying_load_events_mode();
3037 }
3038
3039 let resolved_history_handling = if history_handling == NavigationHistoryBehavior::Auto {
3041 if let LoadOrigin::Script(initiator_origin) = initiator_origin_snapshot {
3047 if load_data.url == doc.url() && initiator_origin.same_origin(doc.origin()) {
3048 NavigationHistoryBehavior::Replace
3049 } else {
3050 NavigationHistoryBehavior::Push
3051 }
3052 } else {
3053 NavigationHistoryBehavior::Push
3055 }
3056 } else if load_data.url.scheme() == "javascript" || doc.is_initial_about_blank() {
3059 NavigationHistoryBehavior::Replace
3060 } else {
3061 history_handling
3062 };
3063
3064 if let Some(sender) = self.webdriver_load_status_sender.borrow().as_ref() {
3065 let _ = sender.send(WebDriverLoadStatus::NavigationStart);
3066 }
3067
3068 ScriptThread::navigate(
3070 self.webview_id,
3071 pipeline_id,
3072 load_data,
3073 resolved_history_handling,
3074 );
3075 };
3076 }
3077
3078 pub(crate) fn set_viewport_details(&self, viewport_details: ViewportDetails) {
3081 self.viewport_details.set(viewport_details);
3082 if !self.layout_mut().set_viewport_details(viewport_details) {
3083 return;
3084 }
3085 self.Document()
3086 .add_restyle_reason(RestyleReason::ViewportChanged);
3087 }
3088
3089 pub(crate) fn viewport_details(&self) -> ViewportDetails {
3090 self.viewport_details.get()
3091 }
3092
3093 pub(crate) fn theme(&self) -> Theme {
3095 self.theme.get()
3096 }
3097
3098 pub(crate) fn set_theme(&self, new_theme: Theme) {
3100 self.theme.set(new_theme);
3101 if !self.layout_mut().set_theme(new_theme) {
3102 return;
3103 }
3104 self.Document()
3105 .add_restyle_reason(RestyleReason::ThemeChanged);
3106 }
3107
3108 pub(crate) fn get_url(&self) -> ServoUrl {
3109 self.Document().url()
3110 }
3111
3112 pub(crate) fn windowproxy_handler(&self) -> &'static WindowProxyHandler {
3113 self.dom_static.windowproxy_handler
3114 }
3115
3116 pub(crate) fn add_resize_event(&self, event: ViewportDetails, event_type: WindowSizeType) {
3117 *self.unhandled_resize_event.borrow_mut() = Some((event, event_type))
3120 }
3121
3122 pub(crate) fn take_unhandled_resize_event(&self) -> Option<(ViewportDetails, WindowSizeType)> {
3123 self.unhandled_resize_event.borrow_mut().take()
3124 }
3125
3126 pub(crate) fn has_unhandled_resize_event(&self) -> bool {
3128 self.unhandled_resize_event.borrow().is_some()
3129 }
3130
3131 pub(crate) fn suspend(&self, can_gc: CanGc) {
3132 self.as_global_scope().suspend();
3134
3135 if self.window_proxy().currently_active() == Some(self.global().pipeline_id()) {
3137 self.window_proxy().unset_currently_active(can_gc);
3138 }
3139
3140 self.Gc();
3145 }
3146
3147 pub(crate) fn resume(&self, can_gc: CanGc) {
3148 self.as_global_scope().resume();
3150
3151 self.window_proxy().set_currently_active(self, can_gc);
3153
3154 self.Document().title_changed();
3157 }
3158
3159 pub(crate) fn need_emit_timeline_marker(&self, timeline_type: TimelineMarkerType) -> bool {
3160 let markers = self.devtools_markers.borrow();
3161 markers.contains(&timeline_type)
3162 }
3163
3164 pub(crate) fn emit_timeline_marker(&self, marker: TimelineMarker) {
3165 let sender = self.devtools_marker_sender.borrow();
3166 let sender = sender.as_ref().expect("There is no marker sender");
3167 sender.send(Some(marker)).unwrap();
3168 }
3169
3170 pub(crate) fn set_devtools_timeline_markers(
3171 &self,
3172 markers: Vec<TimelineMarkerType>,
3173 reply: IpcSender<Option<TimelineMarker>>,
3174 ) {
3175 *self.devtools_marker_sender.borrow_mut() = Some(reply);
3176 self.devtools_markers.borrow_mut().extend(markers);
3177 }
3178
3179 pub(crate) fn drop_devtools_timeline_markers(&self, markers: Vec<TimelineMarkerType>) {
3180 let mut devtools_markers = self.devtools_markers.borrow_mut();
3181 for marker in markers {
3182 devtools_markers.remove(&marker);
3183 }
3184 if devtools_markers.is_empty() {
3185 *self.devtools_marker_sender.borrow_mut() = None;
3186 }
3187 }
3188
3189 pub(crate) fn set_webdriver_script_chan(&self, chan: Option<IpcSender<WebDriverJSResult>>) {
3190 *self.webdriver_script_chan.borrow_mut() = chan;
3191 }
3192
3193 pub(crate) fn set_webdriver_load_status_sender(
3194 &self,
3195 sender: Option<GenericSender<WebDriverLoadStatus>>,
3196 ) {
3197 *self.webdriver_load_status_sender.borrow_mut() = sender;
3198 }
3199
3200 pub(crate) fn is_alive(&self) -> bool {
3201 self.current_state.get() == WindowState::Alive
3202 }
3203
3204 pub(crate) fn is_top_level(&self) -> bool {
3206 self.parent_info.is_none()
3207 }
3208
3209 pub(crate) fn run_the_resize_steps(&self, can_gc: CanGc) -> bool {
3214 let Some((new_size, size_type)) = self.take_unhandled_resize_event() else {
3215 return false;
3216 };
3217
3218 if self.viewport_details() == new_size {
3219 return false;
3220 }
3221
3222 let _realm = enter_realm(self);
3223 debug!(
3224 "Resizing Window for pipeline {:?} from {:?} to {new_size:?}",
3225 self.pipeline_id(),
3226 self.viewport_details(),
3227 );
3228 self.set_viewport_details(new_size);
3229
3230 self.Document()
3234 .add_restyle_reason(RestyleReason::ViewportChanged);
3235
3236 if self.layout().device().used_viewport_units() {
3239 self.Document().dirty_all_nodes();
3240 }
3241
3242 if size_type == WindowSizeType::Resize {
3244 let uievent = UIEvent::new(
3245 self,
3246 DOMString::from("resize"),
3247 EventBubbles::DoesNotBubble,
3248 EventCancelable::NotCancelable,
3249 Some(self),
3250 0i32,
3251 0u32,
3252 can_gc,
3253 );
3254 uievent.upcast::<Event>().fire(self.upcast(), can_gc);
3255 }
3256
3257 true
3258 }
3259
3260 pub(crate) fn evaluate_media_queries_and_report_changes(&self, can_gc: CanGc) {
3263 let _realm = enter_realm(self);
3264
3265 rooted_vec!(let mut mql_list);
3266 self.media_query_lists.for_each(|mql| {
3267 if let MediaQueryListMatchState::Changed = mql.evaluate_changes() {
3268 mql_list.push(Dom::from_ref(&*mql));
3270 }
3271 });
3272 for mql in mql_list.iter() {
3274 let event = MediaQueryListEvent::new(
3275 &mql.global(),
3276 atom!("change"),
3277 false,
3278 false,
3279 mql.Media(),
3280 mql.Matches(),
3281 can_gc,
3282 );
3283 event
3284 .upcast::<Event>()
3285 .fire(mql.upcast::<EventTarget>(), can_gc);
3286 }
3287 }
3288
3289 pub(crate) fn set_throttled(&self, throttled: bool) {
3291 self.throttled.set(throttled);
3292 if throttled {
3293 self.as_global_scope().slow_down_timers();
3294 } else {
3295 self.as_global_scope().speed_up_timers();
3296 }
3297 }
3298
3299 pub(crate) fn throttled(&self) -> bool {
3300 self.throttled.get()
3301 }
3302
3303 pub(crate) fn unminified_css_dir(&self) -> Option<String> {
3304 self.unminified_css_dir.borrow().clone()
3305 }
3306
3307 pub(crate) fn local_script_source(&self) -> &Option<String> {
3308 &self.local_script_source
3309 }
3310
3311 pub(crate) fn set_navigation_start(&self) {
3312 self.navigation_start.set(CrossProcessInstant::now());
3313 }
3314
3315 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
3316 self.as_global_scope()
3317 .script_to_embedder_chan()
3318 .send(msg)
3319 .unwrap();
3320 }
3321
3322 pub(crate) fn send_to_constellation(&self, msg: ScriptToConstellationMessage) {
3323 self.as_global_scope()
3324 .script_to_constellation_chan()
3325 .send(msg)
3326 .unwrap();
3327 }
3328
3329 #[cfg(feature = "webxr")]
3330 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3331 self.navigator
3332 .get()
3333 .as_ref()
3334 .and_then(|nav| nav.xr())
3335 .is_some_and(|xr| xr.pending_or_active_session())
3336 }
3337
3338 #[cfg(not(feature = "webxr"))]
3339 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3340 false
3341 }
3342
3343 #[expect(unsafe_code)]
3344 fn handle_pending_images_post_reflow(
3345 &self,
3346 pending_images: Vec<PendingImage>,
3347 pending_rasterization_images: Vec<PendingRasterizationImage>,
3348 pending_svg_element_for_serialization: Vec<UntrustedNodeAddress>,
3349 ) {
3350 let pipeline_id = self.pipeline_id();
3351 for image in pending_images {
3352 let id = image.id;
3353 let node = unsafe { from_untrusted_node_address(image.node) };
3354
3355 if let PendingImageState::Unrequested(ref url) = image.state {
3356 fetch_image_for_layout(url.clone(), &node, id, self.image_cache.clone());
3357 }
3358
3359 let mut images = self.pending_layout_images.borrow_mut();
3360 if !images.contains_key(&id) {
3361 let trusted_node = Trusted::new(&*node);
3362 let sender = self.register_image_cache_listener(id, move |response| {
3363 trusted_node
3364 .root()
3365 .owner_window()
3366 .pending_layout_image_notification(response);
3367 });
3368
3369 self.image_cache
3370 .add_listener(ImageLoadListener::new(sender, pipeline_id, id));
3371 }
3372
3373 let nodes = images.entry(id).or_default();
3374 if !nodes.iter().any(|n| std::ptr::eq(&*(n.node), &*node)) {
3375 nodes.push(PendingLayoutImageAncillaryData {
3376 node: Dom::from_ref(&*node),
3377 destination: image.destination,
3378 });
3379 }
3380 }
3381
3382 for image in pending_rasterization_images {
3383 let node = unsafe { from_untrusted_node_address(image.node) };
3384
3385 let mut images = self.pending_images_for_rasterization.borrow_mut();
3386 if !images.contains_key(&(image.id, image.size)) {
3387 let image_cache_sender = self.image_cache_sender.clone();
3388 self.image_cache.add_rasterization_complete_listener(
3389 pipeline_id,
3390 image.id,
3391 image.size,
3392 Box::new(move |response| {
3393 let _ = image_cache_sender.send(response);
3394 }),
3395 );
3396 }
3397
3398 let nodes = images.entry((image.id, image.size)).or_default();
3399 if !nodes.iter().any(|n| std::ptr::eq(&**n, &*node)) {
3400 nodes.push(Dom::from_ref(&*node));
3401 }
3402 }
3403
3404 for node in pending_svg_element_for_serialization.into_iter() {
3405 let node = unsafe { from_untrusted_node_address(node) };
3406 let svg = node.downcast::<SVGSVGElement>().unwrap();
3407 svg.serialize_and_cache_subtree();
3408 node.dirty(NodeDamage::Other);
3409 }
3410 }
3411
3412 #[allow(clippy::too_many_arguments)]
3413 pub(crate) fn new(
3414 webview_id: WebViewId,
3415 runtime: Rc<Runtime>,
3416 script_chan: Sender<MainThreadScriptMsg>,
3417 layout: Box<dyn Layout>,
3418 font_context: Arc<FontContext>,
3419 image_cache_sender: Sender<ImageCacheResponseMessage>,
3420 image_cache: Arc<dyn ImageCache>,
3421 resource_threads: ResourceThreads,
3422 storage_threads: StorageThreads,
3423 #[cfg(feature = "bluetooth")] bluetooth_thread: GenericSender<BluetoothRequest>,
3424 mem_profiler_chan: MemProfilerChan,
3425 time_profiler_chan: TimeProfilerChan,
3426 devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
3427 constellation_chan: ScriptToConstellationChan,
3428 embedder_chan: ScriptToEmbedderChan,
3429 control_chan: GenericSender<ScriptThreadMessage>,
3430 pipeline_id: PipelineId,
3431 parent_info: Option<PipelineId>,
3432 viewport_details: ViewportDetails,
3433 origin: MutableOrigin,
3434 creation_url: ServoUrl,
3435 top_level_creation_url: ServoUrl,
3436 navigation_start: CrossProcessInstant,
3437 webgl_chan: Option<WebGLChan>,
3438 #[cfg(feature = "webxr")] webxr_registry: Option<webxr_api::Registry>,
3439 compositor_api: CrossProcessCompositorApi,
3440 unminify_js: bool,
3441 unminify_css: bool,
3442 local_script_source: Option<String>,
3443 user_content_manager: UserContentManager,
3444 player_context: WindowGLContext,
3445 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
3446 inherited_secure_context: Option<bool>,
3447 theme: Theme,
3448 ) -> DomRoot<Self> {
3449 let error_reporter = CSSErrorReporter {
3450 pipelineid: pipeline_id,
3451 script_chan: control_chan,
3452 };
3453
3454 let win = Box::new(Self {
3455 webview_id,
3456 globalscope: GlobalScope::new_inherited(
3457 pipeline_id,
3458 devtools_chan,
3459 mem_profiler_chan,
3460 time_profiler_chan,
3461 constellation_chan,
3462 embedder_chan,
3463 resource_threads,
3464 storage_threads,
3465 origin,
3466 creation_url,
3467 Some(top_level_creation_url),
3468 #[cfg(feature = "webgpu")]
3469 gpu_id_hub,
3470 inherited_secure_context,
3471 unminify_js,
3472 Some(font_context.clone()),
3473 ),
3474 ongoing_navigation: Default::default(),
3475 script_chan,
3476 layout: RefCell::new(layout),
3477 image_cache_sender,
3478 image_cache,
3479 navigator: Default::default(),
3480 location: Default::default(),
3481 history: Default::default(),
3482 indexeddb: Default::default(),
3483 custom_element_registry: Default::default(),
3484 window_proxy: Default::default(),
3485 document: Default::default(),
3486 performance: Default::default(),
3487 navigation_start: Cell::new(navigation_start),
3488 screen: Default::default(),
3489 session_storage: Default::default(),
3490 local_storage: Default::default(),
3491 status: DomRefCell::new(DOMString::new()),
3492 parent_info,
3493 dom_static: GlobalStaticData::new(),
3494 js_runtime: DomRefCell::new(Some(runtime.clone())),
3495 #[cfg(feature = "bluetooth")]
3496 bluetooth_thread,
3497 #[cfg(feature = "bluetooth")]
3498 bluetooth_extra_permission_data: BluetoothExtraPermissionData::new(),
3499 unhandled_resize_event: Default::default(),
3500 viewport_details: Cell::new(viewport_details),
3501 layout_blocker: Cell::new(LayoutBlocker::WaitingForParse),
3502 current_state: Cell::new(WindowState::Alive),
3503 devtools_marker_sender: Default::default(),
3504 devtools_markers: Default::default(),
3505 webdriver_script_chan: Default::default(),
3506 webdriver_load_status_sender: Default::default(),
3507 error_reporter,
3508 media_query_lists: DOMTracker::new(),
3509 #[cfg(feature = "bluetooth")]
3510 test_runner: Default::default(),
3511 webgl_chan,
3512 #[cfg(feature = "webxr")]
3513 webxr_registry,
3514 pending_image_callbacks: Default::default(),
3515 pending_layout_images: Default::default(),
3516 pending_images_for_rasterization: Default::default(),
3517 unminified_css_dir: Default::default(),
3518 local_script_source,
3519 test_worklet: Default::default(),
3520 paint_worklet: Default::default(),
3521 exists_mut_observer: Cell::new(false),
3522 compositor_api,
3523 has_sent_idle_message: Cell::new(false),
3524 unminify_css,
3525 user_content_manager,
3526 player_context,
3527 throttled: Cell::new(false),
3528 layout_marker: DomRefCell::new(Rc::new(Cell::new(true))),
3529 current_event: DomRefCell::new(None),
3530 theme: Cell::new(theme),
3531 trusted_types: Default::default(),
3532 reporting_observer_list: Default::default(),
3533 report_list: Default::default(),
3534 endpoints_list: Default::default(),
3535 script_window_proxies: ScriptThread::window_proxies(),
3536 has_pending_screenshot_readiness_request: Default::default(),
3537 });
3538
3539 WindowBinding::Wrap::<crate::DomTypeHolder>(GlobalScope::get_cx(), win)
3540 }
3541
3542 pub(crate) fn pipeline_id(&self) -> PipelineId {
3543 self.as_global_scope().pipeline_id()
3544 }
3545
3546 pub(crate) fn cache_layout_value<T>(&self, value: T) -> LayoutValue<T>
3548 where
3549 T: Copy + MallocSizeOf,
3550 {
3551 LayoutValue::new(self.layout_marker.borrow().clone(), value)
3552 }
3553}
3554
3555#[derive(MallocSizeOf)]
3560pub(crate) struct LayoutValue<T: MallocSizeOf> {
3561 #[conditional_malloc_size_of]
3562 is_valid: Rc<Cell<bool>>,
3563 value: T,
3564}
3565
3566#[expect(unsafe_code)]
3567unsafe impl<T: JSTraceable + MallocSizeOf> JSTraceable for LayoutValue<T> {
3568 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
3569 unsafe { self.value.trace(trc) };
3570 }
3571}
3572
3573impl<T: Copy + MallocSizeOf> LayoutValue<T> {
3574 fn new(marker: Rc<Cell<bool>>, value: T) -> Self {
3575 LayoutValue {
3576 is_valid: marker,
3577 value,
3578 }
3579 }
3580
3581 pub(crate) fn get(&self) -> Result<T, ()> {
3583 if self.is_valid.get() {
3584 return Ok(self.value);
3585 }
3586 Err(())
3587 }
3588}
3589
3590fn should_move_clip_rect(clip_rect: UntypedRect<Au>, new_viewport: UntypedRect<f32>) -> bool {
3591 let clip_rect = UntypedRect::new(
3592 Point2D::new(
3593 clip_rect.origin.x.to_f32_px(),
3594 clip_rect.origin.y.to_f32_px(),
3595 ),
3596 Size2D::new(
3597 clip_rect.size.width.to_f32_px(),
3598 clip_rect.size.height.to_f32_px(),
3599 ),
3600 );
3601
3602 static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5;
3606 let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE;
3607
3608 (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width ||
3609 (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width ||
3610 (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height ||
3611 (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height
3612}
3613
3614impl Window {
3615 pub(crate) fn post_message(
3617 &self,
3618 target_origin: Option<ImmutableOrigin>,
3619 source_origin: ImmutableOrigin,
3620 source: &WindowProxy,
3621 data: StructuredSerializedData,
3622 ) {
3623 let this = Trusted::new(self);
3624 let source = Trusted::new(source);
3625 let task = task!(post_serialised_message: move || {
3626 let this = this.root();
3627 let source = source.root();
3628 let document = this.Document();
3629
3630 if let Some(ref target_origin) = target_origin {
3632 if !target_origin.same_origin(document.origin()) {
3633 return;
3634 }
3635 }
3636
3637 let cx = this.get_cx();
3639 let obj = this.reflector().get_jsobject();
3640 let _ac = JSAutoRealm::new(*cx, obj.get());
3641 rooted!(in(*cx) let mut message_clone = UndefinedValue());
3642 if let Ok(ports) = structuredclone::read(this.upcast(), data, message_clone.handle_mut(), CanGc::note()) {
3643 MessageEvent::dispatch_jsval(
3645 this.upcast(),
3646 this.upcast(),
3647 message_clone.handle(),
3648 Some(&source_origin.ascii_serialization()),
3649 Some(&*source),
3650 ports,
3651 CanGc::note()
3652 );
3653 } else {
3654 MessageEvent::dispatch_error(
3656 this.upcast(),
3657 this.upcast(),
3658 CanGc::note()
3659 );
3660 }
3661 });
3662 self.as_global_scope()
3664 .task_manager()
3665 .dom_manipulation_task_source()
3666 .queue(task);
3667 }
3668}
3669
3670#[derive(Clone, MallocSizeOf)]
3671pub(crate) struct CSSErrorReporter {
3672 pub(crate) pipelineid: PipelineId,
3673 pub(crate) script_chan: GenericSender<ScriptThreadMessage>,
3674}
3675unsafe_no_jsmanaged_fields!(CSSErrorReporter);
3676
3677impl ParseErrorReporter for CSSErrorReporter {
3678 fn report_error(
3679 &self,
3680 url: &UrlExtraData,
3681 location: SourceLocation,
3682 error: ContextualParseError,
3683 ) {
3684 if log_enabled!(log::Level::Info) {
3685 info!(
3686 "Url:\t{}\n{}:{} {}",
3687 url.0.as_str(),
3688 location.line,
3689 location.column,
3690 error
3691 )
3692 }
3693
3694 let _ = self.script_chan.send(ScriptThreadMessage::ReportCSSError(
3696 self.pipelineid,
3697 url.0.to_string(),
3698 location.line,
3699 location.column,
3700 error.to_string(),
3701 ));
3702 }
3703}
3704
3705fn is_named_element_with_name_attribute(elem: &Element) -> bool {
3706 let type_ = match elem.upcast::<Node>().type_id() {
3707 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
3708 _ => return false,
3709 };
3710 matches!(
3711 type_,
3712 HTMLElementTypeId::HTMLEmbedElement |
3713 HTMLElementTypeId::HTMLFormElement |
3714 HTMLElementTypeId::HTMLImageElement |
3715 HTMLElementTypeId::HTMLObjectElement
3716 )
3717}
3718
3719fn is_named_element_with_id_attribute(elem: &Element) -> bool {
3720 elem.is_html_element()
3721}
3722
3723#[expect(unsafe_code)]
3724#[unsafe(no_mangle)]
3725unsafe extern "C" fn dump_js_stack(cx: *mut RawJSContext) {
3727 unsafe {
3728 DumpJSStack(cx, true, false, false);
3729 }
3730}
3731
3732impl WindowHelpers for Window {
3733 fn create_named_properties_object(
3734 cx: JSContext,
3735 proto: HandleObject,
3736 object: MutableHandleObject,
3737 ) {
3738 Self::create_named_properties_object(cx, proto, object)
3739 }
3740}