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