script/dom/
document.rs

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