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 atomic_refcell::{AtomicRef, AtomicRefCell};
19use bitflags::bitflags;
20use chrono::Local;
21use content_security_policy::sandboxing_directive::SandboxingFlagSet;
22use content_security_policy::{CspList, Policy as CspPolicy, PolicyDisposition};
23use cookie::Cookie;
24use data_url::mime::Mime;
25#[cfg(feature = "devtools")]
26use devtools_traits::ScriptToDevtoolsControlMsg;
27use dom_struct::dom_struct;
28use embedder_traits::{
29 AllowOrDeny, AnimationState, CustomHandlersAutomationMode, EmbedderMsg, Image, LoadStatus,
30 Theme,
31};
32use encoding_rs::{Encoding, UTF_8};
33use html5ever::{LocalName, QualName, local_name, ns};
34use hyper_serde::Serde;
35use indexmap::IndexSet;
36use js::context::{JSContext, NoGC};
37use js::jsapi::JSObject;
38use js::realm::CurrentRealm;
39use js::rust::{HandleObject, HandleValue};
40use layout_api::{
41 ContainerTimingRecord, LCPCandidate, PendingRestyle, ReflowGoal, ReflowPhasesRun,
42 ReflowStatistics, RestyleReason, ScrollContainerQueryFlags, TrustedNodeAddress,
43};
44use malloc_size_of::MallocSizeOfOps;
45use metrics::{InteractiveFlag, InteractiveWindow, ProgressiveWebMetrics};
46use net_traits::CookieSource::NonHTTP;
47use net_traits::CoreResourceMsg::{GetCookieStringForUrl, SetCookiesForUrl};
48use net_traits::image_cache::ImageCache;
49use net_traits::policy_container::PolicyContainer;
50use net_traits::pub_domains::{Site, is_pub_domain};
51use net_traits::request::{
52 InsecureRequestsPolicy, PreloadId, PreloadKey, PreloadedResources, RequestBuilder,
53};
54use net_traits::{ReferrerPolicy, ResourceFetchTiming};
55use paint_api::display_list::PaintTimingInfo;
56use percent_encoding::percent_decode;
57use profile_traits::mem::{Report, ReportKind};
58use profile_traits::time::TimerMetadataFrameType;
59use profile_traits::{generic_channel as profile_generic_channel, path};
60use regex::bytes::Regex;
61use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
62use script_bindings::callback::{RootedCallback, ThisReflector};
63use script_bindings::cell::{DomRefCell, Ref, RefMut};
64use script_bindings::domstring::TracedDOMString;
65use script_bindings::interfaces::DocumentHelpers;
66use script_bindings::reflector::reflect_dom_object_with_proto;
67use script_bindings::tasks::NonSendTaskBox;
68use script_bindings::trace::CustomTraceable;
69use script_traits::{DocumentActivity, ProgressiveWebMetricType};
70use servo_arc::Arc;
71use servo_base::cross_process_instant::CrossProcessInstant;
72use servo_base::generic_channel::GenericSend;
73use servo_base::id::{ContainerTimingID, LCPCandidateID, PipelineId, WebViewId};
74use servo_base::{Epoch, generic_channel};
75use servo_config::pref;
76use servo_constellation_traits::{
77 NavigationHistoryBehavior, PaintMetricEvent, ScriptToConstellationMessage,
78};
79use servo_media::{ClientContextId, ServoMedia};
80use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
81use style::attr::AttrValue;
82use style::context::QuirksMode;
83use style::dom::OpaqueNode;
84use style::invalidation::element::restyle_hints::RestyleHint;
85use style::selector_parser::Snapshot;
86use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
87use style::str::{split_html_space_chars, str_join};
88use style::stylesheet_set::DocumentStylesheetSet;
89use style::stylesheets::{Origin, OriginSet, Stylesheet};
90use style::stylist::Stylist;
91use stylo_atoms::Atom;
92use time::Duration as TimeDuration;
93use url::{Host, Position};
94
95use crate::css::stylesheet_loader::StylesheetContextId;
96use crate::css::stylesheet_set::StylesheetSetRef;
97use crate::dom::animationtimeline::AnimationTimeline;
98use crate::dom::attr::Attr;
99use crate::dom::beforeunloadevent::BeforeUnloadEvent;
100use crate::dom::bindings::callback::{ExceptionHandling, TracedCallback};
101use crate::dom::bindings::codegen::Bindings::AnimationFrameProviderBinding::FrameRequestCallback;
102use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEvent_Binding::BeforeUnloadEventMethods;
103use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
104 DocumentMethods, DocumentReadyState, DocumentVisibilityState, ElementCreationOptions,
105 NamedPropertyValue,
106};
107use crate::dom::bindings::codegen::Bindings::ElementBinding::ScrollLogicalPosition;
108use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
109use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
110use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElement_Binding::HTMLIFrameElementMethods;
111#[cfg(any(feature = "webxr", feature = "gamepad"))]
112use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
113use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
114use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
115use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
116use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionName;
117use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
118 SetHTMLOptions, SetHTMLUnsafeOptions,
119};
120use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, WindowMethods};
121use crate::dom::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMethods;
122use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
123use crate::dom::bindings::codegen::UnionTypes::{
124 BooleanOrImportNodeOptions, NodeOrString, StringOrElementCreationOptions, TrustedHTMLOrString,
125};
126use crate::dom::bindings::domname::{
127 self, is_valid_attribute_local_name, is_valid_element_local_name, namespace_from_domstring,
128};
129use crate::dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible};
130use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
131use crate::dom::bindings::num::Finite;
132use crate::dom::bindings::refcounted::Trusted;
133use crate::dom::bindings::reflector::DomGlobal;
134use crate::dom::bindings::root::{
135 Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout, ToLayoutOptional, UnrootedDom,
136};
137use crate::dom::bindings::str::{DOMString, USVString};
138use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
139use crate::dom::bindings::weakref::DOMTracker;
140use crate::dom::bindings::xmlname::matches_name_production;
141use crate::dom::cdatasection::CDATASection;
142use crate::dom::comment::Comment;
143use crate::dom::compositionevent::CompositionEvent;
144use crate::dom::css::cssstylesheet::CSSStyleSheet;
145use crate::dom::css::fontfaceset::FontFaceSet;
146use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
147use crate::dom::customelementregistry::{CustomElementReactionStack, CustomElementRegistry};
148use crate::dom::customevent::CustomEvent;
149use crate::dom::document::accessibility_data::AccessibilityData;
150use crate::dom::document::animation_manager::AnimationManager;
151use crate::dom::document::focus::{DocumentFocusHandler, FocusableArea};
152use crate::dom::document::iframe_collection::IFrameCollection;
153use crate::dom::document::tree_ordered_index_map::TreeOrderedIndexMap;
154use crate::dom::document::websocket::WebSocket;
155use crate::dom::document_embedder_controls::DocumentEmbedderControls;
156use crate::dom::document_event_handler::DocumentEventHandler;
157use crate::dom::documentfragment::DocumentFragment;
158use crate::dom::documentorshadowroot::{
159 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
160};
161use crate::dom::documenttimeline::DocumentTimeline;
162use crate::dom::documenttype::DocumentType;
163use crate::dom::domimplementation::DOMImplementation;
164use crate::dom::domstringlist::DOMStringList;
165use crate::dom::element::attributes::storage::AttrRef;
166use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
167use crate::dom::event::{Event, EventBubbles, EventCancelable};
168use crate::dom::eventtarget::EventTarget;
169use crate::dom::execcommand::basecommand::{CommandName, DefaultSingleLineContainerName};
170use crate::dom::execcommand::execcommands::DocumentExecCommandSupport;
171use crate::dom::focusevent::FocusEvent;
172use crate::dom::globalscope::GlobalScope;
173use crate::dom::hashchangeevent::HashChangeEvent;
174use crate::dom::history::History;
175use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
176use crate::dom::html::htmlareaelement::HTMLAreaElement;
177use crate::dom::html::htmlbaseelement::HTMLBaseElement;
178use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
179use crate::dom::html::htmlelement::HTMLElement;
180use crate::dom::html::htmlembedelement::HTMLEmbedElement;
181use crate::dom::html::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
182use crate::dom::html::htmlheadelement::HTMLHeadElement;
183use crate::dom::html::htmlhtmlelement::HTMLHtmlElement;
184use crate::dom::html::htmliframeelement::HTMLIFrameElement;
185use crate::dom::html::htmlimageelement::HTMLImageElement;
186use crate::dom::html::htmlmediaelement::HTMLMediaElement;
187use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult, TracedScriptResult};
188use crate::dom::html::htmltitleelement::HTMLTitleElement;
189use crate::dom::htmldetailselement::DetailsNameGroups;
190use crate::dom::intersectionobserver::IntersectionObserver;
191use crate::dom::iterators::ShadowIncluding;
192use crate::dom::keyboardevent::KeyboardEvent;
193use crate::dom::largestcontentfulpaint::LargestContentfulPaint;
194use crate::dom::location::Location;
195use crate::dom::messageevent::MessageEvent;
196use crate::dom::mouseevent::MouseEvent;
197use crate::dom::node::focus::FocusTrigger;
198use crate::dom::node::treewalker::TreeWalker;
199use crate::dom::node::virtualmethods::vtable_for;
200use crate::dom::node::{Node, NodeDamage, NodeFlags, NodeTraits};
201use crate::dom::nodeiterator::NodeIterator;
202use crate::dom::nodelist::NodeList;
203use crate::dom::pagetransitionevent::PageTransitionEvent;
204use crate::dom::performance::performancecontainertiming::PerformanceContainerTiming;
205use crate::dom::performance::performanceentry::PerformanceEntry;
206use crate::dom::performance::performancepainttiming::PerformancePaintTiming;
207use crate::dom::processinginstruction::ProcessingInstruction;
208use crate::dom::range::Range;
209use crate::dom::resizeobserver::{ResizeObservationDepth, ResizeObserver};
210use crate::dom::sanitizer::Sanitizer;
211use crate::dom::selection::Selection;
212use crate::dom::servoparser::ServoParser;
213use crate::dom::shadowroot::shadowroot::ShadowRoot;
214use crate::dom::storageevent::StorageEvent;
215use crate::dom::text::Text;
216use crate::dom::textevent::TextEvent;
217use crate::dom::touchevent::TouchEvent as DomTouchEvent;
218use crate::dom::touchlist::TouchList;
219use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
220use crate::dom::types::{HTMLCanvasElement, VisibilityStateEntry};
221use crate::dom::uievent::UIEvent;
222use crate::dom::window::Window;
223use crate::dom::window::scrolling_box::{ScrollAxisState, ScrollingBox};
224use crate::dom::windowproxy::WindowProxy;
225use crate::dom::xpathevaluator::XPathEvaluator;
226use crate::dom::xpathexpression::XPathExpression;
227use crate::dom::{FlatTreeParent, RootedPromise, WeakRangeVec};
228use crate::event_loop::document_loader::{DocumentLoader, LoadType};
229use crate::event_loop::script_thread::{ScriptThread, SharedRwLocks};
230use crate::event_loop::timers::{OneshotTimerCallback, OneshotTimers};
231use crate::fetch::fetch::DeferredFetchRecordInvokeState;
232use crate::fetch::network_listener::FetchResponseListener;
233use crate::mime::{APPLICATION, CHARSET};
234use crate::navigation::navigate;
235use crate::runtime::script_runtime::compute_size;
236use crate::scripting::script_module::{ModuleRequest, ModuleStatus};
237use crate::tasks::task_manager::TaskManager;
238use crate::tasks::task_source::TaskSourceName;
239use crate::xpath::parse_expression;
240
241#[derive(Clone, Copy, PartialEq)]
242pub(crate) enum FireMouseEventType {
243 Move,
244 Over,
245 Out,
246 Enter,
247 Leave,
248}
249
250impl FireMouseEventType {
251 pub(crate) fn as_str(&self) -> &str {
252 match *self {
253 FireMouseEventType::Move => "mousemove",
254 FireMouseEventType::Over => "mouseover",
255 FireMouseEventType::Out => "mouseout",
256 FireMouseEventType::Enter => "mouseenter",
257 FireMouseEventType::Leave => "mouseleave",
258 }
259 }
260}
261
262#[derive(JSTraceable, MallocSizeOf)]
263pub(crate) struct RefreshRedirectDue {
264 #[no_trace]
265 pub(crate) url: ServoUrl,
266 pub(crate) from_meta_element: bool,
268}
269
270#[derive(Debug, Copy, Clone)]
271pub(crate) enum AbortReason {
272 PipelineExited,
273 DocumentOpen,
274 Destroy,
275 StopLoading,
276 Navigate,
277}
278
279#[derive(Debug)]
280pub(crate) enum SetParserReason {
281 AboutBlankComplete,
282 ParsingHtmlDocument,
283 ParsingHtmlScriptInput,
284 ParsingHtmlFragment,
285 ParsingXmlDocument,
286 #[expect(dead_code)]
287 Abort(AbortReason),
289 ParsingBytesChunk,
290 FinishingParser,
291}
292
293#[derive(JSTraceable, MallocSizeOf)]
297#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
298struct LCPCandidateAndElement {
299 element: Option<Dom<Element>>,
301 #[no_trace]
302 candidate: LCPCandidate,
303}
304
305#[derive(JSTraceable, MallocSizeOf)]
310#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
311struct ContainerTimingRecordAndElements {
312 identifier: DOMString,
313 root_element: Dom<Element>,
314 last_painted_element: Option<Dom<Element>>,
315 #[no_trace]
316 record: ContainerTimingRecord,
317}
318
319impl RefreshRedirectDue {
320 pub(crate) fn invoke(self, cx: &mut JSContext, global: &GlobalScope) {
322 let window = global
323 .downcast::<Window>()
324 .expect("Queued a RefreshRedirectDue on a non-Window globalscope");
325
326 if self.from_meta_element &&
333 window.Document().has_active_sandboxing_flag(
334 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
335 )
336 {
337 return;
338 }
339 let load_data = window.load_data_for_document(self.url, window.pipeline_id());
340 navigate(
341 cx,
342 window,
343 NavigationHistoryBehavior::Replace,
344 false,
345 load_data,
346 );
347 }
348}
349
350#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
351pub(crate) enum IsHTMLDocument {
352 HTMLDocument,
353 NonHTMLDocument,
354}
355
356#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
357pub(crate) enum TheEndLoadingPhase {
358 #[default]
359 Initial,
360 ProcessingDeferredScripts,
361 ProcessingAsSoonAsPossibleScripts,
362 WaitingForLoadEventBlockers,
363 Done,
364}
365
366#[derive(JSTraceable, MallocSizeOf)]
368pub(crate) enum DeclarativeRefresh {
369 PendingLoad {
370 #[no_trace]
371 url: ServoUrl,
372 time: u64,
373 from_meta_element: bool,
375 },
376 CreatedAfterLoad,
377}
378
379#[derive(JSTraceable, MallocSizeOf, PartialEq)]
380#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
381struct PendingScrollEvent {
382 target: Dom<EventTarget>,
384 #[no_trace]
386 event: Atom,
387}
388
389impl PendingScrollEvent {
390 fn equivalent(&self, target: &EventTarget, event: &Atom) -> bool {
391 &*self.target == target && self.event == *event
392 }
393}
394
395#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf)]
398pub(crate) struct RenderingUpdateReason(u8);
399
400bitflags! {
401 impl RenderingUpdateReason: u8 {
402 const ResizeObserverStartedObservingTarget = 1 << 0;
405 const IntersectionObserverStartedObservingTarget = 1 << 1;
408 const FontReadyPromiseFulfilled = 1 << 2;
412 }
413}
414
415#[derive(Clone, Debug, Default, MallocSizeOf)]
417pub(crate) struct NavigationTiming {
418 pub(crate) dom_loading: Cell<Option<CrossProcessInstant>>,
419 pub(crate) navigation_start: Cell<Option<CrossProcessInstant>>,
421 pub(crate) unload_event_start: Cell<Option<CrossProcessInstant>>,
423 pub(crate) unload_event_end: Cell<Option<CrossProcessInstant>>,
425 pub(crate) dom_interactive: Cell<Option<CrossProcessInstant>>,
427 pub(crate) dom_content_loaded_event_start: Cell<Option<CrossProcessInstant>>,
429 pub(crate) dom_content_loaded_event_end: Cell<Option<CrossProcessInstant>>,
431 pub(crate) dom_complete: Cell<Option<CrossProcessInstant>>,
433 pub(crate) load_event_start: Cell<Option<CrossProcessInstant>>,
435 pub(crate) load_event_end: Cell<Option<CrossProcessInstant>>,
437 pub(crate) top_level_dom_complete: Cell<Option<CrossProcessInstant>>,
439}
440
441#[dom_struct]
443pub(crate) struct Document {
444 node: Node,
445 document_or_shadow_root: DocumentOrShadowRoot,
446 window: Dom<Window>,
447 implementation: MutNullableDom<DOMImplementation>,
448 #[no_trace]
449 content_type: Mime,
450 last_modified: Option<String>,
451 #[no_trace]
452 encoding: Cell<&'static Encoding>,
453 has_browsing_context: bool,
454 is_html_document: bool,
455 #[no_trace]
456 activity: Cell<DocumentActivity>,
457 #[no_trace]
459 url: AtomicRefCell<ServoUrl>,
460 #[no_trace]
462 about_base_url: DomRefCell<Option<ServoUrl>>,
463 #[ignore_malloc_size_of = "defined in selectors"]
464 #[no_trace]
465 quirks_mode: Cell<QuirksMode>,
466 event_handler: DocumentEventHandler,
468 focus_handler: DocumentFocusHandler,
470 embedder_controls: DocumentEmbedderControls,
472 id_map: TreeOrderedIndexMap,
473 name_map: TreeOrderedIndexMap,
474 tag_map: DomRefCell<HashMapTracedValues<LocalName, Dom<HTMLCollection>, FxBuildHasher>>,
475 tagns_map: DomRefCell<HashMapTracedValues<QualName, Dom<HTMLCollection>, FxBuildHasher>>,
476 classes_map: DomRefCell<HashMapTracedValues<Vec<Atom>, Dom<HTMLCollection>>>,
477 images: MutNullableDom<HTMLCollection>,
478 embeds: MutNullableDom<HTMLCollection>,
479 links: MutNullableDom<HTMLCollection>,
480 forms: MutNullableDom<HTMLCollection>,
481 scripts: MutNullableDom<HTMLCollection>,
482 anchors: MutNullableDom<HTMLCollection>,
483 applets: MutNullableDom<HTMLCollection>,
484 iframes: IFrameCollection,
486 #[no_trace]
490 shared_style_locks: SharedRwLocks,
491 #[custom_trace]
493 stylesheets: DomRefCell<DocumentStylesheetSet<ServoStylesheetInDocument>>,
494 stylesheet_list: MutNullableDom<StyleSheetList>,
495 ready_state: Cell<DocumentReadyState>,
496 current_script: MutNullableDom<HTMLScriptElement>,
498 #[no_trace]
499 current_the_end_loading_phase: Cell<TheEndLoadingPhase>,
500 pending_parsing_blocking_script: DomRefCell<Option<PendingScript>>,
502 script_blocking_stylesheet_set: DomRefCell<IndexSet<StylesheetContextId>>,
505 render_blocking_element_count: Cell<u32>,
508 deferred_scripts: PendingInOrderScriptVec,
510 asap_in_order_scripts_list: PendingInOrderScriptVec,
512 asap_scripts_set: DomRefCell<Vec<Dom<HTMLScriptElement>>>,
514 animation_frame_ident: Cell<u32>,
517 animation_frame_list: DomRefCell<VecDeque<(u32, Option<AnimationFrameCallback>)>>,
520 running_animation_callbacks: Cell<bool>,
525 loader: DomRefCell<DocumentLoader>,
527 current_parser: MutNullableDom<ServoParser>,
529 base_element: MutNullableDom<HTMLBaseElement>,
531 target_base_element: MutNullableDom<HTMLBaseElement>,
533 appropriate_template_contents_owner_document: MutNullableDom<Document>,
536 pending_restyles: DomRefCell<FxHashMap<Dom<Element>, NoTrace<PendingRestyle>>>,
539 #[no_trace]
543 needs_restyle: Cell<RestyleReason>,
544 #[no_trace]
546 origin: DomRefCell<MutableOrigin>,
547 referrer: Option<String>,
549 target_element: MutNullableDom<Element>,
551 #[no_trace]
553 #[conditional_malloc_size_of]
554 policy_container: DomRefCell<StdArc<PolicyContainer>>,
555 #[no_trace]
557 preloaded_resources: DomRefCell<PreloadedResources>,
558 ignore_destructive_writes_counter: Cell<u32>,
560 ignore_opens_during_unload_counter: Cell<u32>,
562 spurious_animation_frames: Cell<u8>,
566
567 fullscreen_element: MutNullableDom<Element>,
569 form_id_listener_map:
576 DomRefCell<HashMapTracedValues<Atom, HashSet<Dom<Element>>, FxBuildHasher>>,
577 #[no_trace]
578 interactive_time: DomRefCell<ProgressiveWebMetrics>,
579 #[no_trace]
580 tti_window: DomRefCell<InteractiveWindow>,
581 throw_on_dynamic_markup_insertion_counter: Cell<u64>,
583 page_showing: Cell<bool>,
585 salvageable: Cell<bool>,
587 active_parser_was_aborted: Cell<bool>,
589 fired_unload: Cell<bool>,
591 responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>,
593
594 #[no_trace]
597 #[conditional_malloc_size_of]
598 navigation_timing: Rc<NavigationTiming>,
599
600 #[no_trace]
602 resource_fetch_timing: RefCell<Option<ResourceFetchTiming>>,
603
604 script_and_layout_blockers: Cell<u32>,
606 #[ignore_malloc_size_of = "Measuring trait objects is hard"]
608 delayed_tasks: DomRefCell<Vec<Box<dyn NonSendTaskBox>>>,
609 completely_loaded: Cell<bool>,
611 shadow_roots: DomRefCell<FxHashSet<Dom<ShadowRoot>>>,
613 shadow_roots_styles_changed: Cell<bool>,
615 media_controls: DomRefCell<HashMap<String, Dom<ShadowRoot>>>,
621 dirty_canvases: DomRefCell<Vec<Dom<HTMLCanvasElement>>>,
624 has_pending_animated_image_update: Cell<bool>,
626 selection: MutNullableDom<Selection>,
628 timeline: Dom<DocumentTimeline>,
631 animation_manager: AnimationManager,
633 dirty_root: MutNullableDom<Element>,
635 declarative_refresh: DomRefCell<Option<DeclarativeRefresh>>,
637 resize_observers: DomRefCell<Vec<Dom<ResizeObserver>>>,
646 fonts: MutNullableDom<FontFaceSet>,
649 visibility_state: Cell<DocumentVisibilityState>,
651 status_code: Option<u16>,
653 is_initial_about_blank: Cell<bool>,
655 allow_declarative_shadow_roots: Cell<bool>,
657 #[no_trace]
659 inherited_insecure_requests_policy: Cell<Option<InsecureRequestsPolicy>>,
660 has_trustworthy_ancestor_origin: Cell<bool>,
662 intersection_observer_task_queued: Cell<bool>,
664 intersection_observers: DomRefCell<Vec<Dom<IntersectionObserver>>>,
676 highlighted_dom_node: MutNullableDom<Node>,
678 lcp_candidates: DomRefCell<HashMapTracedValues<LCPCandidateID, LCPCandidateAndElement>>,
680 #[no_trace]
683 paint_timing_info: Cell<PaintTimingInfo>,
684 container_timing_records:
686 DomRefCell<HashMapTracedValues<ContainerTimingID, ContainerTimingRecordAndElements>>,
687 #[no_trace]
693 container_first_render_times: DomRefCell<HashMap<OpaqueNode, CrossProcessInstant>>,
694 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
698 pending_scroll_events: DomRefCell<Vec<PendingScrollEvent>>,
702 rendering_update_reasons: Cell<RenderingUpdateReason>,
704 waiting_on_canvas_image_updates: Cell<bool>,
708 root_removal_noted: Cell<bool>,
710 #[no_trace]
718 current_rendering_epoch: Cell<Epoch>,
719 #[conditional_malloc_size_of]
721 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
722 #[no_trace]
723 active_sandboxing_flag_set: Cell<SandboxingFlagSet>,
725 #[no_trace]
726 creation_sandboxing_flag_set: Cell<SandboxingFlagSet>,
733 #[no_trace]
735 favicon: RefCell<Option<Image>>,
736
737 websockets: DOMTracker<WebSocket>,
739
740 active_media_elements: DomRefCell<FxHashSet<Dom<HTMLMediaElement>>>,
743
744 details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
746
747 #[no_trace]
749 protocol_handler_automation_mode: RefCell<CustomHandlersAutomationMode>,
750
751 layout_animations_test_enabled: bool,
753
754 #[no_trace]
756 state_override: DomRefCell<FxHashMap<CommandName, bool>>,
757
758 #[no_trace]
760 value_override: DomRefCell<FxHashMap<CommandName, DOMString>>,
761
762 #[no_trace]
764 default_single_line_container_name: Cell<DefaultSingleLineContainerName>,
765
766 css_styling_flag: Cell<bool>,
768
769 accessibility_data: DomRefCell<AccessibilityData>,
771
772 iframe_load_in_progress: Cell<bool>,
774 mute_iframe_load: Cell<bool>,
776
777 timers: OneshotTimers,
780
781 #[no_trace]
782 pipeline_id: PipelineId,
783
784 #[conditional_malloc_size_of]
786 task_manager: Rc<TaskManager>,
787
788 #[ignore_malloc_size_of = "ImageCache"]
789 #[no_trace]
790 image_cache: StdArc<dyn ImageCache>,
791
792 history: MutNullableDom<History>,
794
795 #[no_trace]
797 theme: Cell<Option<Theme>>,
798
799 #[no_trace]
801 theme_override: Cell<Option<Theme>>,
802
803 #[no_trace]
805 default_language: AtomicRefCell<Option<String>>,
806
807 window_detached: Cell<bool>,
810
811 ancestor_origins_list: MutNullableDom<DOMStringList>,
813
814 #[no_trace]
816 internal_ancestor_origin_objects_list: RefCell<Option<Vec<ImmutableOrigin>>>,
817
818 live_ranges: WeakRangeVec,
821
822 #[ignore_malloc_size_of = "mozjs"]
825 module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
826}
827
828impl Document {
829 pub(crate) fn module_map(
830 &self,
831 ) -> &DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>> {
832 &self.module_map
833 }
834
835 pub(crate) fn history(&self, cx: &mut JSContext) -> DomRoot<History> {
836 self.history.or_init(|| History::new(cx, &self.window))
837 }
838
839 pub(crate) fn image_cache(&self) -> StdArc<dyn ImageCache> {
840 self.image_cache.clone()
841 }
842
843 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
844 self.task_manager.clone()
845 }
846
847 pub(crate) fn timers(&self) -> &OneshotTimers {
848 &self.timers
849 }
850
851 pub(crate) fn pipeline_id(&self) -> PipelineId {
852 self.pipeline_id
853 }
854
855 fn fully_exit_fullscreen(&self, cx: &mut JSContext) {
857 if self.fullscreen_element().is_none() {
860 return;
861 };
862
863 let _ = self.exit_fullscreen(cx);
868 }
869
870 fn unloading_cleanup_steps(&self, cx: &mut JSContext) {
872 self.fully_exit_fullscreen(cx);
875
876 if self.close_outstanding_websockets() {
879 self.salvageable.set(false);
881 }
882
883 self.clean_up_media_elements(cx.no_gc());
884
885 if !self.salvageable.get() && !self.window_detached() {
890 let global_scope = self.window.as_global_scope();
891
892 global_scope.close_event_sources();
894
895 self.timers.clear();
897
898 let msg = ScriptToConstellationMessage::DiscardDocument;
900 let _ = global_scope.script_to_constellation_chan().send(msg);
901 }
902 }
903
904 pub(crate) fn track_websocket(&self, websocket: &WebSocket) {
905 self.websockets.track(websocket);
906 }
907
908 fn close_outstanding_websockets(&self) -> bool {
909 let mut closed_any_websocket = false;
910 self.websockets.for_each(|websocket: DomRoot<WebSocket>| {
911 if websocket.make_disappear() {
912 closed_any_websocket = true;
913 }
914 });
915 closed_any_websocket
916 }
917
918 pub(crate) fn register_active_media_element(&self, element: &HTMLMediaElement) {
919 self.active_media_elements
920 .borrow_mut()
921 .insert(Dom::from_ref(element));
922 }
923
924 pub(crate) fn unregister_active_media_element(&self, element: &HTMLMediaElement) {
925 self.active_media_elements
926 .borrow_mut()
927 .remove(&Dom::from_ref(element));
928 }
929
930 fn clean_up_media_elements(&self, no_gc: &NoGC) {
931 let media_elements: Vec<DomRoot<HTMLMediaElement>> = self
932 .active_media_elements
933 .borrow()
934 .iter()
935 .map(|e| DomRoot::from_ref(&**e))
936 .collect();
937 self.active_media_elements.borrow_mut().clear();
938 for element in &media_elements {
939 element.clean_up_on_document_unload(self, no_gc);
940 }
941 }
942
943 fn document_element_changed(&self) {
944 if self.GetDocumentElement().is_some() {
945 self.root_removal_noted.set(false);
948 } else if !self.root_removal_noted.get() {
949 self.add_restyle_reason(RestyleReason::DOMChanged);
952 self.root_removal_noted.set(true);
953 }
954 }
955
956 pub(crate) fn note_dirty_element(&self, no_gc: &NoGC, element: &Element) {
971 let node = element.upcast::<Node>();
972
973 debug_assert!(*node.owner_doc() == *self);
974 if !node.is_connected() {
975 return;
976 }
977
978 let parent_element = match node.parent_in_flat_tree(no_gc) {
979 FlatTreeParent::Parent(parent) => UnrootedDom::downcast::<Element>(parent),
980 FlatTreeParent::NotInFlatTree | FlatTreeParent::RootNode => return,
981 };
982
983 if let Some(parent_element) = parent_element {
987 if !parent_element.is_styled() {
990 return;
991 }
992 if parent_element.is_display_none() {
995 return;
996 }
997 }
998
999 let Some(old_dirty_root) = self.dirty_root.get() else {
1000 node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
1001 self.set_dirty_root(no_gc, Some(element));
1002 return;
1003 };
1004
1005 let old_dirty_root_node = old_dirty_root.upcast::<Node>();
1006 for ancestor in element
1007 .upcast::<Node>()
1008 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
1009 {
1010 if !ancestor.is::<Element>() {
1012 break;
1013 }
1014
1015 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
1016 return;
1017 }
1018
1019 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
1020
1021 if old_dirty_root_node == &**ancestor {
1025 return;
1026 }
1027 }
1028
1029 let common_element_ancestor = old_dirty_root_node
1030 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
1031 .skip(1) .find_map(|ancestor| {
1033 let element = ancestor.downcast::<Element>().map(DomRoot::from_ref)?;
1035 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
1036 return Some(element);
1037 }
1038 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
1039 None
1040 });
1041
1042 let Some(new_dirty_root) = common_element_ancestor else {
1046 let new_dirty_root = self.GetDocumentElement();
1047 if let Some(new_dirty_root) = new_dirty_root.as_ref() {
1048 new_dirty_root
1049 .upcast::<Node>()
1050 .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
1051 }
1052 self.set_dirty_root(no_gc, new_dirty_root.as_deref());
1053 return;
1054 };
1055
1056 for ancestor in new_dirty_root
1060 .upcast::<Node>()
1061 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
1062 .skip(1)
1063 {
1064 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
1065 }
1066
1067 self.set_dirty_root(no_gc, Some(&*new_dirty_root));
1068 }
1069
1070 fn set_dirty_root(&self, no_gc: &NoGC, new_dirty_root: Option<&Element>) {
1071 debug_assert!(new_dirty_root.as_ref().is_none_or(|new_dirty_root| {
1073 new_dirty_root
1074 .upcast::<Node>()
1075 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
1076 .skip(1)
1077 .all(|node| !node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS))
1078 }));
1079 self.dirty_root.set(new_dirty_root);
1080 }
1081
1082 pub(crate) fn take_dirty_root(&self) -> Option<DomRoot<Element>> {
1083 self.dirty_root.take()
1084 }
1085
1086 #[inline]
1087 pub(crate) fn loader(&self) -> Ref<'_, DocumentLoader> {
1088 self.loader.borrow()
1089 }
1090
1091 #[inline]
1092 pub(crate) fn loader_mut(&self) -> RefMut<'_, DocumentLoader> {
1093 self.loader.borrow_mut()
1094 }
1095
1096 #[inline]
1097 pub(crate) fn has_browsing_context(&self) -> bool {
1098 self.has_browsing_context
1099 }
1100
1101 #[inline]
1103 pub(crate) fn browsing_context(&self) -> Option<DomRoot<WindowProxy>> {
1104 if self.has_browsing_context {
1105 self.window.undiscarded_window_proxy()
1106 } else {
1107 None
1108 }
1109 }
1110
1111 pub(crate) fn webview_id(&self) -> WebViewId {
1112 self.window.webview_id()
1113 }
1114
1115 #[inline]
1116 pub(crate) fn window(&self) -> &Window {
1117 &self.window
1118 }
1119
1120 #[inline]
1121 pub(crate) fn is_html_document(&self) -> bool {
1122 self.is_html_document
1123 }
1124
1125 pub(crate) fn is_xhtml_document(&self) -> bool {
1126 self.content_type.matches(APPLICATION, "xhtml+xml")
1127 }
1128
1129 pub(crate) fn is_fully_active(&self) -> bool {
1131 self.is_active() &&
1135 (self.window.is_top_level() || self.activity.get() == DocumentActivity::FullyActive)
1136 }
1137
1138 pub(crate) fn is_active(&self) -> bool {
1140 self.browsing_context().is_some() &&
1146 !self.window_detached() &&
1147 self.activity.get() != DocumentActivity::Inactive
1148 }
1149
1150 #[inline]
1151 pub(crate) fn current_rendering_epoch(&self) -> Epoch {
1152 self.current_rendering_epoch.get()
1153 }
1154
1155 #[inline]
1157 pub(crate) fn selection(&self) -> Option<DomRoot<Selection>> {
1158 self.selection.get()
1159 }
1160
1161 pub(crate) fn set_activity(&self, cx: &mut JSContext, activity: DocumentActivity) {
1162 assert!(self.has_browsing_context);
1164 if activity == self.activity.get() {
1165 return;
1166 }
1167
1168 self.activity.set(activity);
1170 let media = ServoMedia::get();
1171 let pipeline_id = self.window().pipeline_id();
1172 let client_context_id =
1173 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
1174
1175 if activity != DocumentActivity::FullyActive {
1176 if !self.window_detached() {
1177 self.window().suspend(cx);
1178 }
1179 media.suspend(&client_context_id);
1180 return;
1181 }
1182
1183 if self.window_detached() {
1184 return;
1185 }
1186
1187 self.title_changed();
1188 self.notify_embedder_favicon();
1189 self.dirty_all_nodes(cx.no_gc());
1190 self.window().resume(cx);
1191 media.resume(&client_context_id);
1192
1193 if self.ready_state.get() != DocumentReadyState::Complete {
1194 return;
1195 }
1196
1197 let document = Trusted::new(self);
1201 self.owner_global()
1202 .task_manager()
1203 .dom_manipulation_task_source()
1204 .queue(task!(fire_pageshow_event: move |cx| {
1205 let document = document.root();
1206 let window = document.window();
1207 if document.page_showing.get() {
1209 return;
1210 }
1211 document.page_showing.set(true);
1213 document.update_visibility_state(cx, DocumentVisibilityState::Visible);
1215 let event = PageTransitionEvent::new(
1218 cx,
1219 window,
1220 atom!("pageshow"),
1221 false, false, true, );
1225 let event = event.upcast::<Event>();
1226 event.set_trusted(true);
1227 window.dispatch_event_with_target_override(cx, event);
1228 }))
1229 }
1230
1231 pub(crate) fn origin(&self) -> Ref<'_, MutableOrigin> {
1232 self.origin.borrow()
1233 }
1234
1235 pub(crate) fn paint_timing_eligible(&self) -> bool {
1237 if self.window().is_top_level() {
1241 return true;
1242 }
1243 if let Some(top_level_document) = self.window().top_level_document_if_local() {
1246 return self.origin().same_origin(&top_level_document.origin());
1250 };
1251 false
1252 }
1253
1254 pub(crate) fn mark_as_internal(&self) {
1257 *self.origin.borrow_mut() = MutableOrigin::new(ImmutableOrigin::new_opaque());
1258 self.window().update_jsprincipals_from_document(self);
1259 }
1260
1261 pub(crate) fn set_protocol_handler_automation_mode(&self, mode: CustomHandlersAutomationMode) {
1262 *self.protocol_handler_automation_mode.borrow_mut() = mode;
1263 }
1264
1265 pub(crate) fn url(&self) -> ServoUrl {
1267 self.url.borrow().clone()
1268 }
1269
1270 pub(crate) fn set_url(&self, url: ServoUrl) {
1271 *self.url.borrow_mut() = url;
1272 }
1273
1274 pub(crate) fn about_base_url(&self) -> Option<ServoUrl> {
1275 self.about_base_url.borrow().clone()
1276 }
1277
1278 pub(crate) fn set_about_base_url(&self, about_base_url: Option<ServoUrl>) {
1279 *self.about_base_url.borrow_mut() = about_base_url;
1280 }
1281
1282 pub(crate) fn fallback_base_url(&self) -> ServoUrl {
1284 let document_url = self.url();
1285 if document_url.as_str() == "about:srcdoc" {
1287 return self
1290 .about_base_url()
1291 .expect("about:srcdoc page should always have an about base URL");
1292 }
1293
1294 if document_url.matches_about_blank() &&
1297 let Some(about_base_url) = self.about_base_url()
1298 {
1299 return about_base_url;
1300 }
1301
1302 document_url
1304 }
1305
1306 pub(crate) fn base_url(&self) -> ServoUrl {
1308 match self.base_element() {
1309 None => self.fallback_base_url(),
1311 Some(base) => base.frozen_base_url(),
1313 }
1314 }
1315
1316 pub(crate) fn add_restyle_reason(&self, reason: RestyleReason) {
1317 self.needs_restyle.set(self.needs_restyle.get() | reason)
1318 }
1319
1320 pub(crate) fn clear_restyle_reasons(&self) {
1321 self.needs_restyle.set(RestyleReason::empty());
1322 }
1323
1324 pub(crate) fn stylesheets_changed_since_last_reflow(&self) -> bool {
1325 self.stylesheets.borrow().has_changed()
1326 }
1327
1328 pub(crate) fn restyle_reason(&self, no_gc: &NoGC) -> RestyleReason {
1329 let mut condition = self.needs_restyle.get();
1330 if self.stylesheets_changed_since_last_reflow() {
1331 condition.insert(RestyleReason::StylesheetsChanged);
1332 }
1333
1334 if let Some(root) = self.get_document_element_unrooted(no_gc) &&
1338 root.has_dirty_descendants()
1339 {
1340 condition.insert(RestyleReason::DOMChanged);
1341 }
1342
1343 if !self.pending_restyles.borrow().is_empty() {
1344 condition.insert(RestyleReason::PendingRestyles);
1345 }
1346
1347 condition
1348 }
1349
1350 pub(crate) fn base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1352 self.base_element.get()
1353 }
1354
1355 pub(crate) fn target_base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1357 self.target_base_element.get()
1358 }
1359
1360 pub(crate) fn refresh_base_element(&self, cx: &mut JSContext) {
1362 if let Some(base_element) = self.base_element.get() {
1363 base_element.clear_frozen_base_url();
1364 }
1365 let new_base_element = self
1366 .upcast::<Node>()
1367 .traverse_preorder(ShadowIncluding::No)
1368 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1369 .find(|element| {
1370 element
1371 .upcast::<Element>()
1372 .has_attribute(&local_name!("href"))
1373 });
1374 if let Some(ref new_base_element) = new_base_element {
1375 new_base_element.set_frozen_base_url(cx);
1376 }
1377 self.base_element.set(new_base_element.as_deref());
1378
1379 let new_target_base_element = self
1380 .upcast::<Node>()
1381 .traverse_preorder(ShadowIncluding::No)
1382 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1383 .next();
1384 self.target_base_element
1385 .set(new_target_base_element.as_deref());
1386 }
1387
1388 pub(crate) fn quirks_mode(&self) -> QuirksMode {
1389 self.quirks_mode.get()
1390 }
1391
1392 pub(crate) fn set_quirks_mode(&self, new_mode: QuirksMode) {
1393 let old_mode = self.quirks_mode.replace(new_mode);
1394
1395 if old_mode != new_mode {
1396 self.window.layout_mut().set_quirks_mode(new_mode);
1397 }
1398 }
1399
1400 pub(crate) fn encoding(&self) -> &'static Encoding {
1401 self.encoding.get()
1402 }
1403
1404 pub(crate) fn set_encoding(&self, encoding: &'static Encoding) {
1405 self.encoding.set(encoding);
1406 }
1407
1408 pub(crate) fn content_and_heritage_changed(&self, no_gc: &NoGC, node: &Node) {
1409 if node.is::<Document>() {
1410 self.document_element_changed();
1411 }
1412
1413 node.dirty(no_gc, NodeDamage::ContentOrHeritage);
1418 }
1419
1420 pub(crate) fn unregister_element_id(&self, cx: &mut JSContext, id: &Atom) {
1422 self.id_map.remove(id);
1423 self.reset_form_owner_for_listeners(cx, id);
1424 }
1425
1426 pub(crate) fn register_element_id(&self, cx: &mut JSContext, element: &Element, id: &Atom) {
1428 self.id_map.add(id, element);
1429 self.reset_form_owner_for_listeners(cx, id);
1430 }
1431
1432 pub(crate) fn unregister_element_name(&self, name: &Atom) {
1434 self.name_map.remove(name);
1435 }
1436
1437 pub(crate) fn register_element_name(&self, element: &Element, name: &Atom) {
1439 self.name_map.add(name, element);
1440 }
1441
1442 pub(crate) fn register_form_id_listener<T: ?Sized + FormControl>(
1443 &self,
1444 id: DOMString,
1445 listener: &T,
1446 ) {
1447 let mut map = self.form_id_listener_map.borrow_mut();
1448 let listener = listener.to_element();
1449 let set = map.entry(Atom::from(id)).or_default();
1450 set.insert(Dom::from_ref(listener));
1451 }
1452
1453 pub(crate) fn unregister_form_id_listener<T: ?Sized + FormControl>(
1454 &self,
1455 id: DOMString,
1456 listener: &T,
1457 ) {
1458 let mut map = self.form_id_listener_map.borrow_mut();
1459 if let Occupied(mut entry) = map.entry(Atom::from(id)) {
1460 entry
1461 .get_mut()
1462 .remove(&Dom::from_ref(listener.to_element()));
1463 if entry.get().is_empty() {
1464 entry.remove();
1465 }
1466 }
1467 }
1468
1469 fn find_a_potential_indicated_element(
1471 &self,
1472 cx: &mut JSContext,
1473 fragment: &str,
1474 ) -> Option<DomRoot<Element>> {
1475 self.get_element_by_id(cx.no_gc(), &Atom::from(fragment))
1479 .or_else(|| self.get_anchor_by_name(cx, fragment))
1483 }
1484
1485 fn select_indicated_part(&self, cx: &mut JSContext, fragment: &str) -> Option<DomRoot<Node>> {
1488 if fragment.is_empty() {
1498 return Some(DomRoot::from_ref(self.upcast()));
1499 }
1500 if let Some(potential_indicated_element) =
1502 self.find_a_potential_indicated_element(cx, fragment)
1503 {
1504 return Some(DomRoot::upcast(potential_indicated_element));
1506 }
1507 let fragment_bytes = percent_decode(fragment.as_bytes());
1509 let Ok(decoded_fragment) = fragment_bytes.decode_utf8() else {
1511 return None;
1512 };
1513 if let Some(potential_indicated_element) =
1515 self.find_a_potential_indicated_element(cx, &decoded_fragment)
1516 {
1517 return Some(DomRoot::upcast(potential_indicated_element));
1519 }
1520 if decoded_fragment.eq_ignore_ascii_case("top") {
1522 return Some(DomRoot::from_ref(self.upcast()));
1523 }
1524 None
1526 }
1527
1528 pub(crate) fn scroll_to_the_fragment(&self, cx: &mut JSContext, fragment: &str) {
1530 let Some(indicated_part) = self.select_indicated_part(cx, fragment) else {
1535 self.set_target_element(cx.no_gc(), None);
1536 return;
1537 };
1538 if *indicated_part == *self.upcast() {
1540 self.set_target_element(cx.no_gc(), None);
1542 self.window.scroll(cx, 0.0, 0.0, ScrollBehavior::Instant);
1547 return;
1549 }
1550 let Some(target) = indicated_part.downcast::<Element>() else {
1553 unreachable!("Indicated part should always be an element");
1555 };
1556 self.set_target_element(cx.no_gc(), Some(target));
1558 target.scroll_into_view_with_options(
1562 cx,
1563 ScrollBehavior::Auto,
1564 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Start),
1565 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Nearest),
1566 None,
1567 None,
1568 );
1569
1570 indicated_part.run_the_focusing_steps(
1573 cx,
1574 Some(FocusableArea::Viewport),
1575 FocusTrigger::Other,
1576 );
1577
1578 self.focus_handler()
1580 .set_sequential_focus_navigation_starting_point(target.upcast());
1581 }
1582
1583 fn get_anchor_by_name(&self, cx: &mut JSContext, name: &str) -> Option<DomRoot<Element>> {
1584 let document_element = self.GetDocumentElement()?;
1585 self.name_map
1586 .get_all(cx.no_gc(), document_element.upcast(), &Atom::from(name))
1587 .iter()
1588 .find(|element| element.is::<HTMLAnchorElement>())
1589 .map(|element| DomRoot::from_ref(&**element))
1590 }
1591
1592 pub(crate) fn notify_embedder_of_load_completion(&self) {
1593 if self.window().is_top_level() {
1594 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1595 self.webview_id(),
1596 LoadStatus::Complete,
1597 ));
1598 }
1599 }
1600
1601 pub(crate) fn set_document_readiness_to_loading_for_initialization(&self) {
1606 if self.is_initial_about_blank() {
1610 return;
1611 }
1612
1613 update_with_current_instant(&self.navigation_timing.dom_loading);
1617
1618 if self.window.is_top_level() {
1619 let webview_id = self.webview_id();
1620 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1621 webview_id,
1622 LoadStatus::Started,
1623 ));
1624 self.send_to_embedder(EmbedderMsg::Status(webview_id, None));
1625 }
1626
1627 self.ready_state.set(DocumentReadyState::Loading);
1628 }
1629
1630 pub(crate) fn update_the_current_document_readiness(
1632 &self,
1633 cx: &mut JSContext,
1634 state: DocumentReadyState,
1635 ) {
1636 if self.ready_state.get() == state {
1638 return;
1639 }
1640
1641 self.ready_state.set(state);
1643
1644 match state {
1649 DocumentReadyState::Loading => {},
1650 DocumentReadyState::Complete => {
1651 self.notify_embedder_of_load_completion();
1654
1655 update_with_current_instant(&self.navigation_timing.dom_complete);
1660 },
1661 DocumentReadyState::Interactive => {
1662 update_with_current_instant(&self.navigation_timing.dom_interactive)
1667 },
1668 };
1669
1670 self.upcast::<EventTarget>()
1672 .fire_event(cx, atom!("readystatechange"));
1673 }
1674
1675 pub(crate) fn scripting_enabled(&self) -> bool {
1678 self.has_browsing_context() &&
1681 !self.has_active_sandboxing_flag(
1685 SandboxingFlagSet::SANDBOXED_SCRIPTS_BROWSING_CONTEXT_FLAG,
1686 )
1687 }
1688
1689 pub(crate) fn title_changed(&self) {
1691 if self.browsing_context().is_some() {
1692 self.send_title_to_embedder();
1693 let title = String::from(self.Title());
1694 self.window
1695 .send_to_constellation(ScriptToConstellationMessage::TitleChanged(
1696 self.window.pipeline_id(),
1697 title.clone(),
1698 ));
1699 #[cfg(feature = "devtools")]
1700 if let Some(chan) = self.window.as_global_scope().devtools_chan() {
1701 let _ = chan.send(ScriptToDevtoolsControlMsg::TitleChanged(
1702 self.window.pipeline_id(),
1703 title,
1704 ));
1705 }
1706 }
1707 }
1708
1709 fn title(&self) -> Option<DOMString> {
1713 let title = self.GetDocumentElement().and_then(|root| {
1714 if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
1715 root.upcast::<Node>()
1717 .child_elements()
1718 .find(|node| {
1719 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
1720 })
1721 .map(DomRoot::upcast::<Node>)
1722 } else {
1723 root.upcast::<Node>()
1725 .traverse_preorder(ShadowIncluding::No)
1726 .find(|node| node.is::<HTMLTitleElement>())
1727 }
1728 });
1729
1730 title.map(|title| {
1731 let value = title.child_text_content();
1733 DOMString::from(str_join(value.str().split_html_space_characters(), " "))
1734 })
1735 }
1736
1737 pub(crate) fn send_title_to_embedder(&self) {
1739 let window = self.window();
1740 if window.is_top_level() {
1741 let title = self.title().map(String::from);
1742 self.send_to_embedder(EmbedderMsg::ChangePageTitle(self.webview_id(), title));
1743 }
1744 }
1745
1746 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
1747 let window = self.window();
1748 window.send_to_embedder(msg);
1749 }
1750
1751 pub(crate) fn dirty_all_nodes(&self, no_gc: &NoGC) {
1752 let root = match self.GetDocumentElement() {
1753 Some(root) => root,
1754 None => return,
1755 };
1756 for node in root
1757 .upcast::<Node>()
1758 .traverse_preorder_unrooted(no_gc, ShadowIncluding::Yes)
1759 {
1760 node.dirty(no_gc, NodeDamage::Other)
1761 }
1762 }
1763
1764 pub(crate) fn run_the_scroll_steps(&self, cx: &mut JSContext) {
1766 let boxes_that_were_scrolled: Vec<_> = self
1774 .pending_scroll_events
1775 .borrow()
1776 .iter()
1777 .filter_map(|pending_event| {
1778 if &*pending_event.event == "scroll" {
1779 Some(pending_event.target.as_rooted())
1780 } else {
1781 None
1782 }
1783 })
1784 .collect();
1785
1786 for target in boxes_that_were_scrolled.into_iter() {
1787 let Some(element) = target.downcast::<Element>() else {
1793 continue;
1794 };
1795 let document = element.owner_document();
1796
1797 let mut pending_scroll_events = document.pending_scroll_events.borrow_mut();
1804 let event = "scrollend".into();
1805 if pending_scroll_events
1806 .iter()
1807 .any(|existing| existing.equivalent(&target, &event))
1808 {
1809 continue;
1810 }
1811
1812 pending_scroll_events.push(PendingScrollEvent {
1814 target: target.as_traced(),
1815 event: "scrollend".into(),
1816 });
1817 }
1818
1819 rooted_vec!(let pending_scroll_events <- self.pending_scroll_events.take().into_iter());
1822 for pending_event in pending_scroll_events.iter() {
1823 let event = pending_event.event.clone();
1826 if pending_event.target.is::<Document>() {
1827 pending_event.target.fire_bubbling_event(cx, event);
1828 }
1829 else {
1838 pending_event.target.fire_event(cx, event);
1839 }
1840 }
1841
1842 }
1845
1846 pub(crate) fn handle_viewport_scroll_event(&self) {
1851 self.finish_handle_scroll_event(self.upcast());
1863 }
1864
1865 pub(crate) fn finish_handle_scroll_event(&self, event_target: &EventTarget) {
1870 let event = "scroll".into();
1873 if self
1874 .pending_scroll_events
1875 .borrow()
1876 .iter()
1877 .any(|existing| existing.equivalent(event_target, &event))
1878 {
1879 return;
1880 }
1881
1882 self.pending_scroll_events
1885 .borrow_mut()
1886 .push(PendingScrollEvent {
1887 target: Dom::from_ref(event_target),
1888 event: "scroll".into(),
1889 });
1890 }
1891
1892 pub(crate) fn node_from_nodes_and_strings(
1894 &self,
1895 cx: &mut JSContext,
1896 mut nodes: Vec<NodeOrString>,
1897 ) -> Fallible<DomRoot<Node>> {
1898 if nodes.len() == 1 {
1899 Ok(match nodes.pop().unwrap() {
1900 NodeOrString::Node(node) => node,
1901 NodeOrString::String(string) => DomRoot::upcast(self.CreateTextNode(cx, string)),
1902 })
1903 } else {
1904 let fragment = DomRoot::upcast::<Node>(self.CreateDocumentFragment(cx));
1905 for node in nodes {
1906 match node {
1907 NodeOrString::Node(node) => {
1908 fragment.AppendChild(cx, &node)?;
1909 },
1910 NodeOrString::String(string) => {
1911 let node = DomRoot::upcast::<Node>(self.CreateTextNode(cx, string));
1912 fragment.AppendChild(cx, &node).unwrap();
1915 },
1916 }
1917 }
1918 Ok(fragment)
1919 }
1920 }
1921
1922 pub(crate) fn get_body_attribute(&self, local_name: &LocalName) -> DOMString {
1923 match self.GetBody() {
1924 Some(ref body) if body.is_body_element() => {
1925 body.upcast::<Element>().get_string_attribute(local_name)
1926 },
1927 _ => DOMString::new(),
1928 }
1929 }
1930
1931 pub(crate) fn set_body_attribute(
1932 &self,
1933 cx: &mut JSContext,
1934 local_name: &LocalName,
1935 value: DOMString,
1936 ) {
1937 if let Some(ref body) = self.GetBody().filter(|elem| elem.is_body_element()) {
1938 let body = body.upcast::<Element>();
1939 let value = body.parse_attribute(&ns!(), local_name, value);
1940 body.set_attribute(cx, local_name, value);
1941 }
1942 }
1943
1944 pub(crate) fn set_current_script(&self, script: Option<&HTMLScriptElement>) {
1945 self.current_script.set(script);
1946 }
1947
1948 pub(crate) fn has_a_stylesheet_that_is_blocking_scripts(&self) -> bool {
1950 !self.script_blocking_stylesheet_set.borrow().is_empty()
1951 }
1952
1953 pub(crate) fn add_script_blocking_stylesheet(&self, id: StylesheetContextId) {
1954 self.script_blocking_stylesheet_set.borrow_mut().insert(id);
1955 }
1956
1957 pub(crate) fn remove_script_blocking_stylesheet(&self, id: StylesheetContextId) {
1958 self.script_blocking_stylesheet_set
1959 .borrow_mut()
1960 .shift_remove(&id);
1961 }
1962
1963 pub(crate) fn render_blocking_element_count(&self) -> u32 {
1964 self.render_blocking_element_count.get()
1965 }
1966
1967 pub(crate) fn increment_render_blocking_element_count(&self) {
1969 assert!(self.allows_adding_render_blocking_elements());
1976 let count_cell = &self.render_blocking_element_count;
1977 count_cell.set(count_cell.get() + 1);
1978 }
1979
1980 pub(crate) fn decrement_render_blocking_element_count(&self) {
1982 let count_cell = &self.render_blocking_element_count;
1988 assert!(count_cell.get() > 0);
1989 count_cell.set(count_cell.get() - 1);
1990 }
1991
1992 pub(crate) fn allows_adding_render_blocking_elements(&self) -> bool {
1994 self.is_html_document && self.GetBody().is_none()
1997 }
1998
1999 pub(crate) fn is_render_blocked(&self) -> bool {
2001 self.render_blocking_element_count() > 0
2005 }
2010
2011 pub(crate) fn invalidate_stylesheets(&self, no_gc: &NoGC) {
2012 self.stylesheets.borrow_mut().force_dirty(OriginSet::all());
2013
2014 if let Some(element) = self.GetDocumentElement() {
2018 element.upcast::<Node>().dirty(no_gc, NodeDamage::Style);
2019 }
2020 }
2021
2022 pub(crate) fn has_active_request_animation_frame_callbacks(&self) -> bool {
2025 !self.animation_frame_list.borrow().is_empty()
2026 }
2027
2028 pub(crate) fn request_animation_frame(&self, callback: AnimationFrameCallback) -> u32 {
2030 let ident = self.animation_frame_ident.get() + 1;
2031 self.animation_frame_ident.set(ident);
2032
2033 let had_animation_frame_callbacks;
2034 {
2035 let mut animation_frame_list = self.animation_frame_list.borrow_mut();
2036 had_animation_frame_callbacks = !animation_frame_list.is_empty();
2037 animation_frame_list.push_back((ident, Some(callback)));
2038 }
2039
2040 if !self.running_animation_callbacks.get() && !had_animation_frame_callbacks {
2046 self.window().send_to_constellation(
2047 ScriptToConstellationMessage::ChangeRunningAnimationsState(
2048 AnimationState::AnimationCallbacksPresent,
2049 ),
2050 );
2051 }
2052
2053 ident
2054 }
2055
2056 pub(crate) fn cancel_animation_frame(&self, ident: u32) {
2058 let mut list = self.animation_frame_list.borrow_mut();
2059 if let Some(pair) = list.iter_mut().find(|pair| pair.0 == ident) {
2060 pair.1 = None;
2061 }
2062 }
2063
2064 pub(crate) fn run_the_animation_frame_callbacks(&self, cx: &mut CurrentRealm) {
2066 self.running_animation_callbacks.set(true);
2067 let timing = self.global().performance(cx).Now();
2068
2069 let num_callbacks = self.animation_frame_list.borrow().len();
2070 for _ in 0..num_callbacks {
2071 rooted!(&in(cx) let maybe_callback = self
2072 .animation_frame_list
2073 .borrow_mut()
2074 .pop_front()
2075 .unwrap()
2076 .1);
2077 if let Some(ref callback) = *maybe_callback {
2078 callback.call(cx, self, *timing);
2079 }
2080 }
2081 self.running_animation_callbacks.set(false);
2082
2083 if self.animation_frame_list.borrow().is_empty() {
2084 self.window().send_to_constellation(
2085 ScriptToConstellationMessage::ChangeRunningAnimationsState(
2086 AnimationState::AnimationCallbacksAbsent,
2087 ),
2088 );
2089 }
2090 }
2091
2092 pub(crate) fn policy_container(&self) -> Ref<'_, StdArc<PolicyContainer>> {
2093 self.policy_container.borrow()
2094 }
2095
2096 pub(crate) fn set_policy_container(&self, policy_container: StdArc<PolicyContainer>) {
2097 *self.policy_container.borrow_mut() = policy_container;
2098 }
2099
2100 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
2101 StdArc::make_mut(&mut *self.policy_container.borrow_mut()).set_csp_list(csp_list);
2102 }
2103
2104 pub(crate) fn enforce_csp_policy(&self, policy: CspPolicy) {
2106 let mut csp_list = self.get_csp_list().clone().unwrap_or(CspList(vec![]));
2108 csp_list.push(policy);
2109 StdArc::make_mut(&mut *self.policy_container.borrow_mut()).set_csp_list(Some(csp_list));
2110 }
2111
2112 pub(crate) fn get_csp_list(&self) -> Ref<'_, Option<CspList>> {
2113 Ref::map(self.policy_container.borrow(), |policy_container| {
2114 &policy_container.csp_list
2115 })
2116 }
2117
2118 pub(crate) fn preloaded_resources(&self) -> std::cell::Ref<'_, PreloadedResources> {
2119 self.preloaded_resources.borrow()
2120 }
2121
2122 pub(crate) fn insert_preloaded_resource(&self, key: PreloadKey, preload_id: PreloadId) {
2123 self.preloaded_resources
2124 .borrow_mut()
2125 .insert(key, preload_id);
2126 }
2127
2128 pub(crate) fn fetch_blocking<Listener: FetchResponseListener>(
2129 &self,
2130 load: LoadType,
2131 request: RequestBuilder,
2132 listener: Listener,
2133 ) {
2134 self.loader_mut().add_blocking_load(load);
2135 self.fetch_background(request, listener);
2136 }
2137
2138 pub(crate) fn fetch_background<Listener: FetchResponseListener>(
2139 &self,
2140 request_builder: RequestBuilder,
2141 listener: Listener,
2142 ) {
2143 let networking_task_source = self
2144 .owner_global()
2145 .task_manager()
2146 .networking_task_source()
2147 .to_sendable();
2148 self.window()
2149 .as_global_scope()
2150 .fetch(request_builder, listener, networking_task_source);
2151 }
2152
2153 fn deferred_fetch_control_document(&self) -> DomRoot<Document> {
2155 match self.window().window_proxy().frame_element() {
2156 None => DomRoot::from_ref(self),
2159 Some(container) => container.owner_document().deferred_fetch_control_document(),
2161 }
2162 }
2163
2164 pub(crate) fn available_deferred_fetch_quota(&self, origin: ImmutableOrigin) -> isize {
2166 let control_document = self.deferred_fetch_control_document();
2168 let navigable = control_document.window();
2170 let is_top_level = navigable.is_top_level();
2173 let deferred_fetch_allowed = true;
2177 let deferred_fetch_minimal_allowed = true;
2181 let mut quota = match is_top_level {
2183 true if !deferred_fetch_allowed => 0,
2185 true if !deferred_fetch_minimal_allowed => 640 * 1024,
2187 true => 512 * 1024,
2189 _ if deferred_fetch_allowed => 0,
2193 _ if deferred_fetch_minimal_allowed => 8 * 1024,
2197 _ => 0,
2199 } as isize;
2200 let mut quota_for_request_origin = 64 * 1024_isize;
2202 let deferred_fetches = navigable.as_global_scope().fetch_group().deferred_fetches();
2211 for deferred_fetch in deferred_fetches {
2212 if deferred_fetch.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
2214 continue;
2215 }
2216 let request_length = deferred_fetch.request.total_request_length();
2218 quota -= request_length as isize;
2220 if deferred_fetch.request.url().origin() == origin {
2223 quota_for_request_origin -= request_length as isize;
2224 }
2225 }
2226 if quota <= 0 {
2228 return 0;
2229 }
2230 if quota < quota_for_request_origin {
2232 return quota;
2233 }
2234 quota_for_request_origin
2236 }
2237
2238 pub(crate) fn update_document_for_history_step_application(
2240 &self,
2241 old_url: &ServoUrl,
2242 new_url: &ServoUrl,
2243 ) {
2244 if old_url.as_url()[Position::BeforeFragment..] !=
2274 new_url.as_url()[Position::BeforeFragment..]
2275 {
2276 let window = Trusted::new(self.owner_window().deref());
2277 let old_url = old_url.to_string();
2278 let new_url = new_url.to_string();
2279 self.owner_global()
2280 .task_manager()
2281 .dom_manipulation_task_source()
2282 .queue(task!(hashchange_event: move |cx| {
2283 let window = window.root();
2284 HashChangeEvent::new(
2285 cx,
2286 &window,
2287 atom!("hashchange"),
2288 false,
2289 false,
2290 old_url,
2291 new_url,
2292 )
2293 .upcast::<Event>()
2294 .fire(cx, window.upcast());
2295 }));
2296 }
2297 }
2298
2299 pub(crate) fn finish_load_for_dropped_blocker(&self, load: LoadType) {
2300 let this = Trusted::new(self);
2301 self.owner_global()
2302 .task_manager()
2303 .dom_manipulation_task_source()
2304 .queue(task!(check_finished_load: move |cx| {
2305 this.root().finish_load(load, cx);
2306 }));
2307 }
2308
2309 pub(crate) fn finish_load(&self, load: LoadType, cx: &mut JSContext) {
2312 debug!("Document {:?} got finish_load: {:?}", self.url(), load);
2314 self.loader.borrow_mut().finish_load(&load);
2315
2316 match load {
2317 LoadType::Stylesheet(_) => {
2318 self.process_pending_parsing_blocking_script(cx);
2321
2322 self.process_deferred_scripts(cx);
2324 },
2325 LoadType::PageSource(_) => {
2326 if self.has_browsing_context && self.is_fully_active() {
2329 self.window().allow_layout_if_necessary(cx);
2330 }
2331
2332 self.process_deferred_scripts(cx);
2337 },
2338 _ => {},
2339 }
2340
2341 let document = Trusted::new(self);
2343 self.owner_global()
2344 .task_manager()
2345 .dom_manipulation_task_source()
2346 .queue(task!(wait_for_load_blockers: move |cx| {
2347 document.root().wait_until_load_blockers_have_resolved(cx);
2348 }));
2349 }
2350
2351 pub(crate) fn check_if_unloading_is_cancelled(
2353 &self,
2354 cx: &mut JSContext,
2355 recursive_flag: bool,
2356 ) -> bool {
2357 self.incr_ignore_opens_during_unload_counter();
2360 let beforeunload_event = BeforeUnloadEvent::new(
2362 cx,
2363 &self.window,
2364 atom!("beforeunload"),
2365 EventBubbles::Bubbles,
2366 EventCancelable::Cancelable,
2367 );
2368 let event = beforeunload_event.upcast::<Event>();
2369 event.set_trusted(true);
2370 let event_target = self.window.upcast::<EventTarget>();
2371 let has_listeners = event_target.has_listeners_for(&atom!("beforeunload"));
2372 self.window.dispatch_event_with_target_override(cx, event);
2373 if has_listeners {
2376 self.salvageable.set(false);
2377 }
2378 let mut can_unload = true;
2379 let default_prevented = event.DefaultPrevented();
2381 let return_value_not_empty = !event
2382 .downcast::<BeforeUnloadEvent>()
2383 .unwrap()
2384 .ReturnValue()
2385 .is_empty();
2386 if default_prevented || return_value_not_empty {
2387 let (chan, port) = generic_channel::channel().expect("Failed to create IPC channel!");
2388 let msg = EmbedderMsg::AllowUnload(self.webview_id(), chan);
2389 self.send_to_embedder(msg);
2390 can_unload = port.recv().unwrap() == AllowOrDeny::Allow;
2391 }
2392 if !recursive_flag {
2394 let iframes: Vec<_> = self.iframes().iter().collect();
2397 for iframe in &iframes {
2398 let document = iframe.owner_document();
2400 can_unload = document.check_if_unloading_is_cancelled(cx, true);
2401 if !document.salvageable() {
2402 self.salvageable.set(false);
2403 }
2404 if !can_unload {
2405 break;
2406 }
2407 }
2408 }
2409 self.decr_ignore_opens_during_unload_counter();
2411 can_unload
2412 }
2413
2414 pub(crate) fn unload(&self, cx: &mut JSContext, recursive_flag: bool) {
2416 if self.window_detached() {
2417 return;
2418 }
2419
2420 self.incr_ignore_opens_during_unload_counter();
2423 if self.page_showing.get() {
2425 self.page_showing.set(false);
2427 let event = PageTransitionEvent::new(
2430 cx,
2431 &self.window,
2432 atom!("pagehide"),
2433 false, false, self.salvageable.get(), );
2437 let event = event.upcast::<Event>();
2438 event.set_trusted(true);
2439 self.window.dispatch_event_with_target_override(cx, event);
2440 self.update_visibility_state(cx, DocumentVisibilityState::Hidden);
2442 }
2443 if !self.fired_unload.get() {
2445 let event = Event::new(
2446 cx,
2447 self.window.upcast(),
2448 atom!("unload"),
2449 EventBubbles::Bubbles,
2450 EventCancelable::Cancelable,
2451 );
2452 event.set_trusted(true);
2453 let event_target = self.window.upcast::<EventTarget>();
2454 let has_listeners = event_target.has_listeners_for(&atom!("unload"));
2455 self.window.dispatch_event_with_target_override(cx, &event);
2456 self.fired_unload.set(true);
2457 if has_listeners {
2459 self.salvageable.set(false);
2460 }
2461 }
2462 if !recursive_flag {
2466 let iframes: Vec<_> = self.iframes().iter().collect();
2469 for iframe in &iframes {
2470 let document = iframe.owner_document();
2472 document.unload(cx, true);
2473 if !document.salvageable() {
2474 self.salvageable.set(false);
2475 }
2476 }
2477 }
2478
2479 self.unloading_cleanup_steps(cx);
2481
2482 self.window.as_global_scope().clean_up_all_file_resources();
2484
2485 self.decr_ignore_opens_during_unload_counter();
2487
2488 }
2491
2492 fn completely_finish_loading(&self) {
2494 self.completely_loaded.set(true);
2499 self.notify_constellation_load();
2508
2509 if let Some(DeclarativeRefresh::PendingLoad {
2518 url,
2519 time,
2520 from_meta_element,
2521 }) = &*self.declarative_refresh.borrow()
2522 {
2523 self.window.as_global_scope().schedule_callback(
2524 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
2525 url: url.clone(),
2526 from_meta_element: *from_meta_element,
2527 }),
2528 Duration::from_secs(*time),
2529 );
2530 }
2531 }
2532
2533 fn queue_document_completion(&self, cx: &mut JSContext) {
2535 assert!(!self.is_initial_about_blank());
2539
2540 self.loader.borrow_mut().inhibit_events();
2541
2542 debug!("Document loads are complete.");
2547 let document = Trusted::new(self);
2548 self.owner_global()
2549 .task_manager()
2550 .dom_manipulation_task_source()
2551 .queue(task!(fire_load_event: move |cx| {
2552 let document = document.root();
2553 let window = document.window();
2555 if !window.is_alive() || document.window_detached() {
2556 return;
2557 }
2558
2559 document.update_the_current_document_readiness(cx, DocumentReadyState::Complete);
2561
2562 if document.browsing_context().is_none() {
2564 return;
2565 }
2566
2567 update_with_current_instant(&document.navigation_timing.load_event_start);
2569
2570 let load_event = Event::new(
2572 cx,
2573 window.upcast(),
2574 atom!("load"),
2575 EventBubbles::DoesNotBubble,
2576 EventCancelable::NotCancelable,
2577 );
2578 load_event.set_trusted(true);
2579 debug!("About to dispatch load for {:?}", document.url());
2580 window.dispatch_event_with_target_override(cx, &load_event);
2581
2582 update_with_current_instant(&document.navigation_timing.load_event_end);
2592
2593 document.page_showing.set(true);
2598
2599 let page_show_event = PageTransitionEvent::new(
2601 cx,
2602 window,
2603 atom!("pageshow"),
2604 false, false, false, );
2608 let page_show_event = page_show_event.upcast::<Event>();
2609 page_show_event.set_trusted(true);
2610 page_show_event.fire(cx, window.upcast());
2611
2612 document.completely_finish_loading();
2614
2615 if let Some(fragment) = document.url().fragment() {
2619 document.scroll_to_the_fragment(cx, fragment);
2620 }
2621 }));
2622
2623 #[cfg(feature = "webxr")]
2638 if pref!(dom_webxr_sessionavailable) && self.window.is_top_level() {
2639 self.window.Navigator(cx).Xr(cx).dispatch_sessionavailable();
2640 }
2641 }
2642
2643 pub(crate) fn completely_loaded(&self) -> bool {
2644 self.completely_loaded.get()
2645 }
2646
2647 pub(crate) fn start_the_end_loading_phase(&self) {
2648 if self.is_initial_about_blank() {
2649 self.current_the_end_loading_phase
2651 .set(TheEndLoadingPhase::Done);
2652 } else {
2653 self.current_the_end_loading_phase
2654 .set(TheEndLoadingPhase::ProcessingDeferredScripts);
2655 }
2656 }
2657
2658 pub(crate) fn set_pending_parsing_blocking_script(
2660 &self,
2661 script: &HTMLScriptElement,
2662 load: Option<ScriptResult>,
2663 ) {
2664 assert!(!self.has_pending_parsing_blocking_script());
2665 *self.pending_parsing_blocking_script.borrow_mut() =
2666 Some(PendingScript::new_with_load(script, load));
2667 }
2668
2669 pub(crate) fn has_pending_parsing_blocking_script(&self) -> bool {
2671 self.pending_parsing_blocking_script.borrow().is_some()
2672 }
2673
2674 pub(crate) fn pending_parsing_blocking_script_loaded(
2676 &self,
2677 element: &HTMLScriptElement,
2678 result: ScriptResult,
2679 cx: &mut JSContext,
2680 ) {
2681 {
2682 let mut blocking_script = self.pending_parsing_blocking_script.borrow_mut();
2683 let entry = blocking_script.as_mut().unwrap();
2684 assert!(&*entry.element == element);
2685 entry.loaded(result);
2686 }
2687 self.process_pending_parsing_blocking_script(cx);
2688 }
2689
2690 fn process_pending_parsing_blocking_script(&self, cx: &mut JSContext) {
2691 if self.has_a_stylesheet_that_is_blocking_scripts() {
2692 return;
2693 }
2694 let pair = self
2695 .pending_parsing_blocking_script
2696 .borrow_mut()
2697 .as_mut()
2698 .and_then(PendingScript::take_result);
2699 if let Some((element, result)) = pair {
2700 *self.pending_parsing_blocking_script.borrow_mut() = None;
2701 self.get_current_parser()
2702 .unwrap()
2703 .resume_with_pending_parsing_blocking_script(cx, &element, result);
2704 }
2705 }
2706
2707 pub(crate) fn add_asap_script(&self, script: &HTMLScriptElement) {
2709 self.asap_scripts_set
2710 .borrow_mut()
2711 .push(Dom::from_ref(script));
2712 }
2713
2714 pub(crate) fn asap_script_loaded(
2717 &self,
2718 cx: &mut JSContext,
2719 element: &HTMLScriptElement,
2720 result: ScriptResult,
2721 ) {
2722 {
2723 let mut scripts = self.asap_scripts_set.borrow_mut();
2724 let idx = scripts
2725 .iter()
2726 .position(|entry| &**entry == element)
2727 .unwrap();
2728 scripts.swap_remove(idx);
2729 }
2730 element.execute(cx, result);
2731 self.wait_until_asap_scripts_have_executed();
2732 }
2733
2734 pub(crate) fn push_asap_in_order_script(&self, script: &HTMLScriptElement) {
2736 self.asap_in_order_scripts_list.push(script);
2737 }
2738
2739 pub(crate) fn asap_in_order_script_loaded(
2742 &self,
2743 cx: &mut JSContext,
2744 element: &HTMLScriptElement,
2745 result: ScriptResult,
2746 ) {
2747 self.asap_in_order_scripts_list.loaded(element, result);
2748 while let Some((element, result)) = self
2749 .asap_in_order_scripts_list
2750 .take_next_ready_to_be_executed()
2751 {
2752 element.execute(cx, result);
2753 }
2754
2755 self.wait_until_asap_scripts_have_executed();
2756 }
2757
2758 pub(crate) fn add_deferred_script(&self, script: &HTMLScriptElement) {
2760 self.deferred_scripts.push(script);
2761 }
2762
2763 pub(crate) fn deferred_script_loaded(
2766 &self,
2767 cx: &mut JSContext,
2768 element: &HTMLScriptElement,
2769 result: ScriptResult,
2770 ) {
2771 self.deferred_scripts.loaded(element, result);
2772 self.process_deferred_scripts(cx);
2773 }
2774
2775 fn process_deferred_scripts(&self, cx: &mut JSContext) {
2777 if self.current_the_end_loading_phase.get() != TheEndLoadingPhase::ProcessingDeferredScripts
2778 {
2779 return;
2780 }
2781
2782 loop {
2786 if self.has_a_stylesheet_that_is_blocking_scripts() {
2787 return;
2788 }
2789 if let Some((element, result)) = self.deferred_scripts.take_next_ready_to_be_executed()
2792 {
2793 element.execute(cx, result);
2795 } else {
2796 break;
2797 }
2798 }
2799 if self.deferred_scripts.is_empty() {
2801 self.current_the_end_loading_phase
2802 .set(TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts);
2803 self.dispatch_dom_content_loaded();
2804 }
2805 }
2806
2807 fn dispatch_dom_content_loaded(&self) {
2809 assert_ne!(
2810 self.ReadyState(),
2811 DocumentReadyState::Complete,
2812 "Complete before DOMContentLoaded?"
2813 );
2814
2815 let document = Trusted::new(self);
2818 self.owner_global()
2819 .task_manager()
2820 .dom_manipulation_task_source()
2821 .queue(task!(fire_dom_content_loaded_event: move |cx| {
2822 let document = document.root();
2825 update_with_current_instant(&document.navigation_timing.dom_content_loaded_event_start);
2826 document.upcast::<EventTarget>().fire_bubbling_event(cx, atom!("DOMContentLoaded"));
2828 update_with_current_instant(&document.navigation_timing.dom_content_loaded_event_end);
2831 }));
2840
2841 self.interactive_time
2843 .borrow()
2844 .maybe_set_tti(InteractiveFlag::DOMContentLoaded);
2845
2846 self.wait_until_asap_scripts_have_executed();
2847 }
2848
2849 fn has_finished_all_asap_scripts(&self) -> bool {
2850 self.asap_scripts_set.borrow().is_empty() && self.asap_in_order_scripts_list.is_empty()
2851 }
2852
2853 fn wait_until_asap_scripts_have_executed(&self) {
2855 if self.current_the_end_loading_phase.get() !=
2856 TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts
2857 {
2858 return;
2859 }
2860 if self.has_finished_all_asap_scripts() {
2863 let document = Trusted::new(self);
2864 self.owner_global()
2865 .task_manager()
2866 .dom_manipulation_task_source()
2867 .queue(task!(transition_away_from_asap_scripts: move |cx| {
2868 let document = document.root();
2869 if document.current_the_end_loading_phase.get() !=
2872 TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts
2873 {
2874 return;
2875 }
2876 if !document.has_finished_all_asap_scripts() {
2878 return;
2879 }
2880 document.current_the_end_loading_phase
2881 .set(TheEndLoadingPhase::WaitingForLoadEventBlockers);
2882 document.wait_until_load_blockers_have_resolved(cx);
2883 }));
2884 }
2885 }
2886
2887 pub(crate) fn wait_until_load_blockers_have_resolved(&self, cx: &mut JSContext) {
2889 if self.current_the_end_loading_phase.get() !=
2890 TheEndLoadingPhase::WaitingForLoadEventBlockers
2891 {
2892 return;
2893 }
2894 {
2896 let loader = self.loader.borrow();
2897
2898 if self
2900 .navigation_timing
2901 .top_level_dom_complete
2902 .get()
2903 .is_none() &&
2904 loader.is_only_blocked_by_iframes()
2905 {
2906 update_with_current_instant(&self.navigation_timing.top_level_dom_complete);
2907 }
2908
2909 let not_ready_for_load = loader.is_blocked() || loader.events_inhibited();
2910 if not_ready_for_load {
2911 return;
2912 }
2913 }
2914
2915 self.current_the_end_loading_phase
2916 .set(TheEndLoadingPhase::Done);
2917 self.queue_document_completion(cx);
2918 }
2919
2920 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
2922 if !self.is_fully_active() {
2924 self.salvageable.set(false);
2929 }
2933 for exited_iframe in self.iframes().iter() {
2946 debug!("Destroying nested iframe document");
2947 exited_iframe.destroy_document_and_its_descendants(cx);
2948 }
2949 self.destroy(cx);
2954 }
2957
2958 pub(crate) fn destroy(&self, cx: &mut JSContext) {
2960 let exited_window = self.window();
2961 self.abort(cx, AbortReason::Destroy);
2963 self.salvageable.set(false);
2965 self.unloading_cleanup_steps(cx);
2975
2976 exited_window
2979 .as_global_scope()
2980 .task_manager()
2981 .cancel_all_tasks_and_ignore_future_tasks();
2982
2983 exited_window.discard_browsing_context();
2985
2986 exited_window
2993 .as_global_scope()
2994 .disable_owned_worker_animation_frame_providers();
2995
2996 exited_window.as_global_scope().tear_down();
3003 }
3004
3005 fn active_parser(&self) -> Option<DomRoot<ServoParser>> {
3007 self.get_current_parser()
3010 .filter(|parser| !(parser.has_stopped() || parser.has_aborted()))
3011 }
3012
3013 pub(crate) fn abort(&self, cx: &mut JSContext, reason: AbortReason) {
3015 self.loader.borrow_mut().inhibit_events();
3017
3018 self.script_blocking_stylesheet_set.borrow_mut().clear();
3027 *self.pending_parsing_blocking_script.borrow_mut() = None;
3028 *self.asap_scripts_set.borrow_mut() = vec![];
3029 self.asap_in_order_scripts_list.clear();
3030 self.deferred_scripts.clear();
3031
3032 let global = self.window.as_global_scope();
3033 let loads_cancelled = global.fetch_group_mut().terminate(global);
3034 let event_sources_canceled = global.close_event_sources();
3035
3036 if loads_cancelled || event_sources_canceled {
3037 self.salvageable.set(false);
3039 };
3040
3041 self.owner_global()
3046 .task_manager()
3047 .cancel_pending_tasks_for_source(TaskSourceName::Networking);
3048
3049 if let Some(parser) = self.active_parser() {
3054 self.active_parser_was_aborted.set(true);
3056 parser.abort(cx, reason);
3058 self.salvageable.set(false);
3060 }
3061 }
3062
3063 pub(crate) fn abort_a_document_and_its_descendants(
3065 &self,
3066 cx: &mut JSContext,
3067 reason: AbortReason,
3068 ) {
3069 for iframe in self.iframes().iter() {
3077 if let Some(descendant_document) = iframe.GetContentDocument() {
3078 let trusted_descendant_document = Trusted::new(&*descendant_document);
3079 let document = Trusted::new(self);
3080 descendant_document
3081 .owner_global()
3082 .task_manager()
3083 .navigation_and_traversal_task_source()
3084 .queue(task!(abort_iframe_document: move |cx| {
3085 let descendant_document = trusted_descendant_document.root();
3086 descendant_document.abort(cx, reason);
3088 if !descendant_document.salvageable.get() {
3090 document.root().salvageable.set(false);
3091 }
3092 }));
3093 }
3094 }
3095
3096 self.abort(cx, reason);
3098 }
3099
3100 pub(crate) fn notify_constellation_load(&self) {
3101 self.window()
3102 .send_to_constellation(ScriptToConstellationMessage::LoadComplete);
3103 }
3104
3105 pub(crate) fn set_current_parser(&self, script: Option<&ServoParser>, reason: SetParserReason) {
3106 trace!(
3107 "{} parser for {:?} due to {:?}",
3108 if script.is_some() {
3109 "setting"
3110 } else {
3111 "clearing"
3112 },
3113 self.url(),
3114 reason
3115 );
3116 self.current_parser.set(script);
3117 }
3118
3119 pub(crate) fn get_current_parser(&self) -> Option<DomRoot<ServoParser>> {
3120 self.current_parser.get()
3121 }
3122
3123 pub(crate) fn get_current_parser_line(&self) -> u32 {
3124 self.get_current_parser()
3125 .map(|parser| parser.get_current_line())
3126 .unwrap_or(0)
3127 }
3128
3129 pub(crate) fn set_ancestor_origins_list(&self, ancestor_origins_list: &DOMStringList) {
3131 self.ancestor_origins_list.set(Some(ancestor_origins_list));
3132 }
3133
3134 pub(crate) fn ancestor_origins_list(&self) -> Option<DomRoot<DOMStringList>> {
3136 self.ancestor_origins_list.get()
3137 }
3138
3139 pub(crate) fn set_internal_ancestor_origin_objects_list(
3141 &self,
3142 internal_ancestor_origin_objects_list: Vec<ImmutableOrigin>,
3143 ) {
3144 *self.internal_ancestor_origin_objects_list.borrow_mut() =
3145 Some(internal_ancestor_origin_objects_list);
3146 }
3147
3148 pub(crate) fn internal_ancestor_origin_objects_list(
3150 &self,
3151 ) -> Ref<'_, Option<Vec<ImmutableOrigin>>> {
3152 self.internal_ancestor_origin_objects_list.borrow()
3153 }
3154
3155 pub(crate) fn iframes(&self) -> &IFrameCollection {
3158 &self.iframes
3159 }
3160
3161 pub(crate) fn set_navigation_start(&self, navigation_start: CrossProcessInstant) {
3162 self.interactive_time
3163 .borrow_mut()
3164 .set_navigation_start(navigation_start);
3165 }
3166
3167 pub(crate) fn get_interactive_metrics(&self) -> Ref<'_, ProgressiveWebMetrics> {
3168 self.interactive_time.borrow()
3169 }
3170
3171 pub(crate) fn has_recorded_tti_metric(&self) -> bool {
3172 self.get_interactive_metrics().get_tti().is_some()
3173 }
3174
3175 pub(crate) fn start_tti(&self) {
3176 if self.get_interactive_metrics().needs_tti() {
3177 self.tti_window.borrow_mut().start_window();
3178 }
3179 }
3180
3181 pub(crate) fn record_tti_if_necessary(&self) {
3185 if self.has_recorded_tti_metric() {
3186 return;
3187 }
3188 if self.tti_window.borrow().needs_check() {
3189 self.get_interactive_metrics()
3190 .maybe_set_tti(InteractiveFlag::TimeToInteractive(
3191 self.tti_window.borrow().get_start(),
3192 ));
3193 }
3194 }
3195
3196 pub(crate) fn is_cookie_averse(&self) -> bool {
3198 !self.has_browsing_context || !url_has_network_scheme(&self.url())
3199 }
3200
3201 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
3202 self.document_or_shadow_root.custom_element_registry()
3203 }
3204
3205 pub(crate) fn set_custom_element_registry(&self, registry: &CustomElementRegistry) {
3206 self.document_or_shadow_root
3207 .set_custom_element_registry(Some(registry));
3208 }
3209
3210 pub(crate) fn effective_global_custom_element_registry(
3212 &self,
3213 ) -> Option<DomRoot<CustomElementRegistry>> {
3214 let document_custom_element_registry = self.custom_element_registry();
3217 if CustomElementRegistry::is_a_global_element_registry(
3218 document_custom_element_registry.as_deref(),
3219 ) {
3220 return document_custom_element_registry;
3221 }
3222 None
3224 }
3225
3226 pub(crate) fn teardown_custom_element_registry(&self) {
3229 if let Some(custom_elements) = self.custom_element_registry() {
3230 custom_elements.teardown();
3231 }
3232 }
3233
3234 pub(crate) fn increment_throw_on_dynamic_markup_insertion_counter(&self) {
3235 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
3236 self.throw_on_dynamic_markup_insertion_counter
3237 .set(counter + 1);
3238 }
3239
3240 pub(crate) fn decrement_throw_on_dynamic_markup_insertion_counter(&self) {
3241 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
3242 self.throw_on_dynamic_markup_insertion_counter
3243 .set(counter - 1);
3244 }
3245
3246 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
3247 for image in self.responsive_images.borrow().iter() {
3248 image.react_to_environment_changes(cx);
3249 }
3250 }
3251
3252 pub(crate) fn register_responsive_image(&self, img: &HTMLImageElement) {
3253 self.responsive_images.borrow_mut().push(Dom::from_ref(img));
3254 }
3255
3256 pub(crate) fn unregister_responsive_image(&self, img: &HTMLImageElement) {
3257 let index = self
3258 .responsive_images
3259 .borrow()
3260 .iter()
3261 .position(|x| **x == *img);
3262 if let Some(i) = index {
3263 self.responsive_images.borrow_mut().remove(i);
3264 }
3265 }
3266
3267 pub(crate) fn register_media_controls(&self, id: &str, controls: &ShadowRoot) {
3268 let did_have_these_media_controls = self
3269 .media_controls
3270 .borrow_mut()
3271 .insert(id.to_string(), Dom::from_ref(controls))
3272 .is_some();
3273 debug_assert!(
3274 !did_have_these_media_controls,
3275 "Trying to register known media controls"
3276 );
3277 }
3278
3279 pub(crate) fn unregister_media_controls(&self, id: &str) {
3280 let did_have_these_media_controls = self.media_controls.borrow_mut().remove(id).is_some();
3281 debug_assert!(
3282 did_have_these_media_controls,
3283 "Trying to unregister unknown media controls"
3284 );
3285 }
3286
3287 pub(crate) fn mark_canvas_as_dirty(&self, canvas: &Dom<HTMLCanvasElement>) {
3288 let mut dirty_canvases = self.dirty_canvases.borrow_mut();
3289 if dirty_canvases
3290 .iter()
3291 .any(|dirty_canvas| dirty_canvas == canvas)
3292 {
3293 return;
3294 }
3295 dirty_canvases.push(canvas.clone());
3296 }
3297
3298 pub(crate) fn needs_rendering_update(&self, no_gc: &NoGC) -> bool {
3302 if !self.is_fully_active() {
3303 return false;
3304 }
3305 if !self.window().layout_blocked() &&
3306 (!self.restyle_reason(no_gc).is_empty() ||
3307 self.window().layout().needs_new_display_list() ||
3308 self.window().layout().needs_accessibility_update())
3309 {
3310 return true;
3311 }
3312 if !self.rendering_update_reasons.get().is_empty() {
3313 return true;
3314 }
3315 if self.event_handler.has_pending_input_events() {
3316 return true;
3317 }
3318 if self.has_pending_scroll_events() {
3319 return true;
3320 }
3321 if self.window().has_unhandled_resize_event() {
3322 return true;
3323 }
3324 if self.has_pending_animated_image_update.get() || !self.dirty_canvases.borrow().is_empty()
3325 {
3326 return true;
3327 }
3328 if self.window().has_pending_media_query_evaluation() {
3329 return true;
3330 }
3331 if self
3332 .selection()
3333 .is_some_and(|selection| selection.visible_selection_dirty())
3334 {
3335 return true;
3336 }
3337
3338 false
3339 }
3340
3341 pub(crate) fn mark_paint_timing(&self) {
3343 self.paint_timing_info.set(PaintTimingInfo::now());
3347 }
3348
3349 pub(crate) fn paint_timing_info(&self) -> PaintTimingInfo {
3351 self.paint_timing_info.get()
3352 }
3353
3354 pub(crate) fn update_the_rendering(
3359 &self,
3360 cx: &mut JSContext,
3361 ) -> (ReflowPhasesRun, ReflowStatistics) {
3362 assert!(!self.is_render_blocked());
3363 self.mark_paint_timing();
3365
3366 let mut phases = ReflowPhasesRun::empty();
3369 if self.has_pending_animated_image_update.get() {
3370 self.animation_manager.update_active_image_animation_frames(
3371 &self.window,
3372 self.current_animation_timeline_value(),
3373 );
3374 self.has_pending_animated_image_update.set(false);
3375 phases.insert(ReflowPhasesRun::UpdatedImageData);
3376 }
3377
3378 self.current_rendering_epoch
3379 .set(self.current_rendering_epoch.get().next());
3380 let current_rendering_epoch = self.current_rendering_epoch.get();
3381
3382 let image_keys: Vec<_> = self
3384 .dirty_canvases
3385 .borrow_mut()
3386 .drain(..)
3387 .filter_map(|canvas| canvas.update_rendering(current_rendering_epoch))
3388 .collect();
3389
3390 let pipeline_id = self.window().pipeline_id();
3393 if !image_keys.is_empty() {
3394 phases.insert(ReflowPhasesRun::UpdatedImageData);
3395 self.waiting_on_canvas_image_updates.set(true);
3396 self.window().paint_api().delay_new_frame_for_canvas(
3397 self.webview_id(),
3398 self.window().pipeline_id(),
3399 current_rendering_epoch,
3400 image_keys,
3401 );
3402 }
3403
3404 let (reflow_phases, statistics) = self.window().reflow(cx, ReflowGoal::UpdateTheRendering);
3405 let phases = phases.union(reflow_phases);
3406
3407 self.window().paint_api().update_epoch(
3408 self.webview_id(),
3409 pipeline_id,
3410 current_rendering_epoch,
3411 );
3412
3413 (phases, statistics)
3414 }
3415
3416 pub(crate) fn handle_no_longer_waiting_on_asynchronous_image_updates(&self) {
3417 self.waiting_on_canvas_image_updates.set(false);
3418 }
3419
3420 pub(crate) fn waiting_on_canvas_image_updates(&self) -> bool {
3421 self.waiting_on_canvas_image_updates.get()
3422 }
3423
3424 pub(crate) fn maybe_fulfill_font_ready_promise(&self, cx: &mut JSContext) -> bool {
3434 if !self.is_fully_active() {
3435 return false;
3436 }
3437
3438 let fonts = self.Fonts(cx);
3439 if !fonts.waiting_to_fullfill_promise() {
3440 return false;
3441 }
3442 if self.window().font_context().web_fonts_still_loading() != 0 {
3443 return false;
3444 }
3445 if self.ReadyState() != DocumentReadyState::Complete {
3446 return false;
3447 }
3448 if !self.restyle_reason(cx.no_gc()).is_empty() {
3449 return false;
3450 }
3451 if !self.rendering_update_reasons.get().is_empty() {
3452 return false;
3453 }
3454
3455 let result = fonts.fulfill_ready_promise_if_needed(cx);
3456
3457 if result {
3461 self.add_rendering_update_reason(RenderingUpdateReason::FontReadyPromiseFulfilled);
3462 }
3463
3464 result
3465 }
3466
3467 pub(crate) fn id_map(&self) -> &TreeOrderedIndexMap {
3468 &self.id_map
3469 }
3470
3471 pub(crate) fn add_resize_observer(&self, cx: &mut JSContext, resize_observer: &ResizeObserver) {
3473 self.resize_observers
3474 .safe_borrow_mut(cx)
3475 .push(Dom::from_ref(resize_observer));
3476 }
3477
3478 pub(crate) fn rooted_clone_of_resize_observers(&self) -> Vec<DomRoot<ResizeObserver>> {
3479 self.resize_observers
3480 .borrow()
3481 .iter()
3482 .map(Dom::as_rooted)
3483 .collect()
3484 }
3485
3486 pub(crate) fn gather_active_resize_observations_at_depth(
3489 &self,
3490 cx: &mut JSContext,
3491 depth: &ResizeObservationDepth,
3492 ) -> bool {
3493 let mut has_active_resize_observations = false;
3496 for observer in self.rooted_clone_of_resize_observers() {
3497 observer.gather_active_resize_observations_at_depth(
3498 cx,
3499 depth,
3500 &mut has_active_resize_observations,
3501 );
3502 }
3503 has_active_resize_observations
3504 }
3505
3506 pub(crate) fn broadcast_active_resize_observations(
3508 &self,
3509 cx: &mut JSContext,
3510 ) -> ResizeObservationDepth {
3511 let mut shallowest = ResizeObservationDepth::max();
3514 for observer in self.rooted_clone_of_resize_observers() {
3515 observer.broadcast_active_resize_observations(cx, &mut shallowest);
3516 }
3517 shallowest
3518 }
3519
3520 pub(crate) fn has_skipped_resize_observations(&self) -> bool {
3522 self.resize_observers
3523 .borrow()
3524 .iter()
3525 .any(|observer| observer.has_skipped_resize_observations())
3526 }
3527
3528 pub(crate) fn deliver_resize_loop_error_notification(&self, cx: &mut JSContext) {
3530 let error_info: ErrorInfo = crate::dom::bindings::error::ErrorInfo {
3531 message: "ResizeObserver loop completed with undelivered notifications.".to_string(),
3532 ..Default::default()
3533 };
3534 self.window
3535 .as_global_scope()
3536 .report_an_error(cx, error_info, HandleValue::null());
3537 }
3538
3539 pub(crate) fn status_code(&self) -> Option<u16> {
3540 self.status_code
3541 }
3542
3543 pub(crate) fn encoding_parse_a_url(&self, url: &str) -> Result<ServoUrl, url::ParseError> {
3545 let encoding = self.encoding.get();
3551
3552 let base_url = self.base_url();
3558
3559 url::Url::options()
3561 .base_url(Some(base_url.as_url()))
3562 .encoding_override(Some(&|input| {
3563 servo_url::encoding::encode_as_url_query_string(input, encoding)
3564 }))
3565 .parse(url)
3566 .map(ServoUrl::from)
3567 }
3568
3569 pub(crate) fn allowed_to_use_feature(&self, _feature: PermissionName) -> bool {
3571 if !self.has_browsing_context {
3573 return false;
3574 }
3575
3576 if !self.is_fully_active() {
3578 return false;
3579 }
3580
3581 true
3587 }
3588
3589 pub(crate) fn add_intersection_observer(&self, intersection_observer: &IntersectionObserver) {
3592 self.intersection_observers
3593 .borrow_mut()
3594 .push(Dom::from_ref(intersection_observer));
3595 }
3596
3597 pub(crate) fn remove_intersection_observer(
3601 &self,
3602 intersection_observer: &IntersectionObserver,
3603 ) {
3604 self.intersection_observers
3605 .borrow_mut()
3606 .retain(|observer| *observer != intersection_observer)
3607 }
3608
3609 pub(crate) fn update_intersection_observer_steps(
3611 &self,
3612 cx: &mut JSContext,
3613 time: CrossProcessInstant,
3614 ) {
3615 if self.intersection_observers.borrow().is_empty() {
3616 return;
3617 }
3618 self.window()
3620 .reflow_for_non_flushing_update_the_rendering_queries(cx);
3621
3622 for intersection_observer in &*self.intersection_observers.borrow() {
3624 self.update_single_intersection_observer_steps(cx, intersection_observer, time);
3625 }
3626 }
3627
3628 fn update_single_intersection_observer_steps(
3630 &self,
3631 cx: &mut JSContext,
3632 intersection_observer: &IntersectionObserver,
3633 time: CrossProcessInstant,
3634 ) {
3635 let root_bounds = intersection_observer.root_intersection_rectangle();
3638
3639 intersection_observer.update_intersection_observations_steps(cx, self, time, root_bounds);
3643 }
3644
3645 pub(crate) fn notify_intersection_observers(&self, cx: &mut JSContext) {
3647 self.intersection_observer_task_queued.set(false);
3650
3651 rooted_vec!(let notify_list <- self.intersection_observers.clone().take().into_iter());
3656
3657 for intersection_observer in notify_list.iter() {
3660 intersection_observer.invoke_callback_if_necessary(cx);
3662 }
3663 }
3664
3665 pub(crate) fn queue_an_intersection_observer_task(&self) {
3667 if self.intersection_observer_task_queued.get() {
3670 return;
3671 }
3672
3673 self.intersection_observer_task_queued.set(true);
3676
3677 let document = Trusted::new(self);
3681 self.owner_global()
3682 .task_manager()
3683 .intersection_observer_task_source()
3684 .queue(task!(notify_intersection_observers: move |cx| {
3685 document.root().notify_intersection_observers(cx);
3686 }));
3687 }
3688
3689 pub(crate) fn store_lcp_candidate(&self, candidate: LCPCandidate, element: Option<&Element>) {
3690 self.lcp_candidates.borrow_mut().insert(
3691 candidate.id,
3692 LCPCandidateAndElement {
3693 element: element.map(Dom::from_ref),
3694 candidate,
3695 },
3696 );
3697 }
3698
3699 pub(crate) fn store_container_timing_record(
3700 &self,
3701 record: ContainerTimingRecord,
3702 identifier: DOMString,
3703 root_element: &Element,
3704 last_painted_element: Option<&Element>,
3705 ) {
3706 self.container_timing_records.borrow_mut().insert(
3707 record.id,
3708 ContainerTimingRecordAndElements {
3709 identifier,
3710 root_element: Dom::from_ref(root_element),
3711 last_painted_element: last_painted_element.map(Dom::from_ref),
3712 record,
3713 },
3714 );
3715 }
3716
3717 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
3720 fn container_timing_entries(
3721 &self,
3722 cx: &mut JSContext,
3723 paint_time: CrossProcessInstant,
3724 ids: Vec<ContainerTimingID>,
3725 ) -> Vec<DomRoot<PerformanceEntry>> {
3726 ids.into_iter()
3727 .filter_map(|id| {
3728 let stored = self
3729 .container_timing_records
3730 .safe_borrow_mut(cx)
3731 .remove(&id)?;
3732 let first_render_time = *self
3736 .container_first_render_times
3737 .safe_borrow_mut(cx)
3738 .entry(stored.record.root_element)
3739 .or_insert(paint_time);
3740 Some(DomRoot::upcast::<PerformanceEntry>(
3741 PerformanceContainerTiming::new(
3742 cx,
3743 self.window.as_global_scope(),
3744 stored.identifier,
3745 stored.record.intersection_rect,
3746 stored.record.size,
3747 first_render_time,
3748 paint_time,
3749 stored.last_painted_element.as_deref(),
3750 Some(&stored.root_element),
3751 ),
3752 ))
3753 })
3754 .collect()
3755 }
3756
3757 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
3758 pub(crate) fn handle_paint_metric(&self, cx: &mut JSContext, event: PaintMetricEvent) {
3759 let metrics = self.interactive_time.borrow();
3760 let entry = match event {
3761 PaintMetricEvent::FirstPaint(paint_timing_info, first_reflow) => {
3762 metrics.set_first_paint(paint_timing_info.default_paint_timestamp(), first_reflow);
3763 DomRoot::upcast::<PerformanceEntry>(PerformancePaintTiming::new(
3764 cx,
3765 self.window.as_global_scope(),
3766 ProgressiveWebMetricType::FirstPaint,
3767 paint_timing_info,
3768 ))
3769 },
3770 PaintMetricEvent::FirstContentfulPaint(paint_timing_info, first_reflow) => {
3771 metrics.set_first_contentful_paint(
3772 paint_timing_info.default_paint_timestamp(),
3773 first_reflow,
3774 );
3775 DomRoot::upcast::<PerformanceEntry>(PerformancePaintTiming::new(
3776 cx,
3777 self.window.as_global_scope(),
3778 ProgressiveWebMetricType::FirstContentfulPaint,
3779 paint_timing_info,
3780 ))
3781 },
3782 PaintMetricEvent::LargestContentfulPaint(paint_timing_info, id) => {
3783 let Some(stored_candidate) = self.lcp_candidates.borrow_mut().remove(&id) else {
3784 warn!("Received LCP paint metric for unknown candidate: {id:?}");
3785 return;
3786 };
3787 metrics
3788 .set_largest_contentful_paint(id, paint_timing_info.default_paint_timestamp());
3789 DomRoot::upcast::<PerformanceEntry>(LargestContentfulPaint::new(
3790 cx,
3791 self.window.as_global_scope(),
3792 &stored_candidate.candidate,
3793 stored_candidate.element.as_deref(),
3794 stored_candidate.candidate.load_time,
3795 paint_timing_info,
3796 ))
3797 },
3798 PaintMetricEvent::ContainerTiming(paint_time, ids) => {
3802 drop(metrics);
3803 for entry in self.container_timing_entries(cx, paint_time, ids) {
3804 self.window.Performance(cx).queue_entry(&entry);
3805 }
3806 return;
3807 },
3808 };
3809 self.window.Performance(cx).queue_entry(&entry);
3810 }
3811
3812 fn write(
3814 &self,
3815 cx: &mut JSContext,
3816 text: Vec<TrustedHTMLOrString>,
3817 line_feed: bool,
3818 containing_class: &str,
3819 field: &str,
3820 ) -> ErrorResult {
3821 let mut strings: Vec<String> = Vec::with_capacity(text.len());
3823 let mut is_trusted = true;
3825 for value in text {
3827 match value {
3828 TrustedHTMLOrString::TrustedHTML(trusted_html) => {
3830 strings.push(trusted_html.to_string());
3831 },
3832 TrustedHTMLOrString::String(str_) => {
3833 is_trusted = false;
3835 strings.push(str_.into());
3837 },
3838 };
3839 }
3840 let mut string = itertools::join(strings, "");
3841 if !is_trusted {
3845 string = TrustedHTML::get_trusted_type_compliant_string(
3846 cx,
3847 &self.global(),
3848 TrustedHTMLOrString::String(string.into()),
3849 &format!("{} {}", containing_class, field),
3850 )?
3851 .str()
3852 .to_owned();
3853 }
3854 if line_feed {
3856 string.push('\n');
3857 }
3858 if !self.is_html_document() {
3860 return Err(Error::InvalidState(Some(
3861 "Document must be a HTML document".into(),
3862 )));
3863 }
3864
3865 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
3868 return Err(Error::InvalidState(Some(
3869 "A custom element constructor attempted to open, close or write to this document"
3870 .into(),
3871 )));
3872 }
3873
3874 if self.active_parser_was_aborted.get() {
3876 return Ok(());
3877 }
3878
3879 let parser = match self.get_current_parser() {
3880 Some(ref parser) if parser.can_write() => DomRoot::from_ref(&**parser),
3881 _ => {
3883 if self.is_prompting_or_unloading() ||
3886 self.ignore_destructive_writes_counter.get() > 0
3887 {
3888 return Ok(());
3889 }
3890 self.Open(cx, None, None)?;
3892 self.get_current_parser().unwrap()
3893 },
3894 };
3895
3896 parser.write(cx, string.into());
3898
3899 Ok(())
3900 }
3901
3902 pub(crate) fn details_name_groups<'a: 'b, 'b>(
3903 &'a self,
3904 no_gc: &'b NoGC,
3905 ) -> RefMut<'b, DetailsNameGroups> {
3906 RefMut::map(
3907 self.details_name_groups.safe_borrow_mut(no_gc),
3908 |details_name_groups| details_name_groups.get_or_insert_default(),
3909 )
3910 }
3911
3912 pub(crate) fn accessibility_data_mut(&self) -> RefMut<'_, AccessibilityData> {
3913 self.accessibility_data.borrow_mut()
3914 }
3915
3916 pub(crate) fn accessibility_active(&self) -> bool {
3917 self.window().layout().accessibility_active()
3918 }
3919
3920 pub(crate) fn rooted_nodes_for_accessibility_integrity_check(
3921 &self,
3922 ) -> Option<FxHashSet<OpaqueNode>> {
3923 if !self.accessibility_active() {
3924 return None;
3925 }
3926
3927 let mut accessibility_data = self.accessibility_data_mut();
3928
3929 if pref!(expensive_accessibility_test_assertions_enabled) {
3930 return Some(accessibility_data.unroot_and_drain_all_removed_nodes());
3931 }
3932
3933 accessibility_data.unroot_all_removed_nodes();
3934 None
3935 }
3936
3937 pub(crate) fn get_document_element_unrooted<'a>(
3938 &self,
3939 no_gc: &'a NoGC,
3940 ) -> Option<UnrootedDom<'a, Element>> {
3941 self.upcast::<Node>().child_elements_unrooted(no_gc).next()
3942 }
3943
3944 pub(crate) fn collect_reports(
3945 &self,
3946 reports: &mut Vec<Report>,
3947 ops: &mut MallocSizeOfOps,
3948 ) -> HashSet<*const JSObject> {
3949 let mut computed_objects = HashSet::new();
3950 let mut sizes = DocumentSizes::default();
3951
3952 for node in self
3953 .upcast::<Node>()
3954 .traverse_preorder(ShadowIncluding::Yes)
3955 {
3956 let size = compute_size(node.jsobject(), ops, &computed_objects, None);
3957
3958 match node.type_id() {
3959 NodeTypeId::Element(_) => {
3960 sizes.element_nodes_size += size;
3961
3962 let element = node.downcast::<Element>().expect("node must be Element");
3963 for attr in element.attrs().borrow().iter() {
3964 if let Some(attr) = attr.as_attr() {
3965 let size = compute_size(
3966 attr.upcast::<Node>().jsobject(),
3967 ops,
3968 &computed_objects,
3969 None,
3970 );
3971 sizes.attribute_nodes_size += size;
3972 computed_objects.insert(attr.upcast::<Node>().jsobject());
3973 }
3974 }
3975 },
3976 NodeTypeId::CharacterData(_) => sizes.text_nodes_size += size,
3977 _ => sizes.other_nodes_size += size,
3978 };
3979
3980 computed_objects.insert(node.jsobject());
3981 }
3982
3983 let prefix = format!("url({})", self.url());
3984 reports.push(Report {
3985 path: path![prefix, "js", "dom", "element-nodes"],
3986 kind: ReportKind::ExplicitJemallocHeapSize,
3987 size: sizes.element_nodes_size,
3988 });
3989 reports.push(Report {
3990 path: path![prefix, "js", "dom", "text-nodes"],
3991 kind: ReportKind::ExplicitJemallocHeapSize,
3992 size: sizes.text_nodes_size,
3993 });
3994 reports.push(Report {
3995 path: path![prefix, "js", "dom", "attribute-nodes"],
3996 kind: ReportKind::ExplicitJemallocHeapSize,
3997 size: sizes.attribute_nodes_size,
3998 });
3999 reports.push(Report {
4000 path: path![prefix, "js", "dom", "other-nodes"],
4001 kind: ReportKind::ExplicitJemallocHeapSize,
4002 size: sizes.other_nodes_size,
4003 });
4004
4005 computed_objects
4006 }
4007
4008 pub(crate) fn live_ranges(&self) -> &WeakRangeVec {
4010 &self.live_ranges
4011 }
4012
4013 pub(crate) fn gained_or_lost_system_focus(&self, cx: &mut JSContext, gained_focus: bool) {
4014 let focus_handler = self.focus_handler();
4015 if !self.is_fully_active() || !focus_handler.has_focus() {
4016 return;
4017 }
4018 focus_handler.gained_or_lost_system_focus(cx, gained_focus);
4019 self.refresh_focus_rendering();
4020 }
4021
4022 pub(crate) fn refresh_focus_rendering(&self) {
4023 self.window().layout().set_needs_new_display_list();
4024 }
4025}
4026
4027#[derive(Default)]
4029struct DocumentSizes {
4030 element_nodes_size: usize,
4031 text_nodes_size: usize,
4032 attribute_nodes_size: usize,
4033 other_nodes_size: usize,
4034}
4035
4036impl<'dom> LayoutDom<'dom, Document> {
4037 #[inline]
4038 pub(crate) fn is_html_document_for_layout(&self) -> bool {
4039 self.unsafe_get().is_html_document
4040 }
4041
4042 #[inline]
4043 pub(crate) fn encoding_for_layout(&self) -> &'static Encoding {
4044 self.unsafe_get().encoding.get()
4045 }
4046
4047 #[inline]
4048 pub(crate) fn quirks_mode(self) -> QuirksMode {
4049 self.unsafe_get().quirks_mode.get()
4050 }
4051
4052 #[inline]
4053 pub(crate) fn shared_style_locks(self) -> &'dom SharedRwLocks {
4054 self.unsafe_get().shared_style_locks()
4055 }
4056
4057 #[inline]
4058 pub(crate) fn flush_shadow_root_stylesheets_if_necessary(
4059 self,
4060 stylist: &mut Stylist,
4061 guard: &SharedRwLockReadGuard,
4062 ) {
4063 (*self.unsafe_get()).flush_shadow_root_stylesheets_if_necessary_for_layout(stylist, guard)
4064 }
4065
4066 pub(crate) fn elements_with_id(self, id: &Atom) -> &[LayoutDom<'dom, Element>] {
4067 self.unsafe_get().id_map.get_all_for_layout(id)
4068 }
4069
4070 pub(crate) fn url_for_layout(self) -> ServoUrl {
4071 self.unsafe_get().url.borrow().clone()
4072 }
4073
4074 #[expect(unsafe_code)]
4075 pub(crate) fn visible_selection_for_layout(&self) -> Option<LayoutDom<'dom, Selection>> {
4076 unsafe { self.unsafe_get().selection.to_layout() }
4077 }
4078
4079 pub(crate) fn default_language_for_layout(&self) -> AtomicRef<'dom, Option<String>> {
4080 self.unsafe_get().default_language.borrow()
4081 }
4082}
4083
4084pub(crate) fn get_registrable_domain_suffix_of_or_is_equal_to(
4088 host_suffix_string: &str,
4089 original_host: Host,
4090) -> Option<Host> {
4091 if host_suffix_string.is_empty() {
4093 return None;
4094 }
4095
4096 let host = match Host::parse(host_suffix_string) {
4098 Ok(host) => host,
4099 Err(_) => return None,
4100 };
4101
4102 if host != original_host {
4104 let host = match host {
4106 Host::Domain(ref host) => host,
4107 _ => return None,
4108 };
4109 let original_host = match original_host {
4110 Host::Domain(ref original_host) => original_host,
4111 _ => return None,
4112 };
4113
4114 let index = original_host.len().checked_sub(host.len())?;
4116 let (prefix, suffix) = original_host.split_at(index);
4117
4118 if !prefix.ends_with('.') {
4119 return None;
4120 }
4121 if suffix != host {
4122 return None;
4123 }
4124
4125 if is_pub_domain(host) {
4127 return None;
4128 }
4129 }
4130
4131 Some(host)
4133}
4134
4135fn url_has_network_scheme(url: &ServoUrl) -> bool {
4137 matches!(url.scheme(), "ftp" | "http" | "https")
4138}
4139
4140#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
4141pub(crate) enum HasBrowsingContext {
4142 No,
4143 Yes,
4144}
4145
4146impl Document {
4147 #[expect(clippy::too_many_arguments)]
4148 pub(crate) fn new_inherited(
4149 window: &Window,
4150 has_browsing_context: HasBrowsingContext,
4151 url: Option<ServoUrl>,
4152 about_base_url: Option<ServoUrl>,
4153 origin: MutableOrigin,
4154 is_html_document: IsHTMLDocument,
4155 content_type: Option<Mime>,
4156 last_modified: Option<String>,
4157 activity: DocumentActivity,
4158 doc_loader: DocumentLoader,
4159 referrer: Option<String>,
4160 status_code: Option<u16>,
4161 is_initial_about_blank: bool,
4162 allow_declarative_shadow_roots: bool,
4163 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4164 has_trustworthy_ancestor_origin: bool,
4165 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4166 creation_sandboxing_flag_set: SandboxingFlagSet,
4167 timeline: &DocumentTimeline,
4168 pipeline_id: PipelineId,
4169 image_cache: StdArc<dyn ImageCache>,
4170 ) -> Document {
4171 let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
4172
4173 let frame_type = match window.is_top_level() {
4174 true => TimerMetadataFrameType::RootWindow,
4175 false => TimerMetadataFrameType::IFrame,
4176 };
4177 let interactive_time = ProgressiveWebMetrics::new(
4178 window.time_profiler_chan().clone(),
4179 url.clone(),
4180 frame_type,
4181 );
4182
4183 let content_type = content_type.unwrap_or_else(|| {
4184 match is_html_document {
4185 IsHTMLDocument::HTMLDocument => "text/html",
4187 IsHTMLDocument::NonHTMLDocument => "application/xml",
4189 }
4190 .parse()
4191 .unwrap()
4192 });
4193
4194 let encoding = content_type
4195 .get_parameter(CHARSET)
4196 .and_then(|charset| Encoding::for_label(charset.as_bytes()))
4197 .unwrap_or(UTF_8);
4198
4199 let has_focus = window.parent_info().is_none();
4200 let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
4201 let shared_style_locks = window.script_thread().shared_style_locks().clone();
4202 let quirks_mode = if is_initial_about_blank {
4206 QuirksMode::Quirks
4207 } else {
4208 QuirksMode::NoQuirks
4210 };
4211
4212 Document {
4213 node: Node::new_document_node(),
4214 document_or_shadow_root: DocumentOrShadowRoot::new(window),
4215 window: Dom::from_ref(window),
4216 has_browsing_context,
4217 implementation: Default::default(),
4218 content_type,
4219 last_modified,
4220 url: AtomicRefCell::new(url),
4221 about_base_url: DomRefCell::new(about_base_url),
4222 quirks_mode: Cell::new(quirks_mode),
4223 event_handler: DocumentEventHandler::new(window),
4224 focus_handler: DocumentFocusHandler::new(window, has_focus),
4225 embedder_controls: DocumentEmbedderControls::new(window),
4226 id_map: TreeOrderedIndexMap::id(),
4227 name_map: TreeOrderedIndexMap::name(),
4228 encoding: Cell::new(encoding),
4230 is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
4231 activity: Cell::new(activity),
4232 tag_map: DomRefCell::new(HashMapTracedValues::new_fx()),
4233 tagns_map: DomRefCell::new(HashMapTracedValues::new_fx()),
4234 classes_map: DomRefCell::new(HashMapTracedValues::new()),
4235 images: Default::default(),
4236 embeds: Default::default(),
4237 links: Default::default(),
4238 forms: Default::default(),
4239 scripts: Default::default(),
4240 anchors: Default::default(),
4241 applets: Default::default(),
4242 iframes: IFrameCollection::new(),
4243 shared_style_locks,
4244 stylesheets: DomRefCell::new(DocumentStylesheetSet::new()),
4245 stylesheet_list: MutNullableDom::new(None),
4246 ready_state: Cell::new(DocumentReadyState::Complete),
4249 current_script: Default::default(),
4250 current_the_end_loading_phase: Default::default(),
4251 pending_parsing_blocking_script: Default::default(),
4252 script_blocking_stylesheet_set: Default::default(),
4253 render_blocking_element_count: Default::default(),
4254 deferred_scripts: Default::default(),
4255 asap_in_order_scripts_list: Default::default(),
4256 asap_scripts_set: Default::default(),
4257 animation_frame_ident: Cell::new(0),
4258 animation_frame_list: DomRefCell::new(VecDeque::new()),
4259 running_animation_callbacks: Cell::new(false),
4260 loader: DomRefCell::new(doc_loader),
4261 current_parser: Default::default(),
4262 base_element: Default::default(),
4263 target_base_element: Default::default(),
4264 ancestor_origins_list: Default::default(),
4265 internal_ancestor_origin_objects_list: Default::default(),
4266 appropriate_template_contents_owner_document: Default::default(),
4267 pending_restyles: DomRefCell::new(FxHashMap::default()),
4268 needs_restyle: Cell::new(RestyleReason::DOMChanged),
4269 origin: DomRefCell::new(origin),
4270 referrer,
4271 target_element: MutNullableDom::new(None),
4272 policy_container: DomRefCell::new(StdArc::new(PolicyContainer::default())),
4273 preloaded_resources: Default::default(),
4274 ignore_destructive_writes_counter: Default::default(),
4275 ignore_opens_during_unload_counter: Default::default(),
4276 spurious_animation_frames: Cell::new(0),
4277 fullscreen_element: MutNullableDom::new(None),
4278 form_id_listener_map: Default::default(),
4279 interactive_time: DomRefCell::new(interactive_time),
4280 tti_window: DomRefCell::new(InteractiveWindow::default()),
4281 throw_on_dynamic_markup_insertion_counter: Cell::new(0),
4282 page_showing: Cell::new(false),
4283 salvageable: Cell::new(true),
4284 active_parser_was_aborted: Cell::new(false),
4285 fired_unload: Cell::new(false),
4286 responsive_images: Default::default(),
4287 navigation_timing: Default::default(),
4288 resource_fetch_timing: RefCell::new(None),
4289 completely_loaded: Cell::new(false),
4290 script_and_layout_blockers: Cell::new(0),
4291 delayed_tasks: Default::default(),
4292 shadow_roots: Default::default(),
4293 shadow_roots_styles_changed: Cell::new(false),
4294 media_controls: DomRefCell::new(HashMap::new()),
4295 dirty_canvases: DomRefCell::new(Default::default()),
4296 has_pending_animated_image_update: Cell::new(false),
4297 selection: MutNullableDom::new(None),
4298 timeline: Dom::from_ref(timeline),
4299 animation_manager: AnimationManager::new(),
4300 dirty_root: Default::default(),
4301 declarative_refresh: Default::default(),
4302 resize_observers: Default::default(),
4303 fonts: Default::default(),
4304 visibility_state: Cell::new(DocumentVisibilityState::Visible),
4308 status_code,
4309 is_initial_about_blank: Cell::new(is_initial_about_blank),
4310 allow_declarative_shadow_roots: Cell::new(allow_declarative_shadow_roots),
4311 inherited_insecure_requests_policy: Cell::new(inherited_insecure_requests_policy),
4312 has_trustworthy_ancestor_origin: Cell::new(has_trustworthy_ancestor_origin),
4313 intersection_observer_task_queued: Cell::new(false),
4314 intersection_observers: Default::default(),
4315 highlighted_dom_node: Default::default(),
4316 lcp_candidates: DomRefCell::new(Default::default()),
4317 paint_timing_info: Cell::new(PaintTimingInfo::now()),
4318 container_timing_records: DomRefCell::new(Default::default()),
4319 container_first_render_times: DomRefCell::new(Default::default()),
4320 adopted_stylesheets: Default::default(),
4321 pending_scroll_events: Default::default(),
4322 rendering_update_reasons: Default::default(),
4323 waiting_on_canvas_image_updates: Cell::new(false),
4324 root_removal_noted: Cell::new(true),
4325 current_rendering_epoch: Default::default(),
4326 custom_element_reaction_stack,
4327 active_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
4328 creation_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
4329 favicon: RefCell::new(None),
4330 websockets: DOMTracker::new(),
4331 active_media_elements: Default::default(),
4332 details_name_groups: Default::default(),
4333 protocol_handler_automation_mode: Default::default(),
4334 layout_animations_test_enabled: pref!(layout_animations_test_enabled),
4335 state_override: Default::default(),
4336 value_override: Default::default(),
4337 default_single_line_container_name: Default::default(),
4338 css_styling_flag: Default::default(),
4339 accessibility_data: Default::default(),
4340 iframe_load_in_progress: Default::default(),
4341 mute_iframe_load: Default::default(),
4342 timers: OneshotTimers::new(window.upcast()),
4343 pipeline_id,
4344 task_manager: Rc::new(TaskManager::new(
4345 Some(window.event_loop_sender()),
4346 pipeline_id,
4347 None,
4348 )),
4349 image_cache,
4350 history: Default::default(),
4351 theme: Default::default(),
4352 theme_override: Default::default(),
4353 default_language: Default::default(),
4354 window_detached: Default::default(),
4355 live_ranges: Default::default(),
4356 module_map: Default::default(),
4357 }
4358 }
4359
4360 pub(crate) fn detach_window(&self) {
4361 self.window_detached.set(true);
4362 }
4363
4364 pub(crate) fn window_detached(&self) -> bool {
4365 self.window_detached.get()
4366 }
4367
4368 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
4370 if let Some(csp_list) = self.get_csp_list().as_ref() {
4371 for policy in &csp_list.0 {
4372 if policy.contains_a_directive_whose_name_is("upgrade-insecure-requests") &&
4373 policy.disposition == PolicyDisposition::Enforce
4374 {
4375 return InsecureRequestsPolicy::Upgrade;
4376 }
4377 }
4378 }
4379
4380 self.inherited_insecure_requests_policy
4381 .get()
4382 .unwrap_or(InsecureRequestsPolicy::DoNotUpgrade)
4383 }
4384
4385 pub(crate) fn event_handler(&self) -> &DocumentEventHandler {
4387 &self.event_handler
4388 }
4389
4390 pub(crate) fn focus_handler(&self) -> &DocumentFocusHandler {
4392 &self.focus_handler
4393 }
4394
4395 pub(crate) fn embedder_controls(&self) -> &DocumentEmbedderControls {
4397 &self.embedder_controls
4398 }
4399
4400 fn has_pending_scroll_events(&self) -> bool {
4403 !self.pending_scroll_events.borrow().is_empty()
4404 }
4405
4406 pub(crate) fn add_rendering_update_reason(&self, reason: RenderingUpdateReason) {
4409 self.rendering_update_reasons
4410 .set(self.rendering_update_reasons.get().union(reason));
4411 }
4412
4413 pub(crate) fn clear_rendering_update_reasons(&self) {
4415 self.rendering_update_reasons
4416 .set(RenderingUpdateReason::empty())
4417 }
4418
4419 pub(crate) fn add_script_and_layout_blocker(&self) {
4426 self.script_and_layout_blockers
4427 .set(self.script_and_layout_blockers.get() + 1);
4428 }
4429
4430 pub(crate) fn remove_script_and_layout_blocker(&self, cx: &mut JSContext) {
4434 assert!(self.script_and_layout_blockers.get() > 0);
4435 self.script_and_layout_blockers
4436 .set(self.script_and_layout_blockers.get() - 1);
4437 while self.script_and_layout_blockers.get() == 0 && !self.delayed_tasks.borrow().is_empty()
4438 {
4439 let task = self.delayed_tasks.borrow_mut().remove(0);
4440 task.run_box(cx);
4441 }
4442 }
4443
4444 pub(crate) fn add_delayed_task<T: 'static + NonSendTaskBox>(&self, task: T) {
4446 self.delayed_tasks.borrow_mut().push(Box::new(task));
4447 }
4448
4449 pub(crate) fn is_safe_to_run_script_or_layout(&self) -> bool {
4452 self.script_and_layout_blockers.get() == 0
4453 }
4454
4455 pub(crate) fn ensure_safe_to_run_script_or_layout(&self) {
4458 assert!(
4459 self.is_safe_to_run_script_or_layout(),
4460 "Attempt to use script or layout while DOM not in a stable state"
4461 );
4462 }
4463
4464 #[expect(clippy::too_many_arguments)]
4465 pub(crate) fn new(
4466 cx: &mut JSContext,
4467 window: &Window,
4468 has_browsing_context: HasBrowsingContext,
4469 url: Option<ServoUrl>,
4470 about_base_url: Option<ServoUrl>,
4471 origin: MutableOrigin,
4472 doctype: IsHTMLDocument,
4473 content_type: Option<Mime>,
4474 last_modified: Option<String>,
4475 activity: DocumentActivity,
4476 doc_loader: DocumentLoader,
4477 referrer: Option<String>,
4478 status_code: Option<u16>,
4479 is_initial_about_blank: bool,
4480 allow_declarative_shadow_roots: bool,
4481 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4482 has_trustworthy_ancestor_origin: bool,
4483 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4484 creation_sandboxing_flag_set: SandboxingFlagSet,
4485 pipeline_id: PipelineId,
4486 image_cache: StdArc<dyn ImageCache>,
4487 ) -> DomRoot<Document> {
4488 Self::new_with_proto(
4489 cx,
4490 window,
4491 None,
4492 has_browsing_context,
4493 url,
4494 about_base_url,
4495 origin,
4496 doctype,
4497 content_type,
4498 last_modified,
4499 activity,
4500 doc_loader,
4501 referrer,
4502 status_code,
4503 is_initial_about_blank,
4504 allow_declarative_shadow_roots,
4505 inherited_insecure_requests_policy,
4506 has_trustworthy_ancestor_origin,
4507 custom_element_reaction_stack,
4508 creation_sandboxing_flag_set,
4509 pipeline_id,
4510 image_cache,
4511 )
4512 }
4513
4514 #[expect(clippy::too_many_arguments)]
4515 fn new_with_proto(
4516 cx: &mut JSContext,
4517 window: &Window,
4518 proto: Option<HandleObject>,
4519 has_browsing_context: HasBrowsingContext,
4520 url: Option<ServoUrl>,
4521 about_base_url: Option<ServoUrl>,
4522 origin: MutableOrigin,
4523 doctype: IsHTMLDocument,
4524 content_type: Option<Mime>,
4525 last_modified: Option<String>,
4526 activity: DocumentActivity,
4527 doc_loader: DocumentLoader,
4528 referrer: Option<String>,
4529 status_code: Option<u16>,
4530 is_initial_about_blank: bool,
4531 allow_declarative_shadow_roots: bool,
4532 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4533 has_trustworthy_ancestor_origin: bool,
4534 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4535 creation_sandboxing_flag_set: SandboxingFlagSet,
4536 pipeline_id: PipelineId,
4537 image_cache: StdArc<dyn ImageCache>,
4538 ) -> DomRoot<Document> {
4539 let timeline = DocumentTimeline::new(cx, window);
4540 let document = reflect_dom_object_with_proto(
4541 cx,
4542 Box::new(Document::new_inherited(
4543 window,
4544 has_browsing_context,
4545 url,
4546 about_base_url,
4547 origin,
4548 doctype,
4549 content_type,
4550 last_modified,
4551 activity,
4552 doc_loader,
4553 referrer,
4554 status_code,
4555 is_initial_about_blank,
4556 allow_declarative_shadow_roots,
4557 inherited_insecure_requests_policy,
4558 has_trustworthy_ancestor_origin,
4559 custom_element_reaction_stack,
4560 creation_sandboxing_flag_set,
4561 &timeline,
4562 pipeline_id,
4563 image_cache,
4564 )),
4565 window,
4566 proto,
4567 );
4568 {
4569 let node = document.upcast::<Node>();
4570 node.set_owner_doc(&document);
4571 }
4572 document
4573 }
4574
4575 pub(crate) fn get_redirect_count(&self) -> u16 {
4576 self.resource_fetch_timing()
4577 .as_ref()
4578 .map_or(0, |resource_fetch_timing| {
4579 resource_fetch_timing.redirect_count
4580 })
4581 }
4582
4583 pub(crate) fn set_resource_fetch_timing(&self, timing: ResourceFetchTiming) {
4584 self.resource_fetch_timing.replace(Some(timing));
4585 }
4586
4587 pub(crate) fn resource_fetch_timing(&self) -> Ref<'_, Option<ResourceFetchTiming>> {
4588 self.resource_fetch_timing.borrow()
4589 }
4590
4591 pub(crate) fn navigation_timing(&self) -> Rc<NavigationTiming> {
4592 self.navigation_timing.clone()
4593 }
4594
4595 pub(crate) fn performance_timing_attribute(
4596 &self,
4597 name: &str,
4598 ) -> Fallible<Option<CrossProcessInstant>> {
4599 Ok(match name {
4600 "unloadEventStart" => self.navigation_timing().unload_event_start.get(),
4601 "unloadEventEnd" => self.navigation_timing().unload_event_end.get(),
4602 "domInteractive" => self.navigation_timing().dom_interactive.get(),
4603 "domContentLoadedEventStart" => self
4604 .navigation_timing()
4605 .dom_content_loaded_event_start
4606 .get(),
4607 "domContentLoadedEventEnd" => {
4608 self.navigation_timing().dom_content_loaded_event_end.get()
4609 },
4610 "domComplete" => self.navigation_timing().dom_complete.get(),
4611 "loadEventStart" => self.navigation_timing().load_event_start.get(),
4612 "loadEventEnd" => self.navigation_timing().load_event_end.get(),
4613 "redirectStart" | "redirectEnd" | "secureConnectionStart" | "responseEnd" => self
4614 .resource_fetch_timing()
4615 .as_ref()
4616 .and_then(|resource_fetch_timing| match name {
4617 "redirectStart" => resource_fetch_timing.redirect_start,
4618 "redirectEnd" => resource_fetch_timing.redirect_end,
4619 "secureConnectionStart" => resource_fetch_timing.secure_connection_start,
4620 "responseEnd" => resource_fetch_timing.response_end,
4621 _ => None,
4622 }),
4623 _ => {
4624 return Err(Error::Operation(Some(format!(
4625 "{name} hasn't been implemented."
4626 ))));
4627 },
4628 })
4629 }
4630
4631 pub(crate) fn elements_by_name_count(&self, name: &TracedDOMString) -> u32 {
4632 if name.is_empty() {
4633 return 0;
4634 }
4635 self.count_node_list(|n| Document::is_element_in_get_by_name(n, name))
4636 }
4637
4638 pub(crate) fn elements_by_name_iter<'a>(
4639 &self,
4640 no_gc: &'a NoGC,
4641 name: &'a TracedDOMString,
4642 ) -> impl Iterator<Item = UnrootedDom<'a, Node>> + 'a {
4643 self.get_document_element_unrooted(no_gc)
4644 .into_iter()
4645 .flat_map(|element| {
4646 element
4647 .upcast::<Node>()
4648 .traverse_preorder_unrooted(no_gc, ShadowIncluding::No)
4649 })
4650 .filter(move |node| Document::is_element_in_get_by_name(node, name))
4651 }
4652
4653 fn is_element_in_get_by_name(node: &Node, name: &TracedDOMString) -> bool {
4656 let element = match node.downcast::<Element>() {
4657 Some(element) => element,
4658 None => return false,
4659 };
4660 if element.namespace() != &ns!(html) {
4661 return false;
4662 }
4663 element.get_name().is_some_and(|n| &*n == name)
4664 }
4665
4666 fn count_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> u32 {
4667 let doc = self.GetDocumentElement();
4668 let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
4669 maybe_node
4670 .iter()
4671 .flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
4672 .filter(|node| callback(node))
4673 .count() as u32
4674 }
4675
4676 fn get_html_element(&self) -> Option<DomRoot<HTMLHtmlElement>> {
4677 self.GetDocumentElement().and_then(DomRoot::downcast)
4678 }
4679
4680 pub(crate) fn shared_style_locks(&self) -> &SharedRwLocks {
4682 &self.shared_style_locks
4683 }
4684
4685 pub(crate) fn style_shared_author_lock(&self) -> &SharedRwLock {
4687 &self.shared_style_locks.author
4688 }
4689
4690 pub(crate) fn flush_stylesheets_for_reflow(&self) -> bool {
4692 let mut stylesheets = self.stylesheets.borrow_mut();
4699 let have_changed = stylesheets.has_changed();
4700 stylesheets.flush_without_invalidation();
4701 have_changed
4702 }
4703
4704 pub(crate) fn salvageable(&self) -> bool {
4705 self.salvageable.get()
4706 }
4707
4708 pub(crate) fn make_document_unsalvageable(&self) {
4710 self.salvageable.set(false);
4716 }
4717
4718 pub(crate) fn appropriate_template_contents_owner_document(
4720 &self,
4721 cx: &mut JSContext,
4722 ) -> DomRoot<Document> {
4723 self.appropriate_template_contents_owner_document
4724 .or_init(|| {
4725 let doctype = if self.is_html_document {
4726 IsHTMLDocument::HTMLDocument
4727 } else {
4728 IsHTMLDocument::NonHTMLDocument
4729 };
4730 let new_doc = Document::new(
4731 cx,
4732 self.window(),
4733 HasBrowsingContext::No,
4734 None,
4735 None,
4736 MutableOrigin::new(ImmutableOrigin::new_opaque()),
4738 doctype,
4739 None,
4740 None,
4741 DocumentActivity::Inactive,
4742 DocumentLoader::new(&self.loader()),
4743 None,
4744 None,
4745 false,
4746 self.allow_declarative_shadow_roots(),
4747 Some(self.insecure_requests_policy()),
4748 self.has_trustworthy_ancestor_or_current_origin(),
4749 self.custom_element_reaction_stack.clone(),
4750 self.creation_sandboxing_flag_set(),
4751 self.pipeline_id(),
4752 self.image_cache.clone(),
4753 );
4754 new_doc
4755 .appropriate_template_contents_owner_document
4756 .set(Some(&new_doc));
4757 new_doc
4758 })
4759 }
4760
4761 pub(crate) fn get_element_by_id(&self, no_gc: &NoGC, id: &Atom) -> Option<DomRoot<Element>> {
4762 self.id_map.get(no_gc, self.upcast(), id)
4763 }
4764
4765 pub(crate) fn ensure_pending_restyle(&self, el: &Element) -> RefMut<'_, PendingRestyle> {
4766 let map = self.pending_restyles.borrow_mut();
4767 RefMut::map(map, |m| {
4768 &mut m
4769 .entry(Dom::from_ref(el))
4770 .or_insert_with(|| NoTrace(PendingRestyle::default()))
4771 .0
4772 })
4773 }
4774
4775 pub(crate) fn element_attr_will_change(&self, el: &Element, attr: AttrRef<'_>) {
4776 if !el.upcast::<Node>().is_connected() {
4777 return;
4781 }
4782
4783 let mut entry = self.ensure_pending_restyle(el);
4789 if entry.snapshot.is_none() {
4790 entry.snapshot = Some(Snapshot::new());
4791 }
4792 if attr.local_name() == &local_name!("style") {
4793 entry.hint.insert(RestyleHint::RESTYLE_STYLE_ATTRIBUTE);
4794 }
4795
4796 if vtable_for(el.upcast()).attribute_affects_presentational_hints(attr) ||
4797 el.check_style_on_self_or_eager_pseudos(|style| {
4798 if let Some(ref attribute_references) = style.attribute_references {
4799 return attribute_references.contains_key(attr.local_name());
4800 }
4801 false
4802 })
4803 {
4804 entry.hint.insert(RestyleHint::RESTYLE_SELF);
4805 }
4806
4807 let snapshot = entry.snapshot.as_mut().unwrap();
4808 if attr.local_name() == &local_name!("id") {
4809 if snapshot.id_changed {
4810 return;
4811 }
4812 snapshot.id_changed = true;
4813 } else if attr.local_name() == &local_name!("class") {
4814 if snapshot.class_changed {
4815 return;
4816 }
4817 snapshot.class_changed = true;
4818 } else {
4819 snapshot.other_attributes_changed = true;
4820 }
4821 let local_name = style::LocalName::cast(attr.local_name());
4822 if !snapshot.changed_attrs.contains(local_name) {
4823 snapshot.changed_attrs.push(local_name.clone());
4824 }
4825 if snapshot.attrs.is_none() {
4826 let attrs = el
4827 .attrs()
4828 .borrow()
4829 .iter()
4830 .map(|attr| (attr.as_identifier(), attr.value().clone()))
4831 .collect();
4832 snapshot.attrs = Some(attrs);
4833 }
4834 }
4835
4836 pub(crate) fn set_referrer_policy(&self, policy: ReferrerPolicy) {
4837 StdArc::make_mut(&mut *self.policy_container.borrow_mut()).set_referrer_policy(policy);
4838 }
4839
4840 pub(crate) fn get_referrer_policy(&self) -> ReferrerPolicy {
4841 self.policy_container.borrow().get_referrer_policy()
4842 }
4843
4844 pub(crate) fn set_target_element(&self, no_gc: &NoGC, node: Option<&Element>) {
4845 if let Some(ref element) = self.target_element.get() {
4846 element.set_target_state(no_gc, false);
4847 }
4848
4849 self.target_element.set(node);
4850
4851 if let Some(ref element) = self.target_element.get() {
4852 element.set_target_state(no_gc, true);
4853 }
4854 }
4855
4856 pub(crate) fn incr_ignore_destructive_writes_counter(&self) {
4857 self.ignore_destructive_writes_counter
4858 .set(self.ignore_destructive_writes_counter.get() + 1);
4859 }
4860
4861 pub(crate) fn decr_ignore_destructive_writes_counter(&self) {
4862 self.ignore_destructive_writes_counter
4863 .set(self.ignore_destructive_writes_counter.get() - 1);
4864 }
4865
4866 pub(crate) fn is_prompting_or_unloading(&self) -> bool {
4867 self.ignore_opens_during_unload_counter.get() > 0
4868 }
4869
4870 fn incr_ignore_opens_during_unload_counter(&self) {
4871 self.ignore_opens_during_unload_counter
4872 .set(self.ignore_opens_during_unload_counter.get() + 1);
4873 }
4874
4875 fn decr_ignore_opens_during_unload_counter(&self) {
4876 self.ignore_opens_during_unload_counter
4877 .set(self.ignore_opens_during_unload_counter.get() - 1);
4878 }
4879
4880 pub(crate) fn set_fullscreen_element(&self, element: Option<&Element>) {
4881 self.fullscreen_element.set(element);
4882 }
4883
4884 fn reset_form_owner_for_listeners(&self, cx: &mut JSContext, id: &Atom) {
4885 let map = self.form_id_listener_map.borrow();
4886 if let Some(listeners) = map.get(id) {
4887 for listener in listeners {
4888 listener
4889 .as_maybe_form_control()
4890 .expect("Element must be a form control")
4891 .reset_form_owner(cx);
4892 }
4893 }
4894 }
4895
4896 pub(crate) fn register_shadow_root(&self, shadow_root: &ShadowRoot) {
4897 self.shadow_roots
4898 .borrow_mut()
4899 .insert(Dom::from_ref(shadow_root));
4900 self.invalidate_shadow_roots_stylesheets();
4901 }
4902
4903 pub(crate) fn unregister_shadow_root(&self, shadow_root: &ShadowRoot) {
4904 let mut shadow_roots = self.shadow_roots.borrow_mut();
4905 shadow_roots.remove(&Dom::from_ref(shadow_root));
4906 }
4907
4908 pub(crate) fn invalidate_shadow_roots_stylesheets(&self) {
4909 self.shadow_roots_styles_changed.set(true);
4910 }
4911
4912 pub(crate) fn flush_shadow_root_stylesheets_if_necessary_for_layout(
4913 &self,
4914 stylist: &mut Stylist,
4915 guard: &SharedRwLockReadGuard,
4916 ) {
4917 if !self.shadow_roots_styles_changed.get() {
4918 return;
4919 }
4920 #[expect(unsafe_code)]
4921 unsafe {
4922 for shadow_root in self.shadow_roots.borrow_for_layout().iter() {
4923 let layout: LayoutDom<'_, _> = shadow_root.to_layout();
4924 layout.flush_stylesheets_for_layout(stylist, guard);
4925 }
4926 }
4927 self.shadow_roots_styles_changed.set(false);
4928 }
4929
4930 pub(crate) fn stylesheet_count(&self) -> usize {
4931 self.stylesheets.borrow().len()
4932 }
4933
4934 pub(crate) fn stylesheet_at(
4935 &self,
4936 cx: &mut JSContext,
4937 index: usize,
4938 ) -> Option<DomRoot<CSSStyleSheet>> {
4939 let stylesheets = self.stylesheets.borrow();
4940
4941 stylesheets
4942 .get(Origin::Author, index)
4943 .and_then(|s| s.owner.get_cssom_object(cx))
4944 }
4945
4946 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(
4953 &self,
4954 no_gc: &NoGC,
4955 owner_node: &Element,
4956 sheet: Arc<Stylesheet>,
4957 ) {
4958 let insertion_point = {
4959 let stylesheets = &mut *self.stylesheets.borrow_mut();
4960
4961 stylesheets
4963 .iter()
4964 .map(|(sheet, _origin)| sheet)
4965 .find(|sheet_in_doc| {
4966 match &sheet_in_doc.owner {
4967 StylesheetSource::Element(other_node) => owner_node
4968 .upcast::<Node>()
4969 .is_before(no_gc, other_node.upcast()),
4970 StylesheetSource::Constructed(_) => true,
4973 }
4974 })
4975 .cloned()
4976 };
4977
4978 if self.has_browsing_context() {
4979 self.add_stylesheet_to_stylist(
4980 sheet.clone(),
4981 insertion_point.as_ref().map(|s| s.sheet.clone()),
4982 );
4983 }
4984
4985 let stylesheets = &mut *self.stylesheets.borrow_mut();
4986 DocumentOrShadowRoot::add_stylesheet(
4987 StylesheetSource::Element(Dom::from_ref(owner_node)),
4988 StylesheetSetRef::Document(stylesheets),
4989 sheet,
4990 insertion_point,
4991 self.style_shared_author_lock(),
4992 );
4993 }
4994
4995 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
5000 pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
5001 debug_assert!(cssom_stylesheet.is_constructed());
5002
5003 let sheet = cssom_stylesheet.style_stylesheet().clone();
5004 let insertion_point = {
5005 let stylesheets = &mut *self.stylesheets.borrow_mut();
5006
5007 stylesheets
5008 .iter()
5009 .last()
5010 .map(|(sheet, _origin)| sheet)
5011 .cloned()
5012 };
5013
5014 if self.has_browsing_context() {
5015 self.add_stylesheet_to_stylist(
5016 sheet.clone(),
5017 insertion_point.as_ref().map(|s| s.sheet.clone()),
5018 );
5019 }
5020
5021 let stylesheets = &mut *self.stylesheets.borrow_mut();
5022 DocumentOrShadowRoot::add_stylesheet(
5023 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
5024 StylesheetSetRef::Document(stylesheets),
5025 sheet,
5026 insertion_point,
5027 self.style_shared_author_lock(),
5028 );
5029 }
5030
5031 pub(crate) fn add_stylesheet_to_stylist(
5032 &self,
5033 stylesheet: Arc<Stylesheet>,
5034 before_stylesheet: Option<Arc<Stylesheet>>,
5035 ) {
5036 self.window
5037 .layout_mut()
5038 .add_stylesheet(stylesheet, before_stylesheet);
5039 }
5040
5041 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, stylesheet: &Arc<Stylesheet>) {
5044 if self.has_browsing_context() {
5045 self.window
5046 .layout_mut()
5047 .remove_stylesheet(stylesheet.clone());
5048 }
5049
5050 DocumentOrShadowRoot::remove_stylesheet(
5051 owner,
5052 stylesheet,
5053 StylesheetSetRef::Document(&mut *self.stylesheets.borrow_mut()),
5054 )
5055 }
5056
5057 pub(crate) fn get_elements_with_id(
5058 &self,
5059 cx: &mut JSContext,
5060 id: &Atom,
5061 ) -> Ref<'_, [Dom<Element>]> {
5062 self.id_map.get_all(cx.no_gc(), self.upcast(), id)
5063 }
5064
5065 pub(crate) fn get_elements_with_name(
5066 &self,
5067 cx: &mut JSContext,
5068 name: &Atom,
5069 ) -> Ref<'_, [Dom<Element>]> {
5070 self.name_map.get_all(cx.no_gc(), self.upcast(), name)
5071 }
5072
5073 pub(crate) fn drain_pending_restyles(
5074 &self,
5075 no_gc: &NoGC,
5076 ) -> Vec<(TrustedNodeAddress, PendingRestyle)> {
5077 self.pending_restyles
5078 .borrow_mut()
5079 .drain()
5080 .filter_map(|(element, restyle)| {
5081 let node = element.upcast::<Node>();
5082 if !node.get_flag(NodeFlags::IS_CONNECTED) {
5083 return None;
5084 }
5085 element.note_dirty_descendants(no_gc);
5086 Some((node.to_trusted_node_address(), restyle.0))
5087 })
5088 .collect()
5089 }
5090
5091 pub(crate) fn advance_animation_timeline_for_testing(&self, delta: TimeDuration) {
5092 self.timeline.advance_specific(delta);
5093 let current_timeline_value = self.current_animation_timeline_value();
5094 self.animation_manager
5095 .update_for_new_timeline_value(&self.window, current_timeline_value);
5096 }
5097
5098 pub(crate) fn maybe_mark_animating_nodes_as_dirty(&self, no_gc: &NoGC) {
5099 let current_timeline_value = self.current_animation_timeline_value();
5100 self.animation_manager
5101 .mark_animating_nodes_as_dirty(no_gc, current_timeline_value);
5102 }
5103
5104 pub(crate) fn current_animation_timeline_value(&self) -> f64 {
5105 self.timeline
5106 .upcast::<AnimationTimeline>()
5107 .current_time_in_seconds()
5108 }
5109
5110 pub(crate) fn animation_manager(&self) -> &AnimationManager {
5111 &self.animation_manager
5112 }
5113
5114 pub(crate) fn update_animations_post_reflow(&self) {
5115 let current_timeline_value = self.current_animation_timeline_value();
5116 self.animation_manager
5117 .do_post_reflow_update(&self.window, current_timeline_value);
5118 }
5119
5120 pub(crate) fn cancel_animations_for_node(&self, node: &Node) {
5121 self.animation_manager.cancel_animations_for_node(node);
5122 }
5123
5124 pub(crate) fn remove_style_and_layout_data_from_subtree(
5128 &self,
5129 no_gc: &NoGC,
5130 subtree_root: &Node,
5131 ) {
5132 for node in subtree_root.traverse_preorder_unrooted(no_gc, ShadowIncluding::Yes) {
5133 self.clean_up_style_and_layout_data_for_node(&node);
5134 }
5135 }
5136
5137 pub(crate) fn clean_up_style_and_layout_data_for_node(&self, node: &Node) {
5138 node.clear_layout_data();
5139 if let Some(element) = node.downcast::<Element>() {
5140 element.clean_up_style_data();
5141
5142 if self.dirty_root == Some(element) {
5147 self.dirty_root.clear();
5148 }
5149 }
5150
5151 node.set_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION, false);
5152 node.set_flag(NodeFlags::SELECTION_INHIBITED, false);
5153 node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false);
5154 }
5155
5156 pub(crate) fn update_animations_and_send_events(&self, cx: &mut CurrentRealm) {
5158 if !self.layout_animations_test_enabled {
5160 self.timeline.update(self.window());
5161 }
5162
5163 let current_timeline_value = self.current_animation_timeline_value();
5170 self.animation_manager
5171 .update_for_new_timeline_value(&self.window, current_timeline_value);
5172 self.maybe_mark_animating_nodes_as_dirty(cx.no_gc());
5173
5174 self.window().perform_a_microtask_checkpoint(cx);
5176
5177 self.animation_manager()
5179 .send_pending_events(self.window(), cx);
5180 }
5181
5182 pub(crate) fn set_has_pending_animated_image_update(&self) {
5183 self.has_pending_animated_image_update.set(true);
5184 }
5185
5186 pub(crate) fn shared_declarative_refresh_steps(&self, content: &[u8], from_meta_element: bool) {
5188 if self.will_declaratively_refresh() {
5190 return;
5191 }
5192
5193 static REFRESH_REGEX: LazyLock<Regex> = LazyLock::new(|| {
5195 Regex::new(
5199 r#"(?xs)
5200 ^
5201 \s* # 3
5202 ((?<time>[0-9]+)|\.) # 5-6
5203 [0-9.]* # 8
5204 (
5205 (
5206 (\s*;|\s*,|\s) # 10.3
5207 \s* # 10.4
5208 )
5209 (
5210 (
5211 (U|u)(R|r)(L|l) # 11.2-11.4
5212 \s*=\s* # 11.5-11.7
5213 )?
5214 ('(?<url1>[^']*)'(?s-u:.)*|"(?<url2>[^"]*)"(?s-u:.)*|['"]?(?<url3>(?s-u:.)*)) # 11.8 - 11.10
5215 |
5216 (?<url4>(?s-u:.)*)
5217 )
5218 )?
5219 $
5220 "#,
5221 )
5222 .unwrap()
5223 });
5224
5225 let mut url_record = self.url();
5227 let captures = if let Some(captures) = REFRESH_REGEX.captures(content) {
5228 captures
5229 } else {
5230 return;
5231 };
5232 let time = if let Some(time_string) = captures.name("time") {
5233 u64::from_str(&String::from_utf8_lossy(time_string.as_bytes())).unwrap_or(0)
5234 } else {
5235 0
5236 };
5237 let captured_url = captures.name("url1").or(captures
5238 .name("url2")
5239 .or(captures.name("url3").or(captures.name("url4"))));
5240
5241 if let Some(url_match) = captured_url {
5243 url_record = if let Ok(url) = ServoUrl::parse_with_base(
5244 Some(&url_record),
5245 &String::from_utf8_lossy(url_match.as_bytes()),
5246 ) {
5247 info!("Refresh to {}", url.debug_compact());
5248 url
5249 } else {
5250 return;
5252 };
5253 if url_record.scheme() == "javascript" {
5255 return;
5256 }
5257 }
5258 if self.completely_loaded() {
5260 self.window.as_global_scope().schedule_callback(
5261 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
5262 url: url_record,
5263 from_meta_element,
5264 }),
5265 Duration::from_secs(time),
5266 );
5267 self.set_declarative_refresh(DeclarativeRefresh::CreatedAfterLoad);
5268 } else {
5269 self.set_declarative_refresh(DeclarativeRefresh::PendingLoad {
5270 url: url_record,
5271 time,
5272 from_meta_element,
5273 });
5274 }
5275 }
5276
5277 pub(crate) fn will_declaratively_refresh(&self) -> bool {
5278 self.declarative_refresh.borrow().is_some()
5279 }
5280 pub(crate) fn set_declarative_refresh(&self, refresh: DeclarativeRefresh) {
5281 *self.declarative_refresh.borrow_mut() = Some(refresh);
5282 }
5283
5284 fn update_visibility_state(
5286 &self,
5287 cx: &mut JSContext,
5288 visibility_state: DocumentVisibilityState,
5289 ) {
5290 if self.visibility_state.get() == visibility_state {
5292 return;
5293 }
5294 self.visibility_state.set(visibility_state);
5296 let entry = VisibilityStateEntry::new(
5299 cx,
5300 &self.global(),
5301 visibility_state,
5302 CrossProcessInstant::now(),
5303 );
5304 self.window
5305 .Performance(cx)
5306 .queue_entry(entry.upcast::<PerformanceEntry>());
5307
5308 if let Some(geolocation) = self.window.maybe_geolocation() {
5320 geolocation.handle_visibility_change(visibility_state);
5321 }
5322
5323 #[cfg(feature = "gamepad")]
5325 if visibility_state == DocumentVisibilityState::Hidden {
5326 self.window
5327 .Navigator(cx)
5328 .GetGamepads(cx)
5329 .unwrap_or_default()
5330 .iter_mut()
5331 .for_each(|gamepad| {
5332 if let Some(g) = gamepad {
5333 g.vibration_actuator().handle_visibility_change();
5334 }
5335 });
5336 }
5337
5338 self.upcast::<EventTarget>()
5340 .fire_bubbling_event(cx, atom!("visibilitychange"));
5341 }
5342
5343 pub(crate) fn is_initial_about_blank(&self) -> bool {
5345 self.is_initial_about_blank.get()
5346 }
5347
5348 pub(crate) fn allow_declarative_shadow_roots(&self) -> bool {
5350 self.allow_declarative_shadow_roots.get()
5351 }
5352
5353 pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
5354 self.has_trustworthy_ancestor_origin.get()
5355 }
5356
5357 pub(crate) fn has_trustworthy_ancestor_or_current_origin(&self) -> bool {
5358 self.has_trustworthy_ancestor_origin.get() ||
5359 self.origin().immutable().is_potentially_trustworthy()
5360 }
5361
5362 pub(crate) fn determine_the_network_partition_key(&self) -> Option<Site> {
5364 let top_level_origin = if self.window().is_top_level() {
5367 Some(self.origin().immutable().clone())
5368 } else if let Some(top_level_document) = self.window().top_level_document_if_local() {
5369 Some(top_level_document.origin().immutable().clone())
5370 } else if let Ok(ancestor_origins) = self.internal_ancestor_origin_objects_list.try_borrow()
5371 {
5372 ancestor_origins
5373 .as_ref()
5374 .and_then(|ancestors| ancestors.last().cloned())
5375 } else {
5376 None
5377 };
5378
5379 let top_level_origin = top_level_origin.or_else(|| {
5382 self.window()
5383 .as_global_scope()
5384 .top_level_creation_url()
5385 .map(|url| url.origin())
5386 });
5387
5388 let top_level_origin = top_level_origin.expect("topLevelOrigin must be an origin");
5390
5391 let top_level_site = Site::from_origin(&top_level_origin);
5393
5394 Some(top_level_site)
5400 }
5401
5402 pub(crate) fn highlight_dom_node(&self, node: Option<&Node>) {
5403 self.highlighted_dom_node.set(node);
5404 self.add_restyle_reason(RestyleReason::HighlightedDOMNodeChanged);
5405 }
5406
5407 pub(crate) fn highlighted_dom_node(&self) -> Option<DomRoot<Node>> {
5408 self.highlighted_dom_node.get()
5409 }
5410
5411 pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
5412 self.custom_element_reaction_stack.clone()
5413 }
5414
5415 pub(crate) fn active_sandboxing_flag_set(&self) -> SandboxingFlagSet {
5416 self.active_sandboxing_flag_set.get()
5417 }
5418
5419 pub(crate) fn has_active_sandboxing_flag(&self, flag: SandboxingFlagSet) -> bool {
5420 self.active_sandboxing_flag_set.get().contains(flag)
5421 }
5422
5423 pub(crate) fn set_active_sandboxing_flag_set(&self, flags: SandboxingFlagSet) {
5424 self.active_sandboxing_flag_set.set(flags)
5425 }
5426
5427 pub(crate) fn creation_sandboxing_flag_set(&self) -> SandboxingFlagSet {
5428 self.creation_sandboxing_flag_set.get()
5429 }
5430
5431 pub(crate) fn creation_sandboxing_flag_set_considering_parent_iframe(
5432 &self,
5433 ) -> SandboxingFlagSet {
5434 self.window()
5435 .window_proxy()
5436 .frame_element()
5437 .and_then(|element| element.downcast::<HTMLIFrameElement>())
5438 .map(HTMLIFrameElement::sandboxing_flag_set)
5439 .unwrap_or_else(|| self.creation_sandboxing_flag_set())
5440 }
5441
5442 pub(crate) fn viewport_scrolling_box(&self, flags: ScrollContainerQueryFlags) -> ScrollingBox {
5443 self.window()
5444 .scrolling_box_query(None, flags)
5445 .expect("We should always have a ScrollingBox for the Viewport")
5446 }
5447
5448 pub(crate) fn notify_embedder_favicon(&self) {
5449 if let Some(ref image) = *self.favicon.borrow() {
5450 self.send_to_embedder(EmbedderMsg::NewFavicon(self.webview_id(), image.clone()));
5451 }
5452 }
5453
5454 pub(crate) fn set_favicon(&self, favicon: Image) {
5455 *self.favicon.borrow_mut() = Some(favicon);
5456 self.notify_embedder_favicon();
5457 }
5458
5459 pub(crate) fn fullscreen_element(&self) -> Option<DomRoot<Element>> {
5460 self.fullscreen_element.get()
5461 }
5462
5463 pub(crate) fn state_override(&self, command_name: &CommandName) -> Option<bool> {
5465 self.state_override.borrow().get(command_name).copied()
5466 }
5467
5468 pub(crate) fn set_state_override(&self, command_name: CommandName, state: Option<bool>) {
5470 if let Some(state) = state {
5471 self.state_override.borrow_mut().insert(command_name, state);
5472 } else {
5473 self.value_override.borrow_mut().remove(&command_name);
5474 }
5475 }
5476
5477 pub(crate) fn value_override(&self, command_name: &CommandName) -> Option<DOMString> {
5479 self.value_override.borrow().get(command_name).cloned()
5480 }
5481
5482 pub(crate) fn set_value_override(&self, command_name: CommandName, value: Option<DOMString>) {
5484 if let Some(value) = value {
5485 self.value_override.borrow_mut().insert(command_name, value);
5486 } else {
5487 self.value_override.borrow_mut().remove(&command_name);
5488 }
5489 }
5490
5491 pub(crate) fn clear_command_overrides(&self) {
5494 self.state_override.borrow_mut().clear();
5495 self.value_override.borrow_mut().clear();
5496 }
5497
5498 pub(crate) fn default_single_line_container_name(&self) -> DefaultSingleLineContainerName {
5500 self.default_single_line_container_name.get()
5501 }
5502
5503 pub(crate) fn set_default_single_line_container_name(
5505 &self,
5506 value: DefaultSingleLineContainerName,
5507 ) {
5508 self.default_single_line_container_name.set(value)
5509 }
5510
5511 pub(crate) fn css_styling_flag(&self) -> bool {
5513 self.css_styling_flag.get()
5514 }
5515
5516 pub(crate) fn set_css_styling_flag(&self, value: bool) {
5518 self.css_styling_flag.set(value)
5519 }
5520
5521 pub(crate) fn mute_iframe_load_flag(&self) -> bool {
5522 self.mute_iframe_load.get()
5523 }
5524
5525 pub(crate) fn set_iframe_load_in_progress(&self, value: bool) {
5526 self.iframe_load_in_progress.set(value)
5527 }
5528
5529 pub(crate) fn theme(&self) -> Option<Theme> {
5530 self.theme.get()
5531 }
5532
5533 pub(crate) fn set_theme(&self, new_theme: Option<Theme>) {
5534 self.theme.set(new_theme);
5535 self.window.refresh_theme();
5536 }
5537
5538 pub(crate) fn theme_override(&self) -> Option<Theme> {
5539 self.theme_override.get()
5540 }
5541
5542 pub(crate) fn set_theme_override(&self, new_theme: Option<Theme>) {
5543 self.theme_override.set(new_theme);
5544 self.window.refresh_theme();
5545 }
5546
5547 pub(crate) fn default_language(&self) -> Option<String> {
5548 self.default_language.borrow().clone()
5549 }
5550
5551 pub(crate) fn set_default_language(&self, new_language: Option<String>) {
5552 *self.default_language.borrow_mut() = new_language;
5553 }
5554
5555 pub(crate) fn create_element(&self, cx: &mut JSContext, name: &str) -> DomRoot<Element> {
5556 let element_options =
5557 StringOrElementCreationOptions::ElementCreationOptions(ElementCreationOptions {
5558 is: None,
5559 });
5560 self.CreateElement(cx, name.into(), element_options)
5561 .expect("Must always be able to create element")
5562 }
5563
5564 pub(crate) fn queue_online_offline_events(&self, online: bool) {
5567 let event_name = Atom::from(if online { "online" } else { "offline" });
5568 let window = self.window();
5569 let global_scope = window.as_global_scope();
5570
5571 global_scope
5583 .task_manager()
5584 .networking_task_source()
5585 .queue_simple_event(window.upcast::<EventTarget>(), event_name);
5586 global_scope.set_network_online_state_on_dedicated_workers(online);
5587 }
5588}
5589
5590impl DocumentMethods<crate::DomTypeHolder> for Document {
5591 fn Constructor(
5593 cx: &mut JSContext,
5594 window: &Window,
5595 proto: Option<HandleObject>,
5596 ) -> Fallible<DomRoot<Document>> {
5597 let doc = window.Document();
5599 let docloader = DocumentLoader::new(&doc.loader());
5600 Ok(Document::new_with_proto(
5601 cx,
5602 window,
5603 proto,
5604 HasBrowsingContext::No,
5605 None,
5606 None,
5607 doc.origin().clone(),
5608 IsHTMLDocument::NonHTMLDocument,
5609 None,
5610 None,
5611 DocumentActivity::Inactive,
5612 docloader,
5613 None,
5614 None,
5615 false,
5616 doc.allow_declarative_shadow_roots(),
5617 Some(doc.insecure_requests_policy()),
5618 doc.has_trustworthy_ancestor_or_current_origin(),
5619 doc.custom_element_reaction_stack(),
5620 doc.active_sandboxing_flag_set.get(),
5621 doc.pipeline_id(),
5622 doc.image_cache(),
5623 ))
5624 }
5625
5626 fn ParseHTMLUnsafe(
5628 cx: &mut JSContext,
5629 window: &Window,
5630 s: TrustedHTMLOrString,
5631 options: &SetHTMLUnsafeOptions,
5632 ) -> Fallible<DomRoot<Self>> {
5633 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
5637 cx,
5638 window.as_global_scope(),
5639 s,
5640 "Document parseHTMLUnsafe",
5641 )?;
5642
5643 let url = window.get_url();
5644 let doc = window.Document();
5645 let loader = DocumentLoader::new(&doc.loader());
5646
5647 let content_type = "text/html"
5648 .parse()
5649 .expect("Supported type is not a MIME type");
5650 let document = Document::new(
5653 cx,
5654 window,
5655 HasBrowsingContext::No,
5656 Some(ServoUrl::parse("about:blank").unwrap()),
5657 None,
5658 doc.origin().clone(),
5659 IsHTMLDocument::HTMLDocument,
5660 Some(content_type),
5661 None,
5662 DocumentActivity::Inactive,
5663 loader,
5664 None,
5665 None,
5666 false,
5667 true,
5668 Some(doc.insecure_requests_policy()),
5669 doc.has_trustworthy_ancestor_or_current_origin(),
5670 doc.custom_element_reaction_stack(),
5671 doc.creation_sandboxing_flag_set(),
5672 doc.pipeline_id(),
5673 doc.image_cache(),
5674 );
5675 ServoParser::parse_html_document(cx, &document, Some(compliant_html), url, None, None);
5677
5678 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, false)?;
5681
5682 sanitizer.sanitize(cx, document.upcast(), false)?;
5684
5685 document.update_the_current_document_readiness(cx, DocumentReadyState::Complete);
5687 Ok(document)
5688 }
5689
5690 fn ParseHTML(
5692 cx: &mut JSContext,
5693 window: &Window,
5694 html: DOMString,
5695 options: &SetHTMLOptions,
5696 ) -> Fallible<DomRoot<Document>> {
5697 let url = window.get_url();
5700 let doc = window.Document();
5701 let loader = DocumentLoader::new(&doc.loader());
5702 let content_type = "text/html"
5703 .parse()
5704 .expect("Supported type is not a MIME type");
5705 let document = Document::new(
5706 cx,
5707 window,
5708 HasBrowsingContext::No,
5709 Some(ServoUrl::parse("about:blank").unwrap()),
5710 None,
5711 doc.origin().clone(),
5712 IsHTMLDocument::HTMLDocument,
5713 Some(content_type),
5714 None,
5715 DocumentActivity::Inactive,
5716 loader,
5717 None,
5718 None,
5719 false,
5720 true,
5721 Some(doc.insecure_requests_policy()),
5722 doc.has_trustworthy_ancestor_or_current_origin(),
5723 doc.custom_element_reaction_stack(),
5724 doc.creation_sandboxing_flag_set(),
5725 doc.pipeline_id(),
5726 doc.image_cache(),
5727 );
5728
5729 ServoParser::parse_html_document(cx, &document, Some(html), url, None, None);
5731
5732 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, true)?;
5735
5736 sanitizer.sanitize(cx, document.upcast(), true)?;
5738
5739 Ok(document)
5741 }
5742
5743 fn StyleSheets(&self, cx: &mut JSContext) -> DomRoot<StyleSheetList> {
5745 self.stylesheet_list.or_init(|| {
5746 StyleSheetList::new(
5747 cx,
5748 &self.window,
5749 StyleSheetListOwner::Document(Dom::from_ref(self)),
5750 )
5751 })
5752 }
5753
5754 fn Implementation(&self, cx: &mut JSContext) -> DomRoot<DOMImplementation> {
5756 self.implementation
5757 .or_init(|| DOMImplementation::new(cx, self))
5758 }
5759
5760 fn URL(&self) -> USVString {
5762 USVString(String::from(self.url().as_str()))
5763 }
5764
5765 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
5767 self.document_or_shadow_root.active_element(self.upcast())
5768 }
5769
5770 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
5772 self.custom_element_registry()
5773 }
5774
5775 fn HasFocus(&self) -> bool {
5777 if !self.window().webview_state().has_system_focus.get() {
5785 return false;
5786 }
5787
5788 if self.window().parent_info().is_none() {
5802 self.is_fully_active()
5804 } else {
5805 self.is_fully_active() && self.focus_handler.has_focus()
5807 }
5808 }
5809
5810 fn Domain(&self) -> DOMString {
5812 match self.origin().effective_domain() {
5814 None => DOMString::new(),
5816 Some(Host::Domain(domain)) => DOMString::from(domain),
5818 Some(host) => DOMString::from(host.to_string()),
5819 }
5820 }
5821
5822 fn SetDomain(&self, value: DOMString) -> ErrorResult {
5824 if !self.has_browsing_context {
5826 return Err(Error::Security(Some(
5827 "Document has no browsing context".into(),
5828 )));
5829 }
5830
5831 if self.has_active_sandboxing_flag(
5834 SandboxingFlagSet::SANDBOXED_DOCUMENT_DOMAIN_BROWSING_CONTEXT_FLAG,
5835 ) {
5836 return Err(Error::Security(Some(
5837 "Sandboxed document cannot set its domain".into(),
5838 )));
5839 }
5840
5841 let effective_domain = match self.origin().effective_domain() {
5843 Some(effective_domain) => effective_domain,
5844 None => return Err(Error::Security(Some("Document's origin is opaque".into()))),
5846 };
5847
5848 let host =
5850 match get_registrable_domain_suffix_of_or_is_equal_to(&value.str(), effective_domain) {
5851 None => return Err(Error::Security(Some("Provided domain is not a registrable domain suffix and is not equal to document's effective domain".into()))),
5852 Some(host) => host,
5853 };
5854
5855 self.origin().set_domain(host);
5860
5861 Ok(())
5862 }
5863
5864 fn Referrer(&self) -> DOMString {
5866 match self.referrer {
5867 Some(ref referrer) => DOMString::from(referrer.to_string()),
5868 None => DOMString::new(),
5869 }
5870 }
5871
5872 fn DocumentURI(&self) -> USVString {
5874 self.URL()
5875 }
5876
5877 fn CompatMode(&self) -> DOMString {
5879 DOMString::from(match self.quirks_mode.get() {
5880 QuirksMode::LimitedQuirks | QuirksMode::NoQuirks => "CSS1Compat",
5881 QuirksMode::Quirks => "BackCompat",
5882 })
5883 }
5884
5885 fn CharacterSet(&self) -> DOMString {
5887 DOMString::from_static(self.encoding.get().name())
5888 }
5889
5890 fn Charset(&self) -> DOMString {
5892 self.CharacterSet()
5893 }
5894
5895 fn InputEncoding(&self) -> DOMString {
5897 self.CharacterSet()
5898 }
5899
5900 fn ContentType(&self) -> DOMString {
5902 DOMString::from(self.content_type.to_string())
5903 }
5904
5905 fn GetDoctype(&self) -> Option<DomRoot<DocumentType>> {
5907 self.upcast::<Node>().children().find_map(DomRoot::downcast)
5908 }
5909
5910 fn GetDocumentElement(&self) -> Option<DomRoot<Element>> {
5912 self.upcast::<Node>().child_elements().next()
5913 }
5914
5915 fn GetElementsByTagName(
5917 &self,
5918 cx: &mut JSContext,
5919 qualified_name: DOMString,
5920 ) -> DomRoot<HTMLCollection> {
5921 let qualified_name = LocalName::from(qualified_name);
5922 if let Some(entry) = self.tag_map.borrow_mut().get(&qualified_name) {
5923 return DomRoot::from_ref(entry);
5924 }
5925 let result = HTMLCollection::by_qualified_name(
5926 cx,
5927 &self.window,
5928 self.upcast(),
5929 qualified_name.clone(),
5930 );
5931 self.tag_map
5932 .borrow_mut()
5933 .insert(qualified_name, Dom::from_ref(&*result));
5934 result
5935 }
5936
5937 fn GetElementsByTagNameNS(
5939 &self,
5940 cx: &mut JSContext,
5941 maybe_ns: Option<DOMString>,
5942 tag_name: DOMString,
5943 ) -> DomRoot<HTMLCollection> {
5944 let ns = namespace_from_domstring(maybe_ns);
5945 let local = LocalName::from(tag_name);
5946 let qname = QualName::new(None, ns, local);
5947 if let Some(collection) = self.tagns_map.borrow().get(&qname) {
5948 return DomRoot::from_ref(collection);
5949 }
5950 let result =
5951 HTMLCollection::by_qual_tag_name(cx, &self.window, self.upcast(), qname.clone());
5952 self.tagns_map
5953 .borrow_mut()
5954 .insert(qname, Dom::from_ref(&*result));
5955 result
5956 }
5957
5958 fn GetElementsByClassName(
5960 &self,
5961 cx: &mut JSContext,
5962 classes: DOMString,
5963 ) -> DomRoot<HTMLCollection> {
5964 let class_atoms: Vec<Atom> = split_html_space_chars(&classes.str())
5965 .map(Atom::from)
5966 .collect();
5967 if let Some(collection) = self.classes_map.borrow().get(&class_atoms) {
5968 return DomRoot::from_ref(collection);
5969 }
5970 let result = HTMLCollection::by_atomic_class_name(
5971 cx,
5972 &self.window,
5973 self.upcast(),
5974 class_atoms.clone(),
5975 );
5976 self.classes_map
5977 .borrow_mut()
5978 .insert(class_atoms, Dom::from_ref(&*result));
5979 result
5980 }
5981
5982 fn GetElementById(
5984 &self,
5985 cx: &js::context::JSContext,
5986 id: DOMString,
5987 ) -> Option<DomRoot<Element>> {
5988 self.get_element_by_id(cx, &Atom::from(id))
5989 }
5990
5991 fn CreateElement(
5993 &self,
5994 cx: &mut JSContext,
5995 mut local_name: DOMString,
5996 options: StringOrElementCreationOptions,
5997 ) -> Fallible<DomRoot<Element>> {
5998 if !is_valid_element_local_name(&local_name.str()) {
6000 return Err(Error::InvalidCharacter(Some(
6001 "Provided element local name is invalid".into(),
6002 )));
6003 }
6004
6005 if self.is_html_document {
6007 local_name.make_ascii_lowercase();
6008 }
6009
6010 let ns = if self.is_html_document || self.is_xhtml_document() {
6012 ns!(html)
6013 } else {
6014 ns!()
6015 };
6016 let name = QualName::new(None, ns, LocalName::from(local_name));
6017
6018 let is = match options {
6019 StringOrElementCreationOptions::String(_) => None,
6020 StringOrElementCreationOptions::ElementCreationOptions(options) => {
6021 options.is.as_ref().map(LocalName::from)
6022 },
6023 };
6024 Ok(Element::create(
6025 cx,
6026 name,
6027 is,
6028 self,
6029 ElementCreator::ScriptCreated,
6030 CustomElementCreationMode::Synchronous,
6031 None,
6032 ))
6033 }
6034
6035 fn CreateElementNS(
6037 &self,
6038 cx: &mut JSContext,
6039 namespace: Option<DOMString>,
6040 qualified_name: DOMString,
6041 options: StringOrElementCreationOptions,
6042 ) -> Fallible<DomRoot<Element>> {
6043 let context = domname::Context::Element;
6046 let (namespace, prefix, local_name) =
6047 domname::validate_and_extract(namespace, &qualified_name, context)?;
6048
6049 let name = QualName::new(prefix, namespace, local_name);
6052 let is = match options {
6053 StringOrElementCreationOptions::String(_) => None,
6054 StringOrElementCreationOptions::ElementCreationOptions(options) => {
6055 options.is.as_ref().map(LocalName::from)
6056 },
6057 };
6058
6059 Ok(Element::create(
6061 cx,
6062 name,
6063 is,
6064 self,
6065 ElementCreator::ScriptCreated,
6066 CustomElementCreationMode::Synchronous,
6067 None,
6068 ))
6069 }
6070
6071 fn CreateAttribute(
6073 &self,
6074 cx: &mut JSContext,
6075 mut local_name: DOMString,
6076 ) -> Fallible<DomRoot<Attr>> {
6077 if !is_valid_attribute_local_name(&local_name.str()) {
6079 return Err(Error::InvalidCharacter(Some(
6080 "Provided local name is invalid".into(),
6081 )));
6082 }
6083
6084 if self.is_html_document {
6086 local_name.make_ascii_lowercase();
6087 }
6088 let name = LocalName::from(local_name);
6089 let value = AttrValue::String(String::new());
6090
6091 Ok(Attr::new(
6092 cx,
6093 self,
6094 name.clone(),
6095 value,
6096 name,
6097 ns!(),
6098 None,
6099 None,
6100 ))
6101 }
6102
6103 fn CreateAttributeNS(
6105 &self,
6106 cx: &mut JSContext,
6107 namespace: Option<DOMString>,
6108 qualified_name: DOMString,
6109 ) -> Fallible<DomRoot<Attr>> {
6110 let context = domname::Context::Attribute;
6113 let (namespace, prefix, local_name) =
6114 domname::validate_and_extract(namespace, &qualified_name, context)?;
6115 let value = AttrValue::String(String::new());
6116 let qualified_name = LocalName::from(qualified_name);
6117 Ok(Attr::new(
6118 cx,
6119 self,
6120 local_name,
6121 value,
6122 qualified_name,
6123 namespace,
6124 prefix,
6125 None,
6126 ))
6127 }
6128
6129 fn CreateDocumentFragment(&self, cx: &mut JSContext) -> DomRoot<DocumentFragment> {
6131 DocumentFragment::new(cx, self)
6132 }
6133
6134 fn CreateTextNode(&self, cx: &mut JSContext, data: DOMString) -> DomRoot<Text> {
6136 Text::new(cx, data, self)
6137 }
6138
6139 fn CreateCDATASection(
6141 &self,
6142 cx: &mut JSContext,
6143 data: DOMString,
6144 ) -> Fallible<DomRoot<CDATASection>> {
6145 if self.is_html_document {
6147 return Err(Error::NotSupported(Some(
6148 "Document must be an XML document".into(),
6149 )));
6150 }
6151
6152 if data.contains("]]>") {
6154 return Err(Error::InvalidCharacter(Some(
6155 "CDATA section cannot include `]]>`".into(),
6156 )));
6157 }
6158
6159 Ok(CDATASection::new(cx, data, self))
6161 }
6162
6163 fn CreateComment(&self, cx: &mut JSContext, data: DOMString) -> DomRoot<Comment> {
6165 Comment::new(cx, data, self, None)
6166 }
6167
6168 fn CreateProcessingInstruction(
6170 &self,
6171 cx: &mut JSContext,
6172 target: DOMString,
6173 data: DOMString,
6174 ) -> Fallible<DomRoot<ProcessingInstruction>> {
6175 if !matches_name_production(&target.str()) {
6177 return Err(Error::InvalidCharacter(Some(
6178 "Target name provided is invalid".into(),
6179 )));
6180 }
6181
6182 if data.contains("?>") {
6184 return Err(Error::InvalidCharacter(Some(
6185 "Processing instruction's data cannot contain `>?`".into(),
6186 )));
6187 }
6188
6189 Ok(ProcessingInstruction::new(cx, target, data, self))
6191 }
6192
6193 fn ImportNode(
6195 &self,
6196 cx: &mut JSContext,
6197 node: &Node,
6198 options: BooleanOrImportNodeOptions,
6199 ) -> Fallible<DomRoot<Node>> {
6200 if node.is::<Document>() || node.is::<ShadowRoot>() {
6202 return Err(Error::NotSupported(Some(
6203 "Node cannot be a document or shadow root".into(),
6204 )));
6205 }
6206 let (subtree, registry) = match options {
6208 BooleanOrImportNodeOptions::Boolean(boolean) => (boolean.into(), None),
6211 BooleanOrImportNodeOptions::ImportNodeOptions(options) => {
6213 let subtree = (!options.selfOnly).into();
6215 let registry = if let Some(registry) = options.customElementRegistry {
6217 let this_registry = self
6220 .custom_element_registry()
6221 .expect("Document must have a custom element registry");
6222 if !registry.is_scoped() && registry != this_registry {
6223 return Err(Error::NotSupported(Some(
6224 "Imported customElementRegistry is not scoped and does not match existing registry.".into()
6225 )));
6226 }
6227 Some(registry)
6228 } else {
6229 None
6230 };
6231 (subtree, registry)
6232 },
6233 };
6234 let registry = registry
6237 .or_else(|| CustomElementRegistry::lookup_a_custom_element_registry(self.upcast()));
6238
6239 Ok(Node::clone(cx, node, Some(self), subtree, registry))
6242 }
6243
6244 fn AdoptNode(&self, cx: &mut JSContext, node: &Node) -> Fallible<DomRoot<Node>> {
6246 if node.is::<Document>() {
6248 return Err(Error::NotSupported(Some(
6249 "Node cannot be a document".into(),
6250 )));
6251 }
6252
6253 if node.is::<ShadowRoot>() {
6255 return Err(Error::HierarchyRequest(Some(
6256 "Node cannot be a shadow root".into(),
6257 )));
6258 }
6259
6260 Node::adopt(cx, node, self);
6262
6263 Ok(DomRoot::from_ref(node))
6265 }
6266
6267 fn CreateEvent(
6269 &self,
6270 cx: &mut JSContext,
6271 mut interface: DOMString,
6272 ) -> Fallible<DomRoot<Event>> {
6273 interface.make_ascii_lowercase();
6274 match &*interface.str() {
6275 "beforeunloadevent" => Ok(DomRoot::upcast(BeforeUnloadEvent::new_uninitialized(
6276 cx,
6277 &self.window,
6278 ))),
6279 "compositionevent" => Ok(DomRoot::upcast(CompositionEvent::new_uninitialized(
6280 cx,
6281 &self.window,
6282 ))),
6283 "customevent" => Ok(DomRoot::upcast(CustomEvent::new_uninitialized(
6284 cx,
6285 self.window.upcast(),
6286 ))),
6287 "events" | "event" | "htmlevents" | "svgevents" => {
6290 Ok(Event::new_uninitialized(cx, self.window.upcast()))
6291 },
6292 "focusevent" => Ok(DomRoot::upcast(FocusEvent::new_uninitialized(
6293 cx,
6294 &self.window,
6295 ))),
6296 "hashchangeevent" => Ok(DomRoot::upcast(HashChangeEvent::new_uninitialized(
6297 cx,
6298 &self.window,
6299 ))),
6300 "keyboardevent" => Ok(DomRoot::upcast(KeyboardEvent::new_uninitialized(
6301 cx,
6302 &self.window,
6303 ))),
6304 "messageevent" => Ok(DomRoot::upcast(MessageEvent::new_uninitialized(
6305 cx,
6306 self.window.upcast(),
6307 ))),
6308 "mouseevent" | "mouseevents" => Ok(DomRoot::upcast(MouseEvent::new_uninitialized(
6309 cx,
6310 &self.window,
6311 ))),
6312 "storageevent" => Ok(DomRoot::upcast(StorageEvent::new_uninitialized(
6313 cx,
6314 &self.window,
6315 DOMString::new(),
6316 ))),
6317 "textevent" => Ok(DomRoot::upcast(TextEvent::new_uninitialized(
6318 cx,
6319 &self.window,
6320 ))),
6321 "touchevent" => {
6322 let touches = TouchList::new(cx, &self.window, &[]);
6323 let changed_touches = TouchList::new(cx, &self.window, &[]);
6324 let target_touches = TouchList::new(cx, &self.window, &[]);
6325
6326 Ok(DomRoot::upcast(DomTouchEvent::new_uninitialized(
6327 cx,
6328 &self.window,
6329 &touches,
6330 &changed_touches,
6331 &target_touches,
6332 )))
6333 },
6334 "uievent" | "uievents" => Ok(DomRoot::upcast(UIEvent::new_uninitialized(
6335 cx,
6336 &self.window,
6337 ))),
6338 _ => Err(Error::NotSupported(Some(
6339 "Interface is not supported".into(),
6340 ))),
6341 }
6342 }
6343
6344 fn LastModified(&self) -> DOMString {
6346 DOMString::from(self.last_modified.as_ref().cloned().unwrap_or_else(|| {
6347 Local::now().format("%m/%d/%Y %H:%M:%S").to_string()
6353 }))
6354 }
6355
6356 fn CreateRange(&self, cx: &mut JSContext) -> DomRoot<Range> {
6358 Range::new_with_doc(cx, self, None)
6359 }
6360
6361 fn CreateNodeIterator(
6363 &self,
6364 cx: &mut js::context::JSContext,
6365 root: &Node,
6366 what_to_show: u32,
6367 filter: Option<RootedCallback<NodeFilter>>,
6368 ) -> DomRoot<NodeIterator> {
6369 NodeIterator::new(cx, self, root, what_to_show, filter)
6370 }
6371
6372 fn CreateTreeWalker(
6374 &self,
6375 cx: &mut JSContext,
6376 root: &Node,
6377 what_to_show: u32,
6378 filter: Option<RootedCallback<NodeFilter>>,
6379 ) -> DomRoot<TreeWalker> {
6380 TreeWalker::new(cx, self, root, what_to_show, filter)
6381 }
6382
6383 fn Title(&self) -> DOMString {
6385 self.title().unwrap_or_default()
6386 }
6387
6388 fn SetTitle(&self, cx: &mut JSContext, title: DOMString) {
6390 let root = match self.GetDocumentElement() {
6391 Some(root) => root,
6392 None => return,
6393 };
6394
6395 let node = if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
6398 let elem = root
6401 .upcast::<Node>()
6402 .child_elements_unrooted(cx.no_gc())
6403 .find(|node| {
6404 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
6405 });
6406 match elem {
6407 Some(elem) => UnrootedDom::upcast::<Node>(elem).as_rooted(),
6408 None => {
6410 let name = QualName::new(None, ns!(svg), local_name!("title"));
6413 let elem = Element::create(
6414 cx,
6415 name,
6416 None,
6417 self,
6418 ElementCreator::ScriptCreated,
6419 CustomElementCreationMode::Synchronous,
6420 None,
6421 );
6422
6423 let parent = root.upcast::<Node>();
6425 let child = elem.upcast::<Node>();
6426 parent
6427 .InsertBefore(cx, child, parent.GetFirstChild().as_deref())
6428 .unwrap()
6429 },
6430 }
6431 }
6432 else if root.namespace() == &ns!(html) {
6434 let elem = root
6435 .upcast::<Node>()
6436 .traverse_preorder_unrooted(cx.no_gc(), ShadowIncluding::No)
6437 .find(|node| node.is::<HTMLTitleElement>());
6438 match elem {
6439 Some(elem) => elem.as_rooted(),
6441 None => match self.GetHead() {
6443 Some(head) => {
6444 let name = QualName::new(None, ns!(html), local_name!("title"));
6447 let elem = Element::create(
6448 cx,
6449 name,
6450 None,
6451 self,
6452 ElementCreator::ScriptCreated,
6453 CustomElementCreationMode::Synchronous,
6454 None,
6455 );
6456
6457 head.upcast::<Node>()
6459 .AppendChild(cx, elem.upcast())
6460 .unwrap()
6461 },
6462 None => return,
6464 },
6465 }
6466 }
6467 else {
6469 return;
6471 };
6472
6473 node.set_text_content_for_element(cx, Some(title));
6478 }
6479
6480 fn Dir(&self) -> DOMString {
6482 self.get_html_element()
6483 .map(|html| html.upcast::<HTMLElement>().Dir())
6484 .unwrap_or_default()
6485 }
6486
6487 fn SetDir(&self, cx: &mut JSContext, dir: DOMString) {
6489 if let Some(html) = self.get_html_element() {
6490 html.upcast::<HTMLElement>().SetDir(cx, dir);
6491 }
6492 }
6493
6494 fn GetHead(&self) -> Option<DomRoot<HTMLHeadElement>> {
6496 self.get_html_element()
6497 .and_then(|root| root.upcast::<Node>().children().find_map(DomRoot::downcast))
6498 }
6499
6500 fn GetCurrentScript(&self) -> Option<DomRoot<HTMLScriptElement>> {
6502 self.current_script.get()
6503 }
6504
6505 fn GetBody(&self) -> Option<DomRoot<HTMLElement>> {
6507 self.get_html_element().and_then(|root| {
6510 let node = root.upcast::<Node>();
6511 node.children()
6512 .find(|child| {
6513 matches!(
6514 child.type_id(),
6515 NodeTypeId::Element(ElementTypeId::HTMLElement(
6516 HTMLElementTypeId::HTMLBodyElement,
6517 )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
6518 HTMLElementTypeId::HTMLFrameSetElement,
6519 ))
6520 )
6521 })
6522 .map(|node| DomRoot::downcast(node).unwrap())
6523 })
6524 }
6525
6526 fn SetBody(&self, cx: &mut JSContext, new_body: Option<&HTMLElement>) -> ErrorResult {
6528 let new_body = match new_body {
6530 Some(new_body) => new_body,
6531 None => {
6532 return Err(Error::HierarchyRequest(Some(
6533 "HTML element provided is neither a body nor a frameset element".into(),
6534 )));
6535 },
6536 };
6537
6538 let node = new_body.upcast::<Node>();
6539 match node.type_id() {
6540 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) |
6541 NodeTypeId::Element(ElementTypeId::HTMLElement(
6542 HTMLElementTypeId::HTMLFrameSetElement,
6543 )) => {},
6544 _ => {
6545 return Err(Error::HierarchyRequest(Some(
6546 "HTML element provided is neither a body nor a frameset element".into(),
6547 )));
6548 },
6549 }
6550
6551 let old_body = self.GetBody();
6553 if old_body.as_deref() == Some(new_body) {
6554 return Ok(());
6555 }
6556
6557 match (self.GetDocumentElement(), &old_body) {
6558 (Some(ref root), Some(child)) => {
6561 let root = root.upcast::<Node>();
6562 root.ReplaceChild(cx, new_body.upcast(), child.upcast())
6563 .map(|_| ())
6564 },
6565
6566 (None, _) => Err(Error::HierarchyRequest(Some(
6568 "Document element is missing".into(),
6569 ))),
6570
6571 (Some(ref root), &None) => {
6574 let root = root.upcast::<Node>();
6575 root.AppendChild(cx, new_body.upcast()).map(|_| ())
6576 },
6577 }
6578 }
6579
6580 fn GetElementsByName(&self, cx: &mut JSContext, name: DOMString) -> DomRoot<NodeList> {
6582 NodeList::new_elements_by_name_list(cx, self.window(), self, name)
6583 }
6584
6585 fn Images(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6587 self.images.or_init(|| {
6588 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6589 element.is::<HTMLImageElement>()
6590 })
6591 })
6592 }
6593
6594 fn Embeds(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6596 self.embeds.or_init(|| {
6597 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6598 element.is::<HTMLEmbedElement>()
6599 })
6600 })
6601 }
6602
6603 fn Plugins(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6605 self.Embeds(cx)
6606 }
6607
6608 fn Links(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6610 self.links.or_init(|| {
6611 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6612 (element.is::<HTMLAnchorElement>() || element.is::<HTMLAreaElement>()) &&
6613 element.has_attribute(&local_name!("href"))
6614 })
6615 })
6616 }
6617
6618 fn Forms(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6620 self.forms.or_init(|| {
6621 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6622 element.is::<HTMLFormElement>()
6623 })
6624 })
6625 }
6626
6627 fn Scripts(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6629 self.scripts.or_init(|| {
6630 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6631 element.is::<HTMLScriptElement>()
6632 })
6633 })
6634 }
6635
6636 fn Anchors(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6638 self.anchors.or_init(|| {
6639 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6640 element.is::<HTMLAnchorElement>() && element.has_attribute(&local_name!("href"))
6641 })
6642 })
6643 }
6644
6645 fn Applets(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6647 self.applets
6648 .or_init(|| HTMLCollection::always_empty(cx, &self.window, self.upcast()))
6649 }
6650
6651 fn GetLocation(&self, cx: &mut JSContext) -> Option<DomRoot<Location>> {
6653 if self.is_fully_active() {
6654 Some(self.window.Location(cx))
6655 } else {
6656 None
6657 }
6658 }
6659
6660 fn Children(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6662 HTMLCollection::children(cx, &self.window, self.upcast())
6663 }
6664
6665 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
6667 self.upcast::<Node>().child_elements().next()
6668 }
6669
6670 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
6672 self.upcast::<Node>()
6673 .rev_children()
6674 .find_map(DomRoot::downcast)
6675 }
6676
6677 fn ChildElementCount(&self) -> u32 {
6679 self.upcast::<Node>().child_elements().count() as u32
6680 }
6681
6682 fn Prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6684 self.upcast::<Node>().prepend(cx, nodes)
6685 }
6686
6687 fn Append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6689 self.upcast::<Node>().append(cx, nodes)
6690 }
6691
6692 fn ReplaceChildren(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6694 self.upcast::<Node>().replace_children(cx, nodes)
6695 }
6696
6697 fn MoveBefore(&self, cx: &mut JSContext, node: &Node, child: Option<&Node>) -> ErrorResult {
6699 self.upcast::<Node>().move_before(cx, node, child)
6700 }
6701
6702 fn QuerySelector(
6704 &self,
6705 cx: &mut JSContext,
6706 selectors: DOMString,
6707 ) -> Fallible<Option<DomRoot<Element>>> {
6708 self.upcast::<Node>().query_selector(cx.no_gc(), selectors)
6709 }
6710
6711 fn QuerySelectorAll(
6713 &self,
6714 cx: &mut JSContext,
6715 selectors: DOMString,
6716 ) -> Fallible<DomRoot<NodeList>> {
6717 self.upcast::<Node>().query_selector_all(cx, selectors)
6718 }
6719
6720 fn ReadyState(&self) -> DocumentReadyState {
6722 self.ready_state.get()
6723 }
6724
6725 fn GetDefaultView(&self) -> Option<DomRoot<Window>> {
6727 if self.has_browsing_context {
6728 Some(DomRoot::from_ref(&*self.window))
6729 } else {
6730 None
6731 }
6732 }
6733
6734 fn GetCookie(&self) -> Fallible<DOMString> {
6736 if self.is_cookie_averse() {
6737 return Ok(DOMString::new());
6738 }
6739
6740 if !self.origin().is_tuple() {
6741 return Err(Error::Security(Some("Document's origin is opaque".into())));
6742 }
6743
6744 let url = self.url();
6745 let (tx, rx) =
6746 profile_generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
6747 let _ = self
6748 .window
6749 .as_global_scope()
6750 .resource_threads()
6751 .send(GetCookieStringForUrl(url, tx, NonHTTP));
6752 let cookies = rx.recv().unwrap();
6753 Ok(cookies.map_or(DOMString::new(), DOMString::from))
6754 }
6755
6756 fn SetCookie(&self, cookie: DOMString) -> ErrorResult {
6758 if self.is_cookie_averse() {
6759 return Ok(());
6760 }
6761
6762 if !self.origin().is_tuple() {
6763 return Err(Error::Security(Some("Document's origin is opaque".into())));
6764 }
6765
6766 if !cookie.is_valid_for_cookie() {
6767 return Ok(());
6768 }
6769
6770 let cookies = if let Some(cookie) = Cookie::parse(cookie.to_string()).ok().map(Serde) {
6771 vec![cookie]
6772 } else {
6773 vec![]
6774 };
6775
6776 let _ = self
6777 .window
6778 .as_global_scope()
6779 .resource_threads()
6780 .send(SetCookiesForUrl(self.url(), cookies, NonHTTP));
6781 Ok(())
6782 }
6783
6784 fn BgColor(&self) -> DOMString {
6786 self.get_body_attribute(&local_name!("bgcolor"))
6787 }
6788
6789 fn SetBgColor(&self, cx: &mut JSContext, value: DOMString) {
6791 self.set_body_attribute(cx, &local_name!("bgcolor"), value)
6792 }
6793
6794 fn FgColor(&self) -> DOMString {
6796 self.get_body_attribute(&local_name!("text"))
6797 }
6798
6799 fn SetFgColor(&self, cx: &mut JSContext, value: DOMString) {
6801 self.set_body_attribute(cx, &local_name!("text"), value)
6802 }
6803
6804 fn NamedGetter(&self, cx: &mut JSContext, name: DOMString) -> Option<NamedPropertyValue> {
6806 if name.is_empty() {
6807 return None;
6808 }
6809 let name = Atom::from(name);
6810
6811 let elements_with_name = self.get_elements_with_name(cx, &name);
6814 let name_iter = elements_with_name
6815 .iter()
6816 .filter(|elem| is_named_element_with_name_attribute(elem));
6817 let elements_with_id = self.id_map.get_all(cx.no_gc(), self.upcast(), &name);
6818 let id_iter = elements_with_id
6819 .iter()
6820 .filter(|elem| is_named_element_with_id_attribute(elem));
6821 let mut elements = name_iter.chain(id_iter);
6822
6823 let first = elements.next()?;
6830 if elements.all(|other| first == other) {
6831 if let Some(nested_window_proxy) = first
6832 .downcast::<HTMLIFrameElement>()
6833 .and_then(|iframe| iframe.GetContentWindow())
6834 {
6835 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
6836 }
6837
6838 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
6840 }
6841
6842 #[derive(JSTraceable, MallocSizeOf)]
6845 struct DocumentNamedGetter {
6846 #[no_trace]
6847 name: Atom,
6848 }
6849 impl CollectionFilter for DocumentNamedGetter {
6850 fn filter(&self, elem: &Element, _root: &Node) -> bool {
6851 let type_ = match elem.upcast::<Node>().type_id() {
6852 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
6853 _ => return false,
6854 };
6855 match type_ {
6856 HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
6857 elem.get_name().as_ref() == Some(&self.name)
6858 },
6859 HTMLElementTypeId::HTMLImageElement => elem.get_name().is_some_and(|name| {
6860 name == *self.name ||
6861 !name.is_empty() && elem.get_id().as_ref() == Some(&self.name)
6862 }),
6863 _ => false,
6867 }
6868 }
6869 }
6870 let collection = HTMLCollection::create(
6871 cx,
6872 self.window(),
6873 self.upcast(),
6874 Box::new(DocumentNamedGetter { name }),
6875 );
6876 Some(NamedPropertyValue::HTMLCollection(collection))
6877 }
6878
6879 fn SupportedPropertyNames(&self, no_gc: &NoGC) -> Vec<DOMString> {
6881 let mut names_with_first_named_element_map = HashMap::new();
6882 self.name_map
6883 .for_each(no_gc, self.upcast(), |name, elements| {
6884 if name.is_empty() {
6885 return;
6886 }
6887 let mut name_iter = elements
6888 .iter()
6889 .filter(|elem| is_named_element_with_name_attribute(elem));
6890 if let Some(first) = name_iter.next() {
6891 names_with_first_named_element_map.insert(name.clone(), first.as_rooted());
6892 }
6893 });
6894
6895 self.id_map.for_each(no_gc, self.upcast(), |id, elements| {
6896 if id.is_empty() {
6897 return;
6898 }
6899 let mut id_iter = elements
6900 .iter()
6901 .filter(|elem| is_named_element_with_id_attribute(elem));
6902 if let Some(first) = id_iter.next() {
6903 match names_with_first_named_element_map.entry(id.clone()) {
6904 Vacant(entry) => drop(entry.insert(first.as_rooted())),
6905 Occupied(mut entry) => {
6906 if first
6907 .upcast::<Node>()
6908 .is_before(no_gc, entry.get().upcast())
6909 {
6910 *entry.get_mut() = first.as_rooted();
6911 }
6912 },
6913 }
6914 }
6915 });
6916
6917 let mut names_with_first_named_element_vec: Vec<_> =
6918 names_with_first_named_element_map.into_iter().collect();
6919 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
6920 if a.1 == b.1 {
6921 a.0.cmp(&b.0)
6924 } else if a.1.upcast::<Node>().is_before(no_gc, b.1.upcast::<Node>()) {
6925 Ordering::Less
6926 } else {
6927 Ordering::Greater
6928 }
6929 });
6930
6931 names_with_first_named_element_vec
6932 .into_iter()
6933 .map(|(k, _)| TracedDOMString::from(k).root())
6934 .collect()
6935 }
6936
6937 fn Clear(&self) {
6939 }
6941
6942 fn CaptureEvents(&self) {
6944 }
6946
6947 fn ReleaseEvents(&self) {
6949 }
6951
6952 global_event_handlers!();
6954
6955 event_handler!(
6957 readystatechange,
6958 GetOnreadystatechange,
6959 SetOnreadystatechange
6960 );
6961
6962 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
6964 self.document_or_shadow_root.element_from_point(
6965 self.upcast(),
6966 x,
6967 y,
6968 self.GetDocumentElement(),
6969 self.has_browsing_context,
6970 )
6971 }
6972
6973 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
6975 self.document_or_shadow_root.elements_from_point(
6976 self.upcast(),
6977 x,
6978 y,
6979 self.GetDocumentElement(),
6980 self.has_browsing_context,
6981 )
6982 }
6983
6984 fn GetScrollingElement(&self) -> Option<DomRoot<Element>> {
6986 if self.quirks_mode() == QuirksMode::Quirks {
6988 if let Some(ref body) = self.GetBody() {
6990 let e = body.upcast::<Element>();
6991 if !e.is_potentially_scrollable_body_for_scrolling_element() {
6995 return Some(DomRoot::from_ref(e));
6996 }
6997 }
6998
6999 return None;
7001 }
7002
7003 self.GetDocumentElement()
7006 }
7007
7008 fn Open(
7010 &self,
7011 cx: &mut JSContext,
7012 _unused1: Option<DOMString>,
7013 _unused2: Option<DOMString>,
7014 ) -> Fallible<DomRoot<Document>> {
7015 if !self.is_html_document() {
7017 return Err(Error::InvalidState(Some(
7018 "Document must be a HTML document".into(),
7019 )));
7020 }
7021
7022 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
7025 return Err(Error::InvalidState(Some(
7026 "A custom element constructor attempted to open, close or write to this document"
7027 .into(),
7028 )));
7029 }
7030
7031 let entry_responsible_document = GlobalScope::entry().as_window().Document();
7033
7034 if !self
7037 .origin()
7038 .same_origin(&entry_responsible_document.origin())
7039 {
7040 return Err(Error::Security(Some(
7041 "Document's origin is not the same as entry global's document origin".into(),
7042 )));
7043 }
7044
7045 if self
7048 .active_parser()
7049 .is_some_and(|parser| parser.script_nesting_level() > 0)
7050 {
7051 return Ok(DomRoot::from_ref(self));
7052 }
7053
7054 if self.is_prompting_or_unloading() {
7056 return Ok(DomRoot::from_ref(self));
7057 }
7058
7059 if self.active_parser_was_aborted.get() {
7061 return Ok(DomRoot::from_ref(self));
7062 }
7063
7064 self.window().set_navigation_start();
7068
7069 if self.has_browsing_context() {
7073 self.abort(cx, AbortReason::DocumentOpen);
7076 }
7077
7078 for node in self
7081 .upcast::<Node>()
7082 .traverse_preorder_unrooted(cx.no_gc(), ShadowIncluding::Yes)
7083 {
7084 node.upcast::<EventTarget>().remove_all_listeners(cx);
7085 }
7086
7087 if self.window.Document() == DomRoot::from_ref(self) {
7090 self.window.upcast::<EventTarget>().remove_all_listeners(cx);
7091 }
7092
7093 Node::replace_all(cx, None, self.upcast::<Node>());
7095
7096 if self.is_fully_active() {
7103 let mut new_url = entry_responsible_document.url();
7105
7106 if entry_responsible_document != DomRoot::from_ref(self) {
7108 new_url.set_fragment(None);
7109 }
7110
7111 self.set_url(new_url);
7114 }
7115
7116 self.is_initial_about_blank.set(false);
7118
7119 if self.iframe_load_in_progress.get() {
7122 self.mute_iframe_load.set(true);
7123 }
7124
7125 self.set_quirks_mode(QuirksMode::NoQuirks);
7127
7128 let resource_threads = self.window.as_global_scope().resource_threads().clone();
7134 *self.loader.borrow_mut() =
7135 DocumentLoader::new_with_threads(resource_threads, Some(self.url()));
7136 ServoParser::parse_html_script_input(cx, self, self.url());
7137
7138 self.update_the_current_document_readiness(cx, DocumentReadyState::Loading);
7144
7145 Ok(DomRoot::from_ref(self))
7147 }
7148
7149 fn Open_(
7151 &self,
7152 cx: &mut JSContext,
7153 url: USVString,
7154 target: DOMString,
7155 features: DOMString,
7156 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
7157 if self.is_fully_active() {
7158 self.browsing_context()
7159 .ok_or(Error::InvalidAccess(Some(
7160 "Failed to get browsing context".into(),
7161 )))?
7162 .open(cx, url, target, features)
7163 } else {
7164 Err(Error::InvalidAccess(Some(
7165 "Document is not fully active".into(),
7166 )))
7167 }
7168 }
7169
7170 fn Write(&self, cx: &mut JSContext, text: Vec<TrustedHTMLOrString>) -> ErrorResult {
7172 self.write(cx, text, false, "Document", "write")
7175 }
7176
7177 fn Writeln(&self, cx: &mut JSContext, text: Vec<TrustedHTMLOrString>) -> ErrorResult {
7179 self.write(cx, text, true, "Document", "writeln")
7182 }
7183
7184 fn Close(&self, cx: &mut JSContext) -> ErrorResult {
7186 if !self.is_html_document() {
7187 return Err(Error::InvalidState(Some(
7189 "Document must be a HTML document".into(),
7190 )));
7191 }
7192
7193 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
7196 return Err(Error::InvalidState(Some(
7197 "A custom element constructor attempted to open, close or write to this document"
7198 .into(),
7199 )));
7200 }
7201
7202 let parser = match self.get_current_parser() {
7204 Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
7205 _ => {
7206 return Ok(());
7207 },
7208 };
7209
7210 parser.close(cx);
7212
7213 Ok(())
7214 }
7215
7216 fn ExecCommand(
7218 &self,
7219 cx: &mut JSContext,
7220 command_id: DOMString,
7221 _show_ui: bool,
7222 value: TrustedHTMLOrString,
7223 ) -> Fallible<bool> {
7224 let value = if command_id == "insertHTML" {
7225 TrustedHTML::get_trusted_type_compliant_string(
7226 cx,
7227 self.window.as_global_scope(),
7228 value,
7229 "Document execCommand",
7230 )?
7231 } else {
7232 match value {
7233 TrustedHTMLOrString::TrustedHTML(trusted_html) => {
7234 trusted_html.data().clone().root()
7235 },
7236 TrustedHTMLOrString::String(value) => value,
7237 }
7238 };
7239
7240 Ok(self.exec_command_for_command_id(cx, command_id, value))
7241 }
7242
7243 fn QueryCommandEnabled(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
7245 self.check_support_and_enabled(cx, &command_id).is_some()
7247 }
7248
7249 fn QueryCommandSupported(&self, command_id: DOMString) -> bool {
7251 self.is_command_supported(command_id)
7255 }
7256
7257 fn QueryCommandIndeterm(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
7259 self.is_command_indeterminate(cx, command_id)
7260 }
7261
7262 fn QueryCommandState(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
7264 self.command_state_for_command(cx, command_id)
7265 }
7266
7267 fn QueryCommandValue(&self, cx: &mut JSContext, command_id: DOMString) -> DOMString {
7269 self.command_value_for_command(cx, command_id)
7270 }
7271
7272 event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror);
7274
7275 event_handler!(
7277 fullscreenchange,
7278 GetOnfullscreenchange,
7279 SetOnfullscreenchange
7280 );
7281
7282 fn FullscreenEnabled(&self) -> bool {
7284 self.get_allow_fullscreen()
7285 }
7286
7287 fn Fullscreen(&self) -> bool {
7289 self.fullscreen_element.get().is_some()
7290 }
7291
7292 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
7294 DocumentOrShadowRoot::get_fullscreen_element(&self.node, self.fullscreen_element.get())
7295 }
7296
7297 fn ExitFullscreen(&self, cx: &mut CurrentRealm) -> RootedPromise {
7299 self.exit_fullscreen(cx)
7300 }
7301
7302 fn ServoGetMediaControls(&self, id: DOMString) -> Fallible<DomRoot<ShadowRoot>> {
7306 match self.media_controls.borrow().get(&*id.str()) {
7307 Some(m) => Ok(DomRoot::from_ref(m)),
7308 None => Err(Error::InvalidAccess(Some(
7309 "No registered media controls exist with provided id".into(),
7310 ))),
7311 }
7312 }
7313
7314 fn GetSelection(&self, cx: &mut JSContext) -> Option<DomRoot<Selection>> {
7316 if self.has_browsing_context {
7317 Some(self.selection.or_init(|| Selection::new(cx, self)))
7318 } else {
7319 None
7320 }
7321 }
7322
7323 fn Fonts(&self, cx: &mut JSContext) -> DomRoot<FontFaceSet> {
7325 self.fonts
7326 .or_init(|| FontFaceSet::new(cx, &self.global(), None))
7327 }
7328
7329 fn Hidden(&self) -> bool {
7331 self.visibility_state.get() == DocumentVisibilityState::Hidden
7332 }
7333
7334 fn VisibilityState(&self) -> DocumentVisibilityState {
7336 self.visibility_state.get()
7337 }
7338
7339 fn CreateExpression(
7340 &self,
7341 cx: &mut JSContext,
7342 expression: DOMString,
7343 resolver: Option<RootedCallback<XPathNSResolver>>,
7344 ) -> Fallible<DomRoot<crate::dom::types::XPathExpression>> {
7345 let parsed_expression =
7346 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
7347 Ok(XPathExpression::new(
7348 cx,
7349 &self.window,
7350 None,
7351 parsed_expression,
7352 ))
7353 }
7354
7355 fn CreateNSResolver(&self, cx: &mut JSContext, node_resolver: &Node) -> DomRoot<Node> {
7356 let global = self.global();
7357 let window = global.as_window();
7358 let evaluator = XPathEvaluator::new(cx, window, None);
7359 XPathEvaluatorMethods::<crate::DomTypeHolder>::CreateNSResolver(&*evaluator, node_resolver)
7360 }
7361
7362 fn Evaluate(
7363 &self,
7364 cx: &mut JSContext,
7365 expression: DOMString,
7366 context_node: &Node,
7367 resolver: Option<RootedCallback<XPathNSResolver>>,
7368 result_type: u16,
7369 result: Option<&crate::dom::types::XPathResult>,
7370 ) -> Fallible<DomRoot<crate::dom::types::XPathResult>> {
7371 let parsed_expression =
7372 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
7373 XPathExpression::new(cx, &self.window, None, parsed_expression).evaluate_internal(
7374 cx,
7375 context_node,
7376 result_type,
7377 result,
7378 )
7379 }
7380
7381 fn OnSetAdoptedStyleSheets(
7388 &self,
7389 cx: &mut JSContext,
7390 value: DomRoot<CSSStyleSheet>,
7391 index: u32,
7392 ) -> ErrorResult {
7393 DocumentOrShadowRoot::on_set_adopted_stylesheets(
7394 self.adopted_stylesheets.safe_borrow_mut(cx).as_mut(),
7395 &value,
7396 index,
7397 &StyleSheetListOwner::Document(Dom::from_ref(self)),
7398 )
7399 }
7400
7401 fn OnDeleteAdoptedStyleSheets(
7408 &self,
7409 cx: &mut JSContext,
7410 _value: DomRoot<CSSStyleSheet>,
7411 index: u32,
7412 ) -> ErrorResult {
7413 DocumentOrShadowRoot::on_delete_adopted_stylesheets(
7414 self.adopted_stylesheets.safe_borrow_mut(cx).as_mut(),
7415 index,
7416 &StyleSheetListOwner::Document(Dom::from_ref(self)),
7417 )
7418 }
7419
7420 fn Timeline(&self) -> DomRoot<DocumentTimeline> {
7421 self.timeline.as_rooted()
7422 }
7423}
7424
7425fn update_with_current_instant(marker: &Cell<Option<CrossProcessInstant>>) {
7426 if marker.get().is_none() {
7427 marker.set(Some(CrossProcessInstant::now()))
7428 }
7429}
7430
7431#[derive(JSTraceable, MallocSizeOf)]
7432pub(crate) enum AnimationFrameCallback {
7433 #[cfg(feature = "devtools")]
7434 DevtoolsFramerateTick { actor_name: String },
7435 FrameRequestCallback {
7436 callback: TracedCallback<FrameRequestCallback>,
7437 },
7438}
7439
7440impl js::gc::Rootable for AnimationFrameCallback {}
7441
7442impl AnimationFrameCallback {
7443 fn call(&self, cx: &mut JSContext, document: &Document, now: f64) {
7444 match *self {
7445 #[cfg(feature = "devtools")]
7446 AnimationFrameCallback::DevtoolsFramerateTick { ref actor_name } => {
7447 let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name.clone(), now);
7448 let devtools_sender = document.window().as_global_scope().devtools_chan().unwrap();
7449 devtools_sender.send(msg).unwrap();
7450 },
7451 AnimationFrameCallback::FrameRequestCallback { ref callback } => {
7452 let _ = callback.Call__(cx, Finite::wrap(now), ExceptionHandling::Report);
7455 },
7456 }
7457 }
7458}
7459
7460#[derive(Default, JSTraceable, MallocSizeOf)]
7461#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
7462struct PendingInOrderScriptVec {
7463 scripts: DomRefCell<VecDeque<PendingScript>>,
7464}
7465
7466impl PendingInOrderScriptVec {
7467 fn is_empty(&self) -> bool {
7468 self.scripts.borrow().is_empty()
7469 }
7470
7471 fn push(&self, element: &HTMLScriptElement) {
7472 self.scripts
7473 .borrow_mut()
7474 .push_back(PendingScript::new(element));
7475 }
7476
7477 fn loaded(&self, element: &HTMLScriptElement, result: ScriptResult) {
7478 let mut scripts = self.scripts.borrow_mut();
7479 let entry = scripts
7480 .iter_mut()
7481 .find(|entry| &*entry.element == element)
7482 .unwrap();
7483 entry.loaded(result);
7484 }
7485
7486 fn take_next_ready_to_be_executed(&self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
7487 let mut scripts = self.scripts.borrow_mut();
7488 let pair = scripts.front_mut()?.take_result()?;
7489 scripts.pop_front();
7490 Some(pair)
7491 }
7492
7493 fn clear(&self) {
7494 *self.scripts.borrow_mut() = Default::default();
7495 }
7496}
7497
7498#[derive(JSTraceable, MallocSizeOf)]
7499#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
7500struct PendingScript {
7501 element: Dom<HTMLScriptElement>,
7502 load: Option<TracedScriptResult>,
7503}
7504
7505impl PendingScript {
7506 fn new(element: &HTMLScriptElement) -> Self {
7507 Self {
7508 element: Dom::from_ref(element),
7509 load: None,
7510 }
7511 }
7512
7513 fn new_with_load(element: &HTMLScriptElement, load: Option<ScriptResult>) -> Self {
7514 Self {
7515 element: Dom::from_ref(element),
7516 load: load.map(|result| result.map(Into::into)),
7517 }
7518 }
7519
7520 fn loaded(&mut self, result: ScriptResult) {
7521 assert!(self.load.is_none());
7522 self.load = Some(result.map(Into::into));
7523 }
7524
7525 fn take_result(&mut self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
7526 self.load
7527 .take()
7528 .map(|result| (DomRoot::from_ref(&*self.element), result.map(Into::into)))
7529 }
7530}
7531
7532fn is_named_element_with_name_attribute(elem: &Element) -> bool {
7533 let type_ = match elem.upcast::<Node>().type_id() {
7534 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
7535 _ => return false,
7536 };
7537 match type_ {
7538 HTMLElementTypeId::HTMLFormElement |
7539 HTMLElementTypeId::HTMLIFrameElement |
7540 HTMLElementTypeId::HTMLImageElement => true,
7541 _ => false,
7545 }
7546}
7547
7548fn is_named_element_with_id_attribute(elem: &Element) -> bool {
7549 elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty())
7553}
7554
7555impl DocumentHelpers for Document {
7556 fn ensure_safe_to_run_script_or_layout(&self) {
7557 Document::ensure_safe_to_run_script_or_layout(self)
7558 }
7559}
7560
7561pub(crate) struct SameoriginAncestorNavigablesIterator {
7565 document: DomRoot<Document>,
7566}
7567
7568impl SameoriginAncestorNavigablesIterator {
7569 pub(crate) fn new(document: DomRoot<Document>) -> Self {
7570 Self { document }
7571 }
7572}
7573
7574impl Iterator for SameoriginAncestorNavigablesIterator {
7575 type Item = DomRoot<Document>;
7576
7577 fn next(&mut self) -> Option<Self::Item> {
7578 let window_proxy = self.document.browsing_context()?;
7579 self.document = window_proxy.parent()?.document()?;
7580 Some(self.document.clone())
7581 }
7582}
7583
7584pub(crate) struct SameOriginDescendantNavigablesIterator {
7589 stack: Vec<Box<dyn Iterator<Item = DomRoot<HTMLIFrameElement>>>>,
7590}
7591
7592impl SameOriginDescendantNavigablesIterator {
7593 pub(crate) fn new(document: &Document) -> Self {
7594 let iframes: Vec<DomRoot<HTMLIFrameElement>> = document.iframes().iter().collect();
7595 Self {
7596 stack: vec![Box::new(iframes.into_iter())],
7597 }
7598 }
7599
7600 fn get_next_iframe(&mut self) -> Option<DomRoot<HTMLIFrameElement>> {
7601 let mut cur_iframe = self.stack.last_mut()?.next();
7602 while cur_iframe.is_none() {
7603 self.stack.pop();
7604 cur_iframe = self.stack.last_mut()?.next();
7605 }
7606 cur_iframe
7607 }
7608}
7609
7610impl Iterator for SameOriginDescendantNavigablesIterator {
7611 type Item = DomRoot<Document>;
7612
7613 fn next(&mut self) -> Option<Self::Item> {
7614 while let Some(iframe) = self.get_next_iframe() {
7615 let Some(pipeline_id) = iframe.pipeline_id() else {
7616 continue;
7617 };
7618
7619 if let Some(document) = ScriptThread::find_document(pipeline_id) {
7620 let child_iframes: Vec<DomRoot<HTMLIFrameElement>> =
7621 document.iframes().iter().collect();
7622 self.stack.push(Box::new(child_iframes.into_iter()));
7623 return Some(document);
7624 } else {
7625 continue;
7626 };
7627 }
7628 None
7629 }
7630}