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