1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::cell::{Cell, RefCell};
8use std::cmp::Ordering;
9use std::collections::hash_map::Entry::{Occupied, Vacant};
10use std::collections::{HashMap, HashSet, VecDeque};
11use std::default::Default;
12use std::ops::Deref;
13use std::rc::Rc;
14use std::str::FromStr;
15use std::sync::{Arc as StdArc, LazyLock};
16use std::time::Duration;
17
18use bitflags::bitflags;
19use chrono::Local;
20use content_security_policy::sandboxing_directive::SandboxingFlagSet;
21use content_security_policy::{CspList, Policy as CspPolicy, PolicyDisposition};
22use cookie::Cookie;
23use data_url::mime::Mime;
24use devtools_traits::ScriptToDevtoolsControlMsg;
25use dom_struct::dom_struct;
26use embedder_traits::{
27 AllowOrDeny, AnimationState, CustomHandlersAutomationMode, EmbedderMsg, Image, LoadStatus,
28 Theme,
29};
30use encoding_rs::{Encoding, UTF_8};
31use html5ever::{LocalName, QualName, local_name, ns};
32use hyper_serde::Serde;
33use indexmap::IndexSet;
34use js::context::{JSContext, NoGC};
35use js::jsapi::JSObject;
36use js::realm::CurrentRealm;
37use js::rust::{HandleObject, HandleValue, MutableHandleValue};
38use layout_api::{
39 LCPCandidate, PendingRestyle, ReflowGoal, ReflowPhasesRun, ReflowStatistics, RestyleReason,
40 ScrollContainerQueryFlags, TrustedNodeAddress,
41};
42use malloc_size_of::MallocSizeOfOps;
43use metrics::{InteractiveFlag, InteractiveWindow, ProgressiveWebMetrics};
44use net_traits::CookieSource::NonHTTP;
45use net_traits::CoreResourceMsg::{GetCookieStringForUrl, SetCookiesForUrl};
46use net_traits::image_cache::ImageCache;
47use net_traits::policy_container::PolicyContainer;
48use net_traits::pub_domains::is_pub_domain;
49use net_traits::request::{
50 InsecureRequestsPolicy, PreloadId, PreloadKey, PreloadedResources, RequestBuilder,
51};
52use net_traits::{ReferrerPolicy, ResourceFetchTiming};
53use paint_api::display_list::PaintTimingInfo;
54use percent_encoding::percent_decode;
55use profile_traits::mem::{Report, ReportKind};
56use profile_traits::time::TimerMetadataFrameType;
57use profile_traits::{generic_channel as profile_generic_channel, path};
58use regex::bytes::Regex;
59use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
60use script_bindings::callback::{RootedCallback, ThisReflector};
61use script_bindings::cell::{DomRefCell, Ref, RefMut};
62use script_bindings::interfaces::DocumentHelpers;
63use script_bindings::reflector::reflect_dom_object_with_proto;
64use script_bindings::trace::CustomTraceable;
65use script_traits::{DocumentActivity, ProgressiveWebMetricType};
66use servo_arc::Arc;
67use servo_base::cross_process_instant::CrossProcessInstant;
68use servo_base::generic_channel::GenericSend;
69use servo_base::id::{LCPCandidateID, PipelineId, WebViewId};
70use servo_base::{Epoch, generic_channel};
71use servo_config::pref;
72use servo_constellation_traits::{
73 NavigationHistoryBehavior, PaintMetricEvent, ScriptToConstellationMessage,
74};
75use servo_media::{ClientContextId, ServoMedia};
76use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
77use style::attr::AttrValue;
78use style::context::QuirksMode;
79use style::dom::OpaqueNode;
80use style::invalidation::element::restyle_hints::RestyleHint;
81use style::selector_parser::Snapshot;
82use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
83use style::str::{split_html_space_chars, str_join};
84use style::stylesheet_set::DocumentStylesheetSet;
85use style::stylesheets::{Origin, OriginSet, Stylesheet};
86use style::stylist::Stylist;
87use stylo_atoms::Atom;
88use time::Duration as TimeDuration;
89use url::{Host, Position};
90
91use crate::css::stylesheet_loader::StylesheetContextId;
92use crate::css::stylesheet_set::StylesheetSetRef;
93use crate::dom::animationtimeline::AnimationTimeline;
94use crate::dom::attr::Attr;
95use crate::dom::beforeunloadevent::BeforeUnloadEvent;
96use crate::dom::bindings::callback::{ExceptionHandling, TracedCallback};
97use crate::dom::bindings::codegen::Bindings::AnimationFrameProviderBinding::FrameRequestCallback;
98use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEvent_Binding::BeforeUnloadEventMethods;
99use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
100 DocumentMethods, DocumentReadyState, DocumentVisibilityState, ElementCreationOptions,
101 NamedPropertyValue,
102};
103use crate::dom::bindings::codegen::Bindings::ElementBinding::ScrollLogicalPosition;
104use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
105use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
106use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElement_Binding::HTMLIFrameElementMethods;
107#[cfg(any(feature = "webxr", feature = "gamepad"))]
108use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
109use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
110use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
111use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
112use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionName;
113use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
114 SetHTMLOptions, SetHTMLUnsafeOptions,
115};
116use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, WindowMethods};
117use crate::dom::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMethods;
118use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
119use crate::dom::bindings::codegen::UnionTypes::{
120 BooleanOrImportNodeOptions, NodeOrString, StringOrElementCreationOptions, TrustedHTMLOrString,
121};
122use crate::dom::bindings::domname::{
123 self, is_valid_attribute_local_name, is_valid_element_local_name, namespace_from_domstring,
124};
125use crate::dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible};
126use crate::dom::bindings::frozenarray::CachedFrozenArray;
127use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
128use crate::dom::bindings::num::Finite;
129use crate::dom::bindings::refcounted::Trusted;
130use crate::dom::bindings::reflector::DomGlobal;
131use crate::dom::bindings::root::{
132 Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout, ToLayoutOptional, UnrootedDom,
133};
134use crate::dom::bindings::str::{DOMString, USVString};
135use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
136use crate::dom::bindings::weakref::DOMTracker;
137use crate::dom::bindings::xmlname::matches_name_production;
138use crate::dom::cdatasection::CDATASection;
139use crate::dom::comment::Comment;
140use crate::dom::compositionevent::CompositionEvent;
141use crate::dom::css::cssstylesheet::CSSStyleSheet;
142use crate::dom::css::fontfaceset::FontFaceSet;
143use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
144use crate::dom::customelementregistry::{CustomElementReactionStack, CustomElementRegistry};
145use crate::dom::customevent::CustomEvent;
146use crate::dom::document::accessibility_data::AccessibilityData;
147use crate::dom::document::animation_manager::AnimationManager;
148use crate::dom::document::focus::{DocumentFocusHandler, FocusableArea};
149use crate::dom::document::iframe_collection::IFrameCollection;
150use crate::dom::document::tree_ordered_index_map::TreeOrderedIndexMap;
151use crate::dom::document::websocket::WebSocket;
152use crate::dom::document_embedder_controls::DocumentEmbedderControls;
153use crate::dom::document_event_handler::DocumentEventHandler;
154use crate::dom::documentfragment::DocumentFragment;
155use crate::dom::documentorshadowroot::{
156 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
157};
158use crate::dom::documenttimeline::DocumentTimeline;
159use crate::dom::documenttype::DocumentType;
160use crate::dom::domimplementation::DOMImplementation;
161use crate::dom::domstringlist::DOMStringList;
162use crate::dom::element::attributes::storage::AttrRef;
163use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
164use crate::dom::event::{Event, EventBubbles, EventCancelable};
165use crate::dom::eventtarget::EventTarget;
166use crate::dom::execcommand::basecommand::{CommandName, DefaultSingleLineContainerName};
167use crate::dom::execcommand::execcommands::DocumentExecCommandSupport;
168use crate::dom::focusevent::FocusEvent;
169use crate::dom::globalscope::GlobalScope;
170use crate::dom::hashchangeevent::HashChangeEvent;
171use crate::dom::history::History;
172use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
173use crate::dom::html::htmlareaelement::HTMLAreaElement;
174use crate::dom::html::htmlbaseelement::HTMLBaseElement;
175use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
176use crate::dom::html::htmlelement::HTMLElement;
177use crate::dom::html::htmlembedelement::HTMLEmbedElement;
178use crate::dom::html::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
179use crate::dom::html::htmlheadelement::HTMLHeadElement;
180use crate::dom::html::htmlhtmlelement::HTMLHtmlElement;
181use crate::dom::html::htmliframeelement::HTMLIFrameElement;
182use crate::dom::html::htmlimageelement::HTMLImageElement;
183use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult};
184use crate::dom::html::htmltitleelement::HTMLTitleElement;
185use crate::dom::htmldetailselement::DetailsNameGroups;
186use crate::dom::intersectionobserver::IntersectionObserver;
187use crate::dom::iterators::ShadowIncluding;
188use crate::dom::keyboardevent::KeyboardEvent;
189use crate::dom::largestcontentfulpaint::LargestContentfulPaint;
190use crate::dom::location::Location;
191use crate::dom::messageevent::MessageEvent;
192use crate::dom::mouseevent::MouseEvent;
193use crate::dom::node::focus::FocusTrigger;
194use crate::dom::node::treewalker::TreeWalker;
195use crate::dom::node::virtualmethods::vtable_for;
196use crate::dom::node::{Node, NodeDamage, NodeFlags, NodeTraits};
197use crate::dom::nodeiterator::NodeIterator;
198use crate::dom::nodelist::NodeList;
199use crate::dom::pagetransitionevent::PageTransitionEvent;
200use crate::dom::performance::performanceentry::PerformanceEntry;
201use crate::dom::performance::performancepainttiming::PerformancePaintTiming;
202use crate::dom::processinginstruction::ProcessingInstruction;
203use crate::dom::range::Range;
204use crate::dom::resizeobserver::{ResizeObservationDepth, ResizeObserver};
205use crate::dom::sanitizer::Sanitizer;
206use crate::dom::selection::Selection;
207use crate::dom::servoparser::ServoParser;
208use crate::dom::shadowroot::shadowroot::ShadowRoot;
209use crate::dom::storageevent::StorageEvent;
210use crate::dom::text::Text;
211use crate::dom::textevent::TextEvent;
212use crate::dom::touchevent::TouchEvent as DomTouchEvent;
213use crate::dom::touchlist::TouchList;
214use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
215use crate::dom::types::{HTMLCanvasElement, VisibilityStateEntry};
216use crate::dom::uievent::UIEvent;
217use crate::dom::window::Window;
218use crate::dom::window::scrolling_box::{ScrollAxisState, ScrollingBox};
219use crate::dom::windowproxy::WindowProxy;
220use crate::dom::xpathevaluator::XPathEvaluator;
221use crate::dom::xpathexpression::XPathExpression;
222use crate::dom::{FlatTreeParent, RootedPromise, WeakRangeVec};
223use crate::event_loop::document_loader::{DocumentLoader, LoadType};
224use crate::event_loop::script_thread::{ScriptThread, SharedRwLocks};
225use crate::event_loop::timers::{OneshotTimerCallback, OneshotTimers};
226use crate::fetch::fetch::{DeferredFetchRecordInvokeState, FetchCanceller};
227use crate::fetch::network_listener::FetchResponseListener;
228use crate::mime::{APPLICATION, CHARSET};
229use crate::modules::script_module::{ModuleRequest, ModuleStatus};
230use crate::navigation::navigate;
231use crate::runtime::script_runtime::compute_size;
232use crate::tasks::task::NonSendTaskBox;
233use crate::tasks::task_manager::TaskManager;
234use crate::tasks::task_source::TaskSourceName;
235use crate::xpath::parse_expression;
236
237#[derive(Clone, Copy, PartialEq)]
238pub(crate) enum FireMouseEventType {
239 Move,
240 Over,
241 Out,
242 Enter,
243 Leave,
244}
245
246impl FireMouseEventType {
247 pub(crate) fn as_str(&self) -> &str {
248 match *self {
249 FireMouseEventType::Move => "mousemove",
250 FireMouseEventType::Over => "mouseover",
251 FireMouseEventType::Out => "mouseout",
252 FireMouseEventType::Enter => "mouseenter",
253 FireMouseEventType::Leave => "mouseleave",
254 }
255 }
256}
257
258#[derive(JSTraceable, MallocSizeOf)]
259pub(crate) struct RefreshRedirectDue {
260 #[no_trace]
261 pub(crate) url: ServoUrl,
262 pub(crate) from_meta_element: bool,
264}
265
266#[derive(Debug, Copy, Clone)]
267pub(crate) enum AbortReason {
268 PipelineExited,
269 DocumentOpen,
270 Destroy,
271 StopLoading,
272 Navigate,
273}
274
275#[derive(Debug)]
276pub(crate) enum SetParserReason {
277 AboutBlankComplete,
278 ParsingHtmlDocument,
279 ParsingHtmlScriptInput,
280 ParsingHtmlFragment,
281 ParsingXmlDocument,
282 #[expect(dead_code)]
283 Abort(AbortReason),
285 ParsingBytesChunk,
286 FinishingParser,
287}
288
289#[derive(JSTraceable, MallocSizeOf)]
293#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
294struct LCPCandidateAndElement {
295 element: Option<Dom<Element>>,
297 #[no_trace]
298 candidate: LCPCandidate,
299 #[no_trace]
301 load_time: Option<CrossProcessInstant>,
302}
303
304impl RefreshRedirectDue {
305 pub(crate) fn invoke(self, cx: &mut JSContext, global: &GlobalScope) {
307 let window = global
308 .downcast::<Window>()
309 .expect("Queued a RefreshRedirectDue on a non-Window globalscope");
310
311 if self.from_meta_element &&
318 window.Document().has_active_sandboxing_flag(
319 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
320 )
321 {
322 return;
323 }
324 let load_data = window.load_data_for_document(self.url, window.pipeline_id());
325 navigate(
326 cx,
327 window,
328 NavigationHistoryBehavior::Replace,
329 false,
330 load_data,
331 );
332 }
333}
334
335#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
336pub(crate) enum IsHTMLDocument {
337 HTMLDocument,
338 NonHTMLDocument,
339}
340
341#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
342pub(crate) enum TheEndLoadingPhase {
343 #[default]
344 Initial,
345 ProcessingDeferredScripts,
346 ProcessingAsSoonAsPossibleScripts,
347 WaitingForLoadEventBlockers,
348 Done,
349}
350
351#[derive(JSTraceable, MallocSizeOf)]
353pub(crate) enum DeclarativeRefresh {
354 PendingLoad {
355 #[no_trace]
356 url: ServoUrl,
357 time: u64,
358 from_meta_element: bool,
360 },
361 CreatedAfterLoad,
362}
363
364#[derive(JSTraceable, MallocSizeOf, PartialEq)]
365#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
366struct PendingScrollEvent {
367 target: Dom<EventTarget>,
369 #[no_trace]
371 event: Atom,
372}
373
374impl PendingScrollEvent {
375 fn equivalent(&self, target: &EventTarget, event: &Atom) -> bool {
376 &*self.target == target && self.event == *event
377 }
378}
379
380#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf)]
383pub(crate) struct RenderingUpdateReason(u8);
384
385bitflags! {
386 impl RenderingUpdateReason: u8 {
387 const ResizeObserverStartedObservingTarget = 1 << 0;
390 const IntersectionObserverStartedObservingTarget = 1 << 1;
393 const FontReadyPromiseFulfilled = 1 << 2;
397 }
398}
399
400#[derive(Clone, Debug, Default, MallocSizeOf)]
402pub(crate) struct NavigationTiming {
403 pub(crate) dom_loading: Cell<Option<CrossProcessInstant>>,
404 pub(crate) navigation_start: Cell<Option<CrossProcessInstant>>,
406 pub(crate) unload_event_start: Cell<Option<CrossProcessInstant>>,
408 pub(crate) unload_event_end: Cell<Option<CrossProcessInstant>>,
410 pub(crate) dom_interactive: Cell<Option<CrossProcessInstant>>,
412 pub(crate) dom_content_loaded_event_start: Cell<Option<CrossProcessInstant>>,
414 pub(crate) dom_content_loaded_event_end: Cell<Option<CrossProcessInstant>>,
416 pub(crate) dom_complete: Cell<Option<CrossProcessInstant>>,
418 pub(crate) load_event_start: Cell<Option<CrossProcessInstant>>,
420 pub(crate) load_event_end: Cell<Option<CrossProcessInstant>>,
422 pub(crate) top_level_dom_complete: Cell<Option<CrossProcessInstant>>,
424}
425
426#[dom_struct]
428pub(crate) struct Document {
429 node: Node,
430 document_or_shadow_root: DocumentOrShadowRoot,
431 window: Dom<Window>,
432 implementation: MutNullableDom<DOMImplementation>,
433 #[ignore_malloc_size_of = "type from external crate"]
434 #[no_trace]
435 content_type: Mime,
436 last_modified: Option<String>,
437 #[no_trace]
438 encoding: Cell<&'static Encoding>,
439 has_browsing_context: bool,
440 is_html_document: bool,
441 #[no_trace]
442 activity: Cell<DocumentActivity>,
443 #[no_trace]
445 url: DomRefCell<ServoUrl>,
446 #[no_trace]
448 about_base_url: DomRefCell<Option<ServoUrl>>,
449 #[ignore_malloc_size_of = "defined in selectors"]
450 #[no_trace]
451 quirks_mode: Cell<QuirksMode>,
452 event_handler: DocumentEventHandler,
454 focus_handler: DocumentFocusHandler,
456 embedder_controls: DocumentEmbedderControls,
458 id_map: TreeOrderedIndexMap,
459 name_map: TreeOrderedIndexMap,
460 tag_map: DomRefCell<HashMapTracedValues<LocalName, Dom<HTMLCollection>, FxBuildHasher>>,
461 tagns_map: DomRefCell<HashMapTracedValues<QualName, Dom<HTMLCollection>, FxBuildHasher>>,
462 classes_map: DomRefCell<HashMapTracedValues<Vec<Atom>, Dom<HTMLCollection>>>,
463 images: MutNullableDom<HTMLCollection>,
464 embeds: MutNullableDom<HTMLCollection>,
465 links: MutNullableDom<HTMLCollection>,
466 forms: MutNullableDom<HTMLCollection>,
467 scripts: MutNullableDom<HTMLCollection>,
468 anchors: MutNullableDom<HTMLCollection>,
469 applets: MutNullableDom<HTMLCollection>,
470 iframes: IFrameCollection,
472 #[no_trace]
476 shared_style_locks: SharedRwLocks,
477 #[custom_trace]
479 stylesheets: DomRefCell<DocumentStylesheetSet<ServoStylesheetInDocument>>,
480 stylesheet_list: MutNullableDom<StyleSheetList>,
481 ready_state: Cell<DocumentReadyState>,
482 current_script: MutNullableDom<HTMLScriptElement>,
484 #[no_trace]
485 current_the_end_loading_phase: Cell<TheEndLoadingPhase>,
486 pending_parsing_blocking_script: DomRefCell<Option<PendingScript>>,
488 script_blocking_stylesheet_set: DomRefCell<IndexSet<StylesheetContextId>>,
491 render_blocking_element_count: Cell<u32>,
494 deferred_scripts: PendingInOrderScriptVec,
496 asap_in_order_scripts_list: PendingInOrderScriptVec,
498 asap_scripts_set: DomRefCell<Vec<Dom<HTMLScriptElement>>>,
500 animation_frame_ident: Cell<u32>,
503 animation_frame_list: DomRefCell<VecDeque<(u32, Option<AnimationFrameCallback>)>>,
506 running_animation_callbacks: Cell<bool>,
511 loader: DomRefCell<DocumentLoader>,
513 current_parser: MutNullableDom<ServoParser>,
515 base_element: MutNullableDom<HTMLBaseElement>,
517 target_base_element: MutNullableDom<HTMLBaseElement>,
519 appropriate_template_contents_owner_document: MutNullableDom<Document>,
522 pending_restyles: DomRefCell<FxHashMap<Dom<Element>, NoTrace<PendingRestyle>>>,
525 #[no_trace]
529 needs_restyle: Cell<RestyleReason>,
530 #[no_trace]
532 origin: DomRefCell<MutableOrigin>,
533 referrer: Option<String>,
535 target_element: MutNullableDom<Element>,
537 #[no_trace]
539 #[conditional_malloc_size_of]
540 policy_container: DomRefCell<StdArc<PolicyContainer>>,
541 #[no_trace]
543 preloaded_resources: DomRefCell<PreloadedResources>,
544 ignore_destructive_writes_counter: Cell<u32>,
546 ignore_opens_during_unload_counter: Cell<u32>,
548 spurious_animation_frames: Cell<u8>,
552
553 fullscreen_element: MutNullableDom<Element>,
555 form_id_listener_map:
562 DomRefCell<HashMapTracedValues<Atom, HashSet<Dom<Element>>, FxBuildHasher>>,
563 #[no_trace]
564 interactive_time: DomRefCell<ProgressiveWebMetrics>,
565 #[no_trace]
566 tti_window: DomRefCell<InteractiveWindow>,
567 canceller: FetchCanceller,
569 throw_on_dynamic_markup_insertion_counter: Cell<u64>,
571 page_showing: Cell<bool>,
573 salvageable: Cell<bool>,
575 active_parser_was_aborted: Cell<bool>,
577 fired_unload: Cell<bool>,
579 responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>,
581
582 #[no_trace]
585 #[conditional_malloc_size_of]
586 navigation_timing: Rc<NavigationTiming>,
587
588 #[no_trace]
590 resource_fetch_timing: RefCell<Option<ResourceFetchTiming>>,
591
592 script_and_layout_blockers: Cell<u32>,
594 #[ignore_malloc_size_of = "Measuring trait objects is hard"]
596 delayed_tasks: DomRefCell<Vec<Box<dyn NonSendTaskBox>>>,
597 completely_loaded: Cell<bool>,
599 shadow_roots: DomRefCell<FxHashSet<Dom<ShadowRoot>>>,
601 shadow_roots_styles_changed: Cell<bool>,
603 media_controls: DomRefCell<HashMap<String, Dom<ShadowRoot>>>,
609 dirty_canvases: DomRefCell<Vec<Dom<HTMLCanvasElement>>>,
612 has_pending_animated_image_update: Cell<bool>,
614 selection: MutNullableDom<Selection>,
616 timeline: Dom<DocumentTimeline>,
619 animation_manager: AnimationManager,
621 dirty_root: MutNullableDom<Element>,
623 declarative_refresh: DomRefCell<Option<DeclarativeRefresh>>,
625 resize_observers: DomRefCell<Vec<Dom<ResizeObserver>>>,
634 fonts: MutNullableDom<FontFaceSet>,
637 visibility_state: Cell<DocumentVisibilityState>,
639 status_code: Option<u16>,
641 is_initial_about_blank: Cell<bool>,
643 allow_declarative_shadow_roots: Cell<bool>,
645 #[no_trace]
647 inherited_insecure_requests_policy: Cell<Option<InsecureRequestsPolicy>>,
648 has_trustworthy_ancestor_origin: Cell<bool>,
650 intersection_observer_task_queued: Cell<bool>,
652 intersection_observers: DomRefCell<Vec<Dom<IntersectionObserver>>>,
664 highlighted_dom_node: MutNullableDom<Node>,
666 lcp_candidates: DomRefCell<HashMapTracedValues<LCPCandidateID, LCPCandidateAndElement>>,
668 #[no_trace]
671 paint_timing_info: Cell<PaintTimingInfo>,
672 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
675 #[ignore_malloc_size_of = "mozjs"]
677 adopted_stylesheets_frozen_types: CachedFrozenArray,
678 pending_scroll_events: DomRefCell<Vec<PendingScrollEvent>>,
682 rendering_update_reasons: Cell<RenderingUpdateReason>,
684 waiting_on_canvas_image_updates: Cell<bool>,
688 root_removal_noted: Cell<bool>,
690 #[no_trace]
698 current_rendering_epoch: Cell<Epoch>,
699 #[conditional_malloc_size_of]
701 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
702 #[no_trace]
703 active_sandboxing_flag_set: Cell<SandboxingFlagSet>,
705 #[no_trace]
706 creation_sandboxing_flag_set: Cell<SandboxingFlagSet>,
713 #[no_trace]
715 favicon: RefCell<Option<Image>>,
716
717 websockets: DOMTracker<WebSocket>,
719
720 details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
722
723 #[no_trace]
725 protocol_handler_automation_mode: RefCell<CustomHandlersAutomationMode>,
726
727 layout_animations_test_enabled: bool,
729
730 #[no_trace]
732 state_override: DomRefCell<FxHashMap<CommandName, bool>>,
733
734 #[no_trace]
736 value_override: DomRefCell<FxHashMap<CommandName, DOMString>>,
737
738 #[no_trace]
740 default_single_line_container_name: Cell<DefaultSingleLineContainerName>,
741
742 css_styling_flag: Cell<bool>,
744
745 accessibility_data: DomRefCell<AccessibilityData>,
747
748 iframe_load_in_progress: Cell<bool>,
750 mute_iframe_load: Cell<bool>,
752
753 timers: OneshotTimers,
756
757 #[no_trace]
758 pipeline_id: PipelineId,
759
760 #[conditional_malloc_size_of]
762 task_manager: Rc<TaskManager>,
763
764 #[ignore_malloc_size_of = "ImageCache"]
765 #[no_trace]
766 image_cache: StdArc<dyn ImageCache>,
767
768 history: MutNullableDom<History>,
770
771 #[no_trace]
773 theme: Cell<Option<Theme>>,
774
775 #[no_trace]
777 theme_override: Cell<Option<Theme>>,
778
779 default_language: DomRefCell<Option<String>>,
781
782 window_detached: Cell<bool>,
785
786 ancestor_origins_list: MutNullableDom<DOMStringList>,
788
789 #[no_trace]
791 internal_ancestor_origin_objects_list: RefCell<Option<Vec<ImmutableOrigin>>>,
792
793 live_ranges: WeakRangeVec,
796
797 #[ignore_malloc_size_of = "mozjs"]
800 module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
801}
802
803impl Document {
804 pub(crate) fn module_map(
805 &self,
806 ) -> &DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>> {
807 &self.module_map
808 }
809
810 pub(crate) fn history(&self, cx: &mut JSContext) -> DomRoot<History> {
811 self.history.or_init(|| History::new(cx, &self.window))
812 }
813
814 pub(crate) fn image_cache(&self) -> StdArc<dyn ImageCache> {
815 self.image_cache.clone()
816 }
817
818 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
819 self.task_manager.clone()
820 }
821
822 pub(crate) fn timers(&self) -> &OneshotTimers {
823 &self.timers
824 }
825
826 pub(crate) fn pipeline_id(&self) -> PipelineId {
827 self.pipeline_id
828 }
829
830 fn fully_exit_fullscreen(&self, cx: &mut JSContext) {
832 if self.fullscreen_element().is_none() {
835 return;
836 };
837
838 let _ = self.exit_fullscreen(cx);
843 }
844
845 fn unloading_cleanup_steps(&self, cx: &mut JSContext) {
847 self.fully_exit_fullscreen(cx);
850
851 if self.close_outstanding_websockets() {
854 self.salvageable.set(false);
856 }
857
858 if !self.salvageable.get() && !self.window_detached() {
863 let global_scope = self.window.as_global_scope();
864
865 global_scope.close_event_sources();
867
868 self.timers.clear();
870
871 let msg = ScriptToConstellationMessage::DiscardDocument;
873 let _ = global_scope.script_to_constellation_chan().send(msg);
874 }
875 }
876
877 pub(crate) fn track_websocket(&self, websocket: &WebSocket) {
878 self.websockets.track(websocket);
879 }
880
881 fn close_outstanding_websockets(&self) -> bool {
882 let mut closed_any_websocket = false;
883 self.websockets.for_each(|websocket: DomRoot<WebSocket>| {
884 if websocket.make_disappear() {
885 closed_any_websocket = true;
886 }
887 });
888 closed_any_websocket
889 }
890
891 fn document_element_changed(&self) {
892 if self.GetDocumentElement().is_some() {
893 self.root_removal_noted.set(false);
896 } else if !self.root_removal_noted.get() {
897 self.add_restyle_reason(RestyleReason::DOMChanged);
900 self.root_removal_noted.set(true);
901 }
902 }
903
904 pub(crate) fn note_dirty_element(&self, no_gc: &NoGC, element: &Element) {
919 let node = element.upcast::<Node>();
920
921 debug_assert!(*node.owner_doc() == *self);
922 if !node.is_connected() {
923 return;
924 }
925
926 let parent_element = match node.parent_in_flat_tree(no_gc) {
927 FlatTreeParent::Parent(parent) => UnrootedDom::downcast::<Element>(parent),
928 FlatTreeParent::NotInFlatTree | FlatTreeParent::RootNode => return,
929 };
930
931 if let Some(parent_element) = parent_element {
935 if !parent_element.is_styled() {
938 return;
939 }
940 if parent_element.is_display_none() {
943 return;
944 }
945 }
946
947 let Some(old_dirty_root) = self.dirty_root.get() else {
948 node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
949 self.set_dirty_root(no_gc, Some(element));
950 return;
951 };
952
953 let old_dirty_root_node = old_dirty_root.upcast::<Node>();
954 for ancestor in element
955 .upcast::<Node>()
956 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
957 {
958 if !ancestor.is::<Element>() {
960 break;
961 }
962
963 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
964 return;
965 }
966
967 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
968
969 if old_dirty_root_node == &**ancestor {
973 return;
974 }
975 }
976
977 let common_element_ancestor = old_dirty_root_node
978 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
979 .skip(1) .find_map(|ancestor| {
981 let element = ancestor.downcast::<Element>().map(DomRoot::from_ref)?;
983 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
984 return Some(element);
985 }
986 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
987 None
988 });
989
990 let Some(new_dirty_root) = common_element_ancestor else {
994 let new_dirty_root = self.GetDocumentElement();
995 if let Some(new_dirty_root) = new_dirty_root.as_ref() {
996 new_dirty_root
997 .upcast::<Node>()
998 .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
999 }
1000 self.set_dirty_root(no_gc, new_dirty_root.as_deref());
1001 return;
1002 };
1003
1004 for ancestor in new_dirty_root
1008 .upcast::<Node>()
1009 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
1010 .skip(1)
1011 {
1012 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
1013 }
1014
1015 self.set_dirty_root(no_gc, Some(&*new_dirty_root));
1016 }
1017
1018 fn set_dirty_root(&self, no_gc: &NoGC, new_dirty_root: Option<&Element>) {
1019 debug_assert!(new_dirty_root.as_ref().is_none_or(|new_dirty_root| {
1021 new_dirty_root
1022 .upcast::<Node>()
1023 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
1024 .skip(1)
1025 .all(|node| !node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS))
1026 }));
1027 self.dirty_root.set(new_dirty_root);
1028 }
1029
1030 pub(crate) fn take_dirty_root(&self) -> Option<DomRoot<Element>> {
1031 self.dirty_root.take()
1032 }
1033
1034 #[inline]
1035 pub(crate) fn loader(&self) -> Ref<'_, DocumentLoader> {
1036 self.loader.borrow()
1037 }
1038
1039 #[inline]
1040 pub(crate) fn loader_mut(&self) -> RefMut<'_, DocumentLoader> {
1041 self.loader.borrow_mut()
1042 }
1043
1044 #[inline]
1045 pub(crate) fn has_browsing_context(&self) -> bool {
1046 self.has_browsing_context
1047 }
1048
1049 #[inline]
1051 pub(crate) fn browsing_context(&self) -> Option<DomRoot<WindowProxy>> {
1052 if self.has_browsing_context {
1053 self.window.undiscarded_window_proxy()
1054 } else {
1055 None
1056 }
1057 }
1058
1059 pub(crate) fn webview_id(&self) -> WebViewId {
1060 self.window.webview_id()
1061 }
1062
1063 #[inline]
1064 pub(crate) fn window(&self) -> &Window {
1065 &self.window
1066 }
1067
1068 #[inline]
1069 pub(crate) fn is_html_document(&self) -> bool {
1070 self.is_html_document
1071 }
1072
1073 pub(crate) fn is_xhtml_document(&self) -> bool {
1074 self.content_type.matches(APPLICATION, "xhtml+xml")
1075 }
1076
1077 pub(crate) fn is_fully_active(&self) -> bool {
1079 self.is_active() &&
1083 (self.window.is_top_level() || self.activity.get() == DocumentActivity::FullyActive)
1084 }
1085
1086 pub(crate) fn is_active(&self) -> bool {
1088 self.browsing_context().is_some() &&
1094 !self.window_detached() &&
1095 self.activity.get() != DocumentActivity::Inactive
1096 }
1097
1098 #[inline]
1099 pub(crate) fn current_rendering_epoch(&self) -> Epoch {
1100 self.current_rendering_epoch.get()
1101 }
1102
1103 #[inline]
1105 pub(crate) fn selection(&self) -> Option<DomRoot<Selection>> {
1106 self.selection.get()
1107 }
1108
1109 pub(crate) fn set_activity(&self, cx: &mut JSContext, activity: DocumentActivity) {
1110 assert!(self.has_browsing_context);
1112 if activity == self.activity.get() {
1113 return;
1114 }
1115
1116 self.activity.set(activity);
1118 let media = ServoMedia::get();
1119 let pipeline_id = self.window().pipeline_id();
1120 let client_context_id =
1121 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
1122
1123 if activity != DocumentActivity::FullyActive {
1124 if !self.window_detached() {
1125 self.window().suspend(cx);
1126 }
1127 media.suspend(&client_context_id);
1128 return;
1129 }
1130
1131 if self.window_detached() {
1132 return;
1133 }
1134
1135 self.title_changed();
1136 self.notify_embedder_favicon();
1137 self.dirty_all_nodes(cx.no_gc());
1138 self.window().resume(cx);
1139 media.resume(&client_context_id);
1140
1141 if self.ready_state.get() != DocumentReadyState::Complete {
1142 return;
1143 }
1144
1145 let document = Trusted::new(self);
1149 self.owner_global()
1150 .task_manager()
1151 .dom_manipulation_task_source()
1152 .queue(task!(fire_pageshow_event: move |cx| {
1153 let document = document.root();
1154 let window = document.window();
1155 if document.page_showing.get() {
1157 return;
1158 }
1159 document.page_showing.set(true);
1161 document.update_visibility_state(cx, DocumentVisibilityState::Visible);
1163 let event = PageTransitionEvent::new(
1166 cx,
1167 window,
1168 atom!("pageshow"),
1169 false, false, true, );
1173 let event = event.upcast::<Event>();
1174 event.set_trusted(true);
1175 window.dispatch_event_with_target_override(cx, event);
1176 }))
1177 }
1178
1179 pub(crate) fn origin(&self) -> Ref<'_, MutableOrigin> {
1180 self.origin.borrow()
1181 }
1182
1183 pub(crate) fn paint_timing_eligible(&self) -> bool {
1185 if self.window().is_top_level() {
1189 return true;
1190 }
1191 if let Some(top_level_document) = self.window().top_level_document_if_local() {
1194 return self.origin().same_origin(&top_level_document.origin());
1198 };
1199 false
1200 }
1201
1202 pub(crate) fn mark_as_internal(&self) {
1205 *self.origin.borrow_mut() = MutableOrigin::new(ImmutableOrigin::new_opaque());
1206 self.window().update_jsprincipals_from_document(self);
1207 }
1208
1209 pub(crate) fn set_protocol_handler_automation_mode(&self, mode: CustomHandlersAutomationMode) {
1210 *self.protocol_handler_automation_mode.borrow_mut() = mode;
1211 }
1212
1213 pub(crate) fn url(&self) -> ServoUrl {
1215 self.url.borrow().clone()
1216 }
1217
1218 pub(crate) fn set_url(&self, url: ServoUrl) {
1219 *self.url.borrow_mut() = url;
1220 }
1221
1222 pub(crate) fn about_base_url(&self) -> Option<ServoUrl> {
1223 self.about_base_url.borrow().clone()
1224 }
1225
1226 pub(crate) fn set_about_base_url(&self, about_base_url: Option<ServoUrl>) {
1227 *self.about_base_url.borrow_mut() = about_base_url;
1228 }
1229
1230 pub(crate) fn fallback_base_url(&self) -> ServoUrl {
1232 let document_url = self.url();
1233 if document_url.as_str() == "about:srcdoc" {
1235 return self
1238 .about_base_url()
1239 .expect("about:srcdoc page should always have an about base URL");
1240 }
1241
1242 if document_url.matches_about_blank() &&
1245 let Some(about_base_url) = self.about_base_url()
1246 {
1247 return about_base_url;
1248 }
1249
1250 document_url
1252 }
1253
1254 pub(crate) fn base_url(&self) -> ServoUrl {
1256 match self.base_element() {
1257 None => self.fallback_base_url(),
1259 Some(base) => base.frozen_base_url(),
1261 }
1262 }
1263
1264 pub(crate) fn add_restyle_reason(&self, reason: RestyleReason) {
1265 self.needs_restyle.set(self.needs_restyle.get() | reason)
1266 }
1267
1268 pub(crate) fn clear_restyle_reasons(&self) {
1269 self.needs_restyle.set(RestyleReason::empty());
1270 }
1271
1272 pub(crate) fn stylesheets_changed_since_last_reflow(&self) -> bool {
1273 self.stylesheets.borrow().has_changed()
1274 }
1275
1276 pub(crate) fn restyle_reason(&self, no_gc: &NoGC) -> RestyleReason {
1277 let mut condition = self.needs_restyle.get();
1278 if self.stylesheets_changed_since_last_reflow() {
1279 condition.insert(RestyleReason::StylesheetsChanged);
1280 }
1281
1282 if let Some(root) = self.get_document_element_unrooted(no_gc) &&
1286 root.has_dirty_descendants()
1287 {
1288 condition.insert(RestyleReason::DOMChanged);
1289 }
1290
1291 if !self.pending_restyles.borrow().is_empty() {
1292 condition.insert(RestyleReason::PendingRestyles);
1293 }
1294
1295 condition
1296 }
1297
1298 pub(crate) fn base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1300 self.base_element.get()
1301 }
1302
1303 pub(crate) fn target_base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1305 self.target_base_element.get()
1306 }
1307
1308 pub(crate) fn refresh_base_element(&self, cx: &mut JSContext) {
1310 if let Some(base_element) = self.base_element.get() {
1311 base_element.clear_frozen_base_url();
1312 }
1313 let new_base_element = self
1314 .upcast::<Node>()
1315 .traverse_preorder(ShadowIncluding::No)
1316 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1317 .find(|element| {
1318 element
1319 .upcast::<Element>()
1320 .has_attribute(&local_name!("href"))
1321 });
1322 if let Some(ref new_base_element) = new_base_element {
1323 new_base_element.set_frozen_base_url(cx);
1324 }
1325 self.base_element.set(new_base_element.as_deref());
1326
1327 let new_target_base_element = self
1328 .upcast::<Node>()
1329 .traverse_preorder(ShadowIncluding::No)
1330 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1331 .next();
1332 self.target_base_element
1333 .set(new_target_base_element.as_deref());
1334 }
1335
1336 pub(crate) fn quirks_mode(&self) -> QuirksMode {
1337 self.quirks_mode.get()
1338 }
1339
1340 pub(crate) fn set_quirks_mode(&self, new_mode: QuirksMode) {
1341 let old_mode = self.quirks_mode.replace(new_mode);
1342
1343 if old_mode != new_mode {
1344 self.window.layout_mut().set_quirks_mode(new_mode);
1345 }
1346 }
1347
1348 pub(crate) fn encoding(&self) -> &'static Encoding {
1349 self.encoding.get()
1350 }
1351
1352 pub(crate) fn set_encoding(&self, encoding: &'static Encoding) {
1353 self.encoding.set(encoding);
1354 }
1355
1356 pub(crate) fn content_and_heritage_changed(&self, no_gc: &NoGC, node: &Node) {
1357 if node.is::<Document>() {
1358 self.document_element_changed();
1359 }
1360
1361 node.dirty(no_gc, NodeDamage::ContentOrHeritage);
1366 }
1367
1368 pub(crate) fn unregister_element_id(&self, cx: &mut JSContext, id: &Atom) {
1370 self.id_map.remove(id);
1371 self.reset_form_owner_for_listeners(cx, id);
1372 }
1373
1374 pub(crate) fn register_element_id(&self, cx: &mut JSContext, element: &Element, id: &Atom) {
1376 self.id_map.add(id, element);
1377 self.reset_form_owner_for_listeners(cx, id);
1378 }
1379
1380 pub(crate) fn unregister_element_name(&self, name: &Atom) {
1382 self.name_map.remove(name);
1383 }
1384
1385 pub(crate) fn register_element_name(&self, element: &Element, name: &Atom) {
1387 self.name_map.add(name, element);
1388 }
1389
1390 pub(crate) fn register_form_id_listener<T: ?Sized + FormControl>(
1391 &self,
1392 id: DOMString,
1393 listener: &T,
1394 ) {
1395 let mut map = self.form_id_listener_map.borrow_mut();
1396 let listener = listener.to_element();
1397 let set = map.entry(Atom::from(id)).or_default();
1398 set.insert(Dom::from_ref(listener));
1399 }
1400
1401 pub(crate) fn unregister_form_id_listener<T: ?Sized + FormControl>(
1402 &self,
1403 id: DOMString,
1404 listener: &T,
1405 ) {
1406 let mut map = self.form_id_listener_map.borrow_mut();
1407 if let Occupied(mut entry) = map.entry(Atom::from(id)) {
1408 entry
1409 .get_mut()
1410 .remove(&Dom::from_ref(listener.to_element()));
1411 if entry.get().is_empty() {
1412 entry.remove();
1413 }
1414 }
1415 }
1416
1417 fn find_a_potential_indicated_element(
1419 &self,
1420 cx: &mut JSContext,
1421 fragment: &str,
1422 ) -> Option<DomRoot<Element>> {
1423 self.get_element_by_id(cx.no_gc(), &Atom::from(fragment))
1427 .or_else(|| self.get_anchor_by_name(cx, fragment))
1431 }
1432
1433 fn select_indicated_part(&self, cx: &mut JSContext, fragment: &str) -> Option<DomRoot<Node>> {
1436 if fragment.is_empty() {
1446 return Some(DomRoot::from_ref(self.upcast()));
1447 }
1448 if let Some(potential_indicated_element) =
1450 self.find_a_potential_indicated_element(cx, fragment)
1451 {
1452 return Some(DomRoot::upcast(potential_indicated_element));
1454 }
1455 let fragment_bytes = percent_decode(fragment.as_bytes());
1457 let Ok(decoded_fragment) = fragment_bytes.decode_utf8() else {
1459 return None;
1460 };
1461 if let Some(potential_indicated_element) =
1463 self.find_a_potential_indicated_element(cx, &decoded_fragment)
1464 {
1465 return Some(DomRoot::upcast(potential_indicated_element));
1467 }
1468 if decoded_fragment.eq_ignore_ascii_case("top") {
1470 return Some(DomRoot::from_ref(self.upcast()));
1471 }
1472 None
1474 }
1475
1476 pub(crate) fn scroll_to_the_fragment(&self, cx: &mut JSContext, fragment: &str) {
1478 let Some(indicated_part) = self.select_indicated_part(cx, fragment) else {
1483 self.set_target_element(None);
1484 return;
1485 };
1486 if *indicated_part == *self.upcast() {
1488 self.set_target_element(None);
1490 self.window.scroll(cx, 0.0, 0.0, ScrollBehavior::Instant);
1495 return;
1497 }
1498 let Some(target) = indicated_part.downcast::<Element>() else {
1501 unreachable!("Indicated part should always be an element");
1503 };
1504 self.set_target_element(Some(target));
1506 target.scroll_into_view_with_options(
1510 cx,
1511 ScrollBehavior::Auto,
1512 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Start),
1513 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Nearest),
1514 None,
1515 None,
1516 );
1517
1518 indicated_part.run_the_focusing_steps(
1521 cx,
1522 Some(FocusableArea::Viewport),
1523 FocusTrigger::Other,
1524 );
1525
1526 self.focus_handler()
1528 .set_sequential_focus_navigation_starting_point(target.upcast());
1529 }
1530
1531 fn get_anchor_by_name(&self, cx: &mut JSContext, name: &str) -> Option<DomRoot<Element>> {
1532 let document_element = self.GetDocumentElement()?;
1533 self.name_map
1534 .get_all(cx.no_gc(), document_element.upcast(), &Atom::from(name))
1535 .iter()
1536 .find(|element| element.is::<HTMLAnchorElement>())
1537 .map(|element| DomRoot::from_ref(&**element))
1538 }
1539
1540 pub(crate) fn notify_embedder_of_load_completion(&self) {
1541 if self.window().is_top_level() {
1542 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1543 self.webview_id(),
1544 LoadStatus::Complete,
1545 ));
1546 }
1547 }
1548
1549 pub(crate) fn set_document_readiness_to_loading_for_initialization(&self) {
1554 if self.is_initial_about_blank() {
1558 return;
1559 }
1560
1561 update_with_current_instant(&self.navigation_timing.dom_loading);
1565
1566 if self.window.is_top_level() {
1567 let webview_id = self.webview_id();
1568 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1569 webview_id,
1570 LoadStatus::Started,
1571 ));
1572 self.send_to_embedder(EmbedderMsg::Status(webview_id, None));
1573 }
1574
1575 self.ready_state.set(DocumentReadyState::Loading);
1576 }
1577
1578 pub(crate) fn update_the_current_document_readiness(
1580 &self,
1581 cx: &mut JSContext,
1582 state: DocumentReadyState,
1583 ) {
1584 if self.ready_state.get() == state {
1586 return;
1587 }
1588
1589 self.ready_state.set(state);
1591
1592 match state {
1597 DocumentReadyState::Loading => {},
1598 DocumentReadyState::Complete => {
1599 self.notify_embedder_of_load_completion();
1602
1603 update_with_current_instant(&self.navigation_timing.dom_complete);
1608 },
1609 DocumentReadyState::Interactive => {
1610 update_with_current_instant(&self.navigation_timing.dom_interactive)
1615 },
1616 };
1617
1618 self.upcast::<EventTarget>()
1620 .fire_event(cx, atom!("readystatechange"));
1621 }
1622
1623 pub(crate) fn scripting_enabled(&self) -> bool {
1626 self.has_browsing_context() &&
1629 !self.has_active_sandboxing_flag(
1633 SandboxingFlagSet::SANDBOXED_SCRIPTS_BROWSING_CONTEXT_FLAG,
1634 )
1635 }
1636
1637 pub(crate) fn title_changed(&self) {
1639 if self.browsing_context().is_some() {
1640 self.send_title_to_embedder();
1641 let title = String::from(self.Title());
1642 self.window
1643 .send_to_constellation(ScriptToConstellationMessage::TitleChanged(
1644 self.window.pipeline_id(),
1645 title.clone(),
1646 ));
1647 if let Some(chan) = self.window.as_global_scope().devtools_chan() {
1648 let _ = chan.send(ScriptToDevtoolsControlMsg::TitleChanged(
1649 self.window.pipeline_id(),
1650 title,
1651 ));
1652 }
1653 }
1654 }
1655
1656 fn title(&self) -> Option<DOMString> {
1660 let title = self.GetDocumentElement().and_then(|root| {
1661 if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
1662 root.upcast::<Node>()
1664 .child_elements()
1665 .find(|node| {
1666 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
1667 })
1668 .map(DomRoot::upcast::<Node>)
1669 } else {
1670 root.upcast::<Node>()
1672 .traverse_preorder(ShadowIncluding::No)
1673 .find(|node| node.is::<HTMLTitleElement>())
1674 }
1675 });
1676
1677 title.map(|title| {
1678 let value = title.child_text_content();
1680 DOMString::from(str_join(value.str().split_html_space_characters(), " "))
1681 })
1682 }
1683
1684 pub(crate) fn send_title_to_embedder(&self) {
1686 let window = self.window();
1687 if window.is_top_level() {
1688 let title = self.title().map(String::from);
1689 self.send_to_embedder(EmbedderMsg::ChangePageTitle(self.webview_id(), title));
1690 }
1691 }
1692
1693 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
1694 let window = self.window();
1695 window.send_to_embedder(msg);
1696 }
1697
1698 pub(crate) fn dirty_all_nodes(&self, no_gc: &NoGC) {
1699 let root = match self.GetDocumentElement() {
1700 Some(root) => root,
1701 None => return,
1702 };
1703 for node in root
1704 .upcast::<Node>()
1705 .traverse_preorder_unrooted(no_gc, ShadowIncluding::Yes)
1706 {
1707 node.dirty(no_gc, NodeDamage::Other)
1708 }
1709 }
1710
1711 pub(crate) fn run_the_scroll_steps(&self, cx: &mut JSContext) {
1713 let boxes_that_were_scrolled: Vec<_> = self
1721 .pending_scroll_events
1722 .borrow()
1723 .iter()
1724 .filter_map(|pending_event| {
1725 if &*pending_event.event == "scroll" {
1726 Some(pending_event.target.as_rooted())
1727 } else {
1728 None
1729 }
1730 })
1731 .collect();
1732
1733 for target in boxes_that_were_scrolled.into_iter() {
1734 let Some(element) = target.downcast::<Element>() else {
1740 continue;
1741 };
1742 let document = element.owner_document();
1743
1744 let mut pending_scroll_events = document.pending_scroll_events.borrow_mut();
1751 let event = "scrollend".into();
1752 if pending_scroll_events
1753 .iter()
1754 .any(|existing| existing.equivalent(&target, &event))
1755 {
1756 continue;
1757 }
1758
1759 pending_scroll_events.push(PendingScrollEvent {
1761 target: target.as_traced(),
1762 event: "scrollend".into(),
1763 });
1764 }
1765
1766 rooted_vec!(let pending_scroll_events <- self.pending_scroll_events.take().into_iter());
1769 for pending_event in pending_scroll_events.iter() {
1770 let event = pending_event.event.clone();
1773 if pending_event.target.is::<Document>() {
1774 pending_event.target.fire_bubbling_event(cx, event);
1775 }
1776 else {
1785 pending_event.target.fire_event(cx, event);
1786 }
1787 }
1788
1789 }
1792
1793 pub(crate) fn handle_viewport_scroll_event(&self) {
1798 self.finish_handle_scroll_event(self.upcast());
1810 }
1811
1812 pub(crate) fn finish_handle_scroll_event(&self, event_target: &EventTarget) {
1817 let event = "scroll".into();
1820 if self
1821 .pending_scroll_events
1822 .borrow()
1823 .iter()
1824 .any(|existing| existing.equivalent(event_target, &event))
1825 {
1826 return;
1827 }
1828
1829 self.pending_scroll_events
1832 .borrow_mut()
1833 .push(PendingScrollEvent {
1834 target: Dom::from_ref(event_target),
1835 event: "scroll".into(),
1836 });
1837 }
1838
1839 pub(crate) fn node_from_nodes_and_strings(
1841 &self,
1842 cx: &mut JSContext,
1843 mut nodes: Vec<NodeOrString>,
1844 ) -> Fallible<DomRoot<Node>> {
1845 if nodes.len() == 1 {
1846 Ok(match nodes.pop().unwrap() {
1847 NodeOrString::Node(node) => node,
1848 NodeOrString::String(string) => DomRoot::upcast(self.CreateTextNode(cx, string)),
1849 })
1850 } else {
1851 let fragment = DomRoot::upcast::<Node>(self.CreateDocumentFragment(cx));
1852 for node in nodes {
1853 match node {
1854 NodeOrString::Node(node) => {
1855 fragment.AppendChild(cx, &node)?;
1856 },
1857 NodeOrString::String(string) => {
1858 let node = DomRoot::upcast::<Node>(self.CreateTextNode(cx, string));
1859 fragment.AppendChild(cx, &node).unwrap();
1862 },
1863 }
1864 }
1865 Ok(fragment)
1866 }
1867 }
1868
1869 pub(crate) fn get_body_attribute(&self, local_name: &LocalName) -> DOMString {
1870 match self.GetBody() {
1871 Some(ref body) if body.is_body_element() => {
1872 body.upcast::<Element>().get_string_attribute(local_name)
1873 },
1874 _ => DOMString::new(),
1875 }
1876 }
1877
1878 pub(crate) fn set_body_attribute(
1879 &self,
1880 cx: &mut JSContext,
1881 local_name: &LocalName,
1882 value: DOMString,
1883 ) {
1884 if let Some(ref body) = self.GetBody().filter(|elem| elem.is_body_element()) {
1885 let body = body.upcast::<Element>();
1886 let value = body.parse_attribute(&ns!(), local_name, value);
1887 body.set_attribute(cx, local_name, value);
1888 }
1889 }
1890
1891 pub(crate) fn set_current_script(&self, script: Option<&HTMLScriptElement>) {
1892 self.current_script.set(script);
1893 }
1894
1895 pub(crate) fn has_a_stylesheet_that_is_blocking_scripts(&self) -> bool {
1897 !self.script_blocking_stylesheet_set.borrow().is_empty()
1898 }
1899
1900 pub(crate) fn add_script_blocking_stylesheet(&self, id: StylesheetContextId) {
1901 self.script_blocking_stylesheet_set.borrow_mut().insert(id);
1902 }
1903
1904 pub(crate) fn remove_script_blocking_stylesheet(&self, id: StylesheetContextId) {
1905 self.script_blocking_stylesheet_set
1906 .borrow_mut()
1907 .shift_remove(&id);
1908 }
1909
1910 pub(crate) fn render_blocking_element_count(&self) -> u32 {
1911 self.render_blocking_element_count.get()
1912 }
1913
1914 pub(crate) fn increment_render_blocking_element_count(&self) {
1916 assert!(self.allows_adding_render_blocking_elements());
1923 let count_cell = &self.render_blocking_element_count;
1924 count_cell.set(count_cell.get() + 1);
1925 }
1926
1927 pub(crate) fn decrement_render_blocking_element_count(&self) {
1929 let count_cell = &self.render_blocking_element_count;
1935 assert!(count_cell.get() > 0);
1936 count_cell.set(count_cell.get() - 1);
1937 }
1938
1939 pub(crate) fn allows_adding_render_blocking_elements(&self) -> bool {
1941 self.is_html_document && self.GetBody().is_none()
1944 }
1945
1946 pub(crate) fn is_render_blocked(&self) -> bool {
1948 self.render_blocking_element_count() > 0
1952 }
1957
1958 pub(crate) fn invalidate_stylesheets(&self, no_gc: &NoGC) {
1959 self.stylesheets.borrow_mut().force_dirty(OriginSet::all());
1960
1961 if let Some(element) = self.GetDocumentElement() {
1965 element.upcast::<Node>().dirty(no_gc, NodeDamage::Style);
1966 }
1967 }
1968
1969 pub(crate) fn has_active_request_animation_frame_callbacks(&self) -> bool {
1972 !self.animation_frame_list.borrow().is_empty()
1973 }
1974
1975 pub(crate) fn request_animation_frame(&self, callback: AnimationFrameCallback) -> u32 {
1977 let ident = self.animation_frame_ident.get() + 1;
1978 self.animation_frame_ident.set(ident);
1979
1980 let had_animation_frame_callbacks;
1981 {
1982 let mut animation_frame_list = self.animation_frame_list.borrow_mut();
1983 had_animation_frame_callbacks = !animation_frame_list.is_empty();
1984 animation_frame_list.push_back((ident, Some(callback)));
1985 }
1986
1987 if !self.running_animation_callbacks.get() && !had_animation_frame_callbacks {
1993 self.window().send_to_constellation(
1994 ScriptToConstellationMessage::ChangeRunningAnimationsState(
1995 AnimationState::AnimationCallbacksPresent,
1996 ),
1997 );
1998 }
1999
2000 ident
2001 }
2002
2003 pub(crate) fn cancel_animation_frame(&self, ident: u32) {
2005 let mut list = self.animation_frame_list.borrow_mut();
2006 if let Some(pair) = list.iter_mut().find(|pair| pair.0 == ident) {
2007 pair.1 = None;
2008 }
2009 }
2010
2011 pub(crate) fn run_the_animation_frame_callbacks(&self, cx: &mut CurrentRealm) {
2013 self.running_animation_callbacks.set(true);
2014 let timing = self.global().performance(cx).Now();
2015
2016 let num_callbacks = self.animation_frame_list.borrow().len();
2017 for _ in 0..num_callbacks {
2018 rooted!(&in(cx) let maybe_callback = self
2019 .animation_frame_list
2020 .borrow_mut()
2021 .pop_front()
2022 .unwrap()
2023 .1);
2024 if let Some(ref callback) = *maybe_callback {
2025 callback.call(cx, self, *timing);
2026 }
2027 }
2028 self.running_animation_callbacks.set(false);
2029
2030 if self.animation_frame_list.borrow().is_empty() {
2031 self.window().send_to_constellation(
2032 ScriptToConstellationMessage::ChangeRunningAnimationsState(
2033 AnimationState::AnimationCallbacksAbsent,
2034 ),
2035 );
2036 }
2037 }
2038
2039 pub(crate) fn policy_container(&self) -> Ref<'_, StdArc<PolicyContainer>> {
2040 self.policy_container.borrow()
2041 }
2042
2043 pub(crate) fn set_policy_container(&self, policy_container: StdArc<PolicyContainer>) {
2044 *self.policy_container.borrow_mut() = policy_container;
2045 }
2046
2047 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
2048 StdArc::make_mut(&mut *self.policy_container.borrow_mut()).set_csp_list(csp_list);
2049 }
2050
2051 pub(crate) fn enforce_csp_policy(&self, policy: CspPolicy) {
2053 let mut csp_list = self.get_csp_list().clone().unwrap_or(CspList(vec![]));
2055 csp_list.push(policy);
2056 StdArc::make_mut(&mut *self.policy_container.borrow_mut()).set_csp_list(Some(csp_list));
2057 }
2058
2059 pub(crate) fn get_csp_list(&self) -> Ref<'_, Option<CspList>> {
2060 Ref::map(self.policy_container.borrow(), |policy_container| {
2061 &policy_container.csp_list
2062 })
2063 }
2064
2065 pub(crate) fn preloaded_resources(&self) -> std::cell::Ref<'_, PreloadedResources> {
2066 self.preloaded_resources.borrow()
2067 }
2068
2069 pub(crate) fn insert_preloaded_resource(&self, key: PreloadKey, preload_id: PreloadId) {
2070 self.preloaded_resources
2071 .borrow_mut()
2072 .insert(key, preload_id);
2073 }
2074
2075 pub(crate) fn fetch_blocking<Listener: FetchResponseListener>(
2076 &self,
2077 load: LoadType,
2078 request: RequestBuilder,
2079 listener: Listener,
2080 ) {
2081 self.loader_mut().add_blocking_load(load);
2082 self.fetch_background(request, listener);
2083 }
2084
2085 pub(crate) fn fetch_background<Listener: FetchResponseListener>(
2086 &self,
2087 request_builder: RequestBuilder,
2088 listener: Listener,
2089 ) {
2090 let networking_task_source = self
2091 .owner_global()
2092 .task_manager()
2093 .networking_task_source()
2094 .to_sendable();
2095 self.window()
2096 .as_global_scope()
2097 .fetch(request_builder, listener, networking_task_source);
2098 }
2099
2100 fn deferred_fetch_control_document(&self) -> DomRoot<Document> {
2102 match self.window().window_proxy().frame_element() {
2103 None => DomRoot::from_ref(self),
2106 Some(container) => container.owner_document().deferred_fetch_control_document(),
2108 }
2109 }
2110
2111 pub(crate) fn available_deferred_fetch_quota(&self, origin: ImmutableOrigin) -> isize {
2113 let control_document = self.deferred_fetch_control_document();
2115 let navigable = control_document.window();
2117 let is_top_level = navigable.is_top_level();
2120 let deferred_fetch_allowed = true;
2124 let deferred_fetch_minimal_allowed = true;
2128 let mut quota = match is_top_level {
2130 true if !deferred_fetch_allowed => 0,
2132 true if !deferred_fetch_minimal_allowed => 640 * 1024,
2134 true => 512 * 1024,
2136 _ if deferred_fetch_allowed => 0,
2140 _ if deferred_fetch_minimal_allowed => 8 * 1024,
2144 _ => 0,
2146 } as isize;
2147 let mut quota_for_request_origin = 64 * 1024_isize;
2149 let deferred_fetches = navigable.as_global_scope().fetch_group().deferred_fetches();
2158 for deferred_fetch in deferred_fetches {
2159 if deferred_fetch.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
2161 continue;
2162 }
2163 let request_length = deferred_fetch.request.total_request_length();
2165 quota -= request_length as isize;
2167 if deferred_fetch.request.url().origin() == origin {
2170 quota_for_request_origin -= request_length as isize;
2171 }
2172 }
2173 if quota <= 0 {
2175 return 0;
2176 }
2177 if quota < quota_for_request_origin {
2179 return quota;
2180 }
2181 quota_for_request_origin
2183 }
2184
2185 pub(crate) fn update_document_for_history_step_application(
2187 &self,
2188 old_url: &ServoUrl,
2189 new_url: &ServoUrl,
2190 ) {
2191 if old_url.as_url()[Position::BeforeFragment..] !=
2221 new_url.as_url()[Position::BeforeFragment..]
2222 {
2223 let window = Trusted::new(self.owner_window().deref());
2224 let old_url = old_url.to_string();
2225 let new_url = new_url.to_string();
2226 self.owner_global()
2227 .task_manager()
2228 .dom_manipulation_task_source()
2229 .queue(task!(hashchange_event: move |cx| {
2230 let window = window.root();
2231 HashChangeEvent::new(
2232 cx,
2233 &window,
2234 atom!("hashchange"),
2235 false,
2236 false,
2237 old_url,
2238 new_url,
2239 )
2240 .upcast::<Event>()
2241 .fire(cx, window.upcast());
2242 }));
2243 }
2244 }
2245
2246 pub(crate) fn finish_load_for_dropped_blocker(&self, load: LoadType) {
2247 let this = Trusted::new(self);
2248 self.owner_global()
2249 .task_manager()
2250 .dom_manipulation_task_source()
2251 .queue(task!(check_finished_load: move |cx| {
2252 this.root().finish_load(load, cx);
2253 }));
2254 }
2255
2256 pub(crate) fn finish_load(&self, load: LoadType, cx: &mut JSContext) {
2259 debug!("Document {:?} got finish_load: {:?}", self.url(), load);
2261 self.loader.borrow_mut().finish_load(&load);
2262
2263 match load {
2264 LoadType::Stylesheet(_) => {
2265 self.process_pending_parsing_blocking_script(cx);
2268
2269 self.process_deferred_scripts(cx);
2271 },
2272 LoadType::PageSource(_) => {
2273 if self.has_browsing_context && self.is_fully_active() {
2276 self.window().allow_layout_if_necessary(cx);
2277 }
2278
2279 self.process_deferred_scripts(cx);
2284 },
2285 _ => {},
2286 }
2287
2288 let document = Trusted::new(self);
2290 self.owner_global()
2291 .task_manager()
2292 .dom_manipulation_task_source()
2293 .queue(task!(wait_for_load_blockers: move |cx| {
2294 document.root().wait_until_load_blockers_have_resolved(cx);
2295 }));
2296 }
2297
2298 pub(crate) fn check_if_unloading_is_cancelled(
2300 &self,
2301 cx: &mut JSContext,
2302 recursive_flag: bool,
2303 ) -> bool {
2304 self.incr_ignore_opens_during_unload_counter();
2307 let beforeunload_event = BeforeUnloadEvent::new(
2309 cx,
2310 &self.window,
2311 atom!("beforeunload"),
2312 EventBubbles::Bubbles,
2313 EventCancelable::Cancelable,
2314 );
2315 let event = beforeunload_event.upcast::<Event>();
2316 event.set_trusted(true);
2317 let event_target = self.window.upcast::<EventTarget>();
2318 let has_listeners = event_target.has_listeners_for(&atom!("beforeunload"));
2319 self.window.dispatch_event_with_target_override(cx, event);
2320 if has_listeners {
2323 self.salvageable.set(false);
2324 }
2325 let mut can_unload = true;
2326 let default_prevented = event.DefaultPrevented();
2328 let return_value_not_empty = !event
2329 .downcast::<BeforeUnloadEvent>()
2330 .unwrap()
2331 .ReturnValue()
2332 .is_empty();
2333 if default_prevented || return_value_not_empty {
2334 let (chan, port) = generic_channel::channel().expect("Failed to create IPC channel!");
2335 let msg = EmbedderMsg::AllowUnload(self.webview_id(), chan);
2336 self.send_to_embedder(msg);
2337 can_unload = port.recv().unwrap() == AllowOrDeny::Allow;
2338 }
2339 if !recursive_flag {
2341 let iframes: Vec<_> = self.iframes().iter().collect();
2344 for iframe in &iframes {
2345 let document = iframe.owner_document();
2347 can_unload = document.check_if_unloading_is_cancelled(cx, true);
2348 if !document.salvageable() {
2349 self.salvageable.set(false);
2350 }
2351 if !can_unload {
2352 break;
2353 }
2354 }
2355 }
2356 self.decr_ignore_opens_during_unload_counter();
2358 can_unload
2359 }
2360
2361 pub(crate) fn unload(&self, cx: &mut JSContext, recursive_flag: bool) {
2363 if self.window_detached() {
2364 return;
2365 }
2366
2367 self.incr_ignore_opens_during_unload_counter();
2370 if self.page_showing.get() {
2372 self.page_showing.set(false);
2374 let event = PageTransitionEvent::new(
2377 cx,
2378 &self.window,
2379 atom!("pagehide"),
2380 false, false, self.salvageable.get(), );
2384 let event = event.upcast::<Event>();
2385 event.set_trusted(true);
2386 self.window.dispatch_event_with_target_override(cx, event);
2387 self.update_visibility_state(cx, DocumentVisibilityState::Hidden);
2389 }
2390 if !self.fired_unload.get() {
2392 let event = Event::new(
2393 cx,
2394 self.window.upcast(),
2395 atom!("unload"),
2396 EventBubbles::Bubbles,
2397 EventCancelable::Cancelable,
2398 );
2399 event.set_trusted(true);
2400 let event_target = self.window.upcast::<EventTarget>();
2401 let has_listeners = event_target.has_listeners_for(&atom!("unload"));
2402 self.window.dispatch_event_with_target_override(cx, &event);
2403 self.fired_unload.set(true);
2404 if has_listeners {
2406 self.salvageable.set(false);
2407 }
2408 }
2409 if !recursive_flag {
2413 let iframes: Vec<_> = self.iframes().iter().collect();
2416 for iframe in &iframes {
2417 let document = iframe.owner_document();
2419 document.unload(cx, true);
2420 if !document.salvageable() {
2421 self.salvageable.set(false);
2422 }
2423 }
2424 }
2425
2426 self.unloading_cleanup_steps(cx);
2428
2429 self.window.as_global_scope().clean_up_all_file_resources();
2431
2432 self.decr_ignore_opens_during_unload_counter();
2434
2435 }
2438
2439 fn completely_finish_loading(&self) {
2441 self.completely_loaded.set(true);
2446 self.notify_constellation_load();
2455
2456 if let Some(DeclarativeRefresh::PendingLoad {
2465 url,
2466 time,
2467 from_meta_element,
2468 }) = &*self.declarative_refresh.borrow()
2469 {
2470 self.window.as_global_scope().schedule_callback(
2471 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
2472 url: url.clone(),
2473 from_meta_element: *from_meta_element,
2474 }),
2475 Duration::from_secs(*time),
2476 );
2477 }
2478 }
2479
2480 fn queue_document_completion(&self, cx: &mut JSContext) {
2482 assert!(!self.is_initial_about_blank());
2486
2487 self.loader.borrow_mut().inhibit_events();
2488
2489 debug!("Document loads are complete.");
2494 let document = Trusted::new(self);
2495 self.owner_global()
2496 .task_manager()
2497 .dom_manipulation_task_source()
2498 .queue(task!(fire_load_event: move |cx| {
2499 let document = document.root();
2500 let window = document.window();
2502 if !window.is_alive() || document.window_detached() {
2503 return;
2504 }
2505
2506 document.update_the_current_document_readiness(cx, DocumentReadyState::Complete);
2508
2509 if document.browsing_context().is_none() {
2511 return;
2512 }
2513
2514 update_with_current_instant(&document.navigation_timing.load_event_start);
2516
2517 let load_event = Event::new(
2519 cx,
2520 window.upcast(),
2521 atom!("load"),
2522 EventBubbles::DoesNotBubble,
2523 EventCancelable::NotCancelable,
2524 );
2525 load_event.set_trusted(true);
2526 debug!("About to dispatch load for {:?}", document.url());
2527 window.dispatch_event_with_target_override(cx, &load_event);
2528
2529 update_with_current_instant(&document.navigation_timing.load_event_end);
2539
2540 document.page_showing.set(true);
2545
2546 let page_show_event = PageTransitionEvent::new(
2548 cx,
2549 window,
2550 atom!("pageshow"),
2551 false, false, false, );
2555 let page_show_event = page_show_event.upcast::<Event>();
2556 page_show_event.set_trusted(true);
2557 page_show_event.fire(cx, window.upcast());
2558
2559 document.completely_finish_loading();
2561
2562 if let Some(fragment) = document.url().fragment() {
2566 document.scroll_to_the_fragment(cx, fragment);
2567 }
2568 }));
2569
2570 #[cfg(feature = "webxr")]
2585 if pref!(dom_webxr_sessionavailable) && self.window.is_top_level() {
2586 self.window.Navigator(cx).Xr(cx).dispatch_sessionavailable();
2587 }
2588 }
2589
2590 pub(crate) fn completely_loaded(&self) -> bool {
2591 self.completely_loaded.get()
2592 }
2593
2594 pub(crate) fn start_the_end_loading_phase(&self) {
2595 if self.is_initial_about_blank() {
2596 self.current_the_end_loading_phase
2598 .set(TheEndLoadingPhase::Done);
2599 } else {
2600 self.current_the_end_loading_phase
2601 .set(TheEndLoadingPhase::ProcessingDeferredScripts);
2602 }
2603 }
2604
2605 pub(crate) fn set_pending_parsing_blocking_script(
2607 &self,
2608 script: &HTMLScriptElement,
2609 load: Option<ScriptResult>,
2610 ) {
2611 assert!(!self.has_pending_parsing_blocking_script());
2612 *self.pending_parsing_blocking_script.borrow_mut() =
2613 Some(PendingScript::new_with_load(script, load));
2614 }
2615
2616 pub(crate) fn has_pending_parsing_blocking_script(&self) -> bool {
2618 self.pending_parsing_blocking_script.borrow().is_some()
2619 }
2620
2621 pub(crate) fn pending_parsing_blocking_script_loaded(
2623 &self,
2624 element: &HTMLScriptElement,
2625 result: ScriptResult,
2626 cx: &mut JSContext,
2627 ) {
2628 {
2629 let mut blocking_script = self.pending_parsing_blocking_script.borrow_mut();
2630 let entry = blocking_script.as_mut().unwrap();
2631 assert!(&*entry.element == element);
2632 entry.loaded(result);
2633 }
2634 self.process_pending_parsing_blocking_script(cx);
2635 }
2636
2637 fn process_pending_parsing_blocking_script(&self, cx: &mut JSContext) {
2638 if self.has_a_stylesheet_that_is_blocking_scripts() {
2639 return;
2640 }
2641 let pair = self
2642 .pending_parsing_blocking_script
2643 .borrow_mut()
2644 .as_mut()
2645 .and_then(PendingScript::take_result);
2646 if let Some((element, result)) = pair {
2647 *self.pending_parsing_blocking_script.borrow_mut() = None;
2648 self.get_current_parser()
2649 .unwrap()
2650 .resume_with_pending_parsing_blocking_script(cx, &element, result);
2651 }
2652 }
2653
2654 pub(crate) fn add_asap_script(&self, script: &HTMLScriptElement) {
2656 self.asap_scripts_set
2657 .borrow_mut()
2658 .push(Dom::from_ref(script));
2659 }
2660
2661 pub(crate) fn asap_script_loaded(
2664 &self,
2665 cx: &mut JSContext,
2666 element: &HTMLScriptElement,
2667 result: ScriptResult,
2668 ) {
2669 {
2670 let mut scripts = self.asap_scripts_set.borrow_mut();
2671 let idx = scripts
2672 .iter()
2673 .position(|entry| &**entry == element)
2674 .unwrap();
2675 scripts.swap_remove(idx);
2676 }
2677 element.execute(cx, result);
2678 self.wait_until_asap_scripts_have_executed();
2679 }
2680
2681 pub(crate) fn push_asap_in_order_script(&self, script: &HTMLScriptElement) {
2683 self.asap_in_order_scripts_list.push(script);
2684 }
2685
2686 pub(crate) fn asap_in_order_script_loaded(
2689 &self,
2690 cx: &mut JSContext,
2691 element: &HTMLScriptElement,
2692 result: ScriptResult,
2693 ) {
2694 self.asap_in_order_scripts_list.loaded(element, result);
2695 while let Some((element, result)) = self
2696 .asap_in_order_scripts_list
2697 .take_next_ready_to_be_executed()
2698 {
2699 element.execute(cx, result);
2700 }
2701
2702 self.wait_until_asap_scripts_have_executed();
2703 }
2704
2705 pub(crate) fn add_deferred_script(&self, script: &HTMLScriptElement) {
2707 self.deferred_scripts.push(script);
2708 }
2709
2710 pub(crate) fn deferred_script_loaded(
2713 &self,
2714 cx: &mut JSContext,
2715 element: &HTMLScriptElement,
2716 result: ScriptResult,
2717 ) {
2718 self.deferred_scripts.loaded(element, result);
2719 self.process_deferred_scripts(cx);
2720 }
2721
2722 fn process_deferred_scripts(&self, cx: &mut JSContext) {
2724 if self.current_the_end_loading_phase.get() != TheEndLoadingPhase::ProcessingDeferredScripts
2725 {
2726 return;
2727 }
2728
2729 loop {
2733 if self.has_a_stylesheet_that_is_blocking_scripts() {
2734 return;
2735 }
2736 if let Some((element, result)) = self.deferred_scripts.take_next_ready_to_be_executed()
2739 {
2740 element.execute(cx, result);
2742 } else {
2743 break;
2744 }
2745 }
2746 if self.deferred_scripts.is_empty() {
2748 self.current_the_end_loading_phase
2749 .set(TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts);
2750 self.dispatch_dom_content_loaded();
2751 }
2752 }
2753
2754 fn dispatch_dom_content_loaded(&self) {
2756 assert_ne!(
2757 self.ReadyState(),
2758 DocumentReadyState::Complete,
2759 "Complete before DOMContentLoaded?"
2760 );
2761
2762 let document = Trusted::new(self);
2765 self.owner_global()
2766 .task_manager()
2767 .dom_manipulation_task_source()
2768 .queue(task!(fire_dom_content_loaded_event: move |cx| {
2769 let document = document.root();
2772 update_with_current_instant(&document.navigation_timing.dom_content_loaded_event_start);
2773 document.upcast::<EventTarget>().fire_bubbling_event(cx, atom!("DOMContentLoaded"));
2775 update_with_current_instant(&document.navigation_timing.dom_content_loaded_event_end);
2778 }));
2787
2788 self.interactive_time
2790 .borrow()
2791 .maybe_set_tti(InteractiveFlag::DOMContentLoaded);
2792
2793 self.wait_until_asap_scripts_have_executed();
2794 }
2795
2796 fn has_finished_all_asap_scripts(&self) -> bool {
2797 self.asap_scripts_set.borrow().is_empty() && self.asap_in_order_scripts_list.is_empty()
2798 }
2799
2800 fn wait_until_asap_scripts_have_executed(&self) {
2802 if self.current_the_end_loading_phase.get() !=
2803 TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts
2804 {
2805 return;
2806 }
2807 if self.has_finished_all_asap_scripts() {
2810 let document = Trusted::new(self);
2811 self.owner_global()
2812 .task_manager()
2813 .dom_manipulation_task_source()
2814 .queue(task!(transition_away_from_asap_scripts: move |cx| {
2815 let document = document.root();
2816 if document.current_the_end_loading_phase.get() !=
2819 TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts
2820 {
2821 return;
2822 }
2823 if !document.has_finished_all_asap_scripts() {
2825 return;
2826 }
2827 document.current_the_end_loading_phase
2828 .set(TheEndLoadingPhase::WaitingForLoadEventBlockers);
2829 document.wait_until_load_blockers_have_resolved(cx);
2830 }));
2831 }
2832 }
2833
2834 pub(crate) fn wait_until_load_blockers_have_resolved(&self, cx: &mut JSContext) {
2836 if self.current_the_end_loading_phase.get() !=
2837 TheEndLoadingPhase::WaitingForLoadEventBlockers
2838 {
2839 return;
2840 }
2841 {
2843 let loader = self.loader.borrow();
2844
2845 if self
2847 .navigation_timing
2848 .top_level_dom_complete
2849 .get()
2850 .is_none() &&
2851 loader.is_only_blocked_by_iframes()
2852 {
2853 update_with_current_instant(&self.navigation_timing.top_level_dom_complete);
2854 }
2855
2856 let not_ready_for_load = loader.is_blocked() || loader.events_inhibited();
2857 if not_ready_for_load {
2858 return;
2859 }
2860 }
2861
2862 self.current_the_end_loading_phase
2863 .set(TheEndLoadingPhase::Done);
2864 self.queue_document_completion(cx);
2865 }
2866
2867 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
2869 if !self.is_fully_active() {
2871 self.salvageable.set(false);
2876 }
2880 for exited_iframe in self.iframes().iter() {
2893 debug!("Destroying nested iframe document");
2894 exited_iframe.destroy_document_and_its_descendants(cx);
2895 }
2896 self.destroy(cx);
2901 }
2904
2905 pub(crate) fn destroy(&self, cx: &mut JSContext) {
2907 let exited_window = self.window();
2908 self.abort(cx, AbortReason::Destroy);
2910 self.salvageable.set(false);
2912 self.unloading_cleanup_steps(cx);
2922
2923 exited_window
2926 .as_global_scope()
2927 .task_manager()
2928 .cancel_all_tasks_and_ignore_future_tasks();
2929
2930 exited_window.discard_browsing_context();
2932
2933 exited_window
2940 .as_global_scope()
2941 .disable_owned_worker_animation_frame_providers();
2942
2943 }
2947
2948 fn active_parser(&self) -> Option<DomRoot<ServoParser>> {
2950 self.get_current_parser()
2953 .filter(|parser| !(parser.has_stopped() || parser.has_aborted()))
2954 }
2955
2956 pub(crate) fn abort(&self, cx: &mut JSContext, reason: AbortReason) {
2958 self.loader.borrow_mut().inhibit_events();
2960
2961 self.script_blocking_stylesheet_set.borrow_mut().clear();
2970 *self.pending_parsing_blocking_script.borrow_mut() = None;
2971 *self.asap_scripts_set.borrow_mut() = vec![];
2972 self.asap_in_order_scripts_list.clear();
2973 self.deferred_scripts.clear();
2974
2975 let global = self.window.as_global_scope();
2976 let loads_cancelled = global.fetch_group_mut().terminate(global);
2977 let event_sources_canceled = global.close_event_sources();
2978
2979 if loads_cancelled || event_sources_canceled {
2980 self.salvageable.set(false);
2982 };
2983
2984 self.owner_global()
2989 .task_manager()
2990 .cancel_pending_tasks_for_source(TaskSourceName::Networking);
2991
2992 if let Some(parser) = self.active_parser() {
2997 self.active_parser_was_aborted.set(true);
2999 parser.abort(cx, reason);
3001 self.salvageable.set(false);
3003 }
3004 }
3005
3006 pub(crate) fn abort_a_document_and_its_descendants(
3008 &self,
3009 cx: &mut JSContext,
3010 reason: AbortReason,
3011 ) {
3012 for iframe in self.iframes().iter() {
3020 if let Some(descendant_document) = iframe.GetContentDocument() {
3021 let trusted_descendant_document = Trusted::new(&*descendant_document);
3022 let document = Trusted::new(self);
3023 descendant_document
3024 .owner_global()
3025 .task_manager()
3026 .navigation_and_traversal_task_source()
3027 .queue(task!(abort_iframe_document: move |cx| {
3028 let descendant_document = trusted_descendant_document.root();
3029 descendant_document.abort(cx, reason);
3031 if !descendant_document.salvageable.get() {
3033 document.root().salvageable.set(false);
3034 }
3035 }));
3036 }
3037 }
3038
3039 self.abort(cx, reason);
3041 }
3042
3043 pub(crate) fn notify_constellation_load(&self) {
3044 self.window()
3045 .send_to_constellation(ScriptToConstellationMessage::LoadComplete);
3046 }
3047
3048 pub(crate) fn set_current_parser(&self, script: Option<&ServoParser>, reason: SetParserReason) {
3049 trace!(
3050 "{} parser for {:?} due to {:?}",
3051 if script.is_some() {
3052 "setting"
3053 } else {
3054 "clearing"
3055 },
3056 self.url(),
3057 reason
3058 );
3059 self.current_parser.set(script);
3060 }
3061
3062 pub(crate) fn get_current_parser(&self) -> Option<DomRoot<ServoParser>> {
3063 self.current_parser.get()
3064 }
3065
3066 pub(crate) fn get_current_parser_line(&self) -> u32 {
3067 self.get_current_parser()
3068 .map(|parser| parser.get_current_line())
3069 .unwrap_or(0)
3070 }
3071
3072 pub(crate) fn set_ancestor_origins_list(&self, ancestor_origins_list: &DOMStringList) {
3074 self.ancestor_origins_list.set(Some(ancestor_origins_list));
3075 }
3076
3077 pub(crate) fn ancestor_origins_list(&self) -> Option<DomRoot<DOMStringList>> {
3079 self.ancestor_origins_list.get()
3080 }
3081
3082 pub(crate) fn set_internal_ancestor_origin_objects_list(
3084 &self,
3085 internal_ancestor_origin_objects_list: Vec<ImmutableOrigin>,
3086 ) {
3087 *self.internal_ancestor_origin_objects_list.borrow_mut() =
3088 Some(internal_ancestor_origin_objects_list);
3089 }
3090
3091 pub(crate) fn internal_ancestor_origin_objects_list(
3093 &self,
3094 ) -> Ref<'_, Option<Vec<ImmutableOrigin>>> {
3095 self.internal_ancestor_origin_objects_list.borrow()
3096 }
3097
3098 pub(crate) fn iframes(&self) -> &IFrameCollection {
3101 &self.iframes
3102 }
3103
3104 pub(crate) fn set_navigation_start(&self, navigation_start: CrossProcessInstant) {
3105 self.interactive_time
3106 .borrow_mut()
3107 .set_navigation_start(navigation_start);
3108 }
3109
3110 pub(crate) fn get_interactive_metrics(&self) -> Ref<'_, ProgressiveWebMetrics> {
3111 self.interactive_time.borrow()
3112 }
3113
3114 pub(crate) fn has_recorded_tti_metric(&self) -> bool {
3115 self.get_interactive_metrics().get_tti().is_some()
3116 }
3117
3118 pub(crate) fn start_tti(&self) {
3119 if self.get_interactive_metrics().needs_tti() {
3120 self.tti_window.borrow_mut().start_window();
3121 }
3122 }
3123
3124 pub(crate) fn record_tti_if_necessary(&self) {
3128 if self.has_recorded_tti_metric() {
3129 return;
3130 }
3131 if self.tti_window.borrow().needs_check() {
3132 self.get_interactive_metrics()
3133 .maybe_set_tti(InteractiveFlag::TimeToInteractive(
3134 self.tti_window.borrow().get_start(),
3135 ));
3136 }
3137 }
3138
3139 pub(crate) fn is_cookie_averse(&self) -> bool {
3141 !self.has_browsing_context || !url_has_network_scheme(&self.url())
3142 }
3143
3144 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
3145 self.document_or_shadow_root.custom_element_registry()
3146 }
3147
3148 pub(crate) fn set_custom_element_registry(&self, registry: &CustomElementRegistry) {
3149 self.document_or_shadow_root
3150 .set_custom_element_registry(Some(registry));
3151 }
3152
3153 pub(crate) fn effective_global_custom_element_registry(
3155 &self,
3156 ) -> Option<DomRoot<CustomElementRegistry>> {
3157 let document_custom_element_registry = self.custom_element_registry();
3160 if CustomElementRegistry::is_a_global_element_registry(
3161 document_custom_element_registry.as_deref(),
3162 ) {
3163 return document_custom_element_registry;
3164 }
3165 None
3167 }
3168
3169 pub(crate) fn teardown_custom_element_registry(&self) {
3172 if let Some(custom_elements) = self.custom_element_registry() {
3173 custom_elements.teardown();
3174 }
3175 }
3176
3177 pub(crate) fn increment_throw_on_dynamic_markup_insertion_counter(&self) {
3178 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
3179 self.throw_on_dynamic_markup_insertion_counter
3180 .set(counter + 1);
3181 }
3182
3183 pub(crate) fn decrement_throw_on_dynamic_markup_insertion_counter(&self) {
3184 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
3185 self.throw_on_dynamic_markup_insertion_counter
3186 .set(counter - 1);
3187 }
3188
3189 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
3190 for image in self.responsive_images.borrow().iter() {
3191 image.react_to_environment_changes(cx);
3192 }
3193 }
3194
3195 pub(crate) fn register_responsive_image(&self, img: &HTMLImageElement) {
3196 self.responsive_images.borrow_mut().push(Dom::from_ref(img));
3197 }
3198
3199 pub(crate) fn unregister_responsive_image(&self, img: &HTMLImageElement) {
3200 let index = self
3201 .responsive_images
3202 .borrow()
3203 .iter()
3204 .position(|x| **x == *img);
3205 if let Some(i) = index {
3206 self.responsive_images.borrow_mut().remove(i);
3207 }
3208 }
3209
3210 pub(crate) fn register_media_controls(&self, id: &str, controls: &ShadowRoot) {
3211 let did_have_these_media_controls = self
3212 .media_controls
3213 .borrow_mut()
3214 .insert(id.to_string(), Dom::from_ref(controls))
3215 .is_some();
3216 debug_assert!(
3217 !did_have_these_media_controls,
3218 "Trying to register known media controls"
3219 );
3220 }
3221
3222 pub(crate) fn unregister_media_controls(&self, id: &str) {
3223 let did_have_these_media_controls = self.media_controls.borrow_mut().remove(id).is_some();
3224 debug_assert!(
3225 did_have_these_media_controls,
3226 "Trying to unregister unknown media controls"
3227 );
3228 }
3229
3230 pub(crate) fn mark_canvas_as_dirty(&self, canvas: &Dom<HTMLCanvasElement>) {
3231 let mut dirty_canvases = self.dirty_canvases.borrow_mut();
3232 if dirty_canvases
3233 .iter()
3234 .any(|dirty_canvas| dirty_canvas == canvas)
3235 {
3236 return;
3237 }
3238 dirty_canvases.push(canvas.clone());
3239 }
3240
3241 pub(crate) fn needs_rendering_update(&self, no_gc: &NoGC) -> bool {
3245 if !self.is_fully_active() {
3246 return false;
3247 }
3248 if !self.window().layout_blocked() &&
3249 (!self.restyle_reason(no_gc).is_empty() ||
3250 self.window().layout().needs_new_display_list() ||
3251 self.window().layout().needs_accessibility_update())
3252 {
3253 return true;
3254 }
3255 if !self.rendering_update_reasons.get().is_empty() {
3256 return true;
3257 }
3258 if self.event_handler.has_pending_input_events() {
3259 return true;
3260 }
3261 if self.has_pending_scroll_events() {
3262 return true;
3263 }
3264 if self.window().has_unhandled_resize_event() {
3265 return true;
3266 }
3267 if self.has_pending_animated_image_update.get() || !self.dirty_canvases.borrow().is_empty()
3268 {
3269 return true;
3270 }
3271 if self.window().has_pending_media_query_evaluation() {
3272 return true;
3273 }
3274 if self
3275 .selection()
3276 .is_some_and(|selection| selection.visible_selection_dirty())
3277 {
3278 return true;
3279 }
3280
3281 false
3282 }
3283
3284 pub(crate) fn mark_paint_timing(&self) {
3286 self.paint_timing_info.set(PaintTimingInfo::now());
3290 }
3291
3292 pub(crate) fn paint_timing_info(&self) -> PaintTimingInfo {
3294 self.paint_timing_info.get()
3295 }
3296
3297 pub(crate) fn update_the_rendering(
3302 &self,
3303 cx: &mut JSContext,
3304 ) -> (ReflowPhasesRun, ReflowStatistics) {
3305 assert!(!self.is_render_blocked());
3306 self.mark_paint_timing();
3308
3309 let mut phases = ReflowPhasesRun::empty();
3312 if self.has_pending_animated_image_update.get() {
3313 self.animation_manager.update_active_image_animation_frames(
3314 &self.window,
3315 self.current_animation_timeline_value(),
3316 );
3317 self.has_pending_animated_image_update.set(false);
3318 phases.insert(ReflowPhasesRun::UpdatedImageData);
3319 }
3320
3321 self.current_rendering_epoch
3322 .set(self.current_rendering_epoch.get().next());
3323 let current_rendering_epoch = self.current_rendering_epoch.get();
3324
3325 let image_keys: Vec<_> = self
3327 .dirty_canvases
3328 .borrow_mut()
3329 .drain(..)
3330 .filter_map(|canvas| canvas.update_rendering(current_rendering_epoch))
3331 .collect();
3332
3333 let pipeline_id = self.window().pipeline_id();
3336 if !image_keys.is_empty() {
3337 phases.insert(ReflowPhasesRun::UpdatedImageData);
3338 self.waiting_on_canvas_image_updates.set(true);
3339 self.window().paint_api().delay_new_frame_for_canvas(
3340 self.webview_id(),
3341 self.window().pipeline_id(),
3342 current_rendering_epoch,
3343 image_keys,
3344 );
3345 }
3346
3347 let (reflow_phases, statistics) = self.window().reflow(cx, ReflowGoal::UpdateTheRendering);
3348 let phases = phases.union(reflow_phases);
3349
3350 self.window().paint_api().update_epoch(
3351 self.webview_id(),
3352 pipeline_id,
3353 current_rendering_epoch,
3354 );
3355
3356 (phases, statistics)
3357 }
3358
3359 pub(crate) fn handle_no_longer_waiting_on_asynchronous_image_updates(&self) {
3360 self.waiting_on_canvas_image_updates.set(false);
3361 }
3362
3363 pub(crate) fn waiting_on_canvas_image_updates(&self) -> bool {
3364 self.waiting_on_canvas_image_updates.get()
3365 }
3366
3367 pub(crate) fn maybe_fulfill_font_ready_promise(&self, cx: &mut JSContext) -> bool {
3377 if !self.is_fully_active() {
3378 return false;
3379 }
3380
3381 let fonts = self.Fonts(cx);
3382 if !fonts.waiting_to_fullfill_promise() {
3383 return false;
3384 }
3385 if self.window().font_context().web_fonts_still_loading() != 0 {
3386 return false;
3387 }
3388 if self.ReadyState() != DocumentReadyState::Complete {
3389 return false;
3390 }
3391 if !self.restyle_reason(cx.no_gc()).is_empty() {
3392 return false;
3393 }
3394 if !self.rendering_update_reasons.get().is_empty() {
3395 return false;
3396 }
3397
3398 let result = fonts.fulfill_ready_promise_if_needed(cx);
3399
3400 if result {
3404 self.add_rendering_update_reason(RenderingUpdateReason::FontReadyPromiseFulfilled);
3405 }
3406
3407 result
3408 }
3409
3410 pub(crate) fn id_map(&self) -> &TreeOrderedIndexMap {
3411 &self.id_map
3412 }
3413
3414 pub(crate) fn add_resize_observer(&self, resize_observer: &ResizeObserver) {
3416 self.resize_observers
3417 .borrow_mut()
3418 .push(Dom::from_ref(resize_observer));
3419 }
3420
3421 pub(crate) fn gather_active_resize_observations_at_depth(
3424 &self,
3425 no_gc: &NoGC,
3426 depth: &ResizeObservationDepth,
3427 ) -> bool {
3428 let mut has_active_resize_observations = false;
3429 for observer in self.resize_observers.borrow_mut().iter_mut() {
3430 observer.gather_active_resize_observations_at_depth(
3431 no_gc,
3432 depth,
3433 &mut has_active_resize_observations,
3434 );
3435 }
3436 has_active_resize_observations
3437 }
3438
3439 pub(crate) fn broadcast_active_resize_observations(
3441 &self,
3442 cx: &mut JSContext,
3443 ) -> ResizeObservationDepth {
3444 let mut shallowest = ResizeObservationDepth::max();
3445 let iterator: Vec<DomRoot<ResizeObserver>> = self
3449 .resize_observers
3450 .borrow()
3451 .iter()
3452 .map(|obs| obs.as_rooted())
3453 .collect();
3454 for observer in iterator {
3455 observer.broadcast_active_resize_observations(cx, &mut shallowest);
3456 }
3457 shallowest
3458 }
3459
3460 pub(crate) fn has_skipped_resize_observations(&self) -> bool {
3462 self.resize_observers
3463 .borrow()
3464 .iter()
3465 .any(|observer| observer.has_skipped_resize_observations())
3466 }
3467
3468 pub(crate) fn deliver_resize_loop_error_notification(&self, cx: &mut JSContext) {
3470 let error_info: ErrorInfo = crate::dom::bindings::error::ErrorInfo {
3471 message: "ResizeObserver loop completed with undelivered notifications.".to_string(),
3472 ..Default::default()
3473 };
3474 self.window
3475 .as_global_scope()
3476 .report_an_error(cx, error_info, HandleValue::null());
3477 }
3478
3479 pub(crate) fn status_code(&self) -> Option<u16> {
3480 self.status_code
3481 }
3482
3483 pub(crate) fn encoding_parse_a_url(&self, url: &str) -> Result<ServoUrl, url::ParseError> {
3485 let encoding = self.encoding.get();
3491
3492 let base_url = self.base_url();
3498
3499 url::Url::options()
3501 .base_url(Some(base_url.as_url()))
3502 .encoding_override(Some(&|input| {
3503 servo_url::encoding::encode_as_url_query_string(input, encoding)
3504 }))
3505 .parse(url)
3506 .map(ServoUrl::from)
3507 }
3508
3509 pub(crate) fn allowed_to_use_feature(&self, _feature: PermissionName) -> bool {
3511 if !self.has_browsing_context {
3513 return false;
3514 }
3515
3516 if !self.is_fully_active() {
3518 return false;
3519 }
3520
3521 true
3527 }
3528
3529 pub(crate) fn add_intersection_observer(&self, intersection_observer: &IntersectionObserver) {
3532 self.intersection_observers
3533 .borrow_mut()
3534 .push(Dom::from_ref(intersection_observer));
3535 }
3536
3537 pub(crate) fn remove_intersection_observer(
3541 &self,
3542 intersection_observer: &IntersectionObserver,
3543 ) {
3544 self.intersection_observers
3545 .borrow_mut()
3546 .retain(|observer| *observer != intersection_observer)
3547 }
3548
3549 pub(crate) fn update_intersection_observer_steps(
3551 &self,
3552 cx: &mut JSContext,
3553 time: CrossProcessInstant,
3554 ) {
3555 if self.intersection_observers.borrow().is_empty() {
3556 return;
3557 }
3558 self.window()
3560 .reflow_for_non_flushing_update_the_rendering_queries(cx);
3561
3562 for intersection_observer in &*self.intersection_observers.borrow() {
3564 self.update_single_intersection_observer_steps(cx, intersection_observer, time);
3565 }
3566 }
3567
3568 fn update_single_intersection_observer_steps(
3570 &self,
3571 cx: &mut JSContext,
3572 intersection_observer: &IntersectionObserver,
3573 time: CrossProcessInstant,
3574 ) {
3575 let root_bounds = intersection_observer.root_intersection_rectangle();
3578
3579 intersection_observer.update_intersection_observations_steps(cx, self, time, root_bounds);
3583 }
3584
3585 pub(crate) fn notify_intersection_observers(&self, cx: &mut JSContext) {
3587 self.intersection_observer_task_queued.set(false);
3590
3591 rooted_vec!(let notify_list <- self.intersection_observers.clone().take().into_iter());
3596
3597 for intersection_observer in notify_list.iter() {
3600 intersection_observer.invoke_callback_if_necessary(cx);
3602 }
3603 }
3604
3605 pub(crate) fn queue_an_intersection_observer_task(&self) {
3607 if self.intersection_observer_task_queued.get() {
3610 return;
3611 }
3612
3613 self.intersection_observer_task_queued.set(true);
3616
3617 let document = Trusted::new(self);
3621 self.owner_global()
3622 .task_manager()
3623 .intersection_observer_task_source()
3624 .queue(task!(notify_intersection_observers: move |cx| {
3625 document.root().notify_intersection_observers(cx);
3626 }));
3627 }
3628
3629 pub(crate) fn store_lcp_candidate(&self, candidate: LCPCandidate, element: Option<&Element>) {
3630 let load_time = element
3631 .and_then(|element| element.downcast::<HTMLImageElement>())
3632 .and_then(HTMLImageElement::load_time);
3633 self.lcp_candidates.borrow_mut().insert(
3634 candidate.id,
3635 LCPCandidateAndElement {
3636 element: element.map(Dom::from_ref),
3637 candidate,
3638 load_time,
3639 },
3640 );
3641 }
3642
3643 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
3644 pub(crate) fn handle_paint_metric(&self, cx: &mut JSContext, event: PaintMetricEvent) {
3645 let metrics = self.interactive_time.borrow();
3646 let entry = match event {
3647 PaintMetricEvent::FirstPaint(paint_timing_info, first_reflow) => {
3648 metrics.set_first_paint(paint_timing_info.default_paint_timestamp(), first_reflow);
3649 DomRoot::upcast::<PerformanceEntry>(PerformancePaintTiming::new(
3650 cx,
3651 self.window.as_global_scope(),
3652 ProgressiveWebMetricType::FirstPaint,
3653 paint_timing_info,
3654 ))
3655 },
3656 PaintMetricEvent::FirstContentfulPaint(paint_timing_info, first_reflow) => {
3657 metrics.set_first_contentful_paint(
3658 paint_timing_info.default_paint_timestamp(),
3659 first_reflow,
3660 );
3661 DomRoot::upcast::<PerformanceEntry>(PerformancePaintTiming::new(
3662 cx,
3663 self.window.as_global_scope(),
3664 ProgressiveWebMetricType::FirstContentfulPaint,
3665 paint_timing_info,
3666 ))
3667 },
3668 PaintMetricEvent::LargestContentfulPaint(paint_timing_info, id) => {
3669 let Some(stored_candidate) = self.lcp_candidates.borrow_mut().remove(&id) else {
3670 warn!("Received LCP paint metric for unknown candidate: {id:?}");
3671 return;
3672 };
3673 metrics
3674 .set_largest_contentful_paint(id, paint_timing_info.default_paint_timestamp());
3675 DomRoot::upcast::<PerformanceEntry>(LargestContentfulPaint::new(
3676 cx,
3677 self.window.as_global_scope(),
3678 &stored_candidate.candidate,
3679 stored_candidate.element.as_deref(),
3680 stored_candidate.load_time,
3681 paint_timing_info,
3682 ))
3683 },
3684 };
3685 self.window.Performance(cx).queue_entry(&entry);
3686 }
3687
3688 fn write(
3690 &self,
3691 cx: &mut JSContext,
3692 text: Vec<TrustedHTMLOrString>,
3693 line_feed: bool,
3694 containing_class: &str,
3695 field: &str,
3696 ) -> ErrorResult {
3697 let mut strings: Vec<String> = Vec::with_capacity(text.len());
3699 let mut is_trusted = true;
3701 for value in text {
3703 match value {
3704 TrustedHTMLOrString::TrustedHTML(trusted_html) => {
3706 strings.push(trusted_html.to_string());
3707 },
3708 TrustedHTMLOrString::String(str_) => {
3709 is_trusted = false;
3711 strings.push(str_.into());
3713 },
3714 };
3715 }
3716 let mut string = itertools::join(strings, "");
3717 if !is_trusted {
3721 string = TrustedHTML::get_trusted_type_compliant_string(
3722 cx,
3723 &self.global(),
3724 TrustedHTMLOrString::String(string.into()),
3725 &format!("{} {}", containing_class, field),
3726 )?
3727 .str()
3728 .to_owned();
3729 }
3730 if line_feed {
3732 string.push('\n');
3733 }
3734 if !self.is_html_document() {
3736 return Err(Error::InvalidState(Some(
3737 "Document must be a HTML document".into(),
3738 )));
3739 }
3740
3741 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
3744 return Err(Error::InvalidState(Some(
3745 "A custom element constructor attempted to open, close or write to this document"
3746 .into(),
3747 )));
3748 }
3749
3750 if self.active_parser_was_aborted.get() {
3752 return Ok(());
3753 }
3754
3755 let parser = match self.get_current_parser() {
3756 Some(ref parser) if parser.can_write() => DomRoot::from_ref(&**parser),
3757 _ => {
3759 if self.is_prompting_or_unloading() ||
3762 self.ignore_destructive_writes_counter.get() > 0
3763 {
3764 return Ok(());
3765 }
3766 self.Open(cx, None, None)?;
3768 self.get_current_parser().unwrap()
3769 },
3770 };
3771
3772 parser.write(cx, string.into());
3774
3775 Ok(())
3776 }
3777
3778 pub(crate) fn details_name_groups<'a: 'b, 'b>(
3779 &'a self,
3780 no_gc: &'b NoGC,
3781 ) -> RefMut<'b, DetailsNameGroups> {
3782 RefMut::map(
3783 self.details_name_groups.safe_borrow_mut(no_gc),
3784 |details_name_groups| details_name_groups.get_or_insert_default(),
3785 )
3786 }
3787
3788 pub(crate) fn accessibility_data_mut(&self) -> RefMut<'_, AccessibilityData> {
3789 self.accessibility_data.borrow_mut()
3790 }
3791
3792 pub(crate) fn accessibility_active(&self) -> bool {
3793 self.window().layout().accessibility_active()
3794 }
3795
3796 pub(crate) fn rooted_nodes_for_accessibility_integrity_check(
3797 &self,
3798 ) -> Option<FxHashSet<OpaqueNode>> {
3799 if !self.accessibility_active() {
3800 return None;
3801 }
3802
3803 let mut accessibility_data = self.accessibility_data_mut();
3804
3805 if pref!(expensive_accessibility_test_assertions_enabled) {
3806 return Some(accessibility_data.unroot_and_drain_all_removed_nodes());
3807 }
3808
3809 accessibility_data.unroot_all_removed_nodes();
3810 None
3811 }
3812
3813 pub(crate) fn get_document_element_unrooted<'a>(
3814 &self,
3815 no_gc: &'a NoGC,
3816 ) -> Option<UnrootedDom<'a, Element>> {
3817 self.upcast::<Node>().child_elements_unrooted(no_gc).next()
3818 }
3819
3820 pub(crate) fn collect_reports(
3821 &self,
3822 reports: &mut Vec<Report>,
3823 ops: &mut MallocSizeOfOps,
3824 ) -> HashSet<*const JSObject> {
3825 let mut computed_objects = HashSet::new();
3826 let mut sizes = DocumentSizes::default();
3827
3828 for node in self
3829 .upcast::<Node>()
3830 .traverse_preorder(ShadowIncluding::Yes)
3831 {
3832 let size = compute_size(node.jsobject(), ops, &computed_objects, None);
3833
3834 match node.type_id() {
3835 NodeTypeId::Element(_) => {
3836 sizes.element_nodes_size += size;
3837
3838 let element = node.downcast::<Element>().expect("node must be Element");
3839 for attr in element.attrs().borrow().iter() {
3840 if let Some(attr) = attr.as_attr() {
3841 let size = compute_size(
3842 attr.upcast::<Node>().jsobject(),
3843 ops,
3844 &computed_objects,
3845 None,
3846 );
3847 sizes.attribute_nodes_size += size;
3848 computed_objects.insert(attr.upcast::<Node>().jsobject());
3849 }
3850 }
3851 },
3852 NodeTypeId::CharacterData(_) => sizes.text_nodes_size += size,
3853 _ => sizes.other_nodes_size += size,
3854 };
3855
3856 computed_objects.insert(node.jsobject());
3857 }
3858
3859 let prefix = format!("url({})", self.url());
3860 reports.push(Report {
3861 path: path![prefix, "js", "dom", "element-nodes"],
3862 kind: ReportKind::ExplicitJemallocHeapSize,
3863 size: sizes.element_nodes_size,
3864 });
3865 reports.push(Report {
3866 path: path![prefix, "js", "dom", "text-nodes"],
3867 kind: ReportKind::ExplicitJemallocHeapSize,
3868 size: sizes.text_nodes_size,
3869 });
3870 reports.push(Report {
3871 path: path![prefix, "js", "dom", "attribute-nodes"],
3872 kind: ReportKind::ExplicitJemallocHeapSize,
3873 size: sizes.attribute_nodes_size,
3874 });
3875 reports.push(Report {
3876 path: path![prefix, "js", "dom", "other-nodes"],
3877 kind: ReportKind::ExplicitJemallocHeapSize,
3878 size: sizes.other_nodes_size,
3879 });
3880
3881 computed_objects
3882 }
3883
3884 pub(crate) fn live_ranges(&self) -> &WeakRangeVec {
3886 &self.live_ranges
3887 }
3888
3889 pub(crate) fn gained_or_lost_system_focus(&self, cx: &mut JSContext, gained_focus: bool) {
3890 let focus_handler = self.focus_handler();
3891 if !self.is_fully_active() || !focus_handler.has_focus() {
3892 return;
3893 }
3894 focus_handler.gained_or_lost_system_focus(cx, gained_focus);
3895 self.refresh_focus_rendering();
3896 }
3897
3898 pub(crate) fn refresh_focus_rendering(&self) {
3899 self.window().layout().set_needs_new_display_list();
3900 }
3901}
3902
3903#[derive(Default)]
3905struct DocumentSizes {
3906 element_nodes_size: usize,
3907 text_nodes_size: usize,
3908 attribute_nodes_size: usize,
3909 other_nodes_size: usize,
3910}
3911
3912impl<'dom> LayoutDom<'dom, Document> {
3913 #[inline]
3914 pub(crate) fn is_html_document_for_layout(&self) -> bool {
3915 self.unsafe_get().is_html_document
3916 }
3917
3918 #[inline]
3919 pub(crate) fn quirks_mode(self) -> QuirksMode {
3920 self.unsafe_get().quirks_mode.get()
3921 }
3922
3923 #[inline]
3924 pub(crate) fn shared_style_locks(self) -> &'dom SharedRwLocks {
3925 self.unsafe_get().shared_style_locks()
3926 }
3927
3928 #[inline]
3929 pub(crate) fn flush_shadow_root_stylesheets_if_necessary(
3930 self,
3931 stylist: &mut Stylist,
3932 guard: &SharedRwLockReadGuard,
3933 ) {
3934 (*self.unsafe_get()).flush_shadow_root_stylesheets_if_necessary_for_layout(stylist, guard)
3935 }
3936
3937 pub(crate) fn elements_with_id(self, id: &Atom) -> &[LayoutDom<'dom, Element>] {
3938 self.unsafe_get().id_map.get_all_for_layout(id)
3939 }
3940
3941 #[expect(unsafe_code)]
3942 pub(crate) fn url_for_layout(self) -> ServoUrl {
3943 unsafe { self.unsafe_get().url.borrow_for_layout() }.clone()
3944 }
3945
3946 #[expect(unsafe_code)]
3947 pub(crate) fn visible_selection_for_layout(&self) -> Option<LayoutDom<'dom, Selection>> {
3948 unsafe { self.unsafe_get().selection.to_layout() }
3949 }
3950
3951 #[expect(unsafe_code)]
3952 pub(crate) fn default_language_for_layout(&self) -> Option<&'dom str> {
3953 unsafe { self.unsafe_get().default_language.borrow_for_layout() }.as_deref()
3954 }
3955}
3956
3957pub(crate) fn get_registrable_domain_suffix_of_or_is_equal_to(
3961 host_suffix_string: &str,
3962 original_host: Host,
3963) -> Option<Host> {
3964 if host_suffix_string.is_empty() {
3966 return None;
3967 }
3968
3969 let host = match Host::parse(host_suffix_string) {
3971 Ok(host) => host,
3972 Err(_) => return None,
3973 };
3974
3975 if host != original_host {
3977 let host = match host {
3979 Host::Domain(ref host) => host,
3980 _ => return None,
3981 };
3982 let original_host = match original_host {
3983 Host::Domain(ref original_host) => original_host,
3984 _ => return None,
3985 };
3986
3987 let index = original_host.len().checked_sub(host.len())?;
3989 let (prefix, suffix) = original_host.split_at(index);
3990
3991 if !prefix.ends_with('.') {
3992 return None;
3993 }
3994 if suffix != host {
3995 return None;
3996 }
3997
3998 if is_pub_domain(host) {
4000 return None;
4001 }
4002 }
4003
4004 Some(host)
4006}
4007
4008fn url_has_network_scheme(url: &ServoUrl) -> bool {
4010 matches!(url.scheme(), "ftp" | "http" | "https")
4011}
4012
4013#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
4014pub(crate) enum HasBrowsingContext {
4015 No,
4016 Yes,
4017}
4018
4019impl Document {
4020 #[expect(clippy::too_many_arguments)]
4021 pub(crate) fn new_inherited(
4022 window: &Window,
4023 has_browsing_context: HasBrowsingContext,
4024 url: Option<ServoUrl>,
4025 about_base_url: Option<ServoUrl>,
4026 origin: MutableOrigin,
4027 is_html_document: IsHTMLDocument,
4028 content_type: Option<Mime>,
4029 last_modified: Option<String>,
4030 activity: DocumentActivity,
4031 doc_loader: DocumentLoader,
4032 referrer: Option<String>,
4033 status_code: Option<u16>,
4034 canceller: FetchCanceller,
4035 is_initial_about_blank: bool,
4036 allow_declarative_shadow_roots: bool,
4037 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4038 has_trustworthy_ancestor_origin: bool,
4039 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4040 creation_sandboxing_flag_set: SandboxingFlagSet,
4041 timeline: &DocumentTimeline,
4042 pipeline_id: PipelineId,
4043 image_cache: StdArc<dyn ImageCache>,
4044 ) -> Document {
4045 let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
4046
4047 let frame_type = match window.is_top_level() {
4048 true => TimerMetadataFrameType::RootWindow,
4049 false => TimerMetadataFrameType::IFrame,
4050 };
4051 let interactive_time = ProgressiveWebMetrics::new(
4052 window.time_profiler_chan().clone(),
4053 url.clone(),
4054 frame_type,
4055 );
4056
4057 let content_type = content_type.unwrap_or_else(|| {
4058 match is_html_document {
4059 IsHTMLDocument::HTMLDocument => "text/html",
4061 IsHTMLDocument::NonHTMLDocument => "application/xml",
4063 }
4064 .parse()
4065 .unwrap()
4066 });
4067
4068 let encoding = content_type
4069 .get_parameter(CHARSET)
4070 .and_then(|charset| Encoding::for_label(charset.as_bytes()))
4071 .unwrap_or(UTF_8);
4072
4073 let has_focus = window.parent_info().is_none();
4074 let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
4075 let shared_style_locks = window.script_thread().shared_style_locks().clone();
4076 let quirks_mode = if is_initial_about_blank {
4080 QuirksMode::Quirks
4081 } else {
4082 QuirksMode::NoQuirks
4084 };
4085
4086 Document {
4087 node: Node::new_document_node(),
4088 document_or_shadow_root: DocumentOrShadowRoot::new(window),
4089 window: Dom::from_ref(window),
4090 has_browsing_context,
4091 implementation: Default::default(),
4092 content_type,
4093 last_modified,
4094 url: DomRefCell::new(url),
4095 about_base_url: DomRefCell::new(about_base_url),
4096 quirks_mode: Cell::new(quirks_mode),
4097 event_handler: DocumentEventHandler::new(window),
4098 focus_handler: DocumentFocusHandler::new(window, has_focus),
4099 embedder_controls: DocumentEmbedderControls::new(window),
4100 id_map: TreeOrderedIndexMap::id(),
4101 name_map: TreeOrderedIndexMap::name(),
4102 encoding: Cell::new(encoding),
4104 is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
4105 activity: Cell::new(activity),
4106 tag_map: DomRefCell::new(HashMapTracedValues::new_fx()),
4107 tagns_map: DomRefCell::new(HashMapTracedValues::new_fx()),
4108 classes_map: DomRefCell::new(HashMapTracedValues::new()),
4109 images: Default::default(),
4110 embeds: Default::default(),
4111 links: Default::default(),
4112 forms: Default::default(),
4113 scripts: Default::default(),
4114 anchors: Default::default(),
4115 applets: Default::default(),
4116 iframes: IFrameCollection::new(),
4117 shared_style_locks,
4118 stylesheets: DomRefCell::new(DocumentStylesheetSet::new()),
4119 stylesheet_list: MutNullableDom::new(None),
4120 ready_state: Cell::new(DocumentReadyState::Complete),
4123 current_script: Default::default(),
4124 current_the_end_loading_phase: Default::default(),
4125 pending_parsing_blocking_script: Default::default(),
4126 script_blocking_stylesheet_set: Default::default(),
4127 render_blocking_element_count: Default::default(),
4128 deferred_scripts: Default::default(),
4129 asap_in_order_scripts_list: Default::default(),
4130 asap_scripts_set: Default::default(),
4131 animation_frame_ident: Cell::new(0),
4132 animation_frame_list: DomRefCell::new(VecDeque::new()),
4133 running_animation_callbacks: Cell::new(false),
4134 loader: DomRefCell::new(doc_loader),
4135 current_parser: Default::default(),
4136 base_element: Default::default(),
4137 target_base_element: Default::default(),
4138 ancestor_origins_list: Default::default(),
4139 internal_ancestor_origin_objects_list: Default::default(),
4140 appropriate_template_contents_owner_document: Default::default(),
4141 pending_restyles: DomRefCell::new(FxHashMap::default()),
4142 needs_restyle: Cell::new(RestyleReason::DOMChanged),
4143 origin: DomRefCell::new(origin),
4144 referrer,
4145 target_element: MutNullableDom::new(None),
4146 policy_container: DomRefCell::new(StdArc::new(PolicyContainer::default())),
4147 preloaded_resources: Default::default(),
4148 ignore_destructive_writes_counter: Default::default(),
4149 ignore_opens_during_unload_counter: Default::default(),
4150 spurious_animation_frames: Cell::new(0),
4151 fullscreen_element: MutNullableDom::new(None),
4152 form_id_listener_map: Default::default(),
4153 interactive_time: DomRefCell::new(interactive_time),
4154 tti_window: DomRefCell::new(InteractiveWindow::default()),
4155 canceller,
4156 throw_on_dynamic_markup_insertion_counter: Cell::new(0),
4157 page_showing: Cell::new(false),
4158 salvageable: Cell::new(true),
4159 active_parser_was_aborted: Cell::new(false),
4160 fired_unload: Cell::new(false),
4161 responsive_images: Default::default(),
4162 navigation_timing: Default::default(),
4163 resource_fetch_timing: RefCell::new(None),
4164 completely_loaded: Cell::new(false),
4165 script_and_layout_blockers: Cell::new(0),
4166 delayed_tasks: Default::default(),
4167 shadow_roots: Default::default(),
4168 shadow_roots_styles_changed: Cell::new(false),
4169 media_controls: DomRefCell::new(HashMap::new()),
4170 dirty_canvases: DomRefCell::new(Default::default()),
4171 has_pending_animated_image_update: Cell::new(false),
4172 selection: MutNullableDom::new(None),
4173 timeline: Dom::from_ref(timeline),
4174 animation_manager: AnimationManager::new(),
4175 dirty_root: Default::default(),
4176 declarative_refresh: Default::default(),
4177 resize_observers: Default::default(),
4178 fonts: Default::default(),
4179 visibility_state: Cell::new(DocumentVisibilityState::Visible),
4183 status_code,
4184 is_initial_about_blank: Cell::new(is_initial_about_blank),
4185 allow_declarative_shadow_roots: Cell::new(allow_declarative_shadow_roots),
4186 inherited_insecure_requests_policy: Cell::new(inherited_insecure_requests_policy),
4187 has_trustworthy_ancestor_origin: Cell::new(has_trustworthy_ancestor_origin),
4188 intersection_observer_task_queued: Cell::new(false),
4189 intersection_observers: Default::default(),
4190 highlighted_dom_node: Default::default(),
4191 lcp_candidates: DomRefCell::new(Default::default()),
4192 paint_timing_info: Cell::new(PaintTimingInfo::now()),
4193 adopted_stylesheets: Default::default(),
4194 adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
4195 pending_scroll_events: Default::default(),
4196 rendering_update_reasons: Default::default(),
4197 waiting_on_canvas_image_updates: Cell::new(false),
4198 root_removal_noted: Cell::new(true),
4199 current_rendering_epoch: Default::default(),
4200 custom_element_reaction_stack,
4201 active_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
4202 creation_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
4203 favicon: RefCell::new(None),
4204 websockets: DOMTracker::new(),
4205 details_name_groups: Default::default(),
4206 protocol_handler_automation_mode: Default::default(),
4207 layout_animations_test_enabled: pref!(layout_animations_test_enabled),
4208 state_override: Default::default(),
4209 value_override: Default::default(),
4210 default_single_line_container_name: Default::default(),
4211 css_styling_flag: Default::default(),
4212 accessibility_data: Default::default(),
4213 iframe_load_in_progress: Default::default(),
4214 mute_iframe_load: Default::default(),
4215 timers: OneshotTimers::new(window.upcast()),
4216 pipeline_id,
4217 task_manager: Rc::new(TaskManager::new(
4218 Some(window.event_loop_sender()),
4219 pipeline_id,
4220 None,
4221 )),
4222 image_cache,
4223 history: Default::default(),
4224 theme: Default::default(),
4225 theme_override: Default::default(),
4226 default_language: Default::default(),
4227 window_detached: Default::default(),
4228 live_ranges: Default::default(),
4229 module_map: Default::default(),
4230 }
4231 }
4232
4233 pub(crate) fn detach_window(&self) {
4234 self.window_detached.set(true);
4235 }
4236
4237 pub(crate) fn window_detached(&self) -> bool {
4238 self.window_detached.get()
4239 }
4240
4241 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
4243 if let Some(csp_list) = self.get_csp_list().as_ref() {
4244 for policy in &csp_list.0 {
4245 if policy.contains_a_directive_whose_name_is("upgrade-insecure-requests") &&
4246 policy.disposition == PolicyDisposition::Enforce
4247 {
4248 return InsecureRequestsPolicy::Upgrade;
4249 }
4250 }
4251 }
4252
4253 self.inherited_insecure_requests_policy
4254 .get()
4255 .unwrap_or(InsecureRequestsPolicy::DoNotUpgrade)
4256 }
4257
4258 pub(crate) fn event_handler(&self) -> &DocumentEventHandler {
4260 &self.event_handler
4261 }
4262
4263 pub(crate) fn focus_handler(&self) -> &DocumentFocusHandler {
4265 &self.focus_handler
4266 }
4267
4268 pub(crate) fn embedder_controls(&self) -> &DocumentEmbedderControls {
4270 &self.embedder_controls
4271 }
4272
4273 fn has_pending_scroll_events(&self) -> bool {
4276 !self.pending_scroll_events.borrow().is_empty()
4277 }
4278
4279 pub(crate) fn add_rendering_update_reason(&self, reason: RenderingUpdateReason) {
4282 self.rendering_update_reasons
4283 .set(self.rendering_update_reasons.get().union(reason));
4284 }
4285
4286 pub(crate) fn clear_rendering_update_reasons(&self) {
4288 self.rendering_update_reasons
4289 .set(RenderingUpdateReason::empty())
4290 }
4291
4292 pub(crate) fn add_script_and_layout_blocker(&self) {
4299 self.script_and_layout_blockers
4300 .set(self.script_and_layout_blockers.get() + 1);
4301 }
4302
4303 pub(crate) fn remove_script_and_layout_blocker(&self, cx: &mut JSContext) {
4307 assert!(self.script_and_layout_blockers.get() > 0);
4308 self.script_and_layout_blockers
4309 .set(self.script_and_layout_blockers.get() - 1);
4310 while self.script_and_layout_blockers.get() == 0 && !self.delayed_tasks.borrow().is_empty()
4311 {
4312 let task = self.delayed_tasks.borrow_mut().remove(0);
4313 task.run_box(cx);
4314 }
4315 }
4316
4317 pub(crate) fn add_delayed_task<T: 'static + NonSendTaskBox>(&self, task: T) {
4319 self.delayed_tasks.borrow_mut().push(Box::new(task));
4320 }
4321
4322 pub(crate) fn is_safe_to_run_script_or_layout(&self) -> bool {
4325 self.script_and_layout_blockers.get() == 0
4326 }
4327
4328 pub(crate) fn ensure_safe_to_run_script_or_layout(&self) {
4331 assert!(
4332 self.is_safe_to_run_script_or_layout(),
4333 "Attempt to use script or layout while DOM not in a stable state"
4334 );
4335 }
4336
4337 #[expect(clippy::too_many_arguments)]
4338 pub(crate) fn new(
4339 cx: &mut JSContext,
4340 window: &Window,
4341 has_browsing_context: HasBrowsingContext,
4342 url: Option<ServoUrl>,
4343 about_base_url: Option<ServoUrl>,
4344 origin: MutableOrigin,
4345 doctype: IsHTMLDocument,
4346 content_type: Option<Mime>,
4347 last_modified: Option<String>,
4348 activity: DocumentActivity,
4349 doc_loader: DocumentLoader,
4350 referrer: Option<String>,
4351 status_code: Option<u16>,
4352 canceller: FetchCanceller,
4353 is_initial_about_blank: bool,
4354 allow_declarative_shadow_roots: bool,
4355 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4356 has_trustworthy_ancestor_origin: bool,
4357 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4358 creation_sandboxing_flag_set: SandboxingFlagSet,
4359 pipeline_id: PipelineId,
4360 image_cache: StdArc<dyn ImageCache>,
4361 ) -> DomRoot<Document> {
4362 Self::new_with_proto(
4363 cx,
4364 window,
4365 None,
4366 has_browsing_context,
4367 url,
4368 about_base_url,
4369 origin,
4370 doctype,
4371 content_type,
4372 last_modified,
4373 activity,
4374 doc_loader,
4375 referrer,
4376 status_code,
4377 canceller,
4378 is_initial_about_blank,
4379 allow_declarative_shadow_roots,
4380 inherited_insecure_requests_policy,
4381 has_trustworthy_ancestor_origin,
4382 custom_element_reaction_stack,
4383 creation_sandboxing_flag_set,
4384 pipeline_id,
4385 image_cache,
4386 )
4387 }
4388
4389 #[expect(clippy::too_many_arguments)]
4390 fn new_with_proto(
4391 cx: &mut JSContext,
4392 window: &Window,
4393 proto: Option<HandleObject>,
4394 has_browsing_context: HasBrowsingContext,
4395 url: Option<ServoUrl>,
4396 about_base_url: Option<ServoUrl>,
4397 origin: MutableOrigin,
4398 doctype: IsHTMLDocument,
4399 content_type: Option<Mime>,
4400 last_modified: Option<String>,
4401 activity: DocumentActivity,
4402 doc_loader: DocumentLoader,
4403 referrer: Option<String>,
4404 status_code: Option<u16>,
4405 canceller: FetchCanceller,
4406 is_initial_about_blank: bool,
4407 allow_declarative_shadow_roots: bool,
4408 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4409 has_trustworthy_ancestor_origin: bool,
4410 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4411 creation_sandboxing_flag_set: SandboxingFlagSet,
4412 pipeline_id: PipelineId,
4413 image_cache: StdArc<dyn ImageCache>,
4414 ) -> DomRoot<Document> {
4415 let timeline = DocumentTimeline::new(cx, window);
4416 let document = reflect_dom_object_with_proto(
4417 cx,
4418 Box::new(Document::new_inherited(
4419 window,
4420 has_browsing_context,
4421 url,
4422 about_base_url,
4423 origin,
4424 doctype,
4425 content_type,
4426 last_modified,
4427 activity,
4428 doc_loader,
4429 referrer,
4430 status_code,
4431 canceller,
4432 is_initial_about_blank,
4433 allow_declarative_shadow_roots,
4434 inherited_insecure_requests_policy,
4435 has_trustworthy_ancestor_origin,
4436 custom_element_reaction_stack,
4437 creation_sandboxing_flag_set,
4438 &timeline,
4439 pipeline_id,
4440 image_cache,
4441 )),
4442 window,
4443 proto,
4444 );
4445 {
4446 let node = document.upcast::<Node>();
4447 node.set_owner_doc(&document);
4448 }
4449 document
4450 }
4451
4452 pub(crate) fn get_redirect_count(&self) -> u16 {
4453 self.resource_fetch_timing()
4454 .as_ref()
4455 .map_or(0, |resource_fetch_timing| {
4456 resource_fetch_timing.redirect_count
4457 })
4458 }
4459
4460 pub(crate) fn set_resource_fetch_timing(&self, timing: ResourceFetchTiming) {
4461 self.resource_fetch_timing.replace(Some(timing));
4462 }
4463
4464 pub(crate) fn resource_fetch_timing(&self) -> Ref<'_, Option<ResourceFetchTiming>> {
4465 self.resource_fetch_timing.borrow()
4466 }
4467
4468 pub(crate) fn navigation_timing(&self) -> Rc<NavigationTiming> {
4469 self.navigation_timing.clone()
4470 }
4471
4472 pub(crate) fn performance_timing_attribute(
4473 &self,
4474 name: &str,
4475 ) -> Fallible<Option<CrossProcessInstant>> {
4476 Ok(match name {
4477 "unloadEventStart" => self.navigation_timing().unload_event_start.get(),
4478 "unloadEventEnd" => self.navigation_timing().unload_event_end.get(),
4479 "domInteractive" => self.navigation_timing().dom_interactive.get(),
4480 "domContentLoadedEventStart" => self
4481 .navigation_timing()
4482 .dom_content_loaded_event_start
4483 .get(),
4484 "domContentLoadedEventEnd" => {
4485 self.navigation_timing().dom_content_loaded_event_end.get()
4486 },
4487 "domComplete" => self.navigation_timing().dom_complete.get(),
4488 "loadEventStart" => self.navigation_timing().load_event_start.get(),
4489 "loadEventEnd" => self.navigation_timing().load_event_end.get(),
4490 "redirectStart" | "redirectEnd" | "secureConnectionStart" | "responseEnd" => self
4491 .resource_fetch_timing()
4492 .as_ref()
4493 .and_then(|resource_fetch_timing| match name {
4494 "redirectStart" => resource_fetch_timing.redirect_start,
4495 "redirectEnd" => resource_fetch_timing.redirect_end,
4496 "secureConnectionStart" => resource_fetch_timing.secure_connection_start,
4497 "responseEnd" => resource_fetch_timing.response_end,
4498 _ => None,
4499 }),
4500 _ => {
4501 return Err(Error::Operation(Some(format!(
4502 "{name} hasn't been implemented."
4503 ))));
4504 },
4505 })
4506 }
4507
4508 pub(crate) fn elements_by_name_count(&self, name: &DOMString) -> u32 {
4509 if name.is_empty() {
4510 return 0;
4511 }
4512 self.count_node_list(|n| Document::is_element_in_get_by_name(n, name))
4513 }
4514
4515 pub(crate) fn nth_element_by_name<'a>(
4516 &self,
4517 no_gc: &'a NoGC,
4518 index: u32,
4519 name: &DOMString,
4520 ) -> Option<UnrootedDom<'a, Node>> {
4521 if name.is_empty() {
4522 return None;
4523 }
4524 self.nth_in_node_list(no_gc, index, |n| {
4525 Document::is_element_in_get_by_name(n, name)
4526 })
4527 }
4528
4529 fn is_element_in_get_by_name(node: &Node, name: &DOMString) -> bool {
4532 let element = match node.downcast::<Element>() {
4533 Some(element) => element,
4534 None => return false,
4535 };
4536 if element.namespace() != &ns!(html) {
4537 return false;
4538 }
4539 element.get_name().is_some_and(|n| &*n == name)
4540 }
4541
4542 fn count_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> u32 {
4543 let doc = self.GetDocumentElement();
4544 let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
4545 maybe_node
4546 .iter()
4547 .flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
4548 .filter(|node| callback(node))
4549 .count() as u32
4550 }
4551
4552 fn nth_in_node_list<'a, F: Fn(&Node) -> bool>(
4553 &self,
4554 no_gc: &'a NoGC,
4555 index: u32,
4556 callback: F,
4557 ) -> Option<UnrootedDom<'a, Node>> {
4558 let doc = self.get_document_element_unrooted(no_gc)?;
4559 doc.upcast::<Node>()
4560 .traverse_preorder_unrooted(no_gc, ShadowIncluding::No)
4561 .filter(|node| callback(node))
4562 .nth(index as usize)
4563 }
4564
4565 fn get_html_element(&self) -> Option<DomRoot<HTMLHtmlElement>> {
4566 self.GetDocumentElement().and_then(DomRoot::downcast)
4567 }
4568
4569 pub(crate) fn shared_style_locks(&self) -> &SharedRwLocks {
4571 &self.shared_style_locks
4572 }
4573
4574 pub(crate) fn style_shared_author_lock(&self) -> &SharedRwLock {
4576 &self.shared_style_locks.author
4577 }
4578
4579 pub(crate) fn flush_stylesheets_for_reflow(&self) -> bool {
4581 let mut stylesheets = self.stylesheets.borrow_mut();
4588 let have_changed = stylesheets.has_changed();
4589 stylesheets.flush_without_invalidation();
4590 have_changed
4591 }
4592
4593 pub(crate) fn salvageable(&self) -> bool {
4594 self.salvageable.get()
4595 }
4596
4597 pub(crate) fn make_document_unsalvageable(&self) {
4599 self.salvageable.set(false);
4605 }
4606
4607 pub(crate) fn appropriate_template_contents_owner_document(
4609 &self,
4610 cx: &mut JSContext,
4611 ) -> DomRoot<Document> {
4612 self.appropriate_template_contents_owner_document
4613 .or_init(|| {
4614 let doctype = if self.is_html_document {
4615 IsHTMLDocument::HTMLDocument
4616 } else {
4617 IsHTMLDocument::NonHTMLDocument
4618 };
4619 let new_doc = Document::new(
4620 cx,
4621 self.window(),
4622 HasBrowsingContext::No,
4623 None,
4624 None,
4625 MutableOrigin::new(ImmutableOrigin::new_opaque()),
4627 doctype,
4628 None,
4629 None,
4630 DocumentActivity::Inactive,
4631 DocumentLoader::new(&self.loader()),
4632 None,
4633 None,
4634 Default::default(),
4635 false,
4636 self.allow_declarative_shadow_roots(),
4637 Some(self.insecure_requests_policy()),
4638 self.has_trustworthy_ancestor_or_current_origin(),
4639 self.custom_element_reaction_stack.clone(),
4640 self.creation_sandboxing_flag_set(),
4641 self.pipeline_id(),
4642 self.image_cache.clone(),
4643 );
4644 new_doc
4645 .appropriate_template_contents_owner_document
4646 .set(Some(&new_doc));
4647 new_doc
4648 })
4649 }
4650
4651 pub(crate) fn get_element_by_id(&self, no_gc: &NoGC, id: &Atom) -> Option<DomRoot<Element>> {
4652 self.id_map.get(no_gc, self.upcast(), id)
4653 }
4654
4655 pub(crate) fn ensure_pending_restyle(&self, el: &Element) -> RefMut<'_, PendingRestyle> {
4656 let map = self.pending_restyles.borrow_mut();
4657 RefMut::map(map, |m| {
4658 &mut m
4659 .entry(Dom::from_ref(el))
4660 .or_insert_with(|| NoTrace(PendingRestyle::default()))
4661 .0
4662 })
4663 }
4664
4665 pub(crate) fn element_attr_will_change(&self, el: &Element, attr: AttrRef<'_>) {
4666 let mut entry = self.ensure_pending_restyle(el);
4672 if entry.snapshot.is_none() {
4673 entry.snapshot = Some(Snapshot::new());
4674 }
4675 if attr.local_name() == &local_name!("style") {
4676 entry.hint.insert(RestyleHint::RESTYLE_STYLE_ATTRIBUTE);
4677 }
4678
4679 if vtable_for(el.upcast()).attribute_affects_presentational_hints(attr) ||
4680 el.check_style_on_self_or_eager_pseudos(|style| {
4681 if let Some(ref attribute_references) = style.attribute_references {
4682 return attribute_references.contains_key(attr.local_name());
4683 }
4684 false
4685 })
4686 {
4687 entry.hint.insert(RestyleHint::RESTYLE_SELF);
4688 }
4689
4690 let snapshot = entry.snapshot.as_mut().unwrap();
4691 if attr.local_name() == &local_name!("id") {
4692 if snapshot.id_changed {
4693 return;
4694 }
4695 snapshot.id_changed = true;
4696 } else if attr.local_name() == &local_name!("class") {
4697 if snapshot.class_changed {
4698 return;
4699 }
4700 snapshot.class_changed = true;
4701 } else {
4702 snapshot.other_attributes_changed = true;
4703 }
4704 let local_name = style::LocalName::cast(attr.local_name());
4705 if !snapshot.changed_attrs.contains(local_name) {
4706 snapshot.changed_attrs.push(local_name.clone());
4707 }
4708 if snapshot.attrs.is_none() {
4709 let attrs = el
4710 .attrs()
4711 .borrow()
4712 .iter()
4713 .map(|attr| (attr.as_identifier(), attr.value().clone()))
4714 .collect();
4715 snapshot.attrs = Some(attrs);
4716 }
4717 }
4718
4719 pub(crate) fn set_referrer_policy(&self, policy: ReferrerPolicy) {
4720 StdArc::make_mut(&mut *self.policy_container.borrow_mut()).set_referrer_policy(policy);
4721 }
4722
4723 pub(crate) fn get_referrer_policy(&self) -> ReferrerPolicy {
4724 self.policy_container.borrow().get_referrer_policy()
4725 }
4726
4727 pub(crate) fn set_target_element(&self, node: Option<&Element>) {
4728 if let Some(ref element) = self.target_element.get() {
4729 element.set_target_state(false);
4730 }
4731
4732 self.target_element.set(node);
4733
4734 if let Some(ref element) = self.target_element.get() {
4735 element.set_target_state(true);
4736 }
4737 }
4738
4739 pub(crate) fn incr_ignore_destructive_writes_counter(&self) {
4740 self.ignore_destructive_writes_counter
4741 .set(self.ignore_destructive_writes_counter.get() + 1);
4742 }
4743
4744 pub(crate) fn decr_ignore_destructive_writes_counter(&self) {
4745 self.ignore_destructive_writes_counter
4746 .set(self.ignore_destructive_writes_counter.get() - 1);
4747 }
4748
4749 pub(crate) fn is_prompting_or_unloading(&self) -> bool {
4750 self.ignore_opens_during_unload_counter.get() > 0
4751 }
4752
4753 fn incr_ignore_opens_during_unload_counter(&self) {
4754 self.ignore_opens_during_unload_counter
4755 .set(self.ignore_opens_during_unload_counter.get() + 1);
4756 }
4757
4758 fn decr_ignore_opens_during_unload_counter(&self) {
4759 self.ignore_opens_during_unload_counter
4760 .set(self.ignore_opens_during_unload_counter.get() - 1);
4761 }
4762
4763 pub(crate) fn set_fullscreen_element(&self, element: Option<&Element>) {
4764 self.fullscreen_element.set(element);
4765 }
4766
4767 fn reset_form_owner_for_listeners(&self, cx: &mut JSContext, id: &Atom) {
4768 let map = self.form_id_listener_map.borrow();
4769 if let Some(listeners) = map.get(id) {
4770 for listener in listeners {
4771 listener
4772 .as_maybe_form_control()
4773 .expect("Element must be a form control")
4774 .reset_form_owner(cx);
4775 }
4776 }
4777 }
4778
4779 pub(crate) fn register_shadow_root(&self, shadow_root: &ShadowRoot) {
4780 self.shadow_roots
4781 .borrow_mut()
4782 .insert(Dom::from_ref(shadow_root));
4783 self.invalidate_shadow_roots_stylesheets();
4784 }
4785
4786 pub(crate) fn unregister_shadow_root(&self, shadow_root: &ShadowRoot) {
4787 let mut shadow_roots = self.shadow_roots.borrow_mut();
4788 shadow_roots.remove(&Dom::from_ref(shadow_root));
4789 }
4790
4791 pub(crate) fn invalidate_shadow_roots_stylesheets(&self) {
4792 self.shadow_roots_styles_changed.set(true);
4793 }
4794
4795 pub(crate) fn flush_shadow_root_stylesheets_if_necessary_for_layout(
4796 &self,
4797 stylist: &mut Stylist,
4798 guard: &SharedRwLockReadGuard,
4799 ) {
4800 if !self.shadow_roots_styles_changed.get() {
4801 return;
4802 }
4803 #[expect(unsafe_code)]
4804 unsafe {
4805 for shadow_root in self.shadow_roots.borrow_for_layout().iter() {
4806 let layout: LayoutDom<'_, _> = shadow_root.to_layout();
4807 layout.flush_stylesheets_for_layout(stylist, guard);
4808 }
4809 }
4810 self.shadow_roots_styles_changed.set(false);
4811 }
4812
4813 pub(crate) fn stylesheet_count(&self) -> usize {
4814 self.stylesheets.borrow().len()
4815 }
4816
4817 pub(crate) fn stylesheet_at(
4818 &self,
4819 cx: &mut JSContext,
4820 index: usize,
4821 ) -> Option<DomRoot<CSSStyleSheet>> {
4822 let stylesheets = self.stylesheets.borrow();
4823
4824 stylesheets
4825 .get(Origin::Author, index)
4826 .and_then(|s| s.owner.get_cssom_object(cx))
4827 }
4828
4829 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(
4836 &self,
4837 no_gc: &NoGC,
4838 owner_node: &Element,
4839 sheet: Arc<Stylesheet>,
4840 ) {
4841 let insertion_point = {
4842 let stylesheets = &mut *self.stylesheets.borrow_mut();
4843
4844 stylesheets
4846 .iter()
4847 .map(|(sheet, _origin)| sheet)
4848 .find(|sheet_in_doc| {
4849 match &sheet_in_doc.owner {
4850 StylesheetSource::Element(other_node) => owner_node
4851 .upcast::<Node>()
4852 .is_before(no_gc, other_node.upcast()),
4853 StylesheetSource::Constructed(_) => true,
4856 }
4857 })
4858 .cloned()
4859 };
4860
4861 if self.has_browsing_context() {
4862 self.add_stylesheet_to_stylist(
4863 sheet.clone(),
4864 insertion_point.as_ref().map(|s| s.sheet.clone()),
4865 );
4866 }
4867
4868 let stylesheets = &mut *self.stylesheets.borrow_mut();
4869 DocumentOrShadowRoot::add_stylesheet(
4870 StylesheetSource::Element(Dom::from_ref(owner_node)),
4871 StylesheetSetRef::Document(stylesheets),
4872 sheet,
4873 insertion_point,
4874 self.style_shared_author_lock(),
4875 );
4876 }
4877
4878 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
4883 pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
4884 debug_assert!(cssom_stylesheet.is_constructed());
4885
4886 let sheet = cssom_stylesheet.style_stylesheet().clone();
4887 let insertion_point = {
4888 let stylesheets = &mut *self.stylesheets.borrow_mut();
4889
4890 stylesheets
4891 .iter()
4892 .last()
4893 .map(|(sheet, _origin)| sheet)
4894 .cloned()
4895 };
4896
4897 if self.has_browsing_context() {
4898 self.add_stylesheet_to_stylist(
4899 sheet.clone(),
4900 insertion_point.as_ref().map(|s| s.sheet.clone()),
4901 );
4902 }
4903
4904 let stylesheets = &mut *self.stylesheets.borrow_mut();
4905 DocumentOrShadowRoot::add_stylesheet(
4906 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
4907 StylesheetSetRef::Document(stylesheets),
4908 sheet,
4909 insertion_point,
4910 self.style_shared_author_lock(),
4911 );
4912 }
4913
4914 pub(crate) fn add_stylesheet_to_stylist(
4915 &self,
4916 stylesheet: Arc<Stylesheet>,
4917 before_stylesheet: Option<Arc<Stylesheet>>,
4918 ) {
4919 self.window
4920 .layout_mut()
4921 .add_stylesheet(stylesheet, before_stylesheet);
4922 }
4923
4924 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, stylesheet: &Arc<Stylesheet>) {
4927 if self.has_browsing_context() {
4928 self.window
4929 .layout_mut()
4930 .remove_stylesheet(stylesheet.clone());
4931 }
4932
4933 DocumentOrShadowRoot::remove_stylesheet(
4934 owner,
4935 stylesheet,
4936 StylesheetSetRef::Document(&mut *self.stylesheets.borrow_mut()),
4937 )
4938 }
4939
4940 pub(crate) fn get_elements_with_id(
4941 &self,
4942 cx: &mut JSContext,
4943 id: &Atom,
4944 ) -> Ref<'_, [Dom<Element>]> {
4945 self.id_map.get_all(cx.no_gc(), self.upcast(), id)
4946 }
4947
4948 pub(crate) fn get_elements_with_name(
4949 &self,
4950 cx: &mut JSContext,
4951 name: &Atom,
4952 ) -> Ref<'_, [Dom<Element>]> {
4953 self.name_map.get_all(cx.no_gc(), self.upcast(), name)
4954 }
4955
4956 pub(crate) fn drain_pending_restyles(
4957 &self,
4958 no_gc: &NoGC,
4959 ) -> Vec<(TrustedNodeAddress, PendingRestyle)> {
4960 self.pending_restyles
4961 .borrow_mut()
4962 .drain()
4963 .filter_map(|(element, restyle)| {
4964 let node = element.upcast::<Node>();
4965 if !node.get_flag(NodeFlags::IS_CONNECTED) {
4966 return None;
4967 }
4968 element.note_dirty_descendants(no_gc);
4969 Some((node.to_trusted_node_address(), restyle.0))
4970 })
4971 .collect()
4972 }
4973
4974 pub(crate) fn advance_animation_timeline_for_testing(&self, delta: TimeDuration) {
4975 self.timeline.advance_specific(delta);
4976 let current_timeline_value = self.current_animation_timeline_value();
4977 self.animation_manager
4978 .update_for_new_timeline_value(&self.window, current_timeline_value);
4979 }
4980
4981 pub(crate) fn maybe_mark_animating_nodes_as_dirty(&self, no_gc: &NoGC) {
4982 let current_timeline_value = self.current_animation_timeline_value();
4983 self.animation_manager
4984 .mark_animating_nodes_as_dirty(no_gc, current_timeline_value);
4985 }
4986
4987 pub(crate) fn current_animation_timeline_value(&self) -> f64 {
4988 self.timeline
4989 .upcast::<AnimationTimeline>()
4990 .current_time_in_seconds()
4991 }
4992
4993 pub(crate) fn animation_manager(&self) -> &AnimationManager {
4994 &self.animation_manager
4995 }
4996
4997 pub(crate) fn update_animations_post_reflow(&self) {
4998 let current_timeline_value = self.current_animation_timeline_value();
4999 self.animation_manager
5000 .do_post_reflow_update(&self.window, current_timeline_value);
5001 }
5002
5003 pub(crate) fn cancel_animations_for_node(&self, node: &Node) {
5004 self.animation_manager.cancel_animations_for_node(node);
5005 }
5006
5007 pub(crate) fn remove_style_and_layout_data_from_subtree(
5011 &self,
5012 no_gc: &NoGC,
5013 subtree_root: &Node,
5014 ) {
5015 for node in subtree_root.traverse_preorder_unrooted(no_gc, ShadowIncluding::Yes) {
5016 self.clean_up_style_and_layout_data_for_node(&node);
5017 }
5018 }
5019
5020 pub(crate) fn clean_up_style_and_layout_data_for_node(&self, node: &Node) {
5021 node.clear_layout_data();
5022 if let Some(element) = node.downcast::<Element>() {
5023 element.clean_up_style_data();
5024
5025 if self.dirty_root == Some(element) {
5030 self.dirty_root.clear();
5031 }
5032 }
5033
5034 node.set_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION, false);
5035 node.set_flag(NodeFlags::SELECTION_INHIBITED, false);
5036 node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false);
5037 }
5038
5039 pub(crate) fn update_animations_and_send_events(&self, cx: &mut CurrentRealm) {
5041 if !self.layout_animations_test_enabled {
5043 self.timeline.update(self.window());
5044 }
5045
5046 let current_timeline_value = self.current_animation_timeline_value();
5053 self.animation_manager
5054 .update_for_new_timeline_value(&self.window, current_timeline_value);
5055 self.maybe_mark_animating_nodes_as_dirty(cx.no_gc());
5056
5057 self.window().perform_a_microtask_checkpoint(cx);
5059
5060 self.animation_manager()
5062 .send_pending_events(self.window(), cx);
5063 }
5064
5065 pub(crate) fn set_has_pending_animated_image_update(&self) {
5066 self.has_pending_animated_image_update.set(true);
5067 }
5068
5069 pub(crate) fn shared_declarative_refresh_steps(&self, content: &[u8], from_meta_element: bool) {
5071 if self.will_declaratively_refresh() {
5073 return;
5074 }
5075
5076 static REFRESH_REGEX: LazyLock<Regex> = LazyLock::new(|| {
5078 Regex::new(
5082 r#"(?xs)
5083 ^
5084 \s* # 3
5085 ((?<time>[0-9]+)|\.) # 5-6
5086 [0-9.]* # 8
5087 (
5088 (
5089 (\s*;|\s*,|\s) # 10.3
5090 \s* # 10.4
5091 )
5092 (
5093 (
5094 (U|u)(R|r)(L|l) # 11.2-11.4
5095 \s*=\s* # 11.5-11.7
5096 )?
5097 ('(?<url1>[^']*)'(?s-u:.)*|"(?<url2>[^"]*)"(?s-u:.)*|['"]?(?<url3>(?s-u:.)*)) # 11.8 - 11.10
5098 |
5099 (?<url4>(?s-u:.)*)
5100 )
5101 )?
5102 $
5103 "#,
5104 )
5105 .unwrap()
5106 });
5107
5108 let mut url_record = self.url();
5110 let captures = if let Some(captures) = REFRESH_REGEX.captures(content) {
5111 captures
5112 } else {
5113 return;
5114 };
5115 let time = if let Some(time_string) = captures.name("time") {
5116 u64::from_str(&String::from_utf8_lossy(time_string.as_bytes())).unwrap_or(0)
5117 } else {
5118 0
5119 };
5120 let captured_url = captures.name("url1").or(captures
5121 .name("url2")
5122 .or(captures.name("url3").or(captures.name("url4"))));
5123
5124 if let Some(url_match) = captured_url {
5126 url_record = if let Ok(url) = ServoUrl::parse_with_base(
5127 Some(&url_record),
5128 &String::from_utf8_lossy(url_match.as_bytes()),
5129 ) {
5130 info!("Refresh to {}", url.debug_compact());
5131 url
5132 } else {
5133 return;
5135 };
5136 if url_record.scheme() == "javascript" {
5138 return;
5139 }
5140 }
5141 if self.completely_loaded() {
5143 self.window.as_global_scope().schedule_callback(
5144 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
5145 url: url_record,
5146 from_meta_element,
5147 }),
5148 Duration::from_secs(time),
5149 );
5150 self.set_declarative_refresh(DeclarativeRefresh::CreatedAfterLoad);
5151 } else {
5152 self.set_declarative_refresh(DeclarativeRefresh::PendingLoad {
5153 url: url_record,
5154 time,
5155 from_meta_element,
5156 });
5157 }
5158 }
5159
5160 pub(crate) fn will_declaratively_refresh(&self) -> bool {
5161 self.declarative_refresh.borrow().is_some()
5162 }
5163 pub(crate) fn set_declarative_refresh(&self, refresh: DeclarativeRefresh) {
5164 *self.declarative_refresh.borrow_mut() = Some(refresh);
5165 }
5166
5167 fn update_visibility_state(
5169 &self,
5170 cx: &mut JSContext,
5171 visibility_state: DocumentVisibilityState,
5172 ) {
5173 if self.visibility_state.get() == visibility_state {
5175 return;
5176 }
5177 self.visibility_state.set(visibility_state);
5179 let entry = VisibilityStateEntry::new(
5182 cx,
5183 &self.global(),
5184 visibility_state,
5185 CrossProcessInstant::now(),
5186 );
5187 self.window
5188 .Performance(cx)
5189 .queue_entry(entry.upcast::<PerformanceEntry>());
5190
5191 #[cfg(feature = "gamepad")]
5202 if visibility_state == DocumentVisibilityState::Hidden {
5203 self.window
5204 .Navigator(cx)
5205 .GetGamepads(cx)
5206 .unwrap_or_default()
5207 .iter_mut()
5208 .for_each(|gamepad| {
5209 if let Some(g) = gamepad {
5210 g.vibration_actuator().handle_visibility_change();
5211 }
5212 });
5213 }
5214
5215 self.upcast::<EventTarget>()
5217 .fire_bubbling_event(cx, atom!("visibilitychange"));
5218 }
5219
5220 pub(crate) fn is_initial_about_blank(&self) -> bool {
5222 self.is_initial_about_blank.get()
5223 }
5224
5225 pub(crate) fn allow_declarative_shadow_roots(&self) -> bool {
5227 self.allow_declarative_shadow_roots.get()
5228 }
5229
5230 pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
5231 self.has_trustworthy_ancestor_origin.get()
5232 }
5233
5234 pub(crate) fn has_trustworthy_ancestor_or_current_origin(&self) -> bool {
5235 self.has_trustworthy_ancestor_origin.get() ||
5236 self.origin().immutable().is_potentially_trustworthy()
5237 }
5238
5239 pub(crate) fn highlight_dom_node(&self, node: Option<&Node>) {
5240 self.highlighted_dom_node.set(node);
5241 self.add_restyle_reason(RestyleReason::HighlightedDOMNodeChanged);
5242 }
5243
5244 pub(crate) fn highlighted_dom_node(&self) -> Option<DomRoot<Node>> {
5245 self.highlighted_dom_node.get()
5246 }
5247
5248 pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
5249 self.custom_element_reaction_stack.clone()
5250 }
5251
5252 pub(crate) fn active_sandboxing_flag_set(&self) -> SandboxingFlagSet {
5253 self.active_sandboxing_flag_set.get()
5254 }
5255
5256 pub(crate) fn has_active_sandboxing_flag(&self, flag: SandboxingFlagSet) -> bool {
5257 self.active_sandboxing_flag_set.get().contains(flag)
5258 }
5259
5260 pub(crate) fn set_active_sandboxing_flag_set(&self, flags: SandboxingFlagSet) {
5261 self.active_sandboxing_flag_set.set(flags)
5262 }
5263
5264 pub(crate) fn creation_sandboxing_flag_set(&self) -> SandboxingFlagSet {
5265 self.creation_sandboxing_flag_set.get()
5266 }
5267
5268 pub(crate) fn creation_sandboxing_flag_set_considering_parent_iframe(
5269 &self,
5270 ) -> SandboxingFlagSet {
5271 self.window()
5272 .window_proxy()
5273 .frame_element()
5274 .and_then(|element| element.downcast::<HTMLIFrameElement>())
5275 .map(HTMLIFrameElement::sandboxing_flag_set)
5276 .unwrap_or_else(|| self.creation_sandboxing_flag_set())
5277 }
5278
5279 pub(crate) fn viewport_scrolling_box(&self, flags: ScrollContainerQueryFlags) -> ScrollingBox {
5280 self.window()
5281 .scrolling_box_query(None, flags)
5282 .expect("We should always have a ScrollingBox for the Viewport")
5283 }
5284
5285 pub(crate) fn notify_embedder_favicon(&self) {
5286 if let Some(ref image) = *self.favicon.borrow() {
5287 self.send_to_embedder(EmbedderMsg::NewFavicon(self.webview_id(), image.clone()));
5288 }
5289 }
5290
5291 pub(crate) fn set_favicon(&self, favicon: Image) {
5292 *self.favicon.borrow_mut() = Some(favicon);
5293 self.notify_embedder_favicon();
5294 }
5295
5296 pub(crate) fn fullscreen_element(&self) -> Option<DomRoot<Element>> {
5297 self.fullscreen_element.get()
5298 }
5299
5300 pub(crate) fn state_override(&self, command_name: &CommandName) -> Option<bool> {
5302 self.state_override.borrow().get(command_name).copied()
5303 }
5304
5305 pub(crate) fn set_state_override(&self, command_name: CommandName, state: Option<bool>) {
5307 if let Some(state) = state {
5308 self.state_override.borrow_mut().insert(command_name, state);
5309 } else {
5310 self.value_override.borrow_mut().remove(&command_name);
5311 }
5312 }
5313
5314 pub(crate) fn value_override(&self, command_name: &CommandName) -> Option<DOMString> {
5316 self.value_override.borrow().get(command_name).cloned()
5317 }
5318
5319 pub(crate) fn set_value_override(&self, command_name: CommandName, value: Option<DOMString>) {
5321 if let Some(value) = value {
5322 self.value_override.borrow_mut().insert(command_name, value);
5323 } else {
5324 self.value_override.borrow_mut().remove(&command_name);
5325 }
5326 }
5327
5328 pub(crate) fn clear_command_overrides(&self) {
5331 self.state_override.borrow_mut().clear();
5332 self.value_override.borrow_mut().clear();
5333 }
5334
5335 pub(crate) fn default_single_line_container_name(&self) -> DefaultSingleLineContainerName {
5337 self.default_single_line_container_name.get()
5338 }
5339
5340 pub(crate) fn set_default_single_line_container_name(
5342 &self,
5343 value: DefaultSingleLineContainerName,
5344 ) {
5345 self.default_single_line_container_name.set(value)
5346 }
5347
5348 pub(crate) fn css_styling_flag(&self) -> bool {
5350 self.css_styling_flag.get()
5351 }
5352
5353 pub(crate) fn set_css_styling_flag(&self, value: bool) {
5355 self.css_styling_flag.set(value)
5356 }
5357
5358 pub(crate) fn mute_iframe_load_flag(&self) -> bool {
5359 self.mute_iframe_load.get()
5360 }
5361
5362 pub(crate) fn set_iframe_load_in_progress(&self, value: bool) {
5363 self.iframe_load_in_progress.set(value)
5364 }
5365
5366 pub(crate) fn theme(&self) -> Option<Theme> {
5367 self.theme.get()
5368 }
5369
5370 pub(crate) fn set_theme(&self, new_theme: Option<Theme>) {
5371 self.theme.set(new_theme);
5372 self.window.refresh_theme();
5373 }
5374
5375 pub(crate) fn theme_override(&self) -> Option<Theme> {
5376 self.theme_override.get()
5377 }
5378
5379 pub(crate) fn set_theme_override(&self, new_theme: Option<Theme>) {
5380 self.theme_override.set(new_theme);
5381 self.window.refresh_theme();
5382 }
5383
5384 pub(crate) fn default_language(&self) -> Option<String> {
5385 self.default_language.borrow().clone()
5386 }
5387
5388 pub(crate) fn set_default_language(&self, new_language: Option<String>) {
5389 *self.default_language.borrow_mut() = new_language;
5390 }
5391
5392 pub(crate) fn create_element(&self, cx: &mut JSContext, name: &str) -> DomRoot<Element> {
5393 let element_options =
5394 StringOrElementCreationOptions::ElementCreationOptions(ElementCreationOptions {
5395 is: None,
5396 });
5397 self.CreateElement(cx, name.into(), element_options)
5398 .expect("Must always be able to create element")
5399 }
5400}
5401
5402impl DocumentMethods<crate::DomTypeHolder> for Document {
5403 fn Constructor(
5405 cx: &mut JSContext,
5406 window: &Window,
5407 proto: Option<HandleObject>,
5408 ) -> Fallible<DomRoot<Document>> {
5409 let doc = window.Document();
5411 let docloader = DocumentLoader::new(&doc.loader());
5412 Ok(Document::new_with_proto(
5413 cx,
5414 window,
5415 proto,
5416 HasBrowsingContext::No,
5417 None,
5418 None,
5419 doc.origin().clone(),
5420 IsHTMLDocument::NonHTMLDocument,
5421 None,
5422 None,
5423 DocumentActivity::Inactive,
5424 docloader,
5425 None,
5426 None,
5427 Default::default(),
5428 false,
5429 doc.allow_declarative_shadow_roots(),
5430 Some(doc.insecure_requests_policy()),
5431 doc.has_trustworthy_ancestor_or_current_origin(),
5432 doc.custom_element_reaction_stack(),
5433 doc.active_sandboxing_flag_set.get(),
5434 doc.pipeline_id(),
5435 doc.image_cache(),
5436 ))
5437 }
5438
5439 fn ParseHTMLUnsafe(
5441 cx: &mut JSContext,
5442 window: &Window,
5443 s: TrustedHTMLOrString,
5444 options: &SetHTMLUnsafeOptions,
5445 ) -> Fallible<DomRoot<Self>> {
5446 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
5450 cx,
5451 window.as_global_scope(),
5452 s,
5453 "Document parseHTMLUnsafe",
5454 )?;
5455
5456 let url = window.get_url();
5457 let doc = window.Document();
5458 let loader = DocumentLoader::new(&doc.loader());
5459
5460 let content_type = "text/html"
5461 .parse()
5462 .expect("Supported type is not a MIME type");
5463 let document = Document::new(
5466 cx,
5467 window,
5468 HasBrowsingContext::No,
5469 Some(ServoUrl::parse("about:blank").unwrap()),
5470 None,
5471 doc.origin().clone(),
5472 IsHTMLDocument::HTMLDocument,
5473 Some(content_type),
5474 None,
5475 DocumentActivity::Inactive,
5476 loader,
5477 None,
5478 None,
5479 Default::default(),
5480 false,
5481 true,
5482 Some(doc.insecure_requests_policy()),
5483 doc.has_trustworthy_ancestor_or_current_origin(),
5484 doc.custom_element_reaction_stack(),
5485 doc.creation_sandboxing_flag_set(),
5486 doc.pipeline_id(),
5487 doc.image_cache(),
5488 );
5489 ServoParser::parse_html_document(cx, &document, Some(compliant_html), url, None, None);
5491
5492 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, false)?;
5495
5496 sanitizer.sanitize(cx, document.upcast(), false)?;
5498
5499 document.update_the_current_document_readiness(cx, DocumentReadyState::Complete);
5501 Ok(document)
5502 }
5503
5504 fn ParseHTML(
5506 cx: &mut JSContext,
5507 window: &Window,
5508 html: DOMString,
5509 options: &SetHTMLOptions,
5510 ) -> Fallible<DomRoot<Document>> {
5511 let url = window.get_url();
5514 let doc = window.Document();
5515 let loader = DocumentLoader::new(&doc.loader());
5516 let content_type = "text/html"
5517 .parse()
5518 .expect("Supported type is not a MIME type");
5519 let document = Document::new(
5520 cx,
5521 window,
5522 HasBrowsingContext::No,
5523 Some(ServoUrl::parse("about:blank").unwrap()),
5524 None,
5525 doc.origin().clone(),
5526 IsHTMLDocument::HTMLDocument,
5527 Some(content_type),
5528 None,
5529 DocumentActivity::Inactive,
5530 loader,
5531 None,
5532 None,
5533 Default::default(),
5534 false,
5535 true,
5536 Some(doc.insecure_requests_policy()),
5537 doc.has_trustworthy_ancestor_or_current_origin(),
5538 doc.custom_element_reaction_stack(),
5539 doc.creation_sandboxing_flag_set(),
5540 doc.pipeline_id(),
5541 doc.image_cache(),
5542 );
5543
5544 ServoParser::parse_html_document(cx, &document, Some(html), url, None, None);
5546
5547 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, true)?;
5550
5551 sanitizer.sanitize(cx, document.upcast(), true)?;
5553
5554 Ok(document)
5556 }
5557
5558 fn StyleSheets(&self, cx: &mut JSContext) -> DomRoot<StyleSheetList> {
5560 self.stylesheet_list.or_init(|| {
5561 StyleSheetList::new(
5562 cx,
5563 &self.window,
5564 StyleSheetListOwner::Document(Dom::from_ref(self)),
5565 )
5566 })
5567 }
5568
5569 fn Implementation(&self, cx: &mut JSContext) -> DomRoot<DOMImplementation> {
5571 self.implementation
5572 .or_init(|| DOMImplementation::new(cx, self))
5573 }
5574
5575 fn URL(&self) -> USVString {
5577 USVString(String::from(self.url().as_str()))
5578 }
5579
5580 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
5582 self.document_or_shadow_root.active_element(self.upcast())
5583 }
5584
5585 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
5587 self.custom_element_registry()
5588 }
5589
5590 fn HasFocus(&self) -> bool {
5592 if !self.window().webview_state().has_system_focus.get() {
5600 return false;
5601 }
5602
5603 if self.window().parent_info().is_none() {
5617 self.is_fully_active()
5619 } else {
5620 self.is_fully_active() && self.focus_handler.has_focus()
5622 }
5623 }
5624
5625 fn Domain(&self) -> DOMString {
5627 match self.origin().effective_domain() {
5629 None => DOMString::new(),
5631 Some(Host::Domain(domain)) => DOMString::from(domain),
5633 Some(host) => DOMString::from(host.to_string()),
5634 }
5635 }
5636
5637 fn SetDomain(&self, value: DOMString) -> ErrorResult {
5639 if !self.has_browsing_context {
5641 return Err(Error::Security(Some(
5642 "Document has no browsing context".into(),
5643 )));
5644 }
5645
5646 if self.has_active_sandboxing_flag(
5649 SandboxingFlagSet::SANDBOXED_DOCUMENT_DOMAIN_BROWSING_CONTEXT_FLAG,
5650 ) {
5651 return Err(Error::Security(Some(
5652 "Sandboxed document cannot set its domain".into(),
5653 )));
5654 }
5655
5656 let effective_domain = match self.origin().effective_domain() {
5658 Some(effective_domain) => effective_domain,
5659 None => return Err(Error::Security(Some("Document's origin is opaque".into()))),
5661 };
5662
5663 let host =
5665 match get_registrable_domain_suffix_of_or_is_equal_to(&value.str(), effective_domain) {
5666 None => return Err(Error::Security(Some("Provided domain is not a registrable domain suffix and is not equal to document's effective domain".into()))),
5667 Some(host) => host,
5668 };
5669
5670 self.origin().set_domain(host);
5675
5676 Ok(())
5677 }
5678
5679 fn Referrer(&self) -> DOMString {
5681 match self.referrer {
5682 Some(ref referrer) => DOMString::from(referrer.to_string()),
5683 None => DOMString::new(),
5684 }
5685 }
5686
5687 fn DocumentURI(&self) -> USVString {
5689 self.URL()
5690 }
5691
5692 fn CompatMode(&self) -> DOMString {
5694 DOMString::from(match self.quirks_mode.get() {
5695 QuirksMode::LimitedQuirks | QuirksMode::NoQuirks => "CSS1Compat",
5696 QuirksMode::Quirks => "BackCompat",
5697 })
5698 }
5699
5700 fn CharacterSet(&self) -> DOMString {
5702 DOMString::from_static(self.encoding.get().name())
5703 }
5704
5705 fn Charset(&self) -> DOMString {
5707 self.CharacterSet()
5708 }
5709
5710 fn InputEncoding(&self) -> DOMString {
5712 self.CharacterSet()
5713 }
5714
5715 fn ContentType(&self) -> DOMString {
5717 DOMString::from(self.content_type.to_string())
5718 }
5719
5720 fn GetDoctype(&self) -> Option<DomRoot<DocumentType>> {
5722 self.upcast::<Node>().children().find_map(DomRoot::downcast)
5723 }
5724
5725 fn GetDocumentElement(&self) -> Option<DomRoot<Element>> {
5727 self.upcast::<Node>().child_elements().next()
5728 }
5729
5730 fn GetElementsByTagName(
5732 &self,
5733 cx: &mut JSContext,
5734 qualified_name: DOMString,
5735 ) -> DomRoot<HTMLCollection> {
5736 let qualified_name = LocalName::from(qualified_name);
5737 if let Some(entry) = self.tag_map.borrow_mut().get(&qualified_name) {
5738 return DomRoot::from_ref(entry);
5739 }
5740 let result = HTMLCollection::by_qualified_name(
5741 cx,
5742 &self.window,
5743 self.upcast(),
5744 qualified_name.clone(),
5745 );
5746 self.tag_map
5747 .borrow_mut()
5748 .insert(qualified_name, Dom::from_ref(&*result));
5749 result
5750 }
5751
5752 fn GetElementsByTagNameNS(
5754 &self,
5755 cx: &mut JSContext,
5756 maybe_ns: Option<DOMString>,
5757 tag_name: DOMString,
5758 ) -> DomRoot<HTMLCollection> {
5759 let ns = namespace_from_domstring(maybe_ns);
5760 let local = LocalName::from(tag_name);
5761 let qname = QualName::new(None, ns, local);
5762 if let Some(collection) = self.tagns_map.borrow().get(&qname) {
5763 return DomRoot::from_ref(collection);
5764 }
5765 let result =
5766 HTMLCollection::by_qual_tag_name(cx, &self.window, self.upcast(), qname.clone());
5767 self.tagns_map
5768 .borrow_mut()
5769 .insert(qname, Dom::from_ref(&*result));
5770 result
5771 }
5772
5773 fn GetElementsByClassName(
5775 &self,
5776 cx: &mut JSContext,
5777 classes: DOMString,
5778 ) -> DomRoot<HTMLCollection> {
5779 let class_atoms: Vec<Atom> = split_html_space_chars(&classes.str())
5780 .map(Atom::from)
5781 .collect();
5782 if let Some(collection) = self.classes_map.borrow().get(&class_atoms) {
5783 return DomRoot::from_ref(collection);
5784 }
5785 let result = HTMLCollection::by_atomic_class_name(
5786 cx,
5787 &self.window,
5788 self.upcast(),
5789 class_atoms.clone(),
5790 );
5791 self.classes_map
5792 .borrow_mut()
5793 .insert(class_atoms, Dom::from_ref(&*result));
5794 result
5795 }
5796
5797 fn GetElementById(
5799 &self,
5800 cx: &js::context::JSContext,
5801 id: DOMString,
5802 ) -> Option<DomRoot<Element>> {
5803 self.get_element_by_id(cx, &Atom::from(id))
5804 }
5805
5806 fn CreateElement(
5808 &self,
5809 cx: &mut JSContext,
5810 mut local_name: DOMString,
5811 options: StringOrElementCreationOptions,
5812 ) -> Fallible<DomRoot<Element>> {
5813 if !is_valid_element_local_name(&local_name.str()) {
5815 return Err(Error::InvalidCharacter(Some(
5816 "Provided element local name is invalid".into(),
5817 )));
5818 }
5819
5820 if self.is_html_document {
5822 local_name.make_ascii_lowercase();
5823 }
5824
5825 let ns = if self.is_html_document || self.is_xhtml_document() {
5827 ns!(html)
5828 } else {
5829 ns!()
5830 };
5831 let name = QualName::new(None, ns, LocalName::from(local_name));
5832
5833 let is = match options {
5834 StringOrElementCreationOptions::String(_) => None,
5835 StringOrElementCreationOptions::ElementCreationOptions(options) => {
5836 options.is.as_ref().map(LocalName::from)
5837 },
5838 };
5839 Ok(Element::create(
5840 cx,
5841 name,
5842 is,
5843 self,
5844 ElementCreator::ScriptCreated,
5845 CustomElementCreationMode::Synchronous,
5846 None,
5847 ))
5848 }
5849
5850 fn CreateElementNS(
5852 &self,
5853 cx: &mut JSContext,
5854 namespace: Option<DOMString>,
5855 qualified_name: DOMString,
5856 options: StringOrElementCreationOptions,
5857 ) -> Fallible<DomRoot<Element>> {
5858 let context = domname::Context::Element;
5861 let (namespace, prefix, local_name) =
5862 domname::validate_and_extract(namespace, &qualified_name, context)?;
5863
5864 let name = QualName::new(prefix, namespace, local_name);
5867 let is = match options {
5868 StringOrElementCreationOptions::String(_) => None,
5869 StringOrElementCreationOptions::ElementCreationOptions(options) => {
5870 options.is.as_ref().map(LocalName::from)
5871 },
5872 };
5873
5874 Ok(Element::create(
5876 cx,
5877 name,
5878 is,
5879 self,
5880 ElementCreator::ScriptCreated,
5881 CustomElementCreationMode::Synchronous,
5882 None,
5883 ))
5884 }
5885
5886 fn CreateAttribute(
5888 &self,
5889 cx: &mut JSContext,
5890 mut local_name: DOMString,
5891 ) -> Fallible<DomRoot<Attr>> {
5892 if !is_valid_attribute_local_name(&local_name.str()) {
5894 return Err(Error::InvalidCharacter(Some(
5895 "Provided local name is invalid".into(),
5896 )));
5897 }
5898
5899 if self.is_html_document {
5901 local_name.make_ascii_lowercase();
5902 }
5903 let name = LocalName::from(local_name);
5904 let value = AttrValue::String(String::new());
5905
5906 Ok(Attr::new(
5907 cx,
5908 self,
5909 name.clone(),
5910 value,
5911 name,
5912 ns!(),
5913 None,
5914 None,
5915 ))
5916 }
5917
5918 fn CreateAttributeNS(
5920 &self,
5921 cx: &mut JSContext,
5922 namespace: Option<DOMString>,
5923 qualified_name: DOMString,
5924 ) -> Fallible<DomRoot<Attr>> {
5925 let context = domname::Context::Attribute;
5928 let (namespace, prefix, local_name) =
5929 domname::validate_and_extract(namespace, &qualified_name, context)?;
5930 let value = AttrValue::String(String::new());
5931 let qualified_name = LocalName::from(qualified_name);
5932 Ok(Attr::new(
5933 cx,
5934 self,
5935 local_name,
5936 value,
5937 qualified_name,
5938 namespace,
5939 prefix,
5940 None,
5941 ))
5942 }
5943
5944 fn CreateDocumentFragment(&self, cx: &mut JSContext) -> DomRoot<DocumentFragment> {
5946 DocumentFragment::new(cx, self)
5947 }
5948
5949 fn CreateTextNode(&self, cx: &mut JSContext, data: DOMString) -> DomRoot<Text> {
5951 Text::new(cx, data, self)
5952 }
5953
5954 fn CreateCDATASection(
5956 &self,
5957 cx: &mut JSContext,
5958 data: DOMString,
5959 ) -> Fallible<DomRoot<CDATASection>> {
5960 if self.is_html_document {
5962 return Err(Error::NotSupported(Some(
5963 "Document must be an XML document".into(),
5964 )));
5965 }
5966
5967 if data.contains("]]>") {
5969 return Err(Error::InvalidCharacter(Some(
5970 "CDATA section cannot include `]]>`".into(),
5971 )));
5972 }
5973
5974 Ok(CDATASection::new(cx, data, self))
5976 }
5977
5978 fn CreateComment(&self, cx: &mut JSContext, data: DOMString) -> DomRoot<Comment> {
5980 Comment::new(cx, data, self, None)
5981 }
5982
5983 fn CreateProcessingInstruction(
5985 &self,
5986 cx: &mut JSContext,
5987 target: DOMString,
5988 data: DOMString,
5989 ) -> Fallible<DomRoot<ProcessingInstruction>> {
5990 if !matches_name_production(&target.str()) {
5992 return Err(Error::InvalidCharacter(Some(
5993 "Target name provided is invalid".into(),
5994 )));
5995 }
5996
5997 if data.contains("?>") {
5999 return Err(Error::InvalidCharacter(Some(
6000 "Processing instruction's data cannot contain `>?`".into(),
6001 )));
6002 }
6003
6004 Ok(ProcessingInstruction::new(cx, target, data, self))
6006 }
6007
6008 fn ImportNode(
6010 &self,
6011 cx: &mut JSContext,
6012 node: &Node,
6013 options: BooleanOrImportNodeOptions,
6014 ) -> Fallible<DomRoot<Node>> {
6015 if node.is::<Document>() || node.is::<ShadowRoot>() {
6017 return Err(Error::NotSupported(Some(
6018 "Node cannot be a document or shadow root".into(),
6019 )));
6020 }
6021 let (subtree, registry) = match options {
6023 BooleanOrImportNodeOptions::Boolean(boolean) => (boolean.into(), None),
6026 BooleanOrImportNodeOptions::ImportNodeOptions(options) => {
6028 let subtree = (!options.selfOnly).into();
6030 let registry = if let Some(registry) = options.customElementRegistry {
6032 let this_registry = self
6035 .custom_element_registry()
6036 .expect("Document must have a custom element registry");
6037 if !registry.is_scoped() && registry != this_registry {
6038 return Err(Error::NotSupported(Some(
6039 "Imported customElementRegistry is not scoped and does not match existing registry.".into()
6040 )));
6041 }
6042 Some(registry)
6043 } else {
6044 None
6045 };
6046 (subtree, registry)
6047 },
6048 };
6049 let registry = registry
6052 .or_else(|| CustomElementRegistry::lookup_a_custom_element_registry(self.upcast()));
6053
6054 Ok(Node::clone(cx, node, Some(self), subtree, registry))
6057 }
6058
6059 fn AdoptNode(&self, cx: &mut JSContext, node: &Node) -> Fallible<DomRoot<Node>> {
6061 if node.is::<Document>() {
6063 return Err(Error::NotSupported(Some(
6064 "Node cannot be a document".into(),
6065 )));
6066 }
6067
6068 if node.is::<ShadowRoot>() {
6070 return Err(Error::HierarchyRequest(Some(
6071 "Node cannot be a shadow root".into(),
6072 )));
6073 }
6074
6075 Node::adopt(cx, node, self);
6077
6078 Ok(DomRoot::from_ref(node))
6080 }
6081
6082 fn CreateEvent(
6084 &self,
6085 cx: &mut JSContext,
6086 mut interface: DOMString,
6087 ) -> Fallible<DomRoot<Event>> {
6088 interface.make_ascii_lowercase();
6089 match &*interface.str() {
6090 "beforeunloadevent" => Ok(DomRoot::upcast(BeforeUnloadEvent::new_uninitialized(
6091 cx,
6092 &self.window,
6093 ))),
6094 "compositionevent" => Ok(DomRoot::upcast(CompositionEvent::new_uninitialized(
6095 cx,
6096 &self.window,
6097 ))),
6098 "customevent" => Ok(DomRoot::upcast(CustomEvent::new_uninitialized(
6099 cx,
6100 self.window.upcast(),
6101 ))),
6102 "events" | "event" | "htmlevents" | "svgevents" => {
6105 Ok(Event::new_uninitialized(cx, self.window.upcast()))
6106 },
6107 "focusevent" => Ok(DomRoot::upcast(FocusEvent::new_uninitialized(
6108 cx,
6109 &self.window,
6110 ))),
6111 "hashchangeevent" => Ok(DomRoot::upcast(HashChangeEvent::new_uninitialized(
6112 cx,
6113 &self.window,
6114 ))),
6115 "keyboardevent" => Ok(DomRoot::upcast(KeyboardEvent::new_uninitialized(
6116 cx,
6117 &self.window,
6118 ))),
6119 "messageevent" => Ok(DomRoot::upcast(MessageEvent::new_uninitialized(
6120 cx,
6121 self.window.upcast(),
6122 ))),
6123 "mouseevent" | "mouseevents" => Ok(DomRoot::upcast(MouseEvent::new_uninitialized(
6124 cx,
6125 &self.window,
6126 ))),
6127 "storageevent" => Ok(DomRoot::upcast(StorageEvent::new_uninitialized(
6128 cx,
6129 &self.window,
6130 DOMString::new(),
6131 ))),
6132 "textevent" => Ok(DomRoot::upcast(TextEvent::new_uninitialized(
6133 cx,
6134 &self.window,
6135 ))),
6136 "touchevent" => {
6137 let touches = TouchList::new(cx, &self.window, &[]);
6138 let changed_touches = TouchList::new(cx, &self.window, &[]);
6139 let target_touches = TouchList::new(cx, &self.window, &[]);
6140
6141 Ok(DomRoot::upcast(DomTouchEvent::new_uninitialized(
6142 cx,
6143 &self.window,
6144 &touches,
6145 &changed_touches,
6146 &target_touches,
6147 )))
6148 },
6149 "uievent" | "uievents" => Ok(DomRoot::upcast(UIEvent::new_uninitialized(
6150 cx,
6151 &self.window,
6152 ))),
6153 _ => Err(Error::NotSupported(Some(
6154 "Interface is not supported".into(),
6155 ))),
6156 }
6157 }
6158
6159 fn LastModified(&self) -> DOMString {
6161 DOMString::from(self.last_modified.as_ref().cloned().unwrap_or_else(|| {
6162 Local::now().format("%m/%d/%Y %H:%M:%S").to_string()
6168 }))
6169 }
6170
6171 fn CreateRange(&self, cx: &mut JSContext) -> DomRoot<Range> {
6173 Range::new_with_doc(cx, self, None)
6174 }
6175
6176 fn CreateNodeIterator(
6178 &self,
6179 cx: &mut js::context::JSContext,
6180 root: &Node,
6181 what_to_show: u32,
6182 filter: Option<RootedCallback<NodeFilter>>,
6183 ) -> DomRoot<NodeIterator> {
6184 NodeIterator::new(cx, self, root, what_to_show, filter)
6185 }
6186
6187 fn CreateTreeWalker(
6189 &self,
6190 cx: &mut JSContext,
6191 root: &Node,
6192 what_to_show: u32,
6193 filter: Option<RootedCallback<NodeFilter>>,
6194 ) -> DomRoot<TreeWalker> {
6195 TreeWalker::new(cx, self, root, what_to_show, filter)
6196 }
6197
6198 fn Title(&self) -> DOMString {
6200 self.title().unwrap_or_default()
6201 }
6202
6203 fn SetTitle(&self, cx: &mut JSContext, title: DOMString) {
6205 let root = match self.GetDocumentElement() {
6206 Some(root) => root,
6207 None => return,
6208 };
6209
6210 let node = if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
6213 let elem = root
6216 .upcast::<Node>()
6217 .child_elements_unrooted(cx.no_gc())
6218 .find(|node| {
6219 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
6220 });
6221 match elem {
6222 Some(elem) => UnrootedDom::upcast::<Node>(elem).as_rooted(),
6223 None => {
6225 let name = QualName::new(None, ns!(svg), local_name!("title"));
6228 let elem = Element::create(
6229 cx,
6230 name,
6231 None,
6232 self,
6233 ElementCreator::ScriptCreated,
6234 CustomElementCreationMode::Synchronous,
6235 None,
6236 );
6237
6238 let parent = root.upcast::<Node>();
6240 let child = elem.upcast::<Node>();
6241 parent
6242 .InsertBefore(cx, child, parent.GetFirstChild().as_deref())
6243 .unwrap()
6244 },
6245 }
6246 }
6247 else if root.namespace() == &ns!(html) {
6249 let elem = root
6250 .upcast::<Node>()
6251 .traverse_preorder_unrooted(cx.no_gc(), ShadowIncluding::No)
6252 .find(|node| node.is::<HTMLTitleElement>());
6253 match elem {
6254 Some(elem) => elem.as_rooted(),
6256 None => match self.GetHead() {
6258 Some(head) => {
6259 let name = QualName::new(None, ns!(html), local_name!("title"));
6262 let elem = Element::create(
6263 cx,
6264 name,
6265 None,
6266 self,
6267 ElementCreator::ScriptCreated,
6268 CustomElementCreationMode::Synchronous,
6269 None,
6270 );
6271
6272 head.upcast::<Node>()
6274 .AppendChild(cx, elem.upcast())
6275 .unwrap()
6276 },
6277 None => return,
6279 },
6280 }
6281 }
6282 else {
6284 return;
6286 };
6287
6288 node.set_text_content_for_element(cx, Some(title));
6293 }
6294
6295 fn Dir(&self) -> DOMString {
6297 self.get_html_element()
6298 .map(|html| html.upcast::<HTMLElement>().Dir())
6299 .unwrap_or_default()
6300 }
6301
6302 fn SetDir(&self, cx: &mut JSContext, dir: DOMString) {
6304 if let Some(html) = self.get_html_element() {
6305 html.upcast::<HTMLElement>().SetDir(cx, dir);
6306 }
6307 }
6308
6309 fn GetHead(&self) -> Option<DomRoot<HTMLHeadElement>> {
6311 self.get_html_element()
6312 .and_then(|root| root.upcast::<Node>().children().find_map(DomRoot::downcast))
6313 }
6314
6315 fn GetCurrentScript(&self) -> Option<DomRoot<HTMLScriptElement>> {
6317 self.current_script.get()
6318 }
6319
6320 fn GetBody(&self) -> Option<DomRoot<HTMLElement>> {
6322 self.get_html_element().and_then(|root| {
6325 let node = root.upcast::<Node>();
6326 node.children()
6327 .find(|child| {
6328 matches!(
6329 child.type_id(),
6330 NodeTypeId::Element(ElementTypeId::HTMLElement(
6331 HTMLElementTypeId::HTMLBodyElement,
6332 )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
6333 HTMLElementTypeId::HTMLFrameSetElement,
6334 ))
6335 )
6336 })
6337 .map(|node| DomRoot::downcast(node).unwrap())
6338 })
6339 }
6340
6341 fn SetBody(&self, cx: &mut JSContext, new_body: Option<&HTMLElement>) -> ErrorResult {
6343 let new_body = match new_body {
6345 Some(new_body) => new_body,
6346 None => {
6347 return Err(Error::HierarchyRequest(Some(
6348 "HTML element provided is neither a body nor a frameset element".into(),
6349 )));
6350 },
6351 };
6352
6353 let node = new_body.upcast::<Node>();
6354 match node.type_id() {
6355 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) |
6356 NodeTypeId::Element(ElementTypeId::HTMLElement(
6357 HTMLElementTypeId::HTMLFrameSetElement,
6358 )) => {},
6359 _ => {
6360 return Err(Error::HierarchyRequest(Some(
6361 "HTML element provided is neither a body nor a frameset element".into(),
6362 )));
6363 },
6364 }
6365
6366 let old_body = self.GetBody();
6368 if old_body.as_deref() == Some(new_body) {
6369 return Ok(());
6370 }
6371
6372 match (self.GetDocumentElement(), &old_body) {
6373 (Some(ref root), Some(child)) => {
6376 let root = root.upcast::<Node>();
6377 root.ReplaceChild(cx, new_body.upcast(), child.upcast())
6378 .map(|_| ())
6379 },
6380
6381 (None, _) => Err(Error::HierarchyRequest(Some(
6383 "Document element is missing".into(),
6384 ))),
6385
6386 (Some(ref root), &None) => {
6389 let root = root.upcast::<Node>();
6390 root.AppendChild(cx, new_body.upcast()).map(|_| ())
6391 },
6392 }
6393 }
6394
6395 fn GetElementsByName(&self, cx: &mut JSContext, name: DOMString) -> DomRoot<NodeList> {
6397 NodeList::new_elements_by_name_list(cx, self.window(), self, name)
6398 }
6399
6400 fn Images(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6402 self.images.or_init(|| {
6403 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6404 element.is::<HTMLImageElement>()
6405 })
6406 })
6407 }
6408
6409 fn Embeds(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6411 self.embeds.or_init(|| {
6412 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6413 element.is::<HTMLEmbedElement>()
6414 })
6415 })
6416 }
6417
6418 fn Plugins(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6420 self.Embeds(cx)
6421 }
6422
6423 fn Links(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6425 self.links.or_init(|| {
6426 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6427 (element.is::<HTMLAnchorElement>() || element.is::<HTMLAreaElement>()) &&
6428 element.has_attribute(&local_name!("href"))
6429 })
6430 })
6431 }
6432
6433 fn Forms(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6435 self.forms.or_init(|| {
6436 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6437 element.is::<HTMLFormElement>()
6438 })
6439 })
6440 }
6441
6442 fn Scripts(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6444 self.scripts.or_init(|| {
6445 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6446 element.is::<HTMLScriptElement>()
6447 })
6448 })
6449 }
6450
6451 fn Anchors(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6453 self.anchors.or_init(|| {
6454 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6455 element.is::<HTMLAnchorElement>() && element.has_attribute(&local_name!("href"))
6456 })
6457 })
6458 }
6459
6460 fn Applets(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6462 self.applets
6463 .or_init(|| HTMLCollection::always_empty(cx, &self.window, self.upcast()))
6464 }
6465
6466 fn GetLocation(&self, cx: &mut JSContext) -> Option<DomRoot<Location>> {
6468 if self.is_fully_active() {
6469 Some(self.window.Location(cx))
6470 } else {
6471 None
6472 }
6473 }
6474
6475 fn Children(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6477 HTMLCollection::children(cx, &self.window, self.upcast())
6478 }
6479
6480 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
6482 self.upcast::<Node>().child_elements().next()
6483 }
6484
6485 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
6487 self.upcast::<Node>()
6488 .rev_children()
6489 .find_map(DomRoot::downcast)
6490 }
6491
6492 fn ChildElementCount(&self) -> u32 {
6494 self.upcast::<Node>().child_elements().count() as u32
6495 }
6496
6497 fn Prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6499 self.upcast::<Node>().prepend(cx, nodes)
6500 }
6501
6502 fn Append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6504 self.upcast::<Node>().append(cx, nodes)
6505 }
6506
6507 fn ReplaceChildren(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6509 self.upcast::<Node>().replace_children(cx, nodes)
6510 }
6511
6512 fn MoveBefore(&self, cx: &mut JSContext, node: &Node, child: Option<&Node>) -> ErrorResult {
6514 self.upcast::<Node>().move_before(cx, node, child)
6515 }
6516
6517 fn QuerySelector(
6519 &self,
6520 cx: &mut JSContext,
6521 selectors: DOMString,
6522 ) -> Fallible<Option<DomRoot<Element>>> {
6523 self.upcast::<Node>().query_selector(cx.no_gc(), selectors)
6524 }
6525
6526 fn QuerySelectorAll(
6528 &self,
6529 cx: &mut JSContext,
6530 selectors: DOMString,
6531 ) -> Fallible<DomRoot<NodeList>> {
6532 self.upcast::<Node>().query_selector_all(cx, selectors)
6533 }
6534
6535 fn ReadyState(&self) -> DocumentReadyState {
6537 self.ready_state.get()
6538 }
6539
6540 fn GetDefaultView(&self) -> Option<DomRoot<Window>> {
6542 if self.has_browsing_context {
6543 Some(DomRoot::from_ref(&*self.window))
6544 } else {
6545 None
6546 }
6547 }
6548
6549 fn GetCookie(&self) -> Fallible<DOMString> {
6551 if self.is_cookie_averse() {
6552 return Ok(DOMString::new());
6553 }
6554
6555 if !self.origin().is_tuple() {
6556 return Err(Error::Security(Some("Document's origin is opaque".into())));
6557 }
6558
6559 let url = self.url();
6560 let (tx, rx) =
6561 profile_generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
6562 let _ = self
6563 .window
6564 .as_global_scope()
6565 .resource_threads()
6566 .send(GetCookieStringForUrl(url, tx, NonHTTP));
6567 let cookies = rx.recv().unwrap();
6568 Ok(cookies.map_or(DOMString::new(), DOMString::from))
6569 }
6570
6571 fn SetCookie(&self, cookie: DOMString) -> ErrorResult {
6573 if self.is_cookie_averse() {
6574 return Ok(());
6575 }
6576
6577 if !self.origin().is_tuple() {
6578 return Err(Error::Security(Some("Document's origin is opaque".into())));
6579 }
6580
6581 if !cookie.is_valid_for_cookie() {
6582 return Ok(());
6583 }
6584
6585 let cookies = if let Some(cookie) = Cookie::parse(cookie.to_string()).ok().map(Serde) {
6586 vec![cookie]
6587 } else {
6588 vec![]
6589 };
6590
6591 let _ = self
6592 .window
6593 .as_global_scope()
6594 .resource_threads()
6595 .send(SetCookiesForUrl(self.url(), cookies, NonHTTP));
6596 Ok(())
6597 }
6598
6599 fn BgColor(&self) -> DOMString {
6601 self.get_body_attribute(&local_name!("bgcolor"))
6602 }
6603
6604 fn SetBgColor(&self, cx: &mut JSContext, value: DOMString) {
6606 self.set_body_attribute(cx, &local_name!("bgcolor"), value)
6607 }
6608
6609 fn FgColor(&self) -> DOMString {
6611 self.get_body_attribute(&local_name!("text"))
6612 }
6613
6614 fn SetFgColor(&self, cx: &mut JSContext, value: DOMString) {
6616 self.set_body_attribute(cx, &local_name!("text"), value)
6617 }
6618
6619 fn NamedGetter(&self, cx: &mut JSContext, name: DOMString) -> Option<NamedPropertyValue> {
6621 if name.is_empty() {
6622 return None;
6623 }
6624 let name = Atom::from(name);
6625
6626 let elements_with_name = self.get_elements_with_name(cx, &name);
6629 let name_iter = elements_with_name
6630 .iter()
6631 .filter(|elem| is_named_element_with_name_attribute(elem));
6632 let elements_with_id = self.id_map.get_all(cx.no_gc(), self.upcast(), &name);
6633 let id_iter = elements_with_id
6634 .iter()
6635 .filter(|elem| is_named_element_with_id_attribute(elem));
6636 let mut elements = name_iter.chain(id_iter);
6637
6638 let first = elements.next()?;
6645 if elements.all(|other| first == other) {
6646 if let Some(nested_window_proxy) = first
6647 .downcast::<HTMLIFrameElement>()
6648 .and_then(|iframe| iframe.GetContentWindow())
6649 {
6650 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
6651 }
6652
6653 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
6655 }
6656
6657 #[derive(JSTraceable, MallocSizeOf)]
6660 struct DocumentNamedGetter {
6661 #[no_trace]
6662 name: Atom,
6663 }
6664 impl CollectionFilter for DocumentNamedGetter {
6665 fn filter(&self, elem: &Element, _root: &Node) -> bool {
6666 let type_ = match elem.upcast::<Node>().type_id() {
6667 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
6668 _ => return false,
6669 };
6670 match type_ {
6671 HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
6672 elem.get_name().as_ref() == Some(&self.name)
6673 },
6674 HTMLElementTypeId::HTMLImageElement => elem.get_name().is_some_and(|name| {
6675 name == *self.name ||
6676 !name.is_empty() && elem.get_id().as_ref() == Some(&self.name)
6677 }),
6678 _ => false,
6682 }
6683 }
6684 }
6685 let collection = HTMLCollection::create(
6686 cx,
6687 self.window(),
6688 self.upcast(),
6689 Box::new(DocumentNamedGetter { name }),
6690 );
6691 Some(NamedPropertyValue::HTMLCollection(collection))
6692 }
6693
6694 fn SupportedPropertyNames(&self, no_gc: &NoGC) -> Vec<DOMString> {
6696 let mut names_with_first_named_element_map = HashMap::new();
6697 self.name_map
6698 .for_each(no_gc, self.upcast(), |name, elements| {
6699 if name.is_empty() {
6700 return;
6701 }
6702 let mut name_iter = elements
6703 .iter()
6704 .filter(|elem| is_named_element_with_name_attribute(elem));
6705 if let Some(first) = name_iter.next() {
6706 names_with_first_named_element_map.insert(name.clone(), first.as_rooted());
6707 }
6708 });
6709
6710 self.id_map.for_each(no_gc, self.upcast(), |id, elements| {
6711 if id.is_empty() {
6712 return;
6713 }
6714 let mut id_iter = elements
6715 .iter()
6716 .filter(|elem| is_named_element_with_id_attribute(elem));
6717 if let Some(first) = id_iter.next() {
6718 match names_with_first_named_element_map.entry(id.clone()) {
6719 Vacant(entry) => drop(entry.insert(first.as_rooted())),
6720 Occupied(mut entry) => {
6721 if first
6722 .upcast::<Node>()
6723 .is_before(no_gc, entry.get().upcast())
6724 {
6725 *entry.get_mut() = first.as_rooted();
6726 }
6727 },
6728 }
6729 }
6730 });
6731
6732 let mut names_with_first_named_element_vec: Vec<_> =
6733 names_with_first_named_element_map.into_iter().collect();
6734 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
6735 if a.1 == b.1 {
6736 a.0.cmp(&b.0)
6739 } else if a.1.upcast::<Node>().is_before(no_gc, b.1.upcast::<Node>()) {
6740 Ordering::Less
6741 } else {
6742 Ordering::Greater
6743 }
6744 });
6745
6746 names_with_first_named_element_vec
6747 .into_iter()
6748 .map(|(k, _)| DOMString::from(&*k))
6749 .collect()
6750 }
6751
6752 fn Clear(&self) {
6754 }
6756
6757 fn CaptureEvents(&self) {
6759 }
6761
6762 fn ReleaseEvents(&self) {
6764 }
6766
6767 global_event_handlers!();
6769
6770 event_handler!(
6772 readystatechange,
6773 GetOnreadystatechange,
6774 SetOnreadystatechange
6775 );
6776
6777 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
6779 self.document_or_shadow_root.element_from_point(
6780 self.upcast(),
6781 x,
6782 y,
6783 self.GetDocumentElement(),
6784 self.has_browsing_context,
6785 )
6786 }
6787
6788 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
6790 self.document_or_shadow_root.elements_from_point(
6791 self.upcast(),
6792 x,
6793 y,
6794 self.GetDocumentElement(),
6795 self.has_browsing_context,
6796 )
6797 }
6798
6799 fn GetScrollingElement(&self) -> Option<DomRoot<Element>> {
6801 if self.quirks_mode() == QuirksMode::Quirks {
6803 if let Some(ref body) = self.GetBody() {
6805 let e = body.upcast::<Element>();
6806 if !e.is_potentially_scrollable_body_for_scrolling_element() {
6810 return Some(DomRoot::from_ref(e));
6811 }
6812 }
6813
6814 return None;
6816 }
6817
6818 self.GetDocumentElement()
6821 }
6822
6823 fn Open(
6825 &self,
6826 cx: &mut JSContext,
6827 _unused1: Option<DOMString>,
6828 _unused2: Option<DOMString>,
6829 ) -> Fallible<DomRoot<Document>> {
6830 if !self.is_html_document() {
6832 return Err(Error::InvalidState(Some(
6833 "Document must be a HTML document".into(),
6834 )));
6835 }
6836
6837 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6840 return Err(Error::InvalidState(Some(
6841 "A custom element constructor attempted to open, close or write to this document"
6842 .into(),
6843 )));
6844 }
6845
6846 let entry_responsible_document = GlobalScope::entry().as_window().Document();
6848
6849 if !self
6852 .origin()
6853 .same_origin(&entry_responsible_document.origin())
6854 {
6855 return Err(Error::Security(Some(
6856 "Document's origin is not the same as entry global's document origin".into(),
6857 )));
6858 }
6859
6860 if self
6863 .active_parser()
6864 .is_some_and(|parser| parser.script_nesting_level() > 0)
6865 {
6866 return Ok(DomRoot::from_ref(self));
6867 }
6868
6869 if self.is_prompting_or_unloading() {
6871 return Ok(DomRoot::from_ref(self));
6872 }
6873
6874 if self.active_parser_was_aborted.get() {
6876 return Ok(DomRoot::from_ref(self));
6877 }
6878
6879 self.window().set_navigation_start();
6883
6884 if self.has_browsing_context() {
6888 self.abort(cx, AbortReason::DocumentOpen);
6891 }
6892
6893 for node in self
6896 .upcast::<Node>()
6897 .traverse_preorder_unrooted(cx.no_gc(), ShadowIncluding::Yes)
6898 {
6899 node.upcast::<EventTarget>().remove_all_listeners(cx);
6900 }
6901
6902 if self.window.Document() == DomRoot::from_ref(self) {
6905 self.window.upcast::<EventTarget>().remove_all_listeners(cx);
6906 }
6907
6908 Node::replace_all(cx, None, self.upcast::<Node>());
6910
6911 if self.is_fully_active() {
6918 let mut new_url = entry_responsible_document.url();
6920
6921 if entry_responsible_document != DomRoot::from_ref(self) {
6923 new_url.set_fragment(None);
6924 }
6925
6926 self.set_url(new_url);
6929 }
6930
6931 self.is_initial_about_blank.set(false);
6933
6934 if self.iframe_load_in_progress.get() {
6937 self.mute_iframe_load.set(true);
6938 }
6939
6940 self.set_quirks_mode(QuirksMode::NoQuirks);
6942
6943 let resource_threads = self.window.as_global_scope().resource_threads().clone();
6949 *self.loader.borrow_mut() =
6950 DocumentLoader::new_with_threads(resource_threads, Some(self.url()));
6951 ServoParser::parse_html_script_input(cx, self, self.url());
6952
6953 self.update_the_current_document_readiness(cx, DocumentReadyState::Loading);
6959
6960 Ok(DomRoot::from_ref(self))
6962 }
6963
6964 fn Open_(
6966 &self,
6967 cx: &mut JSContext,
6968 url: USVString,
6969 target: DOMString,
6970 features: DOMString,
6971 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
6972 self.browsing_context()
6973 .ok_or(Error::InvalidAccess(Some(
6974 "Document is not fully active".into(),
6975 )))?
6976 .open(cx, url, target, features)
6977 }
6978
6979 fn Write(&self, cx: &mut JSContext, text: Vec<TrustedHTMLOrString>) -> ErrorResult {
6981 self.write(cx, text, false, "Document", "write")
6984 }
6985
6986 fn Writeln(&self, cx: &mut JSContext, text: Vec<TrustedHTMLOrString>) -> ErrorResult {
6988 self.write(cx, text, true, "Document", "writeln")
6991 }
6992
6993 fn Close(&self, cx: &mut JSContext) -> ErrorResult {
6995 if !self.is_html_document() {
6996 return Err(Error::InvalidState(Some(
6998 "Document must be a HTML document".into(),
6999 )));
7000 }
7001
7002 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
7005 return Err(Error::InvalidState(Some(
7006 "A custom element constructor attempted to open, close or write to this document"
7007 .into(),
7008 )));
7009 }
7010
7011 let parser = match self.get_current_parser() {
7013 Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
7014 _ => {
7015 return Ok(());
7016 },
7017 };
7018
7019 parser.close(cx);
7021
7022 Ok(())
7023 }
7024
7025 fn ExecCommand(
7027 &self,
7028 cx: &mut JSContext,
7029 command_id: DOMString,
7030 _show_ui: bool,
7031 value: TrustedHTMLOrString,
7032 ) -> Fallible<bool> {
7033 let value = if command_id == "insertHTML" {
7034 TrustedHTML::get_trusted_type_compliant_string(
7035 cx,
7036 self.window.as_global_scope(),
7037 value,
7038 "Document execCommand",
7039 )?
7040 } else {
7041 match value {
7042 TrustedHTMLOrString::TrustedHTML(trusted_html) => trusted_html.data().clone(),
7043 TrustedHTMLOrString::String(value) => value,
7044 }
7045 };
7046
7047 Ok(self.exec_command_for_command_id(cx, command_id, value))
7048 }
7049
7050 fn QueryCommandEnabled(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
7052 self.check_support_and_enabled(cx, &command_id).is_some()
7054 }
7055
7056 fn QueryCommandSupported(&self, command_id: DOMString) -> bool {
7058 self.is_command_supported(command_id)
7062 }
7063
7064 fn QueryCommandIndeterm(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
7066 self.is_command_indeterminate(cx, command_id)
7067 }
7068
7069 fn QueryCommandState(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
7071 self.command_state_for_command(cx, command_id)
7072 }
7073
7074 fn QueryCommandValue(&self, cx: &mut JSContext, command_id: DOMString) -> DOMString {
7076 self.command_value_for_command(cx, command_id)
7077 }
7078
7079 event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror);
7081
7082 event_handler!(
7084 fullscreenchange,
7085 GetOnfullscreenchange,
7086 SetOnfullscreenchange
7087 );
7088
7089 fn FullscreenEnabled(&self) -> bool {
7091 self.get_allow_fullscreen()
7092 }
7093
7094 fn Fullscreen(&self) -> bool {
7096 self.fullscreen_element.get().is_some()
7097 }
7098
7099 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
7101 DocumentOrShadowRoot::get_fullscreen_element(&self.node, self.fullscreen_element.get())
7102 }
7103
7104 fn ExitFullscreen(&self, cx: &mut CurrentRealm) -> RootedPromise {
7106 self.exit_fullscreen(cx)
7107 }
7108
7109 fn ServoGetMediaControls(&self, id: DOMString) -> Fallible<DomRoot<ShadowRoot>> {
7113 match self.media_controls.borrow().get(&*id.str()) {
7114 Some(m) => Ok(DomRoot::from_ref(m)),
7115 None => Err(Error::InvalidAccess(Some(
7116 "No registered media controls exist with provided id".into(),
7117 ))),
7118 }
7119 }
7120
7121 fn GetSelection(&self, cx: &mut JSContext) -> Option<DomRoot<Selection>> {
7123 if self.has_browsing_context {
7124 Some(self.selection.or_init(|| Selection::new(cx, self)))
7125 } else {
7126 None
7127 }
7128 }
7129
7130 fn Fonts(&self, cx: &mut JSContext) -> DomRoot<FontFaceSet> {
7132 self.fonts
7133 .or_init(|| FontFaceSet::new(cx, &self.global(), None))
7134 }
7135
7136 fn Hidden(&self) -> bool {
7138 self.visibility_state.get() == DocumentVisibilityState::Hidden
7139 }
7140
7141 fn VisibilityState(&self) -> DocumentVisibilityState {
7143 self.visibility_state.get()
7144 }
7145
7146 fn CreateExpression(
7147 &self,
7148 cx: &mut JSContext,
7149 expression: DOMString,
7150 resolver: Option<RootedCallback<XPathNSResolver>>,
7151 ) -> Fallible<DomRoot<crate::dom::types::XPathExpression>> {
7152 let parsed_expression =
7153 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
7154 Ok(XPathExpression::new(
7155 cx,
7156 &self.window,
7157 None,
7158 parsed_expression,
7159 ))
7160 }
7161
7162 fn CreateNSResolver(&self, cx: &mut JSContext, node_resolver: &Node) -> DomRoot<Node> {
7163 let global = self.global();
7164 let window = global.as_window();
7165 let evaluator = XPathEvaluator::new(cx, window, None);
7166 XPathEvaluatorMethods::<crate::DomTypeHolder>::CreateNSResolver(&*evaluator, node_resolver)
7167 }
7168
7169 fn Evaluate(
7170 &self,
7171 cx: &mut JSContext,
7172 expression: DOMString,
7173 context_node: &Node,
7174 resolver: Option<RootedCallback<XPathNSResolver>>,
7175 result_type: u16,
7176 result: Option<&crate::dom::types::XPathResult>,
7177 ) -> Fallible<DomRoot<crate::dom::types::XPathResult>> {
7178 let parsed_expression =
7179 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
7180 XPathExpression::new(cx, &self.window, None, parsed_expression).evaluate_internal(
7181 cx,
7182 context_node,
7183 result_type,
7184 result,
7185 )
7186 }
7187
7188 fn AdoptedStyleSheets(&self, cx: &mut JSContext, retval: MutableHandleValue) {
7190 self.adopted_stylesheets_frozen_types.get_or_init(
7191 cx,
7192 || {
7193 self.adopted_stylesheets
7194 .borrow()
7195 .clone()
7196 .iter()
7197 .map(|sheet| sheet.as_rooted())
7198 .collect()
7199 },
7200 retval,
7201 );
7202 }
7203
7204 fn SetAdoptedStyleSheets(&self, cx: &mut JSContext, val: HandleValue) -> ErrorResult {
7206 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
7207 cx,
7208 &self.adopted_stylesheets,
7209 val,
7210 &StyleSheetListOwner::Document(Dom::from_ref(self)),
7211 );
7212
7213 if result.is_ok() {
7214 self.adopted_stylesheets_frozen_types.clear()
7215 }
7216
7217 result
7218 }
7219
7220 fn Timeline(&self) -> DomRoot<DocumentTimeline> {
7221 self.timeline.as_rooted()
7222 }
7223}
7224
7225fn update_with_current_instant(marker: &Cell<Option<CrossProcessInstant>>) {
7226 if marker.get().is_none() {
7227 marker.set(Some(CrossProcessInstant::now()))
7228 }
7229}
7230
7231#[derive(JSTraceable, MallocSizeOf)]
7232pub(crate) enum AnimationFrameCallback {
7233 DevtoolsFramerateTick {
7234 actor_name: String,
7235 },
7236 FrameRequestCallback {
7237 callback: TracedCallback<FrameRequestCallback>,
7238 },
7239}
7240
7241impl js::gc::Rootable for AnimationFrameCallback {}
7242
7243impl AnimationFrameCallback {
7244 fn call(&self, cx: &mut JSContext, document: &Document, now: f64) {
7245 match *self {
7246 AnimationFrameCallback::DevtoolsFramerateTick { ref actor_name } => {
7247 let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name.clone(), now);
7248 let devtools_sender = document.window().as_global_scope().devtools_chan().unwrap();
7249 devtools_sender.send(msg).unwrap();
7250 },
7251 AnimationFrameCallback::FrameRequestCallback { ref callback } => {
7252 let _ = callback.Call__(cx, Finite::wrap(now), ExceptionHandling::Report);
7255 },
7256 }
7257 }
7258}
7259
7260#[derive(Default, JSTraceable, MallocSizeOf)]
7261#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
7262struct PendingInOrderScriptVec {
7263 scripts: DomRefCell<VecDeque<PendingScript>>,
7264}
7265
7266impl PendingInOrderScriptVec {
7267 fn is_empty(&self) -> bool {
7268 self.scripts.borrow().is_empty()
7269 }
7270
7271 fn push(&self, element: &HTMLScriptElement) {
7272 self.scripts
7273 .borrow_mut()
7274 .push_back(PendingScript::new(element));
7275 }
7276
7277 fn loaded(&self, element: &HTMLScriptElement, result: ScriptResult) {
7278 let mut scripts = self.scripts.borrow_mut();
7279 let entry = scripts
7280 .iter_mut()
7281 .find(|entry| &*entry.element == element)
7282 .unwrap();
7283 entry.loaded(result);
7284 }
7285
7286 fn take_next_ready_to_be_executed(&self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
7287 let mut scripts = self.scripts.borrow_mut();
7288 let pair = scripts.front_mut()?.take_result()?;
7289 scripts.pop_front();
7290 Some(pair)
7291 }
7292
7293 fn clear(&self) {
7294 *self.scripts.borrow_mut() = Default::default();
7295 }
7296}
7297
7298#[derive(JSTraceable, MallocSizeOf)]
7299#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
7300struct PendingScript {
7301 element: Dom<HTMLScriptElement>,
7302 load: Option<ScriptResult>,
7304}
7305
7306impl PendingScript {
7307 fn new(element: &HTMLScriptElement) -> Self {
7308 Self {
7309 element: Dom::from_ref(element),
7310 load: None,
7311 }
7312 }
7313
7314 fn new_with_load(element: &HTMLScriptElement, load: Option<ScriptResult>) -> Self {
7315 Self {
7316 element: Dom::from_ref(element),
7317 load,
7318 }
7319 }
7320
7321 fn loaded(&mut self, result: ScriptResult) {
7322 assert!(self.load.is_none());
7323 self.load = Some(result);
7324 }
7325
7326 fn take_result(&mut self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
7327 self.load
7328 .take()
7329 .map(|result| (DomRoot::from_ref(&*self.element), result))
7330 }
7331}
7332
7333fn is_named_element_with_name_attribute(elem: &Element) -> bool {
7334 let type_ = match elem.upcast::<Node>().type_id() {
7335 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
7336 _ => return false,
7337 };
7338 match type_ {
7339 HTMLElementTypeId::HTMLFormElement |
7340 HTMLElementTypeId::HTMLIFrameElement |
7341 HTMLElementTypeId::HTMLImageElement => true,
7342 _ => false,
7346 }
7347}
7348
7349fn is_named_element_with_id_attribute(elem: &Element) -> bool {
7350 elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty())
7354}
7355
7356impl DocumentHelpers for Document {
7357 fn ensure_safe_to_run_script_or_layout(&self) {
7358 Document::ensure_safe_to_run_script_or_layout(self)
7359 }
7360}
7361
7362pub(crate) struct SameoriginAncestorNavigablesIterator {
7366 document: DomRoot<Document>,
7367}
7368
7369impl SameoriginAncestorNavigablesIterator {
7370 pub(crate) fn new(document: DomRoot<Document>) -> Self {
7371 Self { document }
7372 }
7373}
7374
7375impl Iterator for SameoriginAncestorNavigablesIterator {
7376 type Item = DomRoot<Document>;
7377
7378 fn next(&mut self) -> Option<Self::Item> {
7379 let window_proxy = self.document.browsing_context()?;
7380 self.document = window_proxy.parent()?.document()?;
7381 Some(self.document.clone())
7382 }
7383}
7384
7385pub(crate) struct SameOriginDescendantNavigablesIterator {
7390 stack: Vec<Box<dyn Iterator<Item = DomRoot<HTMLIFrameElement>>>>,
7391}
7392
7393impl SameOriginDescendantNavigablesIterator {
7394 pub(crate) fn new(document: &Document) -> Self {
7395 let iframes: Vec<DomRoot<HTMLIFrameElement>> = document.iframes().iter().collect();
7396 Self {
7397 stack: vec![Box::new(iframes.into_iter())],
7398 }
7399 }
7400
7401 fn get_next_iframe(&mut self) -> Option<DomRoot<HTMLIFrameElement>> {
7402 let mut cur_iframe = self.stack.last_mut()?.next();
7403 while cur_iframe.is_none() {
7404 self.stack.pop();
7405 cur_iframe = self.stack.last_mut()?.next();
7406 }
7407 cur_iframe
7408 }
7409}
7410
7411impl Iterator for SameOriginDescendantNavigablesIterator {
7412 type Item = DomRoot<Document>;
7413
7414 fn next(&mut self) -> Option<Self::Item> {
7415 while let Some(iframe) = self.get_next_iframe() {
7416 let Some(pipeline_id) = iframe.pipeline_id() else {
7417 continue;
7418 };
7419
7420 if let Some(document) = ScriptThread::find_document(pipeline_id) {
7421 let child_iframes: Vec<DomRoot<HTMLIFrameElement>> =
7422 document.iframes().iter().collect();
7423 self.stack.push(Box::new(child_iframes.into_iter()));
7424 return Some(document);
7425 } else {
7426 continue;
7427 };
7428 }
7429 None
7430 }
7431}