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