/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::ToOwned; use std::cell::{Cell, RefCell, RefMut}; use std::cmp; use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::default::Default; use std::io::{Write, stderr, stdout}; use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; use app_units::Au; use backtrace::Backtrace; use base::cross_process_instant::CrossProcessInstant; use base::id::{BrowsingContextId, PipelineId, WebViewId}; use base64::Engine; #[cfg(feature = "bluetooth")] use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLChan; use compositing_traits::CrossProcessCompositorApi; use constellation_traits::{ DocumentState, LoadData, LoadOrigin, NavigationHistoryBehavior, ScriptToConstellationChan, ScriptToConstellationMessage, StructuredSerializedData, WindowSizeType, }; use crossbeam_channel::{Sender, unbounded}; use cssparser::SourceLocation; use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType}; use dom_struct::dom_struct; use embedder_traits::user_content_manager::{UserContentManager, UserScript}; use embedder_traits::{ AlertResponse, ConfirmResponse, EmbedderMsg, GamepadEvent, GamepadSupportedHapticEffects, GamepadUpdateType, PromptResponse, SimpleDialog, Theme, ViewportDetails, WebDriverJSError, WebDriverJSResult, }; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D}; use euclid::{Point2D, Scale, Size2D, Vector2D}; use fonts::FontContext; use ipc_channel::ipc::{self, IpcSender}; use js::conversions::ToJSValConvertible; use js::glue::DumpJSStack; use js::jsapi::{ GCReason, Heap, JS_GC, JSAutoRealm, JSContext as RawJSContext, JSObject, JSPROP_ENUMERATE, }; use js::jsval::{NullValue, UndefinedValue}; use js::rust::wrappers::JS_DefineProperty; use js::rust::{ CustomAutoRooter, CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleObject, MutableHandleValue, }; use malloc_size_of::MallocSizeOf; use media::WindowGLContext; use net_traits::ResourceThreads; use net_traits::image_cache::{ ImageCache, ImageCacheResponseMessage, ImageLoadListener, ImageResponse, PendingImageId, PendingImageResponse, RasterizationCompleteResponse, }; use net_traits::storage_thread::StorageType; use num_traits::ToPrimitive; use profile_traits::ipc as ProfiledIpc; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods; use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods; use script_bindings::interfaces::WindowHelpers; use script_bindings::root::Root; use script_layout_interface::{ FragmentType, Layout, PendingImageState, QueryMsg, ReflowGoal, ReflowRequest, TrustedNodeAddress, combine_id_with_fragment_type, }; use script_traits::ScriptThreadMessage; use selectors::attr::CaseSensitivity; use servo_arc::Arc as ServoArc; use servo_config::{opts, pref}; use servo_geometry::{DeviceIndependentIntRect, f32_rect_to_au_rect}; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use style::error_reporting::{ContextualParseError, ParseErrorReporter}; use style::properties::PropertyId; use style::properties::style_structs::Font; use style::selector_parser::PseudoElement; use style::str::HTML_SPACE_CHARACTERS; use style::stylesheets::UrlExtraData; use style_traits::CSSPixel; use stylo_atoms::Atom; use url::Position; use webrender_api::ExternalScrollId; use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel}; use super::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions; use super::bindings::trace::HashMapTracedValues; use crate::dom::bindings::cell::{DomRefCell, Ref}; use crate::dom::bindings::codegen::Bindings::DocumentBinding::{ DocumentMethods, DocumentReadyState, NamedPropertyValue, }; use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods; use crate::dom::bindings::codegen::Bindings::HistoryBinding::History_Binding::HistoryMethods; use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{ ImageBitmapOptions, ImageBitmapSource, }; use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryList_Binding::MediaQueryListMethods; use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit; use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction; use crate::dom::bindings::codegen::Bindings::WindowBinding::{ self, FrameRequestCallback, ScrollBehavior, ScrollToOptions, WindowMethods, WindowPostMessageOptions, }; use crate::dom::bindings::codegen::UnionTypes::{RequestOrUSVString, StringOrFunction}; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; use crate::dom::bindings::num::Finite; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{DomGlobal, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::bindings::structuredclone; use crate::dom::bindings::trace::{CustomTraceable, JSTraceable, RootedTraceableBox}; use crate::dom::bindings::utils::GlobalStaticData; use crate::dom::bindings::weakref::DOMTracker; #[cfg(feature = "bluetooth")] use crate::dom::bluetooth::BluetoothExtraPermissionData; use crate::dom::crypto::Crypto; use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner}; use crate::dom::customelementregistry::CustomElementRegistry; use crate::dom::document::{AnimationFrameCallback, Document, ReflowTriggerCondition}; use crate::dom::element::Element; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::eventtarget::EventTarget; use crate::dom::gamepad::{Gamepad, contains_user_gesture}; use crate::dom::gamepadevent::GamepadEventType; use crate::dom::globalscope::GlobalScope; use crate::dom::hashchangeevent::HashChangeEvent; use crate::dom::history::History; use crate::dom::htmlcollection::{CollectionFilter, HTMLCollection}; use crate::dom::htmliframeelement::HTMLIFrameElement; use crate::dom::idbfactory::IDBFactory; use crate::dom::location::Location; use crate::dom::medialist::MediaList; use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState}; use crate::dom::mediaquerylistevent::MediaQueryListEvent; use crate::dom::messageevent::MessageEvent; use crate::dom::navigator::Navigator; use crate::dom::node::{Node, NodeDamage, NodeTraits, from_untrusted_node_address}; use crate::dom::performance::Performance; use crate::dom::promise::Promise; use crate::dom::screen::Screen; use crate::dom::selection::Selection; use crate::dom::shadowroot::ShadowRoot; use crate::dom::storage::Storage; #[cfg(feature = "bluetooth")] use crate::dom::testrunner::TestRunner; use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory; use crate::dom::types::UIEvent; use crate::dom::webglrenderingcontext::WebGLCommandSender; #[cfg(feature = "webgpu")] use crate::dom::webgpu::identityhub::IdentityHub; use crate::dom::windowproxy::{WindowProxy, WindowProxyHandler}; use crate::dom::worklet::Worklet; use crate::dom::workletglobalscope::WorkletGlobalScopeType; use crate::layout_image::fetch_image_for_layout; use crate::messaging::{MainThreadScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender}; use crate::microtask::MicrotaskQueue; use crate::realms::{InRealm, enter_realm}; use crate::script_runtime::{CanGc, JSContext, Runtime}; use crate::script_thread::ScriptThread; use crate::timers::{IsInterval, TimerCallback}; use crate::unminify::unminified_path; use crate::webdriver_handlers::{find_node_by_unique_id_in_document, jsval_to_webdriver}; use crate::{fetch, window_named_properties}; /// A callback to call when a response comes back from the `ImageCache`. /// /// This is wrapped in a struct so that we can implement `MallocSizeOf` /// for this type. #[derive(MallocSizeOf)] pub struct PendingImageCallback( #[ignore_malloc_size_of = "dyn Fn is currently impossible to measure"] Box, ); /// Current state of the window object #[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)] enum WindowState { Alive, Zombie, // Pipeline is closed, but the window hasn't been GCed yet. } /// How long we should wait before performing the initial reflow after `` is parsed, /// assuming that `` take this long to parse. const INITIAL_REFLOW_DELAY: Duration = Duration::from_millis(200); /// During loading and parsing, layouts are suppressed to avoid flashing incomplete page /// contents. /// /// Exceptions: /// - Parsing the body takes so long, that layouts are no longer suppressed in order /// to show the user that the page is loading. /// - Script triggers a layout query or scroll event in which case, we want to layout /// but not display the contents. /// /// For more information see: . #[derive(Clone, Copy, MallocSizeOf)] enum LayoutBlocker { /// The first load event hasn't been fired and we have not started to parse the `` yet. WaitingForParse, /// The body is being parsed the `` starting at the `Instant` specified. Parsing(Instant), /// The body finished parsing and the `load` event has been fired or parsing took so /// long, that we are going to do layout anyway. Note that subsequent changes to the body /// can trigger parsing again, but the `Window` stays in this state. FiredLoadEventOrParsingTimerExpired, } impl LayoutBlocker { fn layout_blocked(&self) -> bool { !matches!(self, Self::FiredLoadEventOrParsingTimerExpired) } } type PendingImageRasterizationKey = (PendingImageId, DeviceIntSize); #[dom_struct] pub(crate) struct Window { globalscope: GlobalScope, /// The webview that contains this [`Window`]. /// /// This may not be the top-level [`Window`], in the case of frames. #[no_trace] webview_id: WebViewId, script_chan: Sender, #[no_trace] #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"] layout: RefCell>, /// A [`FontContext`] which is used to store and match against fonts for this `Window` and to /// trigger the download of web fonts. #[no_trace] #[conditional_malloc_size_of] font_context: Arc, navigator: MutNullableDom, #[ignore_malloc_size_of = "Arc"] #[no_trace] image_cache: Arc, #[no_trace] image_cache_sender: IpcSender, window_proxy: MutNullableDom, document: MutNullableDom, location: MutNullableDom, history: MutNullableDom, indexeddb: MutNullableDom, custom_element_registry: MutNullableDom, performance: MutNullableDom, #[no_trace] navigation_start: Cell, screen: MutNullableDom, session_storage: MutNullableDom, local_storage: MutNullableDom, status: DomRefCell, trusted_types: MutNullableDom, /// For sending timeline markers. Will be ignored if /// no devtools server #[no_trace] devtools_markers: DomRefCell>, #[no_trace] devtools_marker_sender: DomRefCell>>>, /// Most recent unhandled resize event, if any. #[no_trace] unhandled_resize_event: DomRefCell>, /// Platform theme. #[no_trace] theme: Cell, /// Parent id associated with this page, if any. #[no_trace] parent_info: Option, /// Global static data related to the DOM. dom_static: GlobalStaticData, /// The JavaScript runtime. #[ignore_malloc_size_of = "Rc is hard"] js_runtime: DomRefCell>>, /// The [`ViewportDetails`] of this [`Window`]'s frame. #[no_trace] viewport_details: Cell, /// A handle for communicating messages to the bluetooth thread. #[no_trace] #[cfg(feature = "bluetooth")] bluetooth_thread: IpcSender, #[cfg(feature = "bluetooth")] bluetooth_extra_permission_data: BluetoothExtraPermissionData, /// See the documentation for [`LayoutBlocker`]. Essentially, this flag prevents /// layouts from happening before the first load event, apart from a few exceptional /// cases. #[no_trace] layout_blocker: Cell, /// A channel for communicating results of async scripts back to the webdriver server #[no_trace] webdriver_script_chan: DomRefCell>>, /// The current state of the window object current_state: Cell, /// The current size of the viewport. This might change if the `WebView` or containing `