/* 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::cell::{Cell, RefCell}; use std::cmp::Ordering; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::{HashMap, HashSet, VecDeque}; use std::default::Default; use std::rc::Rc; use std::str::FromStr; use std::sync::{LazyLock, Mutex}; use std::time::Duration; use base::cross_process_instant::CrossProcessInstant; use base::id::WebViewId; use base::{Epoch, IpcSend, generic_channel}; use bitflags::bitflags; use chrono::Local; use constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage}; use content_security_policy::sandboxing_directive::SandboxingFlagSet; use content_security_policy::{CspList, PolicyDisposition}; use cookie::Cookie; use data_url::mime::Mime; use devtools_traits::ScriptToDevtoolsControlMsg; use dom_struct::dom_struct; use embedder_traits::{ AllowOrDeny, AnimationState, EmbedderMsg, FocusSequenceNumber, Image, LoadStatus, }; use encoding_rs::{Encoding, UTF_8}; use html5ever::{LocalName, Namespace, QualName, local_name, ns}; use hyper_serde::Serde; use js::rust::{HandleObject, HandleValue, MutableHandleValue}; use layout_api::{ PendingRestyle, ReflowGoal, ReflowPhasesRun, RestyleReason, ScrollContainerQueryFlags, TrustedNodeAddress, }; use metrics::{InteractiveFlag, InteractiveWindow, ProgressiveWebMetrics}; use net_traits::CookieSource::NonHTTP; use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl}; use net_traits::ReferrerPolicy; use net_traits::policy_container::PolicyContainer; use net_traits::pub_domains::is_pub_domain; use net_traits::request::{InsecureRequestsPolicy, PreloadedResources, RequestBuilder}; use net_traits::response::HttpsState; use percent_encoding::percent_decode; use profile_traits::ipc as profile_ipc; use profile_traits::time::TimerMetadataFrameType; use regex::bytes::Regex; use rustc_hash::{FxBuildHasher, FxHashMap}; use script_bindings::codegen::GenericBindings::ElementBinding::ElementMethods; use script_bindings::interfaces::DocumentHelpers; use script_bindings::script_runtime::JSContext; use script_traits::{DocumentActivity, ProgressiveWebMetricType}; use servo_arc::Arc; use servo_config::pref; use servo_media::{ClientContextId, ServoMedia}; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use style::attr::AttrValue; use style::context::QuirksMode; use style::invalidation::element::restyle_hints::RestyleHint; use style::selector_parser::Snapshot; use style::shared_lock::SharedRwLock as StyleSharedRwLock; use style::str::{split_html_space_chars, str_join}; use style::stylesheet_set::DocumentStylesheetSet; use style::stylesheets::{Origin, OriginSet, Stylesheet}; use stylo_atoms::Atom; use url::Host; use crate::animation_timeline::AnimationTimeline; use crate::animations::Animations; use crate::document_loader::{DocumentLoader, LoadType}; use crate::dom::attr::Attr; use crate::dom::beforeunloadevent::BeforeUnloadEvent; use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut}; use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEvent_Binding::BeforeUnloadEventMethods; use crate::dom::bindings::codegen::Bindings::DocumentBinding::{ DocumentMethods, DocumentReadyState, DocumentVisibilityState, NamedPropertyValue, }; use crate::dom::bindings::codegen::Bindings::ElementBinding::{ ScrollIntoViewContainer, ScrollIntoViewOptions, ScrollLogicalPosition, }; use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods; use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElement_Binding::HTMLIFrameElementMethods; use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions; use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter; use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods; use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionName; use crate::dom::bindings::codegen::Bindings::WindowBinding::{ FrameRequestCallback, ScrollBehavior, ScrollOptions, WindowMethods, }; use crate::dom::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMethods; use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver; use crate::dom::bindings::codegen::UnionTypes::{ BooleanOrScrollIntoViewOptions, NodeOrString, StringOrElementCreationOptions, TrustedHTMLOrString, }; use crate::dom::bindings::domname::{ self, is_valid_attribute_local_name, is_valid_element_local_name, namespace_from_domstring, }; use crate::dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible}; use crate::dom::bindings::frozenarray::CachedFrozenArray; use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; use crate::dom::bindings::num::Finite; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto}; use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout}; use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace}; use crate::dom::bindings::weakref::DOMTracker; use crate::dom::bindings::xmlname::matches_name_production; use crate::dom::cdatasection::CDATASection; use crate::dom::comment::Comment; use crate::dom::compositionevent::CompositionEvent; use crate::dom::css::cssstylesheet::CSSStyleSheet; use crate::dom::css::fontfaceset::FontFaceSet; use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner}; use crate::dom::customelementregistry::{CustomElementDefinition, CustomElementReactionStack}; use crate::dom::customevent::CustomEvent; use crate::dom::document_embedder_controls::DocumentEmbedderControls; use crate::dom::document_event_handler::DocumentEventHandler; use crate::dom::documentfragment::DocumentFragment; use crate::dom::documentorshadowroot::{ DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource, }; use crate::dom::documenttype::DocumentType; use crate::dom::domimplementation::DOMImplementation; use crate::dom::element::{ CustomElementCreationMode, Element, ElementCreator, ElementPerformFullscreenEnter, ElementPerformFullscreenExit, }; use crate::dom::event::{Event, EventBubbles, EventCancelable}; use crate::dom::eventtarget::EventTarget; use crate::dom::focusevent::FocusEvent; use crate::dom::globalscope::GlobalScope; use crate::dom::hashchangeevent::HashChangeEvent; use crate::dom::html::htmlanchorelement::HTMLAnchorElement; use crate::dom::html::htmlareaelement::HTMLAreaElement; use crate::dom::html::htmlbaseelement::HTMLBaseElement; use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection}; use crate::dom::html::htmlelement::HTMLElement; use crate::dom::html::htmlembedelement::HTMLEmbedElement; use crate::dom::html::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement}; use crate::dom::html::htmlheadelement::HTMLHeadElement; use crate::dom::html::htmlhtmlelement::HTMLHtmlElement; use crate::dom::html::htmliframeelement::HTMLIFrameElement; use crate::dom::html::htmlimageelement::HTMLImageElement; use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult}; use crate::dom::html::htmltitleelement::HTMLTitleElement; use crate::dom::htmldetailselement::DetailsNameGroups; use crate::dom::intersectionobserver::IntersectionObserver; use crate::dom::keyboardevent::KeyboardEvent; use crate::dom::largestcontentfulpaint::LargestContentfulPaint; use crate::dom::location::{Location, NavigationType}; use crate::dom::messageevent::MessageEvent; use crate::dom::mouseevent::MouseEvent; use crate::dom::node::{ CloneChildrenFlag, Node, NodeDamage, NodeFlags, NodeTraits, ShadowIncluding, }; use crate::dom::nodeiterator::NodeIterator; use crate::dom::nodelist::NodeList; use crate::dom::pagetransitionevent::PageTransitionEvent; use crate::dom::performance::performanceentry::PerformanceEntry; use crate::dom::performance::performancepainttiming::PerformancePaintTiming; use crate::dom::processinginstruction::ProcessingInstruction; use crate::dom::promise::Promise; use crate::dom::range::Range; use crate::dom::resizeobserver::{ResizeObservationDepth, ResizeObserver}; use crate::dom::scrolling_box::ScrollingBox; use crate::dom::selection::Selection; use crate::dom::servoparser::ServoParser; use crate::dom::shadowroot::ShadowRoot; use crate::dom::storageevent::StorageEvent; use crate::dom::text::Text; use crate::dom::touchevent::TouchEvent as DomTouchEvent; use crate::dom::touchlist::TouchList; use crate::dom::treewalker::TreeWalker; use crate::dom::trustedhtml::TrustedHTML; use crate::dom::types::{HTMLCanvasElement, VisibilityStateEntry}; use crate::dom::uievent::UIEvent; use crate::dom::virtualmethods::vtable_for; use crate::dom::websocket::WebSocket; use crate::dom::window::Window; use crate::dom::windowproxy::WindowProxy; use crate::dom::xpathevaluator::XPathEvaluator; use crate::dom::xpathexpression::XPathExpression; use crate::fetch::FetchCanceller; use crate::iframe_collection::IFrameCollection; use crate::image_animation::ImageAnimationManager; use crate::messaging::{CommonScriptMsg, MainThreadScriptMsg}; use crate::mime::{APPLICATION, CHARSET}; use crate::network_listener::{FetchResponseListener, NetworkListener}; use crate::realms::{AlreadyInRealm, InRealm, enter_realm}; use crate::script_runtime::{CanGc, ScriptThreadEventCategory}; use crate::script_thread::ScriptThread; use crate::stylesheet_set::StylesheetSetRef; use crate::task::NonSendTaskBox; use crate::task_source::TaskSourceName; use crate::timers::OneshotTimerCallback; use crate::xpath::parse_expression; #[derive(Clone, Copy, PartialEq)] pub(crate) enum FireMouseEventType { Move, Over, Out, Enter, Leave, } impl FireMouseEventType { pub(crate) fn as_str(&self) -> &str { match *self { FireMouseEventType::Move => "mousemove", FireMouseEventType::Over => "mouseover", FireMouseEventType::Out => "mouseout", FireMouseEventType::Enter => "mouseenter", FireMouseEventType::Leave => "mouseleave", } } } #[derive(JSTraceable, MallocSizeOf)] pub(crate) struct RefreshRedirectDue { #[no_trace] pub(crate) url: ServoUrl, #[ignore_malloc_size_of = "non-owning"] pub(crate) window: DomRoot, } impl RefreshRedirectDue { pub(crate) fn invoke(self, can_gc: CanGc) { self.window.Location().navigate( self.url.clone(), NavigationHistoryBehavior::Replace, NavigationType::DeclarativeRefresh, can_gc, ); } } #[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)] pub(crate) enum IsHTMLDocument { HTMLDocument, NonHTMLDocument, } #[derive(JSTraceable, MallocSizeOf)] #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] struct FocusTransaction { /// The focused element of this document. element: Option>, /// See [`Document::has_focus`]. has_focus: bool, /// Focus options for the transaction focus_options: FocusOptions, } /// Information about a declarative refresh #[derive(JSTraceable, MallocSizeOf)] pub(crate) enum DeclarativeRefresh { PendingLoad { #[no_trace] url: ServoUrl, time: u64, }, CreatedAfterLoad, } /// Reasons why a [`Document`] might need a rendering update that is otherwise /// untracked via other [`Document`] properties. #[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf)] pub(crate) struct RenderingUpdateReason(u8); bitflags! { impl RenderingUpdateReason: u8 { /// When a `ResizeObserver` starts observing a target, this becomes true, which in turn is a /// signal to the [`ScriptThread`] that a rendering update should happen. const ResizeObserverStartedObservingTarget = 1 << 0; /// When an `IntersectionObserver` starts observing a target, this becomes true, which in turn is a /// signal to the [`ScriptThread`] that a rendering update should happen. const IntersectionObserverStartedObservingTarget = 1 << 1; /// All web fonts have loaded and `fonts.ready` promise has been fulfilled. We want to trigger /// one more rendering update possibility after this happens, so that any potential screenshot /// reflects the up-to-date contents. const FontReadyPromiseFulfilled = 1 << 2; } } /// #[dom_struct] pub(crate) struct Document { node: Node, document_or_shadow_root: DocumentOrShadowRoot, window: Dom, implementation: MutNullableDom, #[ignore_malloc_size_of = "type from external crate"] #[no_trace] content_type: Mime, last_modified: Option, #[no_trace] encoding: Cell<&'static Encoding>, has_browsing_context: bool, is_html_document: bool, #[no_trace] activity: Cell, #[no_trace] url: DomRefCell, #[ignore_malloc_size_of = "defined in selectors"] #[no_trace] quirks_mode: Cell, /// A helper used to process and store data related to input event handling. event_handler: DocumentEventHandler, /// A helper to handle showing and hiding user interface controls in the embedding layer. embedder_controls: DocumentEmbedderControls, /// Caches for the getElement methods. It is safe to use FxHash for these maps /// as Atoms are `string_cache` items that will have the hash computed from a u32. id_map: DomRefCell>, FxBuildHasher>>, name_map: DomRefCell>, FxBuildHasher>>, tag_map: DomRefCell, FxBuildHasher>>, tagns_map: DomRefCell, FxBuildHasher>>, classes_map: DomRefCell, Dom>>, images: MutNullableDom, embeds: MutNullableDom, links: MutNullableDom, forms: MutNullableDom, scripts: MutNullableDom, anchors: MutNullableDom, applets: MutNullableDom, /// Information about the `` in this [`Document`]. iframes: RefCell, /// Lock use for style attributes and author-origin stylesheet objects in this document. /// Can be acquired once for accessing many objects. #[no_trace] style_shared_lock: StyleSharedRwLock, /// List of stylesheets associated with nodes in this document. |None| if the list needs to be refreshed. #[custom_trace] stylesheets: DomRefCell>, stylesheet_list: MutNullableDom, ready_state: Cell, /// Whether the DOMContentLoaded event has already been dispatched. domcontentloaded_dispatched: Cell, /// The state of this document's focus transaction. focus_transaction: DomRefCell>, /// The element that currently has the document focus context. focused: MutNullableDom, /// The last sequence number sent to the constellation. #[no_trace] focus_sequence: Cell, /// Indicates whether the container is included in the top-level browsing /// context's focus chain (not considering system focus). Permanently `true` /// for a top-level document. has_focus: Cell, /// The script element that is currently executing. current_script: MutNullableDom, /// pending_parsing_blocking_script: DomRefCell>, /// Number of stylesheets that block executing the next parser-inserted script script_blocking_stylesheets_count: Cell, /// Number of elements that block the rendering of the page. /// render_blocking_element_count: Cell, /// deferred_scripts: PendingInOrderScriptVec, /// asap_in_order_scripts_list: PendingInOrderScriptVec, /// asap_scripts_set: DomRefCell>>, /// /// Current identifier of animation frame callback animation_frame_ident: Cell, /// /// List of animation frame callbacks animation_frame_list: DomRefCell)>>, /// Whether we're in the process of running animation callbacks. /// /// Tracking this is not necessary for correctness. Instead, it is an optimization to avoid /// sending needless `ChangeRunningAnimationsState` messages to the compositor. running_animation_callbacks: Cell, /// Tracks all outstanding loads related to this document. loader: DomRefCell, /// The current active HTML parser, to allow resuming after interruptions. current_parser: MutNullableDom, /// The cached first `base` element with an `href` attribute. base_element: MutNullableDom, /// This field is set to the document itself for inert documents. /// appropriate_template_contents_owner_document: MutNullableDom, /// Information on elements needing restyle to ship over to layout when the /// time comes. pending_restyles: DomRefCell, NoTrace>>, /// A collection of reasons that the [`Document`] needs to be restyled at the next /// opportunity for a reflow. If this is empty, then the [`Document`] does not need to /// be restyled. #[no_trace] needs_restyle: Cell, /// Navigation Timing properties: /// #[no_trace] dom_interactive: Cell>, #[no_trace] dom_content_loaded_event_start: Cell>, #[no_trace] dom_content_loaded_event_end: Cell>, #[no_trace] dom_complete: Cell>, #[no_trace] top_level_dom_complete: Cell>, #[no_trace] load_event_start: Cell>, #[no_trace] load_event_end: Cell>, #[no_trace] unload_event_start: Cell>, #[no_trace] unload_event_end: Cell>, /// #[no_trace] https_state: Cell, /// The document's origin. #[no_trace] origin: MutableOrigin, /// referrer: Option, /// target_element: MutNullableDom, /// #[no_trace] policy_container: DomRefCell, /// #[no_trace] #[conditional_malloc_size_of] preloaded_resources: PreloadedResources, /// ignore_destructive_writes_counter: Cell, /// ignore_opens_during_unload_counter: Cell, /// The number of spurious `requestAnimationFrame()` requests we've received. /// /// A rAF request is considered spurious if nothing was actually reflowed. spurious_animation_frames: Cell, /// Track the total number of elements in this DOM's tree. /// This is sent to layout every time a reflow is done; /// layout uses this to determine if the gains from parallel layout will be worth the overhead. /// /// See also: dom_count: Cell, /// Entry node for fullscreen. fullscreen_element: MutNullableDom, /// Map from ID to set of form control elements that have that ID as /// their 'form' content attribute. Used to reset form controls /// whenever any element with the same ID as the form attribute /// is inserted or removed from the document. /// See /// It is safe to use FxBuildHasher here as Atoms are in the string_cache form_id_listener_map: DomRefCell>, FxBuildHasher>>, #[no_trace] interactive_time: DomRefCell, #[no_trace] tti_window: DomRefCell, /// RAII canceller for Fetch canceller: FetchCanceller, /// throw_on_dynamic_markup_insertion_counter: Cell, /// page_showing: Cell, /// Whether the document is salvageable. salvageable: Cell, /// Whether the document was aborted with an active parser active_parser_was_aborted: Cell, /// Whether the unload event has already been fired. fired_unload: Cell, /// List of responsive images responsive_images: DomRefCell>>, /// Number of redirects for the document load redirect_count: Cell, /// Number of outstanding requests to prevent JS or layout from running. script_and_layout_blockers: Cell, /// List of tasks to execute as soon as last script/layout blocker is removed. #[ignore_malloc_size_of = "Measuring trait objects is hard"] delayed_tasks: DomRefCell>>, /// completely_loaded: Cell, /// Set of shadow roots connected to the document tree. shadow_roots: DomRefCell>>, /// Whether any of the shadow roots need the stylesheets flushed. shadow_roots_styles_changed: Cell, /// List of registered media controls. /// We need to keep this list to allow the media controls to /// access the "privileged" document.servoGetMediaControls(id) API, /// where `id` needs to match any of the registered ShadowRoots /// hosting the media controls UI. media_controls: DomRefCell>>, /// A set of dirty HTML canvas elements that need their WebRender images updated the /// next time the rendering is updated. dirty_canvases: DomRefCell>>, /// Whether or not animated images need to have their contents updated. has_pending_animated_image_update: Cell, /// selection: MutNullableDom, /// A timeline for animations which is used for synchronizing animations. /// animation_timeline: DomRefCell, /// Animations for this Document animations: Animations, /// Image Animation Manager for this Document image_animation_manager: DomRefCell, /// The nearest inclusive ancestors to all the nodes that require a restyle. dirty_root: MutNullableDom, /// declarative_refresh: DomRefCell>, /// /// /// Note: we are storing, but never removing, resize observers. /// The lifetime of resize observers is specified at /// . /// But implementing it comes with known problems: /// - /// - resize_observers: DomRefCell>>, /// The set of all fonts loaded by this document. /// fonts: MutNullableDom, /// visibility_state: Cell, /// status_code: Option, /// is_initial_about_blank: Cell, /// allow_declarative_shadow_roots: Cell, /// #[no_trace] inherited_insecure_requests_policy: Cell>, //// has_trustworthy_ancestor_origin: Cell, /// intersection_observer_task_queued: Cell, /// Active intersection observers that should be processed by this document in /// the update intersection observation steps. /// /// > Let observer list be a list of all IntersectionObservers whose root is in the DOM tree of document. /// > For the top-level browsing context, this includes implicit root observers. /// /// Details of which document that should process an observers is discussed further at /// . /// /// The lifetime of an intersection observer is specified at /// . intersection_observers: DomRefCell>>, /// The node that is currently highlighted by the devtools highlighted_dom_node: MutNullableDom, /// The constructed stylesheet that is adopted by this [Document]. /// adopted_stylesheets: DomRefCell>>, /// Cached frozen array of [`Self::adopted_stylesheets`] #[ignore_malloc_size_of = "mozjs"] adopted_stylesheets_frozen_types: CachedFrozenArray, /// pending_scroll_event_targets: DomRefCell>>, /// Other reasons that a rendering update might be required for this [`Document`]. rendering_update_reasons: Cell, /// Whether or not this [`Document`] is waiting on canvas image updates. If it is /// waiting it will not do any new layout until the canvas images are up-to-date in /// the renderer. waiting_on_canvas_image_updates: Cell, /// The current rendering epoch, which is used to track updates in the renderer. /// /// - Every display list update also advances the Epoch, so that the renderer knows /// when a particular display list is ready in order to take a screenshot. /// - Canvas image updates happen asynchronously and are tagged with this Epoch. Until /// those asynchronous updates are complete, the `Document` will not perform any /// more rendering updates. #[no_trace] current_rendering_epoch: Cell, /// The global custom element reaction stack for this script thread. #[conditional_malloc_size_of] custom_element_reaction_stack: Rc, #[no_trace] /// , active_sandboxing_flag_set: Cell, #[no_trace] /// The [`SandboxingFlagSet`] use to create the browsing context for this [`Document`]. /// These are cached here as they cannot always be retrieved readily if the owner of /// browsing context (either `