1use std::cell::{Cell, RefCell};
6use std::cmp::Ordering;
7use std::collections::hash_map::Entry::{Occupied, Vacant};
8use std::collections::{HashMap, HashSet, VecDeque};
9use std::default::Default;
10use std::ops::Deref;
11use std::rc::Rc;
12use std::str::FromStr;
13use std::sync::{Arc as StdArc, LazyLock};
14use std::time::Duration;
15
16use bitflags::bitflags;
17use chrono::Local;
18use content_security_policy::sandboxing_directive::SandboxingFlagSet;
19use content_security_policy::{CspList, Policy as CspPolicy, PolicyDisposition};
20use cookie::Cookie;
21use data_url::mime::Mime;
22use devtools_traits::ScriptToDevtoolsControlMsg;
23use dom_struct::dom_struct;
24use embedder_traits::{
25 AllowOrDeny, AnimationState, CustomHandlersAutomationMode, EmbedderMsg, Image, LoadStatus,
26 Theme,
27};
28use encoding_rs::{Encoding, UTF_8};
29use html5ever::{LocalName, QualName, local_name, ns};
30use hyper_serde::Serde;
31use indexmap::IndexSet;
32use js::context::{JSContext, NoGC};
33use js::jsapi::JSObject;
34use js::realm::CurrentRealm;
35use js::rust::{HandleObject, HandleValue, MutableHandleValue};
36use layout_api::{
37 PendingRestyle, ReflowGoal, ReflowPhasesRun, ReflowStatistics, RestyleReason,
38 ScrollContainerQueryFlags, TrustedNodeAddress,
39};
40use malloc_size_of::MallocSizeOfOps;
41use metrics::{InteractiveFlag, InteractiveWindow, ProgressiveWebMetrics};
42use net_traits::CookieSource::NonHTTP;
43use net_traits::CoreResourceMsg::{GetCookieStringForUrl, SetCookiesForUrl};
44use net_traits::image_cache::ImageCache;
45use net_traits::policy_container::PolicyContainer;
46use net_traits::pub_domains::is_pub_domain;
47use net_traits::request::{
48 InsecureRequestsPolicy, PreloadId, PreloadKey, PreloadedResources, RequestBuilder,
49};
50use net_traits::{ReferrerPolicy, ResourceFetchTiming};
51use paint_api::largest_contentful_paint_candidate::LCPCandidateID;
52use percent_encoding::percent_decode;
53use profile_traits::mem::{Report, ReportKind};
54use profile_traits::time::TimerMetadataFrameType;
55use profile_traits::{generic_channel as profile_generic_channel, path};
56use regex::bytes::Regex;
57use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
58use script_bindings::callback::ThisReflector;
59use script_bindings::cell::{DomRefCell, Ref, RefMut};
60use script_bindings::interfaces::DocumentHelpers;
61use script_bindings::reflector::reflect_dom_object_with_proto;
62use script_bindings::trace::CustomTraceable;
63use script_traits::{DocumentActivity, ProgressiveWebMetricType};
64use servo_arc::Arc;
65use servo_base::cross_process_instant::CrossProcessInstant;
66use servo_base::generic_channel::GenericSend;
67use servo_base::id::{PipelineId, WebViewId};
68use servo_base::{Epoch, generic_channel};
69use servo_config::pref;
70use servo_constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage};
71use servo_media::{ClientContextId, ServoMedia};
72use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
73use style::attr::AttrValue;
74use style::context::QuirksMode;
75use style::dom::OpaqueNode;
76use style::invalidation::element::restyle_hints::RestyleHint;
77use style::selector_parser::Snapshot;
78use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
79use style::str::{split_html_space_chars, str_join};
80use style::stylesheet_set::DocumentStylesheetSet;
81use style::stylesheets::{Origin, OriginSet, Stylesheet};
82use style::stylist::Stylist;
83use stylo_atoms::Atom;
84use time::Duration as TimeDuration;
85use url::{Host, Position};
86
87use crate::css::stylesheet_loader::StylesheetContextId;
88use crate::css::stylesheet_set::StylesheetSetRef;
89use crate::dom::FlatTreeParent;
90use crate::dom::animationtimeline::AnimationTimeline;
91use crate::dom::attr::Attr;
92use crate::dom::beforeunloadevent::BeforeUnloadEvent;
93use crate::dom::bindings::callback::ExceptionHandling;
94use crate::dom::bindings::codegen::Bindings::AnimationFrameProviderBinding::FrameRequestCallback;
95use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEvent_Binding::BeforeUnloadEventMethods;
96use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
97 DocumentMethods, DocumentReadyState, DocumentVisibilityState, NamedPropertyValue,
98};
99use crate::dom::bindings::codegen::Bindings::ElementBinding::ScrollLogicalPosition;
100use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
101use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElement_Binding::HTMLIFrameElementMethods;
102#[cfg(any(feature = "webxr", feature = "gamepad"))]
103use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
104use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
105use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
106use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
107use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionName;
108use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
109 SetHTMLOptions, SetHTMLUnsafeOptions,
110};
111use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, WindowMethods};
112use crate::dom::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMethods;
113use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
114use crate::dom::bindings::codegen::UnionTypes::{
115 BooleanOrImportNodeOptions, NodeOrString, StringOrElementCreationOptions, TrustedHTMLOrString,
116};
117use crate::dom::bindings::domname::{
118 self, is_valid_attribute_local_name, is_valid_element_local_name, namespace_from_domstring,
119};
120use crate::dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible};
121use crate::dom::bindings::frozenarray::CachedFrozenArray;
122use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
123use crate::dom::bindings::num::Finite;
124use crate::dom::bindings::refcounted::Trusted;
125use crate::dom::bindings::reflector::DomGlobal;
126use crate::dom::bindings::root::{
127 Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout, ToLayoutOptional, UnrootedDom,
128};
129use crate::dom::bindings::str::{DOMString, USVString};
130use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
131use crate::dom::bindings::weakref::DOMTracker;
132use crate::dom::bindings::xmlname::matches_name_production;
133use crate::dom::cdatasection::CDATASection;
134use crate::dom::comment::Comment;
135use crate::dom::compositionevent::CompositionEvent;
136use crate::dom::css::cssstylesheet::CSSStyleSheet;
137use crate::dom::css::fontfaceset::FontFaceSet;
138use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
139use crate::dom::customelementregistry::{CustomElementReactionStack, CustomElementRegistry};
140use crate::dom::customevent::CustomEvent;
141use crate::dom::document::accessibility_data::AccessibilityData;
142use crate::dom::document::animations::Animations;
143use crate::dom::document::focus::{DocumentFocusHandler, FocusableArea};
144use crate::dom::document::iframe_collection::IFrameCollection;
145use crate::dom::document::image_animation::ImageAnimationManager;
146use crate::dom::document::tree_ordered_index_map::TreeOrderedIndexMap;
147use crate::dom::document::websocket::WebSocket;
148use crate::dom::document_embedder_controls::DocumentEmbedderControls;
149use crate::dom::document_event_handler::DocumentEventHandler;
150use crate::dom::documentfragment::DocumentFragment;
151use crate::dom::documentorshadowroot::{
152 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
153};
154use crate::dom::documenttimeline::DocumentTimeline;
155use crate::dom::documenttype::DocumentType;
156use crate::dom::domimplementation::DOMImplementation;
157use crate::dom::domstringlist::DOMStringList;
158use crate::dom::element::attributes::storage::AttrRef;
159use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
160use crate::dom::event::{Event, EventBubbles, EventCancelable};
161use crate::dom::eventtarget::EventTarget;
162use crate::dom::execcommand::basecommand::{CommandName, DefaultSingleLineContainerName};
163use crate::dom::execcommand::execcommands::DocumentExecCommandSupport;
164use crate::dom::focusevent::FocusEvent;
165use crate::dom::globalscope::GlobalScope;
166use crate::dom::hashchangeevent::HashChangeEvent;
167use crate::dom::history::History;
168use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
169use crate::dom::html::htmlareaelement::HTMLAreaElement;
170use crate::dom::html::htmlbaseelement::HTMLBaseElement;
171use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
172use crate::dom::html::htmlelement::HTMLElement;
173use crate::dom::html::htmlembedelement::HTMLEmbedElement;
174use crate::dom::html::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
175use crate::dom::html::htmlheadelement::HTMLHeadElement;
176use crate::dom::html::htmlhtmlelement::HTMLHtmlElement;
177use crate::dom::html::htmliframeelement::HTMLIFrameElement;
178use crate::dom::html::htmlimageelement::HTMLImageElement;
179use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult};
180use crate::dom::html::htmltitleelement::HTMLTitleElement;
181use crate::dom::htmldetailselement::DetailsNameGroups;
182use crate::dom::intersectionobserver::IntersectionObserver;
183use crate::dom::iterators::ShadowIncluding;
184use crate::dom::keyboardevent::KeyboardEvent;
185use crate::dom::largestcontentfulpaint::LargestContentfulPaint;
186use crate::dom::location::Location;
187use crate::dom::messageevent::MessageEvent;
188use crate::dom::mouseevent::MouseEvent;
189use crate::dom::node::focus::FocusTrigger;
190use crate::dom::node::treewalker::TreeWalker;
191use crate::dom::node::virtualmethods::vtable_for;
192use crate::dom::node::{Node, NodeDamage, NodeFlags, NodeTraits};
193use crate::dom::nodeiterator::NodeIterator;
194use crate::dom::nodelist::NodeList;
195use crate::dom::pagetransitionevent::PageTransitionEvent;
196use crate::dom::performance::performanceentry::PerformanceEntry;
197use crate::dom::performance::performancepainttiming::PerformancePaintTiming;
198use crate::dom::processinginstruction::ProcessingInstruction;
199use crate::dom::promise::Promise;
200use crate::dom::range::Range;
201use crate::dom::resizeobserver::{ResizeObservationDepth, ResizeObserver};
202use crate::dom::sanitizer::Sanitizer;
203use crate::dom::selection::Selection;
204use crate::dom::servoparser::ServoParser;
205use crate::dom::shadowroot::ShadowRoot;
206use crate::dom::storageevent::StorageEvent;
207use crate::dom::text::Text;
208use crate::dom::textevent::TextEvent;
209use crate::dom::touchevent::TouchEvent as DomTouchEvent;
210use crate::dom::touchlist::TouchList;
211use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
212use crate::dom::types::{HTMLCanvasElement, VisibilityStateEntry};
213use crate::dom::uievent::UIEvent;
214use crate::dom::window::Window;
215use crate::dom::window::scrolling_box::{ScrollAxisState, ScrollingBox};
216use crate::dom::windowproxy::WindowProxy;
217use crate::dom::xpathevaluator::XPathEvaluator;
218use crate::dom::xpathexpression::XPathExpression;
219use crate::event_loop::document_loader::{DocumentLoader, LoadType};
220use crate::event_loop::script_thread::{ScriptThread, SharedRwLocks};
221use crate::event_loop::timers::{OneshotTimerCallback, OneshotTimers};
222use crate::fetch::fetch::{DeferredFetchRecordInvokeState, FetchCanceller};
223use crate::fetch::network_listener::FetchResponseListener;
224use crate::mime::{APPLICATION, CHARSET};
225use crate::navigation::navigate;
226use crate::runtime::script_runtime::compute_size;
227use crate::tasks::task::NonSendTaskBox;
228use crate::tasks::task_manager::TaskManager;
229use crate::tasks::task_source::TaskSourceName;
230use crate::xpath::parse_expression;
231
232#[derive(Clone, Copy, PartialEq)]
233pub(crate) enum FireMouseEventType {
234 Move,
235 Over,
236 Out,
237 Enter,
238 Leave,
239}
240
241impl FireMouseEventType {
242 pub(crate) fn as_str(&self) -> &str {
243 match *self {
244 FireMouseEventType::Move => "mousemove",
245 FireMouseEventType::Over => "mouseover",
246 FireMouseEventType::Out => "mouseout",
247 FireMouseEventType::Enter => "mouseenter",
248 FireMouseEventType::Leave => "mouseleave",
249 }
250 }
251}
252
253#[derive(JSTraceable, MallocSizeOf)]
254pub(crate) struct RefreshRedirectDue {
255 #[no_trace]
256 pub(crate) url: ServoUrl,
257 pub(crate) from_meta_element: bool,
259}
260impl RefreshRedirectDue {
261 pub(crate) fn invoke(self, cx: &mut JSContext, global: &GlobalScope) {
263 let window = global
264 .downcast::<Window>()
265 .expect("Queued a RefreshRedirectDue on a non-Window globalscope");
266
267 if self.from_meta_element &&
274 window.Document().has_active_sandboxing_flag(
275 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
276 )
277 {
278 return;
279 }
280 let load_data = window.load_data_for_document(self.url, window.pipeline_id());
281 navigate(
282 cx,
283 window,
284 NavigationHistoryBehavior::Replace,
285 false,
286 load_data,
287 );
288 }
289}
290
291#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
292pub(crate) enum IsHTMLDocument {
293 HTMLDocument,
294 NonHTMLDocument,
295}
296
297#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
298pub(crate) enum TheEndLoadingPhase {
299 #[default]
300 Initial,
301 ProcessingDeferredScripts,
302 ProcessingAsSoonAsPossibleScripts,
303 WaitingForLoadEventBlockers,
304 Done,
305}
306
307#[derive(JSTraceable, MallocSizeOf)]
309pub(crate) enum DeclarativeRefresh {
310 PendingLoad {
311 #[no_trace]
312 url: ServoUrl,
313 time: u64,
314 from_meta_element: bool,
316 },
317 CreatedAfterLoad,
318}
319
320#[derive(JSTraceable, MallocSizeOf, PartialEq)]
321#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
322struct PendingScrollEvent {
323 target: Dom<EventTarget>,
325 #[no_trace]
327 event: Atom,
328}
329
330impl PendingScrollEvent {
331 fn equivalent(&self, target: &EventTarget, event: &Atom) -> bool {
332 &*self.target == target && self.event == *event
333 }
334}
335
336#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf)]
339pub(crate) struct RenderingUpdateReason(u8);
340
341bitflags! {
342 impl RenderingUpdateReason: u8 {
343 const ResizeObserverStartedObservingTarget = 1 << 0;
346 const IntersectionObserverStartedObservingTarget = 1 << 1;
349 const FontReadyPromiseFulfilled = 1 << 2;
353 }
354}
355
356#[derive(Clone, Debug, Default, MallocSizeOf)]
358pub(crate) struct NavigationTiming {
359 pub(crate) dom_loading: Cell<Option<CrossProcessInstant>>,
360 pub(crate) navigation_start: Cell<Option<CrossProcessInstant>>,
362 pub(crate) unload_event_start: Cell<Option<CrossProcessInstant>>,
364 pub(crate) unload_event_end: Cell<Option<CrossProcessInstant>>,
366 pub(crate) dom_interactive: Cell<Option<CrossProcessInstant>>,
368 pub(crate) dom_content_loaded_event_start: Cell<Option<CrossProcessInstant>>,
370 pub(crate) dom_content_loaded_event_end: Cell<Option<CrossProcessInstant>>,
372 pub(crate) dom_complete: Cell<Option<CrossProcessInstant>>,
374 pub(crate) load_event_start: Cell<Option<CrossProcessInstant>>,
376 pub(crate) load_event_end: Cell<Option<CrossProcessInstant>>,
378 pub(crate) top_level_dom_complete: Cell<Option<CrossProcessInstant>>,
380}
381
382#[dom_struct]
384pub(crate) struct Document {
385 node: Node,
386 document_or_shadow_root: DocumentOrShadowRoot,
387 window: Dom<Window>,
388 implementation: MutNullableDom<DOMImplementation>,
389 #[ignore_malloc_size_of = "type from external crate"]
390 #[no_trace]
391 content_type: Mime,
392 last_modified: Option<String>,
393 #[no_trace]
394 encoding: Cell<&'static Encoding>,
395 has_browsing_context: bool,
396 is_html_document: bool,
397 #[no_trace]
398 activity: Cell<DocumentActivity>,
399 #[no_trace]
401 url: DomRefCell<ServoUrl>,
402 #[no_trace]
404 about_base_url: DomRefCell<Option<ServoUrl>>,
405 #[ignore_malloc_size_of = "defined in selectors"]
406 #[no_trace]
407 quirks_mode: Cell<QuirksMode>,
408 event_handler: DocumentEventHandler,
410 focus_handler: DocumentFocusHandler,
412 embedder_controls: DocumentEmbedderControls,
414 id_map: TreeOrderedIndexMap,
415 name_map: TreeOrderedIndexMap,
416 tag_map: DomRefCell<HashMapTracedValues<LocalName, Dom<HTMLCollection>, FxBuildHasher>>,
417 tagns_map: DomRefCell<HashMapTracedValues<QualName, Dom<HTMLCollection>, FxBuildHasher>>,
418 classes_map: DomRefCell<HashMapTracedValues<Vec<Atom>, Dom<HTMLCollection>>>,
419 images: MutNullableDom<HTMLCollection>,
420 embeds: MutNullableDom<HTMLCollection>,
421 links: MutNullableDom<HTMLCollection>,
422 forms: MutNullableDom<HTMLCollection>,
423 scripts: MutNullableDom<HTMLCollection>,
424 anchors: MutNullableDom<HTMLCollection>,
425 applets: MutNullableDom<HTMLCollection>,
426 iframes: RefCell<IFrameCollection>,
428 #[no_trace]
432 shared_style_locks: SharedRwLocks,
433 #[custom_trace]
435 stylesheets: DomRefCell<DocumentStylesheetSet<ServoStylesheetInDocument>>,
436 stylesheet_list: MutNullableDom<StyleSheetList>,
437 ready_state: Cell<DocumentReadyState>,
438 current_script: MutNullableDom<HTMLScriptElement>,
440 #[no_trace]
441 current_the_end_loading_phase: Cell<TheEndLoadingPhase>,
442 pending_parsing_blocking_script: DomRefCell<Option<PendingScript>>,
444 script_blocking_stylesheet_set: DomRefCell<IndexSet<StylesheetContextId>>,
447 render_blocking_element_count: Cell<u32>,
450 deferred_scripts: PendingInOrderScriptVec,
452 asap_in_order_scripts_list: PendingInOrderScriptVec,
454 asap_scripts_set: DomRefCell<Vec<Dom<HTMLScriptElement>>>,
456 animation_frame_ident: Cell<u32>,
459 animation_frame_list: DomRefCell<VecDeque<(u32, Option<AnimationFrameCallback>)>>,
462 running_animation_callbacks: Cell<bool>,
467 loader: DomRefCell<DocumentLoader>,
469 current_parser: MutNullableDom<ServoParser>,
471 base_element: MutNullableDom<HTMLBaseElement>,
473 target_base_element: MutNullableDom<HTMLBaseElement>,
475 appropriate_template_contents_owner_document: MutNullableDom<Document>,
478 pending_restyles: DomRefCell<FxHashMap<Dom<Element>, NoTrace<PendingRestyle>>>,
481 #[no_trace]
485 needs_restyle: Cell<RestyleReason>,
486 #[no_trace]
488 origin: DomRefCell<MutableOrigin>,
489 referrer: Option<String>,
491 target_element: MutNullableDom<Element>,
493 #[no_trace]
495 policy_container: DomRefCell<PolicyContainer>,
496 #[no_trace]
498 preloaded_resources: DomRefCell<PreloadedResources>,
499 ignore_destructive_writes_counter: Cell<u32>,
501 ignore_opens_during_unload_counter: Cell<u32>,
503 spurious_animation_frames: Cell<u8>,
507
508 fullscreen_element: MutNullableDom<Element>,
510 form_id_listener_map:
517 DomRefCell<HashMapTracedValues<Atom, HashSet<Dom<Element>>, FxBuildHasher>>,
518 #[no_trace]
519 interactive_time: DomRefCell<ProgressiveWebMetrics>,
520 #[no_trace]
521 tti_window: DomRefCell<InteractiveWindow>,
522 canceller: FetchCanceller,
524 throw_on_dynamic_markup_insertion_counter: Cell<u64>,
526 page_showing: Cell<bool>,
528 salvageable: Cell<bool>,
530 active_parser_was_aborted: Cell<bool>,
532 fired_unload: Cell<bool>,
534 responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>,
536
537 #[no_trace]
540 #[conditional_malloc_size_of]
541 navigation_timing: Rc<NavigationTiming>,
542
543 #[no_trace]
545 resource_fetch_timing: RefCell<Option<ResourceFetchTiming>>,
546
547 script_and_layout_blockers: Cell<u32>,
549 #[ignore_malloc_size_of = "Measuring trait objects is hard"]
551 delayed_tasks: DomRefCell<Vec<Box<dyn NonSendTaskBox>>>,
552 completely_loaded: Cell<bool>,
554 shadow_roots: DomRefCell<HashSet<Dom<ShadowRoot>>>,
556 shadow_roots_styles_changed: Cell<bool>,
558 media_controls: DomRefCell<HashMap<String, Dom<ShadowRoot>>>,
564 dirty_canvases: DomRefCell<Vec<Dom<HTMLCanvasElement>>>,
567 has_pending_animated_image_update: Cell<bool>,
569 selection: MutNullableDom<Selection>,
571 timeline: Dom<DocumentTimeline>,
574 animations: Animations,
576 image_animation_manager: DomRefCell<ImageAnimationManager>,
578 dirty_root: MutNullableDom<Element>,
580 declarative_refresh: DomRefCell<Option<DeclarativeRefresh>>,
582 resize_observers: DomRefCell<Vec<Dom<ResizeObserver>>>,
591 fonts: MutNullableDom<FontFaceSet>,
594 visibility_state: Cell<DocumentVisibilityState>,
596 status_code: Option<u16>,
598 is_initial_about_blank: Cell<bool>,
600 allow_declarative_shadow_roots: Cell<bool>,
602 #[no_trace]
604 inherited_insecure_requests_policy: Cell<Option<InsecureRequestsPolicy>>,
605 has_trustworthy_ancestor_origin: Cell<bool>,
607 intersection_observer_task_queued: Cell<bool>,
609 intersection_observers: DomRefCell<Vec<Dom<IntersectionObserver>>>,
621 highlighted_dom_node: MutNullableDom<Node>,
623 lcp_candidates: DomRefCell<HashMapTracedValues<LCPCandidateID, Dom<Element>>>,
625 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
628 #[ignore_malloc_size_of = "mozjs"]
630 adopted_stylesheets_frozen_types: CachedFrozenArray,
631 pending_scroll_events: DomRefCell<Vec<PendingScrollEvent>>,
635 rendering_update_reasons: Cell<RenderingUpdateReason>,
637 waiting_on_canvas_image_updates: Cell<bool>,
641 root_removal_noted: Cell<bool>,
643 #[no_trace]
651 current_rendering_epoch: Cell<Epoch>,
652 #[conditional_malloc_size_of]
654 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
655 #[no_trace]
656 active_sandboxing_flag_set: Cell<SandboxingFlagSet>,
658 #[no_trace]
659 creation_sandboxing_flag_set: Cell<SandboxingFlagSet>,
666 #[no_trace]
668 favicon: RefCell<Option<Image>>,
669
670 websockets: DOMTracker<WebSocket>,
672
673 details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
675
676 #[no_trace]
678 protocol_handler_automation_mode: RefCell<CustomHandlersAutomationMode>,
679
680 layout_animations_test_enabled: bool,
682
683 #[no_trace]
685 state_override: DomRefCell<FxHashMap<CommandName, bool>>,
686
687 #[no_trace]
689 value_override: DomRefCell<FxHashMap<CommandName, DOMString>>,
690
691 #[no_trace]
693 default_single_line_container_name: Cell<DefaultSingleLineContainerName>,
694
695 css_styling_flag: Cell<bool>,
697
698 accessibility_data: DomRefCell<AccessibilityData>,
700
701 iframe_load_in_progress: Cell<bool>,
703 mute_iframe_load: Cell<bool>,
705
706 timers: OneshotTimers,
709
710 #[no_trace]
711 pipeline_id: PipelineId,
712
713 #[conditional_malloc_size_of]
715 task_manager: Rc<TaskManager>,
716
717 #[ignore_malloc_size_of = "ImageCache"]
718 #[no_trace]
719 image_cache: StdArc<dyn ImageCache>,
720
721 history: MutNullableDom<History>,
723
724 #[no_trace]
726 theme: Cell<Option<Theme>>,
727
728 window_detached: Cell<bool>,
731
732 ancestor_origins_list: MutNullableDom<DOMStringList>,
734
735 #[no_trace]
737 internal_ancestor_origin_objects_list: RefCell<Option<Vec<ImmutableOrigin>>>,
738}
739
740impl Document {
741 pub(crate) fn history(&self, cx: &mut JSContext) -> DomRoot<History> {
742 self.history.or_init(|| History::new(cx, &self.window))
743 }
744
745 pub(crate) fn image_cache(&self) -> StdArc<dyn ImageCache> {
746 self.image_cache.clone()
747 }
748
749 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
750 self.task_manager.clone()
751 }
752
753 pub(crate) fn timers(&self) -> &OneshotTimers {
754 &self.timers
755 }
756
757 pub(crate) fn pipeline_id(&self) -> PipelineId {
758 self.pipeline_id
759 }
760
761 fn fully_exit_fullscreen(&self, cx: &mut JSContext) {
763 if self.fullscreen_element().is_none() {
766 return;
767 };
768
769 let _ = self.exit_fullscreen(cx);
774 }
775
776 fn unloading_cleanup_steps(&self, cx: &mut JSContext) {
778 self.fully_exit_fullscreen(cx);
781
782 if self.close_outstanding_websockets() {
785 self.salvageable.set(false);
787 }
788
789 if !self.salvageable.get() && !self.window_detached() {
794 let global_scope = self.window.as_global_scope();
795
796 global_scope.close_event_sources();
798
799 let msg = ScriptToConstellationMessage::DiscardDocument;
804 let _ = global_scope.script_to_constellation_chan().send(msg);
805 }
806 }
807
808 pub(crate) fn track_websocket(&self, websocket: &WebSocket) {
809 self.websockets.track(websocket);
810 }
811
812 fn close_outstanding_websockets(&self) -> bool {
813 let mut closed_any_websocket = false;
814 self.websockets.for_each(|websocket: DomRoot<WebSocket>| {
815 if websocket.make_disappear() {
816 closed_any_websocket = true;
817 }
818 });
819 closed_any_websocket
820 }
821
822 fn document_element_changed(&self) {
823 if self.GetDocumentElement().is_some() {
824 self.root_removal_noted.set(false);
827 } else if !self.root_removal_noted.get() {
828 self.add_restyle_reason(RestyleReason::DOMChanged);
831 self.root_removal_noted.set(true);
832 }
833 }
834
835 pub(crate) fn note_dirty_element(&self, no_gc: &NoGC, element: &Element) {
850 let node = element.upcast::<Node>();
851
852 debug_assert!(*node.owner_doc() == *self);
853 if !node.is_connected() {
854 return;
855 }
856
857 let parent_element = match node.parent_in_flat_tree(no_gc) {
858 FlatTreeParent::Parent(parent) => UnrootedDom::downcast::<Element>(parent),
859 FlatTreeParent::NotInFlatTree | FlatTreeParent::RootNode => return,
860 };
861
862 if let Some(parent_element) = parent_element {
866 if !parent_element.is_styled() {
869 return;
870 }
871 if parent_element.is_display_none() {
874 return;
875 }
876 }
877
878 let Some(old_dirty_root) = self.dirty_root.get() else {
879 node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
880 self.set_dirty_root(no_gc, Some(element));
881 return;
882 };
883
884 let old_dirty_root_node = old_dirty_root.upcast::<Node>();
885 for ancestor in element
886 .upcast::<Node>()
887 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
888 {
889 if !ancestor.is::<Element>() {
891 break;
892 }
893
894 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
895 return;
896 }
897
898 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
899
900 if old_dirty_root_node == &**ancestor {
904 return;
905 }
906 }
907
908 let common_element_ancestor = old_dirty_root_node
909 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
910 .skip(1) .find_map(|ancestor| {
912 let element = ancestor.downcast::<Element>().map(DomRoot::from_ref)?;
914 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
915 return Some(element);
916 }
917 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
918 None
919 });
920
921 let Some(new_dirty_root) = common_element_ancestor else {
925 let new_dirty_root = self.GetDocumentElement();
926 if let Some(new_dirty_root) = new_dirty_root.as_ref() {
927 new_dirty_root
928 .upcast::<Node>()
929 .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
930 }
931 self.set_dirty_root(no_gc, new_dirty_root.as_deref());
932 return;
933 };
934
935 for ancestor in new_dirty_root
939 .upcast::<Node>()
940 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
941 .skip(1)
942 {
943 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
944 }
945
946 self.set_dirty_root(no_gc, Some(&*new_dirty_root));
947 }
948
949 fn set_dirty_root(&self, no_gc: &NoGC, new_dirty_root: Option<&Element>) {
950 debug_assert!(new_dirty_root.as_ref().is_none_or(|new_dirty_root| {
952 new_dirty_root
953 .upcast::<Node>()
954 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
955 .skip(1)
956 .all(|node| !node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS))
957 }));
958 self.dirty_root.set(new_dirty_root);
959 }
960
961 pub(crate) fn take_dirty_root(&self) -> Option<DomRoot<Element>> {
962 self.dirty_root.take()
963 }
964
965 #[inline]
966 pub(crate) fn loader(&self) -> Ref<'_, DocumentLoader> {
967 self.loader.borrow()
968 }
969
970 #[inline]
971 pub(crate) fn loader_mut(&self) -> RefMut<'_, DocumentLoader> {
972 self.loader.borrow_mut()
973 }
974
975 #[inline]
976 pub(crate) fn has_browsing_context(&self) -> bool {
977 self.has_browsing_context
978 }
979
980 #[inline]
982 pub(crate) fn browsing_context(&self) -> Option<DomRoot<WindowProxy>> {
983 if self.has_browsing_context {
984 self.window.undiscarded_window_proxy()
985 } else {
986 None
987 }
988 }
989
990 pub(crate) fn webview_id(&self) -> WebViewId {
991 self.window.webview_id()
992 }
993
994 #[inline]
995 pub(crate) fn window(&self) -> &Window {
996 &self.window
997 }
998
999 #[inline]
1000 pub(crate) fn is_html_document(&self) -> bool {
1001 self.is_html_document
1002 }
1003
1004 pub(crate) fn is_xhtml_document(&self) -> bool {
1005 self.content_type.matches(APPLICATION, "xhtml+xml")
1006 }
1007
1008 pub(crate) fn is_fully_active(&self) -> bool {
1010 self.is_active() &&
1014 (self.window.is_top_level() || self.activity.get() == DocumentActivity::FullyActive)
1015 }
1016
1017 pub(crate) fn is_active(&self) -> bool {
1019 self.browsing_context().is_some() &&
1025 !self.window_detached() &&
1026 self.activity.get() != DocumentActivity::Inactive
1027 }
1028
1029 #[inline]
1030 pub(crate) fn current_rendering_epoch(&self) -> Epoch {
1031 self.current_rendering_epoch.get()
1032 }
1033
1034 #[inline]
1036 pub(crate) fn selection(&self) -> Option<DomRoot<Selection>> {
1037 self.selection.get()
1038 }
1039
1040 pub(crate) fn set_activity(&self, cx: &mut JSContext, activity: DocumentActivity) {
1041 assert!(self.has_browsing_context);
1043 if activity == self.activity.get() {
1044 return;
1045 }
1046
1047 self.activity.set(activity);
1049 let media = ServoMedia::get();
1050 let pipeline_id = self.window().pipeline_id();
1051 let client_context_id =
1052 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
1053
1054 if activity != DocumentActivity::FullyActive {
1055 if !self.window_detached() {
1056 self.window().suspend(cx);
1057 }
1058 media.suspend(&client_context_id);
1059 return;
1060 }
1061
1062 if self.window_detached() {
1063 return;
1064 }
1065
1066 self.title_changed();
1067 self.notify_embedder_favicon();
1068 self.dirty_all_nodes(cx.no_gc());
1069 self.window().resume(cx);
1070 media.resume(&client_context_id);
1071
1072 if self.ready_state.get() != DocumentReadyState::Complete {
1073 return;
1074 }
1075
1076 let document = Trusted::new(self);
1080 self.owner_global()
1081 .task_manager()
1082 .dom_manipulation_task_source()
1083 .queue(task!(fire_pageshow_event: move |cx| {
1084 let document = document.root();
1085 let window = document.window();
1086 if document.page_showing.get() {
1088 return;
1089 }
1090 document.page_showing.set(true);
1092 document.update_visibility_state(cx, DocumentVisibilityState::Visible);
1094 let event = PageTransitionEvent::new(
1097 cx,
1098 window,
1099 atom!("pageshow"),
1100 false, false, true, );
1104 let event = event.upcast::<Event>();
1105 event.set_trusted(true);
1106 window.dispatch_event_with_target_override(cx, event);
1107 }))
1108 }
1109
1110 pub(crate) fn origin(&self) -> Ref<'_, MutableOrigin> {
1111 self.origin.borrow()
1112 }
1113
1114 pub(crate) fn mark_as_internal(&self) {
1117 *self.origin.borrow_mut() = MutableOrigin::new(ImmutableOrigin::new_opaque());
1118 self.window().update_jsprincipals_from_document(self);
1119 }
1120
1121 pub(crate) fn set_protocol_handler_automation_mode(&self, mode: CustomHandlersAutomationMode) {
1122 *self.protocol_handler_automation_mode.borrow_mut() = mode;
1123 }
1124
1125 pub(crate) fn url(&self) -> ServoUrl {
1127 self.url.borrow().clone()
1128 }
1129
1130 pub(crate) fn set_url(&self, url: ServoUrl) {
1131 *self.url.borrow_mut() = url;
1132 }
1133
1134 pub(crate) fn about_base_url(&self) -> Option<ServoUrl> {
1135 self.about_base_url.borrow().clone()
1136 }
1137
1138 pub(crate) fn set_about_base_url(&self, about_base_url: Option<ServoUrl>) {
1139 *self.about_base_url.borrow_mut() = about_base_url;
1140 }
1141
1142 pub(crate) fn fallback_base_url(&self) -> ServoUrl {
1144 let document_url = self.url();
1145 if document_url.as_str() == "about:srcdoc" {
1147 return self
1150 .about_base_url()
1151 .expect("about:srcdoc page should always have an about base URL");
1152 }
1153
1154 if document_url.matches_about_blank() &&
1157 let Some(about_base_url) = self.about_base_url()
1158 {
1159 return about_base_url;
1160 }
1161
1162 document_url
1164 }
1165
1166 pub(crate) fn base_url(&self) -> ServoUrl {
1168 match self.base_element() {
1169 None => self.fallback_base_url(),
1171 Some(base) => base.frozen_base_url(),
1173 }
1174 }
1175
1176 pub(crate) fn add_restyle_reason(&self, reason: RestyleReason) {
1177 self.needs_restyle.set(self.needs_restyle.get() | reason)
1178 }
1179
1180 pub(crate) fn clear_restyle_reasons(&self) {
1181 self.needs_restyle.set(RestyleReason::empty());
1182 }
1183
1184 pub(crate) fn stylesheets_changed_since_last_reflow(&self) -> bool {
1185 self.stylesheets.borrow().has_changed()
1186 }
1187
1188 pub(crate) fn restyle_reason(&self, no_gc: &NoGC) -> RestyleReason {
1189 let mut condition = self.needs_restyle.get();
1190 if self.stylesheets_changed_since_last_reflow() {
1191 condition.insert(RestyleReason::StylesheetsChanged);
1192 }
1193
1194 if let Some(root) = self.get_document_element_unrooted(no_gc) &&
1198 root.has_dirty_descendants()
1199 {
1200 condition.insert(RestyleReason::DOMChanged);
1201 }
1202
1203 if !self.pending_restyles.borrow().is_empty() {
1204 condition.insert(RestyleReason::PendingRestyles);
1205 }
1206
1207 condition
1208 }
1209
1210 pub(crate) fn base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1212 self.base_element.get()
1213 }
1214
1215 pub(crate) fn target_base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1217 self.target_base_element.get()
1218 }
1219
1220 pub(crate) fn refresh_base_element(&self, cx: &mut JSContext) {
1222 if let Some(base_element) = self.base_element.get() {
1223 base_element.clear_frozen_base_url();
1224 }
1225 let new_base_element = self
1226 .upcast::<Node>()
1227 .traverse_preorder(ShadowIncluding::No)
1228 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1229 .find(|element| {
1230 element
1231 .upcast::<Element>()
1232 .has_attribute(&local_name!("href"))
1233 });
1234 if let Some(ref new_base_element) = new_base_element {
1235 new_base_element.set_frozen_base_url(cx);
1236 }
1237 self.base_element.set(new_base_element.as_deref());
1238
1239 let new_target_base_element = self
1240 .upcast::<Node>()
1241 .traverse_preorder(ShadowIncluding::No)
1242 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1243 .next();
1244 self.target_base_element
1245 .set(new_target_base_element.as_deref());
1246 }
1247
1248 pub(crate) fn quirks_mode(&self) -> QuirksMode {
1249 self.quirks_mode.get()
1250 }
1251
1252 pub(crate) fn set_quirks_mode(&self, new_mode: QuirksMode) {
1253 let old_mode = self.quirks_mode.replace(new_mode);
1254
1255 if old_mode != new_mode {
1256 self.window.layout_mut().set_quirks_mode(new_mode);
1257 }
1258 }
1259
1260 pub(crate) fn encoding(&self) -> &'static Encoding {
1261 self.encoding.get()
1262 }
1263
1264 pub(crate) fn set_encoding(&self, encoding: &'static Encoding) {
1265 self.encoding.set(encoding);
1266 }
1267
1268 pub(crate) fn content_and_heritage_changed(&self, no_gc: &NoGC, node: &Node) {
1269 if node.is::<Document>() {
1270 self.document_element_changed();
1271 }
1272
1273 node.dirty(no_gc, NodeDamage::ContentOrHeritage);
1278 }
1279
1280 pub(crate) fn unregister_element_id(&self, cx: &mut JSContext, id: &Atom) {
1282 self.id_map.remove(id);
1283 self.reset_form_owner_for_listeners(cx, id);
1284 }
1285
1286 pub(crate) fn register_element_id(&self, cx: &mut JSContext, element: &Element, id: &Atom) {
1288 self.id_map.add(id, element);
1289 self.reset_form_owner_for_listeners(cx, id);
1290 }
1291
1292 pub(crate) fn unregister_element_name(&self, name: &Atom) {
1294 self.name_map.remove(name);
1295 }
1296
1297 pub(crate) fn register_element_name(&self, element: &Element, name: &Atom) {
1299 self.name_map.add(name, element);
1300 }
1301
1302 pub(crate) fn register_form_id_listener<T: ?Sized + FormControl>(
1303 &self,
1304 id: DOMString,
1305 listener: &T,
1306 ) {
1307 let mut map = self.form_id_listener_map.borrow_mut();
1308 let listener = listener.to_element();
1309 let set = map.entry(Atom::from(id)).or_default();
1310 set.insert(Dom::from_ref(listener));
1311 }
1312
1313 pub(crate) fn unregister_form_id_listener<T: ?Sized + FormControl>(
1314 &self,
1315 id: DOMString,
1316 listener: &T,
1317 ) {
1318 let mut map = self.form_id_listener_map.borrow_mut();
1319 if let Occupied(mut entry) = map.entry(Atom::from(id)) {
1320 entry
1321 .get_mut()
1322 .remove(&Dom::from_ref(listener.to_element()));
1323 if entry.get().is_empty() {
1324 entry.remove();
1325 }
1326 }
1327 }
1328
1329 fn find_a_potential_indicated_element(
1331 &self,
1332 cx: &mut JSContext,
1333 fragment: &str,
1334 ) -> Option<DomRoot<Element>> {
1335 self.get_element_by_id(cx.no_gc(), &Atom::from(fragment))
1339 .or_else(|| self.get_anchor_by_name(cx, fragment))
1343 }
1344
1345 fn select_indicated_part(&self, cx: &mut JSContext, fragment: &str) -> Option<DomRoot<Node>> {
1348 if fragment.is_empty() {
1358 return Some(DomRoot::from_ref(self.upcast()));
1359 }
1360 if let Some(potential_indicated_element) =
1362 self.find_a_potential_indicated_element(cx, fragment)
1363 {
1364 return Some(DomRoot::upcast(potential_indicated_element));
1366 }
1367 let fragment_bytes = percent_decode(fragment.as_bytes());
1369 let Ok(decoded_fragment) = fragment_bytes.decode_utf8() else {
1371 return None;
1372 };
1373 if let Some(potential_indicated_element) =
1375 self.find_a_potential_indicated_element(cx, &decoded_fragment)
1376 {
1377 return Some(DomRoot::upcast(potential_indicated_element));
1379 }
1380 if decoded_fragment.eq_ignore_ascii_case("top") {
1382 return Some(DomRoot::from_ref(self.upcast()));
1383 }
1384 None
1386 }
1387
1388 pub(crate) fn scroll_to_the_fragment(&self, cx: &mut JSContext, fragment: &str) {
1390 let Some(indicated_part) = self.select_indicated_part(cx, fragment) else {
1395 self.set_target_element(None);
1396 return;
1397 };
1398 if *indicated_part == *self.upcast() {
1400 self.set_target_element(None);
1402 self.window.scroll(cx, 0.0, 0.0, ScrollBehavior::Instant);
1407 return;
1409 }
1410 let Some(target) = indicated_part.downcast::<Element>() else {
1413 unreachable!("Indicated part should always be an element");
1415 };
1416 self.set_target_element(Some(target));
1418 target.scroll_into_view_with_options(
1422 cx,
1423 ScrollBehavior::Auto,
1424 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Start),
1425 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Nearest),
1426 None,
1427 None,
1428 );
1429
1430 indicated_part.run_the_focusing_steps(
1433 cx,
1434 Some(FocusableArea::Viewport),
1435 FocusTrigger::Other,
1436 );
1437
1438 self.focus_handler()
1440 .set_sequential_focus_navigation_starting_point(target.upcast());
1441 }
1442
1443 fn get_anchor_by_name(&self, cx: &mut JSContext, name: &str) -> Option<DomRoot<Element>> {
1444 let document_element = self.GetDocumentElement()?;
1445 self.name_map
1446 .get_all(cx.no_gc(), document_element.upcast(), &Atom::from(name))
1447 .iter()
1448 .find(|element| element.is::<HTMLAnchorElement>())
1449 .map(|element| DomRoot::from_ref(&**element))
1450 }
1451
1452 pub(crate) fn notify_embedder_of_load_completion(&self) {
1453 if self.window().is_top_level() {
1454 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1455 self.webview_id(),
1456 LoadStatus::Complete,
1457 ));
1458 }
1459 }
1460
1461 pub(crate) fn set_document_readiness_to_loading_for_initialization(&self) {
1466 if self.is_initial_about_blank() {
1470 return;
1471 }
1472
1473 update_with_current_instant(&self.navigation_timing.dom_loading);
1477
1478 if self.window.is_top_level() {
1479 let webview_id = self.webview_id();
1480 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1481 webview_id,
1482 LoadStatus::Started,
1483 ));
1484 self.send_to_embedder(EmbedderMsg::Status(webview_id, None));
1485 }
1486
1487 self.ready_state.set(DocumentReadyState::Loading);
1488 }
1489
1490 pub(crate) fn update_the_current_document_readiness(
1492 &self,
1493 cx: &mut JSContext,
1494 state: DocumentReadyState,
1495 ) {
1496 if self.ready_state.get() == state {
1498 return;
1499 }
1500
1501 self.ready_state.set(state);
1503
1504 match state {
1509 DocumentReadyState::Loading => {},
1510 DocumentReadyState::Complete => {
1511 self.notify_embedder_of_load_completion();
1514
1515 update_with_current_instant(&self.navigation_timing.dom_complete);
1520 },
1521 DocumentReadyState::Interactive => {
1522 update_with_current_instant(&self.navigation_timing.dom_interactive)
1527 },
1528 };
1529
1530 self.upcast::<EventTarget>()
1532 .fire_event(cx, atom!("readystatechange"));
1533 }
1534
1535 pub(crate) fn scripting_enabled(&self) -> bool {
1538 self.has_browsing_context() &&
1541 !self.has_active_sandboxing_flag(
1545 SandboxingFlagSet::SANDBOXED_SCRIPTS_BROWSING_CONTEXT_FLAG,
1546 )
1547 }
1548
1549 pub(crate) fn title_changed(&self) {
1551 if self.browsing_context().is_some() {
1552 self.send_title_to_embedder();
1553 let title = String::from(self.Title());
1554 self.window
1555 .send_to_constellation(ScriptToConstellationMessage::TitleChanged(
1556 self.window.pipeline_id(),
1557 title.clone(),
1558 ));
1559 if let Some(chan) = self.window.as_global_scope().devtools_chan() {
1560 let _ = chan.send(ScriptToDevtoolsControlMsg::TitleChanged(
1561 self.window.pipeline_id(),
1562 title,
1563 ));
1564 }
1565 }
1566 }
1567
1568 fn title(&self) -> Option<DOMString> {
1572 let title = self.GetDocumentElement().and_then(|root| {
1573 if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
1574 root.upcast::<Node>()
1576 .child_elements()
1577 .find(|node| {
1578 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
1579 })
1580 .map(DomRoot::upcast::<Node>)
1581 } else {
1582 root.upcast::<Node>()
1584 .traverse_preorder(ShadowIncluding::No)
1585 .find(|node| node.is::<HTMLTitleElement>())
1586 }
1587 });
1588
1589 title.map(|title| {
1590 let value = title.child_text_content();
1592 DOMString::from(str_join(value.str().split_html_space_characters(), " "))
1593 })
1594 }
1595
1596 pub(crate) fn send_title_to_embedder(&self) {
1598 let window = self.window();
1599 if window.is_top_level() {
1600 let title = self.title().map(String::from);
1601 self.send_to_embedder(EmbedderMsg::ChangePageTitle(self.webview_id(), title));
1602 }
1603 }
1604
1605 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
1606 let window = self.window();
1607 window.send_to_embedder(msg);
1608 }
1609
1610 pub(crate) fn dirty_all_nodes(&self, no_gc: &NoGC) {
1611 let root = match self.GetDocumentElement() {
1612 Some(root) => root,
1613 None => return,
1614 };
1615 for node in root
1616 .upcast::<Node>()
1617 .traverse_preorder_non_rooting(no_gc, ShadowIncluding::Yes)
1618 {
1619 node.dirty(no_gc, NodeDamage::Other)
1620 }
1621 }
1622
1623 pub(crate) fn run_the_scroll_steps(&self, cx: &mut JSContext) {
1625 let boxes_that_were_scrolled: Vec<_> = self
1633 .pending_scroll_events
1634 .borrow()
1635 .iter()
1636 .filter_map(|pending_event| {
1637 if &*pending_event.event == "scroll" {
1638 Some(pending_event.target.as_rooted())
1639 } else {
1640 None
1641 }
1642 })
1643 .collect();
1644
1645 for target in boxes_that_were_scrolled.into_iter() {
1646 let Some(element) = target.downcast::<Element>() else {
1652 continue;
1653 };
1654 let document = element.owner_document();
1655
1656 let mut pending_scroll_events = document.pending_scroll_events.borrow_mut();
1663 let event = "scrollend".into();
1664 if pending_scroll_events
1665 .iter()
1666 .any(|existing| existing.equivalent(&target, &event))
1667 {
1668 continue;
1669 }
1670
1671 pending_scroll_events.push(PendingScrollEvent {
1673 target: target.as_traced(),
1674 event: "scrollend".into(),
1675 });
1676 }
1677
1678 rooted_vec!(let pending_scroll_events <- self.pending_scroll_events.take().into_iter());
1681 for pending_event in pending_scroll_events.iter() {
1682 let event = pending_event.event.clone();
1685 if pending_event.target.is::<Document>() {
1686 pending_event.target.fire_bubbling_event(cx, event);
1687 }
1688 else {
1697 pending_event.target.fire_event(cx, event);
1698 }
1699 }
1700
1701 }
1704
1705 pub(crate) fn handle_viewport_scroll_event(&self) {
1710 self.finish_handle_scroll_event(self.upcast());
1722 }
1723
1724 pub(crate) fn finish_handle_scroll_event(&self, event_target: &EventTarget) {
1729 let event = "scroll".into();
1732 if self
1733 .pending_scroll_events
1734 .borrow()
1735 .iter()
1736 .any(|existing| existing.equivalent(event_target, &event))
1737 {
1738 return;
1739 }
1740
1741 self.pending_scroll_events
1744 .borrow_mut()
1745 .push(PendingScrollEvent {
1746 target: Dom::from_ref(event_target),
1747 event: "scroll".into(),
1748 });
1749 }
1750
1751 pub(crate) fn node_from_nodes_and_strings(
1753 &self,
1754 cx: &mut JSContext,
1755 mut nodes: Vec<NodeOrString>,
1756 ) -> Fallible<DomRoot<Node>> {
1757 if nodes.len() == 1 {
1758 Ok(match nodes.pop().unwrap() {
1759 NodeOrString::Node(node) => node,
1760 NodeOrString::String(string) => DomRoot::upcast(self.CreateTextNode(cx, string)),
1761 })
1762 } else {
1763 let fragment = DomRoot::upcast::<Node>(self.CreateDocumentFragment(cx));
1764 for node in nodes {
1765 match node {
1766 NodeOrString::Node(node) => {
1767 fragment.AppendChild(cx, &node)?;
1768 },
1769 NodeOrString::String(string) => {
1770 let node = DomRoot::upcast::<Node>(self.CreateTextNode(cx, string));
1771 fragment.AppendChild(cx, &node).unwrap();
1774 },
1775 }
1776 }
1777 Ok(fragment)
1778 }
1779 }
1780
1781 pub(crate) fn get_body_attribute(&self, local_name: &LocalName) -> DOMString {
1782 match self.GetBody() {
1783 Some(ref body) if body.is_body_element() => {
1784 body.upcast::<Element>().get_string_attribute(local_name)
1785 },
1786 _ => DOMString::new(),
1787 }
1788 }
1789
1790 pub(crate) fn set_body_attribute(
1791 &self,
1792 cx: &mut JSContext,
1793 local_name: &LocalName,
1794 value: DOMString,
1795 ) {
1796 if let Some(ref body) = self.GetBody().filter(|elem| elem.is_body_element()) {
1797 let body = body.upcast::<Element>();
1798 let value = body.parse_attribute(&ns!(), local_name, value);
1799 body.set_attribute(cx, local_name, value);
1800 }
1801 }
1802
1803 pub(crate) fn set_current_script(&self, script: Option<&HTMLScriptElement>) {
1804 self.current_script.set(script);
1805 }
1806
1807 pub(crate) fn has_a_stylesheet_that_is_blocking_scripts(&self) -> bool {
1809 !self.script_blocking_stylesheet_set.borrow().is_empty()
1810 }
1811
1812 pub(crate) fn add_script_blocking_stylesheet(&self, id: StylesheetContextId) {
1813 self.script_blocking_stylesheet_set.borrow_mut().insert(id);
1814 }
1815
1816 pub(crate) fn remove_script_blocking_stylesheet(&self, id: StylesheetContextId) {
1817 self.script_blocking_stylesheet_set
1818 .borrow_mut()
1819 .shift_remove(&id);
1820 }
1821
1822 pub(crate) fn render_blocking_element_count(&self) -> u32 {
1823 self.render_blocking_element_count.get()
1824 }
1825
1826 pub(crate) fn increment_render_blocking_element_count(&self) {
1828 assert!(self.allows_adding_render_blocking_elements());
1835 let count_cell = &self.render_blocking_element_count;
1836 count_cell.set(count_cell.get() + 1);
1837 }
1838
1839 pub(crate) fn decrement_render_blocking_element_count(&self) {
1841 let count_cell = &self.render_blocking_element_count;
1847 assert!(count_cell.get() > 0);
1848 count_cell.set(count_cell.get() - 1);
1849 }
1850
1851 pub(crate) fn allows_adding_render_blocking_elements(&self) -> bool {
1853 self.is_html_document && self.GetBody().is_none()
1856 }
1857
1858 pub(crate) fn is_render_blocked(&self) -> bool {
1860 self.render_blocking_element_count() > 0
1864 }
1869
1870 pub(crate) fn invalidate_stylesheets(&self, no_gc: &NoGC) {
1871 self.stylesheets.borrow_mut().force_dirty(OriginSet::all());
1872
1873 if let Some(element) = self.GetDocumentElement() {
1877 element.upcast::<Node>().dirty(no_gc, NodeDamage::Style);
1878 }
1879 }
1880
1881 pub(crate) fn has_active_request_animation_frame_callbacks(&self) -> bool {
1884 !self.animation_frame_list.borrow().is_empty()
1885 }
1886
1887 pub(crate) fn request_animation_frame(&self, callback: AnimationFrameCallback) -> u32 {
1889 let ident = self.animation_frame_ident.get() + 1;
1890 self.animation_frame_ident.set(ident);
1891
1892 let had_animation_frame_callbacks;
1893 {
1894 let mut animation_frame_list = self.animation_frame_list.borrow_mut();
1895 had_animation_frame_callbacks = !animation_frame_list.is_empty();
1896 animation_frame_list.push_back((ident, Some(callback)));
1897 }
1898
1899 if !self.running_animation_callbacks.get() && !had_animation_frame_callbacks {
1905 self.window().send_to_constellation(
1906 ScriptToConstellationMessage::ChangeRunningAnimationsState(
1907 AnimationState::AnimationCallbacksPresent,
1908 ),
1909 );
1910 }
1911
1912 ident
1913 }
1914
1915 pub(crate) fn cancel_animation_frame(&self, ident: u32) {
1917 let mut list = self.animation_frame_list.borrow_mut();
1918 if let Some(pair) = list.iter_mut().find(|pair| pair.0 == ident) {
1919 pair.1 = None;
1920 }
1921 }
1922
1923 pub(crate) fn run_the_animation_frame_callbacks(&self, cx: &mut CurrentRealm) {
1925 self.running_animation_callbacks.set(true);
1926 let timing = self.global().performance(cx).Now();
1927
1928 let num_callbacks = self.animation_frame_list.borrow().len();
1929 for _ in 0..num_callbacks {
1930 let (_, maybe_callback) = self.animation_frame_list.borrow_mut().pop_front().unwrap();
1931 if let Some(callback) = maybe_callback {
1932 callback.call(cx, self, *timing);
1933 }
1934 }
1935 self.running_animation_callbacks.set(false);
1936
1937 if self.animation_frame_list.borrow().is_empty() {
1938 self.window().send_to_constellation(
1939 ScriptToConstellationMessage::ChangeRunningAnimationsState(
1940 AnimationState::AnimationCallbacksAbsent,
1941 ),
1942 );
1943 }
1944 }
1945
1946 pub(crate) fn policy_container(&self) -> Ref<'_, PolicyContainer> {
1947 self.policy_container.borrow()
1948 }
1949
1950 pub(crate) fn set_policy_container(&self, policy_container: PolicyContainer) {
1951 *self.policy_container.borrow_mut() = policy_container;
1952 }
1953
1954 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
1955 self.policy_container.borrow_mut().set_csp_list(csp_list);
1956 }
1957
1958 pub(crate) fn enforce_csp_policy(&self, policy: CspPolicy) {
1960 let mut csp_list = self.get_csp_list().clone().unwrap_or(CspList(vec![]));
1962 csp_list.push(policy);
1963 self.policy_container
1964 .borrow_mut()
1965 .set_csp_list(Some(csp_list));
1966 }
1967
1968 pub(crate) fn get_csp_list(&self) -> Ref<'_, Option<CspList>> {
1969 Ref::map(self.policy_container.borrow(), |policy_container| {
1970 &policy_container.csp_list
1971 })
1972 }
1973
1974 pub(crate) fn preloaded_resources(&self) -> std::cell::Ref<'_, PreloadedResources> {
1975 self.preloaded_resources.borrow()
1976 }
1977
1978 pub(crate) fn insert_preloaded_resource(&self, key: PreloadKey, preload_id: PreloadId) {
1979 self.preloaded_resources
1980 .borrow_mut()
1981 .insert(key, preload_id);
1982 }
1983
1984 pub(crate) fn fetch_blocking<Listener: FetchResponseListener>(
1985 &self,
1986 load: LoadType,
1987 request: RequestBuilder,
1988 listener: Listener,
1989 ) {
1990 self.loader_mut().add_blocking_load(load);
1991 self.fetch_background(request, listener);
1992 }
1993
1994 pub(crate) fn fetch_background<Listener: FetchResponseListener>(
1995 &self,
1996 request_builder: RequestBuilder,
1997 listener: Listener,
1998 ) {
1999 let networking_task_source = self
2000 .owner_global()
2001 .task_manager()
2002 .networking_task_source()
2003 .to_sendable();
2004 self.window()
2005 .as_global_scope()
2006 .fetch(request_builder, listener, networking_task_source);
2007 }
2008
2009 fn deferred_fetch_control_document(&self) -> DomRoot<Document> {
2011 match self.window().window_proxy().frame_element() {
2012 None => DomRoot::from_ref(self),
2015 Some(container) => container.owner_document().deferred_fetch_control_document(),
2017 }
2018 }
2019
2020 pub(crate) fn available_deferred_fetch_quota(&self, origin: ImmutableOrigin) -> isize {
2022 let control_document = self.deferred_fetch_control_document();
2024 let navigable = control_document.window();
2026 let is_top_level = navigable.is_top_level();
2029 let deferred_fetch_allowed = true;
2033 let deferred_fetch_minimal_allowed = true;
2037 let mut quota = match is_top_level {
2039 true if !deferred_fetch_allowed => 0,
2041 true if !deferred_fetch_minimal_allowed => 640 * 1024,
2043 true => 512 * 1024,
2045 _ if deferred_fetch_allowed => 0,
2049 _ if deferred_fetch_minimal_allowed => 8 * 1024,
2053 _ => 0,
2055 } as isize;
2056 let mut quota_for_request_origin = 64 * 1024_isize;
2058 let deferred_fetches = navigable.as_global_scope().fetch_group().deferred_fetches();
2067 for deferred_fetch in deferred_fetches {
2068 if deferred_fetch.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
2070 continue;
2071 }
2072 let request_length = deferred_fetch.request.total_request_length();
2074 quota -= request_length as isize;
2076 if deferred_fetch.request.url().origin() == origin {
2079 quota_for_request_origin -= request_length as isize;
2080 }
2081 }
2082 if quota <= 0 {
2084 return 0;
2085 }
2086 if quota < quota_for_request_origin {
2088 return quota;
2089 }
2090 quota_for_request_origin
2092 }
2093
2094 pub(crate) fn update_document_for_history_step_application(
2096 &self,
2097 old_url: &ServoUrl,
2098 new_url: &ServoUrl,
2099 ) {
2100 if old_url.as_url()[Position::BeforeFragment..] !=
2130 new_url.as_url()[Position::BeforeFragment..]
2131 {
2132 let window = Trusted::new(self.owner_window().deref());
2133 let old_url = old_url.to_string();
2134 let new_url = new_url.to_string();
2135 self.owner_global()
2136 .task_manager()
2137 .dom_manipulation_task_source()
2138 .queue(task!(hashchange_event: move |cx| {
2139 let window = window.root();
2140 HashChangeEvent::new(
2141 cx,
2142 &window,
2143 atom!("hashchange"),
2144 false,
2145 false,
2146 old_url,
2147 new_url,
2148 )
2149 .upcast::<Event>()
2150 .fire(cx, window.upcast());
2151 }));
2152 }
2153 }
2154
2155 pub(crate) fn finish_load_for_dropped_blocker(&self, load: LoadType) {
2156 let this = Trusted::new(self);
2157 self.owner_global()
2158 .task_manager()
2159 .dom_manipulation_task_source()
2160 .queue(task!(check_finished_load: move |cx| {
2161 this.root().finish_load(load, cx);
2162 }));
2163 }
2164
2165 pub(crate) fn finish_load(&self, load: LoadType, cx: &mut JSContext) {
2168 debug!("Document got finish_load: {:?}", load);
2170 self.loader.borrow_mut().finish_load(&load);
2171
2172 match load {
2173 LoadType::Stylesheet(_) => {
2174 self.process_pending_parsing_blocking_script(cx);
2177
2178 self.process_deferred_scripts(cx);
2180 },
2181 LoadType::PageSource(_) => {
2182 if self.has_browsing_context && self.is_fully_active() {
2185 self.window().allow_layout_if_necessary(cx);
2186 }
2187
2188 self.process_deferred_scripts(cx);
2193 },
2194 _ => {},
2195 }
2196
2197 let document = Trusted::new(self);
2199 self.owner_global()
2200 .task_manager()
2201 .dom_manipulation_task_source()
2202 .queue(task!(wait_for_load_blockers: move |cx| {
2203 document.root().wait_until_load_blockers_have_resolved(cx);
2204 }));
2205 }
2206
2207 pub(crate) fn check_if_unloading_is_cancelled(
2209 &self,
2210 cx: &mut JSContext,
2211 recursive_flag: bool,
2212 ) -> bool {
2213 self.incr_ignore_opens_during_unload_counter();
2216 let beforeunload_event = BeforeUnloadEvent::new(
2218 cx,
2219 &self.window,
2220 atom!("beforeunload"),
2221 EventBubbles::Bubbles,
2222 EventCancelable::Cancelable,
2223 );
2224 let event = beforeunload_event.upcast::<Event>();
2225 event.set_trusted(true);
2226 let event_target = self.window.upcast::<EventTarget>();
2227 let has_listeners = event_target.has_listeners_for(&atom!("beforeunload"));
2228 self.window.dispatch_event_with_target_override(cx, event);
2229 if has_listeners {
2232 self.salvageable.set(false);
2233 }
2234 let mut can_unload = true;
2235 let default_prevented = event.DefaultPrevented();
2237 let return_value_not_empty = !event
2238 .downcast::<BeforeUnloadEvent>()
2239 .unwrap()
2240 .ReturnValue()
2241 .is_empty();
2242 if default_prevented || return_value_not_empty {
2243 let (chan, port) = generic_channel::channel().expect("Failed to create IPC channel!");
2244 let msg = EmbedderMsg::AllowUnload(self.webview_id(), chan);
2245 self.send_to_embedder(msg);
2246 can_unload = port.recv().unwrap() == AllowOrDeny::Allow;
2247 }
2248 if !recursive_flag {
2250 let iframes: Vec<_> = self.iframes().iter().collect();
2253 for iframe in &iframes {
2254 let document = iframe.owner_document();
2256 can_unload = document.check_if_unloading_is_cancelled(cx, true);
2257 if !document.salvageable() {
2258 self.salvageable.set(false);
2259 }
2260 if !can_unload {
2261 break;
2262 }
2263 }
2264 }
2265 self.decr_ignore_opens_during_unload_counter();
2267 can_unload
2268 }
2269
2270 pub(crate) fn unload(&self, cx: &mut JSContext, recursive_flag: bool) {
2272 if self.window_detached() {
2273 return;
2274 }
2275
2276 self.incr_ignore_opens_during_unload_counter();
2279 if self.page_showing.get() {
2281 self.page_showing.set(false);
2283 let event = PageTransitionEvent::new(
2286 cx,
2287 &self.window,
2288 atom!("pagehide"),
2289 false, false, self.salvageable.get(), );
2293 let event = event.upcast::<Event>();
2294 event.set_trusted(true);
2295 self.window.dispatch_event_with_target_override(cx, event);
2296 self.update_visibility_state(cx, DocumentVisibilityState::Hidden);
2298 }
2299 if !self.fired_unload.get() {
2301 let event = Event::new(
2302 cx,
2303 self.window.upcast(),
2304 atom!("unload"),
2305 EventBubbles::Bubbles,
2306 EventCancelable::Cancelable,
2307 );
2308 event.set_trusted(true);
2309 let event_target = self.window.upcast::<EventTarget>();
2310 let has_listeners = event_target.has_listeners_for(&atom!("unload"));
2311 self.window.dispatch_event_with_target_override(cx, &event);
2312 self.fired_unload.set(true);
2313 if has_listeners {
2315 self.salvageable.set(false);
2316 }
2317 }
2318 if !recursive_flag {
2322 let iframes: Vec<_> = self.iframes().iter().collect();
2325 for iframe in &iframes {
2326 let document = iframe.owner_document();
2328 document.unload(cx, true);
2329 if !document.salvageable() {
2330 self.salvageable.set(false);
2331 }
2332 }
2333 }
2334
2335 self.unloading_cleanup_steps(cx);
2337
2338 self.window.as_global_scope().clean_up_all_file_resources();
2340
2341 self.decr_ignore_opens_during_unload_counter();
2343
2344 }
2347
2348 fn completely_finish_loading(&self) {
2350 self.completely_loaded.set(true);
2355 self.notify_constellation_load();
2364
2365 if let Some(DeclarativeRefresh::PendingLoad {
2374 url,
2375 time,
2376 from_meta_element,
2377 }) = &*self.declarative_refresh.borrow()
2378 {
2379 self.window.as_global_scope().schedule_callback(
2380 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
2381 url: url.clone(),
2382 from_meta_element: *from_meta_element,
2383 }),
2384 Duration::from_secs(*time),
2385 );
2386 }
2387 }
2388
2389 fn queue_document_completion(&self, cx: &mut JSContext) {
2391 assert!(!self.is_initial_about_blank());
2395
2396 self.loader.borrow_mut().inhibit_events();
2397
2398 debug!("Document loads are complete.");
2403 let document = Trusted::new(self);
2404 self.owner_global()
2405 .task_manager()
2406 .dom_manipulation_task_source()
2407 .queue(task!(fire_load_event: move |cx| {
2408 let document = document.root();
2409 let window = document.window();
2411 if !window.is_alive() || document.window_detached() {
2412 return;
2413 }
2414
2415 document.update_the_current_document_readiness(cx, DocumentReadyState::Complete);
2417
2418 if document.browsing_context().is_none() {
2420 return;
2421 }
2422
2423 update_with_current_instant(&document.navigation_timing.load_event_start);
2425
2426 let load_event = Event::new(
2428 cx,
2429 window.upcast(),
2430 atom!("load"),
2431 EventBubbles::DoesNotBubble,
2432 EventCancelable::NotCancelable,
2433 );
2434 load_event.set_trusted(true);
2435 debug!("About to dispatch load for {:?}", document.url());
2436 window.dispatch_event_with_target_override(cx, &load_event);
2437
2438 update_with_current_instant(&document.navigation_timing.load_event_end);
2448
2449 document.page_showing.set(true);
2454
2455 let page_show_event = PageTransitionEvent::new(
2457 cx,
2458 window,
2459 atom!("pageshow"),
2460 false, false, false, );
2464 let page_show_event = page_show_event.upcast::<Event>();
2465 page_show_event.set_trusted(true);
2466 page_show_event.fire(cx, window.upcast());
2467
2468 document.completely_finish_loading();
2470
2471 if let Some(fragment) = document.url().fragment() {
2475 document.scroll_to_the_fragment(cx, fragment);
2476 }
2477 }));
2478
2479 #[cfg(feature = "webxr")]
2494 if pref!(dom_webxr_sessionavailable) && self.window.is_top_level() {
2495 self.window.Navigator(cx).Xr(cx).dispatch_sessionavailable();
2496 }
2497 }
2498
2499 pub(crate) fn completely_loaded(&self) -> bool {
2500 self.completely_loaded.get()
2501 }
2502
2503 pub(crate) fn start_the_end_loading_phase(&self) {
2504 if self.is_initial_about_blank() {
2505 self.current_the_end_loading_phase
2507 .set(TheEndLoadingPhase::Done);
2508 } else {
2509 self.current_the_end_loading_phase
2510 .set(TheEndLoadingPhase::ProcessingDeferredScripts);
2511 }
2512 }
2513
2514 pub(crate) fn set_pending_parsing_blocking_script(
2516 &self,
2517 script: &HTMLScriptElement,
2518 load: Option<ScriptResult>,
2519 ) {
2520 assert!(!self.has_pending_parsing_blocking_script());
2521 *self.pending_parsing_blocking_script.borrow_mut() =
2522 Some(PendingScript::new_with_load(script, load));
2523 }
2524
2525 pub(crate) fn has_pending_parsing_blocking_script(&self) -> bool {
2527 self.pending_parsing_blocking_script.borrow().is_some()
2528 }
2529
2530 pub(crate) fn pending_parsing_blocking_script_loaded(
2532 &self,
2533 element: &HTMLScriptElement,
2534 result: ScriptResult,
2535 cx: &mut JSContext,
2536 ) {
2537 {
2538 let mut blocking_script = self.pending_parsing_blocking_script.borrow_mut();
2539 let entry = blocking_script.as_mut().unwrap();
2540 assert!(&*entry.element == element);
2541 entry.loaded(result);
2542 }
2543 self.process_pending_parsing_blocking_script(cx);
2544 }
2545
2546 fn process_pending_parsing_blocking_script(&self, cx: &mut JSContext) {
2547 if self.has_a_stylesheet_that_is_blocking_scripts() {
2548 return;
2549 }
2550 let pair = self
2551 .pending_parsing_blocking_script
2552 .borrow_mut()
2553 .as_mut()
2554 .and_then(PendingScript::take_result);
2555 if let Some((element, result)) = pair {
2556 *self.pending_parsing_blocking_script.borrow_mut() = None;
2557 self.get_current_parser()
2558 .unwrap()
2559 .resume_with_pending_parsing_blocking_script(cx, &element, result);
2560 }
2561 }
2562
2563 pub(crate) fn add_asap_script(&self, script: &HTMLScriptElement) {
2565 self.asap_scripts_set
2566 .borrow_mut()
2567 .push(Dom::from_ref(script));
2568 }
2569
2570 pub(crate) fn asap_script_loaded(
2573 &self,
2574 cx: &mut JSContext,
2575 element: &HTMLScriptElement,
2576 result: ScriptResult,
2577 ) {
2578 {
2579 let mut scripts = self.asap_scripts_set.borrow_mut();
2580 let idx = scripts
2581 .iter()
2582 .position(|entry| &**entry == element)
2583 .unwrap();
2584 scripts.swap_remove(idx);
2585 }
2586 element.execute(cx, result);
2587 self.wait_until_asap_scripts_have_executed();
2588 }
2589
2590 pub(crate) fn push_asap_in_order_script(&self, script: &HTMLScriptElement) {
2592 self.asap_in_order_scripts_list.push(script);
2593 }
2594
2595 pub(crate) fn asap_in_order_script_loaded(
2598 &self,
2599 cx: &mut JSContext,
2600 element: &HTMLScriptElement,
2601 result: ScriptResult,
2602 ) {
2603 self.asap_in_order_scripts_list.loaded(element, result);
2604 while let Some((element, result)) = self
2605 .asap_in_order_scripts_list
2606 .take_next_ready_to_be_executed()
2607 {
2608 element.execute(cx, result);
2609 }
2610
2611 self.wait_until_asap_scripts_have_executed();
2612 }
2613
2614 pub(crate) fn add_deferred_script(&self, script: &HTMLScriptElement) {
2616 self.deferred_scripts.push(script);
2617 }
2618
2619 pub(crate) fn deferred_script_loaded(
2622 &self,
2623 cx: &mut JSContext,
2624 element: &HTMLScriptElement,
2625 result: ScriptResult,
2626 ) {
2627 self.deferred_scripts.loaded(element, result);
2628 self.process_deferred_scripts(cx);
2629 }
2630
2631 fn process_deferred_scripts(&self, cx: &mut JSContext) {
2633 if self.current_the_end_loading_phase.get() != TheEndLoadingPhase::ProcessingDeferredScripts
2634 {
2635 return;
2636 }
2637
2638 loop {
2642 if self.has_a_stylesheet_that_is_blocking_scripts() {
2643 return;
2644 }
2645 if let Some((element, result)) = self.deferred_scripts.take_next_ready_to_be_executed()
2648 {
2649 element.execute(cx, result);
2651 } else {
2652 break;
2653 }
2654 }
2655 if self.deferred_scripts.is_empty() {
2657 self.current_the_end_loading_phase
2658 .set(TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts);
2659 self.dispatch_dom_content_loaded();
2660 }
2661 }
2662
2663 fn dispatch_dom_content_loaded(&self) {
2665 assert_ne!(
2666 self.ReadyState(),
2667 DocumentReadyState::Complete,
2668 "Complete before DOMContentLoaded?"
2669 );
2670
2671 let document = Trusted::new(self);
2674 self.owner_global()
2675 .task_manager()
2676 .dom_manipulation_task_source()
2677 .queue(task!(fire_dom_content_loaded_event: move |cx| {
2678 let document = document.root();
2681 update_with_current_instant(&document.navigation_timing.dom_content_loaded_event_start);
2682 document.upcast::<EventTarget>().fire_bubbling_event(cx, atom!("DOMContentLoaded"));
2684 update_with_current_instant(&document.navigation_timing.dom_content_loaded_event_end);
2687 }));
2696
2697 self.interactive_time
2699 .borrow()
2700 .maybe_set_tti(InteractiveFlag::DOMContentLoaded);
2701
2702 self.wait_until_asap_scripts_have_executed();
2703 }
2704
2705 fn has_finished_all_asap_scripts(&self) -> bool {
2706 self.asap_scripts_set.borrow().is_empty() && self.asap_in_order_scripts_list.is_empty()
2707 }
2708
2709 fn wait_until_asap_scripts_have_executed(&self) {
2711 if self.current_the_end_loading_phase.get() !=
2712 TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts
2713 {
2714 return;
2715 }
2716 if self.has_finished_all_asap_scripts() {
2719 let document = Trusted::new(self);
2720 self.owner_global()
2721 .task_manager()
2722 .dom_manipulation_task_source()
2723 .queue(task!(transition_away_from_asap_scripts: move |cx| {
2724 let document = document.root();
2725 if document.current_the_end_loading_phase.get() !=
2728 TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts
2729 {
2730 return;
2731 }
2732 if !document.has_finished_all_asap_scripts() {
2734 return;
2735 }
2736 document.current_the_end_loading_phase
2737 .set(TheEndLoadingPhase::WaitingForLoadEventBlockers);
2738 document.wait_until_load_blockers_have_resolved(cx);
2739 }));
2740 }
2741 }
2742
2743 pub(crate) fn wait_until_load_blockers_have_resolved(&self, cx: &mut JSContext) {
2745 if self.current_the_end_loading_phase.get() !=
2746 TheEndLoadingPhase::WaitingForLoadEventBlockers
2747 {
2748 return;
2749 }
2750 {
2752 let loader = self.loader.borrow();
2753
2754 if self
2756 .navigation_timing
2757 .top_level_dom_complete
2758 .get()
2759 .is_none() &&
2760 loader.is_only_blocked_by_iframes()
2761 {
2762 update_with_current_instant(&self.navigation_timing.top_level_dom_complete);
2763 }
2764
2765 let not_ready_for_load = loader.is_blocked() || loader.events_inhibited();
2766 if not_ready_for_load {
2767 return;
2768 }
2769 }
2770
2771 self.current_the_end_loading_phase
2772 .set(TheEndLoadingPhase::Done);
2773 self.queue_document_completion(cx);
2774 }
2775
2776 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
2778 if !self.is_fully_active() {
2780 self.salvageable.set(false);
2785 }
2789 for exited_iframe in self.iframes().iter() {
2802 debug!("Destroying nested iframe document");
2803 exited_iframe.destroy_document_and_its_descendants(cx);
2804 }
2805 self.destroy(cx);
2810 }
2813
2814 pub(crate) fn destroy(&self, cx: &mut JSContext) {
2816 let exited_window = self.window();
2817 self.abort(cx);
2819 self.salvageable.set(false);
2821 self.unloading_cleanup_steps(cx);
2831
2832 exited_window
2835 .as_global_scope()
2836 .task_manager()
2837 .cancel_all_tasks_and_ignore_future_tasks();
2838
2839 exited_window.discard_browsing_context();
2841
2842 exited_window
2849 .as_global_scope()
2850 .disable_owned_worker_animation_frame_providers();
2851
2852 }
2856
2857 fn active_parser(&self) -> Option<DomRoot<ServoParser>> {
2859 self.get_current_parser()
2862 .filter(|parser| !(parser.has_stopped() || parser.has_aborted()))
2863 }
2864
2865 pub(crate) fn abort(&self, cx: &mut JSContext) {
2867 self.loader.borrow_mut().inhibit_events();
2869
2870 self.script_blocking_stylesheet_set.borrow_mut().clear();
2879 *self.pending_parsing_blocking_script.borrow_mut() = None;
2880 *self.asap_scripts_set.borrow_mut() = vec![];
2881 self.asap_in_order_scripts_list.clear();
2882 self.deferred_scripts.clear();
2883
2884 let global = self.window.as_global_scope();
2885 let loads_cancelled = global.fetch_group_mut().terminate(global);
2886 let event_sources_canceled = global.close_event_sources();
2887
2888 if loads_cancelled || event_sources_canceled {
2889 self.salvageable.set(false);
2891 };
2892
2893 self.owner_global()
2898 .task_manager()
2899 .cancel_pending_tasks_for_source(TaskSourceName::Networking);
2900
2901 if let Some(parser) = self.active_parser() {
2906 self.active_parser_was_aborted.set(true);
2908 parser.abort(cx);
2910 self.salvageable.set(false);
2912 }
2913 }
2914
2915 pub(crate) fn abort_a_document_and_its_descendants(&self, cx: &mut JSContext) {
2917 for iframe in self.iframes().iter() {
2925 if let Some(descendant_document) = iframe.GetContentDocument() {
2926 let trusted_descendant_document = Trusted::new(&*descendant_document);
2927 let document = Trusted::new(self);
2928 descendant_document
2929 .owner_global()
2930 .task_manager()
2931 .navigation_and_traversal_task_source()
2932 .queue(task!(abort_iframe_document: move |cx| {
2933 let descendant_document = trusted_descendant_document.root();
2934 descendant_document.abort(cx);
2936 if !descendant_document.salvageable.get() {
2938 document.root().salvageable.set(false);
2939 }
2940 }));
2941 }
2942 }
2943
2944 self.abort(cx);
2946 }
2947
2948 pub(crate) fn notify_constellation_load(&self) {
2949 self.window()
2950 .send_to_constellation(ScriptToConstellationMessage::LoadComplete);
2951 }
2952
2953 pub(crate) fn set_current_parser(&self, script: Option<&ServoParser>) {
2954 self.current_parser.set(script);
2955 }
2956
2957 pub(crate) fn get_current_parser(&self) -> Option<DomRoot<ServoParser>> {
2958 self.current_parser.get()
2959 }
2960
2961 pub(crate) fn get_current_parser_line(&self) -> u32 {
2962 self.get_current_parser()
2963 .map(|parser| parser.get_current_line())
2964 .unwrap_or(0)
2965 }
2966
2967 pub(crate) fn set_ancestor_origins_list(&self, ancestor_origins_list: &DOMStringList) {
2969 self.ancestor_origins_list.set(Some(ancestor_origins_list));
2970 }
2971
2972 pub(crate) fn ancestor_origins_list(&self) -> Option<DomRoot<DOMStringList>> {
2974 self.ancestor_origins_list.get()
2975 }
2976
2977 pub(crate) fn set_internal_ancestor_origin_objects_list(
2979 &self,
2980 internal_ancestor_origin_objects_list: Vec<ImmutableOrigin>,
2981 ) {
2982 *self.internal_ancestor_origin_objects_list.borrow_mut() =
2983 Some(internal_ancestor_origin_objects_list);
2984 }
2985
2986 pub(crate) fn internal_ancestor_origin_objects_list(
2988 &self,
2989 ) -> Ref<'_, Option<Vec<ImmutableOrigin>>> {
2990 self.internal_ancestor_origin_objects_list.borrow()
2991 }
2992
2993 pub(crate) fn iframes(&self) -> Ref<'_, IFrameCollection> {
2996 self.iframes.borrow()
2997 }
2998
2999 pub(crate) fn iframes_mut(&self) -> RefMut<'_, IFrameCollection> {
3002 self.iframes.borrow_mut()
3003 }
3004
3005 pub(crate) fn set_navigation_start(&self, navigation_start: CrossProcessInstant) {
3006 self.interactive_time
3007 .borrow_mut()
3008 .set_navigation_start(navigation_start);
3009 }
3010
3011 pub(crate) fn get_interactive_metrics(&self) -> Ref<'_, ProgressiveWebMetrics> {
3012 self.interactive_time.borrow()
3013 }
3014
3015 pub(crate) fn has_recorded_tti_metric(&self) -> bool {
3016 self.get_interactive_metrics().get_tti().is_some()
3017 }
3018
3019 pub(crate) fn start_tti(&self) {
3020 if self.get_interactive_metrics().needs_tti() {
3021 self.tti_window.borrow_mut().start_window();
3022 }
3023 }
3024
3025 pub(crate) fn record_tti_if_necessary(&self) {
3029 if self.has_recorded_tti_metric() {
3030 return;
3031 }
3032 if self.tti_window.borrow().needs_check() {
3033 self.get_interactive_metrics()
3034 .maybe_set_tti(InteractiveFlag::TimeToInteractive(
3035 self.tti_window.borrow().get_start(),
3036 ));
3037 }
3038 }
3039
3040 pub(crate) fn is_cookie_averse(&self) -> bool {
3042 !self.has_browsing_context || !url_has_network_scheme(&self.url())
3043 }
3044
3045 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
3046 self.document_or_shadow_root.custom_element_registry()
3047 }
3048
3049 pub(crate) fn set_custom_element_registry(&self, registry: &CustomElementRegistry) {
3050 self.document_or_shadow_root
3051 .set_custom_element_registry(Some(registry));
3052 }
3053
3054 pub(crate) fn effective_global_custom_element_registry(
3056 &self,
3057 ) -> Option<DomRoot<CustomElementRegistry>> {
3058 let document_custom_element_registry = self.custom_element_registry();
3061 if CustomElementRegistry::is_a_global_element_registry(
3062 document_custom_element_registry.as_deref(),
3063 ) {
3064 return document_custom_element_registry;
3065 }
3066 None
3068 }
3069
3070 pub(crate) fn teardown_custom_element_registry(&self) {
3073 if let Some(custom_elements) = self.custom_element_registry() {
3074 custom_elements.teardown();
3075 }
3076 }
3077
3078 pub(crate) fn increment_throw_on_dynamic_markup_insertion_counter(&self) {
3079 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
3080 self.throw_on_dynamic_markup_insertion_counter
3081 .set(counter + 1);
3082 }
3083
3084 pub(crate) fn decrement_throw_on_dynamic_markup_insertion_counter(&self) {
3085 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
3086 self.throw_on_dynamic_markup_insertion_counter
3087 .set(counter - 1);
3088 }
3089
3090 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
3091 for image in self.responsive_images.borrow().iter() {
3092 image.react_to_environment_changes(cx);
3093 }
3094 }
3095
3096 pub(crate) fn register_responsive_image(&self, img: &HTMLImageElement) {
3097 self.responsive_images.borrow_mut().push(Dom::from_ref(img));
3098 }
3099
3100 pub(crate) fn unregister_responsive_image(&self, img: &HTMLImageElement) {
3101 let index = self
3102 .responsive_images
3103 .borrow()
3104 .iter()
3105 .position(|x| **x == *img);
3106 if let Some(i) = index {
3107 self.responsive_images.borrow_mut().remove(i);
3108 }
3109 }
3110
3111 pub(crate) fn register_media_controls(&self, id: &str, controls: &ShadowRoot) {
3112 let did_have_these_media_controls = self
3113 .media_controls
3114 .borrow_mut()
3115 .insert(id.to_string(), Dom::from_ref(controls))
3116 .is_some();
3117 debug_assert!(
3118 !did_have_these_media_controls,
3119 "Trying to register known media controls"
3120 );
3121 }
3122
3123 pub(crate) fn unregister_media_controls(&self, id: &str) {
3124 let did_have_these_media_controls = self.media_controls.borrow_mut().remove(id).is_some();
3125 debug_assert!(
3126 did_have_these_media_controls,
3127 "Trying to unregister unknown media controls"
3128 );
3129 }
3130
3131 pub(crate) fn mark_canvas_as_dirty(&self, canvas: &Dom<HTMLCanvasElement>) {
3132 let mut dirty_canvases = self.dirty_canvases.borrow_mut();
3133 if dirty_canvases
3134 .iter()
3135 .any(|dirty_canvas| dirty_canvas == canvas)
3136 {
3137 return;
3138 }
3139 dirty_canvases.push(canvas.clone());
3140 }
3141
3142 pub(crate) fn needs_rendering_update(&self, no_gc: &NoGC) -> bool {
3146 if !self.is_fully_active() {
3147 return false;
3148 }
3149 if !self.window().layout_blocked() &&
3150 (!self.restyle_reason(no_gc).is_empty() ||
3151 self.window().layout().needs_new_display_list() ||
3152 self.window().layout().force_accessibility_update())
3153 {
3154 return true;
3155 }
3156 if !self.rendering_update_reasons.get().is_empty() {
3157 return true;
3158 }
3159 if self.event_handler.has_pending_input_events() {
3160 return true;
3161 }
3162 if self.has_pending_scroll_events() {
3163 return true;
3164 }
3165 if self.window().has_unhandled_resize_event() {
3166 return true;
3167 }
3168 if self.has_pending_animated_image_update.get() || !self.dirty_canvases.borrow().is_empty()
3169 {
3170 return true;
3171 }
3172 if self.window().has_pending_media_query_evaluation() {
3173 return true;
3174 }
3175 if self
3176 .selection()
3177 .is_some_and(|selection| selection.visible_selection_dirty())
3178 {
3179 return true;
3180 }
3181
3182 false
3183 }
3184
3185 pub(crate) fn update_the_rendering(
3193 &self,
3194 cx: &mut JSContext,
3195 ) -> (ReflowPhasesRun, ReflowStatistics) {
3196 assert!(!self.is_render_blocked());
3197
3198 let mut phases = ReflowPhasesRun::empty();
3199 if self.has_pending_animated_image_update.get() {
3200 self.image_animation_manager
3201 .borrow()
3202 .update_active_frames(&self.window, self.current_animation_timeline_value());
3203 self.has_pending_animated_image_update.set(false);
3204 phases.insert(ReflowPhasesRun::UpdatedImageData);
3205 }
3206
3207 self.current_rendering_epoch
3208 .set(self.current_rendering_epoch.get().next());
3209 let current_rendering_epoch = self.current_rendering_epoch.get();
3210
3211 let image_keys: Vec<_> = self
3213 .dirty_canvases
3214 .borrow_mut()
3215 .drain(..)
3216 .filter_map(|canvas| canvas.update_rendering(current_rendering_epoch))
3217 .collect();
3218
3219 let pipeline_id = self.window().pipeline_id();
3222 if !image_keys.is_empty() {
3223 phases.insert(ReflowPhasesRun::UpdatedImageData);
3224 self.waiting_on_canvas_image_updates.set(true);
3225 self.window().paint_api().delay_new_frame_for_canvas(
3226 self.webview_id(),
3227 self.window().pipeline_id(),
3228 current_rendering_epoch,
3229 image_keys,
3230 );
3231 }
3232
3233 let (reflow_phases, statistics) = self.window().reflow(cx, ReflowGoal::UpdateTheRendering);
3234 let phases = phases.union(reflow_phases);
3235
3236 self.window().paint_api().update_epoch(
3237 self.webview_id(),
3238 pipeline_id,
3239 current_rendering_epoch,
3240 );
3241
3242 (phases, statistics)
3243 }
3244
3245 pub(crate) fn handle_no_longer_waiting_on_asynchronous_image_updates(&self) {
3246 self.waiting_on_canvas_image_updates.set(false);
3247 }
3248
3249 pub(crate) fn waiting_on_canvas_image_updates(&self) -> bool {
3250 self.waiting_on_canvas_image_updates.get()
3251 }
3252
3253 pub(crate) fn maybe_fulfill_font_ready_promise(&self, cx: &mut JSContext) -> bool {
3263 if !self.is_fully_active() {
3264 return false;
3265 }
3266
3267 let fonts = self.Fonts(cx);
3268 if !fonts.waiting_to_fullfill_promise() {
3269 return false;
3270 }
3271 if self.window().font_context().web_fonts_still_loading() != 0 {
3272 return false;
3273 }
3274 if self.ReadyState() != DocumentReadyState::Complete {
3275 return false;
3276 }
3277 if !self.restyle_reason(cx.no_gc()).is_empty() {
3278 return false;
3279 }
3280 if !self.rendering_update_reasons.get().is_empty() {
3281 return false;
3282 }
3283
3284 let result = fonts.fulfill_ready_promise_if_needed(cx);
3285
3286 if result {
3290 self.add_rendering_update_reason(RenderingUpdateReason::FontReadyPromiseFulfilled);
3291 }
3292
3293 result
3294 }
3295
3296 pub(crate) fn id_map(&self) -> &TreeOrderedIndexMap {
3297 &self.id_map
3298 }
3299
3300 pub(crate) fn add_resize_observer(&self, resize_observer: &ResizeObserver) {
3302 self.resize_observers
3303 .borrow_mut()
3304 .push(Dom::from_ref(resize_observer));
3305 }
3306
3307 pub(crate) fn gather_active_resize_observations_at_depth(
3310 &self,
3311 no_gc: &NoGC,
3312 depth: &ResizeObservationDepth,
3313 ) -> bool {
3314 let mut has_active_resize_observations = false;
3315 for observer in self.resize_observers.borrow_mut().iter_mut() {
3316 observer.gather_active_resize_observations_at_depth(
3317 no_gc,
3318 depth,
3319 &mut has_active_resize_observations,
3320 );
3321 }
3322 has_active_resize_observations
3323 }
3324
3325 #[expect(clippy::redundant_iter_cloned)]
3327 pub(crate) fn broadcast_active_resize_observations(
3328 &self,
3329 cx: &mut JSContext,
3330 ) -> ResizeObservationDepth {
3331 let mut shallowest = ResizeObservationDepth::max();
3332 let iterator: Vec<DomRoot<ResizeObserver>> = self
3336 .resize_observers
3337 .borrow()
3338 .iter()
3339 .cloned()
3340 .map(|obs| DomRoot::from_ref(&*obs))
3341 .collect();
3342 for observer in iterator {
3343 observer.broadcast_active_resize_observations(cx, &mut shallowest);
3344 }
3345 shallowest
3346 }
3347
3348 pub(crate) fn has_skipped_resize_observations(&self) -> bool {
3350 self.resize_observers
3351 .borrow()
3352 .iter()
3353 .any(|observer| observer.has_skipped_resize_observations())
3354 }
3355
3356 pub(crate) fn deliver_resize_loop_error_notification(&self, cx: &mut JSContext) {
3358 let error_info: ErrorInfo = crate::dom::bindings::error::ErrorInfo {
3359 message: "ResizeObserver loop completed with undelivered notifications.".to_string(),
3360 ..Default::default()
3361 };
3362 self.window
3363 .as_global_scope()
3364 .report_an_error(cx, error_info, HandleValue::null());
3365 }
3366
3367 pub(crate) fn status_code(&self) -> Option<u16> {
3368 self.status_code
3369 }
3370
3371 pub(crate) fn encoding_parse_a_url(&self, url: &str) -> Result<ServoUrl, url::ParseError> {
3373 let encoding = self.encoding.get();
3379
3380 let base_url = self.base_url();
3386
3387 url::Url::options()
3389 .base_url(Some(base_url.as_url()))
3390 .encoding_override(Some(&|input| {
3391 servo_url::encoding::encode_as_url_query_string(input, encoding)
3392 }))
3393 .parse(url)
3394 .map(ServoUrl::from)
3395 }
3396
3397 pub(crate) fn allowed_to_use_feature(&self, _feature: PermissionName) -> bool {
3399 if !self.has_browsing_context {
3401 return false;
3402 }
3403
3404 if !self.is_fully_active() {
3406 return false;
3407 }
3408
3409 true
3415 }
3416
3417 pub(crate) fn add_intersection_observer(&self, intersection_observer: &IntersectionObserver) {
3420 self.intersection_observers
3421 .borrow_mut()
3422 .push(Dom::from_ref(intersection_observer));
3423 }
3424
3425 pub(crate) fn remove_intersection_observer(
3429 &self,
3430 intersection_observer: &IntersectionObserver,
3431 ) {
3432 self.intersection_observers
3433 .borrow_mut()
3434 .retain(|observer| *observer != intersection_observer)
3435 }
3436
3437 pub(crate) fn update_intersection_observer_steps(
3439 &self,
3440 cx: &mut JSContext,
3441 time: CrossProcessInstant,
3442 ) {
3443 if self.intersection_observers.borrow().is_empty() {
3444 return;
3445 }
3446 self.window()
3448 .reflow_for_non_flushing_update_the_rendering_queries(cx);
3449
3450 for intersection_observer in &*self.intersection_observers.borrow() {
3452 self.update_single_intersection_observer_steps(cx, intersection_observer, time);
3453 }
3454 }
3455
3456 fn update_single_intersection_observer_steps(
3458 &self,
3459 cx: &mut JSContext,
3460 intersection_observer: &IntersectionObserver,
3461 time: CrossProcessInstant,
3462 ) {
3463 let root_bounds = intersection_observer.root_intersection_rectangle();
3466
3467 intersection_observer.update_intersection_observations_steps(cx, self, time, root_bounds);
3471 }
3472
3473 pub(crate) fn notify_intersection_observers(&self, cx: &mut JSContext) {
3475 self.intersection_observer_task_queued.set(false);
3478
3479 rooted_vec!(let notify_list <- self.intersection_observers.clone().take().into_iter());
3484
3485 for intersection_observer in notify_list.iter() {
3488 intersection_observer.invoke_callback_if_necessary(cx);
3490 }
3491 }
3492
3493 pub(crate) fn queue_an_intersection_observer_task(&self) {
3495 if self.intersection_observer_task_queued.get() {
3498 return;
3499 }
3500
3501 self.intersection_observer_task_queued.set(true);
3504
3505 let document = Trusted::new(self);
3509 self.owner_global()
3510 .task_manager()
3511 .intersection_observer_task_source()
3512 .queue(task!(notify_intersection_observers: move |cx| {
3513 document.root().notify_intersection_observers(cx);
3514 }));
3515 }
3516
3517 pub(crate) fn store_lcp_candidate(&self, id: LCPCandidateID, element: &Element) {
3518 self.lcp_candidates
3519 .borrow_mut()
3520 .insert(id, Dom::from_ref(element));
3521 }
3522
3523 pub(crate) fn handle_paint_metric(
3524 &self,
3525 cx: &mut JSContext,
3526 metric_type: ProgressiveWebMetricType,
3527 metric_value: CrossProcessInstant,
3528 first_reflow: bool,
3529 ) {
3530 let metrics = self.interactive_time.borrow();
3531 match metric_type {
3532 ProgressiveWebMetricType::FirstPaint |
3533 ProgressiveWebMetricType::FirstContentfulPaint => {
3534 let binding = PerformancePaintTiming::new(
3535 cx,
3536 self.window.as_global_scope(),
3537 metric_type.clone(),
3538 metric_value,
3539 );
3540 metrics.set_performance_paint_metric(metric_value, first_reflow, metric_type);
3541 let entry = binding.upcast::<PerformanceEntry>();
3542 self.window.Performance(cx).queue_entry(entry);
3543 },
3544 ProgressiveWebMetricType::LargestContentfulPaint { area, url, id } => {
3545 let binding = LargestContentfulPaint::new(
3546 cx,
3547 self.window.as_global_scope(),
3548 metric_value,
3549 area,
3550 url,
3551 self.lcp_candidates.borrow_mut().remove(&id).as_deref(),
3552 );
3553 metrics.set_largest_contentful_paint(id, metric_value, area);
3554 let entry = binding.upcast::<PerformanceEntry>();
3555 self.window.Performance(cx).queue_entry(entry);
3556 },
3557 ProgressiveWebMetricType::TimeToInteractive => {
3558 unreachable!("Unexpected non-paint metric.")
3559 },
3560 }
3561 }
3562
3563 fn write(
3565 &self,
3566 cx: &mut JSContext,
3567 text: Vec<TrustedHTMLOrString>,
3568 line_feed: bool,
3569 containing_class: &str,
3570 field: &str,
3571 ) -> ErrorResult {
3572 let mut strings: Vec<String> = Vec::with_capacity(text.len());
3574 let mut is_trusted = true;
3576 for value in text {
3578 match value {
3579 TrustedHTMLOrString::TrustedHTML(trusted_html) => {
3581 strings.push(trusted_html.to_string());
3582 },
3583 TrustedHTMLOrString::String(str_) => {
3584 is_trusted = false;
3586 strings.push(str_.into());
3588 },
3589 };
3590 }
3591 let mut string = itertools::join(strings, "");
3592 if !is_trusted {
3596 string = TrustedHTML::get_trusted_type_compliant_string(
3597 cx,
3598 &self.global(),
3599 TrustedHTMLOrString::String(string.into()),
3600 &format!("{} {}", containing_class, field),
3601 )?
3602 .str()
3603 .to_owned();
3604 }
3605 if line_feed {
3607 string.push('\n');
3608 }
3609 if !self.is_html_document() {
3611 return Err(Error::InvalidState(None));
3612 }
3613
3614 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
3617 return Err(Error::InvalidState(None));
3618 }
3619
3620 if self.active_parser_was_aborted.get() {
3622 return Ok(());
3623 }
3624
3625 let parser = match self.get_current_parser() {
3626 Some(ref parser) if parser.can_write() => DomRoot::from_ref(&**parser),
3627 _ => {
3629 if self.is_prompting_or_unloading() ||
3632 self.ignore_destructive_writes_counter.get() > 0
3633 {
3634 return Ok(());
3635 }
3636 self.Open(cx, None, None)?;
3638 self.get_current_parser().unwrap()
3639 },
3640 };
3641
3642 parser.write(cx, string.into());
3644
3645 Ok(())
3646 }
3647
3648 pub(crate) fn details_name_groups<'a: 'b, 'b>(
3649 &'a self,
3650 no_gc: &'b NoGC,
3651 ) -> RefMut<'b, DetailsNameGroups> {
3652 RefMut::map(
3653 self.details_name_groups.safe_borrow_mut(no_gc),
3654 |details_name_groups| details_name_groups.get_or_insert_default(),
3655 )
3656 }
3657
3658 pub(crate) fn accessibility_data_mut(&self) -> RefMut<'_, AccessibilityData> {
3659 self.accessibility_data.borrow_mut()
3660 }
3661
3662 pub(crate) fn accessibility_active(&self) -> bool {
3663 self.window().layout().accessibility_active()
3664 }
3665
3666 pub(crate) fn rooted_nodes_for_accessibility_integrity_check(
3667 &self,
3668 ) -> Option<FxHashSet<OpaqueNode>> {
3669 if !self.accessibility_active() {
3670 return None;
3671 }
3672
3673 let mut accessibility_data = self.accessibility_data_mut();
3674
3675 if pref!(expensive_accessibility_test_assertions_enabled) {
3676 return Some(accessibility_data.unroot_and_drain_all_removed_nodes());
3677 }
3678
3679 accessibility_data.unroot_all_removed_nodes();
3680 None
3681 }
3682
3683 pub(crate) fn get_document_element_unrooted<'a>(
3684 &self,
3685 no_gc: &'a NoGC,
3686 ) -> Option<UnrootedDom<'a, Element>> {
3687 self.upcast::<Node>().child_elements_unrooted(no_gc).next()
3688 }
3689
3690 pub(crate) fn collect_reports(
3691 &self,
3692 reports: &mut Vec<Report>,
3693 ops: &mut MallocSizeOfOps,
3694 ) -> HashSet<*const JSObject> {
3695 let mut computed_objects = HashSet::new();
3696 let mut sizes = DocumentSizes::default();
3697
3698 for node in self
3699 .upcast::<Node>()
3700 .traverse_preorder(ShadowIncluding::Yes)
3701 {
3702 let size = compute_size(node.jsobject(), ops, &computed_objects, None);
3703
3704 match node.type_id() {
3705 NodeTypeId::Element(_) => {
3706 sizes.element_nodes_size += size;
3707
3708 let element = node.downcast::<Element>().expect("node must be Element");
3709 for attr in element.attrs().borrow().iter() {
3710 if let Some(attr) = attr.as_attr() {
3711 let size = compute_size(
3712 attr.upcast::<Node>().jsobject(),
3713 ops,
3714 &computed_objects,
3715 None,
3716 );
3717 sizes.attribute_nodes_size += size;
3718 computed_objects.insert(attr.upcast::<Node>().jsobject());
3719 }
3720 }
3721 },
3722 NodeTypeId::CharacterData(_) => sizes.text_nodes_size += size,
3723 _ => sizes.other_nodes_size += size,
3724 };
3725
3726 computed_objects.insert(node.jsobject());
3727 }
3728
3729 let prefix = format!("url({})", self.url());
3730 reports.push(Report {
3731 path: path![prefix, "js", "dom", "element-nodes"],
3732 kind: ReportKind::ExplicitJemallocHeapSize,
3733 size: sizes.element_nodes_size,
3734 });
3735 reports.push(Report {
3736 path: path![prefix, "js", "dom", "text-nodes"],
3737 kind: ReportKind::ExplicitJemallocHeapSize,
3738 size: sizes.text_nodes_size,
3739 });
3740 reports.push(Report {
3741 path: path![prefix, "js", "dom", "attribute-nodes"],
3742 kind: ReportKind::ExplicitJemallocHeapSize,
3743 size: sizes.attribute_nodes_size,
3744 });
3745 reports.push(Report {
3746 path: path![prefix, "js", "dom", "other-nodes"],
3747 kind: ReportKind::ExplicitJemallocHeapSize,
3748 size: sizes.other_nodes_size,
3749 });
3750
3751 computed_objects
3752 }
3753}
3754
3755#[derive(Default)]
3757struct DocumentSizes {
3758 element_nodes_size: usize,
3759 text_nodes_size: usize,
3760 attribute_nodes_size: usize,
3761 other_nodes_size: usize,
3762}
3763
3764impl<'dom> LayoutDom<'dom, Document> {
3765 #[inline]
3766 pub(crate) fn is_html_document_for_layout(&self) -> bool {
3767 self.unsafe_get().is_html_document
3768 }
3769
3770 #[inline]
3771 pub(crate) fn quirks_mode(self) -> QuirksMode {
3772 self.unsafe_get().quirks_mode.get()
3773 }
3774
3775 #[inline]
3776 pub(crate) fn shared_style_locks(self) -> &'dom SharedRwLocks {
3777 self.unsafe_get().shared_style_locks()
3778 }
3779
3780 #[inline]
3781 pub(crate) fn flush_shadow_root_stylesheets_if_necessary(
3782 self,
3783 stylist: &mut Stylist,
3784 guard: &SharedRwLockReadGuard,
3785 ) {
3786 (*self.unsafe_get()).flush_shadow_root_stylesheets_if_necessary_for_layout(stylist, guard)
3787 }
3788
3789 pub(crate) fn elements_with_id(self, id: &Atom) -> &[LayoutDom<'dom, Element>] {
3790 self.unsafe_get().id_map.get_all_for_layout(id)
3791 }
3792
3793 #[expect(unsafe_code)]
3794 pub(crate) fn url_for_layout(self) -> ServoUrl {
3795 unsafe { self.unsafe_get().url.borrow_for_layout() }.clone()
3796 }
3797
3798 #[expect(unsafe_code)]
3799 pub(crate) fn selection_for_layout(&self) -> Option<LayoutDom<'dom, Selection>> {
3800 unsafe { self.unsafe_get().selection.to_layout() }
3801 }
3802}
3803
3804pub(crate) fn get_registrable_domain_suffix_of_or_is_equal_to(
3808 host_suffix_string: &str,
3809 original_host: Host,
3810) -> Option<Host> {
3811 if host_suffix_string.is_empty() {
3813 return None;
3814 }
3815
3816 let host = match Host::parse(host_suffix_string) {
3818 Ok(host) => host,
3819 Err(_) => return None,
3820 };
3821
3822 if host != original_host {
3824 let host = match host {
3826 Host::Domain(ref host) => host,
3827 _ => return None,
3828 };
3829 let original_host = match original_host {
3830 Host::Domain(ref original_host) => original_host,
3831 _ => return None,
3832 };
3833
3834 let index = original_host.len().checked_sub(host.len())?;
3836 let (prefix, suffix) = original_host.split_at(index);
3837
3838 if !prefix.ends_with('.') {
3839 return None;
3840 }
3841 if suffix != host {
3842 return None;
3843 }
3844
3845 if is_pub_domain(host) {
3847 return None;
3848 }
3849 }
3850
3851 Some(host)
3853}
3854
3855fn url_has_network_scheme(url: &ServoUrl) -> bool {
3857 matches!(url.scheme(), "ftp" | "http" | "https")
3858}
3859
3860#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
3861pub(crate) enum HasBrowsingContext {
3862 No,
3863 Yes,
3864}
3865
3866impl Document {
3867 #[allow(clippy::too_many_arguments)]
3868 pub(crate) fn new_inherited(
3869 window: &Window,
3870 has_browsing_context: HasBrowsingContext,
3871 url: Option<ServoUrl>,
3872 about_base_url: Option<ServoUrl>,
3873 origin: MutableOrigin,
3874 is_html_document: IsHTMLDocument,
3875 content_type: Option<Mime>,
3876 last_modified: Option<String>,
3877 activity: DocumentActivity,
3878 doc_loader: DocumentLoader,
3879 referrer: Option<String>,
3880 status_code: Option<u16>,
3881 canceller: FetchCanceller,
3882 is_initial_about_blank: bool,
3883 allow_declarative_shadow_roots: bool,
3884 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
3885 has_trustworthy_ancestor_origin: bool,
3886 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
3887 creation_sandboxing_flag_set: SandboxingFlagSet,
3888 timeline: &DocumentTimeline,
3889 pipeline_id: PipelineId,
3890 image_cache: StdArc<dyn ImageCache>,
3891 ) -> Document {
3892 let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
3893
3894 let frame_type = match window.is_top_level() {
3895 true => TimerMetadataFrameType::RootWindow,
3896 false => TimerMetadataFrameType::IFrame,
3897 };
3898 let interactive_time = ProgressiveWebMetrics::new(
3899 window.time_profiler_chan().clone(),
3900 url.clone(),
3901 frame_type,
3902 );
3903
3904 let content_type = content_type.unwrap_or_else(|| {
3905 match is_html_document {
3906 IsHTMLDocument::HTMLDocument => "text/html",
3908 IsHTMLDocument::NonHTMLDocument => "application/xml",
3910 }
3911 .parse()
3912 .unwrap()
3913 });
3914
3915 let encoding = content_type
3916 .get_parameter(CHARSET)
3917 .and_then(|charset| Encoding::for_label(charset.as_bytes()))
3918 .unwrap_or(UTF_8);
3919
3920 let has_focus = window.parent_info().is_none();
3921 let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
3922 let shared_style_locks = window.script_thread().shared_style_locks().clone();
3923 let quirks_mode = if is_initial_about_blank {
3927 QuirksMode::Quirks
3928 } else {
3929 QuirksMode::NoQuirks
3931 };
3932
3933 Document {
3934 node: Node::new_document_node(),
3935 document_or_shadow_root: DocumentOrShadowRoot::new(window),
3936 window: Dom::from_ref(window),
3937 has_browsing_context,
3938 implementation: Default::default(),
3939 content_type,
3940 last_modified,
3941 url: DomRefCell::new(url),
3942 about_base_url: DomRefCell::new(about_base_url),
3943 quirks_mode: Cell::new(quirks_mode),
3944 event_handler: DocumentEventHandler::new(window),
3945 focus_handler: DocumentFocusHandler::new(window, has_focus),
3946 embedder_controls: DocumentEmbedderControls::new(window),
3947 id_map: TreeOrderedIndexMap::id(),
3948 name_map: TreeOrderedIndexMap::name(),
3949 encoding: Cell::new(encoding),
3951 is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
3952 activity: Cell::new(activity),
3953 tag_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3954 tagns_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3955 classes_map: DomRefCell::new(HashMapTracedValues::new()),
3956 images: Default::default(),
3957 embeds: Default::default(),
3958 links: Default::default(),
3959 forms: Default::default(),
3960 scripts: Default::default(),
3961 anchors: Default::default(),
3962 applets: Default::default(),
3963 iframes: RefCell::new(IFrameCollection::new()),
3964 shared_style_locks,
3965 stylesheets: DomRefCell::new(DocumentStylesheetSet::new()),
3966 stylesheet_list: MutNullableDom::new(None),
3967 ready_state: Cell::new(DocumentReadyState::Complete),
3970 current_script: Default::default(),
3971 current_the_end_loading_phase: Default::default(),
3972 pending_parsing_blocking_script: Default::default(),
3973 script_blocking_stylesheet_set: Default::default(),
3974 render_blocking_element_count: Default::default(),
3975 deferred_scripts: Default::default(),
3976 asap_in_order_scripts_list: Default::default(),
3977 asap_scripts_set: Default::default(),
3978 animation_frame_ident: Cell::new(0),
3979 animation_frame_list: DomRefCell::new(VecDeque::new()),
3980 running_animation_callbacks: Cell::new(false),
3981 loader: DomRefCell::new(doc_loader),
3982 current_parser: Default::default(),
3983 base_element: Default::default(),
3984 target_base_element: Default::default(),
3985 ancestor_origins_list: Default::default(),
3986 internal_ancestor_origin_objects_list: Default::default(),
3987 appropriate_template_contents_owner_document: Default::default(),
3988 pending_restyles: DomRefCell::new(FxHashMap::default()),
3989 needs_restyle: Cell::new(RestyleReason::DOMChanged),
3990 origin: DomRefCell::new(origin),
3991 referrer,
3992 target_element: MutNullableDom::new(None),
3993 policy_container: DomRefCell::new(PolicyContainer::default()),
3994 preloaded_resources: Default::default(),
3995 ignore_destructive_writes_counter: Default::default(),
3996 ignore_opens_during_unload_counter: Default::default(),
3997 spurious_animation_frames: Cell::new(0),
3998 fullscreen_element: MutNullableDom::new(None),
3999 form_id_listener_map: Default::default(),
4000 interactive_time: DomRefCell::new(interactive_time),
4001 tti_window: DomRefCell::new(InteractiveWindow::default()),
4002 canceller,
4003 throw_on_dynamic_markup_insertion_counter: Cell::new(0),
4004 page_showing: Cell::new(false),
4005 salvageable: Cell::new(true),
4006 active_parser_was_aborted: Cell::new(false),
4007 fired_unload: Cell::new(false),
4008 responsive_images: Default::default(),
4009 navigation_timing: Default::default(),
4010 resource_fetch_timing: RefCell::new(None),
4011 completely_loaded: Cell::new(false),
4012 script_and_layout_blockers: Cell::new(0),
4013 delayed_tasks: Default::default(),
4014 shadow_roots: DomRefCell::new(HashSet::new()),
4015 shadow_roots_styles_changed: Cell::new(false),
4016 media_controls: DomRefCell::new(HashMap::new()),
4017 dirty_canvases: DomRefCell::new(Default::default()),
4018 has_pending_animated_image_update: Cell::new(false),
4019 selection: MutNullableDom::new(None),
4020 timeline: Dom::from_ref(timeline),
4021 animations: Animations::new(),
4022 image_animation_manager: DomRefCell::new(ImageAnimationManager::default()),
4023 dirty_root: Default::default(),
4024 declarative_refresh: Default::default(),
4025 resize_observers: Default::default(),
4026 fonts: Default::default(),
4027 visibility_state: Cell::new(DocumentVisibilityState::Hidden),
4028 status_code,
4029 is_initial_about_blank: Cell::new(is_initial_about_blank),
4030 allow_declarative_shadow_roots: Cell::new(allow_declarative_shadow_roots),
4031 inherited_insecure_requests_policy: Cell::new(inherited_insecure_requests_policy),
4032 has_trustworthy_ancestor_origin: Cell::new(has_trustworthy_ancestor_origin),
4033 intersection_observer_task_queued: Cell::new(false),
4034 intersection_observers: Default::default(),
4035 highlighted_dom_node: Default::default(),
4036 lcp_candidates: DomRefCell::new(Default::default()),
4037 adopted_stylesheets: Default::default(),
4038 adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
4039 pending_scroll_events: Default::default(),
4040 rendering_update_reasons: Default::default(),
4041 waiting_on_canvas_image_updates: Cell::new(false),
4042 root_removal_noted: Cell::new(true),
4043 current_rendering_epoch: Default::default(),
4044 custom_element_reaction_stack,
4045 active_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
4046 creation_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
4047 favicon: RefCell::new(None),
4048 websockets: DOMTracker::new(),
4049 details_name_groups: Default::default(),
4050 protocol_handler_automation_mode: Default::default(),
4051 layout_animations_test_enabled: pref!(layout_animations_test_enabled),
4052 state_override: Default::default(),
4053 value_override: Default::default(),
4054 default_single_line_container_name: Default::default(),
4055 css_styling_flag: Default::default(),
4056 accessibility_data: Default::default(),
4057 iframe_load_in_progress: Default::default(),
4058 mute_iframe_load: Default::default(),
4059 timers: OneshotTimers::new(window.upcast()),
4060 pipeline_id,
4061 task_manager: Rc::new(TaskManager::new(
4062 Some(window.event_loop_sender()),
4063 pipeline_id,
4064 None,
4065 )),
4066 image_cache,
4067 history: Default::default(),
4068 theme: Default::default(),
4069 window_detached: Default::default(),
4070 }
4071 }
4072
4073 pub(crate) fn detach_window(&self) {
4074 self.window_detached.set(true);
4075 }
4076
4077 pub(crate) fn window_detached(&self) -> bool {
4078 self.window_detached.get()
4079 }
4080
4081 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
4083 if let Some(csp_list) = self.get_csp_list().as_ref() {
4084 for policy in &csp_list.0 {
4085 if policy.contains_a_directive_whose_name_is("upgrade-insecure-requests") &&
4086 policy.disposition == PolicyDisposition::Enforce
4087 {
4088 return InsecureRequestsPolicy::Upgrade;
4089 }
4090 }
4091 }
4092
4093 self.inherited_insecure_requests_policy
4094 .get()
4095 .unwrap_or(InsecureRequestsPolicy::DoNotUpgrade)
4096 }
4097
4098 pub(crate) fn event_handler(&self) -> &DocumentEventHandler {
4100 &self.event_handler
4101 }
4102
4103 pub(crate) fn focus_handler(&self) -> &DocumentFocusHandler {
4105 &self.focus_handler
4106 }
4107
4108 pub(crate) fn embedder_controls(&self) -> &DocumentEmbedderControls {
4110 &self.embedder_controls
4111 }
4112
4113 fn has_pending_scroll_events(&self) -> bool {
4116 !self.pending_scroll_events.borrow().is_empty()
4117 }
4118
4119 pub(crate) fn add_rendering_update_reason(&self, reason: RenderingUpdateReason) {
4122 self.rendering_update_reasons
4123 .set(self.rendering_update_reasons.get().union(reason));
4124 }
4125
4126 pub(crate) fn clear_rendering_update_reasons(&self) {
4128 self.rendering_update_reasons
4129 .set(RenderingUpdateReason::empty())
4130 }
4131
4132 pub(crate) fn add_script_and_layout_blocker(&self) {
4139 self.script_and_layout_blockers
4140 .set(self.script_and_layout_blockers.get() + 1);
4141 }
4142
4143 pub(crate) fn remove_script_and_layout_blocker(&self, cx: &mut JSContext) {
4147 assert!(self.script_and_layout_blockers.get() > 0);
4148 self.script_and_layout_blockers
4149 .set(self.script_and_layout_blockers.get() - 1);
4150 while self.script_and_layout_blockers.get() == 0 && !self.delayed_tasks.borrow().is_empty()
4151 {
4152 let task = self.delayed_tasks.borrow_mut().remove(0);
4153 task.run_box(cx);
4154 }
4155 }
4156
4157 pub(crate) fn add_delayed_task<T: 'static + NonSendTaskBox>(&self, task: T) {
4159 self.delayed_tasks.borrow_mut().push(Box::new(task));
4160 }
4161
4162 pub(crate) fn is_safe_to_run_script_or_layout(&self) -> bool {
4165 self.script_and_layout_blockers.get() == 0
4166 }
4167
4168 pub(crate) fn ensure_safe_to_run_script_or_layout(&self) {
4171 assert!(
4172 self.is_safe_to_run_script_or_layout(),
4173 "Attempt to use script or layout while DOM not in a stable state"
4174 );
4175 }
4176
4177 #[allow(clippy::too_many_arguments)]
4178 pub(crate) fn new(
4179 cx: &mut JSContext,
4180 window: &Window,
4181 has_browsing_context: HasBrowsingContext,
4182 url: Option<ServoUrl>,
4183 about_base_url: Option<ServoUrl>,
4184 origin: MutableOrigin,
4185 doctype: IsHTMLDocument,
4186 content_type: Option<Mime>,
4187 last_modified: Option<String>,
4188 activity: DocumentActivity,
4189 doc_loader: DocumentLoader,
4190 referrer: Option<String>,
4191 status_code: Option<u16>,
4192 canceller: FetchCanceller,
4193 is_initial_about_blank: bool,
4194 allow_declarative_shadow_roots: bool,
4195 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4196 has_trustworthy_ancestor_origin: bool,
4197 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4198 creation_sandboxing_flag_set: SandboxingFlagSet,
4199 pipeline_id: PipelineId,
4200 image_cache: StdArc<dyn ImageCache>,
4201 ) -> DomRoot<Document> {
4202 Self::new_with_proto(
4203 cx,
4204 window,
4205 None,
4206 has_browsing_context,
4207 url,
4208 about_base_url,
4209 origin,
4210 doctype,
4211 content_type,
4212 last_modified,
4213 activity,
4214 doc_loader,
4215 referrer,
4216 status_code,
4217 canceller,
4218 is_initial_about_blank,
4219 allow_declarative_shadow_roots,
4220 inherited_insecure_requests_policy,
4221 has_trustworthy_ancestor_origin,
4222 custom_element_reaction_stack,
4223 creation_sandboxing_flag_set,
4224 pipeline_id,
4225 image_cache,
4226 )
4227 }
4228
4229 #[allow(clippy::too_many_arguments)]
4230 fn new_with_proto(
4231 cx: &mut JSContext,
4232 window: &Window,
4233 proto: Option<HandleObject>,
4234 has_browsing_context: HasBrowsingContext,
4235 url: Option<ServoUrl>,
4236 about_base_url: Option<ServoUrl>,
4237 origin: MutableOrigin,
4238 doctype: IsHTMLDocument,
4239 content_type: Option<Mime>,
4240 last_modified: Option<String>,
4241 activity: DocumentActivity,
4242 doc_loader: DocumentLoader,
4243 referrer: Option<String>,
4244 status_code: Option<u16>,
4245 canceller: FetchCanceller,
4246 is_initial_about_blank: bool,
4247 allow_declarative_shadow_roots: bool,
4248 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4249 has_trustworthy_ancestor_origin: bool,
4250 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4251 creation_sandboxing_flag_set: SandboxingFlagSet,
4252 pipeline_id: PipelineId,
4253 image_cache: StdArc<dyn ImageCache>,
4254 ) -> DomRoot<Document> {
4255 let timeline = DocumentTimeline::new(cx, window);
4256 let document = reflect_dom_object_with_proto(
4257 cx,
4258 Box::new(Document::new_inherited(
4259 window,
4260 has_browsing_context,
4261 url,
4262 about_base_url,
4263 origin,
4264 doctype,
4265 content_type,
4266 last_modified,
4267 activity,
4268 doc_loader,
4269 referrer,
4270 status_code,
4271 canceller,
4272 is_initial_about_blank,
4273 allow_declarative_shadow_roots,
4274 inherited_insecure_requests_policy,
4275 has_trustworthy_ancestor_origin,
4276 custom_element_reaction_stack,
4277 creation_sandboxing_flag_set,
4278 &timeline,
4279 pipeline_id,
4280 image_cache,
4281 )),
4282 window,
4283 proto,
4284 );
4285 {
4286 let node = document.upcast::<Node>();
4287 node.set_owner_doc(&document);
4288 }
4289 document
4290 }
4291
4292 pub(crate) fn get_redirect_count(&self) -> u16 {
4293 self.resource_fetch_timing()
4294 .as_ref()
4295 .map_or(0, |resource_fetch_timing| {
4296 resource_fetch_timing.redirect_count
4297 })
4298 }
4299
4300 pub(crate) fn set_resource_fetch_timing(&self, timing: ResourceFetchTiming) {
4301 self.resource_fetch_timing.replace(Some(timing));
4302 }
4303
4304 pub(crate) fn resource_fetch_timing(&self) -> Ref<'_, Option<ResourceFetchTiming>> {
4305 self.resource_fetch_timing.borrow()
4306 }
4307
4308 pub(crate) fn navigation_timing(&self) -> Rc<NavigationTiming> {
4309 self.navigation_timing.clone()
4310 }
4311
4312 pub(crate) fn performance_timing_attribute(
4313 &self,
4314 name: &str,
4315 ) -> Fallible<Option<CrossProcessInstant>> {
4316 Ok(match name {
4317 "unloadEventStart" => self.navigation_timing().unload_event_start.get(),
4318 "unloadEventEnd" => self.navigation_timing().unload_event_end.get(),
4319 "domInteractive" => self.navigation_timing().dom_interactive.get(),
4320 "domContentLoadedEventStart" => self
4321 .navigation_timing()
4322 .dom_content_loaded_event_start
4323 .get(),
4324 "domContentLoadedEventEnd" => {
4325 self.navigation_timing().dom_content_loaded_event_end.get()
4326 },
4327 "domComplete" => self.navigation_timing().dom_complete.get(),
4328 "loadEventStart" => self.navigation_timing().load_event_start.get(),
4329 "loadEventEnd" => self.navigation_timing().load_event_end.get(),
4330 "redirectStart" | "redirectEnd" | "secureConnectionStart" | "responseEnd" => self
4331 .resource_fetch_timing()
4332 .as_ref()
4333 .and_then(|resource_fetch_timing| match name {
4334 "redirectStart" => resource_fetch_timing.redirect_start,
4335 "redirectEnd" => resource_fetch_timing.redirect_end,
4336 "secureConnectionStart" => resource_fetch_timing.secure_connection_start,
4337 "responseEnd" => resource_fetch_timing.response_end,
4338 _ => None,
4339 }),
4340 _ => {
4341 return Err(Error::Operation(Some(format!(
4342 "{name} hasn't been implemented."
4343 ))));
4344 },
4345 })
4346 }
4347
4348 pub(crate) fn elements_by_name_count(&self, name: &DOMString) -> u32 {
4349 if name.is_empty() {
4350 return 0;
4351 }
4352 self.count_node_list(|n| Document::is_element_in_get_by_name(n, name))
4353 }
4354
4355 pub(crate) fn nth_element_by_name<'a>(
4356 &self,
4357 no_gc: &'a NoGC,
4358 index: u32,
4359 name: &DOMString,
4360 ) -> Option<UnrootedDom<'a, Node>> {
4361 if name.is_empty() {
4362 return None;
4363 }
4364 self.nth_in_node_list(no_gc, index, |n| {
4365 Document::is_element_in_get_by_name(n, name)
4366 })
4367 }
4368
4369 fn is_element_in_get_by_name(node: &Node, name: &DOMString) -> bool {
4372 let element = match node.downcast::<Element>() {
4373 Some(element) => element,
4374 None => return false,
4375 };
4376 if element.namespace() != &ns!(html) {
4377 return false;
4378 }
4379 element.get_name().is_some_and(|n| &*n == name)
4380 }
4381
4382 fn count_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> u32 {
4383 let doc = self.GetDocumentElement();
4384 let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
4385 maybe_node
4386 .iter()
4387 .flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
4388 .filter(|node| callback(node))
4389 .count() as u32
4390 }
4391
4392 fn nth_in_node_list<'a, F: Fn(&Node) -> bool>(
4393 &self,
4394 no_gc: &'a NoGC,
4395 index: u32,
4396 callback: F,
4397 ) -> Option<UnrootedDom<'a, Node>> {
4398 let doc = self.get_document_element_unrooted(no_gc)?;
4399 doc.upcast::<Node>()
4400 .traverse_preorder_non_rooting(no_gc, ShadowIncluding::No)
4401 .filter(|node| callback(node))
4402 .nth(index as usize)
4403 }
4404
4405 fn get_html_element(&self) -> Option<DomRoot<HTMLHtmlElement>> {
4406 self.GetDocumentElement().and_then(DomRoot::downcast)
4407 }
4408
4409 pub(crate) fn shared_style_locks(&self) -> &SharedRwLocks {
4411 &self.shared_style_locks
4412 }
4413
4414 pub(crate) fn style_shared_author_lock(&self) -> &SharedRwLock {
4416 &self.shared_style_locks.author
4417 }
4418
4419 pub(crate) fn flush_stylesheets_for_reflow(&self) -> bool {
4421 let mut stylesheets = self.stylesheets.borrow_mut();
4428 let have_changed = stylesheets.has_changed();
4429 stylesheets.flush_without_invalidation();
4430 have_changed
4431 }
4432
4433 pub(crate) fn salvageable(&self) -> bool {
4434 self.salvageable.get()
4435 }
4436
4437 pub(crate) fn make_document_unsalvageable(&self) {
4439 self.salvageable.set(false);
4445 }
4446
4447 pub(crate) fn appropriate_template_contents_owner_document(
4449 &self,
4450 cx: &mut JSContext,
4451 ) -> DomRoot<Document> {
4452 self.appropriate_template_contents_owner_document
4453 .or_init(|| {
4454 let doctype = if self.is_html_document {
4455 IsHTMLDocument::HTMLDocument
4456 } else {
4457 IsHTMLDocument::NonHTMLDocument
4458 };
4459 let new_doc = Document::new(
4460 cx,
4461 self.window(),
4462 HasBrowsingContext::No,
4463 None,
4464 None,
4465 MutableOrigin::new(ImmutableOrigin::new_opaque()),
4467 doctype,
4468 None,
4469 None,
4470 DocumentActivity::Inactive,
4471 DocumentLoader::new(&self.loader()),
4472 None,
4473 None,
4474 Default::default(),
4475 false,
4476 self.allow_declarative_shadow_roots(),
4477 Some(self.insecure_requests_policy()),
4478 self.has_trustworthy_ancestor_or_current_origin(),
4479 self.custom_element_reaction_stack.clone(),
4480 self.creation_sandboxing_flag_set(),
4481 self.pipeline_id(),
4482 self.image_cache.clone(),
4483 );
4484 new_doc
4485 .appropriate_template_contents_owner_document
4486 .set(Some(&new_doc));
4487 new_doc
4488 })
4489 }
4490
4491 pub(crate) fn get_element_by_id(&self, no_gc: &NoGC, id: &Atom) -> Option<DomRoot<Element>> {
4492 self.id_map.get(no_gc, self.upcast(), id)
4493 }
4494
4495 pub(crate) fn ensure_pending_restyle(&self, el: &Element) -> RefMut<'_, PendingRestyle> {
4496 let map = self.pending_restyles.borrow_mut();
4497 RefMut::map(map, |m| {
4498 &mut m
4499 .entry(Dom::from_ref(el))
4500 .or_insert_with(|| NoTrace(PendingRestyle::default()))
4501 .0
4502 })
4503 }
4504
4505 pub(crate) fn element_attr_will_change(&self, el: &Element, attr: AttrRef<'_>) {
4506 let mut entry = self.ensure_pending_restyle(el);
4512 if entry.snapshot.is_none() {
4513 entry.snapshot = Some(Snapshot::new());
4514 }
4515 if attr.local_name() == &local_name!("style") {
4516 entry.hint.insert(RestyleHint::RESTYLE_STYLE_ATTRIBUTE);
4517 }
4518
4519 if vtable_for(el.upcast()).attribute_affects_presentational_hints(attr) ||
4520 el.check_style_on_self_or_eager_pseudos(|style| {
4521 if let Some(ref attribute_references) = style.attribute_references {
4522 return attribute_references.contains_key(attr.local_name());
4523 }
4524 false
4525 })
4526 {
4527 entry.hint.insert(RestyleHint::RESTYLE_SELF);
4528 }
4529
4530 let snapshot = entry.snapshot.as_mut().unwrap();
4531 if attr.local_name() == &local_name!("id") {
4532 if snapshot.id_changed {
4533 return;
4534 }
4535 snapshot.id_changed = true;
4536 } else if attr.local_name() == &local_name!("class") {
4537 if snapshot.class_changed {
4538 return;
4539 }
4540 snapshot.class_changed = true;
4541 } else {
4542 snapshot.other_attributes_changed = true;
4543 }
4544 let local_name = style::LocalName::cast(attr.local_name());
4545 if !snapshot.changed_attrs.contains(local_name) {
4546 snapshot.changed_attrs.push(local_name.clone());
4547 }
4548 if snapshot.attrs.is_none() {
4549 let attrs = el
4550 .attrs()
4551 .borrow()
4552 .iter()
4553 .map(|attr| (attr.identifier().clone(), attr.value().clone()))
4554 .collect();
4555 snapshot.attrs = Some(attrs);
4556 }
4557 }
4558
4559 pub(crate) fn set_referrer_policy(&self, policy: ReferrerPolicy) {
4560 self.policy_container
4561 .borrow_mut()
4562 .set_referrer_policy(policy);
4563 }
4564
4565 pub(crate) fn get_referrer_policy(&self) -> ReferrerPolicy {
4566 self.policy_container.borrow().get_referrer_policy()
4567 }
4568
4569 pub(crate) fn set_target_element(&self, node: Option<&Element>) {
4570 if let Some(ref element) = self.target_element.get() {
4571 element.set_target_state(false);
4572 }
4573
4574 self.target_element.set(node);
4575
4576 if let Some(ref element) = self.target_element.get() {
4577 element.set_target_state(true);
4578 }
4579 }
4580
4581 pub(crate) fn incr_ignore_destructive_writes_counter(&self) {
4582 self.ignore_destructive_writes_counter
4583 .set(self.ignore_destructive_writes_counter.get() + 1);
4584 }
4585
4586 pub(crate) fn decr_ignore_destructive_writes_counter(&self) {
4587 self.ignore_destructive_writes_counter
4588 .set(self.ignore_destructive_writes_counter.get() - 1);
4589 }
4590
4591 pub(crate) fn is_prompting_or_unloading(&self) -> bool {
4592 self.ignore_opens_during_unload_counter.get() > 0
4593 }
4594
4595 fn incr_ignore_opens_during_unload_counter(&self) {
4596 self.ignore_opens_during_unload_counter
4597 .set(self.ignore_opens_during_unload_counter.get() + 1);
4598 }
4599
4600 fn decr_ignore_opens_during_unload_counter(&self) {
4601 self.ignore_opens_during_unload_counter
4602 .set(self.ignore_opens_during_unload_counter.get() - 1);
4603 }
4604
4605 pub(crate) fn set_fullscreen_element(&self, element: Option<&Element>) {
4606 self.fullscreen_element.set(element);
4607 }
4608
4609 fn reset_form_owner_for_listeners(&self, cx: &mut JSContext, id: &Atom) {
4610 let map = self.form_id_listener_map.borrow();
4611 if let Some(listeners) = map.get(id) {
4612 for listener in listeners {
4613 listener
4614 .as_maybe_form_control()
4615 .expect("Element must be a form control")
4616 .reset_form_owner(cx);
4617 }
4618 }
4619 }
4620
4621 pub(crate) fn register_shadow_root(&self, shadow_root: &ShadowRoot) {
4622 self.shadow_roots
4623 .borrow_mut()
4624 .insert(Dom::from_ref(shadow_root));
4625 self.invalidate_shadow_roots_stylesheets();
4626 }
4627
4628 pub(crate) fn unregister_shadow_root(&self, shadow_root: &ShadowRoot) {
4629 let mut shadow_roots = self.shadow_roots.borrow_mut();
4630 shadow_roots.remove(&Dom::from_ref(shadow_root));
4631 }
4632
4633 pub(crate) fn invalidate_shadow_roots_stylesheets(&self) {
4634 self.shadow_roots_styles_changed.set(true);
4635 }
4636
4637 pub(crate) fn flush_shadow_root_stylesheets_if_necessary_for_layout(
4638 &self,
4639 stylist: &mut Stylist,
4640 guard: &SharedRwLockReadGuard,
4641 ) {
4642 if !self.shadow_roots_styles_changed.get() {
4643 return;
4644 }
4645 #[expect(unsafe_code)]
4646 unsafe {
4647 for shadow_root in self.shadow_roots.borrow_for_layout().iter() {
4648 let layout: LayoutDom<'_, _> = shadow_root.to_layout();
4649 layout.flush_stylesheets_for_layout(stylist, guard);
4650 }
4651 }
4652 self.shadow_roots_styles_changed.set(false);
4653 }
4654
4655 pub(crate) fn stylesheet_count(&self) -> usize {
4656 self.stylesheets.borrow().len()
4657 }
4658
4659 pub(crate) fn stylesheet_at(
4660 &self,
4661 cx: &mut JSContext,
4662 index: usize,
4663 ) -> Option<DomRoot<CSSStyleSheet>> {
4664 let stylesheets = self.stylesheets.borrow();
4665
4666 stylesheets
4667 .get(Origin::Author, index)
4668 .and_then(|s| s.owner.get_cssom_object(cx))
4669 }
4670
4671 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) {
4678 let insertion_point = {
4679 let stylesheets = &mut *self.stylesheets.borrow_mut();
4680
4681 stylesheets
4683 .iter()
4684 .map(|(sheet, _origin)| sheet)
4685 .find(|sheet_in_doc| {
4686 match &sheet_in_doc.owner {
4687 StylesheetSource::Element(other_node) => {
4688 owner_node.upcast::<Node>().is_before(other_node.upcast())
4689 },
4690 StylesheetSource::Constructed(_) => true,
4693 }
4694 })
4695 .cloned()
4696 };
4697
4698 if self.has_browsing_context() {
4699 self.add_stylesheet_to_stylist(
4700 sheet.clone(),
4701 insertion_point.as_ref().map(|s| s.sheet.clone()),
4702 );
4703 }
4704
4705 let stylesheets = &mut *self.stylesheets.borrow_mut();
4706 DocumentOrShadowRoot::add_stylesheet(
4707 StylesheetSource::Element(Dom::from_ref(owner_node)),
4708 StylesheetSetRef::Document(stylesheets),
4709 sheet,
4710 insertion_point,
4711 self.style_shared_author_lock(),
4712 );
4713 }
4714
4715 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
4720 pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
4721 debug_assert!(cssom_stylesheet.is_constructed());
4722
4723 let sheet = cssom_stylesheet.style_stylesheet().clone();
4724 let insertion_point = {
4725 let stylesheets = &mut *self.stylesheets.borrow_mut();
4726
4727 stylesheets
4728 .iter()
4729 .last()
4730 .map(|(sheet, _origin)| sheet)
4731 .cloned()
4732 };
4733
4734 if self.has_browsing_context() {
4735 self.add_stylesheet_to_stylist(
4736 sheet.clone(),
4737 insertion_point.as_ref().map(|s| s.sheet.clone()),
4738 );
4739 }
4740
4741 let stylesheets = &mut *self.stylesheets.borrow_mut();
4742 DocumentOrShadowRoot::add_stylesheet(
4743 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
4744 StylesheetSetRef::Document(stylesheets),
4745 sheet,
4746 insertion_point,
4747 self.style_shared_author_lock(),
4748 );
4749 }
4750
4751 pub(crate) fn add_stylesheet_to_stylist(
4752 &self,
4753 stylesheet: Arc<Stylesheet>,
4754 before_stylesheet: Option<Arc<Stylesheet>>,
4755 ) {
4756 self.window
4757 .layout_mut()
4758 .add_stylesheet(stylesheet, before_stylesheet);
4759 }
4760
4761 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, stylesheet: &Arc<Stylesheet>) {
4764 if self.has_browsing_context() {
4765 self.window
4766 .layout_mut()
4767 .remove_stylesheet(stylesheet.clone());
4768 }
4769
4770 DocumentOrShadowRoot::remove_stylesheet(
4771 owner,
4772 stylesheet,
4773 StylesheetSetRef::Document(&mut *self.stylesheets.borrow_mut()),
4774 )
4775 }
4776
4777 pub(crate) fn get_elements_with_id(
4778 &self,
4779 cx: &mut JSContext,
4780 id: &Atom,
4781 ) -> Ref<'_, [Dom<Element>]> {
4782 self.id_map.get_all(cx.no_gc(), self.upcast(), id)
4783 }
4784
4785 pub(crate) fn get_elements_with_name(
4786 &self,
4787 cx: &mut JSContext,
4788 name: &Atom,
4789 ) -> Ref<'_, [Dom<Element>]> {
4790 self.name_map.get_all(cx.no_gc(), self.upcast(), name)
4791 }
4792
4793 pub(crate) fn drain_pending_restyles(
4794 &self,
4795 no_gc: &NoGC,
4796 ) -> Vec<(TrustedNodeAddress, PendingRestyle)> {
4797 self.pending_restyles
4798 .borrow_mut()
4799 .drain()
4800 .filter_map(|(element, restyle)| {
4801 let node = element.upcast::<Node>();
4802 if !node.get_flag(NodeFlags::IS_CONNECTED) {
4803 return None;
4804 }
4805 element.note_dirty_descendants(no_gc);
4806 Some((node.to_trusted_node_address(), restyle.0))
4807 })
4808 .collect()
4809 }
4810
4811 pub(crate) fn advance_animation_timeline_for_testing(&self, delta: TimeDuration) {
4812 self.timeline.advance_specific(delta);
4813 let current_timeline_value = self.current_animation_timeline_value();
4814 self.animations
4815 .update_for_new_timeline_value(&self.window, current_timeline_value);
4816 }
4817
4818 pub(crate) fn maybe_mark_animating_nodes_as_dirty(&self, no_gc: &NoGC) {
4819 let current_timeline_value = self.current_animation_timeline_value();
4820 self.animations
4821 .mark_animating_nodes_as_dirty(no_gc, current_timeline_value);
4822 }
4823
4824 pub(crate) fn current_animation_timeline_value(&self) -> f64 {
4825 self.timeline
4826 .upcast::<AnimationTimeline>()
4827 .current_time_in_seconds()
4828 }
4829
4830 pub(crate) fn animations(&self) -> &Animations {
4831 &self.animations
4832 }
4833
4834 pub(crate) fn update_animations_post_reflow(&self) {
4835 let current_timeline_value = self.current_animation_timeline_value();
4836 self.animations
4837 .do_post_reflow_update(&self.window, current_timeline_value);
4838 self.image_animation_manager
4839 .borrow_mut()
4840 .do_post_reflow_update(&self.window, current_timeline_value);
4841 }
4842
4843 pub(crate) fn cancel_animations_for_node(&self, node: &Node) {
4844 self.animations.cancel_animations_for_node(node);
4845 self.image_animation_manager
4846 .borrow_mut()
4847 .cancel_animations_for_node(node);
4848 }
4849
4850 pub(crate) fn remove_style_and_layout_data_from_subtree(
4854 &self,
4855 no_gc: &NoGC,
4856 subtree_root: &Node,
4857 ) {
4858 for node in subtree_root.traverse_preorder_non_rooting(no_gc, ShadowIncluding::Yes) {
4859 self.clean_up_style_and_layout_data_for_node(&node);
4860 }
4861 }
4862
4863 pub(crate) fn clean_up_style_and_layout_data_for_node(&self, node: &Node) {
4864 node.clear_layout_data();
4865 if let Some(element) = node.downcast::<Element>() {
4866 element.clean_up_style_data();
4867
4868 if self.dirty_root == Some(element) {
4873 self.dirty_root.clear();
4874 }
4875 }
4876
4877 node.set_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION, false);
4878 node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false);
4879 }
4880
4881 pub(crate) fn update_animations_and_send_events(&self, cx: &mut CurrentRealm) {
4883 if !self.layout_animations_test_enabled {
4885 self.timeline.update(self.window());
4886 }
4887
4888 let current_timeline_value = self.current_animation_timeline_value();
4895 self.animations
4896 .update_for_new_timeline_value(&self.window, current_timeline_value);
4897 self.maybe_mark_animating_nodes_as_dirty(cx.no_gc());
4898
4899 self.window().perform_a_microtask_checkpoint(cx);
4901
4902 self.animations().send_pending_events(self.window(), cx);
4904 }
4905
4906 pub(crate) fn image_animation_manager(&self) -> Ref<'_, ImageAnimationManager> {
4907 self.image_animation_manager.borrow()
4908 }
4909
4910 pub(crate) fn set_has_pending_animated_image_update(&self) {
4911 self.has_pending_animated_image_update.set(true);
4912 }
4913
4914 pub(crate) fn shared_declarative_refresh_steps(&self, content: &[u8], from_meta_element: bool) {
4916 if self.will_declaratively_refresh() {
4918 return;
4919 }
4920
4921 static REFRESH_REGEX: LazyLock<Regex> = LazyLock::new(|| {
4923 Regex::new(
4927 r#"(?xs)
4928 ^
4929 \s* # 3
4930 ((?<time>[0-9]+)|\.) # 5-6
4931 [0-9.]* # 8
4932 (
4933 (
4934 (\s*;|\s*,|\s) # 10.3
4935 \s* # 10.4
4936 )
4937 (
4938 (
4939 (U|u)(R|r)(L|l) # 11.2-11.4
4940 \s*=\s* # 11.5-11.7
4941 )?
4942 ('(?<url1>[^']*)'(?s-u:.)*|"(?<url2>[^"]*)"(?s-u:.)*|['"]?(?<url3>(?s-u:.)*)) # 11.8 - 11.10
4943 |
4944 (?<url4>(?s-u:.)*)
4945 )
4946 )?
4947 $
4948 "#,
4949 )
4950 .unwrap()
4951 });
4952
4953 let mut url_record = self.url();
4955 let captures = if let Some(captures) = REFRESH_REGEX.captures(content) {
4956 captures
4957 } else {
4958 return;
4959 };
4960 let time = if let Some(time_string) = captures.name("time") {
4961 u64::from_str(&String::from_utf8_lossy(time_string.as_bytes())).unwrap_or(0)
4962 } else {
4963 0
4964 };
4965 let captured_url = captures.name("url1").or(captures
4966 .name("url2")
4967 .or(captures.name("url3").or(captures.name("url4"))));
4968
4969 if let Some(url_match) = captured_url {
4971 url_record = if let Ok(url) = ServoUrl::parse_with_base(
4972 Some(&url_record),
4973 &String::from_utf8_lossy(url_match.as_bytes()),
4974 ) {
4975 info!("Refresh to {}", url.debug_compact());
4976 url
4977 } else {
4978 return;
4980 };
4981 if url_record.scheme() == "javascript" {
4983 return;
4984 }
4985 }
4986 if self.completely_loaded() {
4988 self.window.as_global_scope().schedule_callback(
4989 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
4990 url: url_record,
4991 from_meta_element,
4992 }),
4993 Duration::from_secs(time),
4994 );
4995 self.set_declarative_refresh(DeclarativeRefresh::CreatedAfterLoad);
4996 } else {
4997 self.set_declarative_refresh(DeclarativeRefresh::PendingLoad {
4998 url: url_record,
4999 time,
5000 from_meta_element,
5001 });
5002 }
5003 }
5004
5005 pub(crate) fn will_declaratively_refresh(&self) -> bool {
5006 self.declarative_refresh.borrow().is_some()
5007 }
5008 pub(crate) fn set_declarative_refresh(&self, refresh: DeclarativeRefresh) {
5009 *self.declarative_refresh.borrow_mut() = Some(refresh);
5010 }
5011
5012 fn update_visibility_state(
5014 &self,
5015 cx: &mut JSContext,
5016 visibility_state: DocumentVisibilityState,
5017 ) {
5018 if self.visibility_state.get() == visibility_state {
5020 return;
5021 }
5022 self.visibility_state.set(visibility_state);
5024 let entry = VisibilityStateEntry::new(
5027 cx,
5028 &self.global(),
5029 visibility_state,
5030 CrossProcessInstant::now(),
5031 );
5032 self.window
5033 .Performance(cx)
5034 .queue_entry(entry.upcast::<PerformanceEntry>());
5035
5036 #[cfg(feature = "gamepad")]
5047 if visibility_state == DocumentVisibilityState::Hidden {
5048 self.window
5049 .Navigator(cx)
5050 .GetGamepads(cx)
5051 .unwrap_or_default()
5052 .iter_mut()
5053 .for_each(|gamepad| {
5054 if let Some(g) = gamepad {
5055 g.vibration_actuator().handle_visibility_change();
5056 }
5057 });
5058 }
5059
5060 self.upcast::<EventTarget>()
5062 .fire_bubbling_event(cx, atom!("visibilitychange"));
5063 }
5064
5065 pub(crate) fn is_initial_about_blank(&self) -> bool {
5067 self.is_initial_about_blank.get()
5068 }
5069
5070 pub(crate) fn allow_declarative_shadow_roots(&self) -> bool {
5072 self.allow_declarative_shadow_roots.get()
5073 }
5074
5075 pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
5076 self.has_trustworthy_ancestor_origin.get()
5077 }
5078
5079 pub(crate) fn has_trustworthy_ancestor_or_current_origin(&self) -> bool {
5080 self.has_trustworthy_ancestor_origin.get() ||
5081 self.origin().immutable().is_potentially_trustworthy()
5082 }
5083
5084 pub(crate) fn highlight_dom_node(&self, node: Option<&Node>) {
5085 self.highlighted_dom_node.set(node);
5086 self.add_restyle_reason(RestyleReason::HighlightedDOMNodeChanged);
5087 }
5088
5089 pub(crate) fn highlighted_dom_node(&self) -> Option<DomRoot<Node>> {
5090 self.highlighted_dom_node.get()
5091 }
5092
5093 pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
5094 self.custom_element_reaction_stack.clone()
5095 }
5096
5097 pub(crate) fn active_sandboxing_flag_set(&self) -> SandboxingFlagSet {
5098 self.active_sandboxing_flag_set.get()
5099 }
5100
5101 pub(crate) fn has_active_sandboxing_flag(&self, flag: SandboxingFlagSet) -> bool {
5102 self.active_sandboxing_flag_set.get().contains(flag)
5103 }
5104
5105 pub(crate) fn set_active_sandboxing_flag_set(&self, flags: SandboxingFlagSet) {
5106 self.active_sandboxing_flag_set.set(flags)
5107 }
5108
5109 pub(crate) fn creation_sandboxing_flag_set(&self) -> SandboxingFlagSet {
5110 self.creation_sandboxing_flag_set.get()
5111 }
5112
5113 pub(crate) fn creation_sandboxing_flag_set_considering_parent_iframe(
5114 &self,
5115 ) -> SandboxingFlagSet {
5116 self.window()
5117 .window_proxy()
5118 .frame_element()
5119 .and_then(|element| element.downcast::<HTMLIFrameElement>())
5120 .map(HTMLIFrameElement::sandboxing_flag_set)
5121 .unwrap_or_else(|| self.creation_sandboxing_flag_set())
5122 }
5123
5124 pub(crate) fn viewport_scrolling_box(&self, flags: ScrollContainerQueryFlags) -> ScrollingBox {
5125 self.window()
5126 .scrolling_box_query(None, flags)
5127 .expect("We should always have a ScrollingBox for the Viewport")
5128 }
5129
5130 pub(crate) fn notify_embedder_favicon(&self) {
5131 if let Some(ref image) = *self.favicon.borrow() {
5132 self.send_to_embedder(EmbedderMsg::NewFavicon(self.webview_id(), image.clone()));
5133 }
5134 }
5135
5136 pub(crate) fn set_favicon(&self, favicon: Image) {
5137 *self.favicon.borrow_mut() = Some(favicon);
5138 self.notify_embedder_favicon();
5139 }
5140
5141 pub(crate) fn fullscreen_element(&self) -> Option<DomRoot<Element>> {
5142 self.fullscreen_element.get()
5143 }
5144
5145 pub(crate) fn state_override(&self, command_name: &CommandName) -> Option<bool> {
5147 self.state_override.borrow().get(command_name).copied()
5148 }
5149
5150 pub(crate) fn set_state_override(&self, command_name: CommandName, state: Option<bool>) {
5152 if let Some(state) = state {
5153 self.state_override.borrow_mut().insert(command_name, state);
5154 } else {
5155 self.value_override.borrow_mut().remove(&command_name);
5156 }
5157 }
5158
5159 pub(crate) fn value_override(&self, command_name: &CommandName) -> Option<DOMString> {
5161 self.value_override.borrow().get(command_name).cloned()
5162 }
5163
5164 pub(crate) fn set_value_override(&self, command_name: CommandName, value: Option<DOMString>) {
5166 if let Some(value) = value {
5167 self.value_override.borrow_mut().insert(command_name, value);
5168 } else {
5169 self.value_override.borrow_mut().remove(&command_name);
5170 }
5171 }
5172
5173 pub(crate) fn clear_command_overrides(&self) {
5176 self.state_override.borrow_mut().clear();
5177 self.value_override.borrow_mut().clear();
5178 }
5179
5180 pub(crate) fn default_single_line_container_name(&self) -> DefaultSingleLineContainerName {
5182 self.default_single_line_container_name.get()
5183 }
5184
5185 pub(crate) fn set_default_single_line_container_name(
5187 &self,
5188 value: DefaultSingleLineContainerName,
5189 ) {
5190 self.default_single_line_container_name.set(value)
5191 }
5192
5193 pub(crate) fn css_styling_flag(&self) -> bool {
5195 self.css_styling_flag.get()
5196 }
5197
5198 pub(crate) fn set_css_styling_flag(&self, value: bool) {
5200 self.css_styling_flag.set(value)
5201 }
5202
5203 pub(crate) fn mute_iframe_load_flag(&self) -> bool {
5204 self.mute_iframe_load.get()
5205 }
5206
5207 pub(crate) fn set_iframe_load_in_progress(&self, value: bool) {
5208 self.iframe_load_in_progress.set(value)
5209 }
5210
5211 pub(crate) fn theme(&self) -> Option<Theme> {
5212 self.theme.get()
5213 }
5214
5215 pub(crate) fn set_theme(&self, new_theme: Option<Theme>) {
5216 self.theme.set(new_theme);
5217 self.window.refresh_theme();
5218 }
5219}
5220
5221impl DocumentMethods<crate::DomTypeHolder> for Document {
5222 fn Constructor(
5224 cx: &mut JSContext,
5225 window: &Window,
5226 proto: Option<HandleObject>,
5227 ) -> Fallible<DomRoot<Document>> {
5228 let doc = window.Document();
5230 let docloader = DocumentLoader::new(&doc.loader());
5231 Ok(Document::new_with_proto(
5232 cx,
5233 window,
5234 proto,
5235 HasBrowsingContext::No,
5236 None,
5237 None,
5238 doc.origin().clone(),
5239 IsHTMLDocument::NonHTMLDocument,
5240 None,
5241 None,
5242 DocumentActivity::Inactive,
5243 docloader,
5244 None,
5245 None,
5246 Default::default(),
5247 false,
5248 doc.allow_declarative_shadow_roots(),
5249 Some(doc.insecure_requests_policy()),
5250 doc.has_trustworthy_ancestor_or_current_origin(),
5251 doc.custom_element_reaction_stack(),
5252 doc.active_sandboxing_flag_set.get(),
5253 doc.pipeline_id(),
5254 doc.image_cache(),
5255 ))
5256 }
5257
5258 fn ParseHTMLUnsafe(
5260 cx: &mut JSContext,
5261 window: &Window,
5262 s: TrustedHTMLOrString,
5263 options: &SetHTMLUnsafeOptions,
5264 ) -> Fallible<DomRoot<Self>> {
5265 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
5269 cx,
5270 window.as_global_scope(),
5271 s,
5272 "Document parseHTMLUnsafe",
5273 )?;
5274
5275 let url = window.get_url();
5276 let doc = window.Document();
5277 let loader = DocumentLoader::new(&doc.loader());
5278
5279 let content_type = "text/html"
5280 .parse()
5281 .expect("Supported type is not a MIME type");
5282 let document = Document::new(
5285 cx,
5286 window,
5287 HasBrowsingContext::No,
5288 Some(ServoUrl::parse("about:blank").unwrap()),
5289 None,
5290 doc.origin().clone(),
5291 IsHTMLDocument::HTMLDocument,
5292 Some(content_type),
5293 None,
5294 DocumentActivity::Inactive,
5295 loader,
5296 None,
5297 None,
5298 Default::default(),
5299 false,
5300 true,
5301 Some(doc.insecure_requests_policy()),
5302 doc.has_trustworthy_ancestor_or_current_origin(),
5303 doc.custom_element_reaction_stack(),
5304 doc.creation_sandboxing_flag_set(),
5305 doc.pipeline_id(),
5306 doc.image_cache(),
5307 );
5308 ServoParser::parse_html_document(cx, &document, Some(compliant_html), url, None, None);
5310
5311 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, false)?;
5314
5315 sanitizer.sanitize(cx, document.upcast(), false)?;
5317
5318 document.update_the_current_document_readiness(cx, DocumentReadyState::Complete);
5320 Ok(document)
5321 }
5322
5323 fn ParseHTML(
5325 cx: &mut JSContext,
5326 window: &Window,
5327 html: DOMString,
5328 options: &SetHTMLOptions,
5329 ) -> Fallible<DomRoot<Document>> {
5330 let url = window.get_url();
5333 let doc = window.Document();
5334 let loader = DocumentLoader::new(&doc.loader());
5335 let content_type = "text/html"
5336 .parse()
5337 .expect("Supported type is not a MIME type");
5338 let document = Document::new(
5339 cx,
5340 window,
5341 HasBrowsingContext::No,
5342 Some(ServoUrl::parse("about:blank").unwrap()),
5343 None,
5344 doc.origin().clone(),
5345 IsHTMLDocument::HTMLDocument,
5346 Some(content_type),
5347 None,
5348 DocumentActivity::Inactive,
5349 loader,
5350 None,
5351 None,
5352 Default::default(),
5353 false,
5354 true,
5355 Some(doc.insecure_requests_policy()),
5356 doc.has_trustworthy_ancestor_or_current_origin(),
5357 doc.custom_element_reaction_stack(),
5358 doc.creation_sandboxing_flag_set(),
5359 doc.pipeline_id(),
5360 doc.image_cache(),
5361 );
5362
5363 ServoParser::parse_html_document(cx, &document, Some(html), url, None, None);
5365
5366 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, true)?;
5369
5370 sanitizer.sanitize(cx, document.upcast(), true)?;
5372
5373 Ok(document)
5375 }
5376
5377 fn StyleSheets(&self, cx: &mut JSContext) -> DomRoot<StyleSheetList> {
5379 self.stylesheet_list.or_init(|| {
5380 StyleSheetList::new(
5381 cx,
5382 &self.window,
5383 StyleSheetListOwner::Document(Dom::from_ref(self)),
5384 )
5385 })
5386 }
5387
5388 fn Implementation(&self, cx: &mut JSContext) -> DomRoot<DOMImplementation> {
5390 self.implementation
5391 .or_init(|| DOMImplementation::new(cx, self))
5392 }
5393
5394 fn URL(&self) -> USVString {
5396 USVString(String::from(self.url().as_str()))
5397 }
5398
5399 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
5401 self.document_or_shadow_root.active_element(self.upcast())
5402 }
5403
5404 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
5406 self.custom_element_registry()
5407 }
5408
5409 fn HasFocus(&self) -> bool {
5411 if self.window().parent_info().is_none() {
5433 self.is_fully_active()
5435 } else {
5436 self.is_fully_active() && self.focus_handler.has_focus()
5438 }
5439 }
5440
5441 fn Domain(&self) -> DOMString {
5443 match self.origin().effective_domain() {
5445 None => DOMString::new(),
5447 Some(Host::Domain(domain)) => DOMString::from(domain),
5449 Some(host) => DOMString::from(host.to_string()),
5450 }
5451 }
5452
5453 fn SetDomain(&self, value: DOMString) -> ErrorResult {
5455 if !self.has_browsing_context {
5457 return Err(Error::Security(None));
5458 }
5459
5460 if self.has_active_sandboxing_flag(
5463 SandboxingFlagSet::SANDBOXED_DOCUMENT_DOMAIN_BROWSING_CONTEXT_FLAG,
5464 ) {
5465 return Err(Error::Security(None));
5466 }
5467
5468 let effective_domain = match self.origin().effective_domain() {
5470 Some(effective_domain) => effective_domain,
5471 None => return Err(Error::Security(None)),
5473 };
5474
5475 let host =
5477 match get_registrable_domain_suffix_of_or_is_equal_to(&value.str(), effective_domain) {
5478 None => return Err(Error::Security(None)),
5479 Some(host) => host,
5480 };
5481
5482 self.origin().set_domain(host);
5487
5488 Ok(())
5489 }
5490
5491 fn Referrer(&self) -> DOMString {
5493 match self.referrer {
5494 Some(ref referrer) => DOMString::from(referrer.to_string()),
5495 None => DOMString::new(),
5496 }
5497 }
5498
5499 fn DocumentURI(&self) -> USVString {
5501 self.URL()
5502 }
5503
5504 fn CompatMode(&self) -> DOMString {
5506 DOMString::from(match self.quirks_mode.get() {
5507 QuirksMode::LimitedQuirks | QuirksMode::NoQuirks => "CSS1Compat",
5508 QuirksMode::Quirks => "BackCompat",
5509 })
5510 }
5511
5512 fn CharacterSet(&self) -> DOMString {
5514 DOMString::from_static(self.encoding.get().name())
5515 }
5516
5517 fn Charset(&self) -> DOMString {
5519 self.CharacterSet()
5520 }
5521
5522 fn InputEncoding(&self) -> DOMString {
5524 self.CharacterSet()
5525 }
5526
5527 fn ContentType(&self) -> DOMString {
5529 DOMString::from(self.content_type.to_string())
5530 }
5531
5532 fn GetDoctype(&self) -> Option<DomRoot<DocumentType>> {
5534 self.upcast::<Node>().children().find_map(DomRoot::downcast)
5535 }
5536
5537 fn GetDocumentElement(&self) -> Option<DomRoot<Element>> {
5539 self.upcast::<Node>().child_elements().next()
5540 }
5541
5542 fn GetElementsByTagName(
5544 &self,
5545 cx: &mut JSContext,
5546 qualified_name: DOMString,
5547 ) -> DomRoot<HTMLCollection> {
5548 let qualified_name = LocalName::from(qualified_name);
5549 if let Some(entry) = self.tag_map.borrow_mut().get(&qualified_name) {
5550 return DomRoot::from_ref(entry);
5551 }
5552 let result = HTMLCollection::by_qualified_name(
5553 cx,
5554 &self.window,
5555 self.upcast(),
5556 qualified_name.clone(),
5557 );
5558 self.tag_map
5559 .borrow_mut()
5560 .insert(qualified_name, Dom::from_ref(&*result));
5561 result
5562 }
5563
5564 fn GetElementsByTagNameNS(
5566 &self,
5567 cx: &mut JSContext,
5568 maybe_ns: Option<DOMString>,
5569 tag_name: DOMString,
5570 ) -> DomRoot<HTMLCollection> {
5571 let ns = namespace_from_domstring(maybe_ns);
5572 let local = LocalName::from(tag_name);
5573 let qname = QualName::new(None, ns, local);
5574 if let Some(collection) = self.tagns_map.borrow().get(&qname) {
5575 return DomRoot::from_ref(collection);
5576 }
5577 let result =
5578 HTMLCollection::by_qual_tag_name(cx, &self.window, self.upcast(), qname.clone());
5579 self.tagns_map
5580 .borrow_mut()
5581 .insert(qname, Dom::from_ref(&*result));
5582 result
5583 }
5584
5585 fn GetElementsByClassName(
5587 &self,
5588 cx: &mut JSContext,
5589 classes: DOMString,
5590 ) -> DomRoot<HTMLCollection> {
5591 let class_atoms: Vec<Atom> = split_html_space_chars(&classes.str())
5592 .map(Atom::from)
5593 .collect();
5594 if let Some(collection) = self.classes_map.borrow().get(&class_atoms) {
5595 return DomRoot::from_ref(collection);
5596 }
5597 let result = HTMLCollection::by_atomic_class_name(
5598 cx,
5599 &self.window,
5600 self.upcast(),
5601 class_atoms.clone(),
5602 );
5603 self.classes_map
5604 .borrow_mut()
5605 .insert(class_atoms, Dom::from_ref(&*result));
5606 result
5607 }
5608
5609 fn GetElementById(
5611 &self,
5612 cx: &js::context::JSContext,
5613 id: DOMString,
5614 ) -> Option<DomRoot<Element>> {
5615 self.get_element_by_id(cx, &Atom::from(id))
5616 }
5617
5618 fn CreateElement(
5620 &self,
5621 cx: &mut JSContext,
5622 mut local_name: DOMString,
5623 options: StringOrElementCreationOptions,
5624 ) -> Fallible<DomRoot<Element>> {
5625 if !is_valid_element_local_name(&local_name.str()) {
5628 debug!("Not a valid element name");
5629 return Err(Error::InvalidCharacter(None));
5630 }
5631
5632 if self.is_html_document {
5633 local_name.make_ascii_lowercase();
5634 }
5635
5636 let ns = if self.is_html_document || self.is_xhtml_document() {
5637 ns!(html)
5638 } else {
5639 ns!()
5640 };
5641
5642 let name = QualName::new(None, ns, LocalName::from(local_name));
5643 let is = match options {
5644 StringOrElementCreationOptions::String(_) => None,
5645 StringOrElementCreationOptions::ElementCreationOptions(options) => {
5646 options.is.as_ref().map(LocalName::from)
5647 },
5648 };
5649 Ok(Element::create(
5650 cx,
5651 name,
5652 is,
5653 self,
5654 ElementCreator::ScriptCreated,
5655 CustomElementCreationMode::Synchronous,
5656 None,
5657 ))
5658 }
5659
5660 fn CreateElementNS(
5662 &self,
5663 cx: &mut JSContext,
5664 namespace: Option<DOMString>,
5665 qualified_name: DOMString,
5666 options: StringOrElementCreationOptions,
5667 ) -> Fallible<DomRoot<Element>> {
5668 let context = domname::Context::Element;
5671 let (namespace, prefix, local_name) =
5672 domname::validate_and_extract(namespace, &qualified_name, context)?;
5673
5674 let name = QualName::new(prefix, namespace, local_name);
5677 let is = match options {
5678 StringOrElementCreationOptions::String(_) => None,
5679 StringOrElementCreationOptions::ElementCreationOptions(options) => {
5680 options.is.as_ref().map(LocalName::from)
5681 },
5682 };
5683
5684 Ok(Element::create(
5686 cx,
5687 name,
5688 is,
5689 self,
5690 ElementCreator::ScriptCreated,
5691 CustomElementCreationMode::Synchronous,
5692 None,
5693 ))
5694 }
5695
5696 fn CreateAttribute(
5698 &self,
5699 cx: &mut JSContext,
5700 mut local_name: DOMString,
5701 ) -> Fallible<DomRoot<Attr>> {
5702 if !is_valid_attribute_local_name(&local_name.str()) {
5705 debug!("Not a valid attribute name");
5706 return Err(Error::InvalidCharacter(None));
5707 }
5708 if self.is_html_document {
5709 local_name.make_ascii_lowercase();
5710 }
5711 let name = LocalName::from(local_name);
5712 let value = AttrValue::String("".to_owned());
5713
5714 Ok(Attr::new(
5715 cx,
5716 self,
5717 name.clone(),
5718 value,
5719 name,
5720 ns!(),
5721 None,
5722 None,
5723 ))
5724 }
5725
5726 fn CreateAttributeNS(
5728 &self,
5729 cx: &mut JSContext,
5730 namespace: Option<DOMString>,
5731 qualified_name: DOMString,
5732 ) -> Fallible<DomRoot<Attr>> {
5733 let context = domname::Context::Attribute;
5736 let (namespace, prefix, local_name) =
5737 domname::validate_and_extract(namespace, &qualified_name, context)?;
5738 let value = AttrValue::String("".to_owned());
5739 let qualified_name = LocalName::from(qualified_name);
5740 Ok(Attr::new(
5741 cx,
5742 self,
5743 local_name,
5744 value,
5745 qualified_name,
5746 namespace,
5747 prefix,
5748 None,
5749 ))
5750 }
5751
5752 fn CreateDocumentFragment(&self, cx: &mut JSContext) -> DomRoot<DocumentFragment> {
5754 DocumentFragment::new(cx, self)
5755 }
5756
5757 fn CreateTextNode(&self, cx: &mut JSContext, data: DOMString) -> DomRoot<Text> {
5759 Text::new(cx, data, self)
5760 }
5761
5762 fn CreateCDATASection(
5764 &self,
5765 cx: &mut JSContext,
5766 data: DOMString,
5767 ) -> Fallible<DomRoot<CDATASection>> {
5768 if self.is_html_document {
5770 return Err(Error::NotSupported(None));
5771 }
5772
5773 if data.contains("]]>") {
5775 return Err(Error::InvalidCharacter(None));
5776 }
5777
5778 Ok(CDATASection::new(cx, data, self))
5780 }
5781
5782 fn CreateComment(&self, cx: &mut JSContext, data: DOMString) -> DomRoot<Comment> {
5784 Comment::new(cx, data, self, None)
5785 }
5786
5787 fn CreateProcessingInstruction(
5789 &self,
5790 cx: &mut JSContext,
5791 target: DOMString,
5792 data: DOMString,
5793 ) -> Fallible<DomRoot<ProcessingInstruction>> {
5794 if !matches_name_production(&target.str()) {
5796 return Err(Error::InvalidCharacter(None));
5797 }
5798
5799 if data.contains("?>") {
5801 return Err(Error::InvalidCharacter(None));
5802 }
5803
5804 Ok(ProcessingInstruction::new(cx, target, data, self))
5806 }
5807
5808 fn ImportNode(
5810 &self,
5811 cx: &mut JSContext,
5812 node: &Node,
5813 options: BooleanOrImportNodeOptions,
5814 ) -> Fallible<DomRoot<Node>> {
5815 if node.is::<Document>() || node.is::<ShadowRoot>() {
5817 return Err(Error::NotSupported(None));
5818 }
5819 let (subtree, registry) = match options {
5821 BooleanOrImportNodeOptions::Boolean(boolean) => (boolean.into(), None),
5824 BooleanOrImportNodeOptions::ImportNodeOptions(options) => {
5826 let subtree = (!options.selfOnly).into();
5828 let registry = if let Some(registry) = options.customElementRegistry {
5830 let this_registry = self
5833 .custom_element_registry()
5834 .expect("Document must have a custom element registry");
5835 if !registry.is_scoped() && registry != this_registry {
5836 return Err(Error::NotSupported(Some(
5837 "Imported customElementRegistry is not scoped and does not match existing registry.".into()
5838 )));
5839 }
5840 Some(registry)
5841 } else {
5842 None
5843 };
5844 (subtree, registry)
5845 },
5846 };
5847 let registry = registry
5850 .or_else(|| CustomElementRegistry::lookup_a_custom_element_registry(self.upcast()));
5851
5852 Ok(Node::clone(cx, node, Some(self), subtree, registry))
5855 }
5856
5857 fn AdoptNode(&self, cx: &mut JSContext, node: &Node) -> Fallible<DomRoot<Node>> {
5859 if node.is::<Document>() {
5861 return Err(Error::NotSupported(None));
5862 }
5863
5864 if node.is::<ShadowRoot>() {
5866 return Err(Error::HierarchyRequest(None));
5867 }
5868
5869 Node::adopt(cx, node, self);
5871
5872 Ok(DomRoot::from_ref(node))
5874 }
5875
5876 fn CreateEvent(
5878 &self,
5879 cx: &mut JSContext,
5880 mut interface: DOMString,
5881 ) -> Fallible<DomRoot<Event>> {
5882 interface.make_ascii_lowercase();
5883 match &*interface.str() {
5884 "beforeunloadevent" => Ok(DomRoot::upcast(BeforeUnloadEvent::new_uninitialized(
5885 cx,
5886 &self.window,
5887 ))),
5888 "compositionevent" => Ok(DomRoot::upcast(CompositionEvent::new_uninitialized(
5889 cx,
5890 &self.window,
5891 ))),
5892 "customevent" => Ok(DomRoot::upcast(CustomEvent::new_uninitialized(
5893 cx,
5894 self.window.upcast(),
5895 ))),
5896 "events" | "event" | "htmlevents" | "svgevents" => {
5899 Ok(Event::new_uninitialized(cx, self.window.upcast()))
5900 },
5901 "focusevent" => Ok(DomRoot::upcast(FocusEvent::new_uninitialized(
5902 cx,
5903 &self.window,
5904 ))),
5905 "hashchangeevent" => Ok(DomRoot::upcast(HashChangeEvent::new_uninitialized(
5906 cx,
5907 &self.window,
5908 ))),
5909 "keyboardevent" => Ok(DomRoot::upcast(KeyboardEvent::new_uninitialized(
5910 cx,
5911 &self.window,
5912 ))),
5913 "messageevent" => Ok(DomRoot::upcast(MessageEvent::new_uninitialized(
5914 cx,
5915 self.window.upcast(),
5916 ))),
5917 "mouseevent" | "mouseevents" => Ok(DomRoot::upcast(MouseEvent::new_uninitialized(
5918 cx,
5919 &self.window,
5920 ))),
5921 "storageevent" => Ok(DomRoot::upcast(StorageEvent::new_uninitialized(
5922 cx,
5923 &self.window,
5924 "".into(),
5925 ))),
5926 "textevent" => Ok(DomRoot::upcast(TextEvent::new_uninitialized(
5927 cx,
5928 &self.window,
5929 ))),
5930 "touchevent" => {
5931 let touches = TouchList::new(cx, &self.window, &[]);
5932 let changed_touches = TouchList::new(cx, &self.window, &[]);
5933 let target_touches = TouchList::new(cx, &self.window, &[]);
5934
5935 Ok(DomRoot::upcast(DomTouchEvent::new_uninitialized(
5936 cx,
5937 &self.window,
5938 &touches,
5939 &changed_touches,
5940 &target_touches,
5941 )))
5942 },
5943 "uievent" | "uievents" => Ok(DomRoot::upcast(UIEvent::new_uninitialized(
5944 cx,
5945 &self.window,
5946 ))),
5947 _ => Err(Error::NotSupported(None)),
5948 }
5949 }
5950
5951 fn LastModified(&self) -> DOMString {
5953 DOMString::from(self.last_modified.as_ref().cloned().unwrap_or_else(|| {
5954 Local::now().format("%m/%d/%Y %H:%M:%S").to_string()
5960 }))
5961 }
5962
5963 fn CreateRange(&self, cx: &mut JSContext) -> DomRoot<Range> {
5965 Range::new_with_doc(cx, self, None)
5966 }
5967
5968 fn CreateNodeIterator(
5970 &self,
5971 cx: &mut js::context::JSContext,
5972 root: &Node,
5973 what_to_show: u32,
5974 filter: Option<Rc<NodeFilter>>,
5975 ) -> DomRoot<NodeIterator> {
5976 NodeIterator::new(cx, self, root, what_to_show, filter)
5977 }
5978
5979 fn CreateTreeWalker(
5981 &self,
5982 cx: &mut JSContext,
5983 root: &Node,
5984 what_to_show: u32,
5985 filter: Option<Rc<NodeFilter>>,
5986 ) -> DomRoot<TreeWalker> {
5987 TreeWalker::new(cx, self, root, what_to_show, filter)
5988 }
5989
5990 fn Title(&self) -> DOMString {
5992 self.title().unwrap_or_default()
5993 }
5994
5995 fn SetTitle(&self, cx: &mut JSContext, title: DOMString) {
5997 let root = match self.GetDocumentElement() {
5998 Some(root) => root,
5999 None => return,
6000 };
6001
6002 let node = if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
6005 let elem = root
6008 .upcast::<Node>()
6009 .child_elements_unrooted(cx.no_gc())
6010 .find(|node| {
6011 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
6012 });
6013 match elem {
6014 Some(elem) => UnrootedDom::upcast::<Node>(elem).as_rooted(),
6015 None => {
6017 let name = QualName::new(None, ns!(svg), local_name!("title"));
6020 let elem = Element::create(
6021 cx,
6022 name,
6023 None,
6024 self,
6025 ElementCreator::ScriptCreated,
6026 CustomElementCreationMode::Synchronous,
6027 None,
6028 );
6029
6030 let parent = root.upcast::<Node>();
6032 let child = elem.upcast::<Node>();
6033 parent
6034 .InsertBefore(cx, child, parent.GetFirstChild().as_deref())
6035 .unwrap()
6036 },
6037 }
6038 }
6039 else if root.namespace() == &ns!(html) {
6041 let elem = root
6042 .upcast::<Node>()
6043 .traverse_preorder_non_rooting(cx.no_gc(), ShadowIncluding::No)
6044 .find(|node| node.is::<HTMLTitleElement>());
6045 match elem {
6046 Some(elem) => elem.as_rooted(),
6048 None => match self.GetHead() {
6050 Some(head) => {
6051 let name = QualName::new(None, ns!(html), local_name!("title"));
6054 let elem = Element::create(
6055 cx,
6056 name,
6057 None,
6058 self,
6059 ElementCreator::ScriptCreated,
6060 CustomElementCreationMode::Synchronous,
6061 None,
6062 );
6063
6064 head.upcast::<Node>()
6066 .AppendChild(cx, elem.upcast())
6067 .unwrap()
6068 },
6069 None => return,
6071 },
6072 }
6073 }
6074 else {
6076 return;
6078 };
6079
6080 node.set_text_content_for_element(cx, Some(title));
6085 }
6086
6087 fn GetHead(&self) -> Option<DomRoot<HTMLHeadElement>> {
6089 self.get_html_element()
6090 .and_then(|root| root.upcast::<Node>().children().find_map(DomRoot::downcast))
6091 }
6092
6093 fn GetCurrentScript(&self) -> Option<DomRoot<HTMLScriptElement>> {
6095 self.current_script.get()
6096 }
6097
6098 fn GetBody(&self) -> Option<DomRoot<HTMLElement>> {
6100 self.get_html_element().and_then(|root| {
6103 let node = root.upcast::<Node>();
6104 node.children()
6105 .find(|child| {
6106 matches!(
6107 child.type_id(),
6108 NodeTypeId::Element(ElementTypeId::HTMLElement(
6109 HTMLElementTypeId::HTMLBodyElement,
6110 )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
6111 HTMLElementTypeId::HTMLFrameSetElement,
6112 ))
6113 )
6114 })
6115 .map(|node| DomRoot::downcast(node).unwrap())
6116 })
6117 }
6118
6119 fn SetBody(&self, cx: &mut JSContext, new_body: Option<&HTMLElement>) -> ErrorResult {
6121 let new_body = match new_body {
6123 Some(new_body) => new_body,
6124 None => return Err(Error::HierarchyRequest(None)),
6125 };
6126
6127 let node = new_body.upcast::<Node>();
6128 match node.type_id() {
6129 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) |
6130 NodeTypeId::Element(ElementTypeId::HTMLElement(
6131 HTMLElementTypeId::HTMLFrameSetElement,
6132 )) => {},
6133 _ => return Err(Error::HierarchyRequest(None)),
6134 }
6135
6136 let old_body = self.GetBody();
6138 if old_body.as_deref() == Some(new_body) {
6139 return Ok(());
6140 }
6141
6142 match (self.GetDocumentElement(), &old_body) {
6143 (Some(ref root), Some(child)) => {
6146 let root = root.upcast::<Node>();
6147 root.ReplaceChild(cx, new_body.upcast(), child.upcast())
6148 .map(|_| ())
6149 },
6150
6151 (None, _) => Err(Error::HierarchyRequest(None)),
6153
6154 (Some(ref root), &None) => {
6157 let root = root.upcast::<Node>();
6158 root.AppendChild(cx, new_body.upcast()).map(|_| ())
6159 },
6160 }
6161 }
6162
6163 fn GetElementsByName(&self, cx: &mut JSContext, name: DOMString) -> DomRoot<NodeList> {
6165 NodeList::new_elements_by_name_list(cx, self.window(), self, name)
6166 }
6167
6168 fn Images(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6170 self.images.or_init(|| {
6171 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6172 element.is::<HTMLImageElement>()
6173 })
6174 })
6175 }
6176
6177 fn Embeds(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6179 self.embeds.or_init(|| {
6180 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6181 element.is::<HTMLEmbedElement>()
6182 })
6183 })
6184 }
6185
6186 fn Plugins(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6188 self.Embeds(cx)
6189 }
6190
6191 fn Links(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6193 self.links.or_init(|| {
6194 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6195 (element.is::<HTMLAnchorElement>() || element.is::<HTMLAreaElement>()) &&
6196 element.has_attribute(&local_name!("href"))
6197 })
6198 })
6199 }
6200
6201 fn Forms(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6203 self.forms.or_init(|| {
6204 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6205 element.is::<HTMLFormElement>()
6206 })
6207 })
6208 }
6209
6210 fn Scripts(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6212 self.scripts.or_init(|| {
6213 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6214 element.is::<HTMLScriptElement>()
6215 })
6216 })
6217 }
6218
6219 fn Anchors(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6221 self.anchors.or_init(|| {
6222 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6223 element.is::<HTMLAnchorElement>() && element.has_attribute(&local_name!("href"))
6224 })
6225 })
6226 }
6227
6228 fn Applets(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6230 self.applets
6231 .or_init(|| HTMLCollection::always_empty(cx, &self.window, self.upcast()))
6232 }
6233
6234 fn GetLocation(&self, cx: &mut JSContext) -> Option<DomRoot<Location>> {
6236 if self.is_fully_active() {
6237 Some(self.window.Location(cx))
6238 } else {
6239 None
6240 }
6241 }
6242
6243 fn Children(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6245 HTMLCollection::children(cx, &self.window, self.upcast())
6246 }
6247
6248 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
6250 self.upcast::<Node>().child_elements().next()
6251 }
6252
6253 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
6255 self.upcast::<Node>()
6256 .rev_children()
6257 .find_map(DomRoot::downcast)
6258 }
6259
6260 fn ChildElementCount(&self) -> u32 {
6262 self.upcast::<Node>().child_elements().count() as u32
6263 }
6264
6265 fn Prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6267 self.upcast::<Node>().prepend(cx, nodes)
6268 }
6269
6270 fn Append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6272 self.upcast::<Node>().append(cx, nodes)
6273 }
6274
6275 fn ReplaceChildren(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6277 self.upcast::<Node>().replace_children(cx, nodes)
6278 }
6279
6280 fn MoveBefore(&self, cx: &mut JSContext, node: &Node, child: Option<&Node>) -> ErrorResult {
6282 self.upcast::<Node>().move_before(cx, node, child)
6283 }
6284
6285 fn QuerySelector(
6287 &self,
6288 cx: &mut JSContext,
6289 selectors: DOMString,
6290 ) -> Fallible<Option<DomRoot<Element>>> {
6291 self.upcast::<Node>().query_selector(cx.no_gc(), selectors)
6292 }
6293
6294 fn QuerySelectorAll(
6296 &self,
6297 cx: &mut JSContext,
6298 selectors: DOMString,
6299 ) -> Fallible<DomRoot<NodeList>> {
6300 self.upcast::<Node>().query_selector_all(cx, selectors)
6301 }
6302
6303 fn ReadyState(&self) -> DocumentReadyState {
6305 self.ready_state.get()
6306 }
6307
6308 fn GetDefaultView(&self) -> Option<DomRoot<Window>> {
6310 if self.has_browsing_context {
6311 Some(DomRoot::from_ref(&*self.window))
6312 } else {
6313 None
6314 }
6315 }
6316
6317 fn GetCookie(&self) -> Fallible<DOMString> {
6319 if self.is_cookie_averse() {
6320 return Ok(DOMString::new());
6321 }
6322
6323 if !self.origin().is_tuple() {
6324 return Err(Error::Security(None));
6325 }
6326
6327 let url = self.url();
6328 let (tx, rx) =
6329 profile_generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
6330 let _ = self
6331 .window
6332 .as_global_scope()
6333 .resource_threads()
6334 .send(GetCookieStringForUrl(url, tx, NonHTTP));
6335 let cookies = rx.recv().unwrap();
6336 Ok(cookies.map_or(DOMString::new(), DOMString::from))
6337 }
6338
6339 fn SetCookie(&self, cookie: DOMString) -> ErrorResult {
6341 if self.is_cookie_averse() {
6342 return Ok(());
6343 }
6344
6345 if !self.origin().is_tuple() {
6346 return Err(Error::Security(None));
6347 }
6348
6349 if !cookie.is_valid_for_cookie() {
6350 return Ok(());
6351 }
6352
6353 let cookies = if let Some(cookie) = Cookie::parse(cookie.to_string()).ok().map(Serde) {
6354 vec![cookie]
6355 } else {
6356 vec![]
6357 };
6358
6359 let _ = self
6360 .window
6361 .as_global_scope()
6362 .resource_threads()
6363 .send(SetCookiesForUrl(self.url(), cookies, NonHTTP));
6364 Ok(())
6365 }
6366
6367 fn BgColor(&self) -> DOMString {
6369 self.get_body_attribute(&local_name!("bgcolor"))
6370 }
6371
6372 fn SetBgColor(&self, cx: &mut JSContext, value: DOMString) {
6374 self.set_body_attribute(cx, &local_name!("bgcolor"), value)
6375 }
6376
6377 fn FgColor(&self) -> DOMString {
6379 self.get_body_attribute(&local_name!("text"))
6380 }
6381
6382 fn SetFgColor(&self, cx: &mut JSContext, value: DOMString) {
6384 self.set_body_attribute(cx, &local_name!("text"), value)
6385 }
6386
6387 fn NamedGetter(&self, cx: &mut JSContext, name: DOMString) -> Option<NamedPropertyValue> {
6389 if name.is_empty() {
6390 return None;
6391 }
6392 let name = Atom::from(name);
6393
6394 let elements_with_name = self.get_elements_with_name(cx, &name);
6397 let name_iter = elements_with_name
6398 .iter()
6399 .filter(|elem| is_named_element_with_name_attribute(elem));
6400 let elements_with_id = self.id_map.get_all(cx.no_gc(), self.upcast(), &name);
6401 let id_iter = elements_with_id
6402 .iter()
6403 .filter(|elem| is_named_element_with_id_attribute(elem));
6404 let mut elements = name_iter.chain(id_iter);
6405
6406 let first = elements.next()?;
6413 if elements.all(|other| first == other) {
6414 if let Some(nested_window_proxy) = first
6415 .downcast::<HTMLIFrameElement>()
6416 .and_then(|iframe| iframe.GetContentWindow())
6417 {
6418 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
6419 }
6420
6421 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
6423 }
6424
6425 #[derive(JSTraceable, MallocSizeOf)]
6428 struct DocumentNamedGetter {
6429 #[no_trace]
6430 name: Atom,
6431 }
6432 impl CollectionFilter for DocumentNamedGetter {
6433 fn filter(&self, elem: &Element, _root: &Node) -> bool {
6434 let type_ = match elem.upcast::<Node>().type_id() {
6435 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
6436 _ => return false,
6437 };
6438 match type_ {
6439 HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
6440 elem.get_name().as_ref() == Some(&self.name)
6441 },
6442 HTMLElementTypeId::HTMLImageElement => elem.get_name().is_some_and(|name| {
6443 name == *self.name ||
6444 !name.is_empty() && elem.get_id().as_ref() == Some(&self.name)
6445 }),
6446 _ => false,
6450 }
6451 }
6452 }
6453 let collection = HTMLCollection::create(
6454 cx,
6455 self.window(),
6456 self.upcast(),
6457 Box::new(DocumentNamedGetter { name }),
6458 );
6459 Some(NamedPropertyValue::HTMLCollection(collection))
6460 }
6461
6462 fn SupportedPropertyNames(&self, no_gc: &NoGC) -> Vec<DOMString> {
6464 let mut names_with_first_named_element_map = HashMap::new();
6465 self.name_map
6466 .for_each(no_gc, self.upcast(), |name, elements| {
6467 if name.is_empty() {
6468 return;
6469 }
6470 let mut name_iter = elements
6471 .iter()
6472 .filter(|elem| is_named_element_with_name_attribute(elem));
6473 if let Some(first) = name_iter.next() {
6474 names_with_first_named_element_map.insert(name.clone(), first.as_rooted());
6475 }
6476 });
6477
6478 self.id_map.for_each(no_gc, self.upcast(), |id, elements| {
6479 if id.is_empty() {
6480 return;
6481 }
6482 let mut id_iter = elements
6483 .iter()
6484 .filter(|elem| is_named_element_with_id_attribute(elem));
6485 if let Some(first) = id_iter.next() {
6486 match names_with_first_named_element_map.entry(id.clone()) {
6487 Vacant(entry) => drop(entry.insert(first.as_rooted())),
6488 Occupied(mut entry) => {
6489 if first.upcast::<Node>().is_before(entry.get().upcast()) {
6490 *entry.get_mut() = first.as_rooted();
6491 }
6492 },
6493 }
6494 }
6495 });
6496
6497 let mut names_with_first_named_element_vec: Vec<_> =
6498 names_with_first_named_element_map.into_iter().collect();
6499 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
6500 if a.1 == b.1 {
6501 a.0.cmp(&b.0)
6504 } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
6505 Ordering::Less
6506 } else {
6507 Ordering::Greater
6508 }
6509 });
6510
6511 names_with_first_named_element_vec
6512 .into_iter()
6513 .map(|(k, _)| DOMString::from(&*k))
6514 .collect()
6515 }
6516
6517 fn Clear(&self) {
6519 }
6521
6522 fn CaptureEvents(&self) {
6524 }
6526
6527 fn ReleaseEvents(&self) {
6529 }
6531
6532 global_event_handlers!();
6534
6535 event_handler!(
6537 readystatechange,
6538 GetOnreadystatechange,
6539 SetOnreadystatechange
6540 );
6541
6542 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
6544 self.document_or_shadow_root.element_from_point(
6545 self.upcast(),
6546 x,
6547 y,
6548 self.GetDocumentElement(),
6549 self.has_browsing_context,
6550 )
6551 }
6552
6553 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
6555 self.document_or_shadow_root.elements_from_point(
6556 self.upcast(),
6557 x,
6558 y,
6559 self.GetDocumentElement(),
6560 self.has_browsing_context,
6561 )
6562 }
6563
6564 fn GetScrollingElement(&self) -> Option<DomRoot<Element>> {
6566 if self.quirks_mode() == QuirksMode::Quirks {
6568 if let Some(ref body) = self.GetBody() {
6570 let e = body.upcast::<Element>();
6571 if !e.is_potentially_scrollable_body_for_scrolling_element() {
6575 return Some(DomRoot::from_ref(e));
6576 }
6577 }
6578
6579 return None;
6581 }
6582
6583 self.GetDocumentElement()
6586 }
6587
6588 fn Open(
6590 &self,
6591 cx: &mut JSContext,
6592 _unused1: Option<DOMString>,
6593 _unused2: Option<DOMString>,
6594 ) -> Fallible<DomRoot<Document>> {
6595 if !self.is_html_document() {
6597 return Err(Error::InvalidState(None));
6598 }
6599
6600 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6603 return Err(Error::InvalidState(None));
6604 }
6605
6606 let entry_responsible_document = GlobalScope::entry().as_window().Document();
6608
6609 if !self
6612 .origin()
6613 .same_origin(&entry_responsible_document.origin())
6614 {
6615 return Err(Error::Security(None));
6616 }
6617
6618 if self
6621 .active_parser()
6622 .is_some_and(|parser| parser.script_nesting_level() > 0)
6623 {
6624 return Ok(DomRoot::from_ref(self));
6625 }
6626
6627 if self.is_prompting_or_unloading() {
6629 return Ok(DomRoot::from_ref(self));
6630 }
6631
6632 if self.active_parser_was_aborted.get() {
6634 return Ok(DomRoot::from_ref(self));
6635 }
6636
6637 self.window().set_navigation_start();
6641
6642 if self.has_browsing_context() {
6646 self.abort(cx);
6649 }
6650
6651 for node in self
6654 .upcast::<Node>()
6655 .traverse_preorder_non_rooting(cx.no_gc(), ShadowIncluding::Yes)
6656 {
6657 node.upcast::<EventTarget>().remove_all_listeners();
6658 }
6659
6660 if self.window.Document() == DomRoot::from_ref(self) {
6663 self.window.upcast::<EventTarget>().remove_all_listeners();
6664 }
6665
6666 Node::replace_all(cx, None, self.upcast::<Node>());
6668
6669 if self.is_fully_active() {
6676 let mut new_url = entry_responsible_document.url();
6678
6679 if entry_responsible_document != DomRoot::from_ref(self) {
6681 new_url.set_fragment(None);
6682 }
6683
6684 self.set_url(new_url);
6687 }
6688
6689 self.is_initial_about_blank.set(false);
6691
6692 if self.iframe_load_in_progress.get() {
6695 self.mute_iframe_load.set(true);
6696 }
6697
6698 self.set_quirks_mode(QuirksMode::NoQuirks);
6700
6701 let resource_threads = self.window.as_global_scope().resource_threads().clone();
6707 *self.loader.borrow_mut() =
6708 DocumentLoader::new_with_threads(resource_threads, Some(self.url()));
6709 ServoParser::parse_html_script_input(cx, self, self.url());
6710
6711 self.update_the_current_document_readiness(cx, DocumentReadyState::Loading);
6717
6718 Ok(DomRoot::from_ref(self))
6720 }
6721
6722 fn Open_(
6724 &self,
6725 cx: &mut JSContext,
6726 url: USVString,
6727 target: DOMString,
6728 features: DOMString,
6729 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
6730 self.browsing_context()
6731 .ok_or(Error::InvalidAccess(None))?
6732 .open(cx, url, target, features)
6733 }
6734
6735 fn Write(&self, cx: &mut JSContext, text: Vec<TrustedHTMLOrString>) -> ErrorResult {
6737 self.write(cx, text, false, "Document", "write")
6740 }
6741
6742 fn Writeln(&self, cx: &mut JSContext, text: Vec<TrustedHTMLOrString>) -> ErrorResult {
6744 self.write(cx, text, true, "Document", "writeln")
6747 }
6748
6749 fn Close(&self, cx: &mut JSContext) -> ErrorResult {
6751 if !self.is_html_document() {
6752 return Err(Error::InvalidState(None));
6754 }
6755
6756 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6759 return Err(Error::InvalidState(None));
6760 }
6761
6762 let parser = match self.get_current_parser() {
6764 Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
6765 _ => {
6766 return Ok(());
6767 },
6768 };
6769
6770 parser.close(cx);
6772
6773 Ok(())
6774 }
6775
6776 fn ExecCommand(
6778 &self,
6779 cx: &mut JSContext,
6780 command_id: DOMString,
6781 _show_ui: bool,
6782 value: TrustedHTMLOrString,
6783 ) -> Fallible<bool> {
6784 let value = if command_id == "insertHTML" {
6785 TrustedHTML::get_trusted_type_compliant_string(
6786 cx,
6787 self.window.as_global_scope(),
6788 value,
6789 "Document execCommand",
6790 )?
6791 } else {
6792 match value {
6793 TrustedHTMLOrString::TrustedHTML(trusted_html) => trusted_html.data().clone(),
6794 TrustedHTMLOrString::String(value) => value,
6795 }
6796 };
6797
6798 Ok(self.exec_command_for_command_id(cx, command_id, value))
6799 }
6800
6801 fn QueryCommandEnabled(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
6803 self.check_support_and_enabled(cx, &command_id).is_some()
6805 }
6806
6807 fn QueryCommandSupported(&self, command_id: DOMString) -> bool {
6809 self.is_command_supported(command_id)
6813 }
6814
6815 fn QueryCommandIndeterm(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
6817 self.is_command_indeterminate(cx, command_id)
6818 }
6819
6820 fn QueryCommandState(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
6822 self.command_state_for_command(cx, command_id)
6823 }
6824
6825 fn QueryCommandValue(&self, cx: &mut JSContext, command_id: DOMString) -> DOMString {
6827 self.command_value_for_command(cx, command_id)
6828 }
6829
6830 event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror);
6832
6833 event_handler!(
6835 fullscreenchange,
6836 GetOnfullscreenchange,
6837 SetOnfullscreenchange
6838 );
6839
6840 fn FullscreenEnabled(&self) -> bool {
6842 self.get_allow_fullscreen()
6843 }
6844
6845 fn Fullscreen(&self) -> bool {
6847 self.fullscreen_element.get().is_some()
6848 }
6849
6850 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
6852 DocumentOrShadowRoot::get_fullscreen_element(&self.node, self.fullscreen_element.get())
6853 }
6854
6855 fn ExitFullscreen(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
6857 self.exit_fullscreen(cx)
6858 }
6859
6860 fn ServoGetMediaControls(&self, id: DOMString) -> Fallible<DomRoot<ShadowRoot>> {
6864 match self.media_controls.borrow().get(&*id.str()) {
6865 Some(m) => Ok(DomRoot::from_ref(m)),
6866 None => Err(Error::InvalidAccess(None)),
6867 }
6868 }
6869
6870 fn GetSelection(&self, cx: &mut JSContext) -> Option<DomRoot<Selection>> {
6872 if self.has_browsing_context {
6873 Some(self.selection.or_init(|| Selection::new(cx, self)))
6874 } else {
6875 None
6876 }
6877 }
6878
6879 fn Fonts(&self, cx: &mut JSContext) -> DomRoot<FontFaceSet> {
6881 self.fonts
6882 .or_init(|| FontFaceSet::new(cx, &self.global(), None))
6883 }
6884
6885 fn Hidden(&self) -> bool {
6887 self.visibility_state.get() == DocumentVisibilityState::Hidden
6888 }
6889
6890 fn VisibilityState(&self) -> DocumentVisibilityState {
6892 self.visibility_state.get()
6893 }
6894
6895 fn CreateExpression(
6896 &self,
6897 cx: &mut JSContext,
6898 expression: DOMString,
6899 resolver: Option<Rc<XPathNSResolver>>,
6900 ) -> Fallible<DomRoot<crate::dom::types::XPathExpression>> {
6901 let parsed_expression =
6902 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
6903 Ok(XPathExpression::new(
6904 cx,
6905 &self.window,
6906 None,
6907 parsed_expression,
6908 ))
6909 }
6910
6911 fn CreateNSResolver(&self, cx: &mut JSContext, node_resolver: &Node) -> DomRoot<Node> {
6912 let global = self.global();
6913 let window = global.as_window();
6914 let evaluator = XPathEvaluator::new(cx, window, None);
6915 XPathEvaluatorMethods::<crate::DomTypeHolder>::CreateNSResolver(&*evaluator, node_resolver)
6916 }
6917
6918 fn Evaluate(
6919 &self,
6920 cx: &mut JSContext,
6921 expression: DOMString,
6922 context_node: &Node,
6923 resolver: Option<Rc<XPathNSResolver>>,
6924 result_type: u16,
6925 result: Option<&crate::dom::types::XPathResult>,
6926 ) -> Fallible<DomRoot<crate::dom::types::XPathResult>> {
6927 let parsed_expression =
6928 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
6929 XPathExpression::new(cx, &self.window, None, parsed_expression).evaluate_internal(
6930 cx,
6931 context_node,
6932 result_type,
6933 result,
6934 )
6935 }
6936
6937 fn AdoptedStyleSheets(&self, cx: &mut JSContext, retval: MutableHandleValue) {
6939 self.adopted_stylesheets_frozen_types.get_or_init(
6940 cx,
6941 || {
6942 self.adopted_stylesheets
6943 .borrow()
6944 .clone()
6945 .iter()
6946 .map(|sheet| sheet.as_rooted())
6947 .collect()
6948 },
6949 retval,
6950 );
6951 }
6952
6953 fn SetAdoptedStyleSheets(&self, cx: &mut JSContext, val: HandleValue) -> ErrorResult {
6955 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
6956 cx,
6957 &self.adopted_stylesheets,
6958 val,
6959 &StyleSheetListOwner::Document(Dom::from_ref(self)),
6960 );
6961
6962 if result.is_ok() {
6963 self.adopted_stylesheets_frozen_types.clear()
6964 }
6965
6966 result
6967 }
6968
6969 fn Timeline(&self) -> DomRoot<DocumentTimeline> {
6970 self.timeline.as_rooted()
6971 }
6972}
6973
6974fn update_with_current_instant(marker: &Cell<Option<CrossProcessInstant>>) {
6975 if marker.get().is_none() {
6976 marker.set(Some(CrossProcessInstant::now()))
6977 }
6978}
6979
6980#[derive(JSTraceable, MallocSizeOf)]
6981pub(crate) enum AnimationFrameCallback {
6982 DevtoolsFramerateTick {
6983 actor_name: String,
6984 },
6985 FrameRequestCallback {
6986 #[conditional_malloc_size_of]
6987 callback: Rc<FrameRequestCallback>,
6988 },
6989}
6990
6991impl AnimationFrameCallback {
6992 fn call(&self, cx: &mut JSContext, document: &Document, now: f64) {
6993 match *self {
6994 AnimationFrameCallback::DevtoolsFramerateTick { ref actor_name } => {
6995 let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name.clone(), now);
6996 let devtools_sender = document.window().as_global_scope().devtools_chan().unwrap();
6997 devtools_sender.send(msg).unwrap();
6998 },
6999 AnimationFrameCallback::FrameRequestCallback { ref callback } => {
7000 let _ = callback.Call__(cx, Finite::wrap(now), ExceptionHandling::Report);
7003 },
7004 }
7005 }
7006}
7007
7008#[derive(Default, JSTraceable, MallocSizeOf)]
7009#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
7010struct PendingInOrderScriptVec {
7011 scripts: DomRefCell<VecDeque<PendingScript>>,
7012}
7013
7014impl PendingInOrderScriptVec {
7015 fn is_empty(&self) -> bool {
7016 self.scripts.borrow().is_empty()
7017 }
7018
7019 fn push(&self, element: &HTMLScriptElement) {
7020 self.scripts
7021 .borrow_mut()
7022 .push_back(PendingScript::new(element));
7023 }
7024
7025 fn loaded(&self, element: &HTMLScriptElement, result: ScriptResult) {
7026 let mut scripts = self.scripts.borrow_mut();
7027 let entry = scripts
7028 .iter_mut()
7029 .find(|entry| &*entry.element == element)
7030 .unwrap();
7031 entry.loaded(result);
7032 }
7033
7034 fn take_next_ready_to_be_executed(&self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
7035 let mut scripts = self.scripts.borrow_mut();
7036 let pair = scripts.front_mut()?.take_result()?;
7037 scripts.pop_front();
7038 Some(pair)
7039 }
7040
7041 fn clear(&self) {
7042 *self.scripts.borrow_mut() = Default::default();
7043 }
7044}
7045
7046#[derive(JSTraceable, MallocSizeOf)]
7047#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
7048struct PendingScript {
7049 element: Dom<HTMLScriptElement>,
7050 load: Option<ScriptResult>,
7052}
7053
7054impl PendingScript {
7055 fn new(element: &HTMLScriptElement) -> Self {
7056 Self {
7057 element: Dom::from_ref(element),
7058 load: None,
7059 }
7060 }
7061
7062 fn new_with_load(element: &HTMLScriptElement, load: Option<ScriptResult>) -> Self {
7063 Self {
7064 element: Dom::from_ref(element),
7065 load,
7066 }
7067 }
7068
7069 fn loaded(&mut self, result: ScriptResult) {
7070 assert!(self.load.is_none());
7071 self.load = Some(result);
7072 }
7073
7074 fn take_result(&mut self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
7075 self.load
7076 .take()
7077 .map(|result| (DomRoot::from_ref(&*self.element), result))
7078 }
7079}
7080
7081fn is_named_element_with_name_attribute(elem: &Element) -> bool {
7082 let type_ = match elem.upcast::<Node>().type_id() {
7083 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
7084 _ => return false,
7085 };
7086 match type_ {
7087 HTMLElementTypeId::HTMLFormElement |
7088 HTMLElementTypeId::HTMLIFrameElement |
7089 HTMLElementTypeId::HTMLImageElement => true,
7090 _ => false,
7094 }
7095}
7096
7097fn is_named_element_with_id_attribute(elem: &Element) -> bool {
7098 elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty())
7102}
7103
7104impl DocumentHelpers for Document {
7105 fn ensure_safe_to_run_script_or_layout(&self) {
7106 Document::ensure_safe_to_run_script_or_layout(self)
7107 }
7108}
7109
7110pub(crate) struct SameoriginAncestorNavigablesIterator {
7114 document: DomRoot<Document>,
7115}
7116
7117impl SameoriginAncestorNavigablesIterator {
7118 pub(crate) fn new(document: DomRoot<Document>) -> Self {
7119 Self { document }
7120 }
7121}
7122
7123impl Iterator for SameoriginAncestorNavigablesIterator {
7124 type Item = DomRoot<Document>;
7125
7126 fn next(&mut self) -> Option<Self::Item> {
7127 let window_proxy = self.document.browsing_context()?;
7128 self.document = window_proxy.parent()?.document()?;
7129 Some(self.document.clone())
7130 }
7131}
7132
7133pub(crate) struct SameOriginDescendantNavigablesIterator {
7138 stack: Vec<Box<dyn Iterator<Item = DomRoot<HTMLIFrameElement>>>>,
7139}
7140
7141impl SameOriginDescendantNavigablesIterator {
7142 pub(crate) fn new(document: &Document) -> Self {
7143 let iframes: Vec<DomRoot<HTMLIFrameElement>> = document.iframes().iter().collect();
7144 Self {
7145 stack: vec![Box::new(iframes.into_iter())],
7146 }
7147 }
7148
7149 fn get_next_iframe(&mut self) -> Option<DomRoot<HTMLIFrameElement>> {
7150 let mut cur_iframe = self.stack.last_mut()?.next();
7151 while cur_iframe.is_none() {
7152 self.stack.pop();
7153 cur_iframe = self.stack.last_mut()?.next();
7154 }
7155 cur_iframe
7156 }
7157}
7158
7159impl Iterator for SameOriginDescendantNavigablesIterator {
7160 type Item = DomRoot<Document>;
7161
7162 fn next(&mut self) -> Option<Self::Item> {
7163 while let Some(iframe) = self.get_next_iframe() {
7164 let Some(pipeline_id) = iframe.pipeline_id() else {
7165 continue;
7166 };
7167
7168 if let Some(document) = ScriptThread::find_document(pipeline_id) {
7169 let child_iframes: Vec<DomRoot<HTMLIFrameElement>> =
7170 document.iframes().iter().collect();
7171 self.stack.push(Box::new(child_iframes.into_iter()));
7172 return Some(document);
7173 } else {
7174 continue;
7175 };
7176 }
7177 None
7178 }
7179}