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()
4396            .as_global_scope()
4397            .perform_a_microtask_checkpoint(can_gc);
4398
4399        // Steps 4 through 7 occur inside `send_pending_events().`
4400        let _realm = enter_realm(self);
4401        self.animations().send_pending_events(self.window(), can_gc);
4402    }
4403
4404    pub(crate) fn image_animation_manager(&self) -> Ref<'_, ImageAnimationManager> {
4405        self.image_animation_manager.borrow()
4406    }
4407
4408    pub(crate) fn set_has_pending_animated_image_update(&self) {
4409        self.has_pending_animated_image_update.set(true);
4410    }
4411
4412    /// <https://html.spec.whatwg.org/multipage/#shared-declarative-refresh-steps>
4413    pub(crate) fn shared_declarative_refresh_steps(&self, content: &[u8]) {
4414        // 1. If document's will declaratively refresh is true, then return.
4415        if self.will_declaratively_refresh() {
4416            return;
4417        }
4418
4419        // 2-11 Parsing
4420        static REFRESH_REGEX: LazyLock<Regex> = LazyLock::new(|| {
4421            // s flag is used to match . on newlines since the only places we use . in the
4422            // regex is to go "to end of the string"
4423            // (?s-u:.) is used to consume invalid unicode bytes
4424            Regex::new(
4425                r#"(?xs)
4426                    ^
4427                    \s* # 3
4428                    ((?<time>[0-9]+)|\.) # 5-6
4429                    [0-9.]* # 8
4430                    (
4431                        (
4432                            (\s*;|\s*,|\s) # 10.3
4433                            \s* # 10.4
4434                        )
4435                        (
4436                            (
4437                                (U|u)(R|r)(L|l) # 11.2-11.4
4438                                \s*=\s* # 11.5-11.7
4439                            )?
4440                        ('(?<url1>[^']*)'(?s-u:.)*|"(?<url2>[^"]*)"(?s-u:.)*|['"]?(?<url3>(?s-u:.)*)) # 11.8 - 11.10
4441                        |
4442                        (?<url4>(?s-u:.)*)
4443                    )
4444                )?
4445                $
4446            "#,
4447            )
4448            .unwrap()
4449        });
4450
4451        // 9. Let urlRecord be document's URL.
4452        let mut url_record = self.url();
4453        let captures = if let Some(captures) = REFRESH_REGEX.captures(content) {
4454            captures
4455        } else {
4456            return;
4457        };
4458        let time = if let Some(time_string) = captures.name("time") {
4459            u64::from_str(&String::from_utf8_lossy(time_string.as_bytes())).unwrap_or(0)
4460        } else {
4461            0
4462        };
4463        let captured_url = captures.name("url1").or(captures
4464            .name("url2")
4465            .or(captures.name("url3").or(captures.name("url4"))));
4466
4467        // 11.11 Parse: Set urlRecord to the result of encoding-parsing a URL given urlString, relative to document.
4468        if let Some(url_match) = captured_url {
4469            url_record = if let Ok(url) = ServoUrl::parse_with_base(
4470                Some(&url_record),
4471                &String::from_utf8_lossy(url_match.as_bytes()),
4472            ) {
4473                info!("Refresh to {}", url.debug_compact());
4474                url
4475            } else {
4476                // 11.12 If urlRecord is failure, then return.
4477                return;
4478            }
4479        }
4480        // 12. Set document's will declaratively refresh to true.
4481        if self.completely_loaded() {
4482            // TODO: handle active sandboxing flag
4483            self.window.as_global_scope().schedule_callback(
4484                OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
4485                    window: DomRoot::from_ref(self.window()),
4486                    url: url_record,
4487                }),
4488                Duration::from_secs(time),
4489            );
4490            self.set_declarative_refresh(DeclarativeRefresh::CreatedAfterLoad);
4491        } else {
4492            self.set_declarative_refresh(DeclarativeRefresh::PendingLoad {
4493                url: url_record,
4494                time,
4495            });
4496        }
4497    }
4498
4499    pub(crate) fn will_declaratively_refresh(&self) -> bool {
4500        self.declarative_refresh.borrow().is_some()
4501    }
4502    pub(crate) fn set_declarative_refresh(&self, refresh: DeclarativeRefresh) {
4503        *self.declarative_refresh.borrow_mut() = Some(refresh);
4504    }
4505
4506    /// <https://html.spec.whatwg.org/multipage/#visibility-state>
4507    fn update_visibility_state(&self, visibility_state: DocumentVisibilityState, can_gc: CanGc) {
4508        // Step 1 If document's visibility state equals visibilityState, then return.
4509        if self.visibility_state.get() == visibility_state {
4510            return;
4511        }
4512        // Step 2 Set document's visibility state to visibilityState.
4513        self.visibility_state.set(visibility_state);
4514        // Step 3 Queue a new VisibilityStateEntry whose visibility state is visibilityState and whose timestamp is
4515        // the current high resolution time given document's relevant global object.
4516        let entry = VisibilityStateEntry::new(
4517            &self.global(),
4518            visibility_state,
4519            CrossProcessInstant::now(),
4520            can_gc,
4521        );
4522        self.window
4523            .Performance()
4524            .queue_entry(entry.upcast::<PerformanceEntry>(), can_gc);
4525
4526        // Step 4 Run the screen orientation change steps with document.
4527        // TODO ScreenOrientation hasn't implemented yet
4528
4529        // Step 5 Run the view transition page visibility change steps with document.
4530        // TODO ViewTransition hasn't implemented yet
4531
4532        // Step 6 Run any page visibility change steps which may be defined in other specifications, with visibility
4533        // state and document. Any other specs' visibility steps will go here.
4534
4535        // <https://www.w3.org/TR/gamepad/#handling-visibility-change>
4536        if visibility_state == DocumentVisibilityState::Hidden {
4537            self.window
4538                .Navigator()
4539                .GetGamepads()
4540                .iter_mut()
4541                .for_each(|gamepad| {
4542                    if let Some(g) = gamepad {
4543                        g.vibration_actuator().handle_visibility_change();
4544                    }
4545                });
4546        }
4547
4548        // Step 7 Fire an event named visibilitychange at document, with its bubbles attribute initialized to true.
4549        self.upcast::<EventTarget>()
4550            .fire_bubbling_event(atom!("visibilitychange"), can_gc);
4551    }
4552
4553    /// <https://html.spec.whatwg.org/multipage/#is-initial-about:blank>
4554    pub(crate) fn is_initial_about_blank(&self) -> bool {
4555        self.is_initial_about_blank.get()
4556    }
4557
4558    /// <https://dom.spec.whatwg.org/#document-allow-declarative-shadow-roots>
4559    pub(crate) fn allow_declarative_shadow_roots(&self) -> bool {
4560        self.allow_declarative_shadow_roots.get()
4561    }
4562
4563    pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
4564        self.has_trustworthy_ancestor_origin.get()
4565    }
4566
4567    pub(crate) fn has_trustworthy_ancestor_or_current_origin(&self) -> bool {
4568        self.has_trustworthy_ancestor_origin.get() ||
4569            self.origin().immutable().is_potentially_trustworthy()
4570    }
4571
4572    pub(crate) fn highlight_dom_node(&self, node: Option<&Node>) {
4573        self.highlighted_dom_node.set(node);
4574        self.add_restyle_reason(RestyleReason::HighlightedDOMNodeChanged);
4575    }
4576
4577    pub(crate) fn highlighted_dom_node(&self) -> Option<DomRoot<Node>> {
4578        self.highlighted_dom_node.get()
4579    }
4580
4581    pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
4582        self.custom_element_reaction_stack.clone()
4583    }
4584
4585    pub(crate) fn has_active_sandboxing_flag(&self, flag: SandboxingFlagSet) -> bool {
4586        self.active_sandboxing_flag_set.get().contains(flag)
4587    }
4588
4589    pub(crate) fn set_active_sandboxing_flag_set(&self, flags: SandboxingFlagSet) {
4590        self.active_sandboxing_flag_set.set(flags)
4591    }
4592
4593    pub(crate) fn creation_sandboxing_flag_set(&self) -> SandboxingFlagSet {
4594        self.creation_sandboxing_flag_set.get()
4595    }
4596
4597    pub(crate) fn creation_sandboxing_flag_set_considering_parent_iframe(
4598        &self,
4599    ) -> SandboxingFlagSet {
4600        self.window()
4601            .window_proxy()
4602            .frame_element()
4603            .and_then(|element| element.downcast::<HTMLIFrameElement>())
4604            .map(HTMLIFrameElement::sandboxing_flag_set)
4605            .unwrap_or_else(|| self.creation_sandboxing_flag_set())
4606    }
4607
4608    pub(crate) fn viewport_scrolling_box(&self, flags: ScrollContainerQueryFlags) -> ScrollingBox {
4609        self.window()
4610            .scrolling_box_query(None, flags)
4611            .expect("We should always have a ScrollingBox for the Viewport")
4612    }
4613
4614    pub(crate) fn notify_embedder_favicon(&self) {
4615        if let Some(ref image) = *self.favicon.borrow() {
4616            self.send_to_embedder(EmbedderMsg::NewFavicon(self.webview_id(), image.clone()));
4617        }
4618    }
4619
4620    pub(crate) fn set_favicon(&self, favicon: Image) {
4621        *self.favicon.borrow_mut() = Some(favicon);
4622        self.notify_embedder_favicon();
4623    }
4624}
4625
4626#[allow(non_snake_case)]
4627impl DocumentMethods<crate::DomTypeHolder> for Document {
4628    /// <https://dom.spec.whatwg.org/#dom-document-document>
4629    fn Constructor(
4630        window: &Window,
4631        proto: Option<HandleObject>,
4632        can_gc: CanGc,
4633    ) -> Fallible<DomRoot<Document>> {
4634        let doc = window.Document();
4635        let docloader = DocumentLoader::new(&doc.loader());
4636        Ok(Document::new_with_proto(
4637            window,
4638            proto,
4639            HasBrowsingContext::No,
4640            None,
4641            doc.origin().clone(),
4642            IsHTMLDocument::NonHTMLDocument,
4643            None,
4644            None,
4645            DocumentActivity::Inactive,
4646            DocumentSource::NotFromParser,
4647            docloader,
4648            None,
4649            None,
4650            Default::default(),
4651            false,
4652            doc.allow_declarative_shadow_roots(),
4653            Some(doc.insecure_requests_policy()),
4654            doc.has_trustworthy_ancestor_or_current_origin(),
4655            doc.custom_element_reaction_stack(),
4656            doc.active_sandboxing_flag_set.get(),
4657            can_gc,
4658        ))
4659    }
4660
4661    /// <https://html.spec.whatwg.org/multipage/#dom-parsehtmlunsafe>
4662    fn ParseHTMLUnsafe(
4663        window: &Window,
4664        s: TrustedHTMLOrString,
4665        can_gc: CanGc,
4666    ) -> Fallible<DomRoot<Self>> {
4667        // Step 1. Let compliantHTML be the result of invoking the
4668        // Get Trusted Type compliant string algorithm with TrustedHTML, the current global object,
4669        // html, "Document parseHTMLUnsafe", and "script".
4670        let compliant_html = TrustedHTML::get_trusted_script_compliant_string(
4671            window.as_global_scope(),
4672            s,
4673            "Document parseHTMLUnsafe",
4674            can_gc,
4675        )?;
4676
4677        let url = window.get_url();
4678        let doc = window.Document();
4679        let loader = DocumentLoader::new(&doc.loader());
4680
4681        let content_type = "text/html"
4682            .parse()
4683            .expect("Supported type is not a MIME type");
4684        // Step 2. Let document be a new Document, whose content type is "text/html".
4685        // Step 3. Set document's allow declarative shadow roots to true.
4686        let document = Document::new(
4687            window,
4688            HasBrowsingContext::No,
4689            Some(ServoUrl::parse("about:blank").unwrap()),
4690            doc.origin().clone(),
4691            IsHTMLDocument::HTMLDocument,
4692            Some(content_type),
4693            None,
4694            DocumentActivity::Inactive,
4695            DocumentSource::FromParser,
4696            loader,
4697            None,
4698            None,
4699            Default::default(),
4700            false,
4701            true,
4702            Some(doc.insecure_requests_policy()),
4703            doc.has_trustworthy_ancestor_or_current_origin(),
4704            doc.custom_element_reaction_stack(),
4705            doc.creation_sandboxing_flag_set(),
4706            can_gc,
4707        );
4708        // Step 4. Parse HTML from string given document and compliantHTML.
4709        ServoParser::parse_html_document(&document, Some(compliant_html), url, can_gc);
4710        // Step 5. Return document.
4711        document.set_ready_state(DocumentReadyState::Complete, can_gc);
4712        Ok(document)
4713    }
4714
4715    /// <https://w3c.github.io/editing/ActiveDocuments/execCommand.html#querycommandsupported()>
4716    fn QueryCommandSupported(&self, _command: DOMString) -> bool {
4717        false
4718    }
4719
4720    /// <https://drafts.csswg.org/cssom/#dom-document-stylesheets>
4721    fn StyleSheets(&self, can_gc: CanGc) -> DomRoot<StyleSheetList> {
4722        self.stylesheet_list.or_init(|| {
4723            StyleSheetList::new(
4724                &self.window,
4725                StyleSheetListOwner::Document(Dom::from_ref(self)),
4726                can_gc,
4727            )
4728        })
4729    }
4730
4731    /// <https://dom.spec.whatwg.org/#dom-document-implementation>
4732    fn Implementation(&self, can_gc: CanGc) -> DomRoot<DOMImplementation> {
4733        self.implementation
4734            .or_init(|| DOMImplementation::new(self, can_gc))
4735    }
4736
4737    /// <https://dom.spec.whatwg.org/#dom-document-url>
4738    fn URL(&self) -> USVString {
4739        USVString(String::from(self.url().as_str()))
4740    }
4741
4742    /// <https://html.spec.whatwg.org/multipage/#dom-document-activeelement>
4743    fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
4744        self.document_or_shadow_root.get_active_element(
4745            self.get_focused_element(),
4746            self.GetBody(),
4747            self.GetDocumentElement(),
4748        )
4749    }
4750
4751    /// <https://html.spec.whatwg.org/multipage/#dom-document-hasfocus>
4752    fn HasFocus(&self) -> bool {
4753        // <https://html.spec.whatwg.org/multipage/#has-focus-steps>
4754        //
4755        // > The has focus steps, given a `Document` object `target`, are as
4756        // > follows:
4757        // >
4758        // > 1. If `target`'s browsing context's top-level browsing context does
4759        // >    not have system focus, then return false.
4760
4761        // > 2. Let `candidate` be `target`'s browsing context's top-level
4762        // >    browsing context's active document.
4763        // >
4764        // > 3. While true:
4765        // >
4766        // >    3.1. If `candidate` is target, then return true.
4767        // >
4768        // >    3.2. If the focused area of `candidate` is a browsing context
4769        // >         container with a non-null nested browsing context, then set
4770        // >         `candidate` to the active document of that browsing context
4771        // >         container's nested browsing context.
4772        // >
4773        // >    3.3. Otherwise, return false.
4774        if self.window().parent_info().is_none() {
4775            // 2 → 3 → (3.1 || ⋯ → 3.3)
4776            self.is_fully_active()
4777        } else {
4778            // 2 → 3 → 3.2 → (⋯ → 3.1 || ⋯ → 3.3)
4779            self.is_fully_active() && self.has_focus.get()
4780        }
4781    }
4782
4783    /// <https://html.spec.whatwg.org/multipage/#dom-document-domain>
4784    fn Domain(&self) -> DOMString {
4785        // Step 1.
4786        if !self.has_browsing_context {
4787            return DOMString::new();
4788        }
4789
4790        // Step 2.
4791        match self.origin.effective_domain() {
4792            // Step 3.
4793            None => DOMString::new(),
4794            // Step 4.
4795            Some(Host::Domain(domain)) => DOMString::from(domain),
4796            Some(host) => DOMString::from(host.to_string()),
4797        }
4798    }
4799
4800    /// <https://html.spec.whatwg.org/multipage/#dom-document-domain>
4801    fn SetDomain(&self, value: DOMString) -> ErrorResult {
4802        // Step 1.
4803        if !self.has_browsing_context {
4804            return Err(Error::Security);
4805        }
4806
4807        // Step 2. If this Document object's active sandboxing flag set has its sandboxed
4808        // document.domain browsing context flag set, then throw a "SecurityError" DOMException.
4809        if self.has_active_sandboxing_flag(
4810            SandboxingFlagSet::SANDBOXED_DOCUMENT_DOMAIN_BROWSING_CONTEXT_FLAG,
4811        ) {
4812            return Err(Error::Security);
4813        }
4814
4815        // Steps 3-4.
4816        let effective_domain = match self.origin.effective_domain() {
4817            Some(effective_domain) => effective_domain,
4818            None => return Err(Error::Security),
4819        };
4820
4821        // Step 5
4822        let host = match get_registrable_domain_suffix_of_or_is_equal_to(&value, effective_domain) {
4823            None => return Err(Error::Security),
4824            Some(host) => host,
4825        };
4826
4827        // Step 6
4828        self.origin.set_domain(host);
4829
4830        Ok(())
4831    }
4832
4833    /// <https://html.spec.whatwg.org/multipage/#dom-document-referrer>
4834    fn Referrer(&self) -> DOMString {
4835        match self.referrer {
4836            Some(ref referrer) => DOMString::from(referrer.to_string()),
4837            None => DOMString::new(),
4838        }
4839    }
4840
4841    /// <https://dom.spec.whatwg.org/#dom-document-documenturi>
4842    fn DocumentURI(&self) -> USVString {
4843        self.URL()
4844    }
4845
4846    /// <https://dom.spec.whatwg.org/#dom-document-compatmode>
4847    fn CompatMode(&self) -> DOMString {
4848        DOMString::from(match self.quirks_mode.get() {
4849            QuirksMode::LimitedQuirks | QuirksMode::NoQuirks => "CSS1Compat",
4850            QuirksMode::Quirks => "BackCompat",
4851        })
4852    }
4853
4854    /// <https://dom.spec.whatwg.org/#dom-document-characterset>
4855    fn CharacterSet(&self) -> DOMString {
4856        DOMString::from(self.encoding.get().name())
4857    }
4858
4859    /// <https://dom.spec.whatwg.org/#dom-document-charset>
4860    fn Charset(&self) -> DOMString {
4861        self.CharacterSet()
4862    }
4863
4864    /// <https://dom.spec.whatwg.org/#dom-document-inputencoding>
4865    fn InputEncoding(&self) -> DOMString {
4866        self.CharacterSet()
4867    }
4868
4869    /// <https://dom.spec.whatwg.org/#dom-document-content_type>
4870    fn ContentType(&self) -> DOMString {
4871        DOMString::from(self.content_type.to_string())
4872    }
4873
4874    /// <https://dom.spec.whatwg.org/#dom-document-doctype>
4875    fn GetDoctype(&self) -> Option<DomRoot<DocumentType>> {
4876        self.upcast::<Node>()
4877            .children()
4878            .filter_map(DomRoot::downcast)
4879            .next()
4880    }
4881
4882    /// <https://dom.spec.whatwg.org/#dom-document-documentelement>
4883    fn GetDocumentElement(&self) -> Option<DomRoot<Element>> {
4884        self.upcast::<Node>().child_elements().next()
4885    }
4886
4887    /// <https://dom.spec.whatwg.org/#dom-document-getelementsbytagname>
4888    fn GetElementsByTagName(
4889        &self,
4890        qualified_name: DOMString,
4891        can_gc: CanGc,
4892    ) -> DomRoot<HTMLCollection> {
4893        let qualified_name = LocalName::from(qualified_name);
4894        if let Some(entry) = self.tag_map.borrow_mut().get(&qualified_name) {
4895            return DomRoot::from_ref(entry);
4896        }
4897        let result = HTMLCollection::by_qualified_name(
4898            &self.window,
4899            self.upcast(),
4900            qualified_name.clone(),
4901            can_gc,
4902        );
4903        self.tag_map
4904            .borrow_mut()
4905            .insert(qualified_name, Dom::from_ref(&*result));
4906        result
4907    }
4908
4909    /// <https://dom.spec.whatwg.org/#dom-document-getelementsbytagnamens>
4910    fn GetElementsByTagNameNS(
4911        &self,
4912        maybe_ns: Option<DOMString>,
4913        tag_name: DOMString,
4914        can_gc: CanGc,
4915    ) -> DomRoot<HTMLCollection> {
4916        let ns = namespace_from_domstring(maybe_ns);
4917        let local = LocalName::from(tag_name);
4918        let qname = QualName::new(None, ns, local);
4919        if let Some(collection) = self.tagns_map.borrow().get(&qname) {
4920            return DomRoot::from_ref(collection);
4921        }
4922        let result =
4923            HTMLCollection::by_qual_tag_name(&self.window, self.upcast(), qname.clone(), can_gc);
4924        self.tagns_map
4925            .borrow_mut()
4926            .insert(qname, Dom::from_ref(&*result));
4927        result
4928    }
4929
4930    /// <https://dom.spec.whatwg.org/#dom-document-getelementsbyclassname>
4931    fn GetElementsByClassName(&self, classes: DOMString, can_gc: CanGc) -> DomRoot<HTMLCollection> {
4932        let class_atoms: Vec<Atom> = split_html_space_chars(&classes.str())
4933            .map(Atom::from)
4934            .collect();
4935        if let Some(collection) = self.classes_map.borrow().get(&class_atoms) {
4936            return DomRoot::from_ref(collection);
4937        }
4938        let result = HTMLCollection::by_atomic_class_name(
4939            &self.window,
4940            self.upcast(),
4941            class_atoms.clone(),
4942            can_gc,
4943        );
4944        self.classes_map
4945            .borrow_mut()
4946            .insert(class_atoms, Dom::from_ref(&*result));
4947        result
4948    }
4949
4950    /// <https://dom.spec.whatwg.org/#dom-nonelementparentnode-getelementbyid>
4951    fn GetElementById(&self, id: DOMString) -> Option<DomRoot<Element>> {
4952        self.get_element_by_id(&Atom::from(id))
4953    }
4954
4955    /// <https://dom.spec.whatwg.org/#dom-document-createelement>
4956    fn CreateElement(
4957        &self,
4958        mut local_name: DOMString,
4959        options: StringOrElementCreationOptions,
4960        can_gc: CanGc,
4961    ) -> Fallible<DomRoot<Element>> {
4962        // Step 1. If localName is not a valid element local name,
4963        //      then throw an "InvalidCharacterError" DOMException.
4964        if !is_valid_element_local_name(&local_name.str()) {
4965            debug!("Not a valid element name");
4966            return Err(Error::InvalidCharacter(None));
4967        }
4968
4969        if self.is_html_document {
4970            local_name.make_ascii_lowercase();
4971        }
4972
4973        let ns = if self.is_html_document || self.is_xhtml_document() {
4974            ns!(html)
4975        } else {
4976            ns!()
4977        };
4978
4979        let name = QualName::new(None, ns, LocalName::from(local_name));
4980        let is = match options {
4981            StringOrElementCreationOptions::String(_) => None,
4982            StringOrElementCreationOptions::ElementCreationOptions(options) => {
4983                options.is.as_ref().map(LocalName::from)
4984            },
4985        };
4986        Ok(Element::create(
4987            name,
4988            is,
4989            self,
4990            ElementCreator::ScriptCreated,
4991            CustomElementCreationMode::Synchronous,
4992            None,
4993            can_gc,
4994        ))
4995    }
4996
4997    /// <https://dom.spec.whatwg.org/#dom-document-createelementns>
4998    fn CreateElementNS(
4999        &self,
5000        namespace: Option<DOMString>,
5001        qualified_name: DOMString,
5002        options: StringOrElementCreationOptions,
5003        can_gc: CanGc,
5004    ) -> Fallible<DomRoot<Element>> {
5005        // Step 1. Let (namespace, prefix, localName) be the result of
5006        //      validating and extracting namespace and qualifiedName given "element".
5007        let context = domname::Context::Element;
5008        let (namespace, prefix, local_name) =
5009            domname::validate_and_extract(namespace, &qualified_name, context)?;
5010
5011        // Step 2. Let is be null.
5012        // Step 3. If options is a dictionary and options["is"] exists, then set is to it.
5013        let name = QualName::new(prefix, namespace, local_name);
5014        let is = match options {
5015            StringOrElementCreationOptions::String(_) => None,
5016            StringOrElementCreationOptions::ElementCreationOptions(options) => {
5017                options.is.as_ref().map(LocalName::from)
5018            },
5019        };
5020
5021        // Step 4. Return the result of creating an element given document, localName, namespace, prefix, is, and true.
5022        Ok(Element::create(
5023            name,
5024            is,
5025            self,
5026            ElementCreator::ScriptCreated,
5027            CustomElementCreationMode::Synchronous,
5028            None,
5029            can_gc,
5030        ))
5031    }
5032
5033    /// <https://dom.spec.whatwg.org/#dom-document-createattribute>
5034    fn CreateAttribute(&self, mut local_name: DOMString, can_gc: CanGc) -> Fallible<DomRoot<Attr>> {
5035        // Step 1. If localName is not a valid attribute local name,
5036        //      then throw an "InvalidCharacterError" DOMException
5037        if !is_valid_attribute_local_name(&local_name.str()) {
5038            debug!("Not a valid attribute name");
5039            return Err(Error::InvalidCharacter(None));
5040        }
5041        if self.is_html_document {
5042            local_name.make_ascii_lowercase();
5043        }
5044        let name = LocalName::from(local_name);
5045        let value = AttrValue::String("".to_owned());
5046
5047        Ok(Attr::new(
5048            self,
5049            name.clone(),
5050            value,
5051            name,
5052            ns!(),
5053            None,
5054            None,
5055            can_gc,
5056        ))
5057    }
5058
5059    /// <https://dom.spec.whatwg.org/#dom-document-createattributens>
5060    fn CreateAttributeNS(
5061        &self,
5062        namespace: Option<DOMString>,
5063        qualified_name: DOMString,
5064        can_gc: CanGc,
5065    ) -> Fallible<DomRoot<Attr>> {
5066        // Step 1. Let (namespace, prefix, localName) be the result of validating and
5067        //      extracting namespace and qualifiedName given "attribute".
5068        let context = domname::Context::Attribute;
5069        let (namespace, prefix, local_name) =
5070            domname::validate_and_extract(namespace, &qualified_name, context)?;
5071        let value = AttrValue::String("".to_owned());
5072        let qualified_name = LocalName::from(qualified_name);
5073        Ok(Attr::new(
5074            self,
5075            local_name,
5076            value,
5077            qualified_name,
5078            namespace,
5079            prefix,
5080            None,
5081            can_gc,
5082        ))
5083    }
5084
5085    /// <https://dom.spec.whatwg.org/#dom-document-createdocumentfragment>
5086    fn CreateDocumentFragment(&self, can_gc: CanGc) -> DomRoot<DocumentFragment> {
5087        DocumentFragment::new(self, can_gc)
5088    }
5089
5090    /// <https://dom.spec.whatwg.org/#dom-document-createtextnode>
5091    fn CreateTextNode(&self, data: DOMString, can_gc: CanGc) -> DomRoot<Text> {
5092        Text::new(data, self, can_gc)
5093    }
5094
5095    /// <https://dom.spec.whatwg.org/#dom-document-createcdatasection>
5096    fn CreateCDATASection(
5097        &self,
5098        data: DOMString,
5099        can_gc: CanGc,
5100    ) -> Fallible<DomRoot<CDATASection>> {
5101        // Step 1
5102        if self.is_html_document {
5103            return Err(Error::NotSupported);
5104        }
5105
5106        // Step 2
5107        if data.contains("]]>") {
5108            return Err(Error::InvalidCharacter(None));
5109        }
5110
5111        // Step 3
5112        Ok(CDATASection::new(data, self, can_gc))
5113    }
5114
5115    /// <https://dom.spec.whatwg.org/#dom-document-createcomment>
5116    fn CreateComment(&self, data: DOMString, can_gc: CanGc) -> DomRoot<Comment> {
5117        Comment::new(data, self, None, can_gc)
5118    }
5119
5120    /// <https://dom.spec.whatwg.org/#dom-document-createprocessinginstruction>
5121    fn CreateProcessingInstruction(
5122        &self,
5123        target: DOMString,
5124        data: DOMString,
5125        can_gc: CanGc,
5126    ) -> Fallible<DomRoot<ProcessingInstruction>> {
5127        // Step 1. If target does not match the Name production, then throw an "InvalidCharacterError" DOMException.
5128        if !matches_name_production(&target.str()) {
5129            return Err(Error::InvalidCharacter(None));
5130        }
5131
5132        // Step 2.
5133        if data.contains("?>") {
5134            return Err(Error::InvalidCharacter(None));
5135        }
5136
5137        // Step 3.
5138        Ok(ProcessingInstruction::new(target, data, self, can_gc))
5139    }
5140
5141    /// <https://dom.spec.whatwg.org/#dom-document-importnode>
5142    fn ImportNode(&self, node: &Node, deep: bool, can_gc: CanGc) -> Fallible<DomRoot<Node>> {
5143        // Step 1.
5144        if node.is::<Document>() || node.is::<ShadowRoot>() {
5145            return Err(Error::NotSupported);
5146        }
5147
5148        // Step 2.
5149        let clone_children = if deep {
5150            CloneChildrenFlag::CloneChildren
5151        } else {
5152            CloneChildrenFlag::DoNotCloneChildren
5153        };
5154
5155        Ok(Node::clone(node, Some(self), clone_children, can_gc))
5156    }
5157
5158    /// <https://dom.spec.whatwg.org/#dom-document-adoptnode>
5159    fn AdoptNode(&self, node: &Node, can_gc: CanGc) -> Fallible<DomRoot<Node>> {
5160        // Step 1.
5161        if node.is::<Document>() {
5162            return Err(Error::NotSupported);
5163        }
5164
5165        // Step 2.
5166        if node.is::<ShadowRoot>() {
5167            return Err(Error::HierarchyRequest);
5168        }
5169
5170        // Step 3.
5171        Node::adopt(node, self, can_gc);
5172
5173        // Step 4.
5174        Ok(DomRoot::from_ref(node))
5175    }
5176
5177    /// <https://dom.spec.whatwg.org/#dom-document-createevent>
5178    fn CreateEvent(&self, mut interface: DOMString, can_gc: CanGc) -> Fallible<DomRoot<Event>> {
5179        interface.make_ascii_lowercase();
5180        match &*interface.str() {
5181            "beforeunloadevent" => Ok(DomRoot::upcast(BeforeUnloadEvent::new_uninitialized(
5182                &self.window,
5183                can_gc,
5184            ))),
5185            "compositionevent" | "textevent" => Ok(DomRoot::upcast(
5186                CompositionEvent::new_uninitialized(&self.window, can_gc),
5187            )),
5188            "customevent" => Ok(DomRoot::upcast(CustomEvent::new_uninitialized(
5189                self.window.upcast(),
5190                can_gc,
5191            ))),
5192            // FIXME(#25136): devicemotionevent, deviceorientationevent
5193            // FIXME(#7529): dragevent
5194            "events" | "event" | "htmlevents" | "svgevents" => {
5195                Ok(Event::new_uninitialized(self.window.upcast(), can_gc))
5196            },
5197            "focusevent" => Ok(DomRoot::upcast(FocusEvent::new_uninitialized(
5198                &self.window,
5199                can_gc,
5200            ))),
5201            "hashchangeevent" => Ok(DomRoot::upcast(HashChangeEvent::new_uninitialized(
5202                &self.window,
5203                can_gc,
5204            ))),
5205            "keyboardevent" => Ok(DomRoot::upcast(KeyboardEvent::new_uninitialized(
5206                &self.window,
5207                can_gc,
5208            ))),
5209            "messageevent" => Ok(DomRoot::upcast(MessageEvent::new_uninitialized(
5210                self.window.upcast(),
5211                can_gc,
5212            ))),
5213            "mouseevent" | "mouseevents" => Ok(DomRoot::upcast(MouseEvent::new_uninitialized(
5214                &self.window,
5215                can_gc,
5216            ))),
5217            "storageevent" => Ok(DomRoot::upcast(StorageEvent::new_uninitialized(
5218                &self.window,
5219                "".into(),
5220                can_gc,
5221            ))),
5222            "touchevent" => Ok(DomRoot::upcast(DomTouchEvent::new_uninitialized(
5223                &self.window,
5224                &TouchList::new(&self.window, &[], can_gc),
5225                &TouchList::new(&self.window, &[], can_gc),
5226                &TouchList::new(&self.window, &[], can_gc),
5227                can_gc,
5228            ))),
5229            "uievent" | "uievents" => Ok(DomRoot::upcast(UIEvent::new_uninitialized(
5230                &self.window,
5231                can_gc,
5232            ))),
5233            _ => Err(Error::NotSupported),
5234        }
5235    }
5236
5237    /// <https://html.spec.whatwg.org/multipage/#dom-document-lastmodified>
5238    fn LastModified(&self) -> DOMString {
5239        DOMString::from(self.last_modified.as_ref().cloned().unwrap_or_else(|| {
5240            // Ideally this would get the local time using `time`, but `time` always fails to get the local
5241            // timezone on Unix unless the application is single threaded unless the library is explicitly
5242            // set to "unsound" mode. Maybe that's fine, but it needs more investigation. see
5243            // https://nvd.nist.gov/vuln/detail/CVE-2020-26235
5244            // When `time` supports a thread-safe way of getting the local time zone we could use it here.
5245            Local::now().format("%m/%d/%Y %H:%M:%S").to_string()
5246        }))
5247    }
5248
5249    /// <https://dom.spec.whatwg.org/#dom-document-createrange>
5250    fn CreateRange(&self, can_gc: CanGc) -> DomRoot<Range> {
5251        Range::new_with_doc(self, None, can_gc)
5252    }
5253
5254    /// <https://dom.spec.whatwg.org/#dom-document-createnodeiteratorroot-whattoshow-filter>
5255    fn CreateNodeIterator(
5256        &self,
5257        root: &Node,
5258        what_to_show: u32,
5259        filter: Option<Rc<NodeFilter>>,
5260        can_gc: CanGc,
5261    ) -> DomRoot<NodeIterator> {
5262        NodeIterator::new(self, root, what_to_show, filter, can_gc)
5263    }
5264
5265    /// <https://dom.spec.whatwg.org/#dom-document-createtreewalker>
5266    fn CreateTreeWalker(
5267        &self,
5268        root: &Node,
5269        what_to_show: u32,
5270        filter: Option<Rc<NodeFilter>>,
5271    ) -> DomRoot<TreeWalker> {
5272        TreeWalker::new(self, root, what_to_show, filter)
5273    }
5274
5275    /// <https://html.spec.whatwg.org/multipage/#document.title>
5276    fn Title(&self) -> DOMString {
5277        self.title().unwrap_or_else(|| DOMString::from(""))
5278    }
5279
5280    /// <https://html.spec.whatwg.org/multipage/#document.title>
5281    fn SetTitle(&self, title: DOMString, can_gc: CanGc) {
5282        let root = match self.GetDocumentElement() {
5283            Some(root) => root,
5284            None => return,
5285        };
5286
5287        let node = if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
5288            let elem = root.upcast::<Node>().child_elements().find(|node| {
5289                node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
5290            });
5291            match elem {
5292                Some(elem) => DomRoot::upcast::<Node>(elem),
5293                None => {
5294                    let name = QualName::new(None, ns!(svg), local_name!("title"));
5295                    let elem = Element::create(
5296                        name,
5297                        None,
5298                        self,
5299                        ElementCreator::ScriptCreated,
5300                        CustomElementCreationMode::Synchronous,
5301                        None,
5302                        can_gc,
5303                    );
5304                    let parent = root.upcast::<Node>();
5305                    let child = elem.upcast::<Node>();
5306                    parent
5307                        .InsertBefore(child, parent.GetFirstChild().as_deref(), can_gc)
5308                        .unwrap()
5309                },
5310            }
5311        } else if root.namespace() == &ns!(html) {
5312            let elem = root
5313                .upcast::<Node>()
5314                .traverse_preorder(ShadowIncluding::No)
5315                .find(|node| node.is::<HTMLTitleElement>());
5316            match elem {
5317                Some(elem) => elem,
5318                None => match self.GetHead() {
5319                    Some(head) => {
5320                        let name = QualName::new(None, ns!(html), local_name!("title"));
5321                        let elem = Element::create(
5322                            name,
5323                            None,
5324                            self,
5325                            ElementCreator::ScriptCreated,
5326                            CustomElementCreationMode::Synchronous,
5327                            None,
5328                            can_gc,
5329                        );
5330                        head.upcast::<Node>()
5331                            .AppendChild(elem.upcast(), can_gc)
5332                            .unwrap()
5333                    },
5334                    None => return,
5335                },
5336            }
5337        } else {
5338            return;
5339        };
5340
5341        node.set_text_content_for_element(Some(title), can_gc);
5342    }
5343
5344    /// <https://html.spec.whatwg.org/multipage/#dom-document-head>
5345    fn GetHead(&self) -> Option<DomRoot<HTMLHeadElement>> {
5346        self.get_html_element().and_then(|root| {
5347            root.upcast::<Node>()
5348                .children()
5349                .filter_map(DomRoot::downcast)
5350                .next()
5351        })
5352    }
5353
5354    /// <https://html.spec.whatwg.org/multipage/#dom-document-currentscript>
5355    fn GetCurrentScript(&self) -> Option<DomRoot<HTMLScriptElement>> {
5356        self.current_script.get()
5357    }
5358
5359    /// <https://html.spec.whatwg.org/multipage/#dom-document-body>
5360    fn GetBody(&self) -> Option<DomRoot<HTMLElement>> {
5361        self.get_html_element().and_then(|root| {
5362            let node = root.upcast::<Node>();
5363            node.children()
5364                .find(|child| {
5365                    matches!(
5366                        child.type_id(),
5367                        NodeTypeId::Element(ElementTypeId::HTMLElement(
5368                            HTMLElementTypeId::HTMLBodyElement,
5369                        )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
5370                            HTMLElementTypeId::HTMLFrameSetElement,
5371                        ))
5372                    )
5373                })
5374                .map(|node| DomRoot::downcast(node).unwrap())
5375        })
5376    }
5377
5378    /// <https://html.spec.whatwg.org/multipage/#dom-document-body>
5379    fn SetBody(&self, new_body: Option<&HTMLElement>, can_gc: CanGc) -> ErrorResult {
5380        // Step 1.
5381        let new_body = match new_body {
5382            Some(new_body) => new_body,
5383            None => return Err(Error::HierarchyRequest),
5384        };
5385
5386        let node = new_body.upcast::<Node>();
5387        match node.type_id() {
5388            NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) |
5389            NodeTypeId::Element(ElementTypeId::HTMLElement(
5390                HTMLElementTypeId::HTMLFrameSetElement,
5391            )) => {},
5392            _ => return Err(Error::HierarchyRequest),
5393        }
5394
5395        // Step 2.
5396        let old_body = self.GetBody();
5397        if old_body.as_deref() == Some(new_body) {
5398            return Ok(());
5399        }
5400
5401        match (self.GetDocumentElement(), &old_body) {
5402            // Step 3.
5403            (Some(ref root), Some(child)) => {
5404                let root = root.upcast::<Node>();
5405                root.ReplaceChild(new_body.upcast(), child.upcast(), can_gc)
5406                    .unwrap();
5407            },
5408
5409            // Step 4.
5410            (None, _) => return Err(Error::HierarchyRequest),
5411
5412            // Step 5.
5413            (Some(ref root), &None) => {
5414                let root = root.upcast::<Node>();
5415                root.AppendChild(new_body.upcast(), can_gc).unwrap();
5416            },
5417        }
5418        Ok(())
5419    }
5420
5421    /// <https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname>
5422    fn GetElementsByName(&self, name: DOMString, can_gc: CanGc) -> DomRoot<NodeList> {
5423        NodeList::new_elements_by_name_list(self.window(), self, name, can_gc)
5424    }
5425
5426    /// <https://html.spec.whatwg.org/multipage/#dom-document-images>
5427    fn Images(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5428        self.images.or_init(|| {
5429            HTMLCollection::new_with_filter_fn(
5430                &self.window,
5431                self.upcast(),
5432                |element, _| element.is::<HTMLImageElement>(),
5433                can_gc,
5434            )
5435        })
5436    }
5437
5438    /// <https://html.spec.whatwg.org/multipage/#dom-document-embeds>
5439    fn Embeds(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5440        self.embeds.or_init(|| {
5441            HTMLCollection::new_with_filter_fn(
5442                &self.window,
5443                self.upcast(),
5444                |element, _| element.is::<HTMLEmbedElement>(),
5445                can_gc,
5446            )
5447        })
5448    }
5449
5450    /// <https://html.spec.whatwg.org/multipage/#dom-document-plugins>
5451    fn Plugins(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5452        self.Embeds(can_gc)
5453    }
5454
5455    /// <https://html.spec.whatwg.org/multipage/#dom-document-links>
5456    fn Links(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5457        self.links.or_init(|| {
5458            HTMLCollection::new_with_filter_fn(
5459                &self.window,
5460                self.upcast(),
5461                |element, _| {
5462                    (element.is::<HTMLAnchorElement>() || element.is::<HTMLAreaElement>()) &&
5463                        element.has_attribute(&local_name!("href"))
5464                },
5465                can_gc,
5466            )
5467        })
5468    }
5469
5470    /// <https://html.spec.whatwg.org/multipage/#dom-document-forms>
5471    fn Forms(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5472        self.forms.or_init(|| {
5473            HTMLCollection::new_with_filter_fn(
5474                &self.window,
5475                self.upcast(),
5476                |element, _| element.is::<HTMLFormElement>(),
5477                can_gc,
5478            )
5479        })
5480    }
5481
5482    /// <https://html.spec.whatwg.org/multipage/#dom-document-scripts>
5483    fn Scripts(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5484        self.scripts.or_init(|| {
5485            HTMLCollection::new_with_filter_fn(
5486                &self.window,
5487                self.upcast(),
5488                |element, _| element.is::<HTMLScriptElement>(),
5489                can_gc,
5490            )
5491        })
5492    }
5493
5494    /// <https://html.spec.whatwg.org/multipage/#dom-document-anchors>
5495    fn Anchors(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5496        self.anchors.or_init(|| {
5497            HTMLCollection::new_with_filter_fn(
5498                &self.window,
5499                self.upcast(),
5500                |element, _| {
5501                    element.is::<HTMLAnchorElement>() && element.has_attribute(&local_name!("href"))
5502                },
5503                can_gc,
5504            )
5505        })
5506    }
5507
5508    /// <https://html.spec.whatwg.org/multipage/#dom-document-applets>
5509    fn Applets(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5510        self.applets
5511            .or_init(|| HTMLCollection::always_empty(&self.window, self.upcast(), can_gc))
5512    }
5513
5514    /// <https://html.spec.whatwg.org/multipage/#dom-document-location>
5515    fn GetLocation(&self) -> Option<DomRoot<Location>> {
5516        if self.is_fully_active() {
5517            Some(self.window.Location())
5518        } else {
5519            None
5520        }
5521    }
5522
5523    /// <https://dom.spec.whatwg.org/#dom-parentnode-children>
5524    fn Children(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
5525        HTMLCollection::children(&self.window, self.upcast(), can_gc)
5526    }
5527
5528    /// <https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild>
5529    fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
5530        self.upcast::<Node>().child_elements().next()
5531    }
5532
5533    /// <https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild>
5534    fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
5535        self.upcast::<Node>()
5536            .rev_children()
5537            .filter_map(DomRoot::downcast)
5538            .next()
5539    }
5540
5541    /// <https://dom.spec.whatwg.org/#dom-parentnode-childelementcount>
5542    fn ChildElementCount(&self) -> u32 {
5543        self.upcast::<Node>().child_elements().count() as u32
5544    }
5545
5546    /// <https://dom.spec.whatwg.org/#dom-parentnode-prepend>
5547    fn Prepend(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
5548        self.upcast::<Node>().prepend(nodes, can_gc)
5549    }
5550
5551    /// <https://dom.spec.whatwg.org/#dom-parentnode-append>
5552    fn Append(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
5553        self.upcast::<Node>().append(nodes, can_gc)
5554    }
5555
5556    /// <https://dom.spec.whatwg.org/#dom-parentnode-replacechildren>
5557    fn ReplaceChildren(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
5558        self.upcast::<Node>().replace_children(nodes, can_gc)
5559    }
5560
5561    /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
5562    fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
5563        let root = self.upcast::<Node>();
5564        root.query_selector(selectors)
5565    }
5566
5567    /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall>
5568    fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>> {
5569        let root = self.upcast::<Node>();
5570        root.query_selector_all(selectors)
5571    }
5572
5573    /// <https://html.spec.whatwg.org/multipage/#dom-document-readystate>
5574    fn ReadyState(&self) -> DocumentReadyState {
5575        self.ready_state.get()
5576    }
5577
5578    /// <https://html.spec.whatwg.org/multipage/#dom-document-defaultview>
5579    fn GetDefaultView(&self) -> Option<DomRoot<Window>> {
5580        if self.has_browsing_context {
5581            Some(DomRoot::from_ref(&*self.window))
5582        } else {
5583            None
5584        }
5585    }
5586
5587    /// <https://html.spec.whatwg.org/multipage/#dom-document-cookie>
5588    fn GetCookie(&self) -> Fallible<DOMString> {
5589        if self.is_cookie_averse() {
5590            return Ok(DOMString::new());
5591        }
5592
5593        if !self.origin.is_tuple() {
5594            return Err(Error::Security);
5595        }
5596
5597        let url = self.url();
5598        let (tx, rx) = profile_ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
5599        let _ = self
5600            .window
5601            .as_global_scope()
5602            .resource_threads()
5603            .send(GetCookiesForUrl(url, tx, NonHTTP));
5604        let cookies = rx.recv().unwrap();
5605        Ok(cookies.map_or(DOMString::new(), DOMString::from))
5606    }
5607
5608    /// <https://html.spec.whatwg.org/multipage/#dom-document-cookie>
5609    fn SetCookie(&self, cookie: DOMString) -> ErrorResult {
5610        if self.is_cookie_averse() {
5611            return Ok(());
5612        }
5613
5614        if !self.origin.is_tuple() {
5615            return Err(Error::Security);
5616        }
5617
5618        let cookies = if let Some(cookie) = Cookie::parse(cookie.to_string()).ok().map(Serde) {
5619            vec![cookie]
5620        } else {
5621            vec![]
5622        };
5623
5624        let _ = self
5625            .window
5626            .as_global_scope()
5627            .resource_threads()
5628            .send(SetCookiesForUrl(self.url(), cookies, NonHTTP));
5629        Ok(())
5630    }
5631
5632    /// <https://html.spec.whatwg.org/multipage/#dom-document-bgcolor>
5633    fn BgColor(&self) -> DOMString {
5634        self.get_body_attribute(&local_name!("bgcolor"))
5635    }
5636
5637    /// <https://html.spec.whatwg.org/multipage/#dom-document-bgcolor>
5638    fn SetBgColor(&self, value: DOMString, can_gc: CanGc) {
5639        self.set_body_attribute(&local_name!("bgcolor"), value, can_gc)
5640    }
5641
5642    /// <https://html.spec.whatwg.org/multipage/#dom-document-fgcolor>
5643    fn FgColor(&self) -> DOMString {
5644        self.get_body_attribute(&local_name!("text"))
5645    }
5646
5647    /// <https://html.spec.whatwg.org/multipage/#dom-document-fgcolor>
5648    fn SetFgColor(&self, value: DOMString, can_gc: CanGc) {
5649        self.set_body_attribute(&local_name!("text"), value, can_gc)
5650    }
5651
5652    /// <https://html.spec.whatwg.org/multipage/#dom-tree-accessors:dom-document-nameditem-filter>
5653    fn NamedGetter(&self, name: DOMString, can_gc: CanGc) -> Option<NamedPropertyValue> {
5654        if name.is_empty() {
5655            return None;
5656        }
5657        let name = Atom::from(name);
5658
5659        // Step 1. Let elements be the list of named elements with the name name that are in a document tree
5660        // with the Document as their root.
5661        let elements_with_name = self.get_elements_with_name(&name);
5662        let name_iter = elements_with_name
5663            .iter()
5664            .filter(|elem| is_named_element_with_name_attribute(elem));
5665        let elements_with_id = self.get_elements_with_id(&name);
5666        let id_iter = elements_with_id
5667            .iter()
5668            .filter(|elem| is_named_element_with_id_attribute(elem));
5669        let mut elements = name_iter.chain(id_iter);
5670
5671        // Step 2. If elements has only one element, and that element is an iframe element,
5672        // and that iframe element's content navigable is not null, then return the active
5673        // WindowProxy of the element's content navigable.
5674
5675        // NOTE: We have to check if all remaining elements are equal to the first, since
5676        // the same element may appear in both lists.
5677        let first = elements.next()?;
5678        if elements.all(|other| first == other) {
5679            if let Some(nested_window_proxy) = first
5680                .downcast::<HTMLIFrameElement>()
5681                .and_then(|iframe| iframe.GetContentWindow())
5682            {
5683                return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
5684            }
5685
5686            // Step 3. Otherwise, if elements has only one element, return that element.
5687            return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
5688        }
5689
5690        // Step 4. Otherwise, return an HTMLCollection rooted at the Document node,
5691        // whose filter matches only named elements with the name name.
5692        #[derive(JSTraceable, MallocSizeOf)]
5693        struct DocumentNamedGetter {
5694            #[no_trace]
5695            name: Atom,
5696        }
5697        impl CollectionFilter for DocumentNamedGetter {
5698            fn filter(&self, elem: &Element, _root: &Node) -> bool {
5699                let type_ = match elem.upcast::<Node>().type_id() {
5700                    NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
5701                    _ => return false,
5702                };
5703                match type_ {
5704                    HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
5705                        elem.get_name().as_ref() == Some(&self.name)
5706                    },
5707                    HTMLElementTypeId::HTMLImageElement => elem.get_name().is_some_and(|name| {
5708                        name == *self.name ||
5709                            !name.is_empty() && elem.get_id().as_ref() == Some(&self.name)
5710                    }),
5711                    // TODO handle <embed> and <object>; these depend on whether the element is
5712                    // “exposed”, a concept that doesn’t fully make sense until embed/object
5713                    // behaviour is actually implemented
5714                    _ => false,
5715                }
5716            }
5717        }
5718        let collection = HTMLCollection::create(
5719            self.window(),
5720            self.upcast(),
5721            Box::new(DocumentNamedGetter { name }),
5722            can_gc,
5723        );
5724        Some(NamedPropertyValue::HTMLCollection(collection))
5725    }
5726
5727    /// <https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names>
5728    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
5729        let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
5730
5731        let name_map = self.name_map.borrow();
5732        for (name, elements) in &(name_map).0 {
5733            if name.is_empty() {
5734                continue;
5735            }
5736            let mut name_iter = elements
5737                .iter()
5738                .filter(|elem| is_named_element_with_name_attribute(elem));
5739            if let Some(first) = name_iter.next() {
5740                names_with_first_named_element_map.insert(name, first);
5741            }
5742        }
5743        let id_map = self.id_map.borrow();
5744        for (id, elements) in &(id_map).0 {
5745            if id.is_empty() {
5746                continue;
5747            }
5748            let mut id_iter = elements
5749                .iter()
5750                .filter(|elem| is_named_element_with_id_attribute(elem));
5751            if let Some(first) = id_iter.next() {
5752                match names_with_first_named_element_map.entry(id) {
5753                    Vacant(entry) => drop(entry.insert(first)),
5754                    Occupied(mut entry) => {
5755                        if first.upcast::<Node>().is_before(entry.get().upcast()) {
5756                            *entry.get_mut() = first;
5757                        }
5758                    },
5759                }
5760            }
5761        }
5762
5763        let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
5764            names_with_first_named_element_map
5765                .iter()
5766                .map(|(k, v)| (*k, *v))
5767                .collect();
5768        names_with_first_named_element_vec.sort_unstable_by(|a, b| {
5769            if a.1 == b.1 {
5770                // This can happen if an img has an id different from its name,
5771                // spec does not say which string to put first.
5772                a.0.cmp(b.0)
5773            } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
5774                Ordering::Less
5775            } else {
5776                Ordering::Greater
5777            }
5778        });
5779
5780        names_with_first_named_element_vec
5781            .iter()
5782            .map(|(k, _v)| DOMString::from(&***k))
5783            .collect()
5784    }
5785
5786    /// <https://html.spec.whatwg.org/multipage/#dom-document-clear>
5787    fn Clear(&self) {
5788        // This method intentionally does nothing
5789    }
5790
5791    /// <https://html.spec.whatwg.org/multipage/#dom-document-captureevents>
5792    fn CaptureEvents(&self) {
5793        // This method intentionally does nothing
5794    }
5795
5796    /// <https://html.spec.whatwg.org/multipage/#dom-document-releaseevents>
5797    fn ReleaseEvents(&self) {
5798        // This method intentionally does nothing
5799    }
5800
5801    // https://html.spec.whatwg.org/multipage/#globaleventhandlers
5802    global_event_handlers!();
5803
5804    // https://html.spec.whatwg.org/multipage/#handler-onreadystatechange
5805    event_handler!(
5806        readystatechange,
5807        GetOnreadystatechange,
5808        SetOnreadystatechange
5809    );
5810
5811    /// <https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint>
5812    fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
5813        self.document_or_shadow_root.element_from_point(
5814            x,
5815            y,
5816            self.GetDocumentElement(),
5817            self.has_browsing_context,
5818        )
5819    }
5820
5821    /// <https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint>
5822    fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
5823        self.document_or_shadow_root.elements_from_point(
5824            x,
5825            y,
5826            self.GetDocumentElement(),
5827            self.has_browsing_context,
5828        )
5829    }
5830
5831    /// <https://drafts.csswg.org/cssom-view/#dom-document-scrollingelement>
5832    fn GetScrollingElement(&self) -> Option<DomRoot<Element>> {
5833        // Step 1. If the Document is in quirks mode, follow these steps:
5834        if self.quirks_mode() == QuirksMode::Quirks {
5835            // Step 1.1. If the body element exists,
5836            if let Some(ref body) = self.GetBody() {
5837                let e = body.upcast::<Element>();
5838                // and it is not potentially scrollable, return the body element and abort these steps.
5839                // For this purpose, a value of overflow:clip on the body element’s parent element
5840                // must be treated as overflow:hidden.
5841                if !e.is_potentially_scrollable_body_for_scrolling_element() {
5842                    return Some(DomRoot::from_ref(e));
5843                }
5844            }
5845
5846            // Step 1.2. Return null and abort these steps.
5847            return None;
5848        }
5849
5850        // Step 2. If there is a root element, return the root element and abort these steps.
5851        // Step 3. Return null.
5852        self.GetDocumentElement()
5853    }
5854
5855    /// <https://html.spec.whatwg.org/multipage/#dom-document-open>
5856    fn Open(
5857        &self,
5858        _unused1: Option<DOMString>,
5859        _unused2: Option<DOMString>,
5860        can_gc: CanGc,
5861    ) -> Fallible<DomRoot<Document>> {
5862        // Step 1
5863        if !self.is_html_document() {
5864            return Err(Error::InvalidState(None));
5865        }
5866
5867        // Step 2
5868        if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
5869            return Err(Error::InvalidState(None));
5870        }
5871
5872        // Step 3
5873        let entry_responsible_document = GlobalScope::entry().as_window().Document();
5874
5875        // Step 4
5876        if !self.origin.same_origin(&entry_responsible_document.origin) {
5877            return Err(Error::Security);
5878        }
5879
5880        // Step 5
5881        if self
5882            .get_current_parser()
5883            .is_some_and(|parser| parser.is_active())
5884        {
5885            return Ok(DomRoot::from_ref(self));
5886        }
5887
5888        // Step 6
5889        if self.is_prompting_or_unloading() {
5890            return Ok(DomRoot::from_ref(self));
5891        }
5892
5893        // Step 7
5894        if self.active_parser_was_aborted.get() {
5895            return Ok(DomRoot::from_ref(self));
5896        }
5897
5898        // TODO: prompt to unload.
5899        // TODO: set unload_event_start and unload_event_end
5900
5901        self.window().set_navigation_start();
5902
5903        // Step 8
5904        // TODO: https://github.com/servo/servo/issues/21937
5905        if self.has_browsing_context() {
5906            // spec says "stop document loading",
5907            // which is a process that does more than just abort
5908            self.abort(can_gc);
5909        }
5910
5911        // Step 9
5912        for node in self
5913            .upcast::<Node>()
5914            .traverse_preorder(ShadowIncluding::Yes)
5915        {
5916            node.upcast::<EventTarget>().remove_all_listeners();
5917        }
5918
5919        // Step 10
5920        if self.window.Document() == DomRoot::from_ref(self) {
5921            self.window.upcast::<EventTarget>().remove_all_listeners();
5922        }
5923
5924        // Step 11. Replace all with null within document.
5925        Node::replace_all(None, self.upcast::<Node>(), can_gc);
5926
5927        // Specs and tests are in a state of flux about whether
5928        // we want to clear the selection when we remove the contents;
5929        // WPT selection/Document-open.html wants us to not clear it
5930        // as of Feb 1 2020
5931
5932        // Step 12. If document is fully active, then:
5933        if self.is_fully_active() {
5934            // Step 12.1. Let newURL be a copy of entryDocument's URL.
5935            let mut new_url = entry_responsible_document.url();
5936
5937            // Step 12.2. If entryDocument is not document, then set newURL's fragment to null.
5938            if entry_responsible_document != DomRoot::from_ref(self) {
5939                new_url.set_fragment(None);
5940            }
5941
5942            // Step 12.3. Run the URL and history update steps with document and newURL.
5943            // TODO: https://github.com/servo/servo/issues/21939
5944            self.set_url(new_url);
5945        }
5946
5947        // Step 13. Set document's is initial about:blank to false.
5948        self.is_initial_about_blank.set(false);
5949
5950        // Step 14. If document's iframe load in progress flag is set, then set document's mute
5951        // iframe load flag.
5952        // TODO: https://github.com/servo/servo/issues/21938
5953
5954        // Step 15: Set document to no-quirks mode.
5955        self.set_quirks_mode(QuirksMode::NoQuirks);
5956
5957        // Step 16. Create a new HTML parser and associate it with document. This is a
5958        // script-created parser (meaning that it can be closed by the document.open() and
5959        // document.close() methods, and that the tokenizer will wait for an explicit call to
5960        // document.close() before emitting an end-of-file token). The encoding confidence is
5961        // irrelevant.
5962        let resource_threads = self.window.as_global_scope().resource_threads().clone();
5963        *self.loader.borrow_mut() =
5964            DocumentLoader::new_with_threads(resource_threads, Some(self.url()));
5965        ServoParser::parse_html_script_input(self, self.url());
5966
5967        // Step 17. Set the insertion point to point at just before the end of the input stream
5968        // (which at this point will be empty).
5969        // Handled when creating the parser in step 16
5970
5971        // Step 18. Update the current document readiness of document to "loading".
5972        self.ready_state.set(DocumentReadyState::Loading);
5973
5974        // Step 19. Return document.
5975        Ok(DomRoot::from_ref(self))
5976    }
5977
5978    /// <https://html.spec.whatwg.org/multipage/#dom-document-open-window>
5979    fn Open_(
5980        &self,
5981        url: USVString,
5982        target: DOMString,
5983        features: DOMString,
5984        can_gc: CanGc,
5985    ) -> Fallible<Option<DomRoot<WindowProxy>>> {
5986        self.browsing_context()
5987            .ok_or(Error::InvalidAccess)?
5988            .open(url, target, features, can_gc)
5989    }
5990
5991    /// <https://html.spec.whatwg.org/multipage/#dom-document-write>
5992    fn Write(&self, text: Vec<TrustedHTMLOrString>, can_gc: CanGc) -> ErrorResult {
5993        // The document.write(...text) method steps are to run the document write steps
5994        // with this, text, false, and "Document write".
5995        self.write(text, false, "Document", "write", can_gc)
5996    }
5997
5998    /// <https://html.spec.whatwg.org/multipage/#dom-document-writeln>
5999    fn Writeln(&self, text: Vec<TrustedHTMLOrString>, can_gc: CanGc) -> ErrorResult {
6000        // The document.writeln(...text) method steps are to run the document write steps
6001        // with this, text, true, and "Document writeln".
6002        self.write(text, true, "Document", "writeln", can_gc)
6003    }
6004
6005    /// <https://html.spec.whatwg.org/multipage/#dom-document-close>
6006    fn Close(&self, can_gc: CanGc) -> ErrorResult {
6007        if !self.is_html_document() {
6008            // Step 1.
6009            return Err(Error::InvalidState(None));
6010        }
6011
6012        // Step 2.
6013        if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6014            return Err(Error::InvalidState(None));
6015        }
6016
6017        let parser = match self.get_current_parser() {
6018            Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
6019            _ => {
6020                // Step 3.
6021                return Ok(());
6022            },
6023        };
6024
6025        // Step 4-6.
6026        parser.close(can_gc);
6027
6028        Ok(())
6029    }
6030
6031    // https://fullscreen.spec.whatwg.org/#handler-document-onfullscreenerror
6032    event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror);
6033
6034    // https://fullscreen.spec.whatwg.org/#handler-document-onfullscreenchange
6035    event_handler!(
6036        fullscreenchange,
6037        GetOnfullscreenchange,
6038        SetOnfullscreenchange
6039    );
6040
6041    /// <https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled>
6042    fn FullscreenEnabled(&self) -> bool {
6043        self.get_allow_fullscreen()
6044    }
6045
6046    /// <https://fullscreen.spec.whatwg.org/#dom-document-fullscreen>
6047    fn Fullscreen(&self) -> bool {
6048        self.fullscreen_element.get().is_some()
6049    }
6050
6051    /// <https://fullscreen.spec.whatwg.org/#dom-document-fullscreenelement>
6052    fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
6053        // TODO ShadowRoot
6054        self.fullscreen_element.get()
6055    }
6056
6057    /// <https://fullscreen.spec.whatwg.org/#dom-document-exitfullscreen>
6058    fn ExitFullscreen(&self, can_gc: CanGc) -> Rc<Promise> {
6059        self.exit_fullscreen(can_gc)
6060    }
6061
6062    // check-tidy: no specs after this line
6063    // Servo only API to get an instance of the controls of a specific
6064    // media element matching the given id.
6065    fn ServoGetMediaControls(&self, id: DOMString) -> Fallible<DomRoot<ShadowRoot>> {
6066        match self.media_controls.borrow().get(&*id.str()) {
6067            Some(m) => Ok(DomRoot::from_ref(m)),
6068            None => Err(Error::InvalidAccess),
6069        }
6070    }
6071
6072    /// <https://w3c.github.io/selection-api/#dom-document-getselection>
6073    fn GetSelection(&self, can_gc: CanGc) -> Option<DomRoot<Selection>> {
6074        if self.has_browsing_context {
6075            Some(self.selection.or_init(|| Selection::new(self, can_gc)))
6076        } else {
6077            None
6078        }
6079    }
6080
6081    /// <https://drafts.csswg.org/css-font-loading/#font-face-source>
6082    fn Fonts(&self, can_gc: CanGc) -> DomRoot<FontFaceSet> {
6083        self.fonts
6084            .or_init(|| FontFaceSet::new(&self.global(), None, can_gc))
6085    }
6086
6087    /// <https://html.spec.whatwg.org/multipage/#dom-document-hidden>
6088    fn Hidden(&self) -> bool {
6089        self.visibility_state.get() == DocumentVisibilityState::Hidden
6090    }
6091
6092    /// <https://html.spec.whatwg.org/multipage/#dom-document-visibilitystate>
6093    fn VisibilityState(&self) -> DocumentVisibilityState {
6094        self.visibility_state.get()
6095    }
6096
6097    fn CreateExpression(
6098        &self,
6099        expression: DOMString,
6100        resolver: Option<Rc<XPathNSResolver>>,
6101        can_gc: CanGc,
6102    ) -> Fallible<DomRoot<super::types::XPathExpression>> {
6103        let global = self.global();
6104        let window = global.as_window();
6105        let evaluator = XPathEvaluator::new(window, None, can_gc);
6106        XPathEvaluatorMethods::<crate::DomTypeHolder>::CreateExpression(
6107            &*evaluator,
6108            expression,
6109            resolver,
6110            can_gc,
6111        )
6112    }
6113
6114    fn CreateNSResolver(&self, node_resolver: &Node, can_gc: CanGc) -> DomRoot<Node> {
6115        let global = self.global();
6116        let window = global.as_window();
6117        let evaluator = XPathEvaluator::new(window, None, can_gc);
6118        XPathEvaluatorMethods::<crate::DomTypeHolder>::CreateNSResolver(&*evaluator, node_resolver)
6119    }
6120
6121    fn Evaluate(
6122        &self,
6123        expression: DOMString,
6124        context_node: &Node,
6125        resolver: Option<Rc<XPathNSResolver>>,
6126        type_: u16,
6127        result: Option<&super::types::XPathResult>,
6128        can_gc: CanGc,
6129    ) -> Fallible<DomRoot<super::types::XPathResult>> {
6130        let global = self.global();
6131        let window = global.as_window();
6132        let evaluator = XPathEvaluator::new(window, None, can_gc);
6133        XPathEvaluatorMethods::<crate::DomTypeHolder>::Evaluate(
6134            &*evaluator,
6135            expression,
6136            context_node,
6137            resolver,
6138            type_,
6139            result,
6140            can_gc,
6141        )
6142    }
6143
6144    /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
6145    fn AdoptedStyleSheets(&self, context: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
6146        self.adopted_stylesheets_frozen_types.get_or_init(
6147            || {
6148                self.adopted_stylesheets
6149                    .borrow()
6150                    .clone()
6151                    .iter()
6152                    .map(|sheet| sheet.as_rooted())
6153                    .collect()
6154            },
6155            context,
6156            retval,
6157            can_gc,
6158        );
6159    }
6160
6161    /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
6162    fn SetAdoptedStyleSheets(&self, context: JSContext, val: HandleValue) -> ErrorResult {
6163        let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
6164            context,
6165            self.adopted_stylesheets.borrow_mut().as_mut(),
6166            val,
6167            &StyleSheetListOwner::Document(Dom::from_ref(self)),
6168        );
6169
6170        // If update is successful, clear the FrozenArray cache.
6171        if result.is_ok() {
6172            self.adopted_stylesheets_frozen_types.clear()
6173        }
6174
6175        result
6176    }
6177}
6178
6179fn update_with_current_instant(marker: &Cell<Option<CrossProcessInstant>>) {
6180    if marker.get().is_none() {
6181        marker.set(Some(CrossProcessInstant::now()))
6182    }
6183}
6184
6185/// Specifies the type of focus event that is sent to a pipeline
6186#[derive(Clone, Copy, PartialEq)]
6187pub(crate) enum FocusType {
6188    Element, // The first focus message - focus the element itself
6189    Parent,  // Focusing a parent element (an iframe)
6190}
6191
6192/// Specifies the initiator of a focus operation.
6193#[derive(Clone, Copy, PartialEq)]
6194pub enum FocusInitiator {
6195    /// The operation is initiated by this document and to be broadcasted
6196    /// through the constellation.
6197    Local,
6198    /// The operation is initiated somewhere else, and we are updating our
6199    /// internal state accordingly.
6200    Remote,
6201}
6202
6203/// Focus events
6204pub(crate) enum FocusEventType {
6205    Focus, // Element gained focus. Doesn't bubble.
6206    Blur,  // Element lost focus. Doesn't bubble.
6207}
6208
6209#[derive(JSTraceable, MallocSizeOf)]
6210pub(crate) enum AnimationFrameCallback {
6211    DevtoolsFramerateTick {
6212        actor_name: String,
6213    },
6214    FrameRequestCallback {
6215        #[conditional_malloc_size_of]
6216        callback: Rc<FrameRequestCallback>,
6217    },
6218}
6219
6220impl AnimationFrameCallback {
6221    fn call(&self, document: &Document, now: f64, can_gc: CanGc) {
6222        match *self {
6223            AnimationFrameCallback::DevtoolsFramerateTick { ref actor_name } => {
6224                let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name.clone(), now);
6225                let devtools_sender = document.window().as_global_scope().devtools_chan().unwrap();
6226                devtools_sender.send(msg).unwrap();
6227            },
6228            AnimationFrameCallback::FrameRequestCallback { ref callback } => {
6229                // TODO(jdm): The spec says that any exceptions should be suppressed:
6230                // https://github.com/servo/servo/issues/6928
6231                let _ = callback.Call__(Finite::wrap(now), ExceptionHandling::Report, can_gc);
6232            },
6233        }
6234    }
6235}
6236
6237#[derive(Default, JSTraceable, MallocSizeOf)]
6238#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
6239struct PendingInOrderScriptVec {
6240    scripts: DomRefCell<VecDeque<PendingScript>>,
6241}
6242
6243impl PendingInOrderScriptVec {
6244    fn is_empty(&self) -> bool {
6245        self.scripts.borrow().is_empty()
6246    }
6247
6248    fn push(&self, element: &HTMLScriptElement) {
6249        self.scripts
6250            .borrow_mut()
6251            .push_back(PendingScript::new(element));
6252    }
6253
6254    fn loaded(&self, element: &HTMLScriptElement, result: ScriptResult) {
6255        let mut scripts = self.scripts.borrow_mut();
6256        let entry = scripts
6257            .iter_mut()
6258            .find(|entry| &*entry.element == element)
6259            .unwrap();
6260        entry.loaded(result);
6261    }
6262
6263    fn take_next_ready_to_be_executed(&self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
6264        let mut scripts = self.scripts.borrow_mut();
6265        let pair = scripts.front_mut()?.take_result()?;
6266        scripts.pop_front();
6267        Some(pair)
6268    }
6269
6270    fn clear(&self) {
6271        *self.scripts.borrow_mut() = Default::default();
6272    }
6273}
6274
6275#[derive(JSTraceable, MallocSizeOf)]
6276#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
6277struct PendingScript {
6278    element: Dom<HTMLScriptElement>,
6279    // TODO(sagudev): could this be all no_trace?
6280    load: Option<ScriptResult>,
6281}
6282
6283impl PendingScript {
6284    fn new(element: &HTMLScriptElement) -> Self {
6285        Self {
6286            element: Dom::from_ref(element),
6287            load: None,
6288        }
6289    }
6290
6291    fn new_with_load(element: &HTMLScriptElement, load: Option<ScriptResult>) -> Self {
6292        Self {
6293            element: Dom::from_ref(element),
6294            load,
6295        }
6296    }
6297
6298    fn loaded(&mut self, result: ScriptResult) {
6299        assert!(self.load.is_none());
6300        self.load = Some(result);
6301    }
6302
6303    fn take_result(&mut self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
6304        self.load
6305            .take()
6306            .map(|result| (DomRoot::from_ref(&*self.element), result))
6307    }
6308}
6309
6310fn is_named_element_with_name_attribute(elem: &Element) -> bool {
6311    let type_ = match elem.upcast::<Node>().type_id() {
6312        NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
6313        _ => return false,
6314    };
6315    match type_ {
6316        HTMLElementTypeId::HTMLFormElement |
6317        HTMLElementTypeId::HTMLIFrameElement |
6318        HTMLElementTypeId::HTMLImageElement => true,
6319        // TODO handle <embed> and <object>; these depend on whether the element is
6320        // “exposed”, a concept that doesn’t fully make sense until embed/object
6321        // behaviour is actually implemented
6322        _ => false,
6323    }
6324}
6325
6326fn is_named_element_with_id_attribute(elem: &Element) -> bool {
6327    // TODO handle <embed> and <object>; these depend on whether the element is
6328    // “exposed”, a concept that doesn’t fully make sense until embed/object
6329    // behaviour is actually implemented
6330    elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty())
6331}
6332
6333impl DocumentHelpers for Document {
6334    fn ensure_safe_to_run_script_or_layout(&self) {
6335        Document::ensure_safe_to_run_script_or_layout(self)
6336    }
6337}