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 DocumentState, LoadData, LoadOrigin, NavigationHistoryBehavior, ScriptToConstellationChan,
30 ScriptToConstellationMessage, StructuredSerializedData, WindowSizeType,
31};
32use crossbeam_channel::{Sender, unbounded};
33use cssparser::SourceLocation;
34use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
35use dom_struct::dom_struct;
36use embedder_traits::user_content_manager::{UserContentManager, UserScript};
37use embedder_traits::{
38 AlertResponse, ConfirmResponse, EmbedderMsg, PromptResponse, SimpleDialog, Theme,
39 UntrustedNodeAddress, ViewportDetails, WebDriverJSError, WebDriverJSResult,
40 WebDriverLoadStatus,
41};
42use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D};
43use euclid::{Point2D, Scale, Size2D, Vector2D};
44use fonts::FontContext;
45use ipc_channel::ipc::{self, IpcSender};
46use js::glue::DumpJSStack;
47use js::jsapi::{
48 GCReason, Heap, JS_GC, JSAutoRealm, JSContext as RawJSContext, JSObject, JSPROP_ENUMERATE,
49};
50use js::jsval::{NullValue, UndefinedValue};
51use js::rust::wrappers::JS_DefineProperty;
52use js::rust::{
53 CustomAutoRooter, CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleObject,
54 MutableHandleValue,
55};
56use layout_api::{
57 BoxAreaType, ElementsFromPointFlags, ElementsFromPointResult, FragmentType, Layout,
58 PendingImage, PendingImageState, PendingRasterizationImage, QueryMsg, ReflowGoal,
59 ReflowPhasesRun, ReflowRequest, ReflowRequestRestyle, RestyleReason, TrustedNodeAddress,
60 combine_id_with_fragment_type,
61};
62use malloc_size_of::MallocSizeOf;
63use media::WindowGLContext;
64use net_traits::ResourceThreads;
65use net_traits::image_cache::{
66 ImageCache, ImageCacheResponseMessage, ImageLoadListener, ImageResponse, PendingImageId,
67 PendingImageResponse, RasterizationCompleteResponse,
68};
69use net_traits::storage_thread::StorageType;
70use num_traits::ToPrimitive;
71use profile_traits::generic_channel as ProfiledGenericChannel;
72use profile_traits::mem::ProfilerChan as MemProfilerChan;
73use profile_traits::time::ProfilerChan as TimeProfilerChan;
74use script_bindings::conversions::SafeToJSValConvertible;
75use script_bindings::interfaces::WindowHelpers;
76use script_bindings::root::Root;
77use script_traits::{ConstellationInputEvent, ScriptThreadMessage};
78use selectors::attr::CaseSensitivity;
79use servo_arc::Arc as ServoArc;
80use servo_config::{opts, pref};
81use servo_geometry::{DeviceIndependentIntRect, f32_rect_to_au_rect};
82use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
83use style::error_reporting::{ContextualParseError, ParseErrorReporter};
84use style::properties::PropertyId;
85use style::properties::style_structs::Font;
86use style::selector_parser::PseudoElement;
87use style::str::HTML_SPACE_CHARACTERS;
88use style::stylesheets::UrlExtraData;
89use style_traits::CSSPixel;
90use stylo_atoms::Atom;
91use url::Position;
92use webrender_api::ExternalScrollId;
93use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint};
94
95use super::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
96use super::bindings::trace::HashMapTracedValues;
97use super::types::SVGSVGElement;
98use crate::dom::bindings::cell::{DomRefCell, Ref};
99use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
100 DocumentMethods, DocumentReadyState, NamedPropertyValue,
101};
102use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
103use crate::dom::bindings::codegen::Bindings::HistoryBinding::History_Binding::HistoryMethods;
104use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
105 ImageBitmapOptions, ImageBitmapSource,
106};
107use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryList_Binding::MediaQueryListMethods;
108use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
109use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
110use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
111use crate::dom::bindings::codegen::Bindings::WindowBinding::{
112 self, FrameRequestCallback, ScrollBehavior, ScrollToOptions, WindowMethods,
113 WindowPostMessageOptions,
114};
115use crate::dom::bindings::codegen::UnionTypes::{
116 RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
117};
118use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
119use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
120use crate::dom::bindings::num::Finite;
121use crate::dom::bindings::refcounted::Trusted;
122use crate::dom::bindings::reflector::{DomGlobal, DomObject};
123use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
124use crate::dom::bindings::str::{DOMString, USVString};
125use crate::dom::bindings::structuredclone;
126use crate::dom::bindings::trace::{CustomTraceable, JSTraceable, RootedTraceableBox};
127use crate::dom::bindings::utils::GlobalStaticData;
128use crate::dom::bindings::weakref::DOMTracker;
129#[cfg(feature = "bluetooth")]
130use crate::dom::bluetooth::BluetoothExtraPermissionData;
131use crate::dom::cookiestore::CookieStore;
132use crate::dom::crypto::Crypto;
133use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
134use crate::dom::customelementregistry::CustomElementRegistry;
135use crate::dom::document::{AnimationFrameCallback, Document};
136use crate::dom::element::Element;
137use crate::dom::event::{Event, EventBubbles, EventCancelable};
138use crate::dom::eventtarget::EventTarget;
139use crate::dom::globalscope::GlobalScope;
140use crate::dom::hashchangeevent::HashChangeEvent;
141use crate::dom::history::History;
142use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
143use crate::dom::html::htmliframeelement::HTMLIFrameElement;
144use crate::dom::idbfactory::IDBFactory;
145use crate::dom::inputevent::HitTestResult;
146use crate::dom::location::Location;
147use crate::dom::medialist::MediaList;
148use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
149use crate::dom::mediaquerylistevent::MediaQueryListEvent;
150use crate::dom::messageevent::MessageEvent;
151use crate::dom::navigator::Navigator;
152use crate::dom::node::{Node, NodeDamage, NodeTraits, from_untrusted_node_address};
153use crate::dom::performance::Performance;
154use crate::dom::promise::Promise;
155use crate::dom::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
156use crate::dom::reportingobserver::ReportingObserver;
157use crate::dom::screen::Screen;
158use crate::dom::selection::Selection;
159use crate::dom::shadowroot::ShadowRoot;
160use crate::dom::storage::Storage;
161#[cfg(feature = "bluetooth")]
162use crate::dom::testrunner::TestRunner;
163use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
164use crate::dom::types::{ImageBitmap, UIEvent};
165use crate::dom::webgl::webglrenderingcontext::WebGLCommandSender;
166#[cfg(feature = "webgpu")]
167use crate::dom::webgpu::identityhub::IdentityHub;
168use crate::dom::windowproxy::{WindowProxy, WindowProxyHandler};
169use crate::dom::worklet::Worklet;
170use crate::dom::workletglobalscope::WorkletGlobalScopeType;
171use crate::layout_image::fetch_image_for_layout;
172use crate::messaging::{MainThreadScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
173use crate::microtask::MicrotaskQueue;
174use crate::realms::{InRealm, enter_realm};
175use crate::script_runtime::{CanGc, JSContext, Runtime};
176use crate::script_thread::ScriptThread;
177use crate::timers::{IsInterval, TimerCallback};
178use crate::unminify::unminified_path;
179use crate::webdriver_handlers::{find_node_by_unique_id_in_document, jsval_to_webdriver};
180use crate::{fetch, window_named_properties};
181
182#[derive(MallocSizeOf)]
187pub struct PendingImageCallback(
188 #[ignore_malloc_size_of = "dyn Fn is currently impossible to measure"]
189 Box<dyn Fn(PendingImageResponse) + 'static>,
190);
191
192#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
194enum WindowState {
195 Alive,
196 Zombie, }
198
199const INITIAL_REFLOW_DELAY: Duration = Duration::from_millis(200);
202
203#[derive(Clone, Copy, MallocSizeOf)]
214enum LayoutBlocker {
215 WaitingForParse,
217 Parsing(Instant),
219 FiredLoadEventOrParsingTimerExpired,
223}
224
225impl LayoutBlocker {
226 fn layout_blocked(&self) -> bool {
227 !matches!(self, Self::FiredLoadEventOrParsingTimerExpired)
228 }
229}
230
231#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf, PartialEq)]
234pub(crate) struct OngoingNavigation(u32);
235
236type PendingImageRasterizationKey = (PendingImageId, DeviceIntSize);
237
238#[dom_struct]
239pub(crate) struct Window {
240 globalscope: GlobalScope,
241 #[no_trace]
245 webview_id: WebViewId,
246 script_chan: Sender<MainThreadScriptMsg>,
247 #[no_trace]
248 #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"]
249 layout: RefCell<Box<dyn Layout>>,
250 navigator: MutNullableDom<Navigator>,
251 #[ignore_malloc_size_of = "Arc"]
252 #[no_trace]
253 image_cache: Arc<dyn ImageCache>,
254 #[no_trace]
255 image_cache_sender: IpcSender<ImageCacheResponseMessage>,
256 window_proxy: MutNullableDom<WindowProxy>,
257 document: MutNullableDom<Document>,
258 location: MutNullableDom<Location>,
259 history: MutNullableDom<History>,
260 indexeddb: MutNullableDom<IDBFactory>,
261 custom_element_registry: MutNullableDom<CustomElementRegistry>,
262 performance: MutNullableDom<Performance>,
263 #[no_trace]
264 navigation_start: Cell<CrossProcessInstant>,
265 screen: MutNullableDom<Screen>,
266 session_storage: MutNullableDom<Storage>,
267 local_storage: MutNullableDom<Storage>,
268 status: DomRefCell<DOMString>,
269 trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
270
271 ongoing_navigation: Cell<OngoingNavigation>,
274
275 #[no_trace]
278 devtools_markers: DomRefCell<HashSet<TimelineMarkerType>>,
279 #[no_trace]
280 devtools_marker_sender: DomRefCell<Option<IpcSender<Option<TimelineMarker>>>>,
281
282 #[no_trace]
284 unhandled_resize_event: DomRefCell<Option<(ViewportDetails, WindowSizeType)>>,
285
286 #[no_trace]
288 theme: Cell<Theme>,
289
290 #[no_trace]
292 parent_info: Option<PipelineId>,
293
294 dom_static: GlobalStaticData,
296
297 #[ignore_malloc_size_of = "Rc<T> is hard"]
299 js_runtime: DomRefCell<Option<Rc<Runtime>>>,
300
301 #[no_trace]
303 viewport_details: Cell<ViewportDetails>,
304
305 #[no_trace]
307 #[cfg(feature = "bluetooth")]
308 bluetooth_thread: IpcSender<BluetoothRequest>,
309
310 #[cfg(feature = "bluetooth")]
311 bluetooth_extra_permission_data: BluetoothExtraPermissionData,
312
313 #[no_trace]
317 layout_blocker: Cell<LayoutBlocker>,
318
319 #[no_trace]
321 webdriver_script_chan: DomRefCell<Option<IpcSender<WebDriverJSResult>>>,
322
323 #[no_trace]
325 webdriver_load_status_sender: RefCell<Option<GenericSender<WebDriverLoadStatus>>>,
326
327 current_state: Cell<WindowState>,
329
330 #[no_trace]
333 current_viewport_size: Cell<UntypedSize2D<Au>>,
334
335 error_reporter: CSSErrorReporter,
336
337 media_query_lists: DOMTracker<MediaQueryList>,
339
340 #[cfg(feature = "bluetooth")]
341 test_runner: MutNullableDom<TestRunner>,
342
343 #[ignore_malloc_size_of = "channels are hard"]
345 #[no_trace]
346 webgl_chan: Option<WebGLChan>,
347
348 #[ignore_malloc_size_of = "defined in webxr"]
349 #[no_trace]
350 #[cfg(feature = "webxr")]
351 webxr_registry: Option<webxr_api::Registry>,
352
353 #[no_trace]
357 pending_image_callbacks: DomRefCell<HashMap<PendingImageId, Vec<PendingImageCallback>>>,
358
359 pending_layout_images: DomRefCell<HashMapTracedValues<PendingImageId, Vec<Dom<Node>>>>,
364
365 pending_images_for_rasterization:
369 DomRefCell<HashMapTracedValues<PendingImageRasterizationKey, Vec<Dom<Node>>>>,
370
371 unminified_css_dir: DomRefCell<Option<String>>,
374
375 local_script_source: Option<String>,
377
378 test_worklet: MutNullableDom<Worklet>,
380 paint_worklet: MutNullableDom<Worklet>,
382
383 exists_mut_observer: Cell<bool>,
385
386 #[ignore_malloc_size_of = "Wraps an IpcSender"]
388 #[no_trace]
389 compositor_api: CrossProcessCompositorApi,
390
391 has_sent_idle_message: Cell<bool>,
394
395 unminify_css: bool,
397
398 #[no_trace]
400 user_content_manager: UserContentManager,
401
402 #[ignore_malloc_size_of = "defined in script_thread"]
404 #[no_trace]
405 player_context: WindowGLContext,
406
407 throttled: Cell<bool>,
408
409 #[ignore_malloc_size_of = "Rc is hard"]
413 layout_marker: DomRefCell<Rc<Cell<bool>>>,
414
415 current_event: DomRefCell<Option<Dom<Event>>>,
417
418 reporting_observer_list: DomRefCell<Vec<DomRoot<ReportingObserver>>>,
420
421 report_list: DomRefCell<Vec<Report>>,
423
424 #[no_trace]
426 endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
427}
428
429impl Window {
430 pub(crate) fn webview_id(&self) -> WebViewId {
431 self.webview_id
432 }
433
434 pub(crate) fn as_global_scope(&self) -> &GlobalScope {
435 self.upcast::<GlobalScope>()
436 }
437
438 pub(crate) fn layout(&self) -> Ref<'_, Box<dyn Layout>> {
439 self.layout.borrow()
440 }
441
442 pub(crate) fn layout_mut(&self) -> RefMut<'_, Box<dyn Layout>> {
443 self.layout.borrow_mut()
444 }
445
446 pub(crate) fn get_exists_mut_observer(&self) -> bool {
447 self.exists_mut_observer.get()
448 }
449
450 pub(crate) fn set_exists_mut_observer(&self) {
451 self.exists_mut_observer.set(true);
452 }
453
454 #[allow(unsafe_code)]
455 pub(crate) fn clear_js_runtime_for_script_deallocation(&self) {
456 self.as_global_scope()
457 .remove_web_messaging_and_dedicated_workers_infra();
458 unsafe {
459 *self.js_runtime.borrow_for_script_deallocation() = None;
460 self.window_proxy.set(None);
461 self.current_state.set(WindowState::Zombie);
462 self.as_global_scope()
463 .task_manager()
464 .cancel_all_tasks_and_ignore_future_tasks();
465 }
466 }
467
468 pub(crate) fn discard_browsing_context(&self) {
471 let proxy = match self.window_proxy.get() {
472 Some(proxy) => proxy,
473 None => panic!("Discarding a BC from a window that has none"),
474 };
475 proxy.discard_browsing_context();
476 self.as_global_scope()
480 .task_manager()
481 .cancel_all_tasks_and_ignore_future_tasks();
482 }
483
484 pub(crate) fn time_profiler_chan(&self) -> &TimeProfilerChan {
486 self.globalscope.time_profiler_chan()
487 }
488
489 pub(crate) fn origin(&self) -> &MutableOrigin {
490 self.globalscope.origin()
491 }
492
493 #[allow(unsafe_code)]
494 pub(crate) fn get_cx(&self) -> JSContext {
495 unsafe { JSContext::from_ptr(self.js_runtime.borrow().as_ref().unwrap().cx()) }
496 }
497
498 pub(crate) fn get_js_runtime(&self) -> Ref<'_, Option<Rc<Runtime>>> {
499 self.js_runtime.borrow()
500 }
501
502 pub(crate) fn main_thread_script_chan(&self) -> &Sender<MainThreadScriptMsg> {
503 &self.script_chan
504 }
505
506 pub(crate) fn parent_info(&self) -> Option<PipelineId> {
507 self.parent_info
508 }
509
510 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
511 let (sender, receiver) = unbounded();
512 (
513 ScriptEventLoopSender::MainThread(sender),
514 ScriptEventLoopReceiver::MainThread(receiver),
515 )
516 }
517
518 pub(crate) fn event_loop_sender(&self) -> ScriptEventLoopSender {
519 ScriptEventLoopSender::MainThread(self.script_chan.clone())
520 }
521
522 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
523 self.image_cache.clone()
524 }
525
526 pub(crate) fn window_proxy(&self) -> DomRoot<WindowProxy> {
528 self.window_proxy.get().unwrap()
529 }
530
531 pub(crate) fn append_reporting_observer(&self, reporting_observer: DomRoot<ReportingObserver>) {
532 self.reporting_observer_list
533 .borrow_mut()
534 .push(reporting_observer);
535 }
536
537 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
538 if let Some(index) = self
539 .reporting_observer_list
540 .borrow()
541 .iter()
542 .position(|observer| &**observer == reporting_observer)
543 {
544 self.reporting_observer_list.borrow_mut().remove(index);
545 }
546 }
547
548 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
549 self.reporting_observer_list.borrow().clone()
550 }
551
552 pub(crate) fn append_report(&self, report: Report) {
553 self.report_list.borrow_mut().push(report);
554 let trusted_window = Trusted::new(self);
555 self.upcast::<GlobalScope>()
556 .task_manager()
557 .dom_manipulation_task_source()
558 .queue(task!(send_to_reporting_endpoints: move || {
559 let window = trusted_window.root();
560 let reports = std::mem::take(&mut *window.report_list.borrow_mut());
561 window.upcast::<GlobalScope>().send_reports_to_endpoints(
562 reports,
563 window.endpoints_list.borrow().clone(),
564 );
565 }));
566 }
567
568 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
569 self.report_list.borrow().clone()
570 }
571
572 pub(crate) fn set_endpoints_list(&self, endpoints: Vec<ReportingEndpoint>) {
573 *self.endpoints_list.borrow_mut() = endpoints;
574 }
575
576 pub(crate) fn undiscarded_window_proxy(&self) -> Option<DomRoot<WindowProxy>> {
579 self.window_proxy.get().and_then(|window_proxy| {
580 if window_proxy.is_browsing_context_discarded() {
581 None
582 } else {
583 Some(window_proxy)
584 }
585 })
586 }
587
588 pub(crate) fn webview_window_proxy(&self) -> Option<DomRoot<WindowProxy>> {
591 self.undiscarded_window_proxy()
592 .and_then(|window_proxy| ScriptThread::find_window_proxy(window_proxy.webview_id().0))
593 }
594
595 #[cfg(feature = "bluetooth")]
596 pub(crate) fn bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
597 self.bluetooth_thread.clone()
598 }
599
600 #[cfg(feature = "bluetooth")]
601 pub(crate) fn bluetooth_extra_permission_data(&self) -> &BluetoothExtraPermissionData {
602 &self.bluetooth_extra_permission_data
603 }
604
605 pub(crate) fn css_error_reporter(&self) -> Option<&dyn ParseErrorReporter> {
606 Some(&self.error_reporter)
607 }
608
609 pub(crate) fn webgl_chan(&self) -> Option<WebGLCommandSender> {
610 self.webgl_chan
611 .as_ref()
612 .map(|chan| WebGLCommandSender::new(chan.clone()))
613 }
614
615 #[cfg(feature = "webxr")]
616 pub(crate) fn webxr_registry(&self) -> Option<webxr_api::Registry> {
617 self.webxr_registry.clone()
618 }
619
620 fn new_paint_worklet(&self, can_gc: CanGc) -> DomRoot<Worklet> {
621 debug!("Creating new paint worklet.");
622 Worklet::new(self, WorkletGlobalScopeType::Paint, can_gc)
623 }
624
625 pub(crate) fn register_image_cache_listener(
626 &self,
627 id: PendingImageId,
628 callback: impl Fn(PendingImageResponse) + 'static,
629 ) -> IpcSender<ImageCacheResponseMessage> {
630 self.pending_image_callbacks
631 .borrow_mut()
632 .entry(id)
633 .or_default()
634 .push(PendingImageCallback(Box::new(callback)));
635 self.image_cache_sender.clone()
636 }
637
638 fn pending_layout_image_notification(&self, response: PendingImageResponse) {
639 let mut images = self.pending_layout_images.borrow_mut();
640 let nodes = images.entry(response.id);
641 let nodes = match nodes {
642 Entry::Occupied(nodes) => nodes,
643 Entry::Vacant(_) => return,
644 };
645 if matches!(response.response, ImageResponse::Loaded(_, _)) {
646 for node in nodes.get() {
647 node.dirty(NodeDamage::Other);
648 }
649 }
650 match response.response {
651 ImageResponse::MetadataLoaded(_) => {},
652 ImageResponse::Loaded(_, _) |
653 ImageResponse::PlaceholderLoaded(_, _) |
654 ImageResponse::None => {
655 nodes.remove();
656 },
657 }
658 }
659
660 pub(crate) fn handle_image_rasterization_complete_notification(
661 &self,
662 response: RasterizationCompleteResponse,
663 ) {
664 let mut images = self.pending_images_for_rasterization.borrow_mut();
665 let nodes = images.entry((response.image_id, response.requested_size));
666 let nodes = match nodes {
667 Entry::Occupied(nodes) => nodes,
668 Entry::Vacant(_) => return,
669 };
670 for node in nodes.get() {
671 node.dirty(NodeDamage::Other);
672 }
673 nodes.remove();
674 }
675
676 pub(crate) fn pending_image_notification(&self, response: PendingImageResponse) {
677 let mut images = std::mem::take(&mut *self.pending_image_callbacks.borrow_mut());
682 let Entry::Occupied(callbacks) = images.entry(response.id) else {
683 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
684 return;
685 };
686
687 for callback in callbacks.get() {
688 callback.0(response.clone());
689 }
690
691 match response.response {
692 ImageResponse::MetadataLoaded(_) => {},
693 ImageResponse::Loaded(_, _) |
694 ImageResponse::PlaceholderLoaded(_, _) |
695 ImageResponse::None => {
696 callbacks.remove();
697 },
698 }
699
700 let _ = std::mem::replace(&mut *self.pending_image_callbacks.borrow_mut(), images);
701 }
702
703 pub(crate) fn compositor_api(&self) -> &CrossProcessCompositorApi {
704 &self.compositor_api
705 }
706
707 pub(crate) fn userscripts(&self) -> &[UserScript] {
708 self.user_content_manager.scripts()
709 }
710
711 pub(crate) fn get_player_context(&self) -> WindowGLContext {
712 self.player_context.clone()
713 }
714
715 pub(crate) fn dispatch_event_with_target_override(&self, event: &Event, can_gc: CanGc) {
717 event.dispatch(self.upcast(), true, can_gc);
718 }
719
720 pub(crate) fn font_context(&self) -> &Arc<FontContext> {
721 self.as_global_scope()
722 .font_context()
723 .expect("A `Window` should always have a `FontContext`")
724 }
725
726 pub(crate) fn ongoing_navigation(&self) -> OngoingNavigation {
727 self.ongoing_navigation.get()
728 }
729
730 pub(crate) fn set_ongoing_navigation(&self) -> OngoingNavigation {
732 let new_value = self.ongoing_navigation.get().0.wrapping_add(1);
736
737 self.ongoing_navigation.set(OngoingNavigation(new_value));
744
745 OngoingNavigation(new_value)
747 }
748
749 fn stop_loading(&self, can_gc: CanGc) {
751 let doc = self.Document();
753
754 self.set_ongoing_navigation();
764
765 doc.abort(can_gc);
767 }
768}
769
770pub(crate) fn base64_btoa(input: DOMString) -> Fallible<DOMString> {
772 if input.chars().any(|c: char| c > '\u{FF}') {
776 Err(Error::InvalidCharacter)
777 } else {
778 let octets = input.chars().map(|c: char| c as u8).collect::<Vec<u8>>();
783
784 let config =
787 base64::engine::general_purpose::GeneralPurposeConfig::new().with_encode_padding(true);
788 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
789 Ok(DOMString::from(engine.encode(octets)))
790 }
791}
792
793pub(crate) fn base64_atob(input: DOMString) -> Fallible<DOMString> {
795 fn is_html_space(c: char) -> bool {
797 HTML_SPACE_CHARACTERS.contains(&c)
798 }
799 let without_spaces = input
800 .chars()
801 .filter(|&c| !is_html_space(c))
802 .collect::<String>();
803 let mut input = &*without_spaces;
804
805 if input.len() % 4 == 0 {
809 if input.ends_with("==") {
810 input = &input[..input.len() - 2]
811 } else if input.ends_with('=') {
812 input = &input[..input.len() - 1]
813 }
814 }
815
816 if input.len() % 4 == 1 {
819 return Err(Error::InvalidCharacter);
820 }
821
822 if input
830 .chars()
831 .any(|c| c != '+' && c != '/' && !c.is_alphanumeric())
832 {
833 return Err(Error::InvalidCharacter);
834 }
835
836 let config = base64::engine::general_purpose::GeneralPurposeConfig::new()
837 .with_decode_padding_mode(base64::engine::DecodePaddingMode::RequireNone)
838 .with_decode_allow_trailing_bits(true);
839 let engine = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
840
841 let data = engine.decode(input).map_err(|_| Error::InvalidCharacter)?;
842 Ok(data.iter().map(|&b| b as char).collect::<String>().into())
843}
844
845impl WindowMethods<crate::DomTypeHolder> for Window {
846 fn Alert_(&self) {
848 self.Alert(DOMString::new());
849 }
850
851 fn Alert(&self, s: DOMString) {
853 {
857 let stderr = stderr();
858 let mut stderr = stderr.lock();
859 let stdout = stdout();
860 let mut stdout = stdout.lock();
861 writeln!(&mut stdout, "\nALERT: {}", s).unwrap();
862 stdout.flush().unwrap();
863 stderr.flush().unwrap();
864 }
865 let (sender, receiver) =
866 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
867 let dialog = SimpleDialog::Alert {
868 message: s.to_string(),
869 response_sender: sender,
870 };
871 let msg = EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog);
872 self.send_to_embedder(msg);
873 receiver.recv().unwrap_or_else(|_| {
874 debug!("Alert dialog was cancelled or failed to show.");
876 AlertResponse::Ok
877 });
878 }
879
880 fn Confirm(&self, s: DOMString) -> bool {
882 let (sender, receiver) =
883 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
884 let dialog = SimpleDialog::Confirm {
885 message: s.to_string(),
886 response_sender: sender,
887 };
888 let msg = EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog);
889 self.send_to_embedder(msg);
890 match receiver.recv() {
891 Ok(ConfirmResponse::Ok) => true,
892 Ok(ConfirmResponse::Cancel) => false,
893 Err(_) => {
894 warn!("Confirm dialog was cancelled or failed to show.");
895 false
896 },
897 }
898 }
899
900 fn Prompt(&self, message: DOMString, default: DOMString) -> Option<DOMString> {
902 let (sender, receiver) =
903 ProfiledGenericChannel::channel(self.global().time_profiler_chan().clone()).unwrap();
904 let dialog = SimpleDialog::Prompt {
905 message: message.to_string(),
906 default: default.to_string(),
907 response_sender: sender,
908 };
909 let msg = EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog);
910 self.send_to_embedder(msg);
911 match receiver.recv() {
912 Ok(PromptResponse::Ok(input)) => Some(input.into()),
913 Ok(PromptResponse::Cancel) => None,
914 Err(_) => {
915 warn!("Prompt dialog was cancelled or failed to show.");
916 None
917 },
918 }
919 }
920
921 fn Stop(&self, can_gc: CanGc) {
923 self.stop_loading(can_gc);
928 }
929
930 fn Focus(&self) {
932 let current = match self.undiscarded_window_proxy() {
936 Some(proxy) => proxy,
937 None => return,
938 };
939
940 current.focus();
942
943 }
949
950 fn Blur(&self) {
952 }
955
956 fn Open(
958 &self,
959 url: USVString,
960 target: DOMString,
961 features: DOMString,
962 can_gc: CanGc,
963 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
964 self.window_proxy().open(url, target, features, can_gc)
965 }
966
967 fn GetOpener(
969 &self,
970 cx: JSContext,
971 in_realm_proof: InRealm,
972 mut retval: MutableHandleValue,
973 ) -> Fallible<()> {
974 let current = match self.window_proxy.get() {
976 Some(proxy) => proxy,
977 None => {
979 retval.set(NullValue());
980 return Ok(());
981 },
982 };
983 if current.is_browsing_context_discarded() {
988 retval.set(NullValue());
989 return Ok(());
990 }
991 current.opener(*cx, in_realm_proof, retval);
993 Ok(())
994 }
995
996 #[allow(unsafe_code)]
997 fn SetOpener(&self, cx: JSContext, value: HandleValue) -> ErrorResult {
999 if value.is_null() {
1001 if let Some(proxy) = self.window_proxy.get() {
1002 proxy.disown();
1003 }
1004 return Ok(());
1005 }
1006 let obj = self.reflector().get_jsobject();
1008 unsafe {
1009 let result =
1010 JS_DefineProperty(*cx, obj, c"opener".as_ptr(), value, JSPROP_ENUMERATE as u32);
1011
1012 if result { Ok(()) } else { Err(Error::JSFailed) }
1013 }
1014 }
1015
1016 fn Closed(&self) -> bool {
1018 self.window_proxy
1019 .get()
1020 .map(|ref proxy| proxy.is_browsing_context_discarded() || proxy.is_closing())
1021 .unwrap_or(true)
1022 }
1023
1024 fn Close(&self) {
1026 let window_proxy = match self.window_proxy.get() {
1029 Some(proxy) => proxy,
1030 None => return,
1031 };
1032 if window_proxy.is_closing() {
1033 return;
1034 }
1035 if let Ok(history_length) = self.History().GetLength() {
1038 let is_auxiliary = window_proxy.is_auxiliary();
1039
1040 let is_script_closable = (self.is_top_level() && history_length == 1) ||
1042 is_auxiliary ||
1043 pref!(dom_allow_scripts_to_close_windows);
1044
1045 if is_script_closable {
1049 window_proxy.close();
1051
1052 let this = Trusted::new(self);
1054 let task = task!(window_close_browsing_context: move || {
1055 let window = this.root();
1056 let document = window.Document();
1057 if document.prompt_to_unload(false, CanGc::note()) {
1063 document.unload(false, CanGc::note());
1065
1066 window.discard_browsing_context();
1069
1070 window.send_to_constellation(ScriptToConstellationMessage::DiscardTopLevelBrowsingContext);
1071 }
1072 });
1073 self.as_global_scope()
1074 .task_manager()
1075 .dom_manipulation_task_source()
1076 .queue(task);
1077 }
1078 }
1079 }
1080
1081 fn Document(&self) -> DomRoot<Document> {
1083 self.document
1084 .get()
1085 .expect("Document accessed before initialization.")
1086 }
1087
1088 fn History(&self) -> DomRoot<History> {
1090 self.history.or_init(|| History::new(self, CanGc::note()))
1091 }
1092
1093 fn IndexedDB(&self) -> DomRoot<IDBFactory> {
1095 self.indexeddb.or_init(|| {
1096 let global_scope = self.upcast::<GlobalScope>();
1097 IDBFactory::new(global_scope, CanGc::note())
1098 })
1099 }
1100
1101 fn CustomElements(&self) -> DomRoot<CustomElementRegistry> {
1103 self.custom_element_registry
1104 .or_init(|| CustomElementRegistry::new(self, CanGc::note()))
1105 }
1106
1107 fn Location(&self) -> DomRoot<Location> {
1109 self.location.or_init(|| Location::new(self, CanGc::note()))
1110 }
1111
1112 fn SessionStorage(&self) -> DomRoot<Storage> {
1114 self.session_storage
1115 .or_init(|| Storage::new(self, StorageType::Session, CanGc::note()))
1116 }
1117
1118 fn LocalStorage(&self) -> DomRoot<Storage> {
1120 self.local_storage
1121 .or_init(|| Storage::new(self, StorageType::Local, CanGc::note()))
1122 }
1123
1124 fn CookieStore(&self, can_gc: CanGc) -> DomRoot<CookieStore> {
1126 self.global().cookie_store(can_gc)
1127 }
1128
1129 fn Crypto(&self) -> DomRoot<Crypto> {
1131 self.as_global_scope().crypto(CanGc::note())
1132 }
1133
1134 fn GetFrameElement(&self) -> Option<DomRoot<Element>> {
1136 let window_proxy = self.window_proxy.get()?;
1138
1139 let container = window_proxy.frame_element()?;
1141
1142 let container_doc = container.owner_document();
1144 let current_doc = GlobalScope::current()
1145 .expect("No current global object")
1146 .as_window()
1147 .Document();
1148 if !current_doc
1149 .origin()
1150 .same_origin_domain(container_doc.origin())
1151 {
1152 return None;
1153 }
1154 Some(DomRoot::from_ref(container))
1156 }
1157
1158 fn Navigator(&self) -> DomRoot<Navigator> {
1160 self.navigator
1161 .or_init(|| Navigator::new(self, CanGc::note()))
1162 }
1163
1164 fn SetTimeout(
1166 &self,
1167 _cx: JSContext,
1168 callback: TrustedScriptOrStringOrFunction,
1169 timeout: i32,
1170 args: Vec<HandleValue>,
1171 can_gc: CanGc,
1172 ) -> Fallible<i32> {
1173 let callback = match callback {
1174 TrustedScriptOrStringOrFunction::String(i) => {
1175 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1176 },
1177 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1178 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1179 },
1180 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1181 };
1182 self.as_global_scope().set_timeout_or_interval(
1183 callback,
1184 args,
1185 Duration::from_millis(timeout.max(0) as u64),
1186 IsInterval::NonInterval,
1187 can_gc,
1188 )
1189 }
1190
1191 fn ClearTimeout(&self, handle: i32) {
1193 self.as_global_scope().clear_timeout_or_interval(handle);
1194 }
1195
1196 fn SetInterval(
1198 &self,
1199 _cx: JSContext,
1200 callback: TrustedScriptOrStringOrFunction,
1201 timeout: i32,
1202 args: Vec<HandleValue>,
1203 can_gc: CanGc,
1204 ) -> Fallible<i32> {
1205 let callback = match callback {
1206 TrustedScriptOrStringOrFunction::String(i) => {
1207 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1208 },
1209 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1210 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1211 },
1212 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1213 };
1214 self.as_global_scope().set_timeout_or_interval(
1215 callback,
1216 args,
1217 Duration::from_millis(timeout.max(0) as u64),
1218 IsInterval::Interval,
1219 can_gc,
1220 )
1221 }
1222
1223 fn ClearInterval(&self, handle: i32) {
1225 self.ClearTimeout(handle);
1226 }
1227
1228 fn QueueMicrotask(&self, callback: Rc<VoidFunction>) {
1230 self.as_global_scope().queue_function_as_microtask(callback);
1231 }
1232
1233 fn CreateImageBitmap(
1235 &self,
1236 image: ImageBitmapSource,
1237 options: &ImageBitmapOptions,
1238 can_gc: CanGc,
1239 ) -> Rc<Promise> {
1240 ImageBitmap::create_image_bitmap(
1241 self.as_global_scope(),
1242 image,
1243 0,
1244 0,
1245 None,
1246 None,
1247 options,
1248 can_gc,
1249 )
1250 }
1251
1252 fn CreateImageBitmap_(
1254 &self,
1255 image: ImageBitmapSource,
1256 sx: i32,
1257 sy: i32,
1258 sw: i32,
1259 sh: i32,
1260 options: &ImageBitmapOptions,
1261 can_gc: CanGc,
1262 ) -> Rc<Promise> {
1263 ImageBitmap::create_image_bitmap(
1264 self.as_global_scope(),
1265 image,
1266 sx,
1267 sy,
1268 Some(sw),
1269 Some(sh),
1270 options,
1271 can_gc,
1272 )
1273 }
1274
1275 fn Window(&self) -> DomRoot<WindowProxy> {
1277 self.window_proxy()
1278 }
1279
1280 fn Self_(&self) -> DomRoot<WindowProxy> {
1282 self.window_proxy()
1283 }
1284
1285 fn Frames(&self) -> DomRoot<WindowProxy> {
1287 self.window_proxy()
1288 }
1289
1290 fn Length(&self) -> u32 {
1292 self.Document().iframes().iter().count() as u32
1293 }
1294
1295 fn GetParent(&self) -> Option<DomRoot<WindowProxy>> {
1297 let window_proxy = self.undiscarded_window_proxy()?;
1299
1300 if let Some(parent) = window_proxy.parent() {
1302 return Some(DomRoot::from_ref(parent));
1303 }
1304 Some(window_proxy)
1306 }
1307
1308 fn GetTop(&self) -> Option<DomRoot<WindowProxy>> {
1310 let window_proxy = self.undiscarded_window_proxy()?;
1312
1313 Some(DomRoot::from_ref(window_proxy.top()))
1315 }
1316
1317 fn Performance(&self) -> DomRoot<Performance> {
1320 self.performance.or_init(|| {
1321 Performance::new(
1322 self.as_global_scope(),
1323 self.navigation_start.get(),
1324 CanGc::note(),
1325 )
1326 })
1327 }
1328
1329 global_event_handlers!();
1331
1332 window_event_handlers!();
1334
1335 fn Screen(&self) -> DomRoot<Screen> {
1337 self.screen.or_init(|| Screen::new(self, CanGc::note()))
1338 }
1339
1340 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
1342 base64_btoa(btoa)
1343 }
1344
1345 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
1347 base64_atob(atob)
1348 }
1349
1350 fn RequestAnimationFrame(&self, callback: Rc<FrameRequestCallback>) -> u32 {
1352 self.Document()
1353 .request_animation_frame(AnimationFrameCallback::FrameRequestCallback { callback })
1354 }
1355
1356 fn CancelAnimationFrame(&self, ident: u32) {
1358 let doc = self.Document();
1359 doc.cancel_animation_frame(ident);
1360 }
1361
1362 fn PostMessage(
1364 &self,
1365 cx: JSContext,
1366 message: HandleValue,
1367 target_origin: USVString,
1368 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1369 ) -> ErrorResult {
1370 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1371 let source = incumbent.as_window();
1372 let source_origin = source.Document().origin().immutable().clone();
1373
1374 self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
1375 }
1376
1377 fn PostMessage_(
1379 &self,
1380 cx: JSContext,
1381 message: HandleValue,
1382 options: RootedTraceableBox<WindowPostMessageOptions>,
1383 ) -> ErrorResult {
1384 let mut rooted = CustomAutoRooter::new(
1385 options
1386 .parent
1387 .transfer
1388 .iter()
1389 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
1390 .collect(),
1391 );
1392 let transfer = CustomAutoRooterGuard::new(*cx, &mut rooted);
1393
1394 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
1395 let source = incumbent.as_window();
1396
1397 let source_origin = source.Document().origin().immutable().clone();
1398
1399 self.post_message_impl(
1400 &options.targetOrigin,
1401 source_origin,
1402 source,
1403 cx,
1404 message,
1405 transfer,
1406 )
1407 }
1408
1409 fn CaptureEvents(&self) {
1411 }
1413
1414 fn ReleaseEvents(&self) {
1416 }
1418
1419 fn Debug(&self, message: DOMString) {
1421 debug!("{}", message);
1422 }
1423
1424 #[allow(unsafe_code)]
1425 fn Gc(&self) {
1426 unsafe {
1427 JS_GC(*self.get_cx(), GCReason::API);
1428 }
1429 }
1430
1431 #[allow(unsafe_code)]
1432 fn Js_backtrace(&self) {
1433 unsafe {
1434 println!("Current JS stack:");
1435 dump_js_stack(*self.get_cx());
1436 let rust_stack = Backtrace::new();
1437 println!("Current Rust stack:\n{:?}", rust_stack);
1438 }
1439 }
1440
1441 fn WebdriverCallback(&self, cx: JSContext, val: HandleValue, realm: InRealm, can_gc: CanGc) {
1442 let rv = jsval_to_webdriver(cx, &self.globalscope, val, realm, can_gc);
1443 let opt_chan = self.webdriver_script_chan.borrow_mut().take();
1444 if let Some(chan) = opt_chan {
1445 let _ = chan.send(rv);
1446 }
1447 }
1448
1449 fn WebdriverException(&self, cx: JSContext, val: HandleValue, realm: InRealm, can_gc: CanGc) {
1450 let rv = jsval_to_webdriver(cx, &self.globalscope, val, realm, can_gc);
1451 let opt_chan = self.webdriver_script_chan.borrow_mut().take();
1452 if let Some(chan) = opt_chan {
1453 if let Ok(rv) = rv {
1454 let _ = chan.send(Err(WebDriverJSError::JSException(rv)));
1455 } else {
1456 let _ = chan.send(rv);
1457 }
1458 }
1459 }
1460
1461 fn WebdriverTimeout(&self) {
1462 let opt_chan = self.webdriver_script_chan.borrow_mut().take();
1463 if let Some(chan) = opt_chan {
1464 let _ = chan.send(Err(WebDriverJSError::Timeout));
1465 }
1466 }
1467
1468 fn WebdriverElement(&self, id: DOMString) -> Option<DomRoot<Element>> {
1469 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1470 }
1471
1472 fn WebdriverFrame(&self, browsing_context_id: DOMString) -> Option<DomRoot<WindowProxy>> {
1473 self.Document()
1474 .iframes()
1475 .iter()
1476 .find(|iframe| {
1477 iframe
1478 .browsing_context_id()
1479 .as_ref()
1480 .map(BrowsingContextId::to_string) ==
1481 Some(browsing_context_id.to_string())
1482 })
1483 .and_then(|iframe| iframe.GetContentWindow())
1484 }
1485
1486 fn WebdriverWindow(&self, webview_id: DOMString) -> Option<DomRoot<WindowProxy>> {
1487 let window_proxy = self.window_proxy.get()?;
1488
1489 if window_proxy.browsing_context_id() != window_proxy.webview_id() {
1491 return None;
1492 }
1493
1494 if self.webview_id().to_string() == webview_id.str() {
1495 Some(DomRoot::from_ref(&window_proxy))
1496 } else {
1497 None
1498 }
1499 }
1500
1501 fn WebdriverShadowRoot(&self, id: DOMString) -> Option<DomRoot<ShadowRoot>> {
1502 find_node_by_unique_id_in_document(&self.Document(), id.into()).and_then(Root::downcast)
1503 }
1504
1505 fn GetComputedStyle(
1507 &self,
1508 element: &Element,
1509 pseudo: Option<DOMString>,
1510 ) -> DomRoot<CSSStyleDeclaration> {
1511 let mut is_null = false;
1515
1516 let pseudo = pseudo.map(|mut s| {
1519 s.make_ascii_lowercase();
1520 s
1521 });
1522 let pseudo = match pseudo {
1523 Some(ref pseudo) if pseudo == ":before" || pseudo == "::before" => {
1524 Some(PseudoElement::Before)
1525 },
1526 Some(ref pseudo) if pseudo == ":after" || pseudo == "::after" => {
1527 Some(PseudoElement::After)
1528 },
1529 Some(ref pseudo) if pseudo == "::selection" => Some(PseudoElement::Selection),
1530 Some(ref pseudo) if pseudo == "::marker" => Some(PseudoElement::Marker),
1531 Some(ref pseudo) if pseudo.starts_with(':') => {
1532 is_null = true;
1535 None
1536 },
1537 _ => None,
1538 };
1539
1540 CSSStyleDeclaration::new(
1556 self,
1557 if is_null {
1558 CSSStyleOwner::Null
1559 } else {
1560 CSSStyleOwner::Element(Dom::from_ref(element))
1561 },
1562 pseudo,
1563 CSSModificationAccess::Readonly,
1564 CanGc::note(),
1565 )
1566 }
1567
1568 fn InnerHeight(&self) -> i32 {
1571 self.viewport_details
1572 .get()
1573 .size
1574 .height
1575 .to_i32()
1576 .unwrap_or(0)
1577 }
1578
1579 fn InnerWidth(&self) -> i32 {
1582 self.viewport_details.get().size.width.to_i32().unwrap_or(0)
1583 }
1584
1585 fn ScrollX(&self) -> i32 {
1587 self.scroll_offset().x as i32
1588 }
1589
1590 fn PageXOffset(&self) -> i32 {
1592 self.ScrollX()
1593 }
1594
1595 fn ScrollY(&self) -> i32 {
1597 self.scroll_offset().y as i32
1598 }
1599
1600 fn PageYOffset(&self) -> i32 {
1602 self.ScrollY()
1603 }
1604
1605 fn Scroll(&self, options: &ScrollToOptions) {
1607 let left = options.left.unwrap_or(0.0f64);
1609 let top = options.top.unwrap_or(0.0f64);
1610 self.scroll(left, top, options.parent.behavior);
1611 }
1612
1613 fn Scroll_(&self, x: f64, y: f64) {
1615 self.scroll(x, y, ScrollBehavior::Auto);
1616 }
1617
1618 fn ScrollTo(&self, options: &ScrollToOptions) {
1620 self.Scroll(options);
1621 }
1622
1623 fn ScrollTo_(&self, x: f64, y: f64) {
1625 self.scroll(x, y, ScrollBehavior::Auto);
1626 }
1627
1628 fn ScrollBy(&self, options: &ScrollToOptions) {
1630 let x = options.left.unwrap_or(0.0f64);
1632 let y = options.top.unwrap_or(0.0f64);
1633 self.ScrollBy_(x, y);
1634 self.scroll(x, y, options.parent.behavior);
1635 }
1636
1637 fn ScrollBy_(&self, x: f64, y: f64) {
1639 let scroll_offset = self.scroll_offset();
1640 let left = x + scroll_offset.x as f64;
1642 let top = y + scroll_offset.y as f64;
1644
1645 self.scroll(left, top, ScrollBehavior::Auto);
1647 }
1648
1649 fn ResizeTo(&self, width: i32, height: i32) {
1651 let window_proxy = match self.window_proxy.get() {
1653 Some(proxy) => proxy,
1654 None => return,
1655 };
1656
1657 if !window_proxy.is_auxiliary() {
1660 return;
1661 }
1662
1663 let dpr = self.device_pixel_ratio();
1664 let size = Size2D::new(width, height).to_f32() * dpr;
1665 self.send_to_embedder(EmbedderMsg::ResizeTo(self.webview_id(), size.to_i32()));
1666 }
1667
1668 fn ResizeBy(&self, x: i32, y: i32) {
1670 let size = self.client_window().size();
1671 self.ResizeTo(x + size.width, y + size.height)
1673 }
1674
1675 fn MoveTo(&self, x: i32, y: i32) {
1677 let dpr = self.device_pixel_ratio();
1680 let point = Point2D::new(x, y).to_f32() * dpr;
1681 let msg = EmbedderMsg::MoveTo(self.webview_id(), point.to_i32());
1682 self.send_to_embedder(msg);
1683 }
1684
1685 fn MoveBy(&self, x: i32, y: i32) {
1687 let origin = self.client_window().min;
1688 self.MoveTo(x + origin.x, y + origin.y)
1690 }
1691
1692 fn ScreenX(&self) -> i32 {
1694 self.client_window().min.x
1695 }
1696
1697 fn ScreenY(&self) -> i32 {
1699 self.client_window().min.y
1700 }
1701
1702 fn OuterHeight(&self) -> i32 {
1704 self.client_window().height()
1705 }
1706
1707 fn OuterWidth(&self) -> i32 {
1709 self.client_window().width()
1710 }
1711
1712 fn DevicePixelRatio(&self) -> Finite<f64> {
1714 Finite::wrap(self.device_pixel_ratio().get() as f64)
1715 }
1716
1717 fn Status(&self) -> DOMString {
1719 self.status.borrow().clone()
1720 }
1721
1722 fn SetStatus(&self, status: DOMString) {
1724 *self.status.borrow_mut() = status
1725 }
1726
1727 fn MatchMedia(&self, query: DOMString) -> DomRoot<MediaQueryList> {
1729 let media_query_list = MediaList::parse_media_list(&query, self);
1730 let document = self.Document();
1731 let mql = MediaQueryList::new(&document, media_query_list, CanGc::note());
1732 self.media_query_lists.track(&*mql);
1733 mql
1734 }
1735
1736 fn Fetch(
1738 &self,
1739 input: RequestOrUSVString,
1740 init: RootedTraceableBox<RequestInit>,
1741 comp: InRealm,
1742 can_gc: CanGc,
1743 ) -> Rc<Promise> {
1744 fetch::Fetch(self.upcast(), input, init, comp, can_gc)
1745 }
1746
1747 #[cfg(feature = "bluetooth")]
1748 fn TestRunner(&self) -> DomRoot<TestRunner> {
1749 self.test_runner
1750 .or_init(|| TestRunner::new(self.upcast(), CanGc::note()))
1751 }
1752
1753 fn RunningAnimationCount(&self) -> u32 {
1754 self.document
1755 .get()
1756 .map_or(0, |d| d.animations().running_animation_count() as u32)
1757 }
1758
1759 fn SetName(&self, name: DOMString) {
1761 if let Some(proxy) = self.undiscarded_window_proxy() {
1762 proxy.set_name(name);
1763 }
1764 }
1765
1766 fn Name(&self) -> DOMString {
1768 match self.undiscarded_window_proxy() {
1769 Some(proxy) => proxy.get_name(),
1770 None => "".into(),
1771 }
1772 }
1773
1774 fn Origin(&self) -> USVString {
1776 USVString(self.origin().immutable().ascii_serialization())
1777 }
1778
1779 fn GetSelection(&self) -> Option<DomRoot<Selection>> {
1781 self.document
1782 .get()
1783 .and_then(|d| d.GetSelection(CanGc::note()))
1784 }
1785
1786 fn Event(&self, cx: JSContext, rval: MutableHandleValue) {
1788 if let Some(ref event) = *self.current_event.borrow() {
1789 event.reflector().get_jsobject().safe_to_jsval(cx, rval);
1790 }
1791 }
1792
1793 fn IsSecureContext(&self) -> bool {
1794 self.as_global_scope().is_secure_context()
1795 }
1796
1797 fn NamedGetter(&self, name: DOMString) -> Option<NamedPropertyValue> {
1799 if name.is_empty() {
1800 return None;
1801 }
1802 let document = self.Document();
1803
1804 let iframes: Vec<_> = document
1806 .iframes()
1807 .iter()
1808 .filter(|iframe| {
1809 if let Some(window) = iframe.GetContentWindow() {
1810 return window.get_name() == name;
1811 }
1812 false
1813 })
1814 .collect();
1815
1816 let iframe_iter = iframes.iter().map(|iframe| iframe.upcast::<Element>());
1817
1818 let name = Atom::from(&*name);
1819
1820 let elements_with_name = document.get_elements_with_name(&name);
1822 let name_iter = elements_with_name
1823 .iter()
1824 .map(|element| &**element)
1825 .filter(|elem| is_named_element_with_name_attribute(elem));
1826 let elements_with_id = document.get_elements_with_id(&name);
1827 let id_iter = elements_with_id
1828 .iter()
1829 .map(|element| &**element)
1830 .filter(|elem| is_named_element_with_id_attribute(elem));
1831
1832 for elem in iframe_iter.clone() {
1834 if let Some(nested_window_proxy) = elem
1835 .downcast::<HTMLIFrameElement>()
1836 .and_then(|iframe| iframe.GetContentWindow())
1837 {
1838 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
1839 }
1840 }
1841
1842 let mut elements = iframe_iter.chain(name_iter).chain(id_iter);
1843
1844 let first = elements.next()?;
1845
1846 if elements.next().is_none() {
1847 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
1849 }
1850
1851 #[derive(JSTraceable, MallocSizeOf)]
1853 struct WindowNamedGetter {
1854 #[no_trace]
1855 name: Atom,
1856 }
1857 impl CollectionFilter for WindowNamedGetter {
1858 fn filter(&self, elem: &Element, _root: &Node) -> bool {
1859 let type_ = match elem.upcast::<Node>().type_id() {
1860 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
1861 _ => return false,
1862 };
1863 if elem.get_id().as_ref() == Some(&self.name) {
1864 return true;
1865 }
1866 match type_ {
1867 HTMLElementTypeId::HTMLEmbedElement |
1868 HTMLElementTypeId::HTMLFormElement |
1869 HTMLElementTypeId::HTMLImageElement |
1870 HTMLElementTypeId::HTMLObjectElement => {
1871 elem.get_name().as_ref() == Some(&self.name)
1872 },
1873 _ => false,
1874 }
1875 }
1876 }
1877 let collection = HTMLCollection::create(
1878 self,
1879 document.upcast(),
1880 Box::new(WindowNamedGetter { name }),
1881 CanGc::note(),
1882 );
1883 Some(NamedPropertyValue::HTMLCollection(collection))
1884 }
1885
1886 fn SupportedPropertyNames(&self) -> Vec<DOMString> {
1888 let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
1889
1890 let document = self.Document();
1891 let name_map = document.name_map();
1892 for (name, elements) in &name_map.0 {
1893 if name.is_empty() {
1894 continue;
1895 }
1896 let mut name_iter = elements
1897 .iter()
1898 .filter(|elem| is_named_element_with_name_attribute(elem));
1899 if let Some(first) = name_iter.next() {
1900 names_with_first_named_element_map.insert(name, first);
1901 }
1902 }
1903 let id_map = document.id_map();
1904 for (id, elements) in &id_map.0 {
1905 if id.is_empty() {
1906 continue;
1907 }
1908 let mut id_iter = elements
1909 .iter()
1910 .filter(|elem| is_named_element_with_id_attribute(elem));
1911 if let Some(first) = id_iter.next() {
1912 match names_with_first_named_element_map.entry(id) {
1913 Entry::Vacant(entry) => drop(entry.insert(first)),
1914 Entry::Occupied(mut entry) => {
1915 if first.upcast::<Node>().is_before(entry.get().upcast()) {
1916 *entry.get_mut() = first;
1917 }
1918 },
1919 }
1920 }
1921 }
1922
1923 let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
1924 names_with_first_named_element_map
1925 .iter()
1926 .map(|(k, v)| (*k, *v))
1927 .collect();
1928 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
1929 if a.1 == b.1 {
1930 a.0.cmp(b.0)
1933 } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
1934 cmp::Ordering::Less
1935 } else {
1936 cmp::Ordering::Greater
1937 }
1938 });
1939
1940 names_with_first_named_element_vec
1941 .iter()
1942 .map(|(k, _v)| DOMString::from(&***k))
1943 .collect()
1944 }
1945
1946 fn StructuredClone(
1948 &self,
1949 cx: JSContext,
1950 value: HandleValue,
1951 options: RootedTraceableBox<StructuredSerializeOptions>,
1952 retval: MutableHandleValue,
1953 ) -> Fallible<()> {
1954 self.as_global_scope()
1955 .structured_clone(cx, value, options, retval)
1956 }
1957
1958 fn TrustedTypes(&self, can_gc: CanGc) -> DomRoot<TrustedTypePolicyFactory> {
1959 self.trusted_types
1960 .or_init(|| TrustedTypePolicyFactory::new(self.as_global_scope(), can_gc))
1961 }
1962}
1963
1964impl Window {
1965 pub(crate) fn scroll_offset(&self) -> Vector2D<f32, LayoutPixel> {
1966 self.scroll_offset_query_with_external_scroll_id(self.pipeline_id().root_scroll_id())
1967 }
1968
1969 #[allow(unsafe_code)]
1972 pub(crate) fn create_named_properties_object(
1973 cx: JSContext,
1974 proto: HandleObject,
1975 object: MutableHandleObject,
1976 ) {
1977 window_named_properties::create(cx, proto, object)
1978 }
1979
1980 pub(crate) fn current_event(&self) -> Option<DomRoot<Event>> {
1981 self.current_event
1982 .borrow()
1983 .as_ref()
1984 .map(|e| DomRoot::from_ref(&**e))
1985 }
1986
1987 pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
1988 let current = self.current_event();
1989 *self.current_event.borrow_mut() = event.map(Dom::from_ref);
1990 current
1991 }
1992
1993 fn post_message_impl(
1995 &self,
1996 target_origin: &USVString,
1997 source_origin: ImmutableOrigin,
1998 source: &Window,
1999 cx: JSContext,
2000 message: HandleValue,
2001 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
2002 ) -> ErrorResult {
2003 let data = structuredclone::write(cx, message, Some(transfer))?;
2005
2006 let target_origin = match target_origin.0[..].as_ref() {
2008 "*" => None,
2009 "/" => Some(source_origin.clone()),
2010 url => match ServoUrl::parse(url) {
2011 Ok(url) => Some(url.origin().clone()),
2012 Err(_) => return Err(Error::Syntax),
2013 },
2014 };
2015
2016 self.post_message(target_origin, source_origin, &source.window_proxy(), data);
2018 Ok(())
2019 }
2020
2021 pub(crate) fn paint_worklet(&self) -> DomRoot<Worklet> {
2023 self.paint_worklet
2024 .or_init(|| self.new_paint_worklet(CanGc::note()))
2025 }
2026
2027 pub(crate) fn has_document(&self) -> bool {
2028 self.document.get().is_some()
2029 }
2030
2031 pub(crate) fn clear_js_runtime(&self) {
2032 self.as_global_scope()
2033 .remove_web_messaging_and_dedicated_workers_infra();
2034
2035 if let Some(custom_elements) = self.custom_element_registry.get() {
2038 custom_elements.teardown();
2039 }
2040
2041 self.current_state.set(WindowState::Zombie);
2042 *self.js_runtime.borrow_mut() = None;
2043
2044 if let Some(proxy) = self.window_proxy.get() {
2047 let pipeline_id = self.pipeline_id();
2048 if let Some(currently_active) = proxy.currently_active() {
2049 if currently_active == pipeline_id {
2050 self.window_proxy.set(None);
2051 }
2052 }
2053 }
2054
2055 if let Some(performance) = self.performance.get() {
2056 performance.clear_and_disable_performance_entry_buffer();
2057 }
2058 self.as_global_scope()
2059 .task_manager()
2060 .cancel_all_tasks_and_ignore_future_tasks();
2061 }
2062
2063 pub(crate) fn scroll(&self, x_: f64, y_: f64, behavior: ScrollBehavior) {
2065 let xfinite = if x_.is_finite() { x_ } else { 0.0f64 };
2067 let yfinite = if y_.is_finite() { y_ } else { 0.0f64 };
2068
2069 let viewport = self.viewport_details.get().size;
2074
2075 let scrolling_area = self.scrolling_area_query(None);
2078 let x = xfinite
2079 .min(scrolling_area.width() as f64 - viewport.width as f64)
2080 .max(0.0f64);
2081 let y = yfinite
2082 .min(scrolling_area.height() as f64 - viewport.height as f64)
2083 .max(0.0f64);
2084
2085 let scroll_offset = self.scroll_offset();
2088 if x == scroll_offset.x as f64 && y == scroll_offset.y as f64 {
2089 return;
2090 }
2091
2092 self.perform_a_scroll(
2098 x.to_f32().unwrap_or(0.0f32),
2099 y.to_f32().unwrap_or(0.0f32),
2100 self.pipeline_id().root_scroll_id(),
2101 behavior,
2102 None,
2103 );
2104 }
2105
2106 pub(crate) fn perform_a_scroll(
2108 &self,
2109 x: f32,
2110 y: f32,
2111 scroll_id: ExternalScrollId,
2112 _behavior: ScrollBehavior,
2113 element: Option<&Element>,
2114 ) {
2115 let reflow_phases_run =
2119 self.reflow(ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)));
2120 if reflow_phases_run.needs_frame() {
2121 self.compositor_api().generate_frame();
2122 }
2123
2124 if reflow_phases_run.contains(ReflowPhasesRun::UpdatedScrollNodeOffset) {
2129 match element {
2130 Some(el) => self.Document().handle_element_scroll_event(el),
2131 None => self.Document().handle_viewport_scroll_event(),
2132 };
2133 }
2134 }
2135
2136 pub(crate) fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
2137 self.viewport_details.get().hidpi_scale_factor
2138 }
2139
2140 fn client_window(&self) -> DeviceIndependentIntRect {
2141 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel!");
2142
2143 self.send_to_embedder(EmbedderMsg::GetWindowRect(self.webview_id(), sender));
2144
2145 receiver.recv().unwrap_or_default()
2146 }
2147
2148 #[allow(unsafe_code)]
2151 pub(crate) fn advance_animation_clock(&self, delta_ms: i32) {
2152 self.Document()
2153 .advance_animation_timeline_for_testing(delta_ms as f64 / 1000.);
2154 ScriptThread::handle_tick_all_animations_for_testing(self.pipeline_id());
2155 }
2156
2157 pub(crate) fn reflow(&self, reflow_goal: ReflowGoal) -> ReflowPhasesRun {
2165 let document = self.Document();
2166
2167 if !document.is_fully_active() {
2169 return ReflowPhasesRun::empty();
2170 }
2171
2172 self.Document().ensure_safe_to_run_script_or_layout();
2173
2174 let pipeline_id = self.pipeline_id();
2178 if reflow_goal == ReflowGoal::UpdateTheRendering &&
2179 self.layout_blocker.get().layout_blocked()
2180 {
2181 debug!("Suppressing pre-load-event reflow pipeline {pipeline_id}");
2182 return ReflowPhasesRun::empty();
2183 }
2184
2185 debug!("script: performing reflow for goal {reflow_goal:?}");
2186 let marker = if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) {
2187 Some(TimelineMarker::start("Reflow".to_owned()))
2188 } else {
2189 None
2190 };
2191
2192 let restyle_reason = document.restyle_reason();
2193 document.clear_restyle_reasons();
2194 let restyle = if restyle_reason.needs_restyle() {
2195 debug!("Invalidating layout cache due to reflow condition {restyle_reason:?}",);
2196 self.layout_marker.borrow().set(false);
2198 *self.layout_marker.borrow_mut() = Rc::new(Cell::new(true));
2200
2201 let stylesheets_changed = document.flush_stylesheets_for_reflow();
2202 let pending_restyles = document.drain_pending_restyles();
2203 let dirty_root = document
2204 .take_dirty_root()
2205 .filter(|_| !stylesheets_changed)
2206 .or_else(|| document.GetDocumentElement())
2207 .map(|root| root.upcast::<Node>().to_trusted_node_address());
2208
2209 Some(ReflowRequestRestyle {
2210 reason: restyle_reason,
2211 dirty_root,
2212 stylesheets_changed,
2213 pending_restyles,
2214 })
2215 } else {
2216 None
2217 };
2218
2219 let reflow = ReflowRequest {
2220 document: document.upcast::<Node>().to_trusted_node_address(),
2221 restyle,
2222 viewport_details: self.viewport_details.get(),
2223 origin: self.origin().immutable().clone(),
2224 reflow_goal,
2225 dom_count: document.dom_count(),
2226 animation_timeline_value: document.current_animation_timeline_value(),
2227 animations: document.animations().sets.clone(),
2228 node_to_animating_image_map: document.image_animation_manager().node_to_image_map(),
2229 theme: self.theme.get(),
2230 highlighted_dom_node: document.highlighted_dom_node().map(|node| node.to_opaque()),
2231 };
2232
2233 let Some(reflow_result) = self.layout.borrow_mut().reflow(reflow) else {
2234 return ReflowPhasesRun::empty();
2235 };
2236
2237 debug!("script: layout complete");
2238 if let Some(marker) = marker {
2239 self.emit_timeline_marker(marker.end());
2240 }
2241
2242 self.handle_pending_images_post_reflow(
2243 reflow_result.pending_images,
2244 reflow_result.pending_rasterization_images,
2245 reflow_result.pending_svg_elements_for_serialization,
2246 );
2247
2248 if let Some(iframe_sizes) = reflow_result.iframe_sizes {
2249 document
2250 .iframes_mut()
2251 .handle_new_iframe_sizes_after_layout(self, iframe_sizes);
2252 }
2253
2254 document.update_animations_post_reflow();
2255 self.update_constellation_epoch();
2256
2257 reflow_result.reflow_phases_run
2258 }
2259
2260 pub(crate) fn maybe_send_idle_document_state_to_constellation(&self) {
2261 if !opts::get().wait_for_stable_image {
2262 return;
2263 }
2264
2265 if self.has_sent_idle_message.get() {
2266 return;
2267 }
2268
2269 let document = self.Document();
2270 if document.ReadyState() != DocumentReadyState::Complete {
2271 return;
2272 }
2273
2274 if document.GetDocumentElement().is_some_and(|elem| {
2278 elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
2279 elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
2280 }) {
2281 return;
2282 }
2283
2284 if self.font_context().web_fonts_still_loading() != 0 {
2285 return;
2286 }
2287
2288 if !self.pending_layout_images.borrow().is_empty() ||
2289 !self.pending_images_for_rasterization.borrow().is_empty()
2290 {
2291 return;
2292 }
2293
2294 if self.Document().needs_rendering_update() {
2295 return;
2296 }
2297
2298 debug!(
2302 "{:?}: Sending DocumentState::Idle to Constellation",
2303 self.pipeline_id()
2304 );
2305 self.send_to_constellation(ScriptToConstellationMessage::SetDocumentState(
2306 DocumentState::Idle,
2307 ));
2308 self.has_sent_idle_message.set(true);
2309 }
2310
2311 pub(crate) fn reflow_if_reflow_timer_expired(&self) {
2314 if !matches!(
2317 self.layout_blocker.get(),
2318 LayoutBlocker::Parsing(instant) if instant + INITIAL_REFLOW_DELAY < Instant::now()
2319 ) {
2320 return;
2321 }
2322 self.allow_layout_if_necessary();
2323 }
2324
2325 pub(crate) fn prevent_layout_until_load_event(&self) {
2329 if !matches!(self.layout_blocker.get(), LayoutBlocker::WaitingForParse) {
2332 return;
2333 }
2334
2335 self.layout_blocker
2336 .set(LayoutBlocker::Parsing(Instant::now()));
2337 }
2338
2339 pub(crate) fn allow_layout_if_necessary(&self) {
2342 if matches!(
2343 self.layout_blocker.get(),
2344 LayoutBlocker::FiredLoadEventOrParsingTimerExpired
2345 ) {
2346 return;
2347 }
2348
2349 self.layout_blocker
2350 .set(LayoutBlocker::FiredLoadEventOrParsingTimerExpired);
2351
2352 if self.Document().update_the_rendering().needs_frame() {
2364 self.compositor_api().generate_frame();
2365 }
2366 }
2367
2368 pub(crate) fn layout_blocked(&self) -> bool {
2369 self.layout_blocker.get().layout_blocked()
2370 }
2371
2372 pub(crate) fn update_constellation_epoch(&self) {
2375 if !opts::get().wait_for_stable_image {
2376 return;
2377 }
2378
2379 let epoch = self.layout.borrow().current_epoch();
2380 debug!(
2381 "{:?}: Updating constellation epoch: {epoch:?}",
2382 self.pipeline_id()
2383 );
2384 let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
2385 let event = ScriptToConstellationMessage::SetLayoutEpoch(epoch, sender);
2386 self.send_to_constellation(event);
2387 let _ = receiver.recv();
2388 }
2389
2390 pub(crate) fn layout_reflow(&self, query_msg: QueryMsg) {
2392 self.reflow(ReflowGoal::LayoutQuery(query_msg));
2393 }
2394
2395 pub(crate) fn resolved_font_style_query(
2396 &self,
2397 node: &Node,
2398 value: String,
2399 ) -> Option<ServoArc<Font>> {
2400 self.layout_reflow(QueryMsg::ResolvedFontStyleQuery);
2401
2402 let document = self.Document();
2403 let animations = document.animations().sets.clone();
2404 self.layout.borrow().query_resolved_font_style(
2405 node.to_trusted_node_address(),
2406 &value,
2407 animations,
2408 document.current_animation_timeline_value(),
2409 )
2410 }
2411
2412 pub(crate) fn box_area_query_without_reflow(
2417 &self,
2418 node: &Node,
2419 area: BoxAreaType,
2420 ) -> Option<UntypedRect<Au>> {
2421 let layout = self.layout.borrow();
2422 layout.ensure_stacking_context_tree(self.viewport_details.get());
2423 layout.query_box_area(node.to_trusted_node_address(), area)
2424 }
2425
2426 pub(crate) fn box_area_query(&self, node: &Node, area: BoxAreaType) -> Option<UntypedRect<Au>> {
2427 self.layout_reflow(QueryMsg::BoxArea);
2428 self.box_area_query_without_reflow(node, area)
2429 }
2430
2431 pub(crate) fn box_areas_query(&self, node: &Node, area: BoxAreaType) -> Vec<UntypedRect<Au>> {
2432 self.layout_reflow(QueryMsg::BoxAreas);
2433 self.layout
2434 .borrow()
2435 .query_box_areas(node.to_trusted_node_address(), area)
2436 }
2437
2438 pub(crate) fn client_rect_query(&self, node: &Node) -> UntypedRect<i32> {
2439 self.layout_reflow(QueryMsg::ClientRectQuery);
2440 self.layout
2441 .borrow()
2442 .query_client_rect(node.to_trusted_node_address())
2443 }
2444
2445 pub(crate) fn scrolling_area_query(&self, node: Option<&Node>) -> UntypedRect<i32> {
2448 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2449 self.layout
2450 .borrow()
2451 .query_scrolling_area(node.map(Node::to_trusted_node_address))
2452 }
2453
2454 pub(crate) fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32, LayoutPixel> {
2455 let external_scroll_id = ExternalScrollId(
2456 combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody),
2457 self.pipeline_id().into(),
2458 );
2459 self.scroll_offset_query_with_external_scroll_id(external_scroll_id)
2460 }
2461
2462 fn scroll_offset_query_with_external_scroll_id(
2463 &self,
2464 external_scroll_id: ExternalScrollId,
2465 ) -> Vector2D<f32, LayoutPixel> {
2466 self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
2467 self.scroll_offset_query_with_external_scroll_id_no_reflow(external_scroll_id)
2468 }
2469
2470 fn scroll_offset_query_with_external_scroll_id_no_reflow(
2471 &self,
2472 external_scroll_id: ExternalScrollId,
2473 ) -> Vector2D<f32, LayoutPixel> {
2474 self.layout
2475 .borrow()
2476 .scroll_offset(external_scroll_id)
2477 .unwrap_or_default()
2478 }
2479
2480 pub(crate) fn scroll_an_element(
2483 &self,
2484 element: &Element,
2485 x_: f64,
2486 y_: f64,
2487 behavior: ScrollBehavior,
2488 ) {
2489 let scroll_id = ExternalScrollId(
2490 combine_id_with_fragment_type(
2491 element.upcast::<Node>().to_opaque().id(),
2492 FragmentType::FragmentBody,
2493 ),
2494 self.pipeline_id().into(),
2495 );
2496
2497 self.perform_a_scroll(
2501 x_.to_f32().unwrap_or(0.0f32),
2502 y_.to_f32().unwrap_or(0.0f32),
2503 scroll_id,
2504 behavior,
2505 Some(element),
2506 );
2507 }
2508
2509 pub(crate) fn resolved_style_query(
2510 &self,
2511 element: TrustedNodeAddress,
2512 pseudo: Option<PseudoElement>,
2513 property: PropertyId,
2514 ) -> DOMString {
2515 self.layout_reflow(QueryMsg::ResolvedStyleQuery);
2516
2517 let document = self.Document();
2518 let animations = document.animations().sets.clone();
2519 DOMString::from(self.layout.borrow().query_resolved_style(
2520 element,
2521 pseudo,
2522 property,
2523 animations,
2524 document.current_animation_timeline_value(),
2525 ))
2526 }
2527
2528 pub(crate) fn get_iframe_viewport_details_if_known(
2532 &self,
2533 browsing_context_id: BrowsingContextId,
2534 ) -> Option<ViewportDetails> {
2535 self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery);
2537 self.Document()
2538 .iframes()
2539 .get(browsing_context_id)
2540 .and_then(|iframe| iframe.size)
2541 }
2542
2543 #[allow(unsafe_code)]
2544 pub(crate) fn offset_parent_query(
2545 &self,
2546 node: &Node,
2547 ) -> (Option<DomRoot<Element>>, UntypedRect<Au>) {
2548 self.layout_reflow(QueryMsg::OffsetParentQuery);
2549 let response = self
2550 .layout
2551 .borrow()
2552 .query_offset_parent(node.to_trusted_node_address());
2553 let element = response.node_address.and_then(|parent_node_address| {
2554 let node = unsafe { from_untrusted_node_address(parent_node_address) };
2555 DomRoot::downcast(node)
2556 });
2557 (element, response.rect)
2558 }
2559
2560 pub(crate) fn text_index_query(
2561 &self,
2562 node: &Node,
2563 point_in_node: UntypedPoint2D<f32>,
2564 ) -> Option<usize> {
2565 self.layout_reflow(QueryMsg::TextIndexQuery);
2566 self.layout
2567 .borrow()
2568 .query_text_indext(node.to_opaque(), point_in_node)
2569 }
2570
2571 pub(crate) fn elements_from_point_query(
2572 &self,
2573 point: LayoutPoint,
2574 flags: ElementsFromPointFlags,
2575 ) -> Vec<ElementsFromPointResult> {
2576 self.layout_reflow(QueryMsg::ElementsFromPoint);
2577 self.layout().query_elements_from_point(point, flags)
2578 }
2579
2580 pub(crate) fn hit_test_from_input_event(
2581 &self,
2582 input_event: &ConstellationInputEvent,
2583 ) -> Option<HitTestResult> {
2584 self.hit_test_from_point_in_viewport(
2585 input_event.hit_test_result.as_ref()?.point_in_viewport,
2586 )
2587 }
2588
2589 #[allow(unsafe_code)]
2590 pub(crate) fn hit_test_from_point_in_viewport(
2591 &self,
2592 point_in_frame: Point2D<f32, CSSPixel>,
2593 ) -> Option<HitTestResult> {
2594 let result = self
2595 .elements_from_point_query(point_in_frame.cast_unit(), ElementsFromPointFlags::empty())
2596 .into_iter()
2597 .nth(0)?;
2598
2599 let point_relative_to_initial_containing_block =
2600 point_in_frame + self.scroll_offset().cast_unit();
2601
2602 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
2605 Some(HitTestResult {
2606 node: unsafe { from_untrusted_node_address(address) },
2607 cursor: result.cursor,
2608 point_in_node: result.point_in_target,
2609 point_in_frame,
2610 point_relative_to_initial_containing_block,
2611 })
2612 }
2613
2614 #[allow(unsafe_code)]
2615 pub(crate) fn init_window_proxy(&self, window_proxy: &WindowProxy) {
2616 assert!(self.window_proxy.get().is_none());
2617 self.window_proxy.set(Some(window_proxy));
2618 }
2619
2620 #[allow(unsafe_code)]
2621 pub(crate) fn init_document(&self, document: &Document) {
2622 assert!(self.document.get().is_none());
2623 assert!(document.window() == self);
2624 self.document.set(Some(document));
2625
2626 if self.unminify_css {
2627 *self.unminified_css_dir.borrow_mut() = Some(unminified_path("unminified-css"));
2628 }
2629 }
2630
2631 pub(crate) fn load_url(
2635 &self,
2636 history_handling: NavigationHistoryBehavior,
2637 force_reload: bool,
2638 load_data: LoadData,
2639 can_gc: CanGc,
2640 ) {
2641 let doc = self.Document();
2642
2643 let initiator_origin_snapshot = &load_data.load_origin;
2645
2646 if !force_reload &&
2649 load_data.url.as_url()[..Position::AfterQuery] ==
2650 doc.url().as_url()[..Position::AfterQuery]
2651 {
2652 if let Some(fragment) = load_data.url.fragment() {
2655 let webdriver_sender = self.webdriver_load_status_sender.borrow().clone();
2656 if let Some(ref sender) = webdriver_sender {
2657 let _ = sender.send(WebDriverLoadStatus::NavigationStart);
2658 }
2659
2660 self.send_to_constellation(ScriptToConstellationMessage::NavigatedToFragment(
2661 load_data.url.clone(),
2662 history_handling,
2663 ));
2664 doc.check_and_scroll_fragment(fragment);
2665 let this = Trusted::new(self);
2666 let old_url = doc.url().into_string();
2667 let new_url = load_data.url.clone().into_string();
2668 let task = task!(hashchange_event: move || {
2669 let this = this.root();
2670 let event = HashChangeEvent::new(
2671 &this,
2672 atom!("hashchange"),
2673 false,
2674 false,
2675 old_url,
2676 new_url,
2677 CanGc::note());
2678 event.upcast::<Event>().fire(this.upcast::<EventTarget>(), CanGc::note());
2679 if let Some(sender) = webdriver_sender {
2680 let _ = sender.send(WebDriverLoadStatus::NavigationStop);
2681 }
2682 });
2683 self.as_global_scope()
2684 .task_manager()
2685 .dom_manipulation_task_source()
2686 .queue(task);
2687 doc.set_url(load_data.url.clone());
2688 return;
2689 }
2690 }
2691
2692 let pipeline_id = self.pipeline_id();
2694 let window_proxy = self.window_proxy();
2695 if let Some(active) = window_proxy.currently_active() {
2696 if pipeline_id == active && doc.is_prompting_or_unloading() {
2697 return;
2698 }
2699 }
2700
2701 if doc.prompt_to_unload(false, can_gc) {
2703 let window_proxy = self.window_proxy();
2704 if window_proxy.parent().is_some() {
2705 window_proxy.start_delaying_load_events_mode();
2709 }
2710
2711 let resolved_history_handling = if history_handling == NavigationHistoryBehavior::Auto {
2713 if let LoadOrigin::Script(initiator_origin) = initiator_origin_snapshot {
2719 if load_data.url == doc.url() && initiator_origin.same_origin(doc.origin()) {
2720 NavigationHistoryBehavior::Replace
2721 } else {
2722 NavigationHistoryBehavior::Push
2723 }
2724 } else {
2725 NavigationHistoryBehavior::Push
2727 }
2728 } else if load_data.url.scheme() == "javascript" || doc.is_initial_about_blank() {
2731 NavigationHistoryBehavior::Replace
2732 } else {
2733 NavigationHistoryBehavior::Push
2734 };
2735
2736 if let Some(sender) = self.webdriver_load_status_sender.borrow().as_ref() {
2737 let _ = sender.send(WebDriverLoadStatus::NavigationStart);
2738 }
2739
2740 ScriptThread::navigate(pipeline_id, load_data, resolved_history_handling);
2742 };
2743 }
2744
2745 pub(crate) fn set_viewport_details(&self, size: ViewportDetails) {
2746 self.viewport_details.set(size);
2747 }
2748
2749 pub(crate) fn viewport_details(&self) -> ViewportDetails {
2750 self.viewport_details.get()
2751 }
2752
2753 pub(crate) fn theme(&self) -> Theme {
2755 self.theme.get()
2756 }
2757
2758 pub(crate) fn handle_theme_change(&self, new_theme: Theme) {
2760 if self.theme.get() == new_theme {
2761 return;
2762 }
2763 self.theme.set(new_theme);
2764 self.Document()
2765 .add_restyle_reason(RestyleReason::ThemeChanged);
2766 }
2767
2768 pub(crate) fn get_url(&self) -> ServoUrl {
2769 self.Document().url()
2770 }
2771
2772 pub(crate) fn windowproxy_handler(&self) -> &'static WindowProxyHandler {
2773 self.dom_static.windowproxy_handler
2774 }
2775
2776 pub(crate) fn add_resize_event(&self, event: ViewportDetails, event_type: WindowSizeType) {
2777 *self.unhandled_resize_event.borrow_mut() = Some((event, event_type))
2780 }
2781
2782 pub(crate) fn take_unhandled_resize_event(&self) -> Option<(ViewportDetails, WindowSizeType)> {
2783 self.unhandled_resize_event.borrow_mut().take()
2784 }
2785
2786 pub(crate) fn has_unhandled_resize_event(&self) -> bool {
2788 self.unhandled_resize_event.borrow().is_some()
2789 }
2790
2791 pub(crate) fn set_viewport_size(&self, new_viewport_size: UntypedSize2D<f32>) {
2792 let new_viewport_size = Size2D::new(
2793 Au::from_f32_px(new_viewport_size.width),
2794 Au::from_f32_px(new_viewport_size.height),
2795 );
2796 if new_viewport_size == self.current_viewport_size.get() {
2797 return;
2798 }
2799
2800 self.current_viewport_size.set(new_viewport_size);
2801
2802 self.Document()
2805 .add_restyle_reason(RestyleReason::ViewportSizeChanged);
2806
2807 if self.layout().device().used_viewport_units() {
2810 self.Document().dirty_all_nodes();
2811 }
2812 }
2813
2814 pub(crate) fn suspend(&self, can_gc: CanGc) {
2815 self.as_global_scope().suspend();
2817
2818 if self.window_proxy().currently_active() == Some(self.global().pipeline_id()) {
2820 self.window_proxy().unset_currently_active(can_gc);
2821 }
2822
2823 self.Gc();
2828 }
2829
2830 pub(crate) fn resume(&self, can_gc: CanGc) {
2831 self.as_global_scope().resume();
2833
2834 self.window_proxy().set_currently_active(self, can_gc);
2836
2837 self.Document().title_changed();
2840 }
2841
2842 pub(crate) fn need_emit_timeline_marker(&self, timeline_type: TimelineMarkerType) -> bool {
2843 let markers = self.devtools_markers.borrow();
2844 markers.contains(&timeline_type)
2845 }
2846
2847 pub(crate) fn emit_timeline_marker(&self, marker: TimelineMarker) {
2848 let sender = self.devtools_marker_sender.borrow();
2849 let sender = sender.as_ref().expect("There is no marker sender");
2850 sender.send(Some(marker)).unwrap();
2851 }
2852
2853 pub(crate) fn set_devtools_timeline_markers(
2854 &self,
2855 markers: Vec<TimelineMarkerType>,
2856 reply: IpcSender<Option<TimelineMarker>>,
2857 ) {
2858 *self.devtools_marker_sender.borrow_mut() = Some(reply);
2859 self.devtools_markers.borrow_mut().extend(markers);
2860 }
2861
2862 pub(crate) fn drop_devtools_timeline_markers(&self, markers: Vec<TimelineMarkerType>) {
2863 let mut devtools_markers = self.devtools_markers.borrow_mut();
2864 for marker in markers {
2865 devtools_markers.remove(&marker);
2866 }
2867 if devtools_markers.is_empty() {
2868 *self.devtools_marker_sender.borrow_mut() = None;
2869 }
2870 }
2871
2872 pub(crate) fn set_webdriver_script_chan(&self, chan: Option<IpcSender<WebDriverJSResult>>) {
2873 *self.webdriver_script_chan.borrow_mut() = chan;
2874 }
2875
2876 pub(crate) fn set_webdriver_load_status_sender(
2877 &self,
2878 sender: Option<GenericSender<WebDriverLoadStatus>>,
2879 ) {
2880 *self.webdriver_load_status_sender.borrow_mut() = sender;
2881 }
2882
2883 pub(crate) fn is_alive(&self) -> bool {
2884 self.current_state.get() == WindowState::Alive
2885 }
2886
2887 pub(crate) fn is_top_level(&self) -> bool {
2889 self.parent_info.is_none()
2890 }
2891
2892 pub(crate) fn run_the_resize_steps(&self, can_gc: CanGc) -> bool {
2897 let Some((new_size, size_type)) = self.take_unhandled_resize_event() else {
2898 return false;
2899 };
2900
2901 if self.viewport_details() == new_size {
2902 return false;
2903 }
2904
2905 let _realm = enter_realm(self);
2906 debug!(
2907 "Resizing Window for pipeline {:?} from {:?} to {new_size:?}",
2908 self.pipeline_id(),
2909 self.viewport_details(),
2910 );
2911 self.set_viewport_details(new_size);
2912
2913 self.Document()
2917 .add_restyle_reason(RestyleReason::ViewportSizeChanged);
2918
2919 if self.layout().device().used_viewport_units() {
2922 self.Document().dirty_all_nodes();
2923 }
2924
2925 if size_type == WindowSizeType::Resize {
2927 let uievent = UIEvent::new(
2928 self,
2929 DOMString::from("resize"),
2930 EventBubbles::DoesNotBubble,
2931 EventCancelable::NotCancelable,
2932 Some(self),
2933 0i32,
2934 can_gc,
2935 );
2936 uievent.upcast::<Event>().fire(self.upcast(), can_gc);
2937 }
2938
2939 true
2940 }
2941
2942 pub(crate) fn evaluate_media_queries_and_report_changes(&self, can_gc: CanGc) {
2945 let _realm = enter_realm(self);
2946
2947 rooted_vec!(let mut mql_list);
2948 self.media_query_lists.for_each(|mql| {
2949 if let MediaQueryListMatchState::Changed = mql.evaluate_changes() {
2950 mql_list.push(Dom::from_ref(&*mql));
2952 }
2953 });
2954 for mql in mql_list.iter() {
2956 let event = MediaQueryListEvent::new(
2957 &mql.global(),
2958 atom!("change"),
2959 false,
2960 false,
2961 mql.Media(),
2962 mql.Matches(),
2963 can_gc,
2964 );
2965 event
2966 .upcast::<Event>()
2967 .fire(mql.upcast::<EventTarget>(), can_gc);
2968 }
2969 }
2970
2971 pub(crate) fn set_throttled(&self, throttled: bool) {
2973 self.throttled.set(throttled);
2974 if throttled {
2975 self.as_global_scope().slow_down_timers();
2976 } else {
2977 self.as_global_scope().speed_up_timers();
2978 }
2979 }
2980
2981 pub(crate) fn throttled(&self) -> bool {
2982 self.throttled.get()
2983 }
2984
2985 pub(crate) fn unminified_css_dir(&self) -> Option<String> {
2986 self.unminified_css_dir.borrow().clone()
2987 }
2988
2989 pub(crate) fn local_script_source(&self) -> &Option<String> {
2990 &self.local_script_source
2991 }
2992
2993 pub(crate) fn set_navigation_start(&self) {
2994 self.navigation_start.set(CrossProcessInstant::now());
2995 }
2996
2997 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
2998 self.send_to_constellation(ScriptToConstellationMessage::ForwardToEmbedder(msg));
2999 }
3000
3001 pub(crate) fn send_to_constellation(&self, msg: ScriptToConstellationMessage) {
3002 self.as_global_scope()
3003 .script_to_constellation_chan()
3004 .send(msg)
3005 .unwrap();
3006 }
3007
3008 #[cfg(feature = "webxr")]
3009 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3010 self.navigator
3011 .get()
3012 .as_ref()
3013 .and_then(|nav| nav.xr())
3014 .is_some_and(|xr| xr.pending_or_active_session())
3015 }
3016
3017 #[cfg(not(feature = "webxr"))]
3018 pub(crate) fn in_immersive_xr_session(&self) -> bool {
3019 false
3020 }
3021
3022 #[allow(unsafe_code)]
3023 fn handle_pending_images_post_reflow(
3024 &self,
3025 pending_images: Vec<PendingImage>,
3026 pending_rasterization_images: Vec<PendingRasterizationImage>,
3027 pending_svg_element_for_serialization: Vec<UntrustedNodeAddress>,
3028 ) {
3029 let pipeline_id = self.pipeline_id();
3030 for image in pending_images {
3031 let id = image.id;
3032 let node = unsafe { from_untrusted_node_address(image.node) };
3033
3034 if let PendingImageState::Unrequested(ref url) = image.state {
3035 fetch_image_for_layout(url.clone(), &node, id, self.image_cache.clone());
3036 }
3037
3038 let mut images = self.pending_layout_images.borrow_mut();
3039 if !images.contains_key(&id) {
3040 let trusted_node = Trusted::new(&*node);
3041 let sender = self.register_image_cache_listener(id, move |response| {
3042 trusted_node
3043 .root()
3044 .owner_window()
3045 .pending_layout_image_notification(response);
3046 });
3047
3048 self.image_cache
3049 .add_listener(ImageLoadListener::new(sender, pipeline_id, id));
3050 }
3051
3052 let nodes = images.entry(id).or_default();
3053 if !nodes.iter().any(|n| std::ptr::eq(&**n, &*node)) {
3054 nodes.push(Dom::from_ref(&*node));
3055 }
3056 }
3057
3058 for image in pending_rasterization_images {
3059 let node = unsafe { from_untrusted_node_address(image.node) };
3060
3061 let mut images = self.pending_images_for_rasterization.borrow_mut();
3062 if !images.contains_key(&(image.id, image.size)) {
3063 self.image_cache.add_rasterization_complete_listener(
3064 pipeline_id,
3065 image.id,
3066 image.size,
3067 self.image_cache_sender.clone(),
3068 );
3069 }
3070
3071 let nodes = images.entry((image.id, image.size)).or_default();
3072 if !nodes.iter().any(|n| std::ptr::eq(&**n, &*node)) {
3073 nodes.push(Dom::from_ref(&*node));
3074 }
3075 }
3076
3077 for node in pending_svg_element_for_serialization.into_iter() {
3078 let node = unsafe { from_untrusted_node_address(node) };
3079 let svg = node.downcast::<SVGSVGElement>().unwrap();
3080 svg.serialize_and_cache_subtree();
3081 node.dirty(NodeDamage::Other);
3082 }
3083 }
3084}
3085
3086impl Window {
3087 #[allow(unsafe_code)]
3088 #[allow(clippy::too_many_arguments)]
3089 pub(crate) fn new(
3090 webview_id: WebViewId,
3091 runtime: Rc<Runtime>,
3092 script_chan: Sender<MainThreadScriptMsg>,
3093 layout: Box<dyn Layout>,
3094 font_context: Arc<FontContext>,
3095 image_cache_sender: IpcSender<ImageCacheResponseMessage>,
3096 image_cache: Arc<dyn ImageCache>,
3097 resource_threads: ResourceThreads,
3098 #[cfg(feature = "bluetooth")] bluetooth_thread: IpcSender<BluetoothRequest>,
3099 mem_profiler_chan: MemProfilerChan,
3100 time_profiler_chan: TimeProfilerChan,
3101 devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
3102 constellation_chan: ScriptToConstellationChan,
3103 control_chan: GenericSender<ScriptThreadMessage>,
3104 pipeline_id: PipelineId,
3105 parent_info: Option<PipelineId>,
3106 viewport_details: ViewportDetails,
3107 origin: MutableOrigin,
3108 creation_url: ServoUrl,
3109 top_level_creation_url: ServoUrl,
3110 navigation_start: CrossProcessInstant,
3111 webgl_chan: Option<WebGLChan>,
3112 #[cfg(feature = "webxr")] webxr_registry: Option<webxr_api::Registry>,
3113 microtask_queue: Rc<MicrotaskQueue>,
3114 compositor_api: CrossProcessCompositorApi,
3115 unminify_js: bool,
3116 unminify_css: bool,
3117 local_script_source: Option<String>,
3118 user_content_manager: UserContentManager,
3119 player_context: WindowGLContext,
3120 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
3121 inherited_secure_context: Option<bool>,
3122 theme: Theme,
3123 ) -> DomRoot<Self> {
3124 let error_reporter = CSSErrorReporter {
3125 pipelineid: pipeline_id,
3126 script_chan: control_chan,
3127 };
3128
3129 let initial_viewport = f32_rect_to_au_rect(UntypedRect::new(
3130 Point2D::zero(),
3131 viewport_details.size.to_untyped(),
3132 ));
3133
3134 let win = Box::new(Self {
3135 webview_id,
3136 globalscope: GlobalScope::new_inherited(
3137 pipeline_id,
3138 devtools_chan,
3139 mem_profiler_chan,
3140 time_profiler_chan,
3141 constellation_chan,
3142 resource_threads,
3143 origin,
3144 creation_url,
3145 Some(top_level_creation_url),
3146 microtask_queue,
3147 #[cfg(feature = "webgpu")]
3148 gpu_id_hub,
3149 inherited_secure_context,
3150 unminify_js,
3151 Some(font_context.clone()),
3152 ),
3153 ongoing_navigation: Default::default(),
3154 script_chan,
3155 layout: RefCell::new(layout),
3156 image_cache_sender,
3157 image_cache,
3158 navigator: Default::default(),
3159 location: Default::default(),
3160 history: Default::default(),
3161 indexeddb: Default::default(),
3162 custom_element_registry: Default::default(),
3163 window_proxy: Default::default(),
3164 document: Default::default(),
3165 performance: Default::default(),
3166 navigation_start: Cell::new(navigation_start),
3167 screen: Default::default(),
3168 session_storage: Default::default(),
3169 local_storage: Default::default(),
3170 status: DomRefCell::new(DOMString::new()),
3171 parent_info,
3172 dom_static: GlobalStaticData::new(),
3173 js_runtime: DomRefCell::new(Some(runtime.clone())),
3174 #[cfg(feature = "bluetooth")]
3175 bluetooth_thread,
3176 #[cfg(feature = "bluetooth")]
3177 bluetooth_extra_permission_data: BluetoothExtraPermissionData::new(),
3178 unhandled_resize_event: Default::default(),
3179 viewport_details: Cell::new(viewport_details),
3180 current_viewport_size: Cell::new(initial_viewport.to_untyped().size),
3181 layout_blocker: Cell::new(LayoutBlocker::WaitingForParse),
3182 current_state: Cell::new(WindowState::Alive),
3183 devtools_marker_sender: Default::default(),
3184 devtools_markers: Default::default(),
3185 webdriver_script_chan: Default::default(),
3186 webdriver_load_status_sender: Default::default(),
3187 error_reporter,
3188 media_query_lists: DOMTracker::new(),
3189 #[cfg(feature = "bluetooth")]
3190 test_runner: Default::default(),
3191 webgl_chan,
3192 #[cfg(feature = "webxr")]
3193 webxr_registry,
3194 pending_image_callbacks: Default::default(),
3195 pending_layout_images: Default::default(),
3196 pending_images_for_rasterization: Default::default(),
3197 unminified_css_dir: Default::default(),
3198 local_script_source,
3199 test_worklet: Default::default(),
3200 paint_worklet: Default::default(),
3201 exists_mut_observer: Cell::new(false),
3202 compositor_api,
3203 has_sent_idle_message: Cell::new(false),
3204 unminify_css,
3205 user_content_manager,
3206 player_context,
3207 throttled: Cell::new(false),
3208 layout_marker: DomRefCell::new(Rc::new(Cell::new(true))),
3209 current_event: DomRefCell::new(None),
3210 theme: Cell::new(theme),
3211 trusted_types: Default::default(),
3212 reporting_observer_list: Default::default(),
3213 report_list: Default::default(),
3214 endpoints_list: Default::default(),
3215 });
3216
3217 WindowBinding::Wrap::<crate::DomTypeHolder>(GlobalScope::get_cx(), win)
3218 }
3219
3220 pub(crate) fn pipeline_id(&self) -> PipelineId {
3221 self.as_global_scope().pipeline_id()
3222 }
3223
3224 pub(crate) fn cache_layout_value<T>(&self, value: T) -> LayoutValue<T>
3226 where
3227 T: Copy + MallocSizeOf,
3228 {
3229 LayoutValue::new(self.layout_marker.borrow().clone(), value)
3230 }
3231}
3232
3233#[derive(MallocSizeOf)]
3238pub(crate) struct LayoutValue<T: MallocSizeOf> {
3239 #[ignore_malloc_size_of = "Rc is hard"]
3240 is_valid: Rc<Cell<bool>>,
3241 value: T,
3242}
3243
3244#[allow(unsafe_code)]
3245unsafe impl<T: JSTraceable + MallocSizeOf> JSTraceable for LayoutValue<T> {
3246 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
3247 unsafe { self.value.trace(trc) };
3248 }
3249}
3250
3251impl<T: Copy + MallocSizeOf> LayoutValue<T> {
3252 fn new(marker: Rc<Cell<bool>>, value: T) -> Self {
3253 LayoutValue {
3254 is_valid: marker,
3255 value,
3256 }
3257 }
3258
3259 pub(crate) fn get(&self) -> Result<T, ()> {
3261 if self.is_valid.get() {
3262 return Ok(self.value);
3263 }
3264 Err(())
3265 }
3266}
3267
3268fn should_move_clip_rect(clip_rect: UntypedRect<Au>, new_viewport: UntypedRect<f32>) -> bool {
3269 let clip_rect = UntypedRect::new(
3270 Point2D::new(
3271 clip_rect.origin.x.to_f32_px(),
3272 clip_rect.origin.y.to_f32_px(),
3273 ),
3274 Size2D::new(
3275 clip_rect.size.width.to_f32_px(),
3276 clip_rect.size.height.to_f32_px(),
3277 ),
3278 );
3279
3280 static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5;
3284 let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE;
3285
3286 (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width ||
3287 (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width ||
3288 (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height ||
3289 (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height
3290}
3291
3292impl Window {
3293 pub(crate) fn post_message(
3295 &self,
3296 target_origin: Option<ImmutableOrigin>,
3297 source_origin: ImmutableOrigin,
3298 source: &WindowProxy,
3299 data: StructuredSerializedData,
3300 ) {
3301 let this = Trusted::new(self);
3302 let source = Trusted::new(source);
3303 let task = task!(post_serialised_message: move || {
3304 let this = this.root();
3305 let source = source.root();
3306 let document = this.Document();
3307
3308 if let Some(ref target_origin) = target_origin {
3310 if !target_origin.same_origin(document.origin()) {
3311 return;
3312 }
3313 }
3314
3315 let cx = this.get_cx();
3317 let obj = this.reflector().get_jsobject();
3318 let _ac = JSAutoRealm::new(*cx, obj.get());
3319 rooted!(in(*cx) let mut message_clone = UndefinedValue());
3320 if let Ok(ports) = structuredclone::read(this.upcast(), data, message_clone.handle_mut()) {
3321 MessageEvent::dispatch_jsval(
3323 this.upcast(),
3324 this.upcast(),
3325 message_clone.handle(),
3326 Some(&source_origin.ascii_serialization()),
3327 Some(&*source),
3328 ports,
3329 CanGc::note()
3330 );
3331 } else {
3332 MessageEvent::dispatch_error(
3334 this.upcast(),
3335 this.upcast(),
3336 CanGc::note()
3337 );
3338 }
3339 });
3340 self.as_global_scope()
3342 .task_manager()
3343 .dom_manipulation_task_source()
3344 .queue(task);
3345 }
3346}
3347
3348#[derive(MallocSizeOf)]
3349pub(crate) struct CSSErrorReporter {
3350 pub(crate) pipelineid: PipelineId,
3351 pub(crate) script_chan: GenericSender<ScriptThreadMessage>,
3352}
3353unsafe_no_jsmanaged_fields!(CSSErrorReporter);
3354
3355impl ParseErrorReporter for CSSErrorReporter {
3356 fn report_error(
3357 &self,
3358 url: &UrlExtraData,
3359 location: SourceLocation,
3360 error: ContextualParseError,
3361 ) {
3362 if log_enabled!(log::Level::Info) {
3363 info!(
3364 "Url:\t{}\n{}:{} {}",
3365 url.0.as_str(),
3366 location.line,
3367 location.column,
3368 error
3369 )
3370 }
3371
3372 let _ = self.script_chan.send(ScriptThreadMessage::ReportCSSError(
3374 self.pipelineid,
3375 url.0.to_string(),
3376 location.line,
3377 location.column,
3378 error.to_string(),
3379 ));
3380 }
3381}
3382
3383fn is_named_element_with_name_attribute(elem: &Element) -> bool {
3384 let type_ = match elem.upcast::<Node>().type_id() {
3385 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
3386 _ => return false,
3387 };
3388 matches!(
3389 type_,
3390 HTMLElementTypeId::HTMLEmbedElement |
3391 HTMLElementTypeId::HTMLFormElement |
3392 HTMLElementTypeId::HTMLImageElement |
3393 HTMLElementTypeId::HTMLObjectElement
3394 )
3395}
3396
3397fn is_named_element_with_id_attribute(elem: &Element) -> bool {
3398 elem.is_html_element()
3399}
3400
3401#[allow(unsafe_code)]
3402#[unsafe(no_mangle)]
3403unsafe extern "C" fn dump_js_stack(cx: *mut RawJSContext) {
3405 unsafe {
3406 DumpJSStack(cx, true, false, false);
3407 }
3408}
3409
3410impl WindowHelpers for Window {
3411 fn create_named_properties_object(
3412 cx: JSContext,
3413 proto: HandleObject,
3414 object: MutableHandleObject,
3415 ) {
3416 Self::create_named_properties_object(cx, proto, object)
3417 }
3418}