script/dom/
document.rs

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