/* 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::{Cow, 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::{stderr, stdout, Write}; use std::ptr::NonNull; use std::rc::Rc; use std::sync::atomic::Ordering; 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}; use base64::Engine; use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLChan; use crossbeam_channel::{unbounded, Sender}; use cssparser::{Parser, ParserInput, SourceLocation}; use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType}; use dom_struct::dom_struct; use embedder_traits::{EmbedderMsg, PromptDefinition, PromptOrigin, PromptResult}; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect}; use euclid::{Point2D, Rect, Scale, Size2D, Vector2D}; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use js::conversions::ToJSValConvertible; use js::jsapi::{GCReason, Heap, JSAutoRealm, JSObject, StackFormat, JSPROP_ENUMERATE, JS_GC}; 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::image_cache::{ ImageCache, ImageResponder, ImageResponse, PendingImageId, PendingImageResponse, }; use net_traits::storage_thread::StorageType; use net_traits::ResourceThreads; 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_layout_interface::{ combine_id_with_fragment_type, FragmentType, IFrameSizes, Layout, PendingImageState, QueryMsg, Reflow, ReflowGoal, ReflowRequest, TrustedNodeAddress, }; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::{ ConstellationControlMsg, DocumentState, HistoryEntryReplacement, IFrameSizeMsg, LoadData, ScriptMsg, ScriptToConstellationChan, ScrollState, StructuredSerializedData, Theme, TimerSchedulerMsg, WindowSizeData, WindowSizeType, }; use selectors::attr::CaseSensitivity; use servo_arc::Arc as ServoArc; use servo_atoms::Atom; use servo_config::pref; use servo_geometry::{f32_rect_to_au_rect, DeviceIndependentIntRect, MaxRect}; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use style::dom::OpaqueNode; use style::error_reporting::{ContextualParseError, ParseErrorReporter}; use style::media_queries; use style::parser::ParserContext as CssParserContext; use style::properties::style_structs::Font; use style::properties::PropertyId; use style::queries::values::PrefersColorScheme; use style::selector_parser::PseudoElement; use style::str::HTML_SPACE_CHARACTERS; use style::stylesheets::{CssRuleType, Origin, UrlExtraData}; use style_traits::{CSSPixel, ParsingMode}; use url::Position; use webrender_api::units::{DevicePixel, LayoutPixel}; use webrender_api::{DocumentId, ExternalScrollId}; use webrender_traits::CrossProcessCompositorApi; 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, }; 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::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::{JSTraceable, RootedTraceableBox}; use crate::dom::bindings::utils::GlobalStaticData; use crate::dom::bindings::weakref::DOMTracker; 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::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::location::Location; 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::{document_from_node, from_untrusted_node_address, Node, NodeDamage}; use crate::dom::performance::Performance; use crate::dom::promise::Promise; use crate::dom::screen::Screen; use crate::dom::selection::Selection; use crate::dom::storage::Storage; use crate::dom::testrunner::TestRunner; 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::microtask::MicrotaskQueue; use crate::realms::{enter_realm, InRealm}; use crate::script_runtime::{ CanGc, CommonScriptMsg, JSContext, Runtime, ScriptChan, ScriptPort, ScriptThreadEventCategory, }; use crate::script_thread::{ with_script_thread, ImageCacheMsg, MainThreadScriptChan, MainThreadScriptMsg, ScriptThread, SendableMainThreadScriptChan, }; use crate::task_manager::TaskManager; use crate::task_source::{TaskSource, TaskSourceName}; use crate::timers::{IsInterval, TimerCallback}; use crate::unminify::unminified_path; use crate::webdriver_handlers::jsval_to_webdriver; use crate::{fetch, window_named_properties}; /// 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) } } #[dom_struct] pub struct Window { globalscope: GlobalScope, #[ignore_malloc_size_of = "trait objects are hard"] script_chan: MainThreadScriptChan, task_manager: TaskManager, #[no_trace] #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"] layout: RefCell>, navigator: MutNullableDom, #[ignore_malloc_size_of = "Arc"] #[no_trace] image_cache: Arc, #[ignore_malloc_size_of = "channels are hard"] #[no_trace] image_cache_chan: Sender, window_proxy: MutNullableDom, document: MutNullableDom, location: MutNullableDom, history: MutNullableDom, custom_element_registry: MutNullableDom, performance: MutNullableDom, #[no_trace] navigation_start: Cell, screen: MutNullableDom, session_storage: MutNullableDom, local_storage: MutNullableDom, status: DomRefCell, /// For sending timeline markers. Will be ignored if /// no devtools server #[no_trace] devtools_markers: DomRefCell>, #[ignore_malloc_size_of = "channels are hard"] #[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 current size of the window, in pixels. #[no_trace] window_size: Cell, /// A handle for communicating messages to the bluetooth thread. #[ignore_malloc_size_of = "channels are hard"] #[no_trace] bluetooth_thread: IpcSender, bluetooth_extra_permission_data: BluetoothExtraPermissionData, /// An enlarged rectangle around the page contents visible in the viewport, used /// to prevent creating display list items for content that is far away from the viewport. #[no_trace] page_clip_rect: Cell>, /// 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 #[ignore_malloc_size_of = "channels are hard"] #[no_trace] webdriver_script_chan: DomRefCell>>, /// The current state of the window object current_state: Cell, #[no_trace] current_viewport: Cell>, error_reporter: CSSErrorReporter, /// A list of scroll offsets for each scrollable element. #[no_trace] scroll_offsets: DomRefCell>>, /// All the MediaQueryLists we need to update media_query_lists: DOMTracker, test_runner: MutNullableDom, /// A handle for communicating messages to the WebGL thread, if available. #[ignore_malloc_size_of = "channels are hard"] #[no_trace] webgl_chan: Option, #[ignore_malloc_size_of = "defined in webxr"] #[no_trace] #[cfg(feature = "webxr")] webxr_registry: Option, /// All of the elements that have an outstanding image request that was /// initiated by layout during a reflow. They are stored in the script thread /// to ensure that the element can be marked dirty when the image data becomes /// available at some point in the future. pending_layout_images: DomRefCell>>>, /// Directory to store unminified css for this window if unminify-css /// opt is enabled. unminified_css_dir: DomRefCell>, /// Directory with stored unminified scripts local_script_source: Option, /// Worklets test_worklet: MutNullableDom, /// paint_worklet: MutNullableDom, /// The Webrender Document id associated with this window. #[ignore_malloc_size_of = "defined in webrender_api"] #[no_trace] webrender_document: DocumentId, /// Flag to identify whether mutation observers are present(true)/absent(false) exists_mut_observer: Cell, /// Cross-process access to the compositor. #[ignore_malloc_size_of = "Wraps an IpcSender"] #[no_trace] compositor_api: CrossProcessCompositorApi, /// Indicate whether a SetDocumentStatus message has been sent after a reflow is complete. /// It is used to avoid sending idle message more than once, which is unneccessary. has_sent_idle_message: Cell, /// Emits notifications when there is a relayout. relayout_event: bool, /// True if it is safe to write to the image. prepare_for_screenshot: bool, /// Unminify Css. unminify_css: bool, /// Where to load userscripts from, if any. An empty string will load from /// the resources/user-agent-js directory, and if the option isn't passed userscripts /// won't be loaded. userscripts_path: Option, /// Replace unpaired surrogates in DOM strings with U+FFFD. /// See replace_surrogates: bool, /// Window's GL context from application #[ignore_malloc_size_of = "defined in script_thread"] #[no_trace] player_context: WindowGLContext, throttled: Cell, /// A shared marker for the validity of any cached layout values. A value of true /// indicates that any such values remain valid; any new layout that invalidates /// those values will cause the marker to be set to false. #[ignore_malloc_size_of = "Rc is hard"] layout_marker: DomRefCell>>, /// current_event: DomRefCell>>, /// Sizes of the various `` that we might have on this [`Window`]. /// This is used to: /// - Let same-`ScriptThread` `