script/dom/
document.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::{Cell, RefCell};
6use std::cmp::Ordering;
7use std::collections::hash_map::Entry::{Occupied, Vacant};
8use std::collections::{HashMap, HashSet, VecDeque};
9use std::default::Default;
10use std::rc::Rc;
11use std::str::FromStr;
12use std::sync::{LazyLock, Mutex};
13use std::time::Duration;
14
15use base::cross_process_instant::CrossProcessInstant;
16use base::id::WebViewId;
17use base::{Epoch, IpcSend, generic_channel};
18use bitflags::bitflags;
19use canvas_traits::canvas::CanvasId;
20use canvas_traits::webgl::{WebGLContextId, WebGLMsg};
21use chrono::Local;
22use constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage};
23use content_security_policy::sandboxing_directive::SandboxingFlagSet;
24use content_security_policy::{CspList, PolicyDisposition};
25use cookie::Cookie;
26use data_url::mime::Mime;
27use devtools_traits::ScriptToDevtoolsControlMsg;
28use dom_struct::dom_struct;
29use embedder_traits::{
30    AllowOrDeny, AnimationState, EmbedderMsg, FocusSequenceNumber, Image, LoadStatus,
31};
32use encoding_rs::{Encoding, UTF_8};
33use html5ever::{LocalName, Namespace, QualName, local_name, ns};
34use hyper_serde::Serde;
35use js::rust::{HandleObject, HandleValue, MutableHandleValue};
36use layout_api::{
37    PendingRestyle, ReflowGoal, ReflowPhasesRun, RestyleReason, ScrollContainerQueryFlags,
38    TrustedNodeAddress,
39};
40use metrics::{InteractiveFlag, InteractiveWindow, ProgressiveWebMetrics};
41use net_traits::CookieSource::NonHTTP;
42use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl};
43use net_traits::policy_container::PolicyContainer;
44use net_traits::pub_domains::is_pub_domain;
45use net_traits::request::{InsecureRequestsPolicy, PreloadedResources, RequestBuilder};
46use net_traits::response::HttpsState;
47use net_traits::{FetchResponseListener, ReferrerPolicy};
48use percent_encoding::percent_decode;
49use profile_traits::ipc as profile_ipc;
50use profile_traits::time::TimerMetadataFrameType;
51use regex::bytes::Regex;
52use rustc_hash::{FxBuildHasher, FxHashMap};
53use script_bindings::codegen::GenericBindings::ElementBinding::ElementMethods;
54use script_bindings::interfaces::DocumentHelpers;
55use script_bindings::script_runtime::JSContext;
56use script_traits::{DocumentActivity, ProgressiveWebMetricType};
57use servo_arc::Arc;
58use servo_config::pref;
59use servo_media::{ClientContextId, ServoMedia};
60use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
61use style::attr::AttrValue;
62use style::context::QuirksMode;
63use style::invalidation::element::restyle_hints::RestyleHint;
64use style::selector_parser::Snapshot;
65use style::shared_lock::SharedRwLock as StyleSharedRwLock;
66use style::str::{split_html_space_chars, str_join};
67use style::stylesheet_set::DocumentStylesheetSet;
68use style::stylesheets::{Origin, OriginSet, Stylesheet};
69use stylo_atoms::Atom;
70use url::Host;
71use uuid::Uuid;
72#[cfg(feature = "webgpu")]
73use webgpu_traits::WebGPUContextId;
74
75use crate::animation_timeline::AnimationTimeline;
76use crate::animations::Animations;
77use crate::canvas_context::CanvasContext as _;
78use crate::document_loader::{DocumentLoader, LoadType};
79use crate::dom::attr::Attr;
80use crate::dom::beforeunloadevent::BeforeUnloadEvent;
81use crate::dom::bindings::callback::ExceptionHandling;
82use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut};
83use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEvent_Binding::BeforeUnloadEventMethods;
84use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
85    DocumentMethods, DocumentReadyState, DocumentVisibilityState, NamedPropertyValue,
86};
87use crate::dom::bindings::codegen::Bindings::ElementBinding::{
88    ScrollIntoViewContainer, ScrollIntoViewOptions, ScrollLogicalPosition,
89};
90use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
91use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElement_Binding::HTMLIFrameElementMethods;
92use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
93use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
94use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
95use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
96use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
97use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionName;
98use crate::dom::bindings::codegen::Bindings::WindowBinding::{
99    FrameRequestCallback, ScrollBehavior, ScrollOptions, WindowMethods,
100};
101use crate::dom::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMethods;
102use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
103use crate::dom::bindings::codegen::UnionTypes::{
104    BooleanOrScrollIntoViewOptions, NodeOrString, StringOrElementCreationOptions,
105    TrustedHTMLOrString,
106};
107use crate::dom::bindings::domname::{
108    self, is_valid_attribute_local_name, is_valid_element_local_name, namespace_from_domstring,
109};
110use crate::dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible};
111use crate::dom::bindings::frozenarray::CachedFrozenArray;
112use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
113use crate::dom::bindings::num::Finite;
114use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
115use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto};
116use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout};
117use crate::dom::bindings::str::{DOMString, USVString};
118use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
119use crate::dom::bindings::weakref::DOMTracker;
120use crate::dom::bindings::xmlname::matches_name_production;
121use crate::dom::canvasrenderingcontext2d::CanvasRenderingContext2D;
122use crate::dom::cdatasection::CDATASection;
123use crate::dom::comment::Comment;
124use crate::dom::compositionevent::CompositionEvent;
125use crate::dom::css::cssstylesheet::CSSStyleSheet;
126use crate::dom::css::fontfaceset::FontFaceSet;
127use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
128use crate::dom::customelementregistry::{CustomElementDefinition, CustomElementReactionStack};
129use crate::dom::customevent::CustomEvent;
130use crate::dom::document_embedder_controls::DocumentEmbedderControls;
131use crate::dom::document_event_handler::DocumentEventHandler;
132use crate::dom::documentfragment::DocumentFragment;
133use crate::dom::documentorshadowroot::{
134    DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
135};
136use crate::dom::documenttype::DocumentType;
137use crate::dom::domimplementation::DOMImplementation;
138use crate::dom::element::{
139    CustomElementCreationMode, Element, ElementCreator, ElementPerformFullscreenEnter,
140    ElementPerformFullscreenExit,
141};
142use crate::dom::event::{Event, EventBubbles, EventCancelable};
143use crate::dom::eventtarget::EventTarget;
144use crate::dom::focusevent::FocusEvent;
145use crate::dom::globalscope::GlobalScope;
146use crate::dom::hashchangeevent::HashChangeEvent;
147use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
148use crate::dom::html::htmlareaelement::HTMLAreaElement;
149use crate::dom::html::htmlbaseelement::HTMLBaseElement;
150use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
151use crate::dom::html::htmlelement::HTMLElement;
152use crate::dom::html::htmlembedelement::HTMLEmbedElement;
153use crate::dom::html::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
154use crate::dom::html::htmlheadelement::HTMLHeadElement;
155use crate::dom::html::htmlhtmlelement::HTMLHtmlElement;
156use crate::dom::html::htmliframeelement::HTMLIFrameElement;
157use crate::dom::html::htmlimageelement::HTMLImageElement;
158use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult};
159use crate::dom::html::htmltitleelement::HTMLTitleElement;
160use crate::dom::intersectionobserver::IntersectionObserver;
161use crate::dom::keyboardevent::KeyboardEvent;
162use crate::dom::location::{Location, NavigationType};
163use crate::dom::messageevent::MessageEvent;
164use crate::dom::mouseevent::MouseEvent;
165use crate::dom::node::{
166    CloneChildrenFlag, Node, NodeDamage, NodeFlags, NodeTraits, ShadowIncluding,
167};
168use crate::dom::nodeiterator::NodeIterator;
169use crate::dom::nodelist::NodeList;
170use crate::dom::pagetransitionevent::PageTransitionEvent;
171use crate::dom::performance::performanceentry::PerformanceEntry;
172use crate::dom::performance::performancepainttiming::PerformancePaintTiming;
173use crate::dom::processinginstruction::ProcessingInstruction;
174use crate::dom::promise::Promise;
175use crate::dom::range::Range;
176use crate::dom::resizeobserver::{ResizeObservationDepth, ResizeObserver};
177use crate::dom::scrolling_box::ScrollingBox;
178use crate::dom::selection::Selection;
179use crate::dom::servoparser::ServoParser;
180use crate::dom::shadowroot::ShadowRoot;
181use crate::dom::storageevent::StorageEvent;
182use crate::dom::text::Text;
183use crate::dom::touchevent::TouchEvent as DomTouchEvent;
184use crate::dom::touchlist::TouchList;
185use crate::dom::treewalker::TreeWalker;
186use crate::dom::trustedhtml::TrustedHTML;
187use crate::dom::types::VisibilityStateEntry;
188use crate::dom::uievent::UIEvent;
189use crate::dom::virtualmethods::vtable_for;
190use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
191#[cfg(feature = "webgpu")]
192use crate::dom::webgpu::gpucanvascontext::GPUCanvasContext;
193use crate::dom::websocket::WebSocket;
194use crate::dom::window::Window;
195use crate::dom::windowproxy::WindowProxy;
196use crate::dom::xpathevaluator::XPathEvaluator;
197use crate::fetch::FetchCanceller;
198use crate::iframe_collection::IFrameCollection;
199use crate::image_animation::ImageAnimationManager;
200use crate::messaging::{CommonScriptMsg, MainThreadScriptMsg};
201use crate::mime::{APPLICATION, CHARSET};
202use crate::network_listener::{NetworkListener, PreInvoke};
203use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
204use crate::script_runtime::{CanGc, ScriptThreadEventCategory};
205use crate::script_thread::ScriptThread;
206use crate::stylesheet_set::StylesheetSetRef;
207use crate::task::NonSendTaskBox;
208use crate::task_source::TaskSourceName;
209use crate::timers::OneshotTimerCallback;
210
211#[derive(Clone, Copy, PartialEq)]
212pub(crate) enum FireMouseEventType {
213    Move,
214    Over,
215    Out,
216    Enter,
217    Leave,
218}
219
220impl FireMouseEventType {
221    pub(crate) fn as_str(&self) -> &str {
222        match *self {
223            FireMouseEventType::Move => "mousemove",
224            FireMouseEventType::Over => "mouseover",
225            FireMouseEventType::Out => "mouseout",
226            FireMouseEventType::Enter => "mouseenter",
227            FireMouseEventType::Leave => "mouseleave",
228        }
229    }
230}
231
232#[derive(JSTraceable, MallocSizeOf)]
233pub(crate) struct RefreshRedirectDue {
234    #[no_trace]
235    pub(crate) url: ServoUrl,
236    #[ignore_malloc_size_of = "non-owning"]
237    pub(crate) window: DomRoot<Window>,
238}
239impl RefreshRedirectDue {
240    pub(crate) fn invoke(self, can_gc: CanGc) {
241        self.window.Location().navigate(
242            self.url.clone(),
243            NavigationHistoryBehavior::Replace,
244            NavigationType::DeclarativeRefresh,
245            can_gc,
246        );
247    }
248}
249
250#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
251pub(crate) enum IsHTMLDocument {
252    HTMLDocument,
253    NonHTMLDocument,
254}
255
256#[derive(JSTraceable, MallocSizeOf)]
257#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
258struct FocusTransaction {
259    /// The focused element of this document.
260    element: Option<Dom<Element>>,
261    /// See [`Document::has_focus`].
262    has_focus: bool,
263    /// Focus options for the transaction
264    focus_options: FocusOptions,
265}
266
267/// Information about a declarative refresh
268#[derive(JSTraceable, MallocSizeOf)]
269pub(crate) enum DeclarativeRefresh {
270    PendingLoad {
271        #[no_trace]
272        url: ServoUrl,
273        time: u64,
274    },
275    CreatedAfterLoad,
276}
277
278/// Reasons why a [`Document`] might need a rendering update that is otherwise
279/// untracked via other [`Document`] properties.
280#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf)]
281pub(crate) struct RenderingUpdateReason(u8);
282
283bitflags! {
284    impl RenderingUpdateReason: u8 {
285        /// When a `ResizeObserver` starts observing a target, this becomes true, which in turn is a
286        /// signal to the [`ScriptThread`] that a rendering update should happen.
287        const ResizeObserverStartedObservingTarget = 1 << 0;
288        /// When an `IntersectionObserver` starts observing a target, this becomes true, which in turn is a
289        /// signal to the [`ScriptThread`] that a rendering update should happen.
290        const IntersectionObserverStartedObservingTarget = 1 << 1;
291        /// All web fonts have loaded and `fonts.ready` promise has been fulfilled. We want to trigger
292        /// one more rendering update possibility after this happens, so that any potential screenshot
293        /// reflects the up-to-date contents.
294        const FontReadyPromiseFulfilled = 1 << 2;
295    }
296}
297
298/// <https://dom.spec.whatwg.org/#document>
299#[dom_struct]
300pub(crate) struct Document {
301    node: Node,
302    document_or_shadow_root: DocumentOrShadowRoot,
303    window: Dom<Window>,
304    implementation: MutNullableDom<DOMImplementation>,
305    #[ignore_malloc_size_of = "type from external crate"]
306    #[no_trace]
307    content_type: Mime,
308    last_modified: Option<String>,
309    #[no_trace]
310    encoding: Cell<&'static Encoding>,
311    has_browsing_context: bool,
312    is_html_document: bool,
313    #[no_trace]
314    activity: Cell<DocumentActivity>,
315    #[no_trace]
316    url: DomRefCell<ServoUrl>,
317    #[ignore_malloc_size_of = "defined in selectors"]
318    #[no_trace]
319    quirks_mode: Cell<QuirksMode>,
320    /// A helper used to process and store data related to input event handling.
321    event_handler: DocumentEventHandler,
322    /// A helper to handle showing and hiding user interface controls in the embedding layer.
323    embedder_controls: DocumentEmbedderControls,
324    /// Caches for the getElement methods. It is safe to use FxHash for these maps
325    /// as Atoms are `string_cache` items that will have the hash computed from a u32.
326    id_map: DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>>,
327    name_map: DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>>,
328    tag_map: DomRefCell<HashMapTracedValues<LocalName, Dom<HTMLCollection>, FxBuildHasher>>,
329    tagns_map: DomRefCell<HashMapTracedValues<QualName, Dom<HTMLCollection>, FxBuildHasher>>,
330    classes_map: DomRefCell<HashMapTracedValues<Vec<Atom>, Dom<HTMLCollection>>>,
331    images: MutNullableDom<HTMLCollection>,
332    embeds: MutNullableDom<HTMLCollection>,
333    links: MutNullableDom<HTMLCollection>,
334    forms: MutNullableDom<HTMLCollection>,
335    scripts: MutNullableDom<HTMLCollection>,
336    anchors: MutNullableDom<HTMLCollection>,
337    applets: MutNullableDom<HTMLCollection>,
338    /// Information about the `<iframes>` in this [`Document`].
339    iframes: RefCell<IFrameCollection>,
340    /// Lock use for style attributes and author-origin stylesheet objects in this document.
341    /// Can be acquired once for accessing many objects.
342    #[no_trace]
343    style_shared_lock: StyleSharedRwLock,
344    /// List of stylesheets associated with nodes in this document. |None| if the list needs to be refreshed.
345    #[custom_trace]
346    stylesheets: DomRefCell<DocumentStylesheetSet<ServoStylesheetInDocument>>,
347    stylesheet_list: MutNullableDom<StyleSheetList>,
348    ready_state: Cell<DocumentReadyState>,
349    /// Whether the DOMContentLoaded event has already been dispatched.
350    domcontentloaded_dispatched: Cell<bool>,
351    /// The state of this document's focus transaction.
352    focus_transaction: DomRefCell<Option<FocusTransaction>>,
353    /// The element that currently has the document focus context.
354    focused: MutNullableDom<Element>,
355    /// The last sequence number sent to the constellation.
356    #[no_trace]
357    focus_sequence: Cell<FocusSequenceNumber>,
358    /// Indicates whether the container is included in the top-level browsing
359    /// context's focus chain (not considering system focus). Permanently `true`
360    /// for a top-level document.
361    has_focus: Cell<bool>,
362    /// The script element that is currently executing.
363    current_script: MutNullableDom<HTMLScriptElement>,
364    /// <https://html.spec.whatwg.org/multipage/#pending-parsing-blocking-script>
365    pending_parsing_blocking_script: DomRefCell<Option<PendingScript>>,
366    /// Number of stylesheets that block executing the next parser-inserted script
367    script_blocking_stylesheets_count: Cell<u32>,
368    /// Number of elements that block the rendering of the page.
369    /// <https://html.spec.whatwg.org/multipage/#implicitly-potentially-render-blocking>
370    render_blocking_element_count: Cell<u32>,
371    /// <https://html.spec.whatwg.org/multipage/#list-of-scripts-that-will-execute-when-the-document-has-finished-parsing>
372    deferred_scripts: PendingInOrderScriptVec,
373    /// <https://html.spec.whatwg.org/multipage/#list-of-scripts-that-will-execute-in-order-as-soon-as-possible>
374    asap_in_order_scripts_list: PendingInOrderScriptVec,
375    /// <https://html.spec.whatwg.org/multipage/#set-of-scripts-that-will-execute-as-soon-as-possible>
376    asap_scripts_set: DomRefCell<Vec<Dom<HTMLScriptElement>>>,
377    /// <https://html.spec.whatwg.org/multipage/#animation-frame-callback-identifier>
378    /// Current identifier of animation frame callback
379    animation_frame_ident: Cell<u32>,
380    /// <https://html.spec.whatwg.org/multipage/#list-of-animation-frame-callbacks>
381    /// List of animation frame callbacks
382    animation_frame_list: DomRefCell<VecDeque<(u32, Option<AnimationFrameCallback>)>>,
383    /// Whether we're in the process of running animation callbacks.
384    ///
385    /// Tracking this is not necessary for correctness. Instead, it is an optimization to avoid
386    /// sending needless `ChangeRunningAnimationsState` messages to the compositor.
387    running_animation_callbacks: Cell<bool>,
388    /// Tracks all outstanding loads related to this document.
389    loader: DomRefCell<DocumentLoader>,
390    /// The current active HTML parser, to allow resuming after interruptions.
391    current_parser: MutNullableDom<ServoParser>,
392    /// The cached first `base` element with an `href` attribute.
393    base_element: MutNullableDom<HTMLBaseElement>,
394    /// This field is set to the document itself for inert documents.
395    /// <https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document>
396    appropriate_template_contents_owner_document: MutNullableDom<Document>,
397    /// Information on elements needing restyle to ship over to layout when the
398    /// time comes.
399    pending_restyles: DomRefCell<FxHashMap<Dom<Element>, NoTrace<PendingRestyle>>>,
400    /// A collection of reasons that the [`Document`] needs to be restyled at the next
401    /// opportunity for a reflow. If this is empty, then the [`Document`] does not need to
402    /// be restyled.
403    #[no_trace]
404    needs_restyle: Cell<RestyleReason>,
405    /// Navigation Timing properties:
406    /// <https://w3c.github.io/navigation-timing/#sec-PerformanceNavigationTiming>
407    #[no_trace]
408    dom_interactive: Cell<Option<CrossProcessInstant>>,
409    #[no_trace]
410    dom_content_loaded_event_start: Cell<Option<CrossProcessInstant>>,
411    #[no_trace]
412    dom_content_loaded_event_end: Cell<Option<CrossProcessInstant>>,
413    #[no_trace]
414    dom_complete: Cell<Option<CrossProcessInstant>>,
415    #[no_trace]
416    top_level_dom_complete: Cell<Option<CrossProcessInstant>>,
417    #[no_trace]
418    load_event_start: Cell<Option<CrossProcessInstant>>,
419    #[no_trace]
420    load_event_end: Cell<Option<CrossProcessInstant>>,
421    #[no_trace]
422    unload_event_start: Cell<Option<CrossProcessInstant>>,
423    #[no_trace]
424    unload_event_end: Cell<Option<CrossProcessInstant>>,
425    /// <https://html.spec.whatwg.org/multipage/#concept-document-https-state>
426    #[no_trace]
427    https_state: Cell<HttpsState>,
428    /// The document's origin.
429    #[no_trace]
430    origin: MutableOrigin,
431    /// <https://html.spec.whatwg.org/multipage/#dom-document-referrer>
432    referrer: Option<String>,
433    /// <https://html.spec.whatwg.org/multipage/#target-element>
434    target_element: MutNullableDom<Element>,
435    /// <https://html.spec.whatwg.org/multipage/#concept-document-policy-container>
436    #[no_trace]
437    policy_container: DomRefCell<PolicyContainer>,
438    /// <https://html.spec.whatwg.org/multipage/#map-of-preloaded-resources>
439    #[no_trace]
440    #[conditional_malloc_size_of]
441    preloaded_resources: PreloadedResources,
442    /// <https://html.spec.whatwg.org/multipage/#ignore-destructive-writes-counter>
443    ignore_destructive_writes_counter: Cell<u32>,
444    /// <https://html.spec.whatwg.org/multipage/#ignore-opens-during-unload-counter>
445    ignore_opens_during_unload_counter: Cell<u32>,
446    /// The number of spurious `requestAnimationFrame()` requests we've received.
447    ///
448    /// A rAF request is considered spurious if nothing was actually reflowed.
449    spurious_animation_frames: Cell<u8>,
450
451    /// Track the total number of elements in this DOM's tree.
452    /// This is sent to layout every time a reflow is done;
453    /// layout uses this to determine if the gains from parallel layout will be worth the overhead.
454    ///
455    /// See also: <https://github.com/servo/servo/issues/10110>
456    dom_count: Cell<u32>,
457    /// Entry node for fullscreen.
458    fullscreen_element: MutNullableDom<Element>,
459    /// Map from ID to set of form control elements that have that ID as
460    /// their 'form' content attribute. Used to reset form controls
461    /// whenever any element with the same ID as the form attribute
462    /// is inserted or removed from the document.
463    /// See <https://html.spec.whatwg.org/multipage/#form-owner>
464    /// It is safe to use FxBuildHasher here as Atoms are in the string_cache
465    form_id_listener_map:
466        DomRefCell<HashMapTracedValues<Atom, HashSet<Dom<Element>>, FxBuildHasher>>,
467    #[no_trace]
468    interactive_time: DomRefCell<ProgressiveWebMetrics>,
469    #[no_trace]
470    tti_window: DomRefCell<InteractiveWindow>,
471    /// RAII canceller for Fetch
472    canceller: FetchCanceller,
473    /// <https://html.spec.whatwg.org/multipage/#throw-on-dynamic-markup-insertion-counter>
474    throw_on_dynamic_markup_insertion_counter: Cell<u64>,
475    /// <https://html.spec.whatwg.org/multipage/#page-showing>
476    page_showing: Cell<bool>,
477    /// Whether the document is salvageable.
478    salvageable: Cell<bool>,
479    /// Whether the document was aborted with an active parser
480    active_parser_was_aborted: Cell<bool>,
481    /// Whether the unload event has already been fired.
482    fired_unload: Cell<bool>,
483    /// List of responsive images
484    responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>,
485    /// Number of redirects for the document load
486    redirect_count: Cell<u16>,
487    /// Number of outstanding requests to prevent JS or layout from running.
488    script_and_layout_blockers: Cell<u32>,
489    /// List of tasks to execute as soon as last script/layout blocker is removed.
490    #[ignore_malloc_size_of = "Measuring trait objects is hard"]
491    delayed_tasks: DomRefCell<Vec<Box<dyn NonSendTaskBox>>>,
492    /// <https://html.spec.whatwg.org/multipage/#completely-loaded>
493    completely_loaded: Cell<bool>,
494    /// Set of shadow roots connected to the document tree.
495    shadow_roots: DomRefCell<HashSet<Dom<ShadowRoot>>>,
496    /// Whether any of the shadow roots need the stylesheets flushed.
497    shadow_roots_styles_changed: Cell<bool>,
498    /// List of registered media controls.
499    /// We need to keep this list to allow the media controls to
500    /// access the "privileged" document.servoGetMediaControls(id) API,
501    /// where `id` needs to match any of the registered ShadowRoots
502    /// hosting the media controls UI.
503    media_controls: DomRefCell<HashMap<String, Dom<ShadowRoot>>>,
504    /// List of all context 2d IDs that need flushing.
505    dirty_2d_contexts:
506        DomRefCell<HashMapTracedValues<CanvasId, Dom<CanvasRenderingContext2D>, FxBuildHasher>>,
507    /// List of all WebGL context IDs that need flushing.
508    dirty_webgl_contexts:
509        DomRefCell<HashMapTracedValues<WebGLContextId, Dom<WebGLRenderingContext>, FxBuildHasher>>,
510    /// Whether or not animated images need to have their contents updated.
511    has_pending_animated_image_update: Cell<bool>,
512    /// List of all WebGPU contexts that need flushing.
513    #[cfg(feature = "webgpu")]
514    dirty_webgpu_contexts:
515        DomRefCell<HashMapTracedValues<WebGPUContextId, Dom<GPUCanvasContext>, FxBuildHasher>>,
516    /// <https://w3c.github.io/slection-api/#dfn-selection>
517    selection: MutNullableDom<Selection>,
518    /// A timeline for animations which is used for synchronizing animations.
519    /// <https://drafts.csswg.org/web-animations/#timeline>
520    animation_timeline: DomRefCell<AnimationTimeline>,
521    /// Animations for this Document
522    animations: Animations,
523    /// Image Animation Manager for this Document
524    image_animation_manager: DomRefCell<ImageAnimationManager>,
525    /// The nearest inclusive ancestors to all the nodes that require a restyle.
526    dirty_root: MutNullableDom<Element>,
527    /// <https://html.spec.whatwg.org/multipage/#will-declaratively-refresh>
528    declarative_refresh: DomRefCell<Option<DeclarativeRefresh>>,
529    /// <https://drafts.csswg.org/resize-observer/#dom-document-resizeobservers-slot>
530    ///
531    /// Note: we are storing, but never removing, resize observers.
532    /// The lifetime of resize observers is specified at
533    /// <https://drafts.csswg.org/resize-observer/#lifetime>.
534    /// But implementing it comes with known problems:
535    /// - <https://bugzilla.mozilla.org/show_bug.cgi?id=1596992>
536    /// - <https://github.com/w3c/csswg-drafts/issues/4518>
537    resize_observers: DomRefCell<Vec<Dom<ResizeObserver>>>,
538    /// The set of all fonts loaded by this document.
539    /// <https://drafts.csswg.org/css-font-loading/#font-face-source>
540    fonts: MutNullableDom<FontFaceSet>,
541    /// <https://html.spec.whatwg.org/multipage/#visibility-state>
542    visibility_state: Cell<DocumentVisibilityState>,
543    /// <https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml>
544    status_code: Option<u16>,
545    /// <https://html.spec.whatwg.org/multipage/#is-initial-about:blank>
546    is_initial_about_blank: Cell<bool>,
547    /// <https://dom.spec.whatwg.org/#document-allow-declarative-shadow-roots>
548    allow_declarative_shadow_roots: Cell<bool>,
549    /// <https://w3c.github.io/webappsec-upgrade-insecure-requests/#insecure-requests-policy>
550    #[no_trace]
551    inherited_insecure_requests_policy: Cell<Option<InsecureRequestsPolicy>>,
552    //// <https://w3c.github.io/webappsec-mixed-content/#categorize-settings-object>
553    has_trustworthy_ancestor_origin: Cell<bool>,
554    /// <https://w3c.github.io/IntersectionObserver/#document-intersectionobservertaskqueued>
555    intersection_observer_task_queued: Cell<bool>,
556    /// Active intersection observers that should be processed by this document in
557    /// the update intersection observation steps.
558    /// <https://w3c.github.io/IntersectionObserver/#run-the-update-intersection-observations-steps>
559    /// > Let observer list be a list of all IntersectionObservers whose root is in the DOM tree of document.
560    /// > For the top-level browsing context, this includes implicit root observers.
561    ///
562    /// Details of which document that should process an observers is discussed further at
563    /// <https://github.com/w3c/IntersectionObserver/issues/525>.
564    ///
565    /// The lifetime of an intersection observer is specified at
566    /// <https://github.com/w3c/IntersectionObserver/issues/525>.
567    intersection_observers: DomRefCell<Vec<Dom<IntersectionObserver>>>,
568    /// The node that is currently highlighted by the devtools
569    highlighted_dom_node: MutNullableDom<Node>,
570    /// The constructed stylesheet that is adopted by this [Document].
571    /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
572    adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
573    /// Cached frozen array of [`Self::adopted_stylesheets`]
574    #[ignore_malloc_size_of = "mozjs"]
575    adopted_stylesheets_frozen_types: CachedFrozenArray,
576    /// <https://drafts.csswg.org/cssom-view/#document-pending-scroll-event-targets>
577    pending_scroll_event_targets: DomRefCell<Vec<Dom<EventTarget>>>,
578    /// Other reasons that a rendering update might be required for this [`Document`].
579    rendering_update_reasons: Cell<RenderingUpdateReason>,
580    /// Whether or not this [`Document`] is waiting on canvas image updates. If it is
581    /// waiting it will not do any new layout until the canvas images are up-to-date in
582    /// the renderer.
583    waiting_on_canvas_image_updates: Cell<bool>,
584    /// The current rendering epoch, which is used to track updates in the renderer.
585    ///
586    ///   - Every display list update also advances the Epoch, so that the renderer knows
587    ///     when a particular display list is ready in order to take a screenshot.
588    ///   - Canvas image updates happen asynchronously and are tagged with this Epoch. Until
589    ///     those asynchronous updates are complete, the `Document` will not perform any
590    ///     more rendering updates.
591    #[no_trace]
592    current_rendering_epoch: Cell<Epoch>,
593    /// The global custom element reaction stack for this script thread.
594    #[conditional_malloc_size_of]
595    custom_element_reaction_stack: Rc<CustomElementReactionStack>,
596    #[no_trace]
597    /// <https://html.spec.whatwg.org/multipage/#active-sandboxing-flag-set>,
598    active_sandboxing_flag_set: Cell<SandboxingFlagSet>,
599    #[no_trace]
600    /// The [`SandboxingFlagSet`] use to create the browsing context for this [`Document`].
601    /// These are cached here as they cannot always be retrieved readily if the owner of
602    /// browsing context (either `<iframe>` or popup) might be in a different `ScriptThread`.
603    ///
604    /// See
605    /// <https://html.spec.whatwg.org/multipage/#determining-the-creation-sandboxing-flags>.
606    creation_sandboxing_flag_set: Cell<SandboxingFlagSet>,
607    /// The cached favicon for that document.
608    #[no_trace]
609    #[ignore_malloc_size_of = "TODO: unimplemented on Image"]
610    favicon: RefCell<Option<Image>>,
611
612    /// All websockets created that are associated with this document.
613    websockets: DOMTracker<WebSocket>,
614}
615
616#[allow(non_snake_case)]
617impl Document {
618    /// <https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps>
619    fn unloading_cleanup_steps(&self) {
620        // Step 1. Let window be document's relevant global object.
621        // Step 2. For each WebSocket object webSocket whose relevant global object is window, make disappear webSocket.
622        if self.close_outstanding_websockets() {
623            // If this affected any WebSocket objects, then make document unsalvageable given document and "websocket".
624            self.salvageable.set(false);
625        }
626
627        // Step 3. For each WebTransport object transport whose relevant global object is window, run the context cleanup steps given transport.
628        // TODO
629
630        // Step 4. If document's salvageable state is false, then:
631        if !self.salvageable.get() {
632            let global_scope = self.window.as_global_scope();
633
634            // Step 4.1. For each EventSource object eventSource whose relevant global object is equal to window, forcibly close eventSource.
635            global_scope.close_event_sources();
636
637            // Step 4.2. Clear window's map of active timers.
638            // TODO
639
640            // Ensure the constellation discards all bfcache information for this document.
641            let msg = ScriptToConstellationMessage::DiscardDocument;
642            let _ = global_scope.script_to_constellation_chan().send(msg);
643        }
644    }
645
646    pub(crate) fn track_websocket(&self, websocket: &WebSocket) {
647        self.websockets.track(websocket);
648    }
649
650    fn close_outstanding_websockets(&self) -> bool {
651        let mut closed_any_websocket = false;
652        self.websockets.for_each(|websocket: DomRoot<WebSocket>| {
653            if websocket.make_disappear() {
654                closed_any_websocket = true;
655            }
656        });
657        closed_any_websocket
658    }
659
660    pub(crate) fn note_node_with_dirty_descendants(&self, node: &Node) {
661        debug_assert!(*node.owner_doc() == *self);
662        if !node.is_connected() {
663            return;
664        }
665
666        let parent = match node.parent_in_flat_tree() {
667            Some(parent) => parent,
668            None => {
669                // There is no parent so this is the Document node, so we
670                // behave as if we were called with the document element.
671                let document_element = match self.GetDocumentElement() {
672                    Some(element) => element,
673                    None => return,
674                };
675                if let Some(dirty_root) = self.dirty_root.get() {
676                    // There was an existing dirty root so we mark its
677                    // ancestors as dirty until the document element.
678                    for ancestor in dirty_root
679                        .upcast::<Node>()
680                        .inclusive_ancestors_in_flat_tree()
681                    {
682                        if ancestor.is::<Element>() {
683                            ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
684                        }
685                    }
686                }
687                self.dirty_root.set(Some(&document_element));
688                return;
689            },
690        };
691
692        if parent.is::<Element>() {
693            if !parent.is_styled() {
694                return;
695            }
696
697            if parent.is_display_none() {
698                return;
699            }
700        }
701
702        let element_parent: DomRoot<Element>;
703        let element = match node.downcast::<Element>() {
704            Some(element) => element,
705            None => {
706                // Current node is not an element, it's probably a text node,
707                // we try to get its element parent.
708                match DomRoot::downcast::<Element>(parent) {
709                    Some(parent) => {
710                        element_parent = parent;
711                        &element_parent
712                    },
713                    None => {
714                        // Parent is not an element so it must be a document,
715                        // and this is not an element either, so there is
716                        // nothing to do.
717                        return;
718                    },
719                }
720            },
721        };
722
723        let dirty_root = match self.dirty_root.get() {
724            None => {
725                element
726                    .upcast::<Node>()
727                    .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
728                self.dirty_root.set(Some(element));
729                return;
730            },
731            Some(root) => root,
732        };
733
734        for ancestor in element.upcast::<Node>().inclusive_ancestors_in_flat_tree() {
735            if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
736                return;
737            }
738
739            if ancestor.is::<Element>() {
740                ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
741            }
742        }
743
744        let new_dirty_root = element
745            .upcast::<Node>()
746            .common_ancestor_in_flat_tree(dirty_root.upcast())
747            .expect("Couldn't find common ancestor");
748
749        let mut has_dirty_descendants = true;
750        for ancestor in dirty_root
751            .upcast::<Node>()
752            .inclusive_ancestors_in_flat_tree()
753        {
754            ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, has_dirty_descendants);
755            has_dirty_descendants &= *ancestor != *new_dirty_root;
756        }
757
758        self.dirty_root
759            .set(Some(new_dirty_root.downcast::<Element>().unwrap()));
760    }
761
762    pub(crate) fn take_dirty_root(&self) -> Option<DomRoot<Element>> {
763        self.dirty_root.take()
764    }
765
766    #[inline]
767    pub(crate) fn loader(&self) -> Ref<'_, DocumentLoader> {
768        self.loader.borrow()
769    }
770
771    #[inline]
772    pub(crate) fn loader_mut(&self) -> RefMut<'_, DocumentLoader> {
773        self.loader.borrow_mut()
774    }
775
776    #[inline]
777    pub(crate) fn has_browsing_context(&self) -> bool {
778        self.has_browsing_context
779    }
780
781    /// <https://html.spec.whatwg.org/multipage/#concept-document-bc>
782    #[inline]
783    pub(crate) fn browsing_context(&self) -> Option<DomRoot<WindowProxy>> {
784        if self.has_browsing_context {
785            self.window.undiscarded_window_proxy()
786        } else {
787            None
788        }
789    }
790
791    pub(crate) fn webview_id(&self) -> WebViewId {
792        self.window.webview_id()
793    }
794
795    #[inline]
796    pub(crate) fn window(&self) -> &Window {
797        &self.window
798    }
799
800    #[inline]
801    pub(crate) fn is_html_document(&self) -> bool {
802        self.is_html_document
803    }
804
805    pub(crate) fn is_xhtml_document(&self) -> bool {
806        self.content_type.matches(APPLICATION, "xhtml+xml")
807    }
808
809    pub(crate) fn set_https_state(&self, https_state: HttpsState) {
810        self.https_state.set(https_state);
811    }
812
813    pub(crate) fn is_fully_active(&self) -> bool {
814        self.activity.get() == DocumentActivity::FullyActive
815    }
816
817    pub(crate) fn is_active(&self) -> bool {
818        self.activity.get() != DocumentActivity::Inactive
819    }
820
821    #[inline]
822    pub(crate) fn current_rendering_epoch(&self) -> Epoch {
823        self.current_rendering_epoch.get()
824    }
825
826    pub(crate) fn set_activity(&self, activity: DocumentActivity, can_gc: CanGc) {
827        // This function should only be called on documents with a browsing context
828        assert!(self.has_browsing_context);
829        if activity == self.activity.get() {
830            return;
831        }
832
833        // Set the document's activity level, reflow if necessary, and suspend or resume timers.
834        self.activity.set(activity);
835        let media = ServoMedia::get();
836        let pipeline_id = self.window().pipeline_id();
837        let client_context_id =
838            ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
839
840        if activity != DocumentActivity::FullyActive {
841            self.window().suspend(can_gc);
842            media.suspend(&client_context_id);
843            return;
844        }
845
846        self.title_changed();
847        self.notify_embedder_favicon();
848        self.dirty_all_nodes();
849        self.window().resume(can_gc);
850        media.resume(&client_context_id);
851
852        if self.ready_state.get() != DocumentReadyState::Complete {
853            return;
854        }
855
856        // This step used to be Step 4.6 in html.spec.whatwg.org/multipage/#history-traversal
857        // But it's now Step 4 in https://html.spec.whatwg.org/multipage/#reactivate-a-document
858        // TODO: See #32687 for more information.
859        let document = Trusted::new(self);
860        self.owner_global()
861            .task_manager()
862            .dom_manipulation_task_source()
863            .queue(task!(fire_pageshow_event: move || {
864                let document = document.root();
865                let window = document.window();
866                // Step 4.6.1
867                if document.page_showing.get() {
868                    return;
869                }
870                // Step 4.6.2 Set document's page showing flag to true.
871                document.page_showing.set(true);
872                // Step 4.6.3 Update the visibility state of document to "visible".
873                document.update_visibility_state(DocumentVisibilityState::Visible, CanGc::note());
874                // Step 4.6.4 Fire a page transition event named pageshow at document's relevant
875                // global object with true.
876                let event = PageTransitionEvent::new(
877                    window,
878                    atom!("pageshow"),
879                    false, // bubbles
880                    false, // cancelable
881                    true, // persisted
882                    CanGc::note(),
883                );
884                let event = event.upcast::<Event>();
885                event.set_trusted(true);
886                window.dispatch_event_with_target_override(event, CanGc::note());
887            }))
888    }
889
890    pub(crate) fn origin(&self) -> &MutableOrigin {
891        &self.origin
892    }
893
894    /// <https://dom.spec.whatwg.org/#concept-document-url>
895    pub(crate) fn url(&self) -> ServoUrl {
896        self.url.borrow().clone()
897    }
898
899    pub(crate) fn set_url(&self, url: ServoUrl) {
900        *self.url.borrow_mut() = url;
901    }
902
903    /// <https://html.spec.whatwg.org/multipage/#fallback-base-url>
904    pub(crate) fn fallback_base_url(&self) -> ServoUrl {
905        // Step 1: If document is an iframe srcdoc document:
906        let document_url = self.url();
907        if document_url.as_str() == "about:srcdoc" {
908            let base_url = self
909                .browsing_context()
910                .and_then(|browsing_context| browsing_context.creator_base_url());
911
912            // Step 1.1: Assert: document's about base URL is non-null.
913            if base_url.is_none() {
914                error!("about:srcdoc page should always have a creator base URL");
915            }
916
917            // Step 1.2: Return document's about base URL.
918            return base_url.unwrap_or(document_url);
919        }
920
921        // Step 2: If document's URL matches about:blank and document's about base URL is
922        // non-null, then return document's about base URL.
923        if document_url.matches_about_blank() {
924            return self
925                .browsing_context()
926                .and_then(|browsing_context| browsing_context.creator_base_url())
927                .unwrap_or(document_url);
928        }
929
930        // Step 3: Return document's URL.
931        document_url
932    }
933
934    /// <https://html.spec.whatwg.org/multipage/#document-base-url>
935    pub(crate) fn base_url(&self) -> ServoUrl {
936        match self.base_element() {
937            // Step 1.
938            None => self.fallback_base_url(),
939            // Step 2.
940            Some(base) => base.frozen_base_url(),
941        }
942    }
943
944    pub(crate) fn add_restyle_reason(&self, reason: RestyleReason) {
945        self.needs_restyle.set(self.needs_restyle.get() | reason)
946    }
947
948    pub(crate) fn clear_restyle_reasons(&self) {
949        self.needs_restyle.set(RestyleReason::empty());
950    }
951
952    pub(crate) fn restyle_reason(&self) -> RestyleReason {
953        let mut condition = self.needs_restyle.get();
954        if self.stylesheets.borrow().has_changed() {
955            condition.insert(RestyleReason::StylesheetsChanged);
956        }
957
958        // FIXME: This should check the dirty bit on the document,
959        // not the document element. Needs some layout changes to make
960        // that workable.
961        if let Some(root) = self.GetDocumentElement() {
962            if root.upcast::<Node>().has_dirty_descendants() {
963                condition.insert(RestyleReason::DOMChanged);
964            }
965        }
966
967        if !self.pending_restyles.borrow().is_empty() {
968            condition.insert(RestyleReason::PendingRestyles);
969        }
970
971        condition
972    }
973
974    /// Returns the first `base` element in the DOM that has an `href` attribute.
975    pub(crate) fn base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
976        self.base_element.get()
977    }
978
979    /// Refresh the cached first base element in the DOM.
980    /// <https://github.com/w3c/web-platform-tests/issues/2122>
981    pub(crate) fn refresh_base_element(&self) {
982        let base = self
983            .upcast::<Node>()
984            .traverse_preorder(ShadowIncluding::No)
985            .filter_map(DomRoot::downcast::<HTMLBaseElement>)
986            .find(|element| {
987                element
988                    .upcast::<Element>()
989                    .has_attribute(&local_name!("href"))
990            });
991        self.base_element.set(base.as_deref());
992    }
993
994    pub(crate) fn dom_count(&self) -> u32 {
995        self.dom_count.get()
996    }
997
998    /// This is called by `bind_to_tree` when a node is added to the DOM.
999    /// The internal count is used by layout to determine whether to be sequential or parallel.
1000    /// (it's sequential for small DOMs)
1001    pub(crate) fn increment_dom_count(&self) {
1002        self.dom_count.set(self.dom_count.get() + 1);
1003    }
1004
1005    /// This is called by `unbind_from_tree` when a node is removed from the DOM.
1006    pub(crate) fn decrement_dom_count(&self) {
1007        self.dom_count.set(self.dom_count.get() - 1);
1008    }
1009
1010    pub(crate) fn quirks_mode(&self) -> QuirksMode {
1011        self.quirks_mode.get()
1012    }
1013
1014    pub(crate) fn set_quirks_mode(&self, new_mode: QuirksMode) {
1015        let old_mode = self.quirks_mode.replace(new_mode);
1016
1017        if old_mode != new_mode {
1018            self.window.layout_mut().set_quirks_mode(new_mode);
1019        }
1020    }
1021
1022    pub(crate) fn encoding(&self) -> &'static Encoding {
1023        self.encoding.get()
1024    }
1025
1026    pub(crate) fn set_encoding(&self, encoding: &'static Encoding) {
1027        self.encoding.set(encoding);
1028    }
1029
1030    pub(crate) fn content_and_heritage_changed(&self, node: &Node) {
1031        if node.is_connected() {
1032            node.note_dirty_descendants();
1033        }
1034
1035        // FIXME(emilio): This is very inefficient, ideally the flag above would
1036        // be enough and incremental layout could figure out from there.
1037        node.dirty(NodeDamage::ContentOrHeritage);
1038    }
1039
1040    /// Remove any existing association between the provided id and any elements in this document.
1041    pub(crate) fn unregister_element_id(&self, to_unregister: &Element, id: Atom, can_gc: CanGc) {
1042        self.document_or_shadow_root
1043            .unregister_named_element(&self.id_map, to_unregister, &id);
1044        self.reset_form_owner_for_listeners(&id, can_gc);
1045    }
1046
1047    /// Associate an element present in this document with the provided id.
1048    pub(crate) fn register_element_id(&self, element: &Element, id: Atom, can_gc: CanGc) {
1049        let root = self.GetDocumentElement().expect(
1050            "The element is in the document, so there must be a document \
1051             element.",
1052        );
1053        self.document_or_shadow_root.register_named_element(
1054            &self.id_map,
1055            element,
1056            &id,
1057            DomRoot::from_ref(root.upcast::<Node>()),
1058        );
1059        self.reset_form_owner_for_listeners(&id, can_gc);
1060    }
1061
1062    /// Remove any existing association between the provided name and any elements in this document.
1063    pub(crate) fn unregister_element_name(&self, to_unregister: &Element, name: Atom) {
1064        self.document_or_shadow_root
1065            .unregister_named_element(&self.name_map, to_unregister, &name);
1066    }
1067
1068    /// Associate an element present in this document with the provided name.
1069    pub(crate) fn register_element_name(&self, element: &Element, name: Atom) {
1070        let root = self.GetDocumentElement().expect(
1071            "The element is in the document, so there must be a document \
1072             element.",
1073        );
1074        self.document_or_shadow_root.register_named_element(
1075            &self.name_map,
1076            element,
1077            &name,
1078            DomRoot::from_ref(root.upcast::<Node>()),
1079        );
1080    }
1081
1082    pub(crate) fn register_form_id_listener<T: ?Sized + FormControl>(
1083        &self,
1084        id: DOMString,
1085        listener: &T,
1086    ) {
1087        let mut map = self.form_id_listener_map.borrow_mut();
1088        let listener = listener.to_element();
1089        let set = map.entry(Atom::from(id)).or_default();
1090        set.insert(Dom::from_ref(listener));
1091    }
1092
1093    pub(crate) fn unregister_form_id_listener<T: ?Sized + FormControl>(
1094        &self,
1095        id: DOMString,
1096        listener: &T,
1097    ) {
1098        let mut map = self.form_id_listener_map.borrow_mut();
1099        if let Occupied(mut entry) = map.entry(Atom::from(id)) {
1100            entry
1101                .get_mut()
1102                .remove(&Dom::from_ref(listener.to_element()));
1103            if entry.get().is_empty() {
1104                entry.remove();
1105            }
1106        }
1107    }
1108
1109    /// Attempt to find a named element in this page's document.
1110    /// <https://html.spec.whatwg.org/multipage/#the-indicated-part-of-the-document>
1111    pub(crate) fn find_fragment_node(&self, fragid: &str) -> Option<DomRoot<Element>> {
1112        // Step 1 is not handled here; the fragid is already obtained by the calling function
1113        // Step 2: Simply use None to indicate the top of the document.
1114        // Step 3 & 4
1115        percent_decode(fragid.as_bytes())
1116            .decode_utf8()
1117            .ok()
1118            // Step 5
1119            .and_then(|decoded_fragid| self.get_element_by_id(&Atom::from(decoded_fragid)))
1120            // Step 6
1121            .or_else(|| self.get_anchor_by_name(fragid))
1122        // Step 7 & 8
1123    }
1124
1125    /// Scroll to the target element, and when we do not find a target
1126    /// and the fragment is empty or "top", scroll to the top.
1127    /// <https://html.spec.whatwg.org/multipage/#scroll-to-the-fragment-identifier>
1128    pub(crate) fn check_and_scroll_fragment(&self, fragment: &str) {
1129        let target = self.find_fragment_node(fragment);
1130
1131        // Step 1
1132        self.set_target_element(target.as_deref());
1133
1134        let point = target
1135            .as_ref()
1136            .map(|element| {
1137                // TODO: This strategy is completely wrong if the element we are scrolling to in
1138                // inside other scrollable containers. Ideally this should use an implementation of
1139                // `scrollIntoView` when that is available:
1140                // See https://github.com/servo/servo/issues/24059.
1141                let rect = element.upcast::<Node>().border_box().unwrap_or_default();
1142
1143                // In order to align with element edges, we snap to unscaled pixel boundaries, since
1144                // the paint thread currently does the same for drawing elements. This is important
1145                // for pages that require pixel perfect scroll positioning for proper display
1146                // (like Acid2).
1147                let device_pixel_ratio = self.window.device_pixel_ratio().get();
1148                (
1149                    rect.origin.x.to_nearest_pixel(device_pixel_ratio),
1150                    rect.origin.y.to_nearest_pixel(device_pixel_ratio),
1151                )
1152            })
1153            .or_else(|| {
1154                if fragment.is_empty() || fragment.eq_ignore_ascii_case("top") {
1155                    // FIXME(stshine): this should be the origin of the stacking context space,
1156                    // which may differ under the influence of writing mode.
1157                    Some((0.0, 0.0))
1158                } else {
1159                    None
1160                }
1161            });
1162
1163        if let Some((x, y)) = point {
1164            self.window.scroll(x, y, ScrollBehavior::Instant)
1165        }
1166    }
1167
1168    fn get_anchor_by_name(&self, name: &str) -> Option<DomRoot<Element>> {
1169        let name = Atom::from(name);
1170        self.name_map.borrow().get(&name).and_then(|elements| {
1171            elements
1172                .iter()
1173                .find(|e| e.is::<HTMLAnchorElement>())
1174                .map(|e| DomRoot::from_ref(&**e))
1175        })
1176    }
1177
1178    // https://html.spec.whatwg.org/multipage/#current-document-readiness
1179    pub(crate) fn set_ready_state(&self, state: DocumentReadyState, can_gc: CanGc) {
1180        match state {
1181            DocumentReadyState::Loading => {
1182                if self.window().is_top_level() {
1183                    self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1184                        self.webview_id(),
1185                        LoadStatus::Started,
1186                    ));
1187                    self.send_to_embedder(EmbedderMsg::Status(self.webview_id(), None));
1188                }
1189            },
1190            DocumentReadyState::Complete => {
1191                if self.window().is_top_level() {
1192                    self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1193                        self.webview_id(),
1194                        LoadStatus::Complete,
1195                    ));
1196                }
1197                update_with_current_instant(&self.dom_complete);
1198            },
1199            DocumentReadyState::Interactive => update_with_current_instant(&self.dom_interactive),
1200        };
1201
1202        self.ready_state.set(state);
1203
1204        self.upcast::<EventTarget>()
1205            .fire_event(atom!("readystatechange"), can_gc);
1206    }
1207
1208    /// Return whether scripting is enabled or not
1209    /// <https://html.spec.whatwg.org/multipage/#concept-n-script>
1210    pub(crate) fn scripting_enabled(&self) -> bool {
1211        // Scripting is enabled for a node node if node's node document's browsing context is non-null,
1212        // and scripting is enabled for node's relevant settings object.
1213        self.has_browsing_context() &&
1214        // Either settings's global object is not a Window object,
1215        // or settings's global object's associated Document's active sandboxing flag
1216        // set does not have its sandboxed scripts browsing context flag set.
1217            !self.has_active_sandboxing_flag(
1218                SandboxingFlagSet::SANDBOXED_SCRIPTS_BROWSING_CONTEXT_FLAG,
1219            )
1220    }
1221
1222    /// Return the element that currently has focus.
1223    // https://w3c.github.io/uievents/#events-focusevent-doc-focus
1224    pub(crate) fn get_focused_element(&self) -> Option<DomRoot<Element>> {
1225        self.focused.get()
1226    }
1227
1228    /// Get the last sequence number sent to the constellation.
1229    ///
1230    /// Received focus-related messages with sequence numbers less than the one
1231    /// returned by this method must be discarded.
1232    pub fn get_focus_sequence(&self) -> FocusSequenceNumber {
1233        self.focus_sequence.get()
1234    }
1235
1236    /// Generate the next sequence number for focus-related messages.
1237    fn increment_fetch_focus_sequence(&self) -> FocusSequenceNumber {
1238        self.focus_sequence.set(FocusSequenceNumber(
1239            self.focus_sequence
1240                .get()
1241                .0
1242                .checked_add(1)
1243                .expect("too many focus messages have been sent"),
1244        ));
1245        self.focus_sequence.get()
1246    }
1247
1248    pub(crate) fn has_focus_transaction(&self) -> bool {
1249        self.focus_transaction.borrow().is_some()
1250    }
1251
1252    /// Initiate a new round of checking for elements requesting focus. The last element to call
1253    /// `request_focus` before `commit_focus_transaction` is called will receive focus.
1254    pub(crate) fn begin_focus_transaction(&self) {
1255        // Initialize it with the current state
1256        *self.focus_transaction.borrow_mut() = Some(FocusTransaction {
1257            element: self.focused.get().as_deref().map(Dom::from_ref),
1258            has_focus: self.has_focus.get(),
1259            focus_options: FocusOptions {
1260                preventScroll: true,
1261            },
1262        });
1263    }
1264
1265    /// <https://html.spec.whatwg.org/multipage/#focus-fixup-rule>
1266    pub(crate) fn perform_focus_fixup_rule(&self, not_focusable: &Element, can_gc: CanGc) {
1267        // Return if `not_focusable` is not the designated focused area of the
1268        // `Document`.
1269        if Some(not_focusable) != self.focused.get().as_deref() {
1270            return;
1271        }
1272
1273        let implicit_transaction = self.focus_transaction.borrow().is_none();
1274
1275        if implicit_transaction {
1276            self.begin_focus_transaction();
1277        }
1278
1279        // Designate the viewport as the new focused area of the `Document`, but
1280        // do not run the focusing steps.
1281        {
1282            let mut focus_transaction = self.focus_transaction.borrow_mut();
1283            focus_transaction.as_mut().unwrap().element = None;
1284        }
1285
1286        if implicit_transaction {
1287            self.commit_focus_transaction(FocusInitiator::Local, can_gc);
1288        }
1289    }
1290
1291    /// Request that the given element receive focus with default options.
1292    /// See [`Self::request_focus_with_options`] for the details.
1293    pub(crate) fn request_focus(
1294        &self,
1295        elem: Option<&Element>,
1296        focus_initiator: FocusInitiator,
1297        can_gc: CanGc,
1298    ) {
1299        self.request_focus_with_options(
1300            elem,
1301            focus_initiator,
1302            FocusOptions {
1303                preventScroll: true,
1304            },
1305            can_gc,
1306        );
1307    }
1308
1309    /// Request that the given element receive focus once the current
1310    /// transaction is complete. `None` specifies to focus the document.
1311    ///
1312    /// If there's no ongoing transaction, this method automatically starts and
1313    /// commits an implicit transaction.
1314    pub(crate) fn request_focus_with_options(
1315        &self,
1316        elem: Option<&Element>,
1317        focus_initiator: FocusInitiator,
1318        focus_options: FocusOptions,
1319        can_gc: CanGc,
1320    ) {
1321        // If an element is specified, and it's non-focusable, ignore the
1322        // request.
1323        if elem.is_some_and(|e| !e.is_focusable_area()) {
1324            return;
1325        }
1326
1327        let implicit_transaction = self.focus_transaction.borrow().is_none();
1328
1329        if implicit_transaction {
1330            self.begin_focus_transaction();
1331        }
1332
1333        {
1334            let mut focus_transaction = self.focus_transaction.borrow_mut();
1335            let focus_transaction = focus_transaction.as_mut().unwrap();
1336            focus_transaction.element = elem.map(Dom::from_ref);
1337            focus_transaction.has_focus = true;
1338            focus_transaction.focus_options = focus_options;
1339        }
1340
1341        if implicit_transaction {
1342            self.commit_focus_transaction(focus_initiator, can_gc);
1343        }
1344    }
1345
1346    /// Update the local focus state accordingly after being notified that the
1347    /// document's container is removed from the top-level browsing context's
1348    /// focus chain (not considering system focus).
1349    pub(crate) fn handle_container_unfocus(&self, can_gc: CanGc) {
1350        if self.window().parent_info().is_none() {
1351            warn!("Top-level document cannot be unfocused");
1352            return;
1353        }
1354
1355        // Since this method is called from an event loop, there mustn't be
1356        // an in-progress focus transaction
1357        assert!(
1358            self.focus_transaction.borrow().is_none(),
1359            "there mustn't be an in-progress focus transaction at this point"
1360        );
1361
1362        // Start an implicit focus transaction
1363        self.begin_focus_transaction();
1364
1365        // Update the transaction
1366        {
1367            let mut focus_transaction = self.focus_transaction.borrow_mut();
1368            focus_transaction.as_mut().unwrap().has_focus = false;
1369        }
1370
1371        // Commit the implicit focus transaction
1372        self.commit_focus_transaction(FocusInitiator::Remote, can_gc);
1373    }
1374
1375    /// Reassign the focus context to the element that last requested focus during this
1376    /// transaction, or the document if no elements requested it.
1377    pub(crate) fn commit_focus_transaction(&self, focus_initiator: FocusInitiator, can_gc: CanGc) {
1378        let (mut new_focused, new_focus_state, prevent_scroll) = {
1379            let focus_transaction = self.focus_transaction.borrow();
1380            let focus_transaction = focus_transaction
1381                .as_ref()
1382                .expect("no focus transaction in progress");
1383            (
1384                focus_transaction
1385                    .element
1386                    .as_ref()
1387                    .map(|e| DomRoot::from_ref(&**e)),
1388                focus_transaction.has_focus,
1389                focus_transaction.focus_options.preventScroll,
1390            )
1391        };
1392        *self.focus_transaction.borrow_mut() = None;
1393
1394        if !new_focus_state {
1395            // In many browsers, a document forgets its focused area when the
1396            // document is removed from the top-level BC's focus chain
1397            if new_focused.take().is_some() {
1398                trace!(
1399                    "Forgetting the document's focused area because the \
1400                    document's container was removed from the top-level BC's \
1401                    focus chain"
1402                );
1403            }
1404        }
1405
1406        let old_focused = self.focused.get();
1407        let old_focus_state = self.has_focus.get();
1408
1409        debug!(
1410            "Committing focus transaction: {:?} → {:?}",
1411            (&old_focused, old_focus_state),
1412            (&new_focused, new_focus_state),
1413        );
1414
1415        // `*_focused_filtered` indicates the local element (if any) included in
1416        // the top-level BC's focus chain.
1417        let old_focused_filtered = old_focused.as_ref().filter(|_| old_focus_state);
1418        let new_focused_filtered = new_focused.as_ref().filter(|_| new_focus_state);
1419
1420        let trace_focus_chain = |name, element, doc| {
1421            trace!(
1422                "{} local focus chain: {}",
1423                name,
1424                match (element, doc) {
1425                    (Some(e), _) => format!("[{:?}, document]", e),
1426                    (None, true) => "[document]".to_owned(),
1427                    (None, false) => "[]".to_owned(),
1428                }
1429            );
1430        };
1431
1432        trace_focus_chain("Old", old_focused_filtered, old_focus_state);
1433        trace_focus_chain("New", new_focused_filtered, new_focus_state);
1434
1435        if old_focused_filtered != new_focused_filtered {
1436            if let Some(elem) = &old_focused_filtered {
1437                let node = elem.upcast::<Node>();
1438                elem.set_focus_state(false);
1439                // FIXME: pass appropriate relatedTarget
1440                if node.is_connected() {
1441                    self.fire_focus_event(FocusEventType::Blur, node.upcast(), None, can_gc);
1442                }
1443            }
1444        }
1445
1446        if old_focus_state != new_focus_state && !new_focus_state {
1447            self.fire_focus_event(FocusEventType::Blur, self.global().upcast(), None, can_gc);
1448        }
1449
1450        self.focused.set(new_focused.as_deref());
1451        self.has_focus.set(new_focus_state);
1452
1453        if old_focus_state != new_focus_state && new_focus_state {
1454            self.fire_focus_event(FocusEventType::Focus, self.global().upcast(), None, can_gc);
1455        }
1456
1457        if old_focused_filtered != new_focused_filtered {
1458            if let Some(elem) = &new_focused_filtered {
1459                elem.set_focus_state(true);
1460                let node = elem.upcast::<Node>();
1461                // FIXME: pass appropriate relatedTarget
1462                self.fire_focus_event(FocusEventType::Focus, node.upcast(), None, can_gc);
1463
1464                // Scroll operation to happen after element gets focus.
1465                // This is needed to ensure that the focused element is visible.
1466                // Only scroll if preventScroll was not specified
1467                if !prevent_scroll {
1468                    elem.ScrollIntoView(BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(
1469                        ScrollIntoViewOptions {
1470                            parent: ScrollOptions {
1471                                behavior: ScrollBehavior::Smooth,
1472                            },
1473                            block: ScrollLogicalPosition::Center,
1474                            inline: ScrollLogicalPosition::Center,
1475                            container: ScrollIntoViewContainer::All,
1476                        },
1477                    ));
1478                }
1479            }
1480        }
1481
1482        if focus_initiator != FocusInitiator::Local {
1483            return;
1484        }
1485
1486        // We are the initiator of the focus operation, so we must broadcast
1487        // the change we intend to make.
1488        match (old_focus_state, new_focus_state) {
1489            (_, true) => {
1490                // Advertise the change in the focus chain.
1491                // <https://html.spec.whatwg.org/multipage/#focus-chain>
1492                // <https://html.spec.whatwg.org/multipage/#focusing-steps>
1493                //
1494                // If the top-level BC doesn't have system focus, this won't
1495                // have an immediate effect, but it will when we gain system
1496                // focus again. Therefore we still have to send `ScriptMsg::
1497                // Focus`.
1498                //
1499                // When a container with a non-null nested browsing context is
1500                // focused, its active document becomes the focused area of the
1501                // top-level browsing context instead. Therefore we need to let
1502                // the constellation know if such a container is focused.
1503                //
1504                // > The focusing steps for an object `new focus target` [...]
1505                // >
1506                // >  3. If `new focus target` is a browsing context container
1507                // >     with non-null nested browsing context, then set
1508                // >     `new focus target` to the nested browsing context's
1509                // >     active document.
1510                let child_browsing_context_id = new_focused
1511                    .as_ref()
1512                    .and_then(|elem| elem.downcast::<HTMLIFrameElement>())
1513                    .and_then(|iframe| iframe.browsing_context_id());
1514
1515                let sequence = self.increment_fetch_focus_sequence();
1516
1517                debug!(
1518                    "Advertising the focus request to the constellation \
1519                        with sequence number {} and child BC ID {}",
1520                    sequence,
1521                    child_browsing_context_id
1522                        .as_ref()
1523                        .map(|id| id as &dyn std::fmt::Display)
1524                        .unwrap_or(&"(none)"),
1525                );
1526
1527                self.window()
1528                    .send_to_constellation(ScriptToConstellationMessage::Focus(
1529                        child_browsing_context_id,
1530                        sequence,
1531                    ));
1532            },
1533            (false, false) => {
1534                // Our `Document` doesn't have focus, and we intend to keep it
1535                // this way.
1536            },
1537            (true, false) => {
1538                unreachable!(
1539                    "Can't lose the document's focus without specifying \
1540                    another one to focus"
1541                );
1542            },
1543        }
1544    }
1545
1546    /// Handles any updates when the document's title has changed.
1547    pub(crate) fn title_changed(&self) {
1548        if self.browsing_context().is_some() {
1549            self.send_title_to_embedder();
1550            let title = String::from(self.Title());
1551            self.window
1552                .send_to_constellation(ScriptToConstellationMessage::TitleChanged(
1553                    self.window.pipeline_id(),
1554                    title.clone(),
1555                ));
1556            if let Some(chan) = self.window.as_global_scope().devtools_chan() {
1557                let _ = chan.send(ScriptToDevtoolsControlMsg::TitleChanged(
1558                    self.window.pipeline_id(),
1559                    title,
1560                ));
1561            }
1562        }
1563    }
1564
1565    /// Determine the title of the [`Document`] according to the specification at:
1566    /// <https://html.spec.whatwg.org/multipage/#document.title>. The difference
1567    /// here is that when the title isn't specified `None` is returned.
1568    fn title(&self) -> Option<DOMString> {
1569        let title = self.GetDocumentElement().and_then(|root| {
1570            if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
1571                // Step 1.
1572                root.upcast::<Node>()
1573                    .child_elements()
1574                    .find(|node| {
1575                        node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
1576                    })
1577                    .map(DomRoot::upcast::<Node>)
1578            } else {
1579                // Step 2.
1580                root.upcast::<Node>()
1581                    .traverse_preorder(ShadowIncluding::No)
1582                    .find(|node| node.is::<HTMLTitleElement>())
1583            }
1584        });
1585
1586        title.map(|title| {
1587            // Steps 3-4.
1588            let value = title.child_text_content();
1589            DOMString::from(str_join(value.str().split_html_space_characters(), " "))
1590        })
1591    }
1592
1593    /// Sends this document's title to the constellation.
1594    pub(crate) fn send_title_to_embedder(&self) {
1595        let window = self.window();
1596        if window.is_top_level() {
1597            let title = self.title().map(String::from);
1598            self.send_to_embedder(EmbedderMsg::ChangePageTitle(self.webview_id(), title));
1599        }
1600    }
1601
1602    pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
1603        let window = self.window();
1604        window.send_to_embedder(msg);
1605    }
1606
1607    pub(crate) fn dirty_all_nodes(&self) {
1608        let root = match self.GetDocumentElement() {
1609            Some(root) => root,
1610            None => return,
1611        };
1612        for node in root
1613            .upcast::<Node>()
1614            .traverse_preorder(ShadowIncluding::Yes)
1615        {
1616            node.dirty(NodeDamage::Other)
1617        }
1618    }
1619
1620    /// <https://drafts.csswg.org/cssom-view/#document-run-the-scroll-steps>
1621    pub(crate) fn run_the_scroll_steps(&self, can_gc: CanGc) {
1622        // Step 1.
1623        // > Run the steps to dispatch pending scrollsnapchanging events for doc.
1624        // TODO(#7673): Implement scroll snapping
1625
1626        // Step 2
1627        // > For each item target in doc’s pending scroll event targets, in the order they
1628        // > were added to the list, run these substeps:
1629        // Step 3.
1630        // > Empty doc’s pending scroll event targets.
1631        // Since the scroll event callback could trigger another scroll event, we are taking all of the
1632        // current scroll event to avoid borrow checking error.
1633        rooted_vec!(let notify_list <- self.pending_scroll_event_targets.take().into_iter());
1634        for target in notify_list.iter() {
1635            if target.downcast::<Document>().is_some() {
1636                // Step 2.1
1637                // > If target is a Document, fire an event named scroll that bubbles at target.
1638                target.fire_bubbling_event(Atom::from("scroll"), can_gc);
1639            } else if target.downcast::<Element>().is_some() {
1640                // Step 2.2
1641                // > Otherwise, fire an event named scroll at target.
1642                target.fire_event(Atom::from("scroll"), can_gc);
1643            }
1644        }
1645
1646        // Step 4.
1647        // > Run the steps to dispatch pending scrollsnapchange events for doc.
1648        // TODO(#7673): Implement scroll snapping
1649    }
1650
1651    /// Whenever a viewport gets scrolled (whether in response to user interaction or by an
1652    /// API), the user agent must run these steps:
1653    /// <https://drafts.csswg.org/cssom-view/#scrolling-events>
1654    pub(crate) fn handle_viewport_scroll_event(&self) {
1655        // Step 2.
1656        // > If doc is a snap container, run the steps to update scrollsnapchanging targets
1657        // > for doc with doc’s eventual snap target in the block axis as newBlockTarget and
1658        // > doc’s eventual snap target in the inline axis as newInlineTarget.
1659        // TODO(#7673): Implement scroll snapping
1660
1661        // Step 3.
1662        // > If doc is already in doc’s pending scroll event targets, abort these steps.
1663        let target = self.upcast::<EventTarget>();
1664        if self
1665            .pending_scroll_event_targets
1666            .borrow()
1667            .iter()
1668            .any(|other_target| *other_target == target)
1669        {
1670            return;
1671        }
1672
1673        // Step 4.
1674        // > Append doc to doc’s pending scroll event targets.
1675        self.pending_scroll_event_targets
1676            .borrow_mut()
1677            .push(Dom::from_ref(target));
1678    }
1679
1680    /// Whenever an element gets scrolled (whether in response to user interaction or by an
1681    /// API), the user agent must run these steps:
1682    /// <https://drafts.csswg.org/cssom-view/#scrolling-events>
1683    pub(crate) fn handle_element_scroll_event(&self, element: &Element) {
1684        // Step 2.
1685        // > If the element is a snap container, run the steps to update scrollsnapchanging
1686        // > targets for the element with the element’s eventual snap target in the block
1687        // > axis as newBlockTarget and the element’s eventual snap target in the inline axis
1688        // > as newInlineTarget.
1689        // TODO(#7673): Implement scroll snapping
1690
1691        // Step 3.
1692        // > If the element is already in doc’s pending scroll event targets, abort these steps.
1693        let target = element.upcast::<EventTarget>();
1694        if self
1695            .pending_scroll_event_targets
1696            .borrow()
1697            .iter()
1698            .any(|other_target| *other_target == target)
1699        {
1700            return;
1701        }
1702
1703        // Step 4.
1704        // > Append the element to doc’s pending scroll event targets.
1705        self.pending_scroll_event_targets
1706            .borrow_mut()
1707            .push(Dom::from_ref(target));
1708    }
1709
1710    // https://dom.spec.whatwg.org/#converting-nodes-into-a-node
1711    pub(crate) fn node_from_nodes_and_strings(
1712        &self,
1713        mut nodes: Vec<NodeOrString>,
1714        can_gc: CanGc,
1715    ) -> Fallible<DomRoot<Node>> {
1716        if nodes.len() == 1 {
1717            Ok(match nodes.pop().unwrap() {
1718                NodeOrString::Node(node) => node,
1719                NodeOrString::String(string) => {
1720                    DomRoot::upcast(self.CreateTextNode(string, can_gc))
1721                },
1722            })
1723        } else {
1724            let fragment = DomRoot::upcast::<Node>(self.CreateDocumentFragment(can_gc));
1725            for node in nodes {
1726                match node {
1727                    NodeOrString::Node(node) => {
1728                        fragment.AppendChild(&node, can_gc)?;
1729                    },
1730                    NodeOrString::String(string) => {
1731                        let node = DomRoot::upcast::<Node>(self.CreateTextNode(string, can_gc));
1732                        // No try!() here because appending a text node
1733                        // should not fail.
1734                        fragment.AppendChild(&node, can_gc).unwrap();
1735                    },
1736                }
1737            }
1738            Ok(fragment)
1739        }
1740    }
1741
1742    pub(crate) fn get_body_attribute(&self, local_name: &LocalName) -> DOMString {
1743        match self.GetBody() {
1744            Some(ref body) if body.is_body_element() => {
1745                body.upcast::<Element>().get_string_attribute(local_name)
1746            },
1747            _ => DOMString::new(),
1748        }
1749    }
1750
1751    pub(crate) fn set_body_attribute(
1752        &self,
1753        local_name: &LocalName,
1754        value: DOMString,
1755        can_gc: CanGc,
1756    ) {
1757        if let Some(ref body) = self.GetBody().filter(|elem| elem.is_body_element()) {
1758            let body = body.upcast::<Element>();
1759            let value = body.parse_attribute(&ns!(), local_name, value);
1760            body.set_attribute(local_name, value, can_gc);
1761        }
1762    }
1763
1764    pub(crate) fn set_current_script(&self, script: Option<&HTMLScriptElement>) {
1765        self.current_script.set(script);
1766    }
1767
1768    pub(crate) fn get_script_blocking_stylesheets_count(&self) -> u32 {
1769        self.script_blocking_stylesheets_count.get()
1770    }
1771
1772    pub(crate) fn increment_script_blocking_stylesheet_count(&self) {
1773        let count_cell = &self.script_blocking_stylesheets_count;
1774        count_cell.set(count_cell.get() + 1);
1775    }
1776
1777    pub(crate) fn decrement_script_blocking_stylesheet_count(&self) {
1778        let count_cell = &self.script_blocking_stylesheets_count;
1779        assert!(count_cell.get() > 0);
1780        count_cell.set(count_cell.get() - 1);
1781    }
1782
1783    pub(crate) fn render_blocking_element_count(&self) -> u32 {
1784        self.render_blocking_element_count.get()
1785    }
1786
1787    pub(crate) fn increment_render_blocking_element_count(&self) {
1788        let count_cell = &self.render_blocking_element_count;
1789        count_cell.set(count_cell.get() + 1);
1790    }
1791
1792    pub(crate) fn decrement_render_blocking_element_count(&self) {
1793        let count_cell = &self.render_blocking_element_count;
1794        assert!(count_cell.get() > 0);
1795        count_cell.set(count_cell.get() - 1);
1796    }
1797
1798    pub(crate) fn invalidate_stylesheets(&self) {
1799        self.stylesheets.borrow_mut().force_dirty(OriginSet::all());
1800
1801        // Mark the document element dirty so a reflow will be performed.
1802        //
1803        // FIXME(emilio): Use the DocumentStylesheetSet invalidation stuff.
1804        if let Some(element) = self.GetDocumentElement() {
1805            element.upcast::<Node>().dirty(NodeDamage::Style);
1806        }
1807    }
1808
1809    /// Whether or not this `Document` has any active requestAnimationFrame callbacks
1810    /// registered.
1811    pub(crate) fn has_active_request_animation_frame_callbacks(&self) -> bool {
1812        !self.animation_frame_list.borrow().is_empty()
1813    }
1814
1815    /// <https://html.spec.whatwg.org/multipage/#dom-window-requestanimationframe>
1816    pub(crate) fn request_animation_frame(&self, callback: AnimationFrameCallback) -> u32 {
1817        let ident = self.animation_frame_ident.get() + 1;
1818        self.animation_frame_ident.set(ident);
1819
1820        let had_animation_frame_callbacks;
1821        {
1822            let mut animation_frame_list = self.animation_frame_list.borrow_mut();
1823            had_animation_frame_callbacks = !animation_frame_list.is_empty();
1824            animation_frame_list.push_back((ident, Some(callback)));
1825        }
1826
1827        // No need to send a `ChangeRunningAnimationsState` if we're running animation callbacks:
1828        // we're guaranteed to already be in the "animation callbacks present" state.
1829        //
1830        // This reduces CPU usage by avoiding needless thread wakeups in the common case of
1831        // repeated rAF.
1832        if !self.running_animation_callbacks.get() && !had_animation_frame_callbacks {
1833            self.window().send_to_constellation(
1834                ScriptToConstellationMessage::ChangeRunningAnimationsState(
1835                    AnimationState::AnimationCallbacksPresent,
1836                ),
1837            );
1838        }
1839
1840        ident
1841    }
1842
1843    /// <https://html.spec.whatwg.org/multipage/#dom-window-cancelanimationframe>
1844    pub(crate) fn cancel_animation_frame(&self, ident: u32) {
1845        let mut list = self.animation_frame_list.borrow_mut();
1846        if let Some(pair) = list.iter_mut().find(|pair| pair.0 == ident) {
1847            pair.1 = None;
1848        }
1849    }
1850
1851    /// <https://html.spec.whatwg.org/multipage/#run-the-animation-frame-callbacks>
1852    pub(crate) fn run_the_animation_frame_callbacks(&self, can_gc: CanGc) {
1853        let _realm = enter_realm(self);
1854
1855        self.running_animation_callbacks.set(true);
1856        let timing = self.global().performance().Now();
1857
1858        let num_callbacks = self.animation_frame_list.borrow().len();
1859        for _ in 0..num_callbacks {
1860            let (_, maybe_callback) = self.animation_frame_list.borrow_mut().pop_front().unwrap();
1861            if let Some(callback) = maybe_callback {
1862                callback.call(self, *timing, can_gc);
1863            }
1864        }
1865        self.running_animation_callbacks.set(false);
1866
1867        if self.animation_frame_list.borrow().is_empty() {
1868            self.window().send_to_constellation(
1869                ScriptToConstellationMessage::ChangeRunningAnimationsState(
1870                    AnimationState::NoAnimationCallbacksPresent,
1871                ),
1872            );
1873        }
1874    }
1875
1876    pub(crate) fn policy_container(&self) -> Ref<'_, PolicyContainer> {
1877        self.policy_container.borrow()
1878    }
1879
1880    pub(crate) fn set_policy_container(&self, policy_container: PolicyContainer) {
1881        *self.policy_container.borrow_mut() = policy_container;
1882    }
1883
1884    pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
1885        self.policy_container.borrow_mut().set_csp_list(csp_list);
1886    }
1887
1888    pub(crate) fn get_csp_list(&self) -> Option<CspList> {
1889        self.policy_container.borrow().csp_list.clone()
1890    }
1891
1892    pub(crate) fn preloaded_resources(&self) -> PreloadedResources {
1893        self.preloaded_resources.clone()
1894    }
1895
1896    /// Add the policy container and HTTPS state to a given request.
1897    ///
1898    /// TODO: Can this hapen for all requests that go through the document?
1899    pub(crate) fn prepare_request(&self, request: RequestBuilder) -> RequestBuilder {
1900        request
1901            .policy_container(self.policy_container().to_owned())
1902            .https_state(self.https_state.get())
1903    }
1904
1905    pub(crate) fn fetch<Listener: FetchResponseListener + PreInvoke + Send + 'static>(
1906        &self,
1907        load: LoadType,
1908        mut request: RequestBuilder,
1909        listener: Listener,
1910    ) {
1911        request = request
1912            .insecure_requests_policy(self.insecure_requests_policy())
1913            .has_trustworthy_ancestor_origin(self.has_trustworthy_ancestor_or_current_origin());
1914        let callback = NetworkListener {
1915            context: std::sync::Arc::new(Mutex::new(listener)),
1916            task_source: self
1917                .owner_global()
1918                .task_manager()
1919                .networking_task_source()
1920                .into(),
1921        }
1922        .into_callback();
1923        self.loader_mut()
1924            .fetch_async_with_callback(load, request, callback);
1925    }
1926
1927    pub(crate) fn fetch_background<Listener: FetchResponseListener + PreInvoke + Send + 'static>(
1928        &self,
1929        mut request: RequestBuilder,
1930        listener: Listener,
1931    ) {
1932        request = request
1933            .insecure_requests_policy(self.insecure_requests_policy())
1934            .has_trustworthy_ancestor_origin(self.has_trustworthy_ancestor_or_current_origin());
1935        let callback = NetworkListener {
1936            context: std::sync::Arc::new(Mutex::new(listener)),
1937            task_source: self
1938                .owner_global()
1939                .task_manager()
1940                .networking_task_source()
1941                .into(),
1942        }
1943        .into_callback();
1944        self.loader_mut().fetch_async_background(request, callback);
1945    }
1946
1947    // https://html.spec.whatwg.org/multipage/#the-end
1948    // https://html.spec.whatwg.org/multipage/#delay-the-load-event
1949    pub(crate) fn finish_load(&self, load: LoadType, can_gc: CanGc) {
1950        // This does not delay the load event anymore.
1951        debug!("Document got finish_load: {:?}", load);
1952        self.loader.borrow_mut().finish_load(&load);
1953
1954        match load {
1955            LoadType::Stylesheet(_) => {
1956                // A stylesheet finishing to load may unblock any pending
1957                // parsing-blocking script or deferred script.
1958                self.process_pending_parsing_blocking_script(can_gc);
1959
1960                // Step 3.
1961                self.process_deferred_scripts(can_gc);
1962            },
1963            LoadType::PageSource(_) => {
1964                // We finished loading the page, so if the `Window` is still waiting for
1965                // the first layout, allow it.
1966                if self.has_browsing_context && self.is_fully_active() {
1967                    self.window().allow_layout_if_necessary();
1968                }
1969
1970                // Deferred scripts have to wait for page to finish loading,
1971                // this is the first opportunity to process them.
1972
1973                // Step 3.
1974                self.process_deferred_scripts(can_gc);
1975            },
1976            _ => {},
1977        }
1978
1979        // Step 4 is in another castle, namely at the end of
1980        // process_deferred_scripts.
1981
1982        // Step 5 can be found in asap_script_loaded and
1983        // asap_in_order_script_loaded.
1984
1985        let loader = self.loader.borrow();
1986
1987        // Servo measures when the top-level content (not iframes) is loaded.
1988        if self.top_level_dom_complete.get().is_none() && loader.is_only_blocked_by_iframes() {
1989            update_with_current_instant(&self.top_level_dom_complete);
1990        }
1991
1992        if loader.is_blocked() || loader.events_inhibited() {
1993            // Step 6.
1994            return;
1995        }
1996
1997        ScriptThread::mark_document_with_no_blocked_loads(self);
1998    }
1999
2000    // https://html.spec.whatwg.org/multipage/#prompt-to-unload-a-document
2001    pub(crate) fn prompt_to_unload(&self, recursive_flag: bool, can_gc: CanGc) -> bool {
2002        // TODO: Step 1, increase the event loop's termination nesting level by 1.
2003        // Step 2
2004        self.incr_ignore_opens_during_unload_counter();
2005        // Step 3-5.
2006        let beforeunload_event = BeforeUnloadEvent::new(
2007            &self.window,
2008            atom!("beforeunload"),
2009            EventBubbles::Bubbles,
2010            EventCancelable::Cancelable,
2011            can_gc,
2012        );
2013        let event = beforeunload_event.upcast::<Event>();
2014        event.set_trusted(true);
2015        let event_target = self.window.upcast::<EventTarget>();
2016        let has_listeners = event_target.has_listeners_for(&atom!("beforeunload"));
2017        self.window
2018            .dispatch_event_with_target_override(event, can_gc);
2019        // TODO: Step 6, decrease the event loop's termination nesting level by 1.
2020        // Step 7
2021        if has_listeners {
2022            self.salvageable.set(false);
2023        }
2024        let mut can_unload = true;
2025        // TODO: Step 8, also check sandboxing modals flag.
2026        let default_prevented = event.DefaultPrevented();
2027        let return_value_not_empty = !event
2028            .downcast::<BeforeUnloadEvent>()
2029            .unwrap()
2030            .ReturnValue()
2031            .is_empty();
2032        if default_prevented || return_value_not_empty {
2033            let (chan, port) = generic_channel::channel().expect("Failed to create IPC channel!");
2034            let msg = EmbedderMsg::AllowUnload(self.webview_id(), chan);
2035            self.send_to_embedder(msg);
2036            can_unload = port.recv().unwrap() == AllowOrDeny::Allow;
2037        }
2038        // Step 9
2039        if !recursive_flag {
2040            // `prompt_to_unload` might cause futher modifications to the DOM so collecting here prevents
2041            // a double borrow if the `IFrameCollection` needs to be validated again.
2042            let iframes: Vec<_> = self.iframes().iter().collect();
2043            for iframe in &iframes {
2044                // TODO: handle the case of cross origin iframes.
2045                let document = iframe.owner_document();
2046                can_unload = document.prompt_to_unload(true, can_gc);
2047                if !document.salvageable() {
2048                    self.salvageable.set(false);
2049                }
2050                if !can_unload {
2051                    break;
2052                }
2053            }
2054        }
2055        // Step 10
2056        self.decr_ignore_opens_during_unload_counter();
2057        can_unload
2058    }
2059
2060    // https://html.spec.whatwg.org/multipage/#unload-a-document
2061    pub(crate) fn unload(&self, recursive_flag: bool, can_gc: CanGc) {
2062        // TODO: Step 1, increase the event loop's termination nesting level by 1.
2063        // Step 2
2064        self.incr_ignore_opens_during_unload_counter();
2065        // Step 3-6 If oldDocument's page showing is true:
2066        if self.page_showing.get() {
2067            // Set oldDocument's page showing to false.
2068            self.page_showing.set(false);
2069            // Fire a page transition event named pagehide at oldDocument's relevant global object with oldDocument's
2070            // salvageable state.
2071            let event = PageTransitionEvent::new(
2072                &self.window,
2073                atom!("pagehide"),
2074                false,                  // bubbles
2075                false,                  // cancelable
2076                self.salvageable.get(), // persisted
2077                can_gc,
2078            );
2079            let event = event.upcast::<Event>();
2080            event.set_trusted(true);
2081            self.window
2082                .dispatch_event_with_target_override(event, can_gc);
2083            // Step 6 Update the visibility state of oldDocument to "hidden".
2084            self.update_visibility_state(DocumentVisibilityState::Hidden, can_gc);
2085        }
2086        // Step 7
2087        if !self.fired_unload.get() {
2088            let event = Event::new(
2089                self.window.upcast(),
2090                atom!("unload"),
2091                EventBubbles::Bubbles,
2092                EventCancelable::Cancelable,
2093                can_gc,
2094            );
2095            event.set_trusted(true);
2096            let event_target = self.window.upcast::<EventTarget>();
2097            let has_listeners = event_target.has_listeners_for(&atom!("unload"));
2098            self.window
2099                .dispatch_event_with_target_override(&event, can_gc);
2100            self.fired_unload.set(true);
2101            // Step 9
2102            if has_listeners {
2103                self.salvageable.set(false);
2104            }
2105        }
2106        // TODO: Step 8, decrease the event loop's termination nesting level by 1.
2107
2108        // Step 13
2109        if !recursive_flag {
2110            // `unload` might cause futher modifications to the DOM so collecting here prevents
2111            // a double borrow if the `IFrameCollection` needs to be validated again.
2112            let iframes: Vec<_> = self.iframes().iter().collect();
2113            for iframe in &iframes {
2114                // TODO: handle the case of cross origin iframes.
2115                let document = iframe.owner_document();
2116                document.unload(true, can_gc);
2117                if !document.salvageable() {
2118                    self.salvageable.set(false);
2119                }
2120            }
2121        }
2122
2123        // Step 18. Run any unloading document cleanup steps for oldDocument that are defined by this specification and other applicable specifications.
2124        self.unloading_cleanup_steps();
2125
2126        // https://w3c.github.io/FileAPI/#lifeTime
2127        self.window.as_global_scope().clean_up_all_file_resources();
2128
2129        // Step 15, End
2130        self.decr_ignore_opens_during_unload_counter();
2131    }
2132
2133    // https://html.spec.whatwg.org/multipage/#the-end
2134    pub(crate) fn maybe_queue_document_completion(&self) {
2135        // https://html.spec.whatwg.org/multipage/#delaying-load-events-mode
2136        let is_in_delaying_load_events_mode = match self.window.undiscarded_window_proxy() {
2137            Some(window_proxy) => window_proxy.is_delaying_load_events_mode(),
2138            None => false,
2139        };
2140
2141        // Note: if the document is not fully active, layout will have exited already,
2142        // and this method will panic.
2143        // The underlying problem might actually be that layout exits while it should be kept alive.
2144        // See https://github.com/servo/servo/issues/22507
2145        let not_ready_for_load = self.loader.borrow().is_blocked() ||
2146            !self.is_fully_active() ||
2147            is_in_delaying_load_events_mode;
2148
2149        if not_ready_for_load {
2150            // Step 6.
2151            return;
2152        }
2153
2154        assert!(!self.loader.borrow().events_inhibited());
2155        self.loader.borrow_mut().inhibit_events();
2156
2157        // The rest will ever run only once per document.
2158        // Step 7.
2159        debug!("Document loads are complete.");
2160        let document = Trusted::new(self);
2161        self.owner_global()
2162            .task_manager()
2163            .dom_manipulation_task_source()
2164            .queue(task!(fire_load_event: move || {
2165                let document = document.root();
2166                let window = document.window();
2167                if !window.is_alive() {
2168                    return;
2169                }
2170
2171                // Step 7.1.
2172                document.set_ready_state(DocumentReadyState::Complete, CanGc::note());
2173
2174                // Step 7.2.
2175                if document.browsing_context().is_none() {
2176                    return;
2177                }
2178                let event = Event::new(
2179                    window.upcast(),
2180                    atom!("load"),
2181                    EventBubbles::DoesNotBubble,
2182                    EventCancelable::NotCancelable,
2183                    CanGc::note(),
2184                );
2185                event.set_trusted(true);
2186
2187                // http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventStart
2188                update_with_current_instant(&document.load_event_start);
2189
2190                debug!("About to dispatch load for {:?}", document.url());
2191                window.dispatch_event_with_target_override(&event, CanGc::note());
2192
2193                // http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventEnd
2194                update_with_current_instant(&document.load_event_end);
2195
2196                if let Some(fragment) = document.url().fragment() {
2197                    document.check_and_scroll_fragment(fragment);
2198                }
2199            }));
2200
2201        // Step 8.
2202        let document = Trusted::new(self);
2203        if document.root().browsing_context().is_some() {
2204            self.owner_global()
2205                .task_manager()
2206                .dom_manipulation_task_source()
2207                .queue(task!(fire_pageshow_event: move || {
2208                    let document = document.root();
2209                    let window = document.window();
2210                    if document.page_showing.get() || !window.is_alive() {
2211                        return;
2212                    }
2213
2214                    document.page_showing.set(true);
2215
2216                    let event = PageTransitionEvent::new(
2217                        window,
2218                        atom!("pageshow"),
2219                        false, // bubbles
2220                        false, // cancelable
2221                        false, // persisted
2222                        CanGc::note(),
2223                    );
2224                    let event = event.upcast::<Event>();
2225                    event.set_trusted(true);
2226
2227                    window.dispatch_event_with_target_override(event, CanGc::note());
2228                }));
2229        }
2230
2231        // Step 9.
2232        // TODO: pending application cache download process tasks.
2233
2234        // Step 10.
2235        // TODO: printing steps.
2236
2237        // Step 11.
2238        // TODO: ready for post-load tasks.
2239
2240        // The dom.webxr.sessionavailable pref allows webxr
2241        // content to immediately begin a session without waiting for a user gesture.
2242        // TODO: should this only happen on the first document loaded?
2243        // https://immersive-web.github.io/webxr/#user-intention
2244        // https://github.com/immersive-web/navigation/issues/10
2245        #[cfg(feature = "webxr")]
2246        if pref!(dom_webxr_sessionavailable) && self.window.is_top_level() {
2247            self.window.Navigator().Xr().dispatch_sessionavailable();
2248        }
2249
2250        // Step 12: completely loaded.
2251        // https://html.spec.whatwg.org/multipage/#completely-loaded
2252        // TODO: fully implement "completely loaded".
2253        let document = Trusted::new(self);
2254        if document.root().browsing_context().is_some() {
2255            self.owner_global()
2256                .task_manager()
2257                .dom_manipulation_task_source()
2258                .queue(task!(completely_loaded: move || {
2259                    let document = document.root();
2260                    document.completely_loaded.set(true);
2261                    if let Some(DeclarativeRefresh::PendingLoad {
2262                        url,
2263                        time
2264                    }) = &*document.declarative_refresh.borrow() {
2265                        // https://html.spec.whatwg.org/multipage/#shared-declarative-refresh-steps
2266                        document.window.as_global_scope().schedule_callback(
2267                            OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
2268                                window: DomRoot::from_ref(document.window()),
2269                                url: url.clone(),
2270                            }),
2271                            Duration::from_secs(*time),
2272                        );
2273                    }
2274                    // Note: this will, among others, result in the "iframe-load-event-steps" being run.
2275                    // https://html.spec.whatwg.org/multipage/#iframe-load-event-steps
2276                    document.notify_constellation_load();
2277                }));
2278        }
2279    }
2280
2281    pub(crate) fn completely_loaded(&self) -> bool {
2282        self.completely_loaded.get()
2283    }
2284
2285    // https://html.spec.whatwg.org/multipage/#pending-parsing-blocking-script
2286    pub(crate) fn set_pending_parsing_blocking_script(
2287        &self,
2288        script: &HTMLScriptElement,
2289        load: Option<ScriptResult>,
2290    ) {
2291        assert!(!self.has_pending_parsing_blocking_script());
2292        *self.pending_parsing_blocking_script.borrow_mut() =
2293            Some(PendingScript::new_with_load(script, load));
2294    }
2295
2296    // https://html.spec.whatwg.org/multipage/#pending-parsing-blocking-script
2297    pub(crate) fn has_pending_parsing_blocking_script(&self) -> bool {
2298        self.pending_parsing_blocking_script.borrow().is_some()
2299    }
2300
2301    /// <https://html.spec.whatwg.org/multipage/#prepare-a-script> step 22.d.
2302    pub(crate) fn pending_parsing_blocking_script_loaded(
2303        &self,
2304        element: &HTMLScriptElement,
2305        result: ScriptResult,
2306        can_gc: CanGc,
2307    ) {
2308        {
2309            let mut blocking_script = self.pending_parsing_blocking_script.borrow_mut();
2310            let entry = blocking_script.as_mut().unwrap();
2311            assert!(&*entry.element == element);
2312            entry.loaded(result);
2313        }
2314        self.process_pending_parsing_blocking_script(can_gc);
2315    }
2316
2317    fn process_pending_parsing_blocking_script(&self, can_gc: CanGc) {
2318        if self.script_blocking_stylesheets_count.get() > 0 {
2319            return;
2320        }
2321        let pair = self
2322            .pending_parsing_blocking_script
2323            .borrow_mut()
2324            .as_mut()
2325            .and_then(PendingScript::take_result);
2326        if let Some((element, result)) = pair {
2327            *self.pending_parsing_blocking_script.borrow_mut() = None;
2328            self.get_current_parser()
2329                .unwrap()
2330                .resume_with_pending_parsing_blocking_script(&element, result, can_gc);
2331        }
2332    }
2333
2334    // https://html.spec.whatwg.org/multipage/#set-of-scripts-that-will-execute-as-soon-as-possible
2335    pub(crate) fn add_asap_script(&self, script: &HTMLScriptElement) {
2336        self.asap_scripts_set
2337            .borrow_mut()
2338            .push(Dom::from_ref(script));
2339    }
2340
2341    /// <https://html.spec.whatwg.org/multipage/#the-end> step 5.
2342    /// <https://html.spec.whatwg.org/multipage/#prepare-a-script> step 22.d.
2343    pub(crate) fn asap_script_loaded(
2344        &self,
2345        element: &HTMLScriptElement,
2346        result: ScriptResult,
2347        can_gc: CanGc,
2348    ) {
2349        {
2350            let mut scripts = self.asap_scripts_set.borrow_mut();
2351            let idx = scripts
2352                .iter()
2353                .position(|entry| &**entry == element)
2354                .unwrap();
2355            scripts.swap_remove(idx);
2356        }
2357        element.execute(result, can_gc);
2358    }
2359
2360    // https://html.spec.whatwg.org/multipage/#list-of-scripts-that-will-execute-in-order-as-soon-as-possible
2361    pub(crate) fn push_asap_in_order_script(&self, script: &HTMLScriptElement) {
2362        self.asap_in_order_scripts_list.push(script);
2363    }
2364
2365    /// <https://html.spec.whatwg.org/multipage/#the-end> step 5.
2366    /// <https://html.spec.whatwg.org/multipage/#prepare-a-script> step> 22.c.
2367    pub(crate) fn asap_in_order_script_loaded(
2368        &self,
2369        element: &HTMLScriptElement,
2370        result: ScriptResult,
2371        can_gc: CanGc,
2372    ) {
2373        self.asap_in_order_scripts_list.loaded(element, result);
2374        while let Some((element, result)) = self
2375            .asap_in_order_scripts_list
2376            .take_next_ready_to_be_executed()
2377        {
2378            element.execute(result, can_gc);
2379        }
2380    }
2381
2382    // https://html.spec.whatwg.org/multipage/#list-of-scripts-that-will-execute-when-the-document-has-finished-parsing
2383    pub(crate) fn add_deferred_script(&self, script: &HTMLScriptElement) {
2384        self.deferred_scripts.push(script);
2385    }
2386
2387    /// <https://html.spec.whatwg.org/multipage/#the-end> step 3.
2388    /// <https://html.spec.whatwg.org/multipage/#prepare-a-script> step 22.d.
2389    pub(crate) fn deferred_script_loaded(
2390        &self,
2391        element: &HTMLScriptElement,
2392        result: ScriptResult,
2393        can_gc: CanGc,
2394    ) {
2395        self.deferred_scripts.loaded(element, result);
2396        self.process_deferred_scripts(can_gc);
2397    }
2398
2399    /// <https://html.spec.whatwg.org/multipage/#the-end> step 3.
2400    fn process_deferred_scripts(&self, can_gc: CanGc) {
2401        if self.ready_state.get() != DocumentReadyState::Interactive {
2402            return;
2403        }
2404        // Part of substep 1.
2405        loop {
2406            if self.script_blocking_stylesheets_count.get() > 0 {
2407                return;
2408            }
2409            if let Some((element, result)) = self.deferred_scripts.take_next_ready_to_be_executed()
2410            {
2411                element.execute(result, can_gc);
2412            } else {
2413                break;
2414            }
2415        }
2416        if self.deferred_scripts.is_empty() {
2417            // https://html.spec.whatwg.org/multipage/#the-end step 4.
2418            self.maybe_dispatch_dom_content_loaded();
2419        }
2420    }
2421
2422    // https://html.spec.whatwg.org/multipage/#the-end step 4.
2423    pub(crate) fn maybe_dispatch_dom_content_loaded(&self) {
2424        if self.domcontentloaded_dispatched.get() {
2425            return;
2426        }
2427        self.domcontentloaded_dispatched.set(true);
2428        assert_ne!(
2429            self.ReadyState(),
2430            DocumentReadyState::Complete,
2431            "Complete before DOMContentLoaded?"
2432        );
2433
2434        update_with_current_instant(&self.dom_content_loaded_event_start);
2435
2436        // Step 4.1.
2437        let document = Trusted::new(self);
2438        self.owner_global()
2439            .task_manager()
2440            .dom_manipulation_task_source()
2441            .queue(
2442                task!(fire_dom_content_loaded_event: move || {
2443                let document = document.root();
2444                document.upcast::<EventTarget>().fire_bubbling_event(atom!("DOMContentLoaded"), CanGc::note());
2445                update_with_current_instant(&document.dom_content_loaded_event_end);
2446                })
2447            );
2448
2449        // html parsing has finished - set dom content loaded
2450        self.interactive_time
2451            .borrow()
2452            .maybe_set_tti(InteractiveFlag::DOMContentLoaded);
2453
2454        // Step 4.2.
2455        // TODO: client message queue.
2456    }
2457
2458    // https://html.spec.whatwg.org/multipage/#abort-a-document
2459    pub(crate) fn abort(&self, can_gc: CanGc) {
2460        // We need to inhibit the loader before anything else.
2461        self.loader.borrow_mut().inhibit_events();
2462
2463        // Step 1.
2464        for iframe in self.iframes().iter() {
2465            if let Some(document) = iframe.GetContentDocument() {
2466                // TODO: abort the active documents of every child browsing context.
2467                document.abort(can_gc);
2468                // TODO: salvageable flag.
2469            }
2470        }
2471
2472        // Step 2.
2473        self.script_blocking_stylesheets_count.set(0);
2474        *self.pending_parsing_blocking_script.borrow_mut() = None;
2475        *self.asap_scripts_set.borrow_mut() = vec![];
2476        self.asap_in_order_scripts_list.clear();
2477        self.deferred_scripts.clear();
2478        let loads_cancelled = self.loader.borrow_mut().cancel_all_loads();
2479        let event_sources_canceled = self.window.as_global_scope().close_event_sources();
2480        if loads_cancelled || event_sources_canceled {
2481            // If any loads were canceled.
2482            self.salvageable.set(false);
2483        };
2484
2485        // Also Step 2.
2486        // Note: the spec says to discard any tasks queued for fetch.
2487        // This cancels all tasks on the networking task source, which might be too broad.
2488        // See https://github.com/whatwg/html/issues/3837
2489        self.owner_global()
2490            .task_manager()
2491            .cancel_pending_tasks_for_source(TaskSourceName::Networking);
2492
2493        // Step 3.
2494        if let Some(parser) = self.get_current_parser() {
2495            self.active_parser_was_aborted.set(true);
2496            parser.abort(can_gc);
2497            self.salvageable.set(false);
2498        }
2499    }
2500
2501    pub(crate) fn notify_constellation_load(&self) {
2502        self.window()
2503            .send_to_constellation(ScriptToConstellationMessage::LoadComplete);
2504    }
2505
2506    pub(crate) fn set_current_parser(&self, script: Option<&ServoParser>) {
2507        self.current_parser.set(script);
2508    }
2509
2510    pub(crate) fn get_current_parser(&self) -> Option<DomRoot<ServoParser>> {
2511        self.current_parser.get()
2512    }
2513
2514    /// A reference to the [`IFrameCollection`] of this [`Document`], holding information about
2515    /// `<iframe>`s found within it.
2516    pub(crate) fn iframes(&self) -> Ref<'_, IFrameCollection> {
2517        self.iframes.borrow_mut().validate(self);
2518        self.iframes.borrow()
2519    }
2520
2521    /// A mutable reference to the [`IFrameCollection`] of this [`Document`], holding information about
2522    /// `<iframe>`s found within it.
2523    pub(crate) fn iframes_mut(&self) -> RefMut<'_, IFrameCollection> {
2524        self.iframes.borrow_mut().validate(self);
2525        self.iframes.borrow_mut()
2526    }
2527
2528    pub(crate) fn invalidate_iframes_collection(&self) {
2529        self.iframes.borrow_mut().invalidate();
2530    }
2531
2532    pub(crate) fn get_dom_interactive(&self) -> Option<CrossProcessInstant> {
2533        self.dom_interactive.get()
2534    }
2535
2536    pub(crate) fn set_navigation_start(&self, navigation_start: CrossProcessInstant) {
2537        self.interactive_time
2538            .borrow_mut()
2539            .set_navigation_start(navigation_start);
2540    }
2541
2542    pub(crate) fn get_interactive_metrics(&self) -> Ref<'_, ProgressiveWebMetrics> {
2543        self.interactive_time.borrow()
2544    }
2545
2546    pub(crate) fn has_recorded_tti_metric(&self) -> bool {
2547        self.get_interactive_metrics().get_tti().is_some()
2548    }
2549
2550    pub(crate) fn get_dom_content_loaded_event_start(&self) -> Option<CrossProcessInstant> {
2551        self.dom_content_loaded_event_start.get()
2552    }
2553
2554    pub(crate) fn get_dom_content_loaded_event_end(&self) -> Option<CrossProcessInstant> {
2555        self.dom_content_loaded_event_end.get()
2556    }
2557
2558    pub(crate) fn get_dom_complete(&self) -> Option<CrossProcessInstant> {
2559        self.dom_complete.get()
2560    }
2561
2562    pub(crate) fn get_top_level_dom_complete(&self) -> Option<CrossProcessInstant> {
2563        self.top_level_dom_complete.get()
2564    }
2565
2566    pub(crate) fn get_load_event_start(&self) -> Option<CrossProcessInstant> {
2567        self.load_event_start.get()
2568    }
2569
2570    pub(crate) fn get_load_event_end(&self) -> Option<CrossProcessInstant> {
2571        self.load_event_end.get()
2572    }
2573
2574    pub(crate) fn get_unload_event_start(&self) -> Option<CrossProcessInstant> {
2575        self.unload_event_start.get()
2576    }
2577
2578    pub(crate) fn get_unload_event_end(&self) -> Option<CrossProcessInstant> {
2579        self.unload_event_end.get()
2580    }
2581
2582    pub(crate) fn start_tti(&self) {
2583        if self.get_interactive_metrics().needs_tti() {
2584            self.tti_window.borrow_mut().start_window();
2585        }
2586    }
2587
2588    /// check tti for this document
2589    /// if it's been 10s since this doc encountered a task over 50ms, then we consider the
2590    /// main thread available and try to set tti
2591    pub(crate) fn record_tti_if_necessary(&self) {
2592        if self.has_recorded_tti_metric() {
2593            return;
2594        }
2595        if self.tti_window.borrow().needs_check() {
2596            self.get_interactive_metrics()
2597                .maybe_set_tti(InteractiveFlag::TimeToInteractive(
2598                    self.tti_window.borrow().get_start(),
2599                ));
2600        }
2601    }
2602
2603    /// <https://html.spec.whatwg.org/multipage/#fire-a-focus-event>
2604    fn fire_focus_event(
2605        &self,
2606        focus_event_type: FocusEventType,
2607        event_target: &EventTarget,
2608        related_target: Option<&EventTarget>,
2609        can_gc: CanGc,
2610    ) {
2611        let (event_name, does_bubble) = match focus_event_type {
2612            FocusEventType::Focus => (DOMString::from("focus"), EventBubbles::DoesNotBubble),
2613            FocusEventType::Blur => (DOMString::from("blur"), EventBubbles::DoesNotBubble),
2614        };
2615        let event = FocusEvent::new(
2616            &self.window,
2617            event_name,
2618            does_bubble,
2619            EventCancelable::NotCancelable,
2620            Some(&self.window),
2621            0i32,
2622            related_target,
2623            can_gc,
2624        );
2625        let event = event.upcast::<Event>();
2626        event.set_trusted(true);
2627        event.fire(event_target, can_gc);
2628    }
2629
2630    /// <https://html.spec.whatwg.org/multipage/#cookie-averse-document-object>
2631    pub(crate) fn is_cookie_averse(&self) -> bool {
2632        !self.has_browsing_context || !url_has_network_scheme(&self.url())
2633    }
2634
2635    /// <https://html.spec.whatwg.org/multipage/#look-up-a-custom-element-definition>
2636    pub(crate) fn lookup_custom_element_definition(
2637        &self,
2638        namespace: &Namespace,
2639        local_name: &LocalName,
2640        is: Option<&LocalName>,
2641    ) -> Option<Rc<CustomElementDefinition>> {
2642        if !pref!(dom_customelements_enabled) {
2643            return None;
2644        }
2645
2646        // Step 1
2647        if *namespace != ns!(html) {
2648            return None;
2649        }
2650
2651        // Step 2
2652        if !self.has_browsing_context {
2653            return None;
2654        }
2655
2656        // Step 3
2657        let registry = self.window.CustomElements();
2658
2659        registry.lookup_definition(local_name, is)
2660    }
2661
2662    pub(crate) fn increment_throw_on_dynamic_markup_insertion_counter(&self) {
2663        let counter = self.throw_on_dynamic_markup_insertion_counter.get();
2664        self.throw_on_dynamic_markup_insertion_counter
2665            .set(counter + 1);
2666    }
2667
2668    pub(crate) fn decrement_throw_on_dynamic_markup_insertion_counter(&self) {
2669        let counter = self.throw_on_dynamic_markup_insertion_counter.get();
2670        self.throw_on_dynamic_markup_insertion_counter
2671            .set(counter - 1);
2672    }
2673
2674    pub(crate) fn react_to_environment_changes(&self) {
2675        for image in self.responsive_images.borrow().iter() {
2676            image.react_to_environment_changes();
2677        }
2678    }
2679
2680    pub(crate) fn register_responsive_image(&self, img: &HTMLImageElement) {
2681        self.responsive_images.borrow_mut().push(Dom::from_ref(img));
2682    }
2683
2684    pub(crate) fn unregister_responsive_image(&self, img: &HTMLImageElement) {
2685        let index = self
2686            .responsive_images
2687            .borrow()
2688            .iter()
2689            .position(|x| **x == *img);
2690        if let Some(i) = index {
2691            self.responsive_images.borrow_mut().remove(i);
2692        }
2693    }
2694
2695    pub(crate) fn register_media_controls(&self, controls: &ShadowRoot) -> String {
2696        let id = Uuid::new_v4().to_string();
2697        self.media_controls
2698            .borrow_mut()
2699            .insert(id.clone(), Dom::from_ref(controls));
2700        id
2701    }
2702
2703    pub(crate) fn unregister_media_controls(&self, id: &str) {
2704        let did_have_these_media_controls = self.media_controls.borrow_mut().remove(id).is_some();
2705        debug_assert!(
2706            did_have_these_media_controls,
2707            "Trying to unregister unknown media controls"
2708        );
2709    }
2710
2711    pub(crate) fn add_dirty_webgl_canvas(&self, context: &WebGLRenderingContext) {
2712        self.dirty_webgl_contexts
2713            .borrow_mut()
2714            .entry(context.context_id())
2715            .or_insert_with(|| Dom::from_ref(context));
2716    }
2717
2718    pub(crate) fn add_dirty_2d_canvas(&self, context: &CanvasRenderingContext2D) {
2719        self.dirty_2d_contexts
2720            .borrow_mut()
2721            .entry(context.context_id())
2722            .or_insert_with(|| Dom::from_ref(context));
2723    }
2724
2725    #[cfg(feature = "webgpu")]
2726    pub(crate) fn add_dirty_webgpu_context(&self, context: &GPUCanvasContext) {
2727        self.dirty_webgpu_contexts
2728            .borrow_mut()
2729            .entry(context.context_id())
2730            .or_insert_with(|| Dom::from_ref(context));
2731    }
2732
2733    /// Whether or not this [`Document`] needs a rendering update, due to changed
2734    /// contents or pending events. This is used to decide whether or not to schedule
2735    /// a call to the "update the rendering" algorithm.
2736    pub(crate) fn needs_rendering_update(&self) -> bool {
2737        if !self.is_fully_active() {
2738            return false;
2739        }
2740        if !self.window().layout_blocked() &&
2741            (!self.restyle_reason().is_empty() ||
2742                self.window().layout().needs_new_display_list())
2743        {
2744            return true;
2745        }
2746        if !self.rendering_update_reasons.get().is_empty() {
2747            return true;
2748        }
2749        if self.event_handler.has_pending_input_events() {
2750            return true;
2751        }
2752        if self.has_pending_scroll_events() {
2753            return true;
2754        }
2755        if self.window().has_unhandled_resize_event() {
2756            return true;
2757        }
2758        if self.has_pending_animated_image_update.get() ||
2759            !self.dirty_2d_contexts.borrow().is_empty() ||
2760            !self.dirty_webgl_contexts.borrow().is_empty()
2761        {
2762            return true;
2763        }
2764
2765        #[cfg(feature = "webgpu")]
2766        if !self.dirty_webgpu_contexts.borrow().is_empty() {
2767            return true;
2768        }
2769
2770        false
2771    }
2772
2773    /// An implementation of step 22 from
2774    /// <https://html.spec.whatwg.org/multipage/#update-the-rendering>:
2775    ///
2776    // > Step 22: For each doc of docs, update the rendering or user interface of
2777    // > doc and its node navigable to reflect the current state.
2778    //
2779    // Returns the set of reflow phases run as a [`ReflowPhasesRun`].
2780    pub(crate) fn update_the_rendering(&self) -> ReflowPhasesRun {
2781        if self.render_blocking_element_count() > 0 {
2782            return Default::default();
2783        }
2784
2785        let mut results = ReflowPhasesRun::empty();
2786        if self.has_pending_animated_image_update.get() {
2787            self.image_animation_manager
2788                .borrow()
2789                .update_active_frames(&self.window, self.current_animation_timeline_value());
2790            self.has_pending_animated_image_update.set(false);
2791            results.insert(ReflowPhasesRun::UpdatedImageData);
2792        }
2793
2794        self.current_rendering_epoch
2795            .set(self.current_rendering_epoch.get().next());
2796        let current_rendering_epoch = self.current_rendering_epoch.get();
2797
2798        // All dirty canvases are flushed before updating the rendering.
2799        let mut image_keys = Vec::new();
2800        #[cfg(feature = "webgpu")]
2801        image_keys.extend(
2802            self.dirty_webgpu_contexts
2803                .borrow_mut()
2804                .drain()
2805                .filter(|(_, context)| context.update_rendering(current_rendering_epoch))
2806                .map(|(_, context)| {
2807                    results.insert(ReflowPhasesRun::UpdatedImageData);
2808                    context.image_key()
2809                }),
2810        );
2811
2812        image_keys.extend(
2813            self.dirty_2d_contexts
2814                .borrow_mut()
2815                .drain()
2816                .filter(|(_, context)| context.update_rendering(current_rendering_epoch))
2817                .map(|(_, context)| {
2818                    results.insert(ReflowPhasesRun::UpdatedImageData);
2819                    context.image_key()
2820                }),
2821        );
2822
2823        let dirty_webgl_context_ids: Vec<_> = self
2824            .dirty_webgl_contexts
2825            .borrow_mut()
2826            .drain()
2827            .filter(|(_, context)| context.onscreen())
2828            .map(|(id, context)| {
2829                image_keys.push(context.image_key());
2830                id
2831            })
2832            .collect();
2833
2834        if !dirty_webgl_context_ids.is_empty() {
2835            results.insert(ReflowPhasesRun::UpdatedImageData);
2836            self.window
2837                .webgl_chan()
2838                .expect("Where's the WebGL channel?")
2839                .send(WebGLMsg::SwapBuffers(
2840                    dirty_webgl_context_ids,
2841                    Some(current_rendering_epoch),
2842                    0,
2843                ))
2844                .unwrap();
2845        }
2846
2847        // The renderer should wait to display the frame until all canvas images are
2848        // uploaded. This allows canvas image uploading to happen asynchronously.
2849        let pipeline_id = self.window().pipeline_id();
2850        if !image_keys.is_empty() {
2851            self.waiting_on_canvas_image_updates.set(true);
2852            self.window().compositor_api().delay_new_frame_for_canvas(
2853                self.window().pipeline_id(),
2854                current_rendering_epoch,
2855                image_keys.into_iter().flatten().collect(),
2856            );
2857        }
2858
2859        let results = results.union(self.window().reflow(ReflowGoal::UpdateTheRendering));
2860
2861        self.window().compositor_api().update_epoch(
2862            self.webview_id(),
2863            pipeline_id,
2864            current_rendering_epoch,
2865        );
2866
2867        results
2868    }
2869
2870    pub(crate) fn handle_no_longer_waiting_on_asynchronous_image_updates(&self) {
2871        self.waiting_on_canvas_image_updates.set(false);
2872    }
2873
2874    pub(crate) fn waiting_on_canvas_image_updates(&self) -> bool {
2875        self.waiting_on_canvas_image_updates.get()
2876    }
2877
2878    /// From <https://drafts.csswg.org/css-font-loading/#fontfaceset-pending-on-the-environment>:
2879    ///
2880    /// > A FontFaceSet is pending on the environment if any of the following are true:
2881    /// >  - the document is still loading
2882    /// >  - the document has pending stylesheet requests
2883    /// >  - the document has pending layout operations which might cause the user agent to request
2884    /// >    a font, or which depend on recently-loaded fonts
2885    ///
2886    /// Returns true if the promise was fulfilled.
2887    pub(crate) fn maybe_fulfill_font_ready_promise(&self, can_gc: CanGc) -> bool {
2888        if !self.is_fully_active() {
2889            return false;
2890        }
2891
2892        let fonts = self.Fonts(can_gc);
2893        if !fonts.waiting_to_fullfill_promise() {
2894            return false;
2895        }
2896        if self.window().font_context().web_fonts_still_loading() != 0 {
2897            return false;
2898        }
2899        if self.ReadyState() != DocumentReadyState::Complete {
2900            return false;
2901        }
2902        if !self.restyle_reason().is_empty() {
2903            return false;
2904        }
2905        if !self.rendering_update_reasons.get().is_empty() {
2906            return false;
2907        }
2908
2909        let result = fonts.fulfill_ready_promise_if_needed(can_gc);
2910
2911        // Add a rendering update after the `fonts.ready` promise is fulfilled just for
2912        // the sake of taking screenshots. This has the effect of delaying screenshots
2913        // until layout has taken a shot at updating the rendering.
2914        if result {
2915            self.add_rendering_update_reason(RenderingUpdateReason::FontReadyPromiseFulfilled);
2916        }
2917
2918        result
2919    }
2920
2921    pub(crate) fn id_map(
2922        &self,
2923    ) -> Ref<'_, HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>> {
2924        self.id_map.borrow()
2925    }
2926
2927    pub(crate) fn name_map(
2928        &self,
2929    ) -> Ref<'_, HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>> {
2930        self.name_map.borrow()
2931    }
2932
2933    /// <https://drafts.csswg.org/resize-observer/#dom-resizeobserver-resizeobserver>
2934    pub(crate) fn add_resize_observer(&self, resize_observer: &ResizeObserver) {
2935        self.resize_observers
2936            .borrow_mut()
2937            .push(Dom::from_ref(resize_observer));
2938    }
2939
2940    /// Whether or not this [`Document`] has any active [`ResizeObserver`].
2941    pub(crate) fn has_resize_observers(&self) -> bool {
2942        !self.resize_observers.borrow().is_empty()
2943    }
2944
2945    /// <https://drafts.csswg.org/resize-observer/#gather-active-observations-h>
2946    /// <https://drafts.csswg.org/resize-observer/#has-active-resize-observations>
2947    pub(crate) fn gather_active_resize_observations_at_depth(
2948        &self,
2949        depth: &ResizeObservationDepth,
2950    ) -> bool {
2951        let mut has_active_resize_observations = false;
2952        for observer in self.resize_observers.borrow_mut().iter_mut() {
2953            observer.gather_active_resize_observations_at_depth(
2954                depth,
2955                &mut has_active_resize_observations,
2956            );
2957        }
2958        has_active_resize_observations
2959    }
2960
2961    /// <https://drafts.csswg.org/resize-observer/#broadcast-active-resize-observations>
2962    pub(crate) fn broadcast_active_resize_observations(
2963        &self,
2964        can_gc: CanGc,
2965    ) -> ResizeObservationDepth {
2966        let mut shallowest = ResizeObservationDepth::max();
2967        // Breaking potential re-borrow cycle on `resize_observers`:
2968        // broadcasting resize observations calls into a JS callback,
2969        // which can add new observers.
2970        let iterator: Vec<DomRoot<ResizeObserver>> = self
2971            .resize_observers
2972            .borrow()
2973            .iter()
2974            .cloned()
2975            .map(|obs| DomRoot::from_ref(&*obs))
2976            .collect();
2977        for observer in iterator {
2978            observer.broadcast_active_resize_observations(&mut shallowest, can_gc);
2979        }
2980        shallowest
2981    }
2982
2983    /// <https://drafts.csswg.org/resize-observer/#has-skipped-observations-h>
2984    pub(crate) fn has_skipped_resize_observations(&self) -> bool {
2985        self.resize_observers
2986            .borrow()
2987            .iter()
2988            .any(|observer| observer.has_skipped_resize_observations())
2989    }
2990
2991    /// <https://drafts.csswg.org/resize-observer/#deliver-resize-loop-error-notification>
2992    pub(crate) fn deliver_resize_loop_error_notification(&self, can_gc: CanGc) {
2993        let error_info: ErrorInfo = crate::dom::bindings::error::ErrorInfo {
2994            message: "ResizeObserver loop completed with undelivered notifications.".to_string(),
2995            ..Default::default()
2996        };
2997        self.window
2998            .as_global_scope()
2999            .report_an_error(error_info, HandleValue::null(), can_gc);
3000    }
3001
3002    pub(crate) fn status_code(&self) -> Option<u16> {
3003        self.status_code
3004    }
3005
3006    /// <https://html.spec.whatwg.org/multipage/#encoding-parsing-a-url>
3007    pub(crate) fn encoding_parse_a_url(&self, url: &str) -> Result<ServoUrl, url::ParseError> {
3008        // NOTE: This algorithm is defined for both Document and environment settings objects.
3009        // This implementation is only for documents.
3010
3011        // Step 1. Let encoding be UTF-8.
3012        // Step 2. If environment is a Document object, then set encoding to environment's character encoding.
3013        let encoding = self.encoding.get();
3014
3015        // Step 3. Otherwise, if environment's relevant global object is a Window object, set encoding to environment's
3016        // relevant global object's associated Document's character encoding.
3017
3018        // Step 4. Let baseURL be environment's base URL, if environment is a Document object;
3019        // otherwise environment's API base URL.
3020        let base_url = self.base_url();
3021
3022        // Step 5. Return the result of applying the URL parser to url, with baseURL and encoding.
3023        url::Url::options()
3024            .base_url(Some(base_url.as_url()))
3025            .encoding_override(Some(&|input| {
3026                servo_url::encoding::encode_as_url_query_string(input, encoding)
3027            }))
3028            .parse(url)
3029            .map(ServoUrl::from)
3030    }
3031
3032    /// <https://html.spec.whatwg.org/multipage/#allowed-to-use>
3033    pub(crate) fn allowed_to_use_feature(&self, _feature: PermissionName) -> bool {
3034        // Step 1. If document's browsing context is null, then return false.
3035        if !self.has_browsing_context {
3036            return false;
3037        }
3038
3039        // Step 2. If document is not fully active, then return false.
3040        if !self.is_fully_active() {
3041            return false;
3042        }
3043
3044        // Step 3. If the result of running is feature enabled in document for origin on
3045        // feature, document, and document's origin is "Enabled", then return true.
3046        // Step 4. Return false.
3047        // TODO: All features are currently enabled for `Document`s because we do not
3048        // implement the Permissions Policy specification.
3049        true
3050    }
3051
3052    /// Add an [`IntersectionObserver`] to the [`Document`], to be processed in the [`Document`]'s event loop.
3053    /// <https://github.com/w3c/IntersectionObserver/issues/525>
3054    pub(crate) fn add_intersection_observer(&self, intersection_observer: &IntersectionObserver) {
3055        self.intersection_observers
3056            .borrow_mut()
3057            .push(Dom::from_ref(intersection_observer));
3058    }
3059
3060    /// Remove an [`IntersectionObserver`] from [`Document`], ommiting it from the event loop.
3061    /// An observer without any target, ideally should be removed to be conformant with
3062    /// <https://w3c.github.io/IntersectionObserver/#lifetime>.
3063    pub(crate) fn remove_intersection_observer(
3064        &self,
3065        intersection_observer: &IntersectionObserver,
3066    ) {
3067        self.intersection_observers
3068            .borrow_mut()
3069            .retain(|observer| *observer != intersection_observer)
3070    }
3071
3072    /// <https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo>
3073    pub(crate) fn update_intersection_observer_steps(
3074        &self,
3075        time: CrossProcessInstant,
3076        can_gc: CanGc,
3077    ) {
3078        // Step 1-2
3079        for intersection_observer in &*self.intersection_observers.borrow() {
3080            self.update_single_intersection_observer_steps(intersection_observer, time, can_gc);
3081        }
3082    }
3083
3084    /// Step 2.1-2.2 of <https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo>
3085    fn update_single_intersection_observer_steps(
3086        &self,
3087        intersection_observer: &IntersectionObserver,
3088        time: CrossProcessInstant,
3089        can_gc: CanGc,
3090    ) {
3091        // Step 1
3092        // > Let rootBounds be observer’s root intersection rectangle.
3093        let root_bounds = intersection_observer.root_intersection_rectangle(self);
3094
3095        // Step 2
3096        // > For each target in observer’s internal [[ObservationTargets]] slot,
3097        // > processed in the same order that observe() was called on each target:
3098        intersection_observer.update_intersection_observations_steps(
3099            self,
3100            time,
3101            root_bounds,
3102            can_gc,
3103        );
3104    }
3105
3106    /// <https://w3c.github.io/IntersectionObserver/#notify-intersection-observers-algo>
3107    pub(crate) fn notify_intersection_observers(&self, can_gc: CanGc) {
3108        // Step 1
3109        // > Set document’s IntersectionObserverTaskQueued flag to false.
3110        self.intersection_observer_task_queued.set(false);
3111
3112        // Step 2
3113        // > Let notify list be a list of all IntersectionObservers whose root is in the DOM tree of document.
3114        // We will copy the observers because callback could modify the current list.
3115        // It will rooted to prevent GC in the iteration.
3116        rooted_vec!(let notify_list <- self.intersection_observers.clone().take().into_iter());
3117
3118        // Step 3
3119        // > For each IntersectionObserver object observer in notify list, run these steps:
3120        for intersection_observer in notify_list.iter() {
3121            // Step 3.1-3.5
3122            intersection_observer.invoke_callback_if_necessary(can_gc);
3123        }
3124    }
3125
3126    /// <https://w3c.github.io/IntersectionObserver/#queue-intersection-observer-task>
3127    pub(crate) fn queue_an_intersection_observer_task(&self) {
3128        // Step 1
3129        // > If document’s IntersectionObserverTaskQueued flag is set to true, return.
3130        if self.intersection_observer_task_queued.get() {
3131            return;
3132        }
3133
3134        // Step 2
3135        // > Set document’s IntersectionObserverTaskQueued flag to true.
3136        self.intersection_observer_task_queued.set(true);
3137
3138        // Step 3
3139        // > Queue a task on the IntersectionObserver task source associated with
3140        // > the document's event loop to notify intersection observers.
3141        let document = Trusted::new(self);
3142        self.owner_global()
3143            .task_manager()
3144            .intersection_observer_task_source()
3145            .queue(task!(notify_intersection_observers: move || {
3146                document.root().notify_intersection_observers(CanGc::note());
3147            }));
3148    }
3149
3150    pub(crate) fn handle_paint_metric(
3151        &self,
3152        metric_type: ProgressiveWebMetricType,
3153        metric_value: CrossProcessInstant,
3154        first_reflow: bool,
3155        can_gc: CanGc,
3156    ) {
3157        let metrics = self.interactive_time.borrow();
3158        match metric_type {
3159            ProgressiveWebMetricType::FirstPaint => {
3160                metrics.set_first_paint(metric_value, first_reflow)
3161            },
3162            ProgressiveWebMetricType::FirstContentfulPaint => {
3163                metrics.set_first_contentful_paint(metric_value, first_reflow)
3164            },
3165            ProgressiveWebMetricType::LargestContentfulPaint { area, lcp_type } => {
3166                metrics.set_largest_contentful_paint(metric_value, area, lcp_type)
3167            },
3168            ProgressiveWebMetricType::TimeToInteractive => {
3169                unreachable!("Unexpected non-paint metric.")
3170            },
3171        }
3172
3173        let entry = PerformancePaintTiming::new(
3174            self.window.as_global_scope(),
3175            metric_type,
3176            metric_value,
3177            can_gc,
3178        );
3179        self.window
3180            .Performance()
3181            .queue_entry(entry.upcast::<PerformanceEntry>(), can_gc);
3182    }
3183
3184    /// <https://html.spec.whatwg.org/multipage/#document-write-steps>
3185    fn write(
3186        &self,
3187        text: Vec<TrustedHTMLOrString>,
3188        line_feed: bool,
3189        containing_class: &str,
3190        field: &str,
3191        can_gc: CanGc,
3192    ) -> ErrorResult {
3193        // Step 1: Let string be the empty string.
3194        let mut strings: Vec<String> = Vec::with_capacity(text.len());
3195        // Step 2: Let isTrusted be false if text contains a string; otherwise true.
3196        let mut is_trusted = true;
3197        // Step 3: For each value of text:
3198        for value in text {
3199            match value {
3200                // Step 3.1: If value is a TrustedHTML object, then append value's associated data to string.
3201                TrustedHTMLOrString::TrustedHTML(trusted_html) => {
3202                    strings.push(trusted_html.to_string().to_owned());
3203                },
3204                TrustedHTMLOrString::String(str_) => {
3205                    // Step 2: Let isTrusted be false if text contains a string; otherwise true.
3206                    is_trusted = false;
3207                    // Step 3.2: Otherwise, append value to string.
3208                    strings.push(str_.into());
3209                },
3210            };
3211        }
3212        let mut string = itertools::join(strings, "");
3213        // Step 4: If isTrusted is false, set string to the result of invoking the
3214        // Get Trusted Type compliant string algorithm with TrustedHTML,
3215        // this's relevant global object, string, sink, and "script".
3216        if !is_trusted {
3217            string = TrustedHTML::get_trusted_script_compliant_string(
3218                &self.global(),
3219                TrustedHTMLOrString::String(string.into()),
3220                &format!("{} {}", containing_class, field),
3221                can_gc,
3222            )?
3223            .str()
3224            .to_owned();
3225        }
3226        // Step 5: If lineFeed is true, append U+000A LINE FEED to string.
3227        if line_feed {
3228            string.push('\n');
3229        }
3230        // Step 6: If document is an XML document, then throw an "InvalidStateError" DOMException.
3231        if !self.is_html_document() {
3232            return Err(Error::InvalidState(None));
3233        }
3234
3235        // Step 7: If document's throw-on-dynamic-markup-insertion counter is greater than 0,
3236        // then throw an "InvalidStateError" DOMException.
3237        if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
3238            return Err(Error::InvalidState(None));
3239        }
3240
3241        // Step 8: If document's active parser was aborted is true, then return.
3242        if !self.is_active() || self.active_parser_was_aborted.get() {
3243            return Ok(());
3244        }
3245
3246        let parser = match self.get_current_parser() {
3247            Some(ref parser) if parser.can_write() => DomRoot::from_ref(&**parser),
3248            // Step 9: If the insertion point is undefined, then:
3249            _ => {
3250                // Step 9.1: If document's unload counter is greater than 0 or
3251                // document's ignore-destructive-writes counter is greater than 0, then return.
3252                if self.is_prompting_or_unloading() ||
3253                    self.ignore_destructive_writes_counter.get() > 0
3254                {
3255                    return Ok(());
3256                }
3257                // Step 9.2: Run the document open steps with document.
3258                self.Open(None, None, can_gc)?;
3259                self.get_current_parser().unwrap()
3260            },
3261        };
3262
3263        // Steps 10-11.
3264        parser.write(string.into(), can_gc);
3265
3266        Ok(())
3267    }
3268}
3269
3270#[derive(MallocSizeOf, PartialEq)]
3271pub(crate) enum DocumentSource {
3272    FromParser,
3273    NotFromParser,
3274}
3275
3276#[allow(unsafe_code)]
3277pub(crate) trait LayoutDocumentHelpers<'dom> {
3278    fn is_html_document_for_layout(&self) -> bool;
3279    fn quirks_mode(self) -> QuirksMode;
3280    fn style_shared_lock(self) -> &'dom StyleSharedRwLock;
3281    fn shadow_roots(self) -> Vec<LayoutDom<'dom, ShadowRoot>>;
3282    fn shadow_roots_styles_changed(self) -> bool;
3283    fn flush_shadow_roots_stylesheets(self);
3284}
3285
3286#[allow(unsafe_code)]
3287impl<'dom> LayoutDocumentHelpers<'dom> for LayoutDom<'dom, Document> {
3288    #[inline]
3289    fn is_html_document_for_layout(&self) -> bool {
3290        self.unsafe_get().is_html_document
3291    }
3292
3293    #[inline]
3294    fn quirks_mode(self) -> QuirksMode {
3295        self.unsafe_get().quirks_mode.get()
3296    }
3297
3298    #[inline]
3299    fn style_shared_lock(self) -> &'dom StyleSharedRwLock {
3300        self.unsafe_get().style_shared_lock()
3301    }
3302
3303    #[inline]
3304    fn shadow_roots(self) -> Vec<LayoutDom<'dom, ShadowRoot>> {
3305        // FIXME(nox): We should just return a
3306        // &'dom HashSet<LayoutDom<'dom, ShadowRoot>> here but not until
3307        // I rework the ToLayout trait as mentioned in
3308        // LayoutDom::to_layout_slice.
3309        unsafe {
3310            self.unsafe_get()
3311                .shadow_roots
3312                .borrow_for_layout()
3313                .iter()
3314                .map(|sr| sr.to_layout())
3315                .collect()
3316        }
3317    }
3318
3319    #[inline]
3320    fn shadow_roots_styles_changed(self) -> bool {
3321        self.unsafe_get().shadow_roots_styles_changed.get()
3322    }
3323
3324    #[inline]
3325    fn flush_shadow_roots_stylesheets(self) {
3326        (*self.unsafe_get()).flush_shadow_roots_stylesheets()
3327    }
3328}
3329
3330// https://html.spec.whatwg.org/multipage/#is-a-registrable-domain-suffix-of-or-is-equal-to
3331// The spec says to return a bool, we actually return an Option<Host> containing
3332// the parsed host in the successful case, to avoid having to re-parse the host.
3333fn get_registrable_domain_suffix_of_or_is_equal_to(
3334    host_suffix_string: &DOMString,
3335    original_host: Host,
3336) -> Option<Host> {
3337    // Step 1
3338    if host_suffix_string.is_empty() {
3339        return None;
3340    }
3341
3342    // Step 2-3.
3343    let host = match Host::parse(&host_suffix_string.str()) {
3344        Ok(host) => host,
3345        Err(_) => return None,
3346    };
3347
3348    // Step 4.
3349    if host != original_host {
3350        // Step 4.1
3351        let host = match host {
3352            Host::Domain(ref host) => host,
3353            _ => return None,
3354        };
3355        let original_host = match original_host {
3356            Host::Domain(ref original_host) => original_host,
3357            _ => return None,
3358        };
3359
3360        // Step 4.2
3361        let index = original_host.len().checked_sub(host.len())?;
3362        let (prefix, suffix) = original_host.split_at(index);
3363
3364        if !prefix.ends_with('.') {
3365            return None;
3366        }
3367        if suffix != host {
3368            return None;
3369        }
3370
3371        // Step 4.3
3372        if is_pub_domain(host) {
3373            return None;
3374        }
3375    }
3376
3377    // Step 5
3378    Some(host)
3379}
3380
3381/// <https://url.spec.whatwg.org/#network-scheme>
3382fn url_has_network_scheme(url: &ServoUrl) -> bool {
3383    matches!(url.scheme(), "ftp" | "http" | "https")
3384}
3385
3386#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
3387pub(crate) enum HasBrowsingContext {
3388    No,
3389    Yes,
3390}
3391
3392impl Document {
3393    #[allow(clippy::too_many_arguments)]
3394    pub(crate) fn new_inherited(
3395        window: &Window,
3396        has_browsing_context: HasBrowsingContext,
3397        url: Option<ServoUrl>,
3398        origin: MutableOrigin,
3399        is_html_document: IsHTMLDocument,
3400        content_type: Option<Mime>,
3401        last_modified: Option<String>,
3402        activity: DocumentActivity,
3403        source: DocumentSource,
3404        doc_loader: DocumentLoader,
3405        referrer: Option<String>,
3406        status_code: Option<u16>,
3407        canceller: FetchCanceller,
3408        is_initial_about_blank: bool,
3409        allow_declarative_shadow_roots: bool,
3410        inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
3411        has_trustworthy_ancestor_origin: bool,
3412        custom_element_reaction_stack: Rc<CustomElementReactionStack>,
3413        creation_sandboxing_flag_set: SandboxingFlagSet,
3414    ) -> Document {
3415        let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
3416
3417        let (ready_state, domcontentloaded_dispatched) = if source == DocumentSource::FromParser {
3418            (DocumentReadyState::Loading, false)
3419        } else {
3420            (DocumentReadyState::Complete, true)
3421        };
3422
3423        let frame_type = match window.is_top_level() {
3424            true => TimerMetadataFrameType::RootWindow,
3425            false => TimerMetadataFrameType::IFrame,
3426        };
3427        let interactive_time = ProgressiveWebMetrics::new(
3428            window.time_profiler_chan().clone(),
3429            url.clone(),
3430            frame_type,
3431        );
3432
3433        let content_type = content_type.unwrap_or_else(|| {
3434            match is_html_document {
3435                // https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
3436                IsHTMLDocument::HTMLDocument => "text/html",
3437                // https://dom.spec.whatwg.org/#concept-document-content-type
3438                IsHTMLDocument::NonHTMLDocument => "application/xml",
3439            }
3440            .parse()
3441            .unwrap()
3442        });
3443
3444        let encoding = content_type
3445            .get_parameter(CHARSET)
3446            .and_then(|charset| Encoding::for_label(charset.as_bytes()))
3447            .unwrap_or(UTF_8);
3448
3449        let has_focus = window.parent_info().is_none();
3450
3451        let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
3452
3453        Document {
3454            node: Node::new_document_node(),
3455            document_or_shadow_root: DocumentOrShadowRoot::new(window),
3456            window: Dom::from_ref(window),
3457            has_browsing_context,
3458            implementation: Default::default(),
3459            content_type,
3460            last_modified,
3461            url: DomRefCell::new(url),
3462            // https://dom.spec.whatwg.org/#concept-document-quirks
3463            quirks_mode: Cell::new(QuirksMode::NoQuirks),
3464            event_handler: DocumentEventHandler::new(window),
3465            embedder_controls: DocumentEmbedderControls::new(window),
3466            id_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3467            name_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3468            // https://dom.spec.whatwg.org/#concept-document-encoding
3469            encoding: Cell::new(encoding),
3470            is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
3471            activity: Cell::new(activity),
3472            tag_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3473            tagns_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3474            classes_map: DomRefCell::new(HashMapTracedValues::new()),
3475            images: Default::default(),
3476            embeds: Default::default(),
3477            links: Default::default(),
3478            forms: Default::default(),
3479            scripts: Default::default(),
3480            anchors: Default::default(),
3481            applets: Default::default(),
3482            iframes: RefCell::new(IFrameCollection::new()),
3483            style_shared_lock: {
3484                /// Per-process shared lock for author-origin stylesheets
3485                ///
3486                /// FIXME: make it per-document or per-pipeline instead:
3487                /// <https://github.com/servo/servo/issues/16027>
3488                /// (Need to figure out what to do with the style attribute
3489                /// of elements adopted into another document.)
3490                static PER_PROCESS_AUTHOR_SHARED_LOCK: LazyLock<StyleSharedRwLock> =
3491                    LazyLock::new(StyleSharedRwLock::new);
3492
3493                PER_PROCESS_AUTHOR_SHARED_LOCK.clone()
3494                // StyleSharedRwLock::new()
3495            },
3496            stylesheets: DomRefCell::new(DocumentStylesheetSet::new()),
3497            stylesheet_list: MutNullableDom::new(None),
3498            ready_state: Cell::new(ready_state),
3499            domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched),
3500            focus_transaction: DomRefCell::new(None),
3501            focused: Default::default(),
3502            focus_sequence: Cell::new(FocusSequenceNumber::default()),
3503            has_focus: Cell::new(has_focus),
3504            current_script: Default::default(),
3505            pending_parsing_blocking_script: Default::default(),
3506            script_blocking_stylesheets_count: Default::default(),
3507            render_blocking_element_count: Default::default(),
3508            deferred_scripts: Default::default(),
3509            asap_in_order_scripts_list: Default::default(),
3510            asap_scripts_set: Default::default(),
3511            animation_frame_ident: Cell::new(0),
3512            animation_frame_list: DomRefCell::new(VecDeque::new()),
3513            running_animation_callbacks: Cell::new(false),
3514            loader: DomRefCell::new(doc_loader),
3515            current_parser: Default::default(),
3516            base_element: Default::default(),
3517            appropriate_template_contents_owner_document: Default::default(),
3518            pending_restyles: DomRefCell::new(FxHashMap::default()),
3519            needs_restyle: Cell::new(RestyleReason::DOMChanged),
3520            dom_interactive: Cell::new(Default::default()),
3521            dom_content_loaded_event_start: Cell::new(Default::default()),
3522            dom_content_loaded_event_end: Cell::new(Default::default()),
3523            dom_complete: Cell::new(Default::default()),
3524            top_level_dom_complete: Cell::new(Default::default()),
3525            load_event_start: Cell::new(Default::default()),
3526            load_event_end: Cell::new(Default::default()),
3527            unload_event_start: Cell::new(Default::default()),
3528            unload_event_end: Cell::new(Default::default()),
3529            https_state: Cell::new(HttpsState::None),
3530            origin,
3531            referrer,
3532            target_element: MutNullableDom::new(None),
3533            policy_container: DomRefCell::new(PolicyContainer::default()),
3534            preloaded_resources: Default::default(),
3535            ignore_destructive_writes_counter: Default::default(),
3536            ignore_opens_during_unload_counter: Default::default(),
3537            spurious_animation_frames: Cell::new(0),
3538            dom_count: Cell::new(1),
3539            fullscreen_element: MutNullableDom::new(None),
3540            form_id_listener_map: Default::default(),
3541            interactive_time: DomRefCell::new(interactive_time),
3542            tti_window: DomRefCell::new(InteractiveWindow::default()),
3543            canceller,
3544            throw_on_dynamic_markup_insertion_counter: Cell::new(0),
3545            page_showing: Cell::new(false),
3546            salvageable: Cell::new(true),
3547            active_parser_was_aborted: Cell::new(false),
3548            fired_unload: Cell::new(false),
3549            responsive_images: Default::default(),
3550            redirect_count: Cell::new(0),
3551            completely_loaded: Cell::new(false),
3552            script_and_layout_blockers: Cell::new(0),
3553            delayed_tasks: Default::default(),
3554            shadow_roots: DomRefCell::new(HashSet::new()),
3555            shadow_roots_styles_changed: Cell::new(false),
3556            media_controls: DomRefCell::new(HashMap::new()),
3557            dirty_2d_contexts: DomRefCell::new(HashMapTracedValues::new_fx()),
3558            dirty_webgl_contexts: DomRefCell::new(HashMapTracedValues::new_fx()),
3559            has_pending_animated_image_update: Cell::new(false),
3560            #[cfg(feature = "webgpu")]
3561            dirty_webgpu_contexts: DomRefCell::new(HashMapTracedValues::new_fx()),
3562            selection: MutNullableDom::new(None),
3563            animation_timeline: if pref!(layout_animations_test_enabled) {
3564                DomRefCell::new(AnimationTimeline::new_for_testing())
3565            } else {
3566                DomRefCell::new(AnimationTimeline::new())
3567            },
3568            animations: Animations::new(),
3569            image_animation_manager: DomRefCell::new(ImageAnimationManager::default()),
3570            dirty_root: Default::default(),
3571            declarative_refresh: Default::default(),
3572            resize_observers: Default::default(),
3573            fonts: Default::default(),
3574            visibility_state: Cell::new(DocumentVisibilityState::Hidden),
3575            status_code,
3576            is_initial_about_blank: Cell::new(is_initial_about_blank),
3577            allow_declarative_shadow_roots: Cell::new(allow_declarative_shadow_roots),
3578            inherited_insecure_requests_policy: Cell::new(inherited_insecure_requests_policy),
3579            has_trustworthy_ancestor_origin: Cell::new(has_trustworthy_ancestor_origin),
3580            intersection_observer_task_queued: Cell::new(false),
3581            intersection_observers: Default::default(),
3582            highlighted_dom_node: Default::default(),
3583            adopted_stylesheets: Default::default(),
3584            adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
3585            pending_scroll_event_targets: Default::default(),
3586            rendering_update_reasons: Default::default(),
3587            waiting_on_canvas_image_updates: Cell::new(false),
3588            current_rendering_epoch: Default::default(),
3589            custom_element_reaction_stack,
3590            active_sandboxing_flag_set: Cell::new(SandboxingFlagSet::empty()),
3591            creation_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
3592            favicon: RefCell::new(None),
3593            websockets: DOMTracker::new(),
3594        }
3595    }
3596
3597    /// Returns a policy value that should be used for fetches initiated by this document.
3598    pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
3599        if let Some(csp_list) = self.get_csp_list() {
3600            for policy in &csp_list.0 {
3601                if policy.contains_a_directive_whose_name_is("upgrade-insecure-requests") &&
3602                    policy.disposition == PolicyDisposition::Enforce
3603                {
3604                    return InsecureRequestsPolicy::Upgrade;
3605                }
3606            }
3607        }
3608
3609        self.inherited_insecure_requests_policy
3610            .get()
3611            .unwrap_or(InsecureRequestsPolicy::DoNotUpgrade)
3612    }
3613
3614    /// Get the [`Document`]'s [`DocumentEventHandler`].
3615    pub(crate) fn event_handler(&self) -> &DocumentEventHandler {
3616        &self.event_handler
3617    }
3618
3619    /// Get the [`Document`]'s [`DocumentEmbedderControls`].
3620    pub(crate) fn embedder_controls(&self) -> &DocumentEmbedderControls {
3621        &self.embedder_controls
3622    }
3623
3624    /// Whether or not this [`Document`] has any pending scroll events to be processed during
3625    /// "update the rendering."
3626    fn has_pending_scroll_events(&self) -> bool {
3627        !self.pending_scroll_event_targets.borrow().is_empty()
3628    }
3629
3630    /// Add a [`RenderingUpdateReason`] to this [`Document`] which will trigger a
3631    /// rendering update at a later time.
3632    pub(crate) fn add_rendering_update_reason(&self, reason: RenderingUpdateReason) {
3633        self.rendering_update_reasons
3634            .set(self.rendering_update_reasons.get().union(reason));
3635    }
3636
3637    /// Clear all [`RenderingUpdateReason`]s from this [`Document`].
3638    pub(crate) fn clear_rendering_update_reasons(&self) {
3639        self.rendering_update_reasons
3640            .set(RenderingUpdateReason::empty())
3641    }
3642
3643    /// Prevent any JS or layout from running until the corresponding call to
3644    /// `remove_script_and_layout_blocker`. Used to isolate periods in which
3645    /// the DOM is in an unstable state and should not be exposed to arbitrary
3646    /// web content. Any attempts to invoke content JS or query layout during
3647    /// that time will trigger a panic. `add_delayed_task` will cause the
3648    /// provided task to be executed as soon as the last blocker is removed.
3649    pub(crate) fn add_script_and_layout_blocker(&self) {
3650        self.script_and_layout_blockers
3651            .set(self.script_and_layout_blockers.get() + 1);
3652    }
3653
3654    /// Terminate the period in which JS or layout is disallowed from running.
3655    /// If no further blockers remain, any delayed tasks in the queue will
3656    /// be executed in queue order until the queue is empty.
3657    pub(crate) fn remove_script_and_layout_blocker(&self) {
3658        assert!(self.script_and_layout_blockers.get() > 0);
3659        self.script_and_layout_blockers
3660            .set(self.script_and_layout_blockers.get() - 1);
3661        while self.script_and_layout_blockers.get() == 0 && !self.delayed_tasks.borrow().is_empty()
3662        {
3663            let task = self.delayed_tasks.borrow_mut().remove(0);
3664            task.run_box();
3665        }
3666    }
3667
3668    /// Enqueue a task to run as soon as any JS and layout blockers are removed.
3669    pub(crate) fn add_delayed_task<T: 'static + NonSendTaskBox>(&self, task: T) {
3670        self.delayed_tasks.borrow_mut().push(Box::new(task));
3671    }
3672
3673    /// Assert that the DOM is in a state that will allow running content JS or
3674    /// performing a layout operation.
3675    pub(crate) fn ensure_safe_to_run_script_or_layout(&self) {
3676        assert_eq!(
3677            self.script_and_layout_blockers.get(),
3678            0,
3679            "Attempt to use script or layout while DOM not in a stable state"
3680        );
3681    }
3682
3683    #[allow(clippy::too_many_arguments)]
3684    pub(crate) fn new(
3685        window: &Window,
3686        has_browsing_context: HasBrowsingContext,
3687        url: Option<ServoUrl>,
3688        origin: MutableOrigin,
3689        doctype: IsHTMLDocument,
3690        content_type: Option<Mime>,
3691        last_modified: Option<String>,
3692        activity: DocumentActivity,
3693        source: DocumentSource,
3694        doc_loader: DocumentLoader,
3695        referrer: Option<String>,
3696        status_code: Option<u16>,
3697        canceller: FetchCanceller,
3698        is_initial_about_blank: bool,
3699        allow_declarative_shadow_roots: bool,
3700        inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
3701        has_trustworthy_ancestor_origin: bool,
3702        custom_element_reaction_stack: Rc<CustomElementReactionStack>,
3703        creation_sandboxing_flag_set: SandboxingFlagSet,
3704        can_gc: CanGc,
3705    ) -> DomRoot<Document> {
3706        Self::new_with_proto(
3707            window,
3708            None,
3709            has_browsing_context,
3710            url,
3711            origin,
3712            doctype,
3713            content_type,
3714            last_modified,
3715            activity,
3716            source,
3717            doc_loader,
3718            referrer,
3719            status_code,
3720            canceller,
3721            is_initial_about_blank,
3722            allow_declarative_shadow_roots,
3723            inherited_insecure_requests_policy,
3724            has_trustworthy_ancestor_origin,
3725            custom_element_reaction_stack,
3726            creation_sandboxing_flag_set,
3727            can_gc,
3728        )
3729    }
3730
3731    #[allow(clippy::too_many_arguments)]
3732    fn new_with_proto(
3733        window: &Window,
3734        proto: Option<HandleObject>,
3735        has_browsing_context: HasBrowsingContext,
3736        url: Option<ServoUrl>,
3737        origin: MutableOrigin,
3738        doctype: IsHTMLDocument,
3739        content_type: Option<Mime>,
3740        last_modified: Option<String>,
3741        activity: DocumentActivity,
3742        source: DocumentSource,
3743        doc_loader: DocumentLoader,
3744        referrer: Option<String>,
3745        status_code: Option<u16>,
3746        canceller: FetchCanceller,
3747        is_initial_about_blank: bool,
3748        allow_declarative_shadow_roots: bool,
3749        inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
3750        has_trustworthy_ancestor_origin: bool,
3751        custom_element_reaction_stack: Rc<CustomElementReactionStack>,
3752        creation_sandboxing_flag_set: SandboxingFlagSet,
3753        can_gc: CanGc,
3754    ) -> DomRoot<Document> {
3755        let document = reflect_dom_object_with_proto(
3756            Box::new(Document::new_inherited(
3757                window,
3758                has_browsing_context,
3759                url,
3760                origin,
3761                doctype,
3762                content_type,
3763                last_modified,
3764                activity,
3765                source,
3766                doc_loader,
3767                referrer,
3768                status_code,
3769                canceller,
3770                is_initial_about_blank,
3771                allow_declarative_shadow_roots,
3772                inherited_insecure_requests_policy,
3773                has_trustworthy_ancestor_origin,
3774                custom_element_reaction_stack,
3775                creation_sandboxing_flag_set,
3776            )),
3777            window,
3778            proto,
3779            can_gc,
3780        );
3781        {
3782            let node = document.upcast::<Node>();
3783            node.set_owner_doc(&document);
3784        }
3785        document
3786    }
3787
3788    pub(crate) fn get_redirect_count(&self) -> u16 {
3789        self.redirect_count.get()
3790    }
3791
3792    pub(crate) fn set_redirect_count(&self, count: u16) {
3793        self.redirect_count.set(count)
3794    }
3795
3796    pub(crate) fn elements_by_name_count(&self, name: &DOMString) -> u32 {
3797        if name.is_empty() {
3798            return 0;
3799        }
3800        self.count_node_list(|n| Document::is_element_in_get_by_name(n, name))
3801    }
3802
3803    pub(crate) fn nth_element_by_name(
3804        &self,
3805        index: u32,
3806        name: &DOMString,
3807    ) -> Option<DomRoot<Node>> {
3808        if name.is_empty() {
3809            return None;
3810        }
3811        self.nth_in_node_list(index, |n| Document::is_element_in_get_by_name(n, name))
3812    }
3813
3814    // Note that document.getByName does not match on the same conditions
3815    // as the document named getter.
3816    fn is_element_in_get_by_name(node: &Node, name: &DOMString) -> bool {
3817        let element = match node.downcast::<Element>() {
3818            Some(element) => element,
3819            None => return false,
3820        };
3821        if element.namespace() != &ns!(html) {
3822            return false;
3823        }
3824        element.get_name().is_some_and(|n| &*n == name)
3825    }
3826
3827    fn count_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> u32 {
3828        let doc = self.GetDocumentElement();
3829        let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
3830        maybe_node
3831            .iter()
3832            .flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
3833            .filter(|node| callback(node))
3834            .count() as u32
3835    }
3836
3837    fn nth_in_node_list<F: Fn(&Node) -> bool>(
3838        &self,
3839        index: u32,
3840        callback: F,
3841    ) -> Option<DomRoot<Node>> {
3842        let doc = self.GetDocumentElement();
3843        let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
3844        maybe_node
3845            .iter()
3846            .flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
3847            .filter(|node| callback(node))
3848            .nth(index as usize)
3849            .map(|n| DomRoot::from_ref(&*n))
3850    }
3851
3852    fn get_html_element(&self) -> Option<DomRoot<HTMLHtmlElement>> {
3853        self.GetDocumentElement().and_then(DomRoot::downcast)
3854    }
3855
3856    /// Return a reference to the per-document shared lock used in stylesheets.
3857    pub(crate) fn style_shared_lock(&self) -> &StyleSharedRwLock {
3858        &self.style_shared_lock
3859    }
3860
3861    /// Flushes the stylesheet list, and returns whether any stylesheet changed.
3862    pub(crate) fn flush_stylesheets_for_reflow(&self) -> bool {
3863        // NOTE(emilio): The invalidation machinery is used on the replicated
3864        // list in layout.
3865        //
3866        // FIXME(emilio): This really should differentiate between CSSOM changes
3867        // and normal stylesheets additions / removals, because in the last case
3868        // layout already has that information and we could avoid dirtying the whole thing.
3869        let mut stylesheets = self.stylesheets.borrow_mut();
3870        let have_changed = stylesheets.has_changed();
3871        stylesheets.flush_without_invalidation();
3872        have_changed
3873    }
3874
3875    pub(crate) fn salvageable(&self) -> bool {
3876        self.salvageable.get()
3877    }
3878
3879    /// <https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document>
3880    pub(crate) fn appropriate_template_contents_owner_document(
3881        &self,
3882        can_gc: CanGc,
3883    ) -> DomRoot<Document> {
3884        self.appropriate_template_contents_owner_document
3885            .or_init(|| {
3886                let doctype = if self.is_html_document {
3887                    IsHTMLDocument::HTMLDocument
3888                } else {
3889                    IsHTMLDocument::NonHTMLDocument
3890                };
3891                let new_doc = Document::new(
3892                    self.window(),
3893                    HasBrowsingContext::No,
3894                    None,
3895                    // https://github.com/whatwg/html/issues/2109
3896                    MutableOrigin::new(ImmutableOrigin::new_opaque()),
3897                    doctype,
3898                    None,
3899                    None,
3900                    DocumentActivity::Inactive,
3901                    DocumentSource::NotFromParser,
3902                    DocumentLoader::new(&self.loader()),
3903                    None,
3904                    None,
3905                    Default::default(),
3906                    false,
3907                    self.allow_declarative_shadow_roots(),
3908                    Some(self.insecure_requests_policy()),
3909                    self.has_trustworthy_ancestor_or_current_origin(),
3910                    self.custom_element_reaction_stack.clone(),
3911                    self.creation_sandboxing_flag_set(),
3912                    can_gc,
3913                );
3914                new_doc
3915                    .appropriate_template_contents_owner_document
3916                    .set(Some(&new_doc));
3917                new_doc
3918            })
3919    }
3920
3921    pub(crate) fn get_element_by_id(&self, id: &Atom) -> Option<DomRoot<Element>> {
3922        self.id_map
3923            .borrow()
3924            .get(id)
3925            .map(|elements| DomRoot::from_ref(&*elements[0]))
3926    }
3927
3928    pub(crate) fn ensure_pending_restyle(&self, el: &Element) -> RefMut<'_, PendingRestyle> {
3929        let map = self.pending_restyles.borrow_mut();
3930        RefMut::map(map, |m| {
3931            &mut m
3932                .entry(Dom::from_ref(el))
3933                .or_insert_with(|| NoTrace(PendingRestyle::default()))
3934                .0
3935        })
3936    }
3937
3938    pub(crate) fn element_attr_will_change(&self, el: &Element, attr: &Attr) {
3939        // FIXME(emilio): Kind of a shame we have to duplicate this.
3940        //
3941        // I'm getting rid of the whole hashtable soon anyway, since all it does
3942        // right now is populate the element restyle data in layout, and we
3943        // could in theory do it in the DOM I think.
3944        let mut entry = self.ensure_pending_restyle(el);
3945        if entry.snapshot.is_none() {
3946            entry.snapshot = Some(Snapshot::new());
3947        }
3948        if attr.local_name() == &local_name!("style") {
3949            entry.hint.insert(RestyleHint::RESTYLE_STYLE_ATTRIBUTE);
3950        }
3951
3952        if vtable_for(el.upcast()).attribute_affects_presentational_hints(attr) {
3953            entry.hint.insert(RestyleHint::RESTYLE_SELF);
3954        }
3955
3956        let snapshot = entry.snapshot.as_mut().unwrap();
3957        if attr.local_name() == &local_name!("id") {
3958            if snapshot.id_changed {
3959                return;
3960            }
3961            snapshot.id_changed = true;
3962        } else if attr.local_name() == &local_name!("class") {
3963            if snapshot.class_changed {
3964                return;
3965            }
3966            snapshot.class_changed = true;
3967        } else {
3968            snapshot.other_attributes_changed = true;
3969        }
3970        let local_name = style::LocalName::cast(attr.local_name());
3971        if !snapshot.changed_attrs.contains(local_name) {
3972            snapshot.changed_attrs.push(local_name.clone());
3973        }
3974        if snapshot.attrs.is_none() {
3975            let attrs = el
3976                .attrs()
3977                .iter()
3978                .map(|attr| (attr.identifier().clone(), attr.value().clone()))
3979                .collect();
3980            snapshot.attrs = Some(attrs);
3981        }
3982    }
3983
3984    pub(crate) fn set_referrer_policy(&self, policy: ReferrerPolicy) {
3985        self.policy_container
3986            .borrow_mut()
3987            .set_referrer_policy(policy);
3988    }
3989
3990    pub(crate) fn get_referrer_policy(&self) -> ReferrerPolicy {
3991        self.policy_container.borrow().get_referrer_policy()
3992    }
3993
3994    pub(crate) fn set_target_element(&self, node: Option<&Element>) {
3995        if let Some(ref element) = self.target_element.get() {
3996            element.set_target_state(false);
3997        }
3998
3999        self.target_element.set(node);
4000
4001        if let Some(ref element) = self.target_element.get() {
4002            element.set_target_state(true);
4003        }
4004    }
4005
4006    pub(crate) fn incr_ignore_destructive_writes_counter(&self) {
4007        self.ignore_destructive_writes_counter
4008            .set(self.ignore_destructive_writes_counter.get() + 1);
4009    }
4010
4011    pub(crate) fn decr_ignore_destructive_writes_counter(&self) {
4012        self.ignore_destructive_writes_counter
4013            .set(self.ignore_destructive_writes_counter.get() - 1);
4014    }
4015
4016    pub(crate) fn is_prompting_or_unloading(&self) -> bool {
4017        self.ignore_opens_during_unload_counter.get() > 0
4018    }
4019
4020    fn incr_ignore_opens_during_unload_counter(&self) {
4021        self.ignore_opens_during_unload_counter
4022            .set(self.ignore_opens_during_unload_counter.get() + 1);
4023    }
4024
4025    fn decr_ignore_opens_during_unload_counter(&self) {
4026        self.ignore_opens_during_unload_counter
4027            .set(self.ignore_opens_during_unload_counter.get() - 1);
4028    }
4029
4030    // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
4031    pub(crate) fn enter_fullscreen(&self, pending: &Element, can_gc: CanGc) -> Rc<Promise> {
4032        // Step 1
4033        let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>();
4034        let promise = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc);
4035        let mut error = false;
4036
4037        // Step 4
4038        // check namespace
4039        match *pending.namespace() {
4040            ns!(mathml) => {
4041                if pending.local_name().as_ref() != "math" {
4042                    error = true;
4043                }
4044            },
4045            ns!(svg) => {
4046                if pending.local_name().as_ref() != "svg" {
4047                    error = true;
4048                }
4049            },
4050            ns!(html) => (),
4051            _ => error = true,
4052        }
4053        // fullscreen element ready check
4054        if !pending.fullscreen_element_ready_check() {
4055            error = true;
4056        }
4057
4058        if pref!(dom_fullscreen_test) {
4059            // For reftests we just take over the current window,
4060            // and don't try to really enter fullscreen.
4061            info!("Tests don't really enter fullscreen.");
4062        } else {
4063            // TODO fullscreen is supported
4064            // TODO This algorithm is allowed to request fullscreen.
4065            warn!("Fullscreen not supported yet");
4066        }
4067
4068        // Step 5 Parallel start
4069
4070        let window = self.window();
4071        // Step 6
4072        if !error {
4073            let event = EmbedderMsg::NotifyFullscreenStateChanged(self.webview_id(), true);
4074            self.send_to_embedder(event);
4075        }
4076
4077        let pipeline_id = self.window().pipeline_id();
4078
4079        // Step 7
4080        let trusted_pending = Trusted::new(pending);
4081        let trusted_promise = TrustedPromise::new(promise.clone());
4082        let handler = ElementPerformFullscreenEnter::new(trusted_pending, trusted_promise, error);
4083        // NOTE: This steps should be running in parallel
4084        // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
4085        let script_msg = CommonScriptMsg::Task(
4086            ScriptThreadEventCategory::EnterFullscreen,
4087            handler,
4088            Some(pipeline_id),
4089            TaskSourceName::DOMManipulation,
4090        );
4091        let msg = MainThreadScriptMsg::Common(script_msg);
4092        window.main_thread_script_chan().send(msg).unwrap();
4093
4094        promise
4095    }
4096
4097    // https://fullscreen.spec.whatwg.org/#exit-fullscreen
4098    pub(crate) fn exit_fullscreen(&self, can_gc: CanGc) -> Rc<Promise> {
4099        let global = self.global();
4100        // Step 1
4101        let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>();
4102        let promise = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc);
4103        // Step 2
4104        if self.fullscreen_element.get().is_none() {
4105            promise.reject_error(Error::Type(String::from("fullscreen is null")), can_gc);
4106            return promise;
4107        }
4108        // TODO Step 3-6
4109        let element = self.fullscreen_element.get().unwrap();
4110
4111        // Step 7 Parallel start
4112
4113        let window = self.window();
4114        // Step 8
4115        let event = EmbedderMsg::NotifyFullscreenStateChanged(self.webview_id(), false);
4116        self.send_to_embedder(event);
4117
4118        // Step 9
4119        let trusted_element = Trusted::new(&*element);
4120        let trusted_promise = TrustedPromise::new(promise.clone());
4121        let handler = ElementPerformFullscreenExit::new(trusted_element, trusted_promise);
4122        let pipeline_id = Some(global.pipeline_id());
4123        // NOTE: This steps should be running in parallel
4124        // https://fullscreen.spec.whatwg.org/#exit-fullscreen
4125        let script_msg = CommonScriptMsg::Task(
4126            ScriptThreadEventCategory::ExitFullscreen,
4127            handler,
4128            pipeline_id,
4129            TaskSourceName::DOMManipulation,
4130        );
4131        let msg = MainThreadScriptMsg::Common(script_msg);
4132        window.main_thread_script_chan().send(msg).unwrap();
4133
4134        promise
4135    }
4136
4137    pub(crate) fn set_fullscreen_element(&self, element: Option<&Element>) {
4138        self.fullscreen_element.set(element);
4139    }
4140
4141    pub(crate) fn get_allow_fullscreen(&self) -> bool {
4142        // https://html.spec.whatwg.org/multipage/#allowed-to-use
4143        match self.browsing_context() {
4144            // Step 1
4145            None => false,
4146            Some(_) => {
4147                // Step 2
4148                let window = self.window();
4149                if window.is_top_level() {
4150                    true
4151                } else {
4152                    // Step 3
4153                    window
4154                        .GetFrameElement()
4155                        .is_some_and(|el| el.has_attribute(&local_name!("allowfullscreen")))
4156                }
4157            },
4158        }
4159    }
4160
4161    fn reset_form_owner_for_listeners(&self, id: &Atom, can_gc: CanGc) {
4162        let map = self.form_id_listener_map.borrow();
4163        if let Some(listeners) = map.get(id) {
4164            for listener in listeners {
4165                listener
4166                    .as_maybe_form_control()
4167                    .expect("Element must be a form control")
4168                    .reset_form_owner(can_gc);
4169            }
4170        }
4171    }
4172
4173    pub(crate) fn register_shadow_root(&self, shadow_root: &ShadowRoot) {
4174        self.shadow_roots
4175            .borrow_mut()
4176            .insert(Dom::from_ref(shadow_root));
4177        self.invalidate_shadow_roots_stylesheets();
4178    }
4179
4180    pub(crate) fn unregister_shadow_root(&self, shadow_root: &ShadowRoot) {
4181        let mut shadow_roots = self.shadow_roots.borrow_mut();
4182        shadow_roots.remove(&Dom::from_ref(shadow_root));
4183    }
4184
4185    pub(crate) fn invalidate_shadow_roots_stylesheets(&self) {
4186        self.shadow_roots_styles_changed.set(true);
4187    }
4188
4189    pub(crate) fn shadow_roots_styles_changed(&self) -> bool {
4190        self.shadow_roots_styles_changed.get()
4191    }
4192
4193    pub(crate) fn flush_shadow_roots_stylesheets(&self) {
4194        if !self.shadow_roots_styles_changed.get() {
4195            return;
4196        }
4197        self.shadow_roots_styles_changed.set(false);
4198    }
4199
4200    pub(crate) fn stylesheet_count(&self) -> usize {
4201        self.stylesheets.borrow().len()
4202    }
4203
4204    pub(crate) fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
4205        let stylesheets = self.stylesheets.borrow();
4206
4207        stylesheets
4208            .get(Origin::Author, index)
4209            .and_then(|s| s.owner.get_cssom_object())
4210    }
4211
4212    /// Add a stylesheet owned by `owner_node` to the list of document sheets, in the
4213    /// correct tree position. Additionally, ensure that the owned stylesheet is inserted
4214    /// before any constructed stylesheet.
4215    ///
4216    /// <https://drafts.csswg.org/cssom/#documentorshadowroot-final-css-style-sheets>
4217    #[cfg_attr(crown, allow(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily.
4218    pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) {
4219        let stylesheets = &mut *self.stylesheets.borrow_mut();
4220
4221        // FIXME(stevennovaryo): This is almost identical with the one in ShadowRoot::add_stylesheet.
4222        let insertion_point = stylesheets
4223            .iter()
4224            .map(|(sheet, _origin)| sheet)
4225            .find(|sheet_in_doc| {
4226                match &sheet_in_doc.owner {
4227                    StylesheetSource::Element(other_node) => {
4228                        owner_node.upcast::<Node>().is_before(other_node.upcast())
4229                    },
4230                    // Non-constructed stylesheet should be ordered before the
4231                    // constructed ones.
4232                    StylesheetSource::Constructed(_) => true,
4233                }
4234            })
4235            .cloned();
4236
4237        if self.has_browsing_context() {
4238            self.window.layout_mut().add_stylesheet(
4239                sheet.clone(),
4240                insertion_point.as_ref().map(|s| s.sheet.clone()),
4241            );
4242        }
4243
4244        DocumentOrShadowRoot::add_stylesheet(
4245            StylesheetSource::Element(Dom::from_ref(owner_node)),
4246            StylesheetSetRef::Document(stylesheets),
4247            sheet,
4248            insertion_point,
4249            self.style_shared_lock(),
4250        );
4251    }
4252
4253    /// Append a constructed stylesheet to the back of document stylesheet set. Because
4254    /// it would be the last element, we therefore would not mess with the ordering.
4255    ///
4256    /// <https://drafts.csswg.org/cssom/#documentorshadowroot-final-css-style-sheets>
4257    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
4258    pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
4259        debug_assert!(cssom_stylesheet.is_constructed());
4260
4261        let stylesheets = &mut *self.stylesheets.borrow_mut();
4262        let sheet = cssom_stylesheet.style_stylesheet().clone();
4263
4264        let insertion_point = stylesheets
4265            .iter()
4266            .last()
4267            .map(|(sheet, _origin)| sheet)
4268            .cloned();
4269
4270        if self.has_browsing_context() {
4271            self.window.layout_mut().add_stylesheet(
4272                sheet.clone(),
4273                insertion_point.as_ref().map(|s| s.sheet.clone()),
4274            );
4275        }
4276
4277        DocumentOrShadowRoot::add_stylesheet(
4278            StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
4279            StylesheetSetRef::Document(stylesheets),
4280            sheet,
4281            insertion_point,
4282            self.style_shared_lock(),
4283        );
4284    }
4285
4286    /// Given a stylesheet, load all web fonts from it in Layout.
4287    pub(crate) fn load_web_fonts_from_stylesheet(&self, stylesheet: &Arc<Stylesheet>) {
4288        self.window
4289            .layout()
4290            .load_web_fonts_from_stylesheet(stylesheet);
4291    }
4292
4293    /// Remove a stylesheet owned by `owner` from the list of document sheets.
4294    #[cfg_attr(crown, allow(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily.
4295    pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, stylesheet: &Arc<Stylesheet>) {
4296        if self.has_browsing_context() {
4297            self.window
4298                .layout_mut()
4299                .remove_stylesheet(stylesheet.clone());
4300        }
4301
4302        DocumentOrShadowRoot::remove_stylesheet(
4303            owner,
4304            stylesheet,
4305            StylesheetSetRef::Document(&mut *self.stylesheets.borrow_mut()),
4306        )
4307    }
4308
4309    pub(crate) fn get_elements_with_id(&self, id: &Atom) -> Ref<'_, [Dom<Element>]> {
4310        Ref::map(self.id_map.borrow(), |map| {
4311            map.get(id).map(|vec| &**vec).unwrap_or_default()
4312        })
4313    }
4314
4315    pub(crate) fn get_elements_with_name(&self, name: &Atom) -> Ref<'_, [Dom<Element>]> {
4316        Ref::map(self.name_map.borrow(), |map| {
4317            map.get(name).map(|vec| &**vec).unwrap_or_default()
4318        })
4319    }
4320
4321    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
4322    pub(crate) fn drain_pending_restyles(&self) -> Vec<(TrustedNodeAddress, PendingRestyle)> {
4323        self.pending_restyles
4324            .borrow_mut()
4325            .drain()
4326            .filter_map(|(elem, restyle)| {
4327                let node = elem.upcast::<Node>();
4328                if !node.get_flag(NodeFlags::IS_CONNECTED) {
4329                    return None;
4330                }
4331                node.note_dirty_descendants();
4332                Some((node.to_trusted_node_address(), restyle.0))
4333            })
4334            .collect()
4335    }
4336
4337    pub(crate) fn advance_animation_timeline_for_testing(&self, delta: f64) {
4338        self.animation_timeline.borrow_mut().advance_specific(delta);
4339        let current_timeline_value = self.current_animation_timeline_value();
4340        self.animations
4341            .update_for_new_timeline_value(&self.window, current_timeline_value);
4342    }
4343
4344    pub(crate) fn maybe_mark_animating_nodes_as_dirty(&self) {
4345        let current_timeline_value = self.current_animation_timeline_value();
4346        self.animations
4347            .mark_animating_nodes_as_dirty(current_timeline_value);
4348    }
4349
4350    pub(crate) fn current_animation_timeline_value(&self) -> f64 {
4351        self.animation_timeline.borrow().current_value()
4352    }
4353
4354    pub(crate) fn animations(&self) -> &Animations {
4355        &self.animations
4356    }
4357
4358    pub(crate) fn update_animations_post_reflow(&self) {
4359        self.animations
4360            .do_post_reflow_update(&self.window, self.current_animation_timeline_value());
4361        self.image_animation_manager
4362            .borrow()
4363            .maybe_schedule_update_after_layout(
4364                &self.window,
4365                self.current_animation_timeline_value(),
4366            );
4367    }
4368
4369    pub(crate) fn cancel_animations_for_node(&self, node: &Node) {
4370        self.animations.cancel_animations_for_node(node);
4371        self.image_animation_manager
4372            .borrow()
4373            .cancel_animations_for_node(node);
4374    }
4375
4376    /// An implementation of <https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events>.
4377    pub(crate) fn update_animations_and_send_events(&self, can_gc: CanGc) {
4378        // Only update the time if it isn't being managed by a test.
4379        if !pref!(layout_animations_test_enabled) {
4380            self.animation_timeline.borrow_mut().update();
4381        }
4382
4383        // > 1. Update the current time of all timelines associated with doc passing now
4384        // > as the timestamp.
4385        // > 2. Remove replaced animations for doc.
4386        //
4387        // We still want to update the animations, because our timeline
4388        // value might have been advanced previously via the TestBinding.
4389        let current_timeline_value = self.current_animation_timeline_value();
4390        self.animations
4391            .update_for_new_timeline_value(&self.window, current_timeline_value);
4392        self.maybe_mark_animating_nodes_as_dirty();
4393
4394        // > 3. Perform a microtask checkpoint.
4395        self.window().perform_a_microtask_checkpoint(can_gc);
4396
4397        // Steps 4 through 7 occur inside `send_pending_events().`
4398        let _realm = enter_realm(self);
4399        self.animations().send_pending_events(self.window(), can_gc);
4400    }
4401
4402    pub(crate) fn image_animation_manager(&self) -> Ref<'_, ImageAnimationManager> {
4403        self.image_animation_manager.borrow()
4404    }
4405
4406    pub(crate) fn set_has_pending_animated_image_update(&self) {
4407        self.has_pending_animated_image_update.set(true);
4408    }
4409
4410    /// <https://html.spec.whatwg.org/multipage/#shared-declarative-refresh-steps>
4411    pub(crate) fn shared_declarative_refresh_steps(&self, content: &[u8]) {
4412        // 1. If document's will declaratively refresh is true, then return.
4413        if self.will_declaratively_refresh() {
4414            return;
4415        }
4416
4417        // 2-11 Parsing
4418        static REFRESH_REGEX: LazyLock<Regex> = LazyLock::new(|| {
4419            // s flag is used to match . on newlines since the only places we use . in the
4420            // regex is to go "to end of the string"
4421            // (?s-u:.) is used to consume invalid unicode bytes
4422            Regex::new(
4423                r#"(?xs)
4424                    ^
4425                    \s* # 3
4426                    ((?<time>[0-9]+)|\.) # 5-6
4427                    [0-9.]* # 8
4428                    (
4429                        (
4430                            (\s*;|\s*,|\s) # 10.3
4431                            \s* # 10.4
4432                        )
4433                        (
4434                            (
4435                                (U|u)(R|r)(L|l) # 11.2-11.4
4436                                \s*=\s* # 11.5-11.7
4437                            )?
4438                        ('(?<url1>[^']*)'(?s-u:.)*|"(?<url2>[^"]*)"(?s-u:.)*|['"]?(?<url3>(?s-u:.)*)) # 11.8 - 11.10
4439                        |
4440                        (?<url4>(?s-u:.)*)
4441                    )
4442                )?
4443                $
4444            "#,
4445            )
4446            .unwrap()
4447        });
4448
4449        // 9. Let urlRecord be document's URL.
4450        let mut url_record = self.url();
4451        let captures = if let Some(captures) = REFRESH_REGEX.captures(content) {
4452            captures
4453        } else {
4454            return;
4455        };
4456        let time = if let Some(time_string) = captures.name("time") {
4457            u64::from_str(&String::from_utf8_lossy(time_string.as_bytes())).unwrap_or(0)
4458        } else {
4459            0
4460        };
4461        let captured_url = captures.name("url1").or(captures
4462            .name("url2")
4463            .or(captures.name("url3").or(captures.name("url4"))));
4464
4465        // 11.11 Parse: Set urlRecord to the result of encoding-parsing a URL given urlString, relative to document.
4466        if let Some(url_match) = captured_url {
4467            url_record = if let Ok(url) = ServoUrl::parse_with_base(
4468                Some(&url_record),
4469                &String::from_utf8_lossy(url_match.as_bytes()),
4470            ) {
4471                info!("Refresh to {}", url.debug_compact());
4472                url
4473            } else {
4474                // 11.12 If urlRecord is failure, then return.
4475                return;
4476            }
4477        }
4478        // 12. Set document's will declaratively refresh to true.
4479        if self.completely_loaded() {
4480            // TODO: handle active sandboxing flag
4481            self.window.as_global_scope().schedule_callback(
4482                OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
4483                    window: DomRoot::from_ref(self.window()),
4484                    url: url_record,
4485                }),
4486                Duration::from_secs(time),
4487            );
4488            self.set_declarative_refresh(DeclarativeRefresh::CreatedAfterLoad);
4489        } else {
4490            self.set_declarative_refresh(DeclarativeRefresh::PendingLoad {
4491                url: url_record,
4492                time,
4493            });
4494        }
4495    }
4496
4497    pub(crate) fn will_declaratively_refresh(&self) -> bool {
4498        self.declarative_refresh.borrow().is_some()
4499    }
4500    pub(crate) fn set_declarative_refresh(&self, refresh: DeclarativeRefresh) {
4501        *self.declarative_refresh.borrow_mut() = Some(refresh);
4502    }
4503
4504    /// <https://html.spec.whatwg.org/multipage/#visibility-state>
4505    fn update_visibility_state(&self, visibility_state: DocumentVisibilityState, can_gc: CanGc) {
4506        // Step 1 If document's visibility state equals visibilityState, then return.
4507        if self.visibility_state.get() == visibility_state {
4508            return;
4509        }
4510        // Step 2 Set document's visibility state to visibilityState.
4511        self.visibility_state.set(visibility_state);
4512        // Step 3 Queue a new VisibilityStateEntry whose visibility state is visibilityState and whose timestamp is
4513        // the current high resolution time given document's relevant global object.
4514        let entry = VisibilityStateEntry::new(
4515            &self.global(),
4516            visibility_state,
4517            CrossProcessInstant::now(),
4518            can_gc,
4519        );
4520        self.window
4521            .Performance()
4522            .queue_entry(entry.upcast::<PerformanceEntry>(), can_gc);
4523
4524        // Step 4 Run the screen orientation change steps with document.
4525        // TODO ScreenOrientation hasn't implemented yet
4526
4527        // Step 5 Run the view transition page visibility change steps with document.
4528        // TODO ViewTransition hasn't implemented yet
4529
4530        // Step 6 Run any page visibility change steps which may be defined in other specifications, with visibility
4531        // state and document. Any other specs' visibility steps will go here.
4532
4533        // <https://www.w3.org/TR/gamepad/#handling-visibility-change>
4534        if visibility_state == DocumentVisibilityState::Hidden {
4535            self.window
4536                .Navigator()
4537                .GetGamepads()
4538                .iter_mut()
4539                .for_each(|gamepad| {
4540                    if let Some(g) = gamepad {
4541                        g.vibration_actuator().handle_visibility_change();
4542                    }
4543                });
4544        }
4545
4546        // Step 7 Fire an event named visibilitychange at document, with its bubbles attribute initialized to true.
4547        self.upcast::<EventTarget>()
4548            .fire_bubbling_event(atom!("visibilitychange"), can_gc);
4549    }
4550
4551    /// <https://html.spec.whatwg.org/multipage/#is-initial-about:blank>
4552    pub(crate) fn is_initial_about_blank(&self) -> bool {
4553        self.is_initial_about_blank.get()
4554    }
4555
4556    /// <https://dom.spec.whatwg.org/#document-allow-declarative-shadow-roots>
4557    pub(crate) fn allow_declarative_shadow_roots(&self) -> bool {
4558        self.allow_declarative_shadow_roots.get()
4559    }
4560
4561    pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
4562        self.has_trustworthy_ancestor_origin.get()
4563    }
4564
4565    pub(crate) fn has_trustworthy_ancestor_or_current_origin(&self) -> bool {
4566        self.has_trustworthy_ancestor_origin.get() ||
4567            self.origin().immutable().is_potentially_trustworthy()
4568    }
4569
4570    pub(crate) fn highlight_dom_node(&self, node: Option<&Node>) {
4571        self.highlighted_dom_node.set(node);
4572        self.add_restyle_reason(RestyleReason::HighlightedDOMNodeChanged);
4573    }
4574
4575    pub(crate) fn highlighted_dom_node(&self) -> Option<DomRoot<Node>> {
4576        self.highlighted_dom_node.get()
4577    }
4578
4579    pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
4580        self.custom_element_reaction_stack.clone()
4581    }
4582
4583    pub(crate) fn has_active_sandboxing_flag(&self, flag: SandboxingFlagSet) -> bool {
4584        self.active_sandboxing_flag_set.get().contains(flag)
4585    }
4586
4587    pub(crate) fn set_active_sandboxing_flag_set(&self, flags: SandboxingFlagSet) {
4588        self.active_sandboxing_flag_set.set(flags)
4589    }
4590
4591    pub(crate) fn creation_sandboxing_flag_set(&self) -> SandboxingFlagSet {
4592        self.creation_sandboxing_flag_set.get()
4593    }
4594
4595    pub(crate) fn creation_sandboxing_flag_set_considering_parent_iframe(
4596        &self,
4597    ) -> SandboxingFlagSet {
4598        self.window()
4599            .window_proxy()
4600            .frame_element()
4601            .and_then(|element| element.downcast::<HTMLIFrameElement>())
4602            .map(HTMLIFrameElement::sandboxing_flag_set)
4603            .unwrap_or_else(|| self.creation_sandboxing_flag_set())
4604    }
4605
4606    pub(crate) fn viewport_scrolling_box(&self, flags: ScrollContainerQueryFlags) -> ScrollingBox {
4607        self.window()
4608            .scrolling_box_query(None, flags)
4609            .expect("We should always have a ScrollingBox for the Viewport")
4610    }
4611
4612    pub(crate) fn notify_embedder_favicon(&self) {
4613        if let Some(ref image) = *self.favicon.borrow() {
4614            self.send_to_embedder(EmbedderMsg::NewFavicon(self.webview_id(), image.clone()));
4615        }
4616    }
4617
4618    pub(crate) fn set_favicon(&self, favicon: Image) {
4619        *self.favicon.borrow_mut() = Some(favicon);
4620        self.notify_embedder_favicon();
4621    }
4622}
4623
4624#[allow(non_snake_case)]
4625impl DocumentMethods<crate::DomTypeHolder> for Document {
4626    /// <https://dom.spec.whatwg.org/#dom-document-document>
4627    fn Constructor(
4628        window: &Window,
4629        proto: Option<HandleObject>,
4630        can_gc: CanGc,
4631    ) -> Fallible<DomRoot<Document>> {
4632        let doc = window.Document();
4633        let docloader = DocumentLoader::new(&doc.loader());
4634        Ok(Document::new_with_proto(
4635            window,
4636            proto,
4637            HasBrowsingContext::No,
4638            None,
4639            doc.origin().clone(),
4640            IsHTMLDocument::NonHTMLDocument,
4641            None,
4642            None,
4643            DocumentActivity::Inactive,
4644            DocumentSource::NotFromParser,
4645            docloader,
4646            None,
4647            None,
4648            Default::default(),
4649            false,
4650            doc.allow_declarative_shadow_roots(),
4651            Some(doc.insecure_requests_policy()),
4652            doc.has_trustworthy_ancestor_or_current_origin(),
4653            doc.custom_element_reaction_stack(),
4654            doc.active_sandboxing_flag_set.get(),
4655            can_gc,
4656        ))
4657    }
4658
4659    /// <https://html.spec.whatwg.org/multipage/#dom-parsehtmlunsafe>
4660    fn ParseHTMLUnsafe(
4661        window: &Window,
4662        s: TrustedHTMLOrString,
4663        can_gc: CanGc,
4664    ) -> Fallible<DomRoot<Self>> {
4665        // Step 1. Let compliantHTML be the result of invoking the
4666        // Get Trusted Type compliant string algorithm with TrustedHTML, the current global object,
4667        // html, "Document parseHTMLUnsafe", and "script".
4668        let compliant_html = TrustedHTML::get_trusted_script_compliant_string(
4669            window.as_global_scope(),
4670            s,
4671            "Document parseHTMLUnsafe",
4672            can_gc,
4673        )?;
4674
4675        let url = window.get_url();
4676        let doc = window.Document();
4677        let loader = DocumentLoader::new(&doc.loader());
4678
4679        let content_type = "text/html"
4680            .parse()
4681            .expect("Supported type is not a MIME type");
4682        // Step 2. Let document be a new Document, whose content type is "text/html".
4683        // Step 3. Set document's allow declarative shadow roots to true.
4684        let document = Document::new(
4685            window,
4686            HasBrowsingContext::No,
4687            Some(ServoUrl::parse("about:blank").unwrap()),
4688            doc.origin().clone(),
4689            IsHTMLDocument::HTMLDocument,
4690            Some(content_type),
4691            None,
4692            DocumentActivity::Inactive,
4693            DocumentSource::FromParser,
4694            loader,
4695            None,
4696            None,
4697            Default::default(),
4698            false,
4699            true,
4700            Some(doc.insecure_requests_policy()),
4701            doc.has_trustworthy_ancestor_or_current_origin(),
4702            doc.custom_element_reaction_stack(),
4703            doc.creation_sandboxing_flag_set(),
4704            can_gc,
4705        );
4706        // Step 4. Parse HTML from string given document and compliantHTML.
4707        ServoParser::parse_html_document(&document, Some(compliant_html), url, can_gc);
4708        // Step 5. Return document.
4709        document.set_ready_state(DocumentReadyState::Complete, can_gc);
4710        Ok(document)
4711    }
4712
4713    /// <https://w3c.github.io/editing/ActiveDocuments/execCommand.html#querycommandsupported()>
4714    fn QueryCommandSupported(&self, _command: DOMString) -> bool {
4715        false
4716    }
4717
4718    /// <https://drafts.csswg.org/cssom/#dom-document-stylesheets>
4719    fn StyleSheets(&self, can_gc: CanGc) -> DomRoot<StyleSheetList> {
4720        self.stylesheet_list.or_init(|| {
4721            StyleSheetList::new(
4722                &self.window,
4723                StyleSheetListOwner::Document(Dom::from_ref(self)),
4724                can_gc,
4725            )
4726        })
4727    }
4728
4729    /// <https://dom.spec.whatwg.org/#dom-document-implementation>
4730    fn Implementation(&self, can_gc: CanGc) -> DomRoot<DOMImplementation> {
4731        self.implementation
4732            .or_init(|| DOMImplementation::new(self, can_gc))
4733    }
4734
4735    /// <https://dom.spec.whatwg.org/#dom-document-url>
4736    fn URL(&self) -> USVString {
4737        USVString(String::from(self.url().as_str()))
4738    }
4739
4740    /// <https://html.spec.whatwg.org/multipage/#dom-document-activeelement>
4741    fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
4742        self.document_or_shadow_root.get_active_element(
4743            self.get_focused_element(),
4744            self.GetBody(),
4745            self.GetDocumentElement(),
4746        )
4747    }
4748
4749    /// <https://html.spec.whatwg.org/multipage/#dom-document-hasfocus>
4750    fn HasFocus(&self) -> bool {
4751        // <https://html.spec.whatwg.org/multipage/#has-focus-steps>
4752        //
4753        // > The has focus steps, given a `Document` object `target`, are as
4754        // > follows:
4755        // >
4756        // > 1. If `target`'s browsing context's top-level browsing context does
4757        // >    not have system focus, then return false.
4758
4759        // > 2. Let `candidate` be `target`'s browsing context's top-level
4760        // >    browsing context's active document.
4761        // >
4762        // > 3. While true:
4763        // >
4764        // >    3.1. If `candidate` is target, then return true.
4765        // >
4766        // >    3.2. If the focused area of `candidate` is a browsing context
4767        // >         container with a non-null nested browsing context, then set
4768        // >         `candidate` to the active document of that browsing context
4769        // >         container's nested browsing context.
4770        // >
4771        // >    3.3. Otherwise, return false.
4772        if self.window().parent_info().is_none() {
4773            // 2 → 3 → (3.1 || ⋯ → 3.3)
4774            self.is_fully_active()
4775        } else {
4776            // 2 → 3 → 3.2 → (⋯ → 3.1 || ⋯ → 3.3)
4777            self.is_fully_active() && self.has_focus.get()
4778        }
4779    }
4780
4781    /// <https://html.spec.whatwg.org/multipage/#dom-document-domain>
4782    fn Domain(&self) -> DOMString {
4783        // Step 1.
4784        if !self.has_browsing_context {
4785            return DOMString::new();
4786        }
4787
4788        // Step 2.
4789        match self.origin.effective_domain() {
4790            // Step 3.
4791            None => DOMString::new(),
4792            // Step 4.
4793            Some(Host::Domain(domain)) => DOMString::from(domain),
4794            Some(host) => DOMString::from(host.to_string()),
4795        }
4796    }
4797
4798    /// <https://html.spec.whatwg.org/multipage/#dom-document-domain>
4799    fn SetDomain(&self, value: DOMString) -> ErrorResult {
4800        // Step 1.
4801        if !self.has_browsing_context {
4802            return Err(Error::Security);
4803        }
4804
4805        // Step 2. If this Document object's active sandboxing flag set has its sandboxed
4806        // document.domain browsing context flag set, then throw a "SecurityError" DOMException.
4807        if self.has_active_sandboxing_flag(
4808            SandboxingFlagSet::SANDBOXED_DOCUMENT_DOMAIN_BROWSING_CONTEXT_FLAG,
4809        ) {
4810            return Err(Error::Security);
4811        }
4812
4813        // Steps 3-4.
4814        let effective_domain = match self.origin.effective_domain() {
4815            Some(effective_domain) => effective_domain,
4816            None => return Err(Error::Security),
4817        };
4818
4819        // Step 5
4820        let host = match get_registrable_domain_suffix_of_or_is_equal_to(&value, effective_domain) {
4821            None => return Err(Error::Security),
4822            Some(host) => host,
4823        };
4824
4825        // Step 6
4826        self.origin.set_domain(host);
4827
4828        Ok(())
4829    }
4830
4831    /// <https://html.spec.whatwg.org/multipage/#dom-document-referrer>
4832    fn Referrer(&self) -> DOMString {
4833        match self.referrer {
4834            Some(ref referrer) => DOMString::from(referrer.to_string()),
4835            None => DOMString::new(),
4836        }
4837    }
4838
4839    /// <https://dom.spec.whatwg.org/#dom-document-documenturi>
4840    fn DocumentURI(&self) -> USVString {
4841        self.URL()
4842    }
4843
4844    /// <https://dom.spec.whatwg.org/#dom-document-compatmode>
4845    fn CompatMode(&self) -> DOMString {
4846        DOMString::from(match self.quirks_mode.get() {
4847            QuirksMode::LimitedQuirks | QuirksMode::NoQuirks => "CSS1Compat",
4848            QuirksMode::Quirks => "BackCompat",
4849        })
4850    }
4851
4852    /// <https://dom.spec.whatwg.org/#dom-document-characterset>
4853    fn CharacterSet(&self) -> DOMString {
4854        DOMString::from(self.encoding.get().name())
4855    }
4856
4857    /// <https://dom.spec.whatwg.org/#dom-document-charset>
4858    fn Charset(&self) -> DOMString {
4859        self.CharacterSet()
4860    }
4861
4862    /// <https://dom.spec.whatwg.org/#dom-document-inputencoding>
4863    fn InputEncoding(&self) -> DOMString {
4864        self.CharacterSet()
4865    }
4866
4867    /// <https://dom.spec.whatwg.org/#dom-document-content_type>
4868    fn ContentType(&self) -> DOMString {
4869        DOMString::from(self.content_type.to_string())
4870    }
4871
4872    /// <https://dom.spec.whatwg.org/#dom-document-doctype>
4873    fn GetDoctype(&self) -> Option<DomRoot<DocumentType>> {
4874        self.upcast::<Node>()
4875            .children()
4876            .filter_map(DomRoot::downcast)
4877            .next()
4878    }
4879
4880    /// <https://dom.spec.whatwg.org/#dom-document-documentelement>
4881    fn GetDocumentElement(&self) -> Option<DomRoot<Element>> {
4882        self.upcast::<Node>().child_elements().next()
4883    }
4884
4885    /// <https://dom.spec.whatwg.org/#dom-document-getelementsbytagname>
4886    fn GetElementsByTagName(
4887        &self,
4888        qualified_name: DOMString,
4889        can_gc: CanGc,
4890    ) -> DomRoot<HTMLCollection> {
4891        let qualified_name = LocalName::from(qualified_name);
4892        if let Some(entry) = self.tag_map.borrow_mut().get(&qualified_name) {
4893            return DomRoot::from_ref(entry);
4894        }
4895        let result = HTMLCollection::by_qualified_name(
4896            &self.window,
4897            self.upcast(),
4898            qualified_name.clone(),
4899            can_gc,
4900        );
4901        self.tag_map
4902            .borrow_mut()
4903            .insert(qualified_name, Dom::from_ref(&*result));
4904        result
4905    }
4906
4907    /// <https://dom.spec.whatwg.org/#dom-document-getelementsbytagnamens>
4908    fn GetElementsByTagNameNS(
4909        &self,
4910        maybe_ns: Option<DOMString>,
4911        tag_name: DOMString,
4912        can_gc: CanGc,
4913    ) -> DomRoot<HTMLCollection> {
4914        let ns = namespace_from_domstring(maybe_ns);
4915        let local = LocalName::from(tag_name);
4916        let qname = QualName::new(None, ns, local);
4917        if let Some(collection) = self.tagns_map.borrow().get(&qname) {
4918            return DomRoot::from_ref(collection);
4919        }
4920        let result =
4921            HTMLCollection::by_qual_tag_name(&self.window, self.upcast(), qname.clone(), can_gc);
4922        self.tagns_map
4923            .borrow_mut()
4924            .insert(qname, Dom::from_ref(&*result));
4925        result
4926    }
4927
4928    /// <https://dom.spec.whatwg.org/#dom-document-getelementsbyclassname>
4929    fn GetElementsByClassName(&self, classes: DOMString, can_gc: CanGc) -> DomRoot<HTMLCollection> {
4930        let class_atoms: Vec<Atom> = split_html_space_chars(&classes.str())
4931            .map(Atom::from)
4932            .collect();
4933        if let Some(collection) = self.classes_map.borrow().get(&class_atoms) {
4934            return DomRoot::from_ref(collection);
4935        }
4936        let result = HTMLCollection::by_atomic_class_name(
4937            &self.window,
4938            self.upcast(),
4939            class_atoms.clone(),
4940            can_gc,
4941        );
4942        self.classes_map
4943            .borrow_mut()
4944            .insert(class_atoms, Dom::from_ref(&*result));
4945        result
4946    }
4947
4948    /// <https://dom.spec.whatwg.org/#dom-nonelementparentnode-getelementbyid>
4949    fn GetElementById(&self, id: DOMString) -> Option<DomRoot<Element>> {
4950        self.get_element_by_id(&Atom::from(id))
4951    }
4952
4953    /// <https://dom.spec.whatwg.org/#dom-document-createelement>
4954    fn CreateElement(
4955        &self,
4956        mut local_name: DOMString,
4957        options: StringOrElementCreationOptions,
4958        can_gc: CanGc,
4959    ) -> Fallible<DomRoot<Element>> {
4960        // Step 1. If localName is not a valid element local name,
4961        //      then throw an "InvalidCharacterError" DOMException.
4962        if !is_valid_element_local_name(&local_name.str()) {
4963            debug!("Not a valid element name");
4964            return Err(Error::InvalidCharacter(None));
4965        }
4966
4967        if self.is_html_document {
4968            local_name.make_ascii_lowercase();
4969        }
4970
4971        let ns = if self.is_html_document || self.is_xhtml_document() {
4972            ns!(html)
4973        } else {
4974            ns!()
4975        };
4976
4977        let name = QualName::new(None, ns, LocalName::from(local_name));
4978        let is = match options {
4979            StringOrElementCreationOptions::String(_) => None,
4980            StringOrElementCreationOptions::ElementCreationOptions(options) => {
4981                options.is.as_ref().map(LocalName::from)
4982            },
4983        };
4984        Ok(Element::create(
4985            name,
4986            is,
4987            self,
4988            ElementCreator::ScriptCreated,
4989            CustomElementCreationMode::Synchronous,
4990            None,
4991            can_gc,
4992        ))
4993    }
4994
4995    /// <https://dom.spec.whatwg.org/#dom-document-createelementns>
4996    fn CreateElementNS(
4997        &self,
4998        namespace: Option<DOMString>,
4999        qualified_name: DOMString,
5000        options: StringOrElementCreationOptions,
5001        can_gc: CanGc,
5002    ) -> Fallible<DomRoot<Element>> {
5003        // Step 1. Let (namespace, prefix, localName) be the result of
5004        //      validating and extracting namespace and qualifiedName given "element".
5005        let context = domname::Context::Element;
5006        let (namespace, prefix, local_name) =
5007            domname::validate_and_extract(namespace, &qualified_name, context)?;
5008
5009        // Step 2. Let is be null.
5010        // Step 3. If options is a dictionary and options["is"] exists, then set is to it.
5011        let name = QualName::new(prefix, namespace, local_name);
5012        let is = match options {
5013            StringOrElementCreationOptions::String(_) => None,
5014            StringOrElementCreationOptions::ElementCreationOptions(options) => {
5015                options.is.as_ref().map(LocalName::from)
5016            },
5017        };
5018
5019        // Step 4. Return the result of creating an element given document, localName, namespace, prefix, is, and true.
5020        Ok(Element::create(
5021            name,
5022            is,
5023            self,
5024            ElementCreator::ScriptCreated,
5025            CustomElementCreationMode::Synchronous,
5026            None,
5027            can_gc,
5028        ))
5029    }
5030
5031    /// <https://dom.spec.whatwg.org/#dom-document-createattribute>
5032    fn CreateAttribute(&self, mut local_name: DOMString, can_gc: CanGc) -> Fallible<DomRoot<Attr>> {
5033        // Step 1. If localName is not a valid attribute local name,
5034        //      then throw an "InvalidCharacterError" DOMException
5035        if !is_valid_attribute_local_name(&local_name.str()) {
5036            debug!("Not a valid attribute name");
5037            return Err(Error::InvalidCharacter(None));
5038        }
5039        if self.is_html_document {
5040            local_name.make_ascii_lowercase();
5041        }
5042        let name = LocalName::from(local_name);
5043        let value = AttrValue::String("".to_owned());
5044
5045        Ok(Attr::new(
5046            self,
5047            name.clone(),
5048            value,
5049            name,
5050            ns!(),
5051            None,
5052            None,
5053            can_gc,
5054        ))
5055    }
5056
5057    /// <https://dom.spec.whatwg.org/#dom-document-createattributens>
5058    fn CreateAttributeNS(
5059        &self,
5060        namespace: Option<DOMString>,
5061        qualified_name: DOMString,
5062        can_gc: CanGc,
5063    ) -> Fallible<DomRoot<Attr>> {
5064        // Step 1. Let (namespace, prefix, localName) be the result of validating and
5065        //      extracting namespace and qualifiedName given "attribute".
5066        let context = domname::Context::Attribute;
5067        let (namespace, prefix, local_name) =
5068            domname::validate_and_extract(namespace, &qualified_name, context)?;
5069        let value = AttrValue::String("".to_owned());
5070        let qualified_name = LocalName::from(qualified_name);
5071        Ok(Attr::new(
5072            self,
5073            local_name,
5074            value,
5075            qualified_name,
5076            namespace,
5077            prefix,
5078            None,
5079            can_gc,
5080        ))
5081    }
5082
5083    /// <https://dom.spec.whatwg.org/#dom-document-createdocumentfragment>
5084    fn CreateDocumentFragment(&self, can_gc: CanGc) -> DomRoot<DocumentFragment> {
5085        DocumentFragment::new(self, can_gc)
5086    }
5087
5088    /// <https://dom.spec.whatwg.org/#dom-document-createtextnode>
5089    fn CreateTextNode(&self, data: DOMString, can_gc: CanGc) -> DomRoot<Text> {
5090        Text::new(data, self, can_gc)
5091    }
5092
5093    /// <https://dom.spec.whatwg.org/#dom-document-createcdatasection>
5094    fn CreateCDATASection(
5095        &self,
5096        data: DOMString,
5097        can_gc: CanGc,
5098    ) -> Fallible<DomRoot<CDATASection>> {
5099        // Step 1
5100        if self.is_html_document {
5101            return Err(Error::NotSupported);
5102        }
5103
5104        // Step 2
5105        if data.contains("]]>") {
5106            return Err(Error::InvalidCharacter(None));
5107        }
5108
5109        // Step 3
5110        Ok(CDATASection::new(data, self, can_gc))
5111    }
5112
5113    /// <https://dom.spec.whatwg.org/#dom-document-createcomment>
5114    fn CreateComment(&self, data: DOMString, can_gc: CanGc) -> DomRoot<Comment> {
5115        Comment::new(data, self, None, can_gc)
5116    }
5117
5118    /// <https://dom.spec.whatwg.org/#dom-document-createprocessinginstruction>
5119    fn CreateProcessingInstruction(
5120        &self,
5121        target: DOMString,
5122        data: DOMString,
5123        can_gc: CanGc,
5124    ) -> Fallible<DomRoot<ProcessingInstruction>> {
5125        // Step 1. If target does not match the Name production, then throw an "InvalidCharacterError" DOMException.
5126        if !matches_name_production(&target.str()) {
5127            return Err(Error::InvalidCharacter(None));
5128        }
5129
5130        // Step 2.
5131        if data.contains("?>") {
5132            return Err(Error::InvalidCharacter(None));
5133        }
5134
5135        // Step 3.
5136        Ok(ProcessingInstruction::new(target, data, self, can_gc))
5137    }
5138
5139    /// <https://dom.spec.whatwg.org/#dom-document-importnode>
5140    fn ImportNode(&self, node: &Node, deep: bool, can_gc: CanGc) -> Fallible<DomRoot<Node>> {
5141        // Step 1.
5142        if node.is::<Document>() || node.is::<ShadowRoot>() {
5143            return Err(Error::NotSupported);
5144        }
5145
5146        // Step 2.
5147        let clone_children = if deep {
5148            CloneChildrenFlag::CloneChildren
5149        } else {
5150            CloneChildrenFlag::DoNotCloneChildren
5151        };
5152
5153        Ok(Node::clone(node, Some(self), clone_children, can_gc))
5154    }
5155
5156    /// <https://dom.spec.whatwg.org/#dom-document-adoptnode>
5157    fn AdoptNode(&self, node: &Node, can_gc: CanGc) -> Fallible<DomRoot<Node>> {
5158        // Step 1.
5159        if node.is::<Document>() {
5160            return Err(Error::NotSupported);
5161        }
5162
5163        // Step 2.
5164        if node.is::<ShadowRoot>() {
5165            return Err(Error::HierarchyRequest);
5166        }
5167
5168        // Step 3.
5169        Node::adopt(node, self, can_gc);
5170
5171        // Step 4.
5172        Ok(DomRoot::from_ref(node))
5173    }
5174
5175    /// <https://dom.spec.whatwg.org/#dom-document-createevent>
5176    fn CreateEvent(&self, mut interface: DOMString, can_gc: CanGc) -> Fallible<DomRoot<Event>> {
5177        interface.make_ascii_lowercase();
5178        match &*interface.str() {
5179            "beforeunloadevent" => Ok(DomRoot::upcast(BeforeUnloadEvent::new_uninitialized(
5180                &self.window,
5181                can_gc,
5182            ))),
5183            "compositionevent" | "textevent" => Ok(DomRoot::upcast(
5184                CompositionEvent::new_uninitialized(&self.window, can_gc),
5185            )),
5186            "customevent" => Ok(DomRoot::upcast(CustomEvent::new_uninitialized(
5187                self.window.upcast(),
5188                can_gc,
5189            ))),
5190            // FIXME(#25136): devicemotionevent, deviceorientationevent
5191            // FIXME(#7529): dragevent
5192            "events" | "event" | "htmlevents" | "svgevents" => {
5193                Ok(Event::new_uninitialized(self.window.upcast(), can_gc))
5194            },
5195            "focusevent" => Ok(DomRoot::upcast(FocusEvent::new_uninitialized(
5196                &self.window,
5197                can_gc,
5198            ))),
5199            "hashchangeevent" => Ok(DomRoot::upcast(HashChangeEvent::new_uninitialized(
5200                &self.window,
5201                can_gc,
5202            ))),
5203            "keyboardevent" => Ok(DomRoot::upcast(KeyboardEvent::new_uninitialized(
5204                &self.window,
5205                can_gc,
5206            ))),
5207            "messageevent" => Ok(DomRoot::upcast(MessageEvent::new_uninitialized(
5208                self.window.upcast(),
5209                can_gc,
5210            ))),
5211            "mouseevent" | "mouseevents" => Ok(DomRoot::upcast(MouseEvent::new_uninitialized(
5212                &self.window,
5213                can_gc,
5214            ))),
5215            "storageevent" => Ok(DomRoot::upcast(StorageEvent::new_uninitialized(
5216                &self.window,
5217                "".into(),
5218                can_gc,
5219            ))),
5220            "touchevent" => Ok(DomRoot::upcast(DomTouchEvent::new_uninitialized(
5221                &self.window,
5222                &TouchList::new(&self.window, &[], can_gc),
5223                &TouchList::new(&self.window, &[], can_gc),
5224                &TouchList::new(&self.window, &[], can_gc),
5225                can_gc,
5226            ))),
5227            "uievent" | "uievents" => Ok(DomRoot::upcast(UIEvent::new_uninitialized(
5228                &self.window,
5229                can_gc,
5230            ))),
5231            _ => Err(Error::NotSupported),
5232        }
5233    }
5234
5235    /// <https://html.spec.whatwg.org/multipage/#dom-document-lastmodified>
5236    fn LastModified(&self) -> DOMString {
5237        DOMString::from(self.last_modified.as_ref().cloned().unwrap_or_else(|| {
5238            // Ideally this would get the local time using `time`, but `time` always fails to get the local
5239            // timezone on Unix unless the application is single threaded unless the library is explicitly
5240            // set to "unsound" mode. Maybe that's fine, but it needs more investigation. see
5241            // https://nvd.nist.gov/vuln/detail/CVE-2020-26235
5242            // When `time` supports a thread-safe way of getting the local time zone we could use it here.
5243            Local::now().format("%m/%d/%Y %H:%M:%S").to_string()
5244        }))
5245    }
5246
5247    /// <https://dom.spec.whatwg.org/#dom-document-createrange>
5248    fn CreateRange(&self, can_gc: CanGc) -> DomRoot<Range> {
5249        Range::new_with_doc(self, None, can_gc)
5250    }
5251
5252    /// <https://dom.spec.whatwg.org/#dom-document-createnodeiteratorroot-whattoshow-filter>
5253    fn CreateNodeIterator(
5254        &self,
5255        root: &Node,
5256        what_to_show: u32,
5257        filter: Option<Rc<NodeFilter>>,
5258        can_gc: CanGc,
5259    ) -> DomRoot<NodeIterator> {
5260        NodeIterator::new(self, root, what_to_show, filter, can_gc)
5261    }
5262
5263    /// <https://dom.spec.whatwg.org/#dom-document-createtreewalker>
5264    fn CreateTreeWalker(
5265        &self,
5266        root: &Node,
5267        what_to_show: u32,
5268        filter: Option<Rc<NodeFilter>>,
5269    ) -> DomRoot<TreeWalker> {
5270        TreeWalker::new(self, root, what_to_show, filter)
5271    }
5272
5273    /// <https://html.spec.whatwg.org/multipage/#document.title>
5274    fn Title(&self) -> DOMString {
5275        self.title().unwrap_or_else(|| DOMString::from(""))
5276    }
5277
5278    /// <https://html.spec.whatwg.org/multipage/#document.title>
5279    fn SetTitle(&self, title: DOMString, can_gc: CanGc) {
5280        let root = match self.GetDocumentElement() {
5281            Some(root) => root,
5282            None => return,
5283        };
5284
5285        let node = if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
5286            let elem = root.upcast::<Node>().child_elements().find(|node| {
5287                node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
5288            });
5289            match elem {
5290                Some(elem) => DomRoot::upcast::<Node>(elem),
5291                None => {
5292                    let name = QualName::new(None, ns!(svg), local_name!("title"));
5293                    let elem = Element::create(
5294                        name,
5295                        None,
5296                        self,
5297                        ElementCreator::ScriptCreated,
5298                        CustomElementCreationMode::Synchronous,
5299                        None,
5300                        can_gc,
5301                    );
5302                    let parent = root.upcast::<Node>();
5303                    let child = elem.upcast::<Node>();
5304                    parent
5305                        .InsertBefore(child, parent.GetFirstChild().as_deref(), can_gc)
5306                        .unwrap()
5307                },
5308            }
5309        } else if root.namespace() == &ns!(html) {
5310            let elem = root
5311                .upcast::<Node>()
5312                .traverse_preorder(ShadowIncluding::No)
5313                .find(|node| node.is::<HTMLTitleElement>());
5314            match elem {
5315                Some(elem) => elem,
5316                None => match self.GetHead() {
5317                    Some(head) => {
5318                        let name = QualName::new(None, ns!(html), local_name!("title"));
5319                        let elem = Element::create(
5320                            name,
5321                            None,
5322                            self,
5323                            ElementCreator::ScriptCreated,
5324                            CustomElementCreationMode::Synchronous,
5325                            None,
5326                            can_gc,
5327                        );
5328                        head.upcast::<Node>()
5329                            .AppendChild(elem.upcast(), can_gc)
5330                            .unwrap()
5331                    },
5332                    None => return,
5333                },
5334            }
5335        } else {
5336            return;
5337        };
5338
5339        node.set_text_content_for_element(Some(title), can_gc);
5340    }
5341
5342    /// <https://html.spec.whatwg.org/multipage/#dom-document-head>
5343    fn GetHead(&self) -> Option<DomRoot<HTMLHeadElement>> {
5344        self.get_html_element().and_then(|root| {
5345            root.upcast::<Node>()
5346                .children()
5347                .filter_map(DomRoot::downcast)
5348                .next()
5349        })
5350    }
5351
5352    /// <https://html.spec.whatwg.org/multipage/#dom-document-currentscript>
5353    fn GetCurrentScript(&self) -> Option<DomRoot<HTMLScriptElement>> {
5354        self.current_script.get()
5355    }
5356
5357    /// <https://html.spec.whatwg.org/multipage/#dom-document-body>
5358    fn GetBody(&self) -> Option<DomRoot<HTMLElement>> {
5359        self.get_html_element().and_then(|root| {
5360            let node = root.upcast::<Node>();
5361            node.children()
5362                .find(|child| {
5363                    matches!(
5364                        child.type_id(),
5365                        NodeTypeId::Element(ElementTypeId::HTMLElement(
5366                            HTMLElementTypeId::HTMLBodyElement,
5367                        )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
5368                            HTMLElementTypeId::HTMLFrameSetElement,
5369                        ))
5370                    )
5371                })
5372                .map(|node| DomRoot::downcast(node).unwrap())
5373        })
5374    }
5375
5376    /// <https://html.spec.whatwg.org/multipage/#dom-document-body>
5377    fn SetBody(&self, new_body: Option<&HTMLElement>, can_gc: CanGc) -> ErrorResult {
5378        // Step 1.
5379        let new_body = match new_body {
5380            Some(new_body) => new_body,
5381            None => return Err(Error::HierarchyRequest),
5382        };
5383
5384        let node = new_body.upcast::<Node>();
5385        match node.type_id() {
5386            NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) |
5387            NodeTypeId::Element(ElementTypeId::HTMLElement(
5388                HTMLElementTypeId::HTMLFrameSetElement,
5389            )) => {},
5390            _ => return Err(Error::HierarchyRequest),
5391        }
5392
5393        // Step 2.
5394        let old_body = self.GetBody();
5395        if old_body.as_deref() == Some(new_body) {
5396            return Ok(());
5397        }
5398
5399        match (self.GetDocumentElement(), &old_body) {
5400            // Step 3.
5401            (Some(ref root), Some(child)) => {
5402                let root = root.upcast::<Node>();
5403                root.ReplaceChild(new_body.upcast(), child.upcast(), can_gc)
5404                    .unwrap();
5405            },
5406
5407            // Step 4.
5408            (None, _) => return Err(Error::HierarchyRequest),
5409
5410            // Step 5.
5411            (Some(ref root), &None) => {
5412                let root = root.upcast::<Node>();
5413                root.AppendChild(new_body.upcast(), can_gc).unwrap();
5414            },
5415        }
5416        Ok(())
5417    }
5418
5419    /// <https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname>
5420    fn GetElementsByName(&self, name: DOMString, can_gc: CanGc) -> DomRoot<NodeList> {
5421        NodeList::new_elements_by_name_list(self.window(), self, name, can_gc)
5422    }
5423
5424    /// <https://html.spec.whatwg.org/multipage/#dom-document-images>
5425    fn Images(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5426        self.images.or_init(|| {
5427            HTMLCollection::new_with_filter_fn(
5428                &self.window,
5429                self.upcast(),
5430                |element, _| element.is::<HTMLImageElement>(),
5431                can_gc,
5432            )
5433        })
5434    }
5435
5436    /// <https://html.spec.whatwg.org/multipage/#dom-document-embeds>
5437    fn Embeds(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5438        self.embeds.or_init(|| {
5439            HTMLCollection::new_with_filter_fn(
5440                &self.window,
5441                self.upcast(),
5442                |element, _| element.is::<HTMLEmbedElement>(),
5443                can_gc,
5444            )
5445        })
5446    }
5447
5448    /// <https://html.spec.whatwg.org/multipage/#dom-document-plugins>
5449    fn Plugins(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5450        self.Embeds(can_gc)
5451    }
5452
5453    /// <https://html.spec.whatwg.org/multipage/#dom-document-links>
5454    fn Links(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5455        self.links.or_init(|| {
5456            HTMLCollection::new_with_filter_fn(
5457                &self.window,
5458                self.upcast(),
5459                |element, _| {
5460                    (element.is::<HTMLAnchorElement>() || element.is::<HTMLAreaElement>()) &&
5461                        element.has_attribute(&local_name!("href"))
5462                },
5463                can_gc,
5464            )
5465        })
5466    }
5467
5468    /// <https://html.spec.whatwg.org/multipage/#dom-document-forms>
5469    fn Forms(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5470        self.forms.or_init(|| {
5471            HTMLCollection::new_with_filter_fn(
5472                &self.window,
5473                self.upcast(),
5474                |element, _| element.is::<HTMLFormElement>(),
5475                can_gc,
5476            )
5477        })
5478    }
5479
5480    /// <https://html.spec.whatwg.org/multipage/#dom-document-scripts>
5481    fn Scripts(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5482        self.scripts.or_init(|| {
5483            HTMLCollection::new_with_filter_fn(
5484                &self.window,
5485                self.upcast(),
5486                |element, _| element.is::<HTMLScriptElement>(),
5487                can_gc,
5488            )
5489        })
5490    }
5491
5492    /// <https://html.spec.whatwg.org/multipage/#dom-document-anchors>
5493    fn Anchors(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5494        self.anchors.or_init(|| {
5495            HTMLCollection::new_with_filter_fn(
5496                &self.window,
5497                self.upcast(),
5498                |element, _| {
5499                    element.is::<HTMLAnchorElement>() && element.has_attribute(&local_name!("href"))
5500                },
5501                can_gc,
5502            )
5503        })
5504    }
5505
5506    /// <https://html.spec.whatwg.org/multipage/#dom-document-applets>
5507    fn Applets(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5508        self.applets
5509            .or_init(|| HTMLCollection::always_empty(&self.window, self.upcast(), can_gc))
5510    }
5511
5512    /// <https://html.spec.whatwg.org/multipage/#dom-document-location>
5513    fn GetLocation(&self) -> Option<DomRoot<Location>> {
5514        if self.is_fully_active() {
5515            Some(self.window.Location())
5516        } else {
5517            None
5518        }
5519    }
5520
5521    /// <https://dom.spec.whatwg.org/#dom-parentnode-children>
5522    fn Children(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5523        HTMLCollection::children(&self.window, self.upcast(), can_gc)
5524    }
5525
5526    /// <https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild>
5527    fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
5528        self.upcast::<Node>().child_elements().next()
5529    }
5530
5531    /// <https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild>
5532    fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
5533        self.upcast::<Node>()
5534            .rev_children()
5535            .filter_map(DomRoot::downcast)
5536            .next()
5537    }
5538
5539    /// <https://dom.spec.whatwg.org/#dom-parentnode-childelementcount>
5540    fn ChildElementCount(&self) -> u32 {
5541        self.upcast::<Node>().child_elements().count() as u32
5542    }
5543
5544    /// <https://dom.spec.whatwg.org/#dom-parentnode-prepend>
5545    fn Prepend(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
5546        self.upcast::<Node>().prepend(nodes, can_gc)
5547    }
5548
5549    /// <https://dom.spec.whatwg.org/#dom-parentnode-append>
5550    fn Append(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
5551        self.upcast::<Node>().append(nodes, can_gc)
5552    }
5553
5554    /// <https://dom.spec.whatwg.org/#dom-parentnode-replacechildren>
5555    fn ReplaceChildren(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
5556        self.upcast::<Node>().replace_children(nodes, can_gc)
5557    }
5558
5559    /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
5560    fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
5561        let root = self.upcast::<Node>();
5562        root.query_selector(selectors)
5563    }
5564
5565    /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall>
5566    fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>> {
5567        let root = self.upcast::<Node>();
5568        root.query_selector_all(selectors)
5569    }
5570
5571    /// <https://html.spec.whatwg.org/multipage/#dom-document-readystate>
5572    fn ReadyState(&self) -> DocumentReadyState {
5573        self.ready_state.get()
5574    }
5575
5576    /// <https://html.spec.whatwg.org/multipage/#dom-document-defaultview>
5577    fn GetDefaultView(&self) -> Option<DomRoot<Window>> {
5578        if self.has_browsing_context {
5579            Some(DomRoot::from_ref(&*self.window))
5580        } else {
5581            None
5582        }
5583    }
5584
5585    /// <https://html.spec.whatwg.org/multipage/#dom-document-cookie>
5586    fn GetCookie(&self) -> Fallible<DOMString> {
5587        if self.is_cookie_averse() {
5588            return Ok(DOMString::new());
5589        }
5590
5591        if !self.origin.is_tuple() {
5592            return Err(Error::Security);
5593        }
5594
5595        let url = self.url();
5596        let (tx, rx) = profile_ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
5597        let _ = self
5598            .window
5599            .as_global_scope()
5600            .resource_threads()
5601            .send(GetCookiesForUrl(url, tx, NonHTTP));
5602        let cookies = rx.recv().unwrap();
5603        Ok(cookies.map_or(DOMString::new(), DOMString::from))
5604    }
5605
5606    /// <https://html.spec.whatwg.org/multipage/#dom-document-cookie>
5607    fn SetCookie(&self, cookie: DOMString) -> ErrorResult {
5608        if self.is_cookie_averse() {
5609            return Ok(());
5610        }
5611
5612        if !self.origin.is_tuple() {
5613            return Err(Error::Security);
5614        }
5615
5616        let cookies = if let Some(cookie) = Cookie::parse(cookie.to_string()).ok().map(Serde) {
5617            vec![cookie]
5618        } else {
5619            vec![]
5620        };
5621
5622        let _ = self
5623            .window
5624            .as_global_scope()
5625            .resource_threads()
5626            .send(SetCookiesForUrl(self.url(), cookies, NonHTTP));
5627        Ok(())
5628    }
5629
5630    /// <https://html.spec.whatwg.org/multipage/#dom-document-bgcolor>
5631    fn BgColor(&self) -> DOMString {
5632        self.get_body_attribute(&local_name!("bgcolor"))
5633    }
5634
5635    /// <https://html.spec.whatwg.org/multipage/#dom-document-bgcolor>
5636    fn SetBgColor(&self, value: DOMString, can_gc: CanGc) {
5637        self.set_body_attribute(&local_name!("bgcolor"), value, can_gc)
5638    }
5639
5640    /// <https://html.spec.whatwg.org/multipage/#dom-document-fgcolor>
5641    fn FgColor(&self) -> DOMString {
5642        self.get_body_attribute(&local_name!("text"))
5643    }
5644
5645    /// <https://html.spec.whatwg.org/multipage/#dom-document-fgcolor>
5646    fn SetFgColor(&self, value: DOMString, can_gc: CanGc) {
5647        self.set_body_attribute(&local_name!("text"), value, can_gc)
5648    }
5649
5650    /// <https://html.spec.whatwg.org/multipage/#dom-tree-accessors:dom-document-nameditem-filter>
5651    fn NamedGetter(&self, name: DOMString, can_gc: CanGc) -> Option<NamedPropertyValue> {
5652        if name.is_empty() {
5653            return None;
5654        }
5655        let name = Atom::from(name);
5656
5657        // Step 1. Let elements be the list of named elements with the name name that are in a document tree
5658        // with the Document as their root.
5659        let elements_with_name = self.get_elements_with_name(&name);
5660        let name_iter = elements_with_name
5661            .iter()
5662            .filter(|elem| is_named_element_with_name_attribute(elem));
5663        let elements_with_id = self.get_elements_with_id(&name);
5664        let id_iter = elements_with_id
5665            .iter()
5666            .filter(|elem| is_named_element_with_id_attribute(elem));
5667        let mut elements = name_iter.chain(id_iter);
5668
5669        // Step 2. If elements has only one element, and that element is an iframe element,
5670        // and that iframe element's content navigable is not null, then return the active
5671        // WindowProxy of the element's content navigable.
5672
5673        // NOTE: We have to check if all remaining elements are equal to the first, since
5674        // the same element may appear in both lists.
5675        let first = elements.next()?;
5676        if elements.all(|other| first == other) {
5677            if let Some(nested_window_proxy) = first
5678                .downcast::<HTMLIFrameElement>()
5679                .and_then(|iframe| iframe.GetContentWindow())
5680            {
5681                return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
5682            }
5683
5684            // Step 3. Otherwise, if elements has only one element, return that element.
5685            return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
5686        }
5687
5688        // Step 4. Otherwise, return an HTMLCollection rooted at the Document node,
5689        // whose filter matches only named elements with the name name.
5690        #[derive(JSTraceable, MallocSizeOf)]
5691        struct DocumentNamedGetter {
5692            #[no_trace]
5693            name: Atom,
5694        }
5695        impl CollectionFilter for DocumentNamedGetter {
5696            fn filter(&self, elem: &Element, _root: &Node) -> bool {
5697                let type_ = match elem.upcast::<Node>().type_id() {
5698                    NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
5699                    _ => return false,
5700                };
5701                match type_ {
5702                    HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
5703                        elem.get_name().as_ref() == Some(&self.name)
5704                    },
5705                    HTMLElementTypeId::HTMLImageElement => elem.get_name().is_some_and(|name| {
5706                        name == *self.name ||
5707                            !name.is_empty() && elem.get_id().as_ref() == Some(&self.name)
5708                    }),
5709                    // TODO handle <embed> and <object>; these depend on whether the element is
5710                    // “exposed”, a concept that doesn’t fully make sense until embed/object
5711                    // behaviour is actually implemented
5712                    _ => false,
5713                }
5714            }
5715        }
5716        let collection = HTMLCollection::create(
5717            self.window(),
5718            self.upcast(),
5719            Box::new(DocumentNamedGetter { name }),
5720            can_gc,
5721        );
5722        Some(NamedPropertyValue::HTMLCollection(collection))
5723    }
5724
5725    /// <https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names>
5726    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
5727        let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
5728
5729        let name_map = self.name_map.borrow();
5730        for (name, elements) in &(name_map).0 {
5731            if name.is_empty() {
5732                continue;
5733            }
5734            let mut name_iter = elements
5735                .iter()
5736                .filter(|elem| is_named_element_with_name_attribute(elem));
5737            if let Some(first) = name_iter.next() {
5738                names_with_first_named_element_map.insert(name, first);
5739            }
5740        }
5741        let id_map = self.id_map.borrow();
5742        for (id, elements) in &(id_map).0 {
5743            if id.is_empty() {
5744                continue;
5745            }
5746            let mut id_iter = elements
5747                .iter()
5748                .filter(|elem| is_named_element_with_id_attribute(elem));
5749            if let Some(first) = id_iter.next() {
5750                match names_with_first_named_element_map.entry(id) {
5751                    Vacant(entry) => drop(entry.insert(first)),
5752                    Occupied(mut entry) => {
5753                        if first.upcast::<Node>().is_before(entry.get().upcast()) {
5754                            *entry.get_mut() = first;
5755                        }
5756                    },
5757                }
5758            }
5759        }
5760
5761        let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
5762            names_with_first_named_element_map
5763                .iter()
5764                .map(|(k, v)| (*k, *v))
5765                .collect();
5766        names_with_first_named_element_vec.sort_unstable_by(|a, b| {
5767            if a.1 == b.1 {
5768                // This can happen if an img has an id different from its name,
5769                // spec does not say which string to put first.
5770                a.0.cmp(b.0)
5771            } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
5772                Ordering::Less
5773            } else {
5774                Ordering::Greater
5775            }
5776        });
5777
5778        names_with_first_named_element_vec
5779            .iter()
5780            .map(|(k, _v)| DOMString::from(&***k))
5781            .collect()
5782    }
5783
5784    /// <https://html.spec.whatwg.org/multipage/#dom-document-clear>
5785    fn Clear(&self) {
5786        // This method intentionally does nothing
5787    }
5788
5789    /// <https://html.spec.whatwg.org/multipage/#dom-document-captureevents>
5790    fn CaptureEvents(&self) {
5791        // This method intentionally does nothing
5792    }
5793
5794    /// <https://html.spec.whatwg.org/multipage/#dom-document-releaseevents>
5795    fn ReleaseEvents(&self) {
5796        // This method intentionally does nothing
5797    }
5798
5799    // https://html.spec.whatwg.org/multipage/#globaleventhandlers
5800    global_event_handlers!();
5801
5802    // https://html.spec.whatwg.org/multipage/#handler-onreadystatechange
5803    event_handler!(
5804        readystatechange,
5805        GetOnreadystatechange,
5806        SetOnreadystatechange
5807    );
5808
5809    /// <https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint>
5810    fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
5811        self.document_or_shadow_root.element_from_point(
5812            x,
5813            y,
5814            self.GetDocumentElement(),
5815            self.has_browsing_context,
5816        )
5817    }
5818
5819    /// <https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint>
5820    fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
5821        self.document_or_shadow_root.elements_from_point(
5822            x,
5823            y,
5824            self.GetDocumentElement(),
5825            self.has_browsing_context,
5826        )
5827    }
5828
5829    /// <https://drafts.csswg.org/cssom-view/#dom-document-scrollingelement>
5830    fn GetScrollingElement(&self) -> Option<DomRoot<Element>> {
5831        // Step 1. If the Document is in quirks mode, follow these steps:
5832        if self.quirks_mode() == QuirksMode::Quirks {
5833            // Step 1.1. If the body element exists,
5834            if let Some(ref body) = self.GetBody() {
5835                let e = body.upcast::<Element>();
5836                // and it is not potentially scrollable, return the body element and abort these steps.
5837                // For this purpose, a value of overflow:clip on the body element’s parent element
5838                // must be treated as overflow:hidden.
5839                if !e.is_potentially_scrollable_body_for_scrolling_element() {
5840                    return Some(DomRoot::from_ref(e));
5841                }
5842            }
5843
5844            // Step 1.2. Return null and abort these steps.
5845            return None;
5846        }
5847
5848        // Step 2. If there is a root element, return the root element and abort these steps.
5849        // Step 3. Return null.
5850        self.GetDocumentElement()
5851    }
5852
5853    /// <https://html.spec.whatwg.org/multipage/#dom-document-open>
5854    fn Open(
5855        &self,
5856        _unused1: Option<DOMString>,
5857        _unused2: Option<DOMString>,
5858        can_gc: CanGc,
5859    ) -> Fallible<DomRoot<Document>> {
5860        // Step 1
5861        if !self.is_html_document() {
5862            return Err(Error::InvalidState(None));
5863        }
5864
5865        // Step 2
5866        if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
5867            return Err(Error::InvalidState(None));
5868        }
5869
5870        // Step 3
5871        let entry_responsible_document = GlobalScope::entry().as_window().Document();
5872
5873        // Step 4
5874        if !self.origin.same_origin(&entry_responsible_document.origin) {
5875            return Err(Error::Security);
5876        }
5877
5878        // Step 5
5879        if self
5880            .get_current_parser()
5881            .is_some_and(|parser| parser.is_active())
5882        {
5883            return Ok(DomRoot::from_ref(self));
5884        }
5885
5886        // Step 6
5887        if self.is_prompting_or_unloading() {
5888            return Ok(DomRoot::from_ref(self));
5889        }
5890
5891        // Step 7
5892        if self.active_parser_was_aborted.get() {
5893            return Ok(DomRoot::from_ref(self));
5894        }
5895
5896        // TODO: prompt to unload.
5897        // TODO: set unload_event_start and unload_event_end
5898
5899        self.window().set_navigation_start();
5900
5901        // Step 8
5902        // TODO: https://github.com/servo/servo/issues/21937
5903        if self.has_browsing_context() {
5904            // spec says "stop document loading",
5905            // which is a process that does more than just abort
5906            self.abort(can_gc);
5907        }
5908
5909        // Step 9
5910        for node in self
5911            .upcast::<Node>()
5912            .traverse_preorder(ShadowIncluding::Yes)
5913        {
5914            node.upcast::<EventTarget>().remove_all_listeners();
5915        }
5916
5917        // Step 10
5918        if self.window.Document() == DomRoot::from_ref(self) {
5919            self.window.upcast::<EventTarget>().remove_all_listeners();
5920        }
5921
5922        // Step 11. Replace all with null within document.
5923        Node::replace_all(None, self.upcast::<Node>(), can_gc);
5924
5925        // Specs and tests are in a state of flux about whether
5926        // we want to clear the selection when we remove the contents;
5927        // WPT selection/Document-open.html wants us to not clear it
5928        // as of Feb 1 2020
5929
5930        // Step 12. If document is fully active, then:
5931        if self.is_fully_active() {
5932            // Step 12.1. Let newURL be a copy of entryDocument's URL.
5933            let mut new_url = entry_responsible_document.url();
5934
5935            // Step 12.2. If entryDocument is not document, then set newURL's fragment to null.
5936            if entry_responsible_document != DomRoot::from_ref(self) {
5937                new_url.set_fragment(None);
5938            }
5939
5940            // Step 12.3. Run the URL and history update steps with document and newURL.
5941            // TODO: https://github.com/servo/servo/issues/21939
5942            self.set_url(new_url);
5943        }
5944
5945        // Step 13. Set document's is initial about:blank to false.
5946        self.is_initial_about_blank.set(false);
5947
5948        // Step 14. If document's iframe load in progress flag is set, then set document's mute
5949        // iframe load flag.
5950        // TODO: https://github.com/servo/servo/issues/21938
5951
5952        // Step 15: Set document to no-quirks mode.
5953        self.set_quirks_mode(QuirksMode::NoQuirks);
5954
5955        // Step 16. Create a new HTML parser and associate it with document. This is a
5956        // script-created parser (meaning that it can be closed by the document.open() and
5957        // document.close() methods, and that the tokenizer will wait for an explicit call to
5958        // document.close() before emitting an end-of-file token). The encoding confidence is
5959        // irrelevant.
5960        let resource_threads = self.window.as_global_scope().resource_threads().clone();
5961        *self.loader.borrow_mut() =
5962            DocumentLoader::new_with_threads(resource_threads, Some(self.url()));
5963        ServoParser::parse_html_script_input(self, self.url());
5964
5965        // Step 17. Set the insertion point to point at just before the end of the input stream
5966        // (which at this point will be empty).
5967        // Handled when creating the parser in step 16
5968
5969        // Step 18. Update the current document readiness of document to "loading".
5970        self.ready_state.set(DocumentReadyState::Loading);
5971
5972        // Step 19. Return document.
5973        Ok(DomRoot::from_ref(self))
5974    }
5975
5976    /// <https://html.spec.whatwg.org/multipage/#dom-document-open-window>
5977    fn Open_(
5978        &self,
5979        url: USVString,
5980        target: DOMString,
5981        features: DOMString,
5982        can_gc: CanGc,
5983    ) -> Fallible<Option<DomRoot<WindowProxy>>> {
5984        self.browsing_context()
5985            .ok_or(Error::InvalidAccess)?
5986            .open(url, target, features, can_gc)
5987    }
5988
5989    /// <https://html.spec.whatwg.org/multipage/#dom-document-write>
5990    fn Write(&self, text: Vec<TrustedHTMLOrString>, can_gc: CanGc) -> ErrorResult {
5991        // The document.write(...text) method steps are to run the document write steps
5992        // with this, text, false, and "Document write".
5993        self.write(text, false, "Document", "write", can_gc)
5994    }
5995
5996    /// <https://html.spec.whatwg.org/multipage/#dom-document-writeln>
5997    fn Writeln(&self, text: Vec<TrustedHTMLOrString>, can_gc: CanGc) -> ErrorResult {
5998        // The document.writeln(...text) method steps are to run the document write steps
5999        // with this, text, true, and "Document writeln".
6000        self.write(text, true, "Document", "writeln", can_gc)
6001    }
6002
6003    /// <https://html.spec.whatwg.org/multipage/#dom-document-close>
6004    fn Close(&self, can_gc: CanGc) -> ErrorResult {
6005        if !self.is_html_document() {
6006            // Step 1.
6007            return Err(Error::InvalidState(None));
6008        }
6009
6010        // Step 2.
6011        if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6012            return Err(Error::InvalidState(None));
6013        }
6014
6015        let parser = match self.get_current_parser() {
6016            Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
6017            _ => {
6018                // Step 3.
6019                return Ok(());
6020            },
6021        };
6022
6023        // Step 4-6.
6024        parser.close(can_gc);
6025
6026        Ok(())
6027    }
6028
6029    // https://fullscreen.spec.whatwg.org/#handler-document-onfullscreenerror
6030    event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror);
6031
6032    // https://fullscreen.spec.whatwg.org/#handler-document-onfullscreenchange
6033    event_handler!(
6034        fullscreenchange,
6035        GetOnfullscreenchange,
6036        SetOnfullscreenchange
6037    );
6038
6039    /// <https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled>
6040    fn FullscreenEnabled(&self) -> bool {
6041        self.get_allow_fullscreen()
6042    }
6043
6044    /// <https://fullscreen.spec.whatwg.org/#dom-document-fullscreen>
6045    fn Fullscreen(&self) -> bool {
6046        self.fullscreen_element.get().is_some()
6047    }
6048
6049    /// <https://fullscreen.spec.whatwg.org/#dom-document-fullscreenelement>
6050    fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
6051        // TODO ShadowRoot
6052        self.fullscreen_element.get()
6053    }
6054
6055    /// <https://fullscreen.spec.whatwg.org/#dom-document-exitfullscreen>
6056    fn ExitFullscreen(&self, can_gc: CanGc) -> Rc<Promise> {
6057        self.exit_fullscreen(can_gc)
6058    }
6059
6060    // check-tidy: no specs after this line
6061    // Servo only API to get an instance of the controls of a specific
6062    // media element matching the given id.
6063    fn ServoGetMediaControls(&self, id: DOMString) -> Fallible<DomRoot<ShadowRoot>> {
6064        match self.media_controls.borrow().get(&*id.str()) {
6065            Some(m) => Ok(DomRoot::from_ref(m)),
6066            None => Err(Error::InvalidAccess),
6067        }
6068    }
6069
6070    /// <https://w3c.github.io/selection-api/#dom-document-getselection>
6071    fn GetSelection(&self, can_gc: CanGc) -> Option<DomRoot<Selection>> {
6072        if self.has_browsing_context {
6073            Some(self.selection.or_init(|| Selection::new(self, can_gc)))
6074        } else {
6075            None
6076        }
6077    }
6078
6079    /// <https://drafts.csswg.org/css-font-loading/#font-face-source>
6080    fn Fonts(&self, can_gc: CanGc) -> DomRoot<FontFaceSet> {
6081        self.fonts
6082            .or_init(|| FontFaceSet::new(&self.global(), None, can_gc))
6083    }
6084
6085    /// <https://html.spec.whatwg.org/multipage/#dom-document-hidden>
6086    fn Hidden(&self) -> bool {
6087        self.visibility_state.get() == DocumentVisibilityState::Hidden
6088    }
6089
6090    /// <https://html.spec.whatwg.org/multipage/#dom-document-visibilitystate>
6091    fn VisibilityState(&self) -> DocumentVisibilityState {
6092        self.visibility_state.get()
6093    }
6094
6095    fn CreateExpression(
6096        &self,
6097        expression: DOMString,
6098        resolver: Option<Rc<XPathNSResolver>>,
6099        can_gc: CanGc,
6100    ) -> Fallible<DomRoot<super::types::XPathExpression>> {
6101        let global = self.global();
6102        let window = global.as_window();
6103        let evaluator = XPathEvaluator::new(window, None, can_gc);
6104        XPathEvaluatorMethods::<crate::DomTypeHolder>::CreateExpression(
6105            &*evaluator,
6106            expression,
6107            resolver,
6108            can_gc,
6109        )
6110    }
6111
6112    fn CreateNSResolver(&self, node_resolver: &Node, can_gc: CanGc) -> DomRoot<Node> {
6113        let global = self.global();
6114        let window = global.as_window();
6115        let evaluator = XPathEvaluator::new(window, None, can_gc);
6116        XPathEvaluatorMethods::<crate::DomTypeHolder>::CreateNSResolver(&*evaluator, node_resolver)
6117    }
6118
6119    fn Evaluate(
6120        &self,
6121        expression: DOMString,
6122        context_node: &Node,
6123        resolver: Option<Rc<XPathNSResolver>>,
6124        type_: u16,
6125        result: Option<&super::types::XPathResult>,
6126        can_gc: CanGc,
6127    ) -> Fallible<DomRoot<super::types::XPathResult>> {
6128        let global = self.global();
6129        let window = global.as_window();
6130        let evaluator = XPathEvaluator::new(window, None, can_gc);
6131        XPathEvaluatorMethods::<crate::DomTypeHolder>::Evaluate(
6132            &*evaluator,
6133            expression,
6134            context_node,
6135            resolver,
6136            type_,
6137            result,
6138            can_gc,
6139        )
6140    }
6141
6142    /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
6143    fn AdoptedStyleSheets(&self, context: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
6144        self.adopted_stylesheets_frozen_types.get_or_init(
6145            || {
6146                self.adopted_stylesheets
6147                    .borrow()
6148                    .clone()
6149                    .iter()
6150                    .map(|sheet| sheet.as_rooted())
6151                    .collect()
6152            },
6153            context,
6154            retval,
6155            can_gc,
6156        );
6157    }
6158
6159    /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
6160    fn SetAdoptedStyleSheets(&self, context: JSContext, val: HandleValue) -> ErrorResult {
6161        let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
6162            context,
6163            self.adopted_stylesheets.borrow_mut().as_mut(),
6164            val,
6165            &StyleSheetListOwner::Document(Dom::from_ref(self)),
6166        );
6167
6168        // If update is successful, clear the FrozenArray cache.
6169        if result.is_ok() {
6170            self.adopted_stylesheets_frozen_types.clear()
6171        }
6172
6173        result
6174    }
6175}
6176
6177fn update_with_current_instant(marker: &Cell<Option<CrossProcessInstant>>) {
6178    if marker.get().is_none() {
6179        marker.set(Some(CrossProcessInstant::now()))
6180    }
6181}
6182
6183/// Specifies the type of focus event that is sent to a pipeline
6184#[derive(Clone, Copy, PartialEq)]
6185pub(crate) enum FocusType {
6186    Element, // The first focus message - focus the element itself
6187    Parent,  // Focusing a parent element (an iframe)
6188}
6189
6190/// Specifies the initiator of a focus operation.
6191#[derive(Clone, Copy, PartialEq)]
6192pub enum FocusInitiator {
6193    /// The operation is initiated by this document and to be broadcasted
6194    /// through the constellation.
6195    Local,
6196    /// The operation is initiated somewhere else, and we are updating our
6197    /// internal state accordingly.
6198    Remote,
6199}
6200
6201/// Focus events
6202pub(crate) enum FocusEventType {
6203    Focus, // Element gained focus. Doesn't bubble.
6204    Blur,  // Element lost focus. Doesn't bubble.
6205}
6206
6207#[derive(JSTraceable, MallocSizeOf)]
6208pub(crate) enum AnimationFrameCallback {
6209    DevtoolsFramerateTick {
6210        actor_name: String,
6211    },
6212    FrameRequestCallback {
6213        #[conditional_malloc_size_of]
6214        callback: Rc<FrameRequestCallback>,
6215    },
6216}
6217
6218impl AnimationFrameCallback {
6219    fn call(&self, document: &Document, now: f64, can_gc: CanGc) {
6220        match *self {
6221            AnimationFrameCallback::DevtoolsFramerateTick { ref actor_name } => {
6222                let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name.clone(), now);
6223                let devtools_sender = document.window().as_global_scope().devtools_chan().unwrap();
6224                devtools_sender.send(msg).unwrap();
6225            },
6226            AnimationFrameCallback::FrameRequestCallback { ref callback } => {
6227                // TODO(jdm): The spec says that any exceptions should be suppressed:
6228                // https://github.com/servo/servo/issues/6928
6229                let _ = callback.Call__(Finite::wrap(now), ExceptionHandling::Report, can_gc);
6230            },
6231        }
6232    }
6233}
6234
6235#[derive(Default, JSTraceable, MallocSizeOf)]
6236#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
6237struct PendingInOrderScriptVec {
6238    scripts: DomRefCell<VecDeque<PendingScript>>,
6239}
6240
6241impl PendingInOrderScriptVec {
6242    fn is_empty(&self) -> bool {
6243        self.scripts.borrow().is_empty()
6244    }
6245
6246    fn push(&self, element: &HTMLScriptElement) {
6247        self.scripts
6248            .borrow_mut()
6249            .push_back(PendingScript::new(element));
6250    }
6251
6252    fn loaded(&self, element: &HTMLScriptElement, result: ScriptResult) {
6253        let mut scripts = self.scripts.borrow_mut();
6254        let entry = scripts
6255            .iter_mut()
6256            .find(|entry| &*entry.element == element)
6257            .unwrap();
6258        entry.loaded(result);
6259    }
6260
6261    fn take_next_ready_to_be_executed(&self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
6262        let mut scripts = self.scripts.borrow_mut();
6263        let pair = scripts.front_mut()?.take_result()?;
6264        scripts.pop_front();
6265        Some(pair)
6266    }
6267
6268    fn clear(&self) {
6269        *self.scripts.borrow_mut() = Default::default();
6270    }
6271}
6272
6273#[derive(JSTraceable, MallocSizeOf)]
6274#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
6275struct PendingScript {
6276    element: Dom<HTMLScriptElement>,
6277    // TODO(sagudev): could this be all no_trace?
6278    load: Option<ScriptResult>,
6279}
6280
6281impl PendingScript {
6282    fn new(element: &HTMLScriptElement) -> Self {
6283        Self {
6284            element: Dom::from_ref(element),
6285            load: None,
6286        }
6287    }
6288
6289    fn new_with_load(element: &HTMLScriptElement, load: Option<ScriptResult>) -> Self {
6290        Self {
6291            element: Dom::from_ref(element),
6292            load,
6293        }
6294    }
6295
6296    fn loaded(&mut self, result: ScriptResult) {
6297        assert!(self.load.is_none());
6298        self.load = Some(result);
6299    }
6300
6301    fn take_result(&mut self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
6302        self.load
6303            .take()
6304            .map(|result| (DomRoot::from_ref(&*self.element), result))
6305    }
6306}
6307
6308fn is_named_element_with_name_attribute(elem: &Element) -> bool {
6309    let type_ = match elem.upcast::<Node>().type_id() {
6310        NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
6311        _ => return false,
6312    };
6313    match type_ {
6314        HTMLElementTypeId::HTMLFormElement |
6315        HTMLElementTypeId::HTMLIFrameElement |
6316        HTMLElementTypeId::HTMLImageElement => true,
6317        // TODO handle <embed> and <object>; these depend on whether the element is
6318        // “exposed”, a concept that doesn’t fully make sense until embed/object
6319        // behaviour is actually implemented
6320        _ => false,
6321    }
6322}
6323
6324fn is_named_element_with_id_attribute(elem: &Element) -> bool {
6325    // TODO handle <embed> and <object>; these depend on whether the element is
6326    // “exposed”, a concept that doesn’t fully make sense until embed/object
6327    // behaviour is actually implemented
6328    elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty())
6329}
6330
6331impl DocumentHelpers for Document {
6332    fn ensure_safe_to_run_script_or_layout(&self) {
6333        Document::ensure_safe_to_run_script_or_layout(self)
6334    }
6335}