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, Mutex};
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};
27use encoding_rs::{Encoding, UTF_8};
28use html5ever::{LocalName, QualName, local_name, ns};
29use hyper_serde::Serde;
30use indexmap::IndexSet;
31use js::context::{JSContext, NoGC};
32use js::realm::CurrentRealm;
33use js::rust::{HandleObject, HandleValue, MutableHandleValue};
34use layout_api::{
35 PendingRestyle, ReflowGoal, ReflowPhasesRun, ReflowStatistics, RestyleReason,
36 ScrollContainerQueryFlags, TrustedNodeAddress,
37};
38use metrics::{InteractiveFlag, InteractiveWindow, ProgressiveWebMetrics};
39use net_traits::CookieSource::NonHTTP;
40use net_traits::CoreResourceMsg::{GetCookieStringForUrl, SetCookiesForUrl};
41use net_traits::image_cache::ImageCache;
42use net_traits::policy_container::PolicyContainer;
43use net_traits::pub_domains::is_pub_domain;
44use net_traits::request::{
45 InsecureRequestsPolicy, PreloadId, PreloadKey, PreloadedResources, RequestBuilder,
46};
47use net_traits::{ReferrerPolicy, ResourceFetchTiming};
48use paint_api::largest_contentful_paint_candidate::LCPCandidateID;
49use percent_encoding::percent_decode;
50use profile_traits::generic_channel as profile_generic_channel;
51use profile_traits::time::TimerMetadataFrameType;
52use regex::bytes::Regex;
53use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
54use script_bindings::cell::{DomRefCell, Ref, RefMut};
55use script_bindings::interfaces::DocumentHelpers;
56use script_bindings::reflector::reflect_dom_object_with_proto;
57use script_bindings::trace::CustomTraceable;
58use script_traits::{DocumentActivity, ProgressiveWebMetricType};
59use servo_arc::Arc;
60use servo_base::cross_process_instant::CrossProcessInstant;
61use servo_base::generic_channel::GenericSend;
62use servo_base::id::{PipelineId, WebViewId};
63use servo_base::{Epoch, generic_channel};
64use servo_config::pref;
65use servo_constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage};
66use servo_media::{ClientContextId, ServoMedia};
67use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
68use style::attr::AttrValue;
69use style::context::QuirksMode;
70use style::dom::OpaqueNode;
71use style::invalidation::element::restyle_hints::RestyleHint;
72use style::selector_parser::Snapshot;
73use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
74use style::str::{split_html_space_chars, str_join};
75use style::stylesheet_set::DocumentStylesheetSet;
76use style::stylesheets::{Origin, OriginSet, Stylesheet};
77use style::stylist::Stylist;
78use stylo_atoms::Atom;
79use time::Duration as TimeDuration;
80use url::{Host, Position};
81
82use crate::animations::Animations;
83use crate::document_loader::{DocumentLoader, LoadType};
84use crate::dom::FlatTreeParent;
85use crate::dom::animationtimeline::AnimationTimeline;
86use crate::dom::attr::Attr;
87use crate::dom::beforeunloadevent::BeforeUnloadEvent;
88use crate::dom::bindings::callback::ExceptionHandling;
89use crate::dom::bindings::codegen::Bindings::AnimationFrameProviderBinding::FrameRequestCallback;
90use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEvent_Binding::BeforeUnloadEventMethods;
91use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
92 DocumentMethods, DocumentReadyState, DocumentVisibilityState, NamedPropertyValue,
93};
94use crate::dom::bindings::codegen::Bindings::ElementBinding::ScrollLogicalPosition;
95use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
96use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElement_Binding::HTMLIFrameElementMethods;
97#[cfg(any(feature = "webxr", feature = "gamepad"))]
98use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
99use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
100use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
101use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
102use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionName;
103use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
104 SetHTMLOptions, SetHTMLUnsafeOptions,
105};
106use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, WindowMethods};
107use crate::dom::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMethods;
108use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
109use crate::dom::bindings::codegen::UnionTypes::{
110 BooleanOrImportNodeOptions, NodeOrString, StringOrElementCreationOptions, TrustedHTMLOrString,
111};
112use crate::dom::bindings::domname::{
113 self, is_valid_attribute_local_name, is_valid_element_local_name, namespace_from_domstring,
114};
115use crate::dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible};
116use crate::dom::bindings::frozenarray::CachedFrozenArray;
117use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
118use crate::dom::bindings::num::Finite;
119use crate::dom::bindings::refcounted::Trusted;
120use crate::dom::bindings::reflector::DomGlobal;
121use crate::dom::bindings::root::{
122 Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout, ToLayoutOptional, UnrootedDom,
123};
124use crate::dom::bindings::str::{DOMString, USVString};
125use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
126use crate::dom::bindings::weakref::DOMTracker;
127use crate::dom::bindings::xmlname::matches_name_production;
128use crate::dom::cdatasection::CDATASection;
129use crate::dom::comment::Comment;
130use crate::dom::compositionevent::CompositionEvent;
131use crate::dom::css::cssstylesheet::CSSStyleSheet;
132use crate::dom::css::fontfaceset::FontFaceSet;
133use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
134use crate::dom::customelementregistry::{CustomElementReactionStack, CustomElementRegistry};
135use crate::dom::customevent::CustomEvent;
136use crate::dom::document::accessibility_data::AccessibilityData;
137use crate::dom::document::focus::{DocumentFocusHandler, FocusableArea};
138use crate::dom::document::tree_ordered_index_map::TreeOrderedIndexMap;
139use crate::dom::document::websocket::WebSocket;
140use crate::dom::document_embedder_controls::DocumentEmbedderControls;
141use crate::dom::document_event_handler::DocumentEventHandler;
142use crate::dom::documentfragment::DocumentFragment;
143use crate::dom::documentorshadowroot::{
144 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
145};
146use crate::dom::documenttimeline::DocumentTimeline;
147use crate::dom::documenttype::DocumentType;
148use crate::dom::domimplementation::DOMImplementation;
149use crate::dom::element::attributes::storage::AttrRef;
150use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
151use crate::dom::event::{Event, EventBubbles, EventCancelable};
152use crate::dom::eventtarget::EventTarget;
153use crate::dom::execcommand::basecommand::{CommandName, DefaultSingleLineContainerName};
154use crate::dom::execcommand::execcommands::DocumentExecCommandSupport;
155use crate::dom::focusevent::FocusEvent;
156use crate::dom::globalscope::GlobalScope;
157use crate::dom::hashchangeevent::HashChangeEvent;
158use crate::dom::history::History;
159use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
160use crate::dom::html::htmlareaelement::HTMLAreaElement;
161use crate::dom::html::htmlbaseelement::HTMLBaseElement;
162use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
163use crate::dom::html::htmlelement::HTMLElement;
164use crate::dom::html::htmlembedelement::HTMLEmbedElement;
165use crate::dom::html::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
166use crate::dom::html::htmlheadelement::HTMLHeadElement;
167use crate::dom::html::htmlhtmlelement::HTMLHtmlElement;
168use crate::dom::html::htmliframeelement::HTMLIFrameElement;
169use crate::dom::html::htmlimageelement::HTMLImageElement;
170use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult};
171use crate::dom::html::htmltitleelement::HTMLTitleElement;
172use crate::dom::htmldetailselement::DetailsNameGroups;
173use crate::dom::intersectionobserver::IntersectionObserver;
174use crate::dom::iterators::ShadowIncluding;
175use crate::dom::keyboardevent::KeyboardEvent;
176use crate::dom::largestcontentfulpaint::LargestContentfulPaint;
177use crate::dom::location::Location;
178use crate::dom::messageevent::MessageEvent;
179use crate::dom::mouseevent::MouseEvent;
180use crate::dom::node::treewalker::TreeWalker;
181use crate::dom::node::virtualmethods::vtable_for;
182use crate::dom::node::{Node, NodeDamage, NodeFlags, NodeTraits};
183use crate::dom::nodeiterator::NodeIterator;
184use crate::dom::nodelist::NodeList;
185use crate::dom::pagetransitionevent::PageTransitionEvent;
186use crate::dom::performance::performanceentry::PerformanceEntry;
187use crate::dom::performance::performancepainttiming::PerformancePaintTiming;
188use crate::dom::processinginstruction::ProcessingInstruction;
189use crate::dom::promise::Promise;
190use crate::dom::range::Range;
191use crate::dom::resizeobserver::{ResizeObservationDepth, ResizeObserver};
192use crate::dom::sanitizer::Sanitizer;
193use crate::dom::selection::Selection;
194use crate::dom::servoparser::ServoParser;
195use crate::dom::shadowroot::ShadowRoot;
196use crate::dom::storageevent::StorageEvent;
197use crate::dom::text::Text;
198use crate::dom::touchevent::TouchEvent as DomTouchEvent;
199use crate::dom::touchlist::TouchList;
200use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
201use crate::dom::types::{HTMLCanvasElement, VisibilityStateEntry};
202use crate::dom::uievent::UIEvent;
203use crate::dom::window::Window;
204use crate::dom::window::scrolling_box::{ScrollAxisState, ScrollingBox};
205use crate::dom::windowproxy::WindowProxy;
206use crate::dom::xpathevaluator::XPathEvaluator;
207use crate::dom::xpathexpression::XPathExpression;
208use crate::fetch::{DeferredFetchRecordInvokeState, FetchCanceller};
209use crate::iframe_collection::IFrameCollection;
210use crate::image_animation::ImageAnimationManager;
211use crate::mime::{APPLICATION, CHARSET};
212use crate::navigation::navigate;
213use crate::network_listener::{FetchResponseListener, NetworkListener};
214use crate::script_thread::{ScriptThread, SharedRwLocks};
215use crate::stylesheet_loader::StylesheetContextId;
216use crate::stylesheet_set::StylesheetSetRef;
217use crate::tasks::task::NonSendTaskBox;
218use crate::tasks::task_manager::TaskManager;
219use crate::tasks::task_source::TaskSourceName;
220use crate::timers::{OneshotTimerCallback, OneshotTimers};
221use crate::xpath::parse_expression;
222
223#[derive(Clone, Copy, PartialEq)]
224pub(crate) enum FireMouseEventType {
225 Move,
226 Over,
227 Out,
228 Enter,
229 Leave,
230}
231
232impl FireMouseEventType {
233 pub(crate) fn as_str(&self) -> &str {
234 match *self {
235 FireMouseEventType::Move => "mousemove",
236 FireMouseEventType::Over => "mouseover",
237 FireMouseEventType::Out => "mouseout",
238 FireMouseEventType::Enter => "mouseenter",
239 FireMouseEventType::Leave => "mouseleave",
240 }
241 }
242}
243
244#[derive(JSTraceable, MallocSizeOf)]
245pub(crate) struct RefreshRedirectDue {
246 #[no_trace]
247 pub(crate) url: ServoUrl,
248 pub(crate) from_meta_element: bool,
250}
251impl RefreshRedirectDue {
252 pub(crate) fn invoke(self, cx: &mut JSContext, global: &GlobalScope) {
254 let window = global
255 .downcast::<Window>()
256 .expect("Queued a RefreshRedirectDue on a non-Window globalscope");
257
258 if self.from_meta_element &&
265 window.Document().has_active_sandboxing_flag(
266 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
267 )
268 {
269 return;
270 }
271 let load_data = window.load_data_for_document(self.url, window.pipeline_id());
272 navigate(
273 cx,
274 window,
275 NavigationHistoryBehavior::Replace,
276 false,
277 load_data,
278 );
279 }
280}
281
282#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
283pub(crate) enum IsHTMLDocument {
284 HTMLDocument,
285 NonHTMLDocument,
286}
287
288#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
289pub(crate) enum TheEndLoadingPhase {
290 #[default]
291 Initial,
292 ProcessingDeferredScripts,
293 ProcessingAsSoonAsPossibleScripts,
294 WaitingForLoadEventBlockers,
295 Done,
296}
297
298#[derive(JSTraceable, MallocSizeOf)]
300pub(crate) enum DeclarativeRefresh {
301 PendingLoad {
302 #[no_trace]
303 url: ServoUrl,
304 time: u64,
305 from_meta_element: bool,
307 },
308 CreatedAfterLoad,
309}
310
311#[derive(JSTraceable, MallocSizeOf, PartialEq)]
312#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
313struct PendingScrollEvent {
314 target: Dom<EventTarget>,
316 #[no_trace]
318 event: Atom,
319}
320
321impl PendingScrollEvent {
322 fn equivalent(&self, target: &EventTarget, event: &Atom) -> bool {
323 &*self.target == target && self.event == *event
324 }
325}
326
327#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf)]
330pub(crate) struct RenderingUpdateReason(u8);
331
332bitflags! {
333 impl RenderingUpdateReason: u8 {
334 const ResizeObserverStartedObservingTarget = 1 << 0;
337 const IntersectionObserverStartedObservingTarget = 1 << 1;
340 const FontReadyPromiseFulfilled = 1 << 2;
344 }
345}
346
347#[derive(Clone, Debug, Default, MallocSizeOf)]
349pub(crate) struct NavigationTiming {
350 pub(crate) dom_loading: Cell<Option<CrossProcessInstant>>,
351 pub(crate) navigation_start: Cell<Option<CrossProcessInstant>>,
353 pub(crate) unload_event_start: Cell<Option<CrossProcessInstant>>,
355 pub(crate) unload_event_end: Cell<Option<CrossProcessInstant>>,
357 pub(crate) dom_interactive: Cell<Option<CrossProcessInstant>>,
359 pub(crate) dom_content_loaded_event_start: Cell<Option<CrossProcessInstant>>,
361 pub(crate) dom_content_loaded_event_end: Cell<Option<CrossProcessInstant>>,
363 pub(crate) dom_complete: Cell<Option<CrossProcessInstant>>,
365 pub(crate) load_event_start: Cell<Option<CrossProcessInstant>>,
367 pub(crate) load_event_end: Cell<Option<CrossProcessInstant>>,
369 pub(crate) top_level_dom_complete: Cell<Option<CrossProcessInstant>>,
371}
372
373#[dom_struct]
375pub(crate) struct Document {
376 node: Node,
377 document_or_shadow_root: DocumentOrShadowRoot,
378 window: Dom<Window>,
379 implementation: MutNullableDom<DOMImplementation>,
380 #[ignore_malloc_size_of = "type from external crate"]
381 #[no_trace]
382 content_type: Mime,
383 last_modified: Option<String>,
384 #[no_trace]
385 encoding: Cell<&'static Encoding>,
386 has_browsing_context: bool,
387 is_html_document: bool,
388 #[no_trace]
389 activity: Cell<DocumentActivity>,
390 #[no_trace]
392 url: DomRefCell<ServoUrl>,
393 #[no_trace]
395 about_base_url: DomRefCell<Option<ServoUrl>>,
396 #[ignore_malloc_size_of = "defined in selectors"]
397 #[no_trace]
398 quirks_mode: Cell<QuirksMode>,
399 event_handler: DocumentEventHandler,
401 focus_handler: DocumentFocusHandler,
403 embedder_controls: DocumentEmbedderControls,
405 id_map: TreeOrderedIndexMap,
406 name_map: TreeOrderedIndexMap,
407 tag_map: DomRefCell<HashMapTracedValues<LocalName, Dom<HTMLCollection>, FxBuildHasher>>,
408 tagns_map: DomRefCell<HashMapTracedValues<QualName, Dom<HTMLCollection>, FxBuildHasher>>,
409 classes_map: DomRefCell<HashMapTracedValues<Vec<Atom>, Dom<HTMLCollection>>>,
410 images: MutNullableDom<HTMLCollection>,
411 embeds: MutNullableDom<HTMLCollection>,
412 links: MutNullableDom<HTMLCollection>,
413 forms: MutNullableDom<HTMLCollection>,
414 scripts: MutNullableDom<HTMLCollection>,
415 anchors: MutNullableDom<HTMLCollection>,
416 applets: MutNullableDom<HTMLCollection>,
417 iframes: RefCell<IFrameCollection>,
419 #[no_trace]
423 shared_style_locks: SharedRwLocks,
424 #[custom_trace]
426 stylesheets: DomRefCell<DocumentStylesheetSet<ServoStylesheetInDocument>>,
427 stylesheet_list: MutNullableDom<StyleSheetList>,
428 ready_state: Cell<DocumentReadyState>,
429 domcontentloaded_dispatched: Cell<bool>,
432 current_script: MutNullableDom<HTMLScriptElement>,
434 #[no_trace]
435 current_the_end_loading_phase: Cell<TheEndLoadingPhase>,
436 pending_parsing_blocking_script: DomRefCell<Option<PendingScript>>,
438 script_blocking_stylesheet_set: DomRefCell<IndexSet<StylesheetContextId>>,
441 render_blocking_element_count: Cell<u32>,
444 deferred_scripts: PendingInOrderScriptVec,
446 asap_in_order_scripts_list: PendingInOrderScriptVec,
448 asap_scripts_set: DomRefCell<Vec<Dom<HTMLScriptElement>>>,
450 animation_frame_ident: Cell<u32>,
453 animation_frame_list: DomRefCell<VecDeque<(u32, Option<AnimationFrameCallback>)>>,
456 running_animation_callbacks: Cell<bool>,
461 loader: DomRefCell<DocumentLoader>,
463 current_parser: MutNullableDom<ServoParser>,
465 base_element: MutNullableDom<HTMLBaseElement>,
467 target_base_element: MutNullableDom<HTMLBaseElement>,
469 appropriate_template_contents_owner_document: MutNullableDom<Document>,
472 pending_restyles: DomRefCell<FxHashMap<Dom<Element>, NoTrace<PendingRestyle>>>,
475 #[no_trace]
479 needs_restyle: Cell<RestyleReason>,
480 #[no_trace]
482 origin: DomRefCell<MutableOrigin>,
483 referrer: Option<String>,
485 target_element: MutNullableDom<Element>,
487 #[no_trace]
489 policy_container: DomRefCell<PolicyContainer>,
490 #[no_trace]
492 preloaded_resources: DomRefCell<PreloadedResources>,
493 ignore_destructive_writes_counter: Cell<u32>,
495 ignore_opens_during_unload_counter: Cell<u32>,
497 spurious_animation_frames: Cell<u8>,
501
502 fullscreen_element: MutNullableDom<Element>,
504 form_id_listener_map:
511 DomRefCell<HashMapTracedValues<Atom, HashSet<Dom<Element>>, FxBuildHasher>>,
512 #[no_trace]
513 interactive_time: DomRefCell<ProgressiveWebMetrics>,
514 #[no_trace]
515 tti_window: DomRefCell<InteractiveWindow>,
516 canceller: FetchCanceller,
518 throw_on_dynamic_markup_insertion_counter: Cell<u64>,
520 page_showing: Cell<bool>,
522 salvageable: Cell<bool>,
524 active_parser_was_aborted: Cell<bool>,
526 fired_unload: Cell<bool>,
528 responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>,
530
531 #[no_trace]
534 #[conditional_malloc_size_of]
535 navigation_timing: Rc<NavigationTiming>,
536
537 #[no_trace]
539 resource_fetch_timing: RefCell<Option<ResourceFetchTiming>>,
540
541 script_and_layout_blockers: Cell<u32>,
543 #[ignore_malloc_size_of = "Measuring trait objects is hard"]
545 delayed_tasks: DomRefCell<Vec<Box<dyn NonSendTaskBox>>>,
546 completely_loaded: Cell<bool>,
548 shadow_roots: DomRefCell<HashSet<Dom<ShadowRoot>>>,
550 shadow_roots_styles_changed: Cell<bool>,
552 media_controls: DomRefCell<HashMap<String, Dom<ShadowRoot>>>,
558 dirty_canvases: DomRefCell<Vec<Dom<HTMLCanvasElement>>>,
561 has_pending_animated_image_update: Cell<bool>,
563 selection: MutNullableDom<Selection>,
565 timeline: Dom<DocumentTimeline>,
568 animations: Animations,
570 image_animation_manager: DomRefCell<ImageAnimationManager>,
572 dirty_root: MutNullableDom<Element>,
574 declarative_refresh: DomRefCell<Option<DeclarativeRefresh>>,
576 resize_observers: DomRefCell<Vec<Dom<ResizeObserver>>>,
585 fonts: MutNullableDom<FontFaceSet>,
588 visibility_state: Cell<DocumentVisibilityState>,
590 status_code: Option<u16>,
592 is_initial_about_blank: Cell<bool>,
594 allow_declarative_shadow_roots: Cell<bool>,
596 #[no_trace]
598 inherited_insecure_requests_policy: Cell<Option<InsecureRequestsPolicy>>,
599 has_trustworthy_ancestor_origin: Cell<bool>,
601 intersection_observer_task_queued: Cell<bool>,
603 intersection_observers: DomRefCell<Vec<Dom<IntersectionObserver>>>,
615 highlighted_dom_node: MutNullableDom<Node>,
617 lcp_candidates: DomRefCell<HashMapTracedValues<LCPCandidateID, Dom<Element>>>,
619 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
622 #[ignore_malloc_size_of = "mozjs"]
624 adopted_stylesheets_frozen_types: CachedFrozenArray,
625 pending_scroll_events: DomRefCell<Vec<PendingScrollEvent>>,
629 rendering_update_reasons: Cell<RenderingUpdateReason>,
631 waiting_on_canvas_image_updates: Cell<bool>,
635 root_removal_noted: Cell<bool>,
637 #[no_trace]
645 current_rendering_epoch: Cell<Epoch>,
646 #[conditional_malloc_size_of]
648 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
649 #[no_trace]
650 active_sandboxing_flag_set: Cell<SandboxingFlagSet>,
652 #[no_trace]
653 creation_sandboxing_flag_set: Cell<SandboxingFlagSet>,
660 #[no_trace]
662 favicon: RefCell<Option<Image>>,
663
664 websockets: DOMTracker<WebSocket>,
666
667 details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
669
670 #[no_trace]
672 protocol_handler_automation_mode: RefCell<CustomHandlersAutomationMode>,
673
674 layout_animations_test_enabled: bool,
676
677 #[no_trace]
679 state_override: DomRefCell<FxHashMap<CommandName, bool>>,
680
681 #[no_trace]
683 value_override: DomRefCell<FxHashMap<CommandName, DOMString>>,
684
685 #[no_trace]
687 default_single_line_container_name: Cell<DefaultSingleLineContainerName>,
688
689 css_styling_flag: Cell<bool>,
691
692 accessibility_data: DomRefCell<AccessibilityData>,
694
695 iframe_load_in_progress: Cell<bool>,
697 mute_iframe_load: Cell<bool>,
699
700 timers: OneshotTimers,
703
704 #[no_trace]
705 pipeline_id: PipelineId,
706
707 #[conditional_malloc_size_of]
709 task_manager: Rc<TaskManager>,
710
711 #[ignore_malloc_size_of = "ImageCache"]
712 #[no_trace]
713 image_cache: StdArc<dyn ImageCache>,
714
715 history: MutNullableDom<History>,
717}
718
719impl Document {
720 pub(crate) fn history(&self, cx: &mut JSContext) -> DomRoot<History> {
721 self.history.or_init(|| History::new(cx, &self.window))
722 }
723
724 pub(crate) fn image_cache(&self) -> StdArc<dyn ImageCache> {
725 self.image_cache.clone()
726 }
727
728 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
729 self.task_manager.clone()
730 }
731
732 pub(crate) fn timers(&self) -> &OneshotTimers {
733 &self.timers
734 }
735
736 pub(crate) fn pipeline_id(&self) -> PipelineId {
737 self.pipeline_id
738 }
739
740 fn unloading_cleanup_steps(&self) {
742 if self.close_outstanding_websockets() {
745 self.salvageable.set(false);
747 }
748
749 if !self.salvageable.get() {
754 let global_scope = self.window.as_global_scope();
755
756 global_scope.close_event_sources();
758
759 let msg = ScriptToConstellationMessage::DiscardDocument;
764 let _ = global_scope.script_to_constellation_chan().send(msg);
765 }
766 }
767
768 pub(crate) fn track_websocket(&self, websocket: &WebSocket) {
769 self.websockets.track(websocket);
770 }
771
772 fn close_outstanding_websockets(&self) -> bool {
773 let mut closed_any_websocket = false;
774 self.websockets.for_each(|websocket: DomRoot<WebSocket>| {
775 if websocket.make_disappear() {
776 closed_any_websocket = true;
777 }
778 });
779 closed_any_websocket
780 }
781
782 fn document_element_changed(&self) {
783 if self.GetDocumentElement().is_some() {
784 self.root_removal_noted.set(false);
787 } else if !self.root_removal_noted.get() {
788 self.add_restyle_reason(RestyleReason::DOMChanged);
791 self.root_removal_noted.set(true);
792 }
793 }
794
795 pub(crate) fn note_dirty_element(&self, element: &Element) {
810 let node = element.upcast::<Node>();
811
812 debug_assert!(*node.owner_doc() == *self);
813 if !node.is_connected() {
814 return;
815 }
816
817 let parent_element = match node.parent_in_flat_tree() {
818 FlatTreeParent::Parent(parent) => DomRoot::downcast::<Element>(parent),
819 FlatTreeParent::NotInFlatTree | FlatTreeParent::RootNode => return,
820 };
821
822 if let Some(parent_element) = parent_element {
826 if !parent_element.is_styled() {
829 return;
830 }
831 if parent_element.is_display_none() {
834 return;
835 }
836 }
837
838 let Some(old_dirty_root) = self.dirty_root.get() else {
839 node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
840 self.set_dirty_root(Some(element));
841 return;
842 };
843
844 let old_dirty_root_node = old_dirty_root.upcast::<Node>();
845 for ancestor in element.upcast::<Node>().inclusive_ancestors_in_flat_tree() {
846 if !ancestor.is::<Element>() {
848 break;
849 }
850
851 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
852 return;
853 }
854
855 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
856
857 if old_dirty_root_node == &*ancestor {
861 return;
862 }
863 }
864
865 let common_element_ancestor = old_dirty_root_node
866 .inclusive_ancestors_in_flat_tree()
867 .skip(1) .find_map(|ancestor| {
869 let element = ancestor.downcast::<Element>().map(DomRoot::from_ref)?;
871 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
872 return Some(element);
873 }
874 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
875 None
876 });
877
878 let Some(new_dirty_root) = common_element_ancestor else {
882 let new_dirty_root = self.GetDocumentElement();
883 if let Some(new_dirty_root) = new_dirty_root.as_ref() {
884 new_dirty_root
885 .upcast::<Node>()
886 .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
887 }
888 self.set_dirty_root(new_dirty_root.as_deref());
889 return;
890 };
891
892 for ancestor in new_dirty_root
896 .upcast::<Node>()
897 .inclusive_ancestors_in_flat_tree()
898 .skip(1)
899 {
900 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
901 }
902
903 self.set_dirty_root(Some(&*new_dirty_root));
904 }
905
906 fn set_dirty_root(&self, new_dirty_root: Option<&Element>) {
907 debug_assert!(new_dirty_root.as_ref().is_none_or(|new_dirty_root| {
909 new_dirty_root
910 .upcast::<Node>()
911 .inclusive_ancestors_in_flat_tree()
912 .skip(1)
913 .all(|node| !node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS))
914 }));
915 self.dirty_root.set(new_dirty_root);
916 }
917
918 pub(crate) fn take_dirty_root(&self) -> Option<DomRoot<Element>> {
919 self.dirty_root.take()
920 }
921
922 #[inline]
923 pub(crate) fn loader(&self) -> Ref<'_, DocumentLoader> {
924 self.loader.borrow()
925 }
926
927 #[inline]
928 pub(crate) fn loader_mut(&self) -> RefMut<'_, DocumentLoader> {
929 self.loader.borrow_mut()
930 }
931
932 #[inline]
933 pub(crate) fn has_browsing_context(&self) -> bool {
934 self.has_browsing_context
935 }
936
937 #[inline]
939 pub(crate) fn browsing_context(&self) -> Option<DomRoot<WindowProxy>> {
940 if self.has_browsing_context {
941 self.window.undiscarded_window_proxy()
942 } else {
943 None
944 }
945 }
946
947 pub(crate) fn webview_id(&self) -> WebViewId {
948 self.window.webview_id()
949 }
950
951 #[inline]
952 pub(crate) fn window(&self) -> &Window {
953 &self.window
954 }
955
956 #[inline]
957 pub(crate) fn is_html_document(&self) -> bool {
958 self.is_html_document
959 }
960
961 pub(crate) fn is_xhtml_document(&self) -> bool {
962 self.content_type.matches(APPLICATION, "xhtml+xml")
963 }
964
965 pub(crate) fn is_fully_active(&self) -> bool {
966 self.activity.get() == DocumentActivity::FullyActive
967 }
968
969 pub(crate) fn is_active(&self) -> bool {
970 self.activity.get() != DocumentActivity::Inactive
971 }
972
973 #[inline]
974 pub(crate) fn current_rendering_epoch(&self) -> Epoch {
975 self.current_rendering_epoch.get()
976 }
977
978 #[inline]
980 pub(crate) fn selection(&self) -> Option<DomRoot<Selection>> {
981 self.selection.get()
982 }
983
984 pub(crate) fn set_activity(&self, cx: &mut JSContext, activity: DocumentActivity) {
985 assert!(self.has_browsing_context);
987 if activity == self.activity.get() {
988 return;
989 }
990
991 self.activity.set(activity);
993 let media = ServoMedia::get();
994 let pipeline_id = self.window().pipeline_id();
995 let client_context_id =
996 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
997
998 if activity != DocumentActivity::FullyActive {
999 self.window().suspend(cx);
1000 media.suspend(&client_context_id);
1001 return;
1002 }
1003
1004 self.title_changed();
1005 self.notify_embedder_favicon();
1006 self.dirty_all_nodes(cx.no_gc());
1007 self.window().resume(cx);
1008 media.resume(&client_context_id);
1009
1010 if self.ready_state.get() != DocumentReadyState::Complete {
1011 return;
1012 }
1013
1014 let document = Trusted::new(self);
1018 self.owner_global()
1019 .task_manager()
1020 .dom_manipulation_task_source()
1021 .queue(task!(fire_pageshow_event: move |cx| {
1022 let document = document.root();
1023 let window = document.window();
1024 if document.page_showing.get() {
1026 return;
1027 }
1028 document.page_showing.set(true);
1030 document.update_visibility_state(cx, DocumentVisibilityState::Visible);
1032 let event = PageTransitionEvent::new(
1035 cx,
1036 window,
1037 atom!("pageshow"),
1038 false, false, true, );
1042 let event = event.upcast::<Event>();
1043 event.set_trusted(true);
1044 window.dispatch_event_with_target_override(cx, event);
1045 }))
1046 }
1047
1048 pub(crate) fn origin(&self) -> Ref<'_, MutableOrigin> {
1049 self.origin.borrow()
1050 }
1051
1052 pub(crate) fn mark_as_internal(&self) {
1055 *self.origin.borrow_mut() = MutableOrigin::new(ImmutableOrigin::new_opaque());
1056 }
1057
1058 pub(crate) fn set_protocol_handler_automation_mode(&self, mode: CustomHandlersAutomationMode) {
1059 *self.protocol_handler_automation_mode.borrow_mut() = mode;
1060 }
1061
1062 pub(crate) fn url(&self) -> ServoUrl {
1064 self.url.borrow().clone()
1065 }
1066
1067 pub(crate) fn set_url(&self, url: ServoUrl) {
1068 *self.url.borrow_mut() = url;
1069 }
1070
1071 pub(crate) fn about_base_url(&self) -> Option<ServoUrl> {
1072 self.about_base_url.borrow().clone()
1073 }
1074
1075 pub(crate) fn set_about_base_url(&self, about_base_url: Option<ServoUrl>) {
1076 *self.about_base_url.borrow_mut() = about_base_url;
1077 }
1078
1079 pub(crate) fn fallback_base_url(&self) -> ServoUrl {
1081 let document_url = self.url();
1082 if document_url.as_str() == "about:srcdoc" {
1084 return self
1087 .about_base_url()
1088 .expect("about:srcdoc page should always have an about base URL");
1089 }
1090
1091 if document_url.matches_about_blank() &&
1094 let Some(about_base_url) = self.about_base_url()
1095 {
1096 return about_base_url;
1097 }
1098
1099 document_url
1101 }
1102
1103 pub(crate) fn base_url(&self) -> ServoUrl {
1105 match self.base_element() {
1106 None => self.fallback_base_url(),
1108 Some(base) => base.frozen_base_url(),
1110 }
1111 }
1112
1113 pub(crate) fn add_restyle_reason(&self, reason: RestyleReason) {
1114 self.needs_restyle.set(self.needs_restyle.get() | reason)
1115 }
1116
1117 pub(crate) fn clear_restyle_reasons(&self) {
1118 self.needs_restyle.set(RestyleReason::empty());
1119 }
1120
1121 pub(crate) fn stylesheets_changed_since_last_reflow(&self) -> bool {
1122 self.stylesheets.borrow().has_changed()
1123 }
1124
1125 pub(crate) fn restyle_reason(&self, no_gc: &NoGC) -> RestyleReason {
1126 let mut condition = self.needs_restyle.get();
1127 if self.stylesheets_changed_since_last_reflow() {
1128 condition.insert(RestyleReason::StylesheetsChanged);
1129 }
1130
1131 if let Some(root) = self.get_document_element_unrooted(no_gc) &&
1135 root.has_dirty_descendants()
1136 {
1137 condition.insert(RestyleReason::DOMChanged);
1138 }
1139
1140 if !self.pending_restyles.borrow().is_empty() {
1141 condition.insert(RestyleReason::PendingRestyles);
1142 }
1143
1144 condition
1145 }
1146
1147 pub(crate) fn base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1149 self.base_element.get()
1150 }
1151
1152 pub(crate) fn target_base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1154 self.target_base_element.get()
1155 }
1156
1157 pub(crate) fn refresh_base_element(&self, cx: &mut JSContext) {
1159 if let Some(base_element) = self.base_element.get() {
1160 base_element.clear_frozen_base_url();
1161 }
1162 let new_base_element = self
1163 .upcast::<Node>()
1164 .traverse_preorder(ShadowIncluding::No)
1165 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1166 .find(|element| {
1167 element
1168 .upcast::<Element>()
1169 .has_attribute(&local_name!("href"))
1170 });
1171 if let Some(ref new_base_element) = new_base_element {
1172 new_base_element.set_frozen_base_url(cx);
1173 }
1174 self.base_element.set(new_base_element.as_deref());
1175
1176 let new_target_base_element = self
1177 .upcast::<Node>()
1178 .traverse_preorder(ShadowIncluding::No)
1179 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1180 .next();
1181 self.target_base_element
1182 .set(new_target_base_element.as_deref());
1183 }
1184
1185 pub(crate) fn quirks_mode(&self) -> QuirksMode {
1186 self.quirks_mode.get()
1187 }
1188
1189 pub(crate) fn set_quirks_mode(&self, new_mode: QuirksMode) {
1190 let old_mode = self.quirks_mode.replace(new_mode);
1191
1192 if old_mode != new_mode {
1193 self.window.layout_mut().set_quirks_mode(new_mode);
1194 }
1195 }
1196
1197 pub(crate) fn encoding(&self) -> &'static Encoding {
1198 self.encoding.get()
1199 }
1200
1201 pub(crate) fn set_encoding(&self, encoding: &'static Encoding) {
1202 self.encoding.set(encoding);
1203 }
1204
1205 pub(crate) fn content_and_heritage_changed(&self, no_gc: &NoGC, node: &Node) {
1206 if node.is::<Document>() {
1207 self.document_element_changed();
1208 }
1209
1210 node.dirty(no_gc, NodeDamage::ContentOrHeritage);
1215 }
1216
1217 pub(crate) fn unregister_element_id(&self, cx: &mut JSContext, id: &Atom) {
1219 self.id_map.remove(id);
1220 self.reset_form_owner_for_listeners(cx, id);
1221 }
1222
1223 pub(crate) fn register_element_id(&self, cx: &mut JSContext, element: &Element, id: &Atom) {
1225 self.id_map.add(id, element);
1226 self.reset_form_owner_for_listeners(cx, id);
1227 }
1228
1229 pub(crate) fn unregister_element_name(&self, name: &Atom) {
1231 self.name_map.remove(name);
1232 }
1233
1234 pub(crate) fn register_element_name(&self, element: &Element, name: &Atom) {
1236 self.name_map.add(name, element);
1237 }
1238
1239 pub(crate) fn register_form_id_listener<T: ?Sized + FormControl>(
1240 &self,
1241 id: DOMString,
1242 listener: &T,
1243 ) {
1244 let mut map = self.form_id_listener_map.borrow_mut();
1245 let listener = listener.to_element();
1246 let set = map.entry(Atom::from(id)).or_default();
1247 set.insert(Dom::from_ref(listener));
1248 }
1249
1250 pub(crate) fn unregister_form_id_listener<T: ?Sized + FormControl>(
1251 &self,
1252 id: DOMString,
1253 listener: &T,
1254 ) {
1255 let mut map = self.form_id_listener_map.borrow_mut();
1256 if let Occupied(mut entry) = map.entry(Atom::from(id)) {
1257 entry
1258 .get_mut()
1259 .remove(&Dom::from_ref(listener.to_element()));
1260 if entry.get().is_empty() {
1261 entry.remove();
1262 }
1263 }
1264 }
1265
1266 fn find_a_potential_indicated_element(
1268 &self,
1269 cx: &mut JSContext,
1270 fragment: &str,
1271 ) -> Option<DomRoot<Element>> {
1272 self.get_element_by_id(cx.no_gc(), &Atom::from(fragment))
1276 .or_else(|| self.get_anchor_by_name(cx, fragment))
1280 }
1281
1282 fn select_indicated_part(&self, cx: &mut JSContext, fragment: &str) -> Option<DomRoot<Node>> {
1285 if fragment.is_empty() {
1295 return Some(DomRoot::from_ref(self.upcast()));
1296 }
1297 if let Some(potential_indicated_element) =
1299 self.find_a_potential_indicated_element(cx, fragment)
1300 {
1301 return Some(DomRoot::upcast(potential_indicated_element));
1303 }
1304 let fragment_bytes = percent_decode(fragment.as_bytes());
1306 let Ok(decoded_fragment) = fragment_bytes.decode_utf8() else {
1308 return None;
1309 };
1310 if let Some(potential_indicated_element) =
1312 self.find_a_potential_indicated_element(cx, &decoded_fragment)
1313 {
1314 return Some(DomRoot::upcast(potential_indicated_element));
1316 }
1317 if decoded_fragment.eq_ignore_ascii_case("top") {
1319 return Some(DomRoot::from_ref(self.upcast()));
1320 }
1321 None
1323 }
1324
1325 pub(crate) fn scroll_to_the_fragment(&self, cx: &mut JSContext, fragment: &str) {
1327 let Some(indicated_part) = self.select_indicated_part(cx, fragment) else {
1332 self.set_target_element(None);
1333 return;
1334 };
1335 if *indicated_part == *self.upcast() {
1337 self.set_target_element(None);
1339 self.window.scroll(cx, 0.0, 0.0, ScrollBehavior::Instant);
1344 return;
1346 }
1347 let Some(target) = indicated_part.downcast::<Element>() else {
1350 unreachable!("Indicated part should always be an element");
1352 };
1353 self.set_target_element(Some(target));
1355 target.scroll_into_view_with_options(
1359 cx,
1360 ScrollBehavior::Auto,
1361 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Start),
1362 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Nearest),
1363 None,
1364 None,
1365 );
1366
1367 indicated_part.run_the_focusing_steps(cx, Some(FocusableArea::Viewport));
1370
1371 self.focus_handler()
1373 .set_sequential_focus_navigation_starting_point(target.upcast());
1374 }
1375
1376 fn get_anchor_by_name(&self, cx: &mut JSContext, name: &str) -> Option<DomRoot<Element>> {
1377 let document_element = self.GetDocumentElement()?;
1378 self.name_map
1379 .get_all(cx.no_gc(), document_element.upcast(), &Atom::from(name))
1380 .iter()
1381 .find(|element| element.is::<HTMLAnchorElement>())
1382 .map(|element| DomRoot::from_ref(&**element))
1383 }
1384
1385 pub(crate) fn set_ready_state(&self, cx: &mut JSContext, state: DocumentReadyState) {
1387 match state {
1388 DocumentReadyState::Loading => {
1389 if self.window().is_top_level() {
1390 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1391 self.webview_id(),
1392 LoadStatus::Started,
1393 ));
1394 self.send_to_embedder(EmbedderMsg::Status(self.webview_id(), None));
1395 update_with_current_instant(&self.navigation_timing.dom_loading);
1396 }
1397 },
1398 DocumentReadyState::Complete => {
1399 if self.window().is_top_level() {
1400 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1401 self.webview_id(),
1402 LoadStatus::Complete,
1403 ));
1404 }
1405 update_with_current_instant(&self.navigation_timing.dom_complete);
1406 },
1407 DocumentReadyState::Interactive => {
1408 update_with_current_instant(&self.navigation_timing.dom_interactive)
1409 },
1410 };
1411
1412 self.ready_state.set(state);
1413
1414 self.upcast::<EventTarget>()
1415 .fire_event(cx, atom!("readystatechange"));
1416 }
1417
1418 pub(crate) fn scripting_enabled(&self) -> bool {
1421 self.has_browsing_context() &&
1424 !self.has_active_sandboxing_flag(
1428 SandboxingFlagSet::SANDBOXED_SCRIPTS_BROWSING_CONTEXT_FLAG,
1429 )
1430 }
1431
1432 pub(crate) fn title_changed(&self) {
1434 if self.browsing_context().is_some() {
1435 self.send_title_to_embedder();
1436 let title = String::from(self.Title());
1437 self.window
1438 .send_to_constellation(ScriptToConstellationMessage::TitleChanged(
1439 self.window.pipeline_id(),
1440 title.clone(),
1441 ));
1442 if let Some(chan) = self.window.as_global_scope().devtools_chan() {
1443 let _ = chan.send(ScriptToDevtoolsControlMsg::TitleChanged(
1444 self.window.pipeline_id(),
1445 title,
1446 ));
1447 }
1448 }
1449 }
1450
1451 fn title(&self) -> Option<DOMString> {
1455 let title = self.GetDocumentElement().and_then(|root| {
1456 if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
1457 root.upcast::<Node>()
1459 .child_elements()
1460 .find(|node| {
1461 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
1462 })
1463 .map(DomRoot::upcast::<Node>)
1464 } else {
1465 root.upcast::<Node>()
1467 .traverse_preorder(ShadowIncluding::No)
1468 .find(|node| node.is::<HTMLTitleElement>())
1469 }
1470 });
1471
1472 title.map(|title| {
1473 let value = title.child_text_content();
1475 DOMString::from(str_join(value.str().split_html_space_characters(), " "))
1476 })
1477 }
1478
1479 pub(crate) fn send_title_to_embedder(&self) {
1481 let window = self.window();
1482 if window.is_top_level() {
1483 let title = self.title().map(String::from);
1484 self.send_to_embedder(EmbedderMsg::ChangePageTitle(self.webview_id(), title));
1485 }
1486 }
1487
1488 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
1489 let window = self.window();
1490 window.send_to_embedder(msg);
1491 }
1492
1493 pub(crate) fn dirty_all_nodes(&self, no_gc: &NoGC) {
1494 let root = match self.GetDocumentElement() {
1495 Some(root) => root,
1496 None => return,
1497 };
1498 for node in root
1499 .upcast::<Node>()
1500 .traverse_preorder_non_rooting(no_gc, ShadowIncluding::Yes)
1501 {
1502 node.dirty(no_gc, NodeDamage::Other)
1503 }
1504 }
1505
1506 pub(crate) fn run_the_scroll_steps(&self, cx: &mut JSContext) {
1508 let boxes_that_were_scrolled: Vec<_> = self
1516 .pending_scroll_events
1517 .borrow()
1518 .iter()
1519 .filter_map(|pending_event| {
1520 if &*pending_event.event == "scroll" {
1521 Some(pending_event.target.as_rooted())
1522 } else {
1523 None
1524 }
1525 })
1526 .collect();
1527
1528 for target in boxes_that_were_scrolled.into_iter() {
1529 let Some(element) = target.downcast::<Element>() else {
1535 continue;
1536 };
1537 let document = element.owner_document();
1538
1539 let mut pending_scroll_events = document.pending_scroll_events.borrow_mut();
1546 let event = "scrollend".into();
1547 if pending_scroll_events
1548 .iter()
1549 .any(|existing| existing.equivalent(&target, &event))
1550 {
1551 continue;
1552 }
1553
1554 pending_scroll_events.push(PendingScrollEvent {
1556 target: target.as_traced(),
1557 event: "scrollend".into(),
1558 });
1559 }
1560
1561 rooted_vec!(let pending_scroll_events <- self.pending_scroll_events.take().into_iter());
1564 for pending_event in pending_scroll_events.iter() {
1565 let event = pending_event.event.clone();
1568 if pending_event.target.is::<Document>() {
1569 pending_event.target.fire_bubbling_event(cx, event);
1570 }
1571 else {
1580 pending_event.target.fire_event(cx, event);
1581 }
1582 }
1583
1584 }
1587
1588 pub(crate) fn handle_viewport_scroll_event(&self) {
1593 self.finish_handle_scroll_event(self.upcast());
1605 }
1606
1607 pub(crate) fn finish_handle_scroll_event(&self, event_target: &EventTarget) {
1612 let event = "scroll".into();
1615 if self
1616 .pending_scroll_events
1617 .borrow()
1618 .iter()
1619 .any(|existing| existing.equivalent(event_target, &event))
1620 {
1621 return;
1622 }
1623
1624 self.pending_scroll_events
1627 .borrow_mut()
1628 .push(PendingScrollEvent {
1629 target: Dom::from_ref(event_target),
1630 event: "scroll".into(),
1631 });
1632 }
1633
1634 pub(crate) fn node_from_nodes_and_strings(
1636 &self,
1637 cx: &mut JSContext,
1638 mut nodes: Vec<NodeOrString>,
1639 ) -> Fallible<DomRoot<Node>> {
1640 if nodes.len() == 1 {
1641 Ok(match nodes.pop().unwrap() {
1642 NodeOrString::Node(node) => node,
1643 NodeOrString::String(string) => DomRoot::upcast(self.CreateTextNode(cx, string)),
1644 })
1645 } else {
1646 let fragment = DomRoot::upcast::<Node>(self.CreateDocumentFragment(cx));
1647 for node in nodes {
1648 match node {
1649 NodeOrString::Node(node) => {
1650 fragment.AppendChild(cx, &node)?;
1651 },
1652 NodeOrString::String(string) => {
1653 let node = DomRoot::upcast::<Node>(self.CreateTextNode(cx, string));
1654 fragment.AppendChild(cx, &node).unwrap();
1657 },
1658 }
1659 }
1660 Ok(fragment)
1661 }
1662 }
1663
1664 pub(crate) fn get_body_attribute(&self, local_name: &LocalName) -> DOMString {
1665 match self.GetBody() {
1666 Some(ref body) if body.is_body_element() => {
1667 body.upcast::<Element>().get_string_attribute(local_name)
1668 },
1669 _ => DOMString::new(),
1670 }
1671 }
1672
1673 pub(crate) fn set_body_attribute(
1674 &self,
1675 cx: &mut JSContext,
1676 local_name: &LocalName,
1677 value: DOMString,
1678 ) {
1679 if let Some(ref body) = self.GetBody().filter(|elem| elem.is_body_element()) {
1680 let body = body.upcast::<Element>();
1681 let value = body.parse_attribute(&ns!(), local_name, value);
1682 body.set_attribute(cx, local_name, value);
1683 }
1684 }
1685
1686 pub(crate) fn set_current_script(&self, script: Option<&HTMLScriptElement>) {
1687 self.current_script.set(script);
1688 }
1689
1690 pub(crate) fn has_a_stylesheet_that_is_blocking_scripts(&self) -> bool {
1692 !self.script_blocking_stylesheet_set.borrow().is_empty()
1693 }
1694
1695 pub(crate) fn add_script_blocking_stylesheet(&self, id: StylesheetContextId) {
1696 self.script_blocking_stylesheet_set.borrow_mut().insert(id);
1697 }
1698
1699 pub(crate) fn remove_script_blocking_stylesheet(&self, id: StylesheetContextId) {
1700 self.script_blocking_stylesheet_set
1701 .borrow_mut()
1702 .shift_remove(&id);
1703 }
1704
1705 pub(crate) fn render_blocking_element_count(&self) -> u32 {
1706 self.render_blocking_element_count.get()
1707 }
1708
1709 pub(crate) fn increment_render_blocking_element_count(&self) {
1711 assert!(self.allows_adding_render_blocking_elements());
1718 let count_cell = &self.render_blocking_element_count;
1719 count_cell.set(count_cell.get() + 1);
1720 }
1721
1722 pub(crate) fn decrement_render_blocking_element_count(&self) {
1724 let count_cell = &self.render_blocking_element_count;
1730 assert!(count_cell.get() > 0);
1731 count_cell.set(count_cell.get() - 1);
1732 }
1733
1734 pub(crate) fn allows_adding_render_blocking_elements(&self) -> bool {
1736 self.is_html_document && self.GetBody().is_none()
1739 }
1740
1741 pub(crate) fn is_render_blocked(&self) -> bool {
1743 self.render_blocking_element_count() > 0
1747 }
1752
1753 pub(crate) fn invalidate_stylesheets(&self, no_gc: &NoGC) {
1754 self.stylesheets.borrow_mut().force_dirty(OriginSet::all());
1755
1756 if let Some(element) = self.GetDocumentElement() {
1760 element.upcast::<Node>().dirty(no_gc, NodeDamage::Style);
1761 }
1762 }
1763
1764 pub(crate) fn has_active_request_animation_frame_callbacks(&self) -> bool {
1767 !self.animation_frame_list.borrow().is_empty()
1768 }
1769
1770 pub(crate) fn request_animation_frame(&self, callback: AnimationFrameCallback) -> u32 {
1772 let ident = self.animation_frame_ident.get() + 1;
1773 self.animation_frame_ident.set(ident);
1774
1775 let had_animation_frame_callbacks;
1776 {
1777 let mut animation_frame_list = self.animation_frame_list.borrow_mut();
1778 had_animation_frame_callbacks = !animation_frame_list.is_empty();
1779 animation_frame_list.push_back((ident, Some(callback)));
1780 }
1781
1782 if !self.running_animation_callbacks.get() && !had_animation_frame_callbacks {
1788 self.window().send_to_constellation(
1789 ScriptToConstellationMessage::ChangeRunningAnimationsState(
1790 AnimationState::AnimationCallbacksPresent,
1791 ),
1792 );
1793 }
1794
1795 ident
1796 }
1797
1798 pub(crate) fn cancel_animation_frame(&self, ident: u32) {
1800 let mut list = self.animation_frame_list.borrow_mut();
1801 if let Some(pair) = list.iter_mut().find(|pair| pair.0 == ident) {
1802 pair.1 = None;
1803 }
1804 }
1805
1806 pub(crate) fn run_the_animation_frame_callbacks(&self, cx: &mut CurrentRealm) {
1808 self.running_animation_callbacks.set(true);
1809 let timing = self.global().performance(cx).Now();
1810
1811 let num_callbacks = self.animation_frame_list.borrow().len();
1812 for _ in 0..num_callbacks {
1813 let (_, maybe_callback) = self.animation_frame_list.borrow_mut().pop_front().unwrap();
1814 if let Some(callback) = maybe_callback {
1815 callback.call(cx, self, *timing);
1816 }
1817 }
1818 self.running_animation_callbacks.set(false);
1819
1820 if self.animation_frame_list.borrow().is_empty() {
1821 self.window().send_to_constellation(
1822 ScriptToConstellationMessage::ChangeRunningAnimationsState(
1823 AnimationState::AnimationCallbacksAbsent,
1824 ),
1825 );
1826 }
1827 }
1828
1829 pub(crate) fn policy_container(&self) -> Ref<'_, PolicyContainer> {
1830 self.policy_container.borrow()
1831 }
1832
1833 pub(crate) fn set_policy_container(&self, policy_container: PolicyContainer) {
1834 *self.policy_container.borrow_mut() = policy_container;
1835 }
1836
1837 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
1838 self.policy_container.borrow_mut().set_csp_list(csp_list);
1839 }
1840
1841 pub(crate) fn enforce_csp_policy(&self, policy: CspPolicy) {
1843 let mut csp_list = self.get_csp_list().clone().unwrap_or(CspList(vec![]));
1845 csp_list.push(policy);
1846 self.policy_container
1847 .borrow_mut()
1848 .set_csp_list(Some(csp_list));
1849 }
1850
1851 pub(crate) fn get_csp_list(&self) -> Ref<'_, Option<CspList>> {
1852 Ref::map(self.policy_container.borrow(), |policy_container| {
1853 &policy_container.csp_list
1854 })
1855 }
1856
1857 pub(crate) fn preloaded_resources(&self) -> std::cell::Ref<'_, PreloadedResources> {
1858 self.preloaded_resources.borrow()
1859 }
1860
1861 pub(crate) fn insert_preloaded_resource(&self, key: PreloadKey, preload_id: PreloadId) {
1862 self.preloaded_resources
1863 .borrow_mut()
1864 .insert(key, preload_id);
1865 }
1866
1867 pub(crate) fn fetch<Listener: FetchResponseListener>(
1868 &self,
1869 load: LoadType,
1870 request: RequestBuilder,
1871 listener: Listener,
1872 ) {
1873 let callback = NetworkListener {
1874 context: std::sync::Arc::new(Mutex::new(Some(listener))),
1875 task_source: self
1876 .owner_global()
1877 .task_manager()
1878 .networking_task_source()
1879 .into(),
1880 }
1881 .into_callback();
1882 self.loader_mut()
1883 .fetch_async_with_callback(load, request, callback);
1884 }
1885
1886 pub(crate) fn fetch_background<Listener: FetchResponseListener>(
1887 &self,
1888 request: RequestBuilder,
1889 listener: Listener,
1890 ) {
1891 let callback = NetworkListener {
1892 context: std::sync::Arc::new(Mutex::new(Some(listener))),
1893 task_source: self
1894 .owner_global()
1895 .task_manager()
1896 .networking_task_source()
1897 .into(),
1898 }
1899 .into_callback();
1900 self.loader_mut().fetch_async_background(request, callback);
1901 }
1902
1903 fn deferred_fetch_control_document(&self) -> DomRoot<Document> {
1905 match self.window().window_proxy().frame_element() {
1906 None => DomRoot::from_ref(self),
1909 Some(container) => container.owner_document().deferred_fetch_control_document(),
1911 }
1912 }
1913
1914 pub(crate) fn available_deferred_fetch_quota(&self, origin: ImmutableOrigin) -> isize {
1916 let control_document = self.deferred_fetch_control_document();
1918 let navigable = control_document.window();
1920 let is_top_level = navigable.is_top_level();
1923 let deferred_fetch_allowed = true;
1927 let deferred_fetch_minimal_allowed = true;
1931 let mut quota = match is_top_level {
1933 true if !deferred_fetch_allowed => 0,
1935 true if !deferred_fetch_minimal_allowed => 640 * 1024,
1937 true => 512 * 1024,
1939 _ if deferred_fetch_allowed => 0,
1943 _ if deferred_fetch_minimal_allowed => 8 * 1024,
1947 _ => 0,
1949 } as isize;
1950 let mut quota_for_request_origin = 64 * 1024_isize;
1952 for deferred_fetch in navigable.as_global_scope().deferred_fetches() {
1961 if deferred_fetch.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
1963 continue;
1964 }
1965 let request_length = deferred_fetch.request.total_request_length();
1967 quota -= request_length as isize;
1969 if deferred_fetch.request.url().origin() == origin {
1972 quota_for_request_origin -= request_length as isize;
1973 }
1974 }
1975 if quota <= 0 {
1977 return 0;
1978 }
1979 if quota < quota_for_request_origin {
1981 return quota;
1982 }
1983 quota_for_request_origin
1985 }
1986
1987 pub(crate) fn update_document_for_history_step_application(
1989 &self,
1990 old_url: &ServoUrl,
1991 new_url: &ServoUrl,
1992 ) {
1993 if old_url.as_url()[Position::BeforeFragment..] !=
2023 new_url.as_url()[Position::BeforeFragment..]
2024 {
2025 let window = Trusted::new(self.owner_window().deref());
2026 let old_url = old_url.to_string();
2027 let new_url = new_url.to_string();
2028 self.owner_global()
2029 .task_manager()
2030 .dom_manipulation_task_source()
2031 .queue(task!(hashchange_event: move |cx| {
2032 let window = window.root();
2033 HashChangeEvent::new(
2034 cx,
2035 &window,
2036 atom!("hashchange"),
2037 false,
2038 false,
2039 old_url,
2040 new_url,
2041 )
2042 .upcast::<Event>()
2043 .fire(cx, window.upcast());
2044 }));
2045 }
2046 }
2047
2048 pub(crate) fn finish_load_for_dropped_blocker(&self, load: LoadType) {
2049 let this = Trusted::new(self);
2050 self.owner_global()
2051 .task_manager()
2052 .dom_manipulation_task_source()
2053 .queue(task!(check_finished_load: move |cx| {
2054 this.root().finish_load(load, cx);
2055 }));
2056 }
2057
2058 pub(crate) fn finish_load(&self, load: LoadType, cx: &mut JSContext) {
2061 debug!("Document got finish_load: {:?}", load);
2063 self.loader.borrow_mut().finish_load(&load);
2064
2065 match load {
2066 LoadType::Stylesheet(_) => {
2067 self.process_pending_parsing_blocking_script(cx);
2070
2071 self.process_deferred_scripts(cx);
2073 },
2074 LoadType::PageSource(_) => {
2075 if self.has_browsing_context && self.is_fully_active() {
2078 self.window().allow_layout_if_necessary(cx);
2079 }
2080
2081 self.process_deferred_scripts(cx);
2086 },
2087 _ => {},
2088 }
2089
2090 let loader = self.loader.borrow();
2099
2100 if self
2102 .navigation_timing
2103 .top_level_dom_complete
2104 .get()
2105 .is_none() &&
2106 loader.is_only_blocked_by_iframes()
2107 {
2108 update_with_current_instant(&self.navigation_timing.top_level_dom_complete);
2109 }
2110
2111 if loader.is_blocked() || loader.events_inhibited() {
2112 return;
2114 }
2115
2116 ScriptThread::mark_document_with_no_blocked_loads(self);
2117
2118 let document = Trusted::new(self);
2122 self.owner_global()
2123 .task_manager()
2124 .dom_manipulation_task_source()
2125 .queue(task!(wait_for_load_blockers: move |cx| {
2126 document.root().wait_until_load_blockers_have_resolved(cx);
2127 }));
2128 }
2129
2130 pub(crate) fn check_if_unloading_is_cancelled(
2132 &self,
2133 cx: &mut JSContext,
2134 recursive_flag: bool,
2135 ) -> bool {
2136 self.incr_ignore_opens_during_unload_counter();
2139 let beforeunload_event = BeforeUnloadEvent::new(
2141 cx,
2142 &self.window,
2143 atom!("beforeunload"),
2144 EventBubbles::Bubbles,
2145 EventCancelable::Cancelable,
2146 );
2147 let event = beforeunload_event.upcast::<Event>();
2148 event.set_trusted(true);
2149 let event_target = self.window.upcast::<EventTarget>();
2150 let has_listeners = event_target.has_listeners_for(&atom!("beforeunload"));
2151 self.window.dispatch_event_with_target_override(cx, event);
2152 if has_listeners {
2155 self.salvageable.set(false);
2156 }
2157 let mut can_unload = true;
2158 let default_prevented = event.DefaultPrevented();
2160 let return_value_not_empty = !event
2161 .downcast::<BeforeUnloadEvent>()
2162 .unwrap()
2163 .ReturnValue()
2164 .is_empty();
2165 if default_prevented || return_value_not_empty {
2166 let (chan, port) = generic_channel::channel().expect("Failed to create IPC channel!");
2167 let msg = EmbedderMsg::AllowUnload(self.webview_id(), chan);
2168 self.send_to_embedder(msg);
2169 can_unload = port.recv().unwrap() == AllowOrDeny::Allow;
2170 }
2171 if !recursive_flag {
2173 let iframes: Vec<_> = self.iframes().iter().collect();
2176 for iframe in &iframes {
2177 let document = iframe.owner_document();
2179 can_unload = document.check_if_unloading_is_cancelled(cx, true);
2180 if !document.salvageable() {
2181 self.salvageable.set(false);
2182 }
2183 if !can_unload {
2184 break;
2185 }
2186 }
2187 }
2188 self.decr_ignore_opens_during_unload_counter();
2190 can_unload
2191 }
2192
2193 pub(crate) fn unload(&self, cx: &mut JSContext, recursive_flag: bool) {
2195 self.incr_ignore_opens_during_unload_counter();
2198 if self.page_showing.get() {
2200 self.page_showing.set(false);
2202 let event = PageTransitionEvent::new(
2205 cx,
2206 &self.window,
2207 atom!("pagehide"),
2208 false, false, self.salvageable.get(), );
2212 let event = event.upcast::<Event>();
2213 event.set_trusted(true);
2214 self.window.dispatch_event_with_target_override(cx, event);
2215 self.update_visibility_state(cx, DocumentVisibilityState::Hidden);
2217 }
2218 if !self.fired_unload.get() {
2220 let event = Event::new(
2221 cx,
2222 self.window.upcast(),
2223 atom!("unload"),
2224 EventBubbles::Bubbles,
2225 EventCancelable::Cancelable,
2226 );
2227 event.set_trusted(true);
2228 let event_target = self.window.upcast::<EventTarget>();
2229 let has_listeners = event_target.has_listeners_for(&atom!("unload"));
2230 self.window.dispatch_event_with_target_override(cx, &event);
2231 self.fired_unload.set(true);
2232 if has_listeners {
2234 self.salvageable.set(false);
2235 }
2236 }
2237 if !recursive_flag {
2241 let iframes: Vec<_> = self.iframes().iter().collect();
2244 for iframe in &iframes {
2245 let document = iframe.owner_document();
2247 document.unload(cx, true);
2248 if !document.salvageable() {
2249 self.salvageable.set(false);
2250 }
2251 }
2252 }
2253
2254 self.unloading_cleanup_steps();
2256
2257 self.window.as_global_scope().clean_up_all_file_resources();
2259
2260 self.decr_ignore_opens_during_unload_counter();
2262
2263 }
2266
2267 fn completely_finish_loading(&self) {
2269 self.completely_loaded.set(true);
2274 self.notify_constellation_load();
2283
2284 if let Some(DeclarativeRefresh::PendingLoad {
2293 url,
2294 time,
2295 from_meta_element,
2296 }) = &*self.declarative_refresh.borrow()
2297 {
2298 self.window.as_global_scope().schedule_callback(
2299 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
2300 url: url.clone(),
2301 from_meta_element: *from_meta_element,
2302 }),
2303 Duration::from_secs(*time),
2304 );
2305 }
2306 }
2307
2308 pub(crate) fn maybe_queue_document_completion(&self, cx: &mut JSContext) {
2311 let is_in_delaying_load_events_mode = match self.window.undiscarded_window_proxy() {
2313 Some(window_proxy) => window_proxy.is_delaying_load_events_mode(),
2314 None => false,
2315 };
2316
2317 let not_ready_for_load = self.loader.borrow().is_blocked() ||
2322 !self.is_fully_active() ||
2323 is_in_delaying_load_events_mode ||
2324 self.loader.borrow().events_inhibited();
2327
2328 if not_ready_for_load {
2329 return;
2331 }
2332
2333 self.queue_document_completion(cx);
2334 }
2335
2336 fn queue_document_completion(&self, cx: &mut JSContext) {
2338 self.loader.borrow_mut().inhibit_events();
2339
2340 debug!("Document loads are complete.");
2345 let document = Trusted::new(self);
2346 self.owner_global()
2347 .task_manager()
2348 .dom_manipulation_task_source()
2349 .queue(task!(fire_load_event: move |cx| {
2350 let document = document.root();
2351 let window = document.window();
2353 if !window.is_alive() {
2354 return;
2355 }
2356
2357 document.set_ready_state(cx,DocumentReadyState::Complete);
2359
2360 if document.browsing_context().is_none() {
2362 return;
2363 }
2364
2365 update_with_current_instant(&document.navigation_timing.load_event_start);
2367
2368 let load_event = Event::new(
2370 cx,
2371 window.upcast(),
2372 atom!("load"),
2373 EventBubbles::DoesNotBubble,
2374 EventCancelable::NotCancelable,
2375 );
2376 load_event.set_trusted(true);
2377 debug!("About to dispatch load for {:?}", document.url());
2378 window.dispatch_event_with_target_override(cx, &load_event);
2379
2380 update_with_current_instant(&document.navigation_timing.load_event_end);
2390
2391 document.page_showing.set(true);
2396
2397 let page_show_event = PageTransitionEvent::new(
2399 cx,
2400 window,
2401 atom!("pageshow"),
2402 false, false, false, );
2406 let page_show_event = page_show_event.upcast::<Event>();
2407 page_show_event.set_trusted(true);
2408 page_show_event.fire(cx, window.upcast());
2409
2410 document.completely_finish_loading();
2412
2413 if let Some(fragment) = document.url().fragment() {
2417 document.scroll_to_the_fragment(cx, fragment);
2418 }
2419 }));
2420
2421 #[cfg(feature = "webxr")]
2436 if pref!(dom_webxr_sessionavailable) && self.window.is_top_level() {
2437 self.window.Navigator(cx).Xr(cx).dispatch_sessionavailable();
2438 }
2439 }
2440
2441 pub(crate) fn completely_loaded(&self) -> bool {
2442 self.completely_loaded.get()
2443 }
2444
2445 pub(crate) fn start_the_end_loading_phase(&self) {
2446 self.current_the_end_loading_phase
2447 .set(TheEndLoadingPhase::ProcessingDeferredScripts);
2448 }
2449
2450 pub(crate) fn set_pending_parsing_blocking_script(
2452 &self,
2453 script: &HTMLScriptElement,
2454 load: Option<ScriptResult>,
2455 ) {
2456 assert!(!self.has_pending_parsing_blocking_script());
2457 *self.pending_parsing_blocking_script.borrow_mut() =
2458 Some(PendingScript::new_with_load(script, load));
2459 }
2460
2461 pub(crate) fn has_pending_parsing_blocking_script(&self) -> bool {
2463 self.pending_parsing_blocking_script.borrow().is_some()
2464 }
2465
2466 pub(crate) fn pending_parsing_blocking_script_loaded(
2468 &self,
2469 element: &HTMLScriptElement,
2470 result: ScriptResult,
2471 cx: &mut JSContext,
2472 ) {
2473 {
2474 let mut blocking_script = self.pending_parsing_blocking_script.borrow_mut();
2475 let entry = blocking_script.as_mut().unwrap();
2476 assert!(&*entry.element == element);
2477 entry.loaded(result);
2478 }
2479 self.process_pending_parsing_blocking_script(cx);
2480 }
2481
2482 fn process_pending_parsing_blocking_script(&self, cx: &mut JSContext) {
2483 if self.has_a_stylesheet_that_is_blocking_scripts() {
2484 return;
2485 }
2486 let pair = self
2487 .pending_parsing_blocking_script
2488 .borrow_mut()
2489 .as_mut()
2490 .and_then(PendingScript::take_result);
2491 if let Some((element, result)) = pair {
2492 *self.pending_parsing_blocking_script.borrow_mut() = None;
2493 self.get_current_parser()
2494 .unwrap()
2495 .resume_with_pending_parsing_blocking_script(cx, &element, result);
2496 }
2497 }
2498
2499 pub(crate) fn add_asap_script(&self, script: &HTMLScriptElement) {
2501 self.asap_scripts_set
2502 .borrow_mut()
2503 .push(Dom::from_ref(script));
2504 }
2505
2506 pub(crate) fn asap_script_loaded(
2509 &self,
2510 cx: &mut JSContext,
2511 element: &HTMLScriptElement,
2512 result: ScriptResult,
2513 ) {
2514 {
2515 let mut scripts = self.asap_scripts_set.borrow_mut();
2516 let idx = scripts
2517 .iter()
2518 .position(|entry| &**entry == element)
2519 .unwrap();
2520 scripts.swap_remove(idx);
2521 }
2522 element.execute(cx, result);
2523 self.wait_until_asap_scripts_have_executed();
2524 }
2525
2526 pub(crate) fn push_asap_in_order_script(&self, script: &HTMLScriptElement) {
2528 self.asap_in_order_scripts_list.push(script);
2529 }
2530
2531 pub(crate) fn asap_in_order_script_loaded(
2534 &self,
2535 cx: &mut JSContext,
2536 element: &HTMLScriptElement,
2537 result: ScriptResult,
2538 ) {
2539 self.asap_in_order_scripts_list.loaded(element, result);
2540 while let Some((element, result)) = self
2541 .asap_in_order_scripts_list
2542 .take_next_ready_to_be_executed()
2543 {
2544 element.execute(cx, result);
2545 }
2546
2547 self.wait_until_asap_scripts_have_executed();
2548 }
2549
2550 pub(crate) fn add_deferred_script(&self, script: &HTMLScriptElement) {
2552 self.deferred_scripts.push(script);
2553 }
2554
2555 pub(crate) fn deferred_script_loaded(
2558 &self,
2559 cx: &mut JSContext,
2560 element: &HTMLScriptElement,
2561 result: ScriptResult,
2562 ) {
2563 self.deferred_scripts.loaded(element, result);
2564 self.process_deferred_scripts(cx);
2565 }
2566
2567 fn process_deferred_scripts(&self, cx: &mut JSContext) {
2569 if self.current_the_end_loading_phase.get() != TheEndLoadingPhase::ProcessingDeferredScripts
2570 {
2571 return;
2572 }
2573
2574 loop {
2578 if self.has_a_stylesheet_that_is_blocking_scripts() {
2579 return;
2580 }
2581 if let Some((element, result)) = self.deferred_scripts.take_next_ready_to_be_executed()
2584 {
2585 element.execute(cx, result);
2587 } else {
2588 break;
2589 }
2590 }
2591 if self.deferred_scripts.is_empty() {
2593 self.current_the_end_loading_phase
2594 .set(TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts);
2595 self.maybe_dispatch_dom_content_loaded();
2597 }
2598 }
2599
2600 pub(crate) fn maybe_dispatch_dom_content_loaded(&self) {
2602 if self.domcontentloaded_dispatched.get() {
2604 return;
2605 }
2606 self.domcontentloaded_dispatched.set(true);
2607
2608 self.dispatch_dom_content_loaded();
2609 }
2610
2611 fn dispatch_dom_content_loaded(&self) {
2613 assert_ne!(
2614 self.ReadyState(),
2615 DocumentReadyState::Complete,
2616 "Complete before DOMContentLoaded?"
2617 );
2618
2619 let document = Trusted::new(self);
2622 self.owner_global()
2623 .task_manager()
2624 .dom_manipulation_task_source()
2625 .queue(task!(fire_dom_content_loaded_event: move |cx| {
2626 let document = document.root();
2629 update_with_current_instant(&document.navigation_timing.dom_content_loaded_event_start);
2630 document.upcast::<EventTarget>().fire_bubbling_event(cx, atom!("DOMContentLoaded"));
2632 update_with_current_instant(&document.navigation_timing.dom_content_loaded_event_end);
2635 }));
2644
2645 self.interactive_time
2647 .borrow()
2648 .maybe_set_tti(InteractiveFlag::DOMContentLoaded);
2649
2650 self.wait_until_asap_scripts_have_executed();
2651 }
2652
2653 fn has_finished_all_asap_scripts(&self) -> bool {
2654 self.asap_scripts_set.borrow().is_empty() && self.asap_in_order_scripts_list.is_empty()
2655 }
2656
2657 fn wait_until_asap_scripts_have_executed(&self) {
2659 if self.current_the_end_loading_phase.get() !=
2660 TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts
2661 {
2662 return;
2663 }
2664 if self.has_finished_all_asap_scripts() {
2667 let document = Trusted::new(self);
2668 self.owner_global()
2669 .task_manager()
2670 .dom_manipulation_task_source()
2671 .queue(task!(transition_away_from_asap_scripts: move |cx| {
2672 let document = document.root();
2673 if document.current_the_end_loading_phase.get() !=
2676 TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts
2677 {
2678 return;
2679 }
2680 if !document.has_finished_all_asap_scripts() {
2682 return;
2683 }
2684 document.current_the_end_loading_phase
2685 .set(TheEndLoadingPhase::WaitingForLoadEventBlockers);
2686 document.wait_until_load_blockers_have_resolved(cx);
2687 }));
2688 }
2689 }
2690
2691 pub(crate) fn wait_until_load_blockers_have_resolved(&self, _cx: &mut JSContext) {
2693 if self.current_the_end_loading_phase.get() !=
2694 TheEndLoadingPhase::WaitingForLoadEventBlockers
2695 {
2696 return;
2697 }
2698 {
2700 let loader = self.loader.borrow();
2701
2702 if self
2704 .navigation_timing
2705 .top_level_dom_complete
2706 .get()
2707 .is_none() &&
2708 loader.is_only_blocked_by_iframes()
2709 {
2710 update_with_current_instant(&self.navigation_timing.top_level_dom_complete);
2711 }
2712
2713 let not_ready_for_load = loader.is_blocked() || loader.events_inhibited();
2714 if not_ready_for_load {
2715 return;
2716 }
2717 }
2718
2719 self.current_the_end_loading_phase
2720 .set(TheEndLoadingPhase::Done);
2721 }
2724
2725 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
2727 if !self.is_fully_active() {
2729 self.salvageable.set(false);
2734 }
2738 for exited_iframe in self.iframes().iter() {
2751 debug!("Destroying nested iframe document");
2752 exited_iframe.destroy_document_and_its_descendants(cx);
2753 }
2754 self.destroy(cx);
2759 }
2762
2763 pub(crate) fn destroy(&self, cx: &mut JSContext) {
2765 let exited_window = self.window();
2766 self.abort(cx);
2768 self.salvageable.set(false);
2770 self.unloading_cleanup_steps();
2780
2781 exited_window
2784 .as_global_scope()
2785 .task_manager()
2786 .cancel_all_tasks_and_ignore_future_tasks();
2787
2788 exited_window.discard_browsing_context();
2790
2791 exited_window
2798 .as_global_scope()
2799 .disable_owned_worker_animation_frame_providers();
2800
2801 }
2805
2806 fn terminate_fetch_group(&self) -> bool {
2808 let mut load_cancellers = self.loader.borrow_mut().cancel_all_loads();
2809
2810 for canceller in &mut load_cancellers {
2814 if !canceller.keep_alive() {
2815 canceller.terminate();
2816 }
2817 }
2818 self.owner_global().process_deferred_fetches();
2820
2821 !load_cancellers.is_empty()
2822 }
2823
2824 fn active_parser(&self) -> Option<DomRoot<ServoParser>> {
2826 self.get_current_parser()
2829 .filter(|parser| !(parser.has_stopped() || parser.has_aborted()))
2830 }
2831
2832 pub(crate) fn abort(&self, cx: &mut JSContext) {
2834 self.loader.borrow_mut().inhibit_events();
2836
2837 self.script_blocking_stylesheet_set.borrow_mut().clear();
2846 *self.pending_parsing_blocking_script.borrow_mut() = None;
2847 *self.asap_scripts_set.borrow_mut() = vec![];
2848 self.asap_in_order_scripts_list.clear();
2849 self.deferred_scripts.clear();
2850 let loads_cancelled = self.terminate_fetch_group();
2851 let event_sources_canceled = self.window.as_global_scope().close_event_sources();
2852 if loads_cancelled || event_sources_canceled {
2853 self.salvageable.set(false);
2855 };
2856
2857 self.owner_global()
2862 .task_manager()
2863 .cancel_pending_tasks_for_source(TaskSourceName::Networking);
2864
2865 if let Some(parser) = self.active_parser() {
2870 self.active_parser_was_aborted.set(true);
2872 parser.abort(cx);
2874 self.salvageable.set(false);
2876 }
2877 }
2878
2879 pub(crate) fn abort_a_document_and_its_descendants(&self, cx: &mut JSContext) {
2881 for iframe in self.iframes().iter() {
2889 if let Some(descendant_document) = iframe.GetContentDocument() {
2890 let trusted_descendant_document = Trusted::new(&*descendant_document);
2891 let document = Trusted::new(self);
2892 descendant_document
2893 .owner_global()
2894 .task_manager()
2895 .navigation_and_traversal_task_source()
2896 .queue(task!(abort_iframe_document: move |cx| {
2897 let descendant_document = trusted_descendant_document.root();
2898 descendant_document.abort(cx);
2900 if !descendant_document.salvageable.get() {
2902 document.root().salvageable.set(false);
2903 }
2904 }));
2905 }
2906 }
2907
2908 self.abort(cx);
2910 }
2911
2912 pub(crate) fn notify_constellation_load(&self) {
2913 self.window()
2914 .send_to_constellation(ScriptToConstellationMessage::LoadComplete);
2915 }
2916
2917 pub(crate) fn set_current_parser(&self, script: Option<&ServoParser>) {
2918 self.current_parser.set(script);
2919 }
2920
2921 pub(crate) fn get_current_parser(&self) -> Option<DomRoot<ServoParser>> {
2922 self.current_parser.get()
2923 }
2924
2925 pub(crate) fn get_current_parser_line(&self) -> u32 {
2926 self.get_current_parser()
2927 .map(|parser| parser.get_current_line())
2928 .unwrap_or(0)
2929 }
2930
2931 pub(crate) fn iframes(&self) -> Ref<'_, IFrameCollection> {
2934 self.iframes.borrow()
2935 }
2936
2937 pub(crate) fn iframes_mut(&self) -> RefMut<'_, IFrameCollection> {
2940 self.iframes.borrow_mut()
2941 }
2942
2943 pub(crate) fn set_navigation_start(&self, navigation_start: CrossProcessInstant) {
2944 self.interactive_time
2945 .borrow_mut()
2946 .set_navigation_start(navigation_start);
2947 }
2948
2949 pub(crate) fn get_interactive_metrics(&self) -> Ref<'_, ProgressiveWebMetrics> {
2950 self.interactive_time.borrow()
2951 }
2952
2953 pub(crate) fn has_recorded_tti_metric(&self) -> bool {
2954 self.get_interactive_metrics().get_tti().is_some()
2955 }
2956
2957 pub(crate) fn start_tti(&self) {
2958 if self.get_interactive_metrics().needs_tti() {
2959 self.tti_window.borrow_mut().start_window();
2960 }
2961 }
2962
2963 pub(crate) fn record_tti_if_necessary(&self) {
2967 if self.has_recorded_tti_metric() {
2968 return;
2969 }
2970 if self.tti_window.borrow().needs_check() {
2971 self.get_interactive_metrics()
2972 .maybe_set_tti(InteractiveFlag::TimeToInteractive(
2973 self.tti_window.borrow().get_start(),
2974 ));
2975 }
2976 }
2977
2978 pub(crate) fn is_cookie_averse(&self) -> bool {
2980 !self.has_browsing_context || !url_has_network_scheme(&self.url())
2981 }
2982
2983 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
2984 self.document_or_shadow_root.custom_element_registry()
2985 }
2986
2987 pub(crate) fn set_custom_element_registry(&self, registry: &CustomElementRegistry) {
2988 self.document_or_shadow_root
2989 .set_custom_element_registry(Some(registry));
2990 }
2991
2992 pub(crate) fn teardown_custom_element_registry(&self) {
2995 if let Some(custom_elements) = self.custom_element_registry() {
2996 custom_elements.teardown();
2997 }
2998 }
2999
3000 pub(crate) fn increment_throw_on_dynamic_markup_insertion_counter(&self) {
3001 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
3002 self.throw_on_dynamic_markup_insertion_counter
3003 .set(counter + 1);
3004 }
3005
3006 pub(crate) fn decrement_throw_on_dynamic_markup_insertion_counter(&self) {
3007 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
3008 self.throw_on_dynamic_markup_insertion_counter
3009 .set(counter - 1);
3010 }
3011
3012 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
3013 for image in self.responsive_images.borrow().iter() {
3014 image.react_to_environment_changes(cx);
3015 }
3016 }
3017
3018 pub(crate) fn register_responsive_image(&self, img: &HTMLImageElement) {
3019 self.responsive_images.borrow_mut().push(Dom::from_ref(img));
3020 }
3021
3022 pub(crate) fn unregister_responsive_image(&self, img: &HTMLImageElement) {
3023 let index = self
3024 .responsive_images
3025 .borrow()
3026 .iter()
3027 .position(|x| **x == *img);
3028 if let Some(i) = index {
3029 self.responsive_images.borrow_mut().remove(i);
3030 }
3031 }
3032
3033 pub(crate) fn register_media_controls(&self, id: &str, controls: &ShadowRoot) {
3034 let did_have_these_media_controls = self
3035 .media_controls
3036 .borrow_mut()
3037 .insert(id.to_string(), Dom::from_ref(controls))
3038 .is_some();
3039 debug_assert!(
3040 !did_have_these_media_controls,
3041 "Trying to register known media controls"
3042 );
3043 }
3044
3045 pub(crate) fn unregister_media_controls(&self, id: &str) {
3046 let did_have_these_media_controls = self.media_controls.borrow_mut().remove(id).is_some();
3047 debug_assert!(
3048 did_have_these_media_controls,
3049 "Trying to unregister unknown media controls"
3050 );
3051 }
3052
3053 pub(crate) fn mark_canvas_as_dirty(&self, canvas: &Dom<HTMLCanvasElement>) {
3054 let mut dirty_canvases = self.dirty_canvases.borrow_mut();
3055 if dirty_canvases
3056 .iter()
3057 .any(|dirty_canvas| dirty_canvas == canvas)
3058 {
3059 return;
3060 }
3061 dirty_canvases.push(canvas.clone());
3062 }
3063
3064 pub(crate) fn needs_rendering_update(&self, no_gc: &NoGC) -> bool {
3068 if !self.is_fully_active() {
3069 return false;
3070 }
3071 if !self.window().layout_blocked() &&
3072 (!self.restyle_reason(no_gc).is_empty() ||
3073 self.window().layout().needs_new_display_list() ||
3074 self.window().layout().needs_accessibility_update())
3075 {
3076 return true;
3077 }
3078 if !self.rendering_update_reasons.get().is_empty() {
3079 return true;
3080 }
3081 if self.event_handler.has_pending_input_events() {
3082 return true;
3083 }
3084 if self.has_pending_scroll_events() {
3085 return true;
3086 }
3087 if self.window().has_unhandled_resize_event() {
3088 return true;
3089 }
3090 if self.has_pending_animated_image_update.get() || !self.dirty_canvases.borrow().is_empty()
3091 {
3092 return true;
3093 }
3094 if self.window().has_pending_media_query_evaluation() {
3095 return true;
3096 }
3097 if self
3098 .selection()
3099 .is_some_and(|selection| selection.visible_selection_dirty())
3100 {
3101 return true;
3102 }
3103
3104 false
3105 }
3106
3107 pub(crate) fn update_the_rendering(
3115 &self,
3116 cx: &mut JSContext,
3117 ) -> (ReflowPhasesRun, ReflowStatistics) {
3118 assert!(!self.is_render_blocked());
3119
3120 let mut phases = ReflowPhasesRun::empty();
3121 if self.has_pending_animated_image_update.get() {
3122 self.image_animation_manager
3123 .borrow()
3124 .update_active_frames(&self.window, self.current_animation_timeline_value());
3125 self.has_pending_animated_image_update.set(false);
3126 phases.insert(ReflowPhasesRun::UpdatedImageData);
3127 }
3128
3129 self.current_rendering_epoch
3130 .set(self.current_rendering_epoch.get().next());
3131 let current_rendering_epoch = self.current_rendering_epoch.get();
3132
3133 let image_keys: Vec<_> = self
3135 .dirty_canvases
3136 .borrow_mut()
3137 .drain(..)
3138 .filter_map(|canvas| canvas.update_rendering(current_rendering_epoch))
3139 .collect();
3140
3141 let pipeline_id = self.window().pipeline_id();
3144 if !image_keys.is_empty() {
3145 phases.insert(ReflowPhasesRun::UpdatedImageData);
3146 self.waiting_on_canvas_image_updates.set(true);
3147 self.window().paint_api().delay_new_frame_for_canvas(
3148 self.webview_id(),
3149 self.window().pipeline_id(),
3150 current_rendering_epoch,
3151 image_keys,
3152 );
3153 }
3154
3155 let (reflow_phases, statistics) = self.window().reflow(cx, ReflowGoal::UpdateTheRendering);
3156 let phases = phases.union(reflow_phases);
3157
3158 self.window().paint_api().update_epoch(
3159 self.webview_id(),
3160 pipeline_id,
3161 current_rendering_epoch,
3162 );
3163
3164 (phases, statistics)
3165 }
3166
3167 pub(crate) fn handle_no_longer_waiting_on_asynchronous_image_updates(&self) {
3168 self.waiting_on_canvas_image_updates.set(false);
3169 }
3170
3171 pub(crate) fn waiting_on_canvas_image_updates(&self) -> bool {
3172 self.waiting_on_canvas_image_updates.get()
3173 }
3174
3175 pub(crate) fn maybe_fulfill_font_ready_promise(&self, cx: &mut JSContext) -> bool {
3185 if !self.is_fully_active() {
3186 return false;
3187 }
3188
3189 let fonts = self.Fonts(cx);
3190 if !fonts.waiting_to_fullfill_promise() {
3191 return false;
3192 }
3193 if self.window().font_context().web_fonts_still_loading() != 0 {
3194 return false;
3195 }
3196 if self.ReadyState() != DocumentReadyState::Complete {
3197 return false;
3198 }
3199 if !self.restyle_reason(cx.no_gc()).is_empty() {
3200 return false;
3201 }
3202 if !self.rendering_update_reasons.get().is_empty() {
3203 return false;
3204 }
3205
3206 let result = fonts.fulfill_ready_promise_if_needed(cx);
3207
3208 if result {
3212 self.add_rendering_update_reason(RenderingUpdateReason::FontReadyPromiseFulfilled);
3213 }
3214
3215 result
3216 }
3217
3218 pub(crate) fn id_map(&self) -> &TreeOrderedIndexMap {
3219 &self.id_map
3220 }
3221
3222 pub(crate) fn add_resize_observer(&self, resize_observer: &ResizeObserver) {
3224 self.resize_observers
3225 .borrow_mut()
3226 .push(Dom::from_ref(resize_observer));
3227 }
3228
3229 pub(crate) fn gather_active_resize_observations_at_depth(
3232 &self,
3233 depth: &ResizeObservationDepth,
3234 ) -> bool {
3235 let mut has_active_resize_observations = false;
3236 for observer in self.resize_observers.borrow_mut().iter_mut() {
3237 observer.gather_active_resize_observations_at_depth(
3238 depth,
3239 &mut has_active_resize_observations,
3240 );
3241 }
3242 has_active_resize_observations
3243 }
3244
3245 #[expect(clippy::redundant_iter_cloned)]
3247 pub(crate) fn broadcast_active_resize_observations(
3248 &self,
3249 cx: &mut JSContext,
3250 ) -> ResizeObservationDepth {
3251 let mut shallowest = ResizeObservationDepth::max();
3252 let iterator: Vec<DomRoot<ResizeObserver>> = self
3256 .resize_observers
3257 .borrow()
3258 .iter()
3259 .cloned()
3260 .map(|obs| DomRoot::from_ref(&*obs))
3261 .collect();
3262 for observer in iterator {
3263 observer.broadcast_active_resize_observations(cx, &mut shallowest);
3264 }
3265 shallowest
3266 }
3267
3268 pub(crate) fn has_skipped_resize_observations(&self) -> bool {
3270 self.resize_observers
3271 .borrow()
3272 .iter()
3273 .any(|observer| observer.has_skipped_resize_observations())
3274 }
3275
3276 pub(crate) fn deliver_resize_loop_error_notification(&self, cx: &mut JSContext) {
3278 let error_info: ErrorInfo = crate::dom::bindings::error::ErrorInfo {
3279 message: "ResizeObserver loop completed with undelivered notifications.".to_string(),
3280 ..Default::default()
3281 };
3282 self.window
3283 .as_global_scope()
3284 .report_an_error(cx, error_info, HandleValue::null());
3285 }
3286
3287 pub(crate) fn status_code(&self) -> Option<u16> {
3288 self.status_code
3289 }
3290
3291 pub(crate) fn encoding_parse_a_url(&self, url: &str) -> Result<ServoUrl, url::ParseError> {
3293 let encoding = self.encoding.get();
3299
3300 let base_url = self.base_url();
3306
3307 url::Url::options()
3309 .base_url(Some(base_url.as_url()))
3310 .encoding_override(Some(&|input| {
3311 servo_url::encoding::encode_as_url_query_string(input, encoding)
3312 }))
3313 .parse(url)
3314 .map(ServoUrl::from)
3315 }
3316
3317 pub(crate) fn allowed_to_use_feature(&self, _feature: PermissionName) -> bool {
3319 if !self.has_browsing_context {
3321 return false;
3322 }
3323
3324 if !self.is_fully_active() {
3326 return false;
3327 }
3328
3329 true
3335 }
3336
3337 pub(crate) fn add_intersection_observer(&self, intersection_observer: &IntersectionObserver) {
3340 self.intersection_observers
3341 .borrow_mut()
3342 .push(Dom::from_ref(intersection_observer));
3343 }
3344
3345 pub(crate) fn remove_intersection_observer(
3349 &self,
3350 intersection_observer: &IntersectionObserver,
3351 ) {
3352 self.intersection_observers
3353 .borrow_mut()
3354 .retain(|observer| *observer != intersection_observer)
3355 }
3356
3357 pub(crate) fn update_intersection_observer_steps(
3359 &self,
3360 cx: &mut JSContext,
3361 time: CrossProcessInstant,
3362 ) {
3363 if self.intersection_observers.borrow().is_empty() {
3364 return;
3365 }
3366 self.window()
3368 .reflow_for_non_flushing_update_the_rendering_queries(cx);
3369
3370 for intersection_observer in &*self.intersection_observers.borrow() {
3372 self.update_single_intersection_observer_steps(cx, intersection_observer, time);
3373 }
3374 }
3375
3376 fn update_single_intersection_observer_steps(
3378 &self,
3379 cx: &mut JSContext,
3380 intersection_observer: &IntersectionObserver,
3381 time: CrossProcessInstant,
3382 ) {
3383 let root_bounds = intersection_observer.root_intersection_rectangle();
3386
3387 intersection_observer.update_intersection_observations_steps(cx, self, time, root_bounds);
3391 }
3392
3393 pub(crate) fn notify_intersection_observers(&self, cx: &mut JSContext) {
3395 self.intersection_observer_task_queued.set(false);
3398
3399 rooted_vec!(let notify_list <- self.intersection_observers.clone().take().into_iter());
3404
3405 for intersection_observer in notify_list.iter() {
3408 intersection_observer.invoke_callback_if_necessary(cx);
3410 }
3411 }
3412
3413 pub(crate) fn queue_an_intersection_observer_task(&self) {
3415 if self.intersection_observer_task_queued.get() {
3418 return;
3419 }
3420
3421 self.intersection_observer_task_queued.set(true);
3424
3425 let document = Trusted::new(self);
3429 self.owner_global()
3430 .task_manager()
3431 .intersection_observer_task_source()
3432 .queue(task!(notify_intersection_observers: move |cx| {
3433 document.root().notify_intersection_observers(cx);
3434 }));
3435 }
3436
3437 pub(crate) fn store_lcp_candidate(&self, id: LCPCandidateID, element: &Element) {
3438 self.lcp_candidates
3439 .borrow_mut()
3440 .insert(id, Dom::from_ref(element));
3441 }
3442
3443 pub(crate) fn handle_paint_metric(
3444 &self,
3445 cx: &mut JSContext,
3446 metric_type: ProgressiveWebMetricType,
3447 metric_value: CrossProcessInstant,
3448 first_reflow: bool,
3449 ) {
3450 let metrics = self.interactive_time.borrow();
3451 match metric_type {
3452 ProgressiveWebMetricType::FirstPaint |
3453 ProgressiveWebMetricType::FirstContentfulPaint => {
3454 let binding = PerformancePaintTiming::new(
3455 cx,
3456 self.window.as_global_scope(),
3457 metric_type.clone(),
3458 metric_value,
3459 );
3460 metrics.set_performance_paint_metric(metric_value, first_reflow, metric_type);
3461 let entry = binding.upcast::<PerformanceEntry>();
3462 self.window.Performance(cx).queue_entry(entry);
3463 },
3464 ProgressiveWebMetricType::LargestContentfulPaint { area, url, id } => {
3465 let binding = LargestContentfulPaint::new(
3466 cx,
3467 self.window.as_global_scope(),
3468 metric_value,
3469 area,
3470 url,
3471 self.lcp_candidates.borrow_mut().remove(&id).as_deref(),
3472 );
3473 metrics.set_largest_contentful_paint(id, metric_value, area);
3474 let entry = binding.upcast::<PerformanceEntry>();
3475 self.window.Performance(cx).queue_entry(entry);
3476 },
3477 ProgressiveWebMetricType::TimeToInteractive => {
3478 unreachable!("Unexpected non-paint metric.")
3479 },
3480 }
3481 }
3482
3483 fn write(
3485 &self,
3486 cx: &mut JSContext,
3487 text: Vec<TrustedHTMLOrString>,
3488 line_feed: bool,
3489 containing_class: &str,
3490 field: &str,
3491 ) -> ErrorResult {
3492 let mut strings: Vec<String> = Vec::with_capacity(text.len());
3494 let mut is_trusted = true;
3496 for value in text {
3498 match value {
3499 TrustedHTMLOrString::TrustedHTML(trusted_html) => {
3501 strings.push(trusted_html.to_string());
3502 },
3503 TrustedHTMLOrString::String(str_) => {
3504 is_trusted = false;
3506 strings.push(str_.into());
3508 },
3509 };
3510 }
3511 let mut string = itertools::join(strings, "");
3512 if !is_trusted {
3516 string = TrustedHTML::get_trusted_type_compliant_string(
3517 cx,
3518 &self.global(),
3519 TrustedHTMLOrString::String(string.into()),
3520 &format!("{} {}", containing_class, field),
3521 )?
3522 .str()
3523 .to_owned();
3524 }
3525 if line_feed {
3527 string.push('\n');
3528 }
3529 if !self.is_html_document() {
3531 return Err(Error::InvalidState(None));
3532 }
3533
3534 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
3537 return Err(Error::InvalidState(None));
3538 }
3539
3540 if self.active_parser_was_aborted.get() {
3542 return Ok(());
3543 }
3544
3545 let parser = match self.get_current_parser() {
3546 Some(ref parser) if parser.can_write() => DomRoot::from_ref(&**parser),
3547 _ => {
3549 if self.is_prompting_or_unloading() ||
3552 self.ignore_destructive_writes_counter.get() > 0
3553 {
3554 return Ok(());
3555 }
3556 self.Open(cx, None, None)?;
3558 self.get_current_parser().unwrap()
3559 },
3560 };
3561
3562 parser.write(cx, string.into());
3564
3565 Ok(())
3566 }
3567
3568 pub(crate) fn details_name_groups(&self) -> RefMut<'_, DetailsNameGroups> {
3569 RefMut::map(
3570 self.details_name_groups.borrow_mut(),
3571 |details_name_groups| details_name_groups.get_or_insert_default(),
3572 )
3573 }
3574
3575 pub(crate) fn accessibility_data_mut(&self) -> RefMut<'_, AccessibilityData> {
3576 self.accessibility_data.borrow_mut()
3577 }
3578
3579 pub(crate) fn accessibility_active(&self) -> bool {
3580 self.window().layout().accessibility_active()
3581 }
3582
3583 pub(crate) fn rooted_nodes_for_accessibility_integrity_check(
3584 &self,
3585 ) -> Option<FxHashSet<OpaqueNode>> {
3586 if !self.accessibility_active() {
3587 return None;
3588 }
3589
3590 let mut accessibility_data = self.accessibility_data_mut();
3591
3592 if pref!(expensive_accessibility_test_assertions_enabled) {
3593 return Some(accessibility_data.unroot_and_drain_all_removed_nodes());
3594 }
3595
3596 accessibility_data.unroot_all_removed_nodes();
3597 None
3598 }
3599
3600 pub(crate) fn get_document_element_unrooted<'a>(
3601 &self,
3602 no_gc: &'a NoGC,
3603 ) -> Option<UnrootedDom<'a, Element>> {
3604 self.upcast::<Node>().child_elements_unrooted(no_gc).next()
3605 }
3606}
3607
3608#[derive(MallocSizeOf, PartialEq)]
3609pub(crate) enum DocumentSource {
3610 FromParser,
3611 NotFromParser,
3612}
3613
3614impl<'dom> LayoutDom<'dom, Document> {
3615 #[inline]
3616 pub(crate) fn is_html_document_for_layout(&self) -> bool {
3617 self.unsafe_get().is_html_document
3618 }
3619
3620 #[inline]
3621 pub(crate) fn quirks_mode(self) -> QuirksMode {
3622 self.unsafe_get().quirks_mode.get()
3623 }
3624
3625 #[inline]
3626 pub(crate) fn shared_style_locks(self) -> &'dom SharedRwLocks {
3627 self.unsafe_get().shared_style_locks()
3628 }
3629
3630 #[inline]
3631 pub(crate) fn flush_shadow_root_stylesheets_if_necessary(
3632 self,
3633 stylist: &mut Stylist,
3634 guard: &SharedRwLockReadGuard,
3635 ) {
3636 (*self.unsafe_get()).flush_shadow_root_stylesheets_if_necessary_for_layout(stylist, guard)
3637 }
3638
3639 pub(crate) fn elements_with_id(self, id: &Atom) -> &[LayoutDom<'dom, Element>] {
3640 self.unsafe_get().id_map.get_all_for_layout(id)
3641 }
3642
3643 #[expect(unsafe_code)]
3644 pub(crate) fn url_for_layout(self) -> ServoUrl {
3645 unsafe { self.unsafe_get().url.borrow_for_layout() }.clone()
3646 }
3647
3648 #[expect(unsafe_code)]
3649 pub(crate) fn selection_for_layout(&self) -> Option<LayoutDom<'dom, Selection>> {
3650 unsafe { self.unsafe_get().selection.to_layout() }
3651 }
3652}
3653
3654pub(crate) fn get_registrable_domain_suffix_of_or_is_equal_to(
3658 host_suffix_string: &str,
3659 original_host: Host,
3660) -> Option<Host> {
3661 if host_suffix_string.is_empty() {
3663 return None;
3664 }
3665
3666 let host = match Host::parse(host_suffix_string) {
3668 Ok(host) => host,
3669 Err(_) => return None,
3670 };
3671
3672 if host != original_host {
3674 let host = match host {
3676 Host::Domain(ref host) => host,
3677 _ => return None,
3678 };
3679 let original_host = match original_host {
3680 Host::Domain(ref original_host) => original_host,
3681 _ => return None,
3682 };
3683
3684 let index = original_host.len().checked_sub(host.len())?;
3686 let (prefix, suffix) = original_host.split_at(index);
3687
3688 if !prefix.ends_with('.') {
3689 return None;
3690 }
3691 if suffix != host {
3692 return None;
3693 }
3694
3695 if is_pub_domain(host) {
3697 return None;
3698 }
3699 }
3700
3701 Some(host)
3703}
3704
3705fn url_has_network_scheme(url: &ServoUrl) -> bool {
3707 matches!(url.scheme(), "ftp" | "http" | "https")
3708}
3709
3710#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
3711pub(crate) enum HasBrowsingContext {
3712 No,
3713 Yes,
3714}
3715
3716impl Document {
3717 #[allow(clippy::too_many_arguments)]
3718 pub(crate) fn new_inherited(
3719 window: &Window,
3720 has_browsing_context: HasBrowsingContext,
3721 url: Option<ServoUrl>,
3722 about_base_url: Option<ServoUrl>,
3723 origin: MutableOrigin,
3724 is_html_document: IsHTMLDocument,
3725 content_type: Option<Mime>,
3726 last_modified: Option<String>,
3727 activity: DocumentActivity,
3728 source: DocumentSource,
3729 doc_loader: DocumentLoader,
3730 referrer: Option<String>,
3731 status_code: Option<u16>,
3732 canceller: FetchCanceller,
3733 is_initial_about_blank: bool,
3734 allow_declarative_shadow_roots: bool,
3735 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
3736 has_trustworthy_ancestor_origin: bool,
3737 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
3738 creation_sandboxing_flag_set: SandboxingFlagSet,
3739 timeline: &DocumentTimeline,
3740 pipeline_id: PipelineId,
3741 image_cache: StdArc<dyn ImageCache>,
3742 ) -> Document {
3743 let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
3744
3745 let (ready_state, domcontentloaded_dispatched) = if source == DocumentSource::FromParser {
3746 (DocumentReadyState::Loading, false)
3747 } else {
3748 (DocumentReadyState::Complete, true)
3749 };
3750
3751 let frame_type = match window.is_top_level() {
3752 true => TimerMetadataFrameType::RootWindow,
3753 false => TimerMetadataFrameType::IFrame,
3754 };
3755 let interactive_time = ProgressiveWebMetrics::new(
3756 window.time_profiler_chan().clone(),
3757 url.clone(),
3758 frame_type,
3759 );
3760
3761 let content_type = content_type.unwrap_or_else(|| {
3762 match is_html_document {
3763 IsHTMLDocument::HTMLDocument => "text/html",
3765 IsHTMLDocument::NonHTMLDocument => "application/xml",
3767 }
3768 .parse()
3769 .unwrap()
3770 });
3771
3772 let encoding = content_type
3773 .get_parameter(CHARSET)
3774 .and_then(|charset| Encoding::for_label(charset.as_bytes()))
3775 .unwrap_or(UTF_8);
3776
3777 let has_focus = window.parent_info().is_none();
3778 let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
3779 let shared_style_locks = window.script_thread().shared_style_locks().clone();
3780
3781 Document {
3782 node: Node::new_document_node(),
3783 document_or_shadow_root: DocumentOrShadowRoot::new(window),
3784 window: Dom::from_ref(window),
3785 has_browsing_context,
3786 implementation: Default::default(),
3787 content_type,
3788 last_modified,
3789 url: DomRefCell::new(url),
3790 about_base_url: DomRefCell::new(about_base_url),
3791 quirks_mode: Cell::new(QuirksMode::NoQuirks),
3793 event_handler: DocumentEventHandler::new(window),
3794 focus_handler: DocumentFocusHandler::new(window, has_focus),
3795 embedder_controls: DocumentEmbedderControls::new(window),
3796 id_map: TreeOrderedIndexMap::id(),
3797 name_map: TreeOrderedIndexMap::name(),
3798 encoding: Cell::new(encoding),
3800 is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
3801 activity: Cell::new(activity),
3802 tag_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3803 tagns_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3804 classes_map: DomRefCell::new(HashMapTracedValues::new()),
3805 images: Default::default(),
3806 embeds: Default::default(),
3807 links: Default::default(),
3808 forms: Default::default(),
3809 scripts: Default::default(),
3810 anchors: Default::default(),
3811 applets: Default::default(),
3812 iframes: RefCell::new(IFrameCollection::new()),
3813 shared_style_locks,
3814 stylesheets: DomRefCell::new(DocumentStylesheetSet::new()),
3815 stylesheet_list: MutNullableDom::new(None),
3816 ready_state: Cell::new(ready_state),
3817 domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched),
3818 current_script: Default::default(),
3819 current_the_end_loading_phase: Default::default(),
3820 pending_parsing_blocking_script: Default::default(),
3821 script_blocking_stylesheet_set: Default::default(),
3822 render_blocking_element_count: Default::default(),
3823 deferred_scripts: Default::default(),
3824 asap_in_order_scripts_list: Default::default(),
3825 asap_scripts_set: Default::default(),
3826 animation_frame_ident: Cell::new(0),
3827 animation_frame_list: DomRefCell::new(VecDeque::new()),
3828 running_animation_callbacks: Cell::new(false),
3829 loader: DomRefCell::new(doc_loader),
3830 current_parser: Default::default(),
3831 base_element: Default::default(),
3832 target_base_element: Default::default(),
3833 appropriate_template_contents_owner_document: Default::default(),
3834 pending_restyles: DomRefCell::new(FxHashMap::default()),
3835 needs_restyle: Cell::new(RestyleReason::DOMChanged),
3836 origin: DomRefCell::new(origin),
3837 referrer,
3838 target_element: MutNullableDom::new(None),
3839 policy_container: DomRefCell::new(PolicyContainer::default()),
3840 preloaded_resources: Default::default(),
3841 ignore_destructive_writes_counter: Default::default(),
3842 ignore_opens_during_unload_counter: Default::default(),
3843 spurious_animation_frames: Cell::new(0),
3844 fullscreen_element: MutNullableDom::new(None),
3845 form_id_listener_map: Default::default(),
3846 interactive_time: DomRefCell::new(interactive_time),
3847 tti_window: DomRefCell::new(InteractiveWindow::default()),
3848 canceller,
3849 throw_on_dynamic_markup_insertion_counter: Cell::new(0),
3850 page_showing: Cell::new(false),
3851 salvageable: Cell::new(true),
3852 active_parser_was_aborted: Cell::new(false),
3853 fired_unload: Cell::new(false),
3854 responsive_images: Default::default(),
3855 navigation_timing: Default::default(),
3856 resource_fetch_timing: RefCell::new(None),
3857 completely_loaded: Cell::new(false),
3858 script_and_layout_blockers: Cell::new(0),
3859 delayed_tasks: Default::default(),
3860 shadow_roots: DomRefCell::new(HashSet::new()),
3861 shadow_roots_styles_changed: Cell::new(false),
3862 media_controls: DomRefCell::new(HashMap::new()),
3863 dirty_canvases: DomRefCell::new(Default::default()),
3864 has_pending_animated_image_update: Cell::new(false),
3865 selection: MutNullableDom::new(None),
3866 timeline: Dom::from_ref(timeline),
3867 animations: Animations::new(),
3868 image_animation_manager: DomRefCell::new(ImageAnimationManager::default()),
3869 dirty_root: Default::default(),
3870 declarative_refresh: Default::default(),
3871 resize_observers: Default::default(),
3872 fonts: Default::default(),
3873 visibility_state: Cell::new(DocumentVisibilityState::Hidden),
3874 status_code,
3875 is_initial_about_blank: Cell::new(is_initial_about_blank),
3876 allow_declarative_shadow_roots: Cell::new(allow_declarative_shadow_roots),
3877 inherited_insecure_requests_policy: Cell::new(inherited_insecure_requests_policy),
3878 has_trustworthy_ancestor_origin: Cell::new(has_trustworthy_ancestor_origin),
3879 intersection_observer_task_queued: Cell::new(false),
3880 intersection_observers: Default::default(),
3881 highlighted_dom_node: Default::default(),
3882 lcp_candidates: DomRefCell::new(Default::default()),
3883 adopted_stylesheets: Default::default(),
3884 adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
3885 pending_scroll_events: Default::default(),
3886 rendering_update_reasons: Default::default(),
3887 waiting_on_canvas_image_updates: Cell::new(false),
3888 root_removal_noted: Cell::new(true),
3889 current_rendering_epoch: Default::default(),
3890 custom_element_reaction_stack,
3891 active_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
3892 creation_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
3893 favicon: RefCell::new(None),
3894 websockets: DOMTracker::new(),
3895 details_name_groups: Default::default(),
3896 protocol_handler_automation_mode: Default::default(),
3897 layout_animations_test_enabled: pref!(layout_animations_test_enabled),
3898 state_override: Default::default(),
3899 value_override: Default::default(),
3900 default_single_line_container_name: Default::default(),
3901 css_styling_flag: Default::default(),
3902 accessibility_data: Default::default(),
3903 iframe_load_in_progress: Default::default(),
3904 mute_iframe_load: Default::default(),
3905 timers: OneshotTimers::new(window.upcast()),
3906 pipeline_id,
3907 task_manager: Rc::new(TaskManager::new(
3908 Some(window.event_loop_sender()),
3909 pipeline_id,
3910 None,
3911 )),
3912 image_cache,
3913 history: Default::default(),
3914 }
3915 }
3916
3917 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
3919 if let Some(csp_list) = self.get_csp_list().as_ref() {
3920 for policy in &csp_list.0 {
3921 if policy.contains_a_directive_whose_name_is("upgrade-insecure-requests") &&
3922 policy.disposition == PolicyDisposition::Enforce
3923 {
3924 return InsecureRequestsPolicy::Upgrade;
3925 }
3926 }
3927 }
3928
3929 self.inherited_insecure_requests_policy
3930 .get()
3931 .unwrap_or(InsecureRequestsPolicy::DoNotUpgrade)
3932 }
3933
3934 pub(crate) fn event_handler(&self) -> &DocumentEventHandler {
3936 &self.event_handler
3937 }
3938
3939 pub(crate) fn focus_handler(&self) -> &DocumentFocusHandler {
3941 &self.focus_handler
3942 }
3943
3944 pub(crate) fn embedder_controls(&self) -> &DocumentEmbedderControls {
3946 &self.embedder_controls
3947 }
3948
3949 fn has_pending_scroll_events(&self) -> bool {
3952 !self.pending_scroll_events.borrow().is_empty()
3953 }
3954
3955 pub(crate) fn add_rendering_update_reason(&self, reason: RenderingUpdateReason) {
3958 self.rendering_update_reasons
3959 .set(self.rendering_update_reasons.get().union(reason));
3960 }
3961
3962 pub(crate) fn clear_rendering_update_reasons(&self) {
3964 self.rendering_update_reasons
3965 .set(RenderingUpdateReason::empty())
3966 }
3967
3968 pub(crate) fn add_script_and_layout_blocker(&self) {
3975 self.script_and_layout_blockers
3976 .set(self.script_and_layout_blockers.get() + 1);
3977 }
3978
3979 pub(crate) fn remove_script_and_layout_blocker(&self, cx: &mut JSContext) {
3983 assert!(self.script_and_layout_blockers.get() > 0);
3984 self.script_and_layout_blockers
3985 .set(self.script_and_layout_blockers.get() - 1);
3986 while self.script_and_layout_blockers.get() == 0 && !self.delayed_tasks.borrow().is_empty()
3987 {
3988 let task = self.delayed_tasks.borrow_mut().remove(0);
3989 task.run_box(cx);
3990 }
3991 }
3992
3993 pub(crate) fn add_delayed_task<T: 'static + NonSendTaskBox>(&self, task: T) {
3995 self.delayed_tasks.borrow_mut().push(Box::new(task));
3996 }
3997
3998 pub(crate) fn is_safe_to_run_script_or_layout(&self) -> bool {
4001 self.script_and_layout_blockers.get() == 0
4002 }
4003
4004 pub(crate) fn ensure_safe_to_run_script_or_layout(&self) {
4007 assert!(
4008 self.is_safe_to_run_script_or_layout(),
4009 "Attempt to use script or layout while DOM not in a stable state"
4010 );
4011 }
4012
4013 #[allow(clippy::too_many_arguments)]
4014 pub(crate) fn new(
4015 cx: &mut JSContext,
4016 window: &Window,
4017 has_browsing_context: HasBrowsingContext,
4018 url: Option<ServoUrl>,
4019 about_base_url: Option<ServoUrl>,
4020 origin: MutableOrigin,
4021 doctype: IsHTMLDocument,
4022 content_type: Option<Mime>,
4023 last_modified: Option<String>,
4024 activity: DocumentActivity,
4025 source: DocumentSource,
4026 doc_loader: DocumentLoader,
4027 referrer: Option<String>,
4028 status_code: Option<u16>,
4029 canceller: FetchCanceller,
4030 is_initial_about_blank: bool,
4031 allow_declarative_shadow_roots: bool,
4032 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4033 has_trustworthy_ancestor_origin: bool,
4034 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4035 creation_sandboxing_flag_set: SandboxingFlagSet,
4036 pipeline_id: PipelineId,
4037 image_cache: StdArc<dyn ImageCache>,
4038 ) -> DomRoot<Document> {
4039 Self::new_with_proto(
4040 cx,
4041 window,
4042 None,
4043 has_browsing_context,
4044 url,
4045 about_base_url,
4046 origin,
4047 doctype,
4048 content_type,
4049 last_modified,
4050 activity,
4051 source,
4052 doc_loader,
4053 referrer,
4054 status_code,
4055 canceller,
4056 is_initial_about_blank,
4057 allow_declarative_shadow_roots,
4058 inherited_insecure_requests_policy,
4059 has_trustworthy_ancestor_origin,
4060 custom_element_reaction_stack,
4061 creation_sandboxing_flag_set,
4062 pipeline_id,
4063 image_cache,
4064 )
4065 }
4066
4067 #[allow(clippy::too_many_arguments)]
4068 fn new_with_proto(
4069 cx: &mut JSContext,
4070 window: &Window,
4071 proto: Option<HandleObject>,
4072 has_browsing_context: HasBrowsingContext,
4073 url: Option<ServoUrl>,
4074 about_base_url: Option<ServoUrl>,
4075 origin: MutableOrigin,
4076 doctype: IsHTMLDocument,
4077 content_type: Option<Mime>,
4078 last_modified: Option<String>,
4079 activity: DocumentActivity,
4080 source: DocumentSource,
4081 doc_loader: DocumentLoader,
4082 referrer: Option<String>,
4083 status_code: Option<u16>,
4084 canceller: FetchCanceller,
4085 is_initial_about_blank: bool,
4086 allow_declarative_shadow_roots: bool,
4087 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4088 has_trustworthy_ancestor_origin: bool,
4089 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4090 creation_sandboxing_flag_set: SandboxingFlagSet,
4091 pipeline_id: PipelineId,
4092 image_cache: StdArc<dyn ImageCache>,
4093 ) -> DomRoot<Document> {
4094 let timeline = DocumentTimeline::new(cx, window);
4095 let document = reflect_dom_object_with_proto(
4096 cx,
4097 Box::new(Document::new_inherited(
4098 window,
4099 has_browsing_context,
4100 url,
4101 about_base_url,
4102 origin,
4103 doctype,
4104 content_type,
4105 last_modified,
4106 activity,
4107 source,
4108 doc_loader,
4109 referrer,
4110 status_code,
4111 canceller,
4112 is_initial_about_blank,
4113 allow_declarative_shadow_roots,
4114 inherited_insecure_requests_policy,
4115 has_trustworthy_ancestor_origin,
4116 custom_element_reaction_stack,
4117 creation_sandboxing_flag_set,
4118 &timeline,
4119 pipeline_id,
4120 image_cache,
4121 )),
4122 window,
4123 proto,
4124 );
4125 {
4126 let node = document.upcast::<Node>();
4127 node.set_owner_doc(&document);
4128 }
4129 document
4130 }
4131
4132 pub(crate) fn get_redirect_count(&self) -> u16 {
4133 self.resource_fetch_timing()
4134 .as_ref()
4135 .map_or(0, |resource_fetch_timing| {
4136 resource_fetch_timing.redirect_count
4137 })
4138 }
4139
4140 pub(crate) fn set_resource_fetch_timing(&self, timing: ResourceFetchTiming) {
4141 self.resource_fetch_timing.replace(Some(timing));
4142 }
4143
4144 pub(crate) fn resource_fetch_timing(&self) -> Ref<'_, Option<ResourceFetchTiming>> {
4145 self.resource_fetch_timing.borrow()
4146 }
4147
4148 pub(crate) fn navigation_timing(&self) -> Rc<NavigationTiming> {
4149 self.navigation_timing.clone()
4150 }
4151
4152 pub(crate) fn performance_timing_attribute(
4153 &self,
4154 name: &str,
4155 ) -> Fallible<Option<CrossProcessInstant>> {
4156 Ok(match name {
4157 "unloadEventStart" => self.navigation_timing().unload_event_start.get(),
4158 "unloadEventEnd" => self.navigation_timing().unload_event_end.get(),
4159 "domInteractive" => self.navigation_timing().dom_interactive.get(),
4160 "domContentLoadedEventStart" => self
4161 .navigation_timing()
4162 .dom_content_loaded_event_start
4163 .get(),
4164 "domContentLoadedEventEnd" => {
4165 self.navigation_timing().dom_content_loaded_event_end.get()
4166 },
4167 "domComplete" => self.navigation_timing().dom_complete.get(),
4168 "loadEventStart" => self.navigation_timing().load_event_start.get(),
4169 "loadEventEnd" => self.navigation_timing().load_event_end.get(),
4170 "redirectStart" | "redirectEnd" | "secureConnectionStart" | "responseEnd" => self
4171 .resource_fetch_timing()
4172 .as_ref()
4173 .and_then(|resource_fetch_timing| match name {
4174 "redirectStart" => resource_fetch_timing.redirect_start,
4175 "redirectEnd" => resource_fetch_timing.redirect_end,
4176 "secureConnectionStart" => resource_fetch_timing.secure_connection_start,
4177 "responseEnd" => resource_fetch_timing.response_end,
4178 _ => None,
4179 }),
4180 _ => {
4181 return Err(Error::Operation(Some(format!(
4182 "{name} hasn't been implemented."
4183 ))));
4184 },
4185 })
4186 }
4187
4188 pub(crate) fn elements_by_name_count(&self, name: &DOMString) -> u32 {
4189 if name.is_empty() {
4190 return 0;
4191 }
4192 self.count_node_list(|n| Document::is_element_in_get_by_name(n, name))
4193 }
4194
4195 pub(crate) fn nth_element_by_name<'a>(
4196 &self,
4197 no_gc: &'a NoGC,
4198 index: u32,
4199 name: &DOMString,
4200 ) -> Option<UnrootedDom<'a, Node>> {
4201 if name.is_empty() {
4202 return None;
4203 }
4204 self.nth_in_node_list(no_gc, index, |n| {
4205 Document::is_element_in_get_by_name(n, name)
4206 })
4207 }
4208
4209 fn is_element_in_get_by_name(node: &Node, name: &DOMString) -> bool {
4212 let element = match node.downcast::<Element>() {
4213 Some(element) => element,
4214 None => return false,
4215 };
4216 if element.namespace() != &ns!(html) {
4217 return false;
4218 }
4219 element.get_name().is_some_and(|n| &*n == name)
4220 }
4221
4222 fn count_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> u32 {
4223 let doc = self.GetDocumentElement();
4224 let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
4225 maybe_node
4226 .iter()
4227 .flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
4228 .filter(|node| callback(node))
4229 .count() as u32
4230 }
4231
4232 fn nth_in_node_list<'a, F: Fn(&Node) -> bool>(
4233 &self,
4234 no_gc: &'a NoGC,
4235 index: u32,
4236 callback: F,
4237 ) -> Option<UnrootedDom<'a, Node>> {
4238 let doc = self.get_document_element_unrooted(no_gc)?;
4239 doc.upcast::<Node>()
4240 .traverse_preorder_non_rooting(no_gc, ShadowIncluding::No)
4241 .filter(|node| callback(node))
4242 .nth(index as usize)
4243 }
4244
4245 fn get_html_element(&self) -> Option<DomRoot<HTMLHtmlElement>> {
4246 self.GetDocumentElement().and_then(DomRoot::downcast)
4247 }
4248
4249 pub(crate) fn shared_style_locks(&self) -> &SharedRwLocks {
4251 &self.shared_style_locks
4252 }
4253
4254 pub(crate) fn style_shared_author_lock(&self) -> &SharedRwLock {
4256 &self.shared_style_locks.author
4257 }
4258
4259 pub(crate) fn flush_stylesheets_for_reflow(&self) -> bool {
4261 let mut stylesheets = self.stylesheets.borrow_mut();
4268 let have_changed = stylesheets.has_changed();
4269 stylesheets.flush_without_invalidation();
4270 have_changed
4271 }
4272
4273 pub(crate) fn salvageable(&self) -> bool {
4274 self.salvageable.get()
4275 }
4276
4277 pub(crate) fn make_document_unsalvageable(&self) {
4279 self.salvageable.set(false);
4285 }
4286
4287 pub(crate) fn appropriate_template_contents_owner_document(
4289 &self,
4290 cx: &mut JSContext,
4291 ) -> DomRoot<Document> {
4292 self.appropriate_template_contents_owner_document
4293 .or_init(|| {
4294 let doctype = if self.is_html_document {
4295 IsHTMLDocument::HTMLDocument
4296 } else {
4297 IsHTMLDocument::NonHTMLDocument
4298 };
4299 let new_doc = Document::new(
4300 cx,
4301 self.window(),
4302 HasBrowsingContext::No,
4303 None,
4304 None,
4305 MutableOrigin::new(ImmutableOrigin::new_opaque()),
4307 doctype,
4308 None,
4309 None,
4310 DocumentActivity::Inactive,
4311 DocumentSource::NotFromParser,
4312 DocumentLoader::new(&self.loader()),
4313 None,
4314 None,
4315 Default::default(),
4316 false,
4317 self.allow_declarative_shadow_roots(),
4318 Some(self.insecure_requests_policy()),
4319 self.has_trustworthy_ancestor_or_current_origin(),
4320 self.custom_element_reaction_stack.clone(),
4321 self.creation_sandboxing_flag_set(),
4322 self.pipeline_id(),
4323 self.image_cache.clone(),
4324 );
4325 new_doc
4326 .appropriate_template_contents_owner_document
4327 .set(Some(&new_doc));
4328 new_doc
4329 })
4330 }
4331
4332 pub(crate) fn get_element_by_id(&self, no_gc: &NoGC, id: &Atom) -> Option<DomRoot<Element>> {
4333 self.id_map.get(no_gc, self.upcast(), id)
4334 }
4335
4336 pub(crate) fn ensure_pending_restyle(&self, el: &Element) -> RefMut<'_, PendingRestyle> {
4337 let map = self.pending_restyles.borrow_mut();
4338 RefMut::map(map, |m| {
4339 &mut m
4340 .entry(Dom::from_ref(el))
4341 .or_insert_with(|| NoTrace(PendingRestyle::default()))
4342 .0
4343 })
4344 }
4345
4346 pub(crate) fn element_attr_will_change(&self, el: &Element, attr: AttrRef<'_>) {
4347 let mut entry = self.ensure_pending_restyle(el);
4353 if entry.snapshot.is_none() {
4354 entry.snapshot = Some(Snapshot::new());
4355 }
4356 if attr.local_name() == &local_name!("style") {
4357 entry.hint.insert(RestyleHint::RESTYLE_STYLE_ATTRIBUTE);
4358 }
4359
4360 if vtable_for(el.upcast()).attribute_affects_presentational_hints(attr) ||
4361 el.check_style_on_self_or_eager_pseudos(|style| {
4362 if let Some(ref attribute_references) = style.attribute_references {
4363 return attribute_references.contains_key(attr.local_name());
4364 }
4365 false
4366 })
4367 {
4368 entry.hint.insert(RestyleHint::RESTYLE_SELF);
4369 }
4370
4371 let snapshot = entry.snapshot.as_mut().unwrap();
4372 if attr.local_name() == &local_name!("id") {
4373 if snapshot.id_changed {
4374 return;
4375 }
4376 snapshot.id_changed = true;
4377 } else if attr.local_name() == &local_name!("class") {
4378 if snapshot.class_changed {
4379 return;
4380 }
4381 snapshot.class_changed = true;
4382 } else {
4383 snapshot.other_attributes_changed = true;
4384 }
4385 let local_name = style::LocalName::cast(attr.local_name());
4386 if !snapshot.changed_attrs.contains(local_name) {
4387 snapshot.changed_attrs.push(local_name.clone());
4388 }
4389 if snapshot.attrs.is_none() {
4390 let attrs = el
4391 .attrs()
4392 .borrow()
4393 .iter()
4394 .map(|attr| (attr.identifier().clone(), attr.value().clone()))
4395 .collect();
4396 snapshot.attrs = Some(attrs);
4397 }
4398 }
4399
4400 pub(crate) fn set_referrer_policy(&self, policy: ReferrerPolicy) {
4401 self.policy_container
4402 .borrow_mut()
4403 .set_referrer_policy(policy);
4404 }
4405
4406 pub(crate) fn get_referrer_policy(&self) -> ReferrerPolicy {
4407 self.policy_container.borrow().get_referrer_policy()
4408 }
4409
4410 pub(crate) fn set_target_element(&self, node: Option<&Element>) {
4411 if let Some(ref element) = self.target_element.get() {
4412 element.set_target_state(false);
4413 }
4414
4415 self.target_element.set(node);
4416
4417 if let Some(ref element) = self.target_element.get() {
4418 element.set_target_state(true);
4419 }
4420 }
4421
4422 pub(crate) fn incr_ignore_destructive_writes_counter(&self) {
4423 self.ignore_destructive_writes_counter
4424 .set(self.ignore_destructive_writes_counter.get() + 1);
4425 }
4426
4427 pub(crate) fn decr_ignore_destructive_writes_counter(&self) {
4428 self.ignore_destructive_writes_counter
4429 .set(self.ignore_destructive_writes_counter.get() - 1);
4430 }
4431
4432 pub(crate) fn is_prompting_or_unloading(&self) -> bool {
4433 self.ignore_opens_during_unload_counter.get() > 0
4434 }
4435
4436 fn incr_ignore_opens_during_unload_counter(&self) {
4437 self.ignore_opens_during_unload_counter
4438 .set(self.ignore_opens_during_unload_counter.get() + 1);
4439 }
4440
4441 fn decr_ignore_opens_during_unload_counter(&self) {
4442 self.ignore_opens_during_unload_counter
4443 .set(self.ignore_opens_during_unload_counter.get() - 1);
4444 }
4445
4446 pub(crate) fn set_fullscreen_element(&self, element: Option<&Element>) {
4447 self.fullscreen_element.set(element);
4448 }
4449
4450 fn reset_form_owner_for_listeners(&self, cx: &mut JSContext, id: &Atom) {
4451 let map = self.form_id_listener_map.borrow();
4452 if let Some(listeners) = map.get(id) {
4453 for listener in listeners {
4454 listener
4455 .as_maybe_form_control()
4456 .expect("Element must be a form control")
4457 .reset_form_owner(cx);
4458 }
4459 }
4460 }
4461
4462 pub(crate) fn register_shadow_root(&self, shadow_root: &ShadowRoot) {
4463 self.shadow_roots
4464 .borrow_mut()
4465 .insert(Dom::from_ref(shadow_root));
4466 self.invalidate_shadow_roots_stylesheets();
4467 }
4468
4469 pub(crate) fn unregister_shadow_root(&self, shadow_root: &ShadowRoot) {
4470 let mut shadow_roots = self.shadow_roots.borrow_mut();
4471 shadow_roots.remove(&Dom::from_ref(shadow_root));
4472 }
4473
4474 pub(crate) fn invalidate_shadow_roots_stylesheets(&self) {
4475 self.shadow_roots_styles_changed.set(true);
4476 }
4477
4478 pub(crate) fn flush_shadow_root_stylesheets_if_necessary_for_layout(
4479 &self,
4480 stylist: &mut Stylist,
4481 guard: &SharedRwLockReadGuard,
4482 ) {
4483 if !self.shadow_roots_styles_changed.get() {
4484 return;
4485 }
4486 #[expect(unsafe_code)]
4487 unsafe {
4488 for shadow_root in self.shadow_roots.borrow_for_layout().iter() {
4489 let layout: LayoutDom<'_, _> = shadow_root.to_layout();
4490 layout.flush_stylesheets_for_layout(stylist, guard);
4491 }
4492 }
4493 self.shadow_roots_styles_changed.set(false);
4494 }
4495
4496 pub(crate) fn stylesheet_count(&self) -> usize {
4497 self.stylesheets.borrow().len()
4498 }
4499
4500 pub(crate) fn stylesheet_at(
4501 &self,
4502 cx: &mut JSContext,
4503 index: usize,
4504 ) -> Option<DomRoot<CSSStyleSheet>> {
4505 let stylesheets = self.stylesheets.borrow();
4506
4507 stylesheets
4508 .get(Origin::Author, index)
4509 .and_then(|s| s.owner.get_cssom_object(cx))
4510 }
4511
4512 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) {
4519 let insertion_point = {
4520 let stylesheets = &mut *self.stylesheets.borrow_mut();
4521
4522 stylesheets
4524 .iter()
4525 .map(|(sheet, _origin)| sheet)
4526 .find(|sheet_in_doc| {
4527 match &sheet_in_doc.owner {
4528 StylesheetSource::Element(other_node) => {
4529 owner_node.upcast::<Node>().is_before(other_node.upcast())
4530 },
4531 StylesheetSource::Constructed(_) => true,
4534 }
4535 })
4536 .cloned()
4537 };
4538
4539 if self.has_browsing_context() {
4540 self.add_stylesheet_to_stylist(
4541 sheet.clone(),
4542 insertion_point.as_ref().map(|s| s.sheet.clone()),
4543 );
4544 }
4545
4546 let stylesheets = &mut *self.stylesheets.borrow_mut();
4547 DocumentOrShadowRoot::add_stylesheet(
4548 StylesheetSource::Element(Dom::from_ref(owner_node)),
4549 StylesheetSetRef::Document(stylesheets),
4550 sheet,
4551 insertion_point,
4552 self.style_shared_author_lock(),
4553 );
4554 }
4555
4556 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
4561 pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
4562 debug_assert!(cssom_stylesheet.is_constructed());
4563
4564 let sheet = cssom_stylesheet.style_stylesheet().clone();
4565 let insertion_point = {
4566 let stylesheets = &mut *self.stylesheets.borrow_mut();
4567
4568 stylesheets
4569 .iter()
4570 .last()
4571 .map(|(sheet, _origin)| sheet)
4572 .cloned()
4573 };
4574
4575 if self.has_browsing_context() {
4576 self.add_stylesheet_to_stylist(
4577 sheet.clone(),
4578 insertion_point.as_ref().map(|s| s.sheet.clone()),
4579 );
4580 }
4581
4582 let stylesheets = &mut *self.stylesheets.borrow_mut();
4583 DocumentOrShadowRoot::add_stylesheet(
4584 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
4585 StylesheetSetRef::Document(stylesheets),
4586 sheet,
4587 insertion_point,
4588 self.style_shared_author_lock(),
4589 );
4590 }
4591
4592 pub(crate) fn add_stylesheet_to_stylist(
4593 &self,
4594 stylesheet: Arc<Stylesheet>,
4595 before_stylesheet: Option<Arc<Stylesheet>>,
4596 ) {
4597 self.window
4598 .layout_mut()
4599 .add_stylesheet(stylesheet, before_stylesheet);
4600 }
4601
4602 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, stylesheet: &Arc<Stylesheet>) {
4605 if self.has_browsing_context() {
4606 self.window
4607 .layout_mut()
4608 .remove_stylesheet(stylesheet.clone());
4609 }
4610
4611 DocumentOrShadowRoot::remove_stylesheet(
4612 owner,
4613 stylesheet,
4614 StylesheetSetRef::Document(&mut *self.stylesheets.borrow_mut()),
4615 )
4616 }
4617
4618 pub(crate) fn get_elements_with_id(
4619 &self,
4620 cx: &mut JSContext,
4621 id: &Atom,
4622 ) -> Ref<'_, [Dom<Element>]> {
4623 self.id_map.get_all(cx.no_gc(), self.upcast(), id)
4624 }
4625
4626 pub(crate) fn get_elements_with_name(
4627 &self,
4628 cx: &mut JSContext,
4629 name: &Atom,
4630 ) -> Ref<'_, [Dom<Element>]> {
4631 self.name_map.get_all(cx.no_gc(), self.upcast(), name)
4632 }
4633
4634 pub(crate) fn drain_pending_restyles(
4635 &self,
4636 no_gc: &NoGC,
4637 ) -> Vec<(TrustedNodeAddress, PendingRestyle)> {
4638 self.pending_restyles
4639 .borrow_mut()
4640 .drain()
4641 .filter_map(|(element, restyle)| {
4642 let node = element.upcast::<Node>();
4643 if !node.get_flag(NodeFlags::IS_CONNECTED) {
4644 return None;
4645 }
4646 element.note_dirty_descendants(no_gc);
4647 Some((node.to_trusted_node_address(), restyle.0))
4648 })
4649 .collect()
4650 }
4651
4652 pub(crate) fn advance_animation_timeline_for_testing(&self, delta: TimeDuration) {
4653 self.timeline.advance_specific(delta);
4654 let current_timeline_value = self.current_animation_timeline_value();
4655 self.animations
4656 .update_for_new_timeline_value(&self.window, current_timeline_value);
4657 }
4658
4659 pub(crate) fn maybe_mark_animating_nodes_as_dirty(&self, no_gc: &NoGC) {
4660 let current_timeline_value = self.current_animation_timeline_value();
4661 self.animations
4662 .mark_animating_nodes_as_dirty(no_gc, current_timeline_value);
4663 }
4664
4665 pub(crate) fn current_animation_timeline_value(&self) -> f64 {
4666 self.timeline
4667 .upcast::<AnimationTimeline>()
4668 .current_time_in_seconds()
4669 }
4670
4671 pub(crate) fn animations(&self) -> &Animations {
4672 &self.animations
4673 }
4674
4675 pub(crate) fn update_animations_post_reflow(&self) {
4676 let current_timeline_value = self.current_animation_timeline_value();
4677 self.animations
4678 .do_post_reflow_update(&self.window, current_timeline_value);
4679 self.image_animation_manager
4680 .borrow_mut()
4681 .do_post_reflow_update(&self.window, current_timeline_value);
4682 }
4683
4684 pub(crate) fn cancel_animations_for_node(&self, node: &Node) {
4685 self.animations.cancel_animations_for_node(node);
4686 self.image_animation_manager
4687 .borrow_mut()
4688 .cancel_animations_for_node(node);
4689 }
4690
4691 pub(crate) fn remove_style_and_layout_data_from_subtree(
4695 &self,
4696 no_gc: &NoGC,
4697 subtree_root: &Node,
4698 ) {
4699 for node in subtree_root.traverse_preorder_non_rooting(no_gc, ShadowIncluding::Yes) {
4700 self.clean_up_style_and_layout_data_for_node(&node);
4701 }
4702 }
4703
4704 pub(crate) fn clean_up_style_and_layout_data_for_node(&self, node: &Node) {
4705 node.clear_layout_data();
4706 if let Some(element) = node.downcast::<Element>() {
4707 element.clean_up_style_data();
4708
4709 if self.dirty_root == Some(element) {
4714 self.dirty_root.clear();
4715 }
4716 }
4717
4718 node.set_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION, false);
4719 node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false);
4720 }
4721
4722 pub(crate) fn update_animations_and_send_events(&self, cx: &mut CurrentRealm) {
4724 if !self.layout_animations_test_enabled {
4726 self.timeline.update(self.window());
4727 }
4728
4729 let current_timeline_value = self.current_animation_timeline_value();
4736 self.animations
4737 .update_for_new_timeline_value(&self.window, current_timeline_value);
4738 self.maybe_mark_animating_nodes_as_dirty(cx.no_gc());
4739
4740 self.window().perform_a_microtask_checkpoint(cx);
4742
4743 self.animations().send_pending_events(self.window(), cx);
4745 }
4746
4747 pub(crate) fn image_animation_manager(&self) -> Ref<'_, ImageAnimationManager> {
4748 self.image_animation_manager.borrow()
4749 }
4750
4751 pub(crate) fn set_has_pending_animated_image_update(&self) {
4752 self.has_pending_animated_image_update.set(true);
4753 }
4754
4755 pub(crate) fn shared_declarative_refresh_steps(&self, content: &[u8], from_meta_element: bool) {
4757 if self.will_declaratively_refresh() {
4759 return;
4760 }
4761
4762 static REFRESH_REGEX: LazyLock<Regex> = LazyLock::new(|| {
4764 Regex::new(
4768 r#"(?xs)
4769 ^
4770 \s* # 3
4771 ((?<time>[0-9]+)|\.) # 5-6
4772 [0-9.]* # 8
4773 (
4774 (
4775 (\s*;|\s*,|\s) # 10.3
4776 \s* # 10.4
4777 )
4778 (
4779 (
4780 (U|u)(R|r)(L|l) # 11.2-11.4
4781 \s*=\s* # 11.5-11.7
4782 )?
4783 ('(?<url1>[^']*)'(?s-u:.)*|"(?<url2>[^"]*)"(?s-u:.)*|['"]?(?<url3>(?s-u:.)*)) # 11.8 - 11.10
4784 |
4785 (?<url4>(?s-u:.)*)
4786 )
4787 )?
4788 $
4789 "#,
4790 )
4791 .unwrap()
4792 });
4793
4794 let mut url_record = self.url();
4796 let captures = if let Some(captures) = REFRESH_REGEX.captures(content) {
4797 captures
4798 } else {
4799 return;
4800 };
4801 let time = if let Some(time_string) = captures.name("time") {
4802 u64::from_str(&String::from_utf8_lossy(time_string.as_bytes())).unwrap_or(0)
4803 } else {
4804 0
4805 };
4806 let captured_url = captures.name("url1").or(captures
4807 .name("url2")
4808 .or(captures.name("url3").or(captures.name("url4"))));
4809
4810 if let Some(url_match) = captured_url {
4812 url_record = if let Ok(url) = ServoUrl::parse_with_base(
4813 Some(&url_record),
4814 &String::from_utf8_lossy(url_match.as_bytes()),
4815 ) {
4816 info!("Refresh to {}", url.debug_compact());
4817 url
4818 } else {
4819 return;
4821 };
4822 if url_record.scheme() == "javascript" {
4824 return;
4825 }
4826 }
4827 if self.completely_loaded() {
4829 self.window.as_global_scope().schedule_callback(
4830 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
4831 url: url_record,
4832 from_meta_element,
4833 }),
4834 Duration::from_secs(time),
4835 );
4836 self.set_declarative_refresh(DeclarativeRefresh::CreatedAfterLoad);
4837 } else {
4838 self.set_declarative_refresh(DeclarativeRefresh::PendingLoad {
4839 url: url_record,
4840 time,
4841 from_meta_element,
4842 });
4843 }
4844 }
4845
4846 pub(crate) fn will_declaratively_refresh(&self) -> bool {
4847 self.declarative_refresh.borrow().is_some()
4848 }
4849 pub(crate) fn set_declarative_refresh(&self, refresh: DeclarativeRefresh) {
4850 *self.declarative_refresh.borrow_mut() = Some(refresh);
4851 }
4852
4853 fn update_visibility_state(
4855 &self,
4856 cx: &mut JSContext,
4857 visibility_state: DocumentVisibilityState,
4858 ) {
4859 if self.visibility_state.get() == visibility_state {
4861 return;
4862 }
4863 self.visibility_state.set(visibility_state);
4865 let entry = VisibilityStateEntry::new(
4868 cx,
4869 &self.global(),
4870 visibility_state,
4871 CrossProcessInstant::now(),
4872 );
4873 self.window
4874 .Performance(cx)
4875 .queue_entry(entry.upcast::<PerformanceEntry>());
4876
4877 #[cfg(feature = "gamepad")]
4888 if visibility_state == DocumentVisibilityState::Hidden {
4889 self.window
4890 .Navigator(cx)
4891 .GetGamepads(cx)
4892 .unwrap_or_default()
4893 .iter_mut()
4894 .for_each(|gamepad| {
4895 if let Some(g) = gamepad {
4896 g.vibration_actuator().handle_visibility_change();
4897 }
4898 });
4899 }
4900
4901 self.upcast::<EventTarget>()
4903 .fire_bubbling_event(cx, atom!("visibilitychange"));
4904 }
4905
4906 pub(crate) fn is_initial_about_blank(&self) -> bool {
4908 self.is_initial_about_blank.get()
4909 }
4910
4911 pub(crate) fn allow_declarative_shadow_roots(&self) -> bool {
4913 self.allow_declarative_shadow_roots.get()
4914 }
4915
4916 pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
4917 self.has_trustworthy_ancestor_origin.get()
4918 }
4919
4920 pub(crate) fn has_trustworthy_ancestor_or_current_origin(&self) -> bool {
4921 self.has_trustworthy_ancestor_origin.get() ||
4922 self.origin().immutable().is_potentially_trustworthy()
4923 }
4924
4925 pub(crate) fn highlight_dom_node(&self, node: Option<&Node>) {
4926 self.highlighted_dom_node.set(node);
4927 self.add_restyle_reason(RestyleReason::HighlightedDOMNodeChanged);
4928 }
4929
4930 pub(crate) fn highlighted_dom_node(&self) -> Option<DomRoot<Node>> {
4931 self.highlighted_dom_node.get()
4932 }
4933
4934 pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
4935 self.custom_element_reaction_stack.clone()
4936 }
4937
4938 pub(crate) fn active_sandboxing_flag_set(&self) -> SandboxingFlagSet {
4939 self.active_sandboxing_flag_set.get()
4940 }
4941
4942 pub(crate) fn has_active_sandboxing_flag(&self, flag: SandboxingFlagSet) -> bool {
4943 self.active_sandboxing_flag_set.get().contains(flag)
4944 }
4945
4946 pub(crate) fn set_active_sandboxing_flag_set(&self, flags: SandboxingFlagSet) {
4947 self.active_sandboxing_flag_set.set(flags)
4948 }
4949
4950 pub(crate) fn creation_sandboxing_flag_set(&self) -> SandboxingFlagSet {
4951 self.creation_sandboxing_flag_set.get()
4952 }
4953
4954 pub(crate) fn creation_sandboxing_flag_set_considering_parent_iframe(
4955 &self,
4956 ) -> SandboxingFlagSet {
4957 self.window()
4958 .window_proxy()
4959 .frame_element()
4960 .and_then(|element| element.downcast::<HTMLIFrameElement>())
4961 .map(HTMLIFrameElement::sandboxing_flag_set)
4962 .unwrap_or_else(|| self.creation_sandboxing_flag_set())
4963 }
4964
4965 pub(crate) fn viewport_scrolling_box(&self, flags: ScrollContainerQueryFlags) -> ScrollingBox {
4966 self.window()
4967 .scrolling_box_query(None, flags)
4968 .expect("We should always have a ScrollingBox for the Viewport")
4969 }
4970
4971 pub(crate) fn notify_embedder_favicon(&self) {
4972 if let Some(ref image) = *self.favicon.borrow() {
4973 self.send_to_embedder(EmbedderMsg::NewFavicon(self.webview_id(), image.clone()));
4974 }
4975 }
4976
4977 pub(crate) fn set_favicon(&self, favicon: Image) {
4978 *self.favicon.borrow_mut() = Some(favicon);
4979 self.notify_embedder_favicon();
4980 }
4981
4982 pub(crate) fn fullscreen_element(&self) -> Option<DomRoot<Element>> {
4983 self.fullscreen_element.get()
4984 }
4985
4986 pub(crate) fn state_override(&self, command_name: &CommandName) -> Option<bool> {
4988 self.state_override.borrow().get(command_name).copied()
4989 }
4990
4991 pub(crate) fn set_state_override(&self, command_name: CommandName, state: Option<bool>) {
4993 if let Some(state) = state {
4994 self.state_override.borrow_mut().insert(command_name, state);
4995 } else {
4996 self.value_override.borrow_mut().remove(&command_name);
4997 }
4998 }
4999
5000 pub(crate) fn value_override(&self, command_name: &CommandName) -> Option<DOMString> {
5002 self.value_override.borrow().get(command_name).cloned()
5003 }
5004
5005 pub(crate) fn set_value_override(&self, command_name: CommandName, value: Option<DOMString>) {
5007 if let Some(value) = value {
5008 self.value_override.borrow_mut().insert(command_name, value);
5009 } else {
5010 self.value_override.borrow_mut().remove(&command_name);
5011 }
5012 }
5013
5014 pub(crate) fn clear_command_overrides(&self) {
5017 self.state_override.borrow_mut().clear();
5018 self.value_override.borrow_mut().clear();
5019 }
5020
5021 pub(crate) fn default_single_line_container_name(&self) -> DefaultSingleLineContainerName {
5023 self.default_single_line_container_name.get()
5024 }
5025
5026 pub(crate) fn set_default_single_line_container_name(
5028 &self,
5029 value: DefaultSingleLineContainerName,
5030 ) {
5031 self.default_single_line_container_name.set(value)
5032 }
5033
5034 pub(crate) fn css_styling_flag(&self) -> bool {
5036 self.css_styling_flag.get()
5037 }
5038
5039 pub(crate) fn set_css_styling_flag(&self, value: bool) {
5041 self.css_styling_flag.set(value)
5042 }
5043
5044 pub(crate) fn mute_iframe_load_flag(&self) -> bool {
5045 self.mute_iframe_load.get()
5046 }
5047
5048 pub(crate) fn set_iframe_load_in_progress(&self, value: bool) {
5049 self.iframe_load_in_progress.set(value)
5050 }
5051}
5052
5053impl DocumentMethods<crate::DomTypeHolder> for Document {
5054 fn Constructor(
5056 cx: &mut JSContext,
5057 window: &Window,
5058 proto: Option<HandleObject>,
5059 ) -> Fallible<DomRoot<Document>> {
5060 let doc = window.Document();
5062 let docloader = DocumentLoader::new(&doc.loader());
5063 Ok(Document::new_with_proto(
5064 cx,
5065 window,
5066 proto,
5067 HasBrowsingContext::No,
5068 None,
5069 None,
5070 doc.origin().clone(),
5071 IsHTMLDocument::NonHTMLDocument,
5072 None,
5073 None,
5074 DocumentActivity::Inactive,
5075 DocumentSource::NotFromParser,
5076 docloader,
5077 None,
5078 None,
5079 Default::default(),
5080 false,
5081 doc.allow_declarative_shadow_roots(),
5082 Some(doc.insecure_requests_policy()),
5083 doc.has_trustworthy_ancestor_or_current_origin(),
5084 doc.custom_element_reaction_stack(),
5085 doc.active_sandboxing_flag_set.get(),
5086 doc.pipeline_id(),
5087 doc.image_cache(),
5088 ))
5089 }
5090
5091 fn ParseHTMLUnsafe(
5093 cx: &mut JSContext,
5094 window: &Window,
5095 s: TrustedHTMLOrString,
5096 options: &SetHTMLUnsafeOptions,
5097 ) -> Fallible<DomRoot<Self>> {
5098 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
5102 cx,
5103 window.as_global_scope(),
5104 s,
5105 "Document parseHTMLUnsafe",
5106 )?;
5107
5108 let url = window.get_url();
5109 let doc = window.Document();
5110 let loader = DocumentLoader::new(&doc.loader());
5111
5112 let content_type = "text/html"
5113 .parse()
5114 .expect("Supported type is not a MIME type");
5115 let document = Document::new(
5118 cx,
5119 window,
5120 HasBrowsingContext::No,
5121 Some(ServoUrl::parse("about:blank").unwrap()),
5122 None,
5123 doc.origin().clone(),
5124 IsHTMLDocument::HTMLDocument,
5125 Some(content_type),
5126 None,
5127 DocumentActivity::Inactive,
5128 DocumentSource::FromParser,
5129 loader,
5130 None,
5131 None,
5132 Default::default(),
5133 false,
5134 true,
5135 Some(doc.insecure_requests_policy()),
5136 doc.has_trustworthy_ancestor_or_current_origin(),
5137 doc.custom_element_reaction_stack(),
5138 doc.creation_sandboxing_flag_set(),
5139 doc.pipeline_id(),
5140 doc.image_cache(),
5141 );
5142 ServoParser::parse_html_document(cx, &document, Some(compliant_html), url, None, None);
5144
5145 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, false)?;
5148
5149 sanitizer.sanitize(cx, document.upcast(), false)?;
5151
5152 document.set_ready_state(cx, DocumentReadyState::Complete);
5154 Ok(document)
5155 }
5156
5157 fn ParseHTML(
5159 cx: &mut JSContext,
5160 window: &Window,
5161 html: DOMString,
5162 options: &SetHTMLOptions,
5163 ) -> Fallible<DomRoot<Document>> {
5164 let url = window.get_url();
5167 let doc = window.Document();
5168 let loader = DocumentLoader::new(&doc.loader());
5169 let content_type = "text/html"
5170 .parse()
5171 .expect("Supported type is not a MIME type");
5172 let document = Document::new(
5173 cx,
5174 window,
5175 HasBrowsingContext::No,
5176 Some(ServoUrl::parse("about:blank").unwrap()),
5177 None,
5178 doc.origin().clone(),
5179 IsHTMLDocument::HTMLDocument,
5180 Some(content_type),
5181 None,
5182 DocumentActivity::Inactive,
5183 DocumentSource::FromParser,
5184 loader,
5185 None,
5186 None,
5187 Default::default(),
5188 false,
5189 true,
5190 Some(doc.insecure_requests_policy()),
5191 doc.has_trustworthy_ancestor_or_current_origin(),
5192 doc.custom_element_reaction_stack(),
5193 doc.creation_sandboxing_flag_set(),
5194 doc.pipeline_id(),
5195 doc.image_cache(),
5196 );
5197
5198 ServoParser::parse_html_document(cx, &document, Some(html), url, None, None);
5200
5201 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, true)?;
5204
5205 sanitizer.sanitize(cx, document.upcast(), true)?;
5207
5208 Ok(document)
5210 }
5211
5212 fn StyleSheets(&self, cx: &mut JSContext) -> DomRoot<StyleSheetList> {
5214 self.stylesheet_list.or_init(|| {
5215 StyleSheetList::new(
5216 cx,
5217 &self.window,
5218 StyleSheetListOwner::Document(Dom::from_ref(self)),
5219 )
5220 })
5221 }
5222
5223 fn Implementation(&self, cx: &mut JSContext) -> DomRoot<DOMImplementation> {
5225 self.implementation
5226 .or_init(|| DOMImplementation::new(cx, self))
5227 }
5228
5229 fn URL(&self) -> USVString {
5231 USVString(String::from(self.url().as_str()))
5232 }
5233
5234 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
5236 self.document_or_shadow_root.active_element(self.upcast())
5237 }
5238
5239 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
5241 self.custom_element_registry()
5242 }
5243
5244 fn HasFocus(&self) -> bool {
5246 if self.window().parent_info().is_none() {
5268 self.is_fully_active()
5270 } else {
5271 self.is_fully_active() && self.focus_handler.has_focus()
5273 }
5274 }
5275
5276 fn Domain(&self) -> DOMString {
5278 match self.origin().effective_domain() {
5280 None => DOMString::new(),
5282 Some(Host::Domain(domain)) => DOMString::from(domain),
5284 Some(host) => DOMString::from(host.to_string()),
5285 }
5286 }
5287
5288 fn SetDomain(&self, value: DOMString) -> ErrorResult {
5290 if !self.has_browsing_context {
5292 return Err(Error::Security(None));
5293 }
5294
5295 if self.has_active_sandboxing_flag(
5298 SandboxingFlagSet::SANDBOXED_DOCUMENT_DOMAIN_BROWSING_CONTEXT_FLAG,
5299 ) {
5300 return Err(Error::Security(None));
5301 }
5302
5303 let effective_domain = match self.origin().effective_domain() {
5305 Some(effective_domain) => effective_domain,
5306 None => return Err(Error::Security(None)),
5308 };
5309
5310 let host =
5312 match get_registrable_domain_suffix_of_or_is_equal_to(&value.str(), effective_domain) {
5313 None => return Err(Error::Security(None)),
5314 Some(host) => host,
5315 };
5316
5317 self.origin().set_domain(host);
5322
5323 Ok(())
5324 }
5325
5326 fn Referrer(&self) -> DOMString {
5328 match self.referrer {
5329 Some(ref referrer) => DOMString::from(referrer.to_string()),
5330 None => DOMString::new(),
5331 }
5332 }
5333
5334 fn DocumentURI(&self) -> USVString {
5336 self.URL()
5337 }
5338
5339 fn CompatMode(&self) -> DOMString {
5341 DOMString::from(match self.quirks_mode.get() {
5342 QuirksMode::LimitedQuirks | QuirksMode::NoQuirks => "CSS1Compat",
5343 QuirksMode::Quirks => "BackCompat",
5344 })
5345 }
5346
5347 fn CharacterSet(&self) -> DOMString {
5349 DOMString::from(self.encoding.get().name())
5350 }
5351
5352 fn Charset(&self) -> DOMString {
5354 self.CharacterSet()
5355 }
5356
5357 fn InputEncoding(&self) -> DOMString {
5359 self.CharacterSet()
5360 }
5361
5362 fn ContentType(&self) -> DOMString {
5364 DOMString::from(self.content_type.to_string())
5365 }
5366
5367 fn GetDoctype(&self) -> Option<DomRoot<DocumentType>> {
5369 self.upcast::<Node>().children().find_map(DomRoot::downcast)
5370 }
5371
5372 fn GetDocumentElement(&self) -> Option<DomRoot<Element>> {
5374 self.upcast::<Node>().child_elements().next()
5375 }
5376
5377 fn GetElementsByTagName(
5379 &self,
5380 cx: &mut JSContext,
5381 qualified_name: DOMString,
5382 ) -> DomRoot<HTMLCollection> {
5383 let qualified_name = LocalName::from(qualified_name);
5384 if let Some(entry) = self.tag_map.borrow_mut().get(&qualified_name) {
5385 return DomRoot::from_ref(entry);
5386 }
5387 let result = HTMLCollection::by_qualified_name(
5388 cx,
5389 &self.window,
5390 self.upcast(),
5391 qualified_name.clone(),
5392 );
5393 self.tag_map
5394 .borrow_mut()
5395 .insert(qualified_name, Dom::from_ref(&*result));
5396 result
5397 }
5398
5399 fn GetElementsByTagNameNS(
5401 &self,
5402 cx: &mut JSContext,
5403 maybe_ns: Option<DOMString>,
5404 tag_name: DOMString,
5405 ) -> DomRoot<HTMLCollection> {
5406 let ns = namespace_from_domstring(maybe_ns);
5407 let local = LocalName::from(tag_name);
5408 let qname = QualName::new(None, ns, local);
5409 if let Some(collection) = self.tagns_map.borrow().get(&qname) {
5410 return DomRoot::from_ref(collection);
5411 }
5412 let result =
5413 HTMLCollection::by_qual_tag_name(cx, &self.window, self.upcast(), qname.clone());
5414 self.tagns_map
5415 .borrow_mut()
5416 .insert(qname, Dom::from_ref(&*result));
5417 result
5418 }
5419
5420 fn GetElementsByClassName(
5422 &self,
5423 cx: &mut JSContext,
5424 classes: DOMString,
5425 ) -> DomRoot<HTMLCollection> {
5426 let class_atoms: Vec<Atom> = split_html_space_chars(&classes.str())
5427 .map(Atom::from)
5428 .collect();
5429 if let Some(collection) = self.classes_map.borrow().get(&class_atoms) {
5430 return DomRoot::from_ref(collection);
5431 }
5432 let result = HTMLCollection::by_atomic_class_name(
5433 cx,
5434 &self.window,
5435 self.upcast(),
5436 class_atoms.clone(),
5437 );
5438 self.classes_map
5439 .borrow_mut()
5440 .insert(class_atoms, Dom::from_ref(&*result));
5441 result
5442 }
5443
5444 fn GetElementById(
5446 &self,
5447 cx: &js::context::JSContext,
5448 id: DOMString,
5449 ) -> Option<DomRoot<Element>> {
5450 self.get_element_by_id(cx, &Atom::from(id))
5451 }
5452
5453 fn CreateElement(
5455 &self,
5456 cx: &mut JSContext,
5457 mut local_name: DOMString,
5458 options: StringOrElementCreationOptions,
5459 ) -> Fallible<DomRoot<Element>> {
5460 if !is_valid_element_local_name(&local_name.str()) {
5463 debug!("Not a valid element name");
5464 return Err(Error::InvalidCharacter(None));
5465 }
5466
5467 if self.is_html_document {
5468 local_name.make_ascii_lowercase();
5469 }
5470
5471 let ns = if self.is_html_document || self.is_xhtml_document() {
5472 ns!(html)
5473 } else {
5474 ns!()
5475 };
5476
5477 let name = QualName::new(None, ns, LocalName::from(local_name));
5478 let is = match options {
5479 StringOrElementCreationOptions::String(_) => None,
5480 StringOrElementCreationOptions::ElementCreationOptions(options) => {
5481 options.is.as_ref().map(LocalName::from)
5482 },
5483 };
5484 Ok(Element::create(
5485 cx,
5486 name,
5487 is,
5488 self,
5489 ElementCreator::ScriptCreated,
5490 CustomElementCreationMode::Synchronous,
5491 None,
5492 ))
5493 }
5494
5495 fn CreateElementNS(
5497 &self,
5498 cx: &mut JSContext,
5499 namespace: Option<DOMString>,
5500 qualified_name: DOMString,
5501 options: StringOrElementCreationOptions,
5502 ) -> Fallible<DomRoot<Element>> {
5503 let context = domname::Context::Element;
5506 let (namespace, prefix, local_name) =
5507 domname::validate_and_extract(namespace, &qualified_name, context)?;
5508
5509 let name = QualName::new(prefix, namespace, local_name);
5512 let is = match options {
5513 StringOrElementCreationOptions::String(_) => None,
5514 StringOrElementCreationOptions::ElementCreationOptions(options) => {
5515 options.is.as_ref().map(LocalName::from)
5516 },
5517 };
5518
5519 Ok(Element::create(
5521 cx,
5522 name,
5523 is,
5524 self,
5525 ElementCreator::ScriptCreated,
5526 CustomElementCreationMode::Synchronous,
5527 None,
5528 ))
5529 }
5530
5531 fn CreateAttribute(
5533 &self,
5534 cx: &mut JSContext,
5535 mut local_name: DOMString,
5536 ) -> Fallible<DomRoot<Attr>> {
5537 if !is_valid_attribute_local_name(&local_name.str()) {
5540 debug!("Not a valid attribute name");
5541 return Err(Error::InvalidCharacter(None));
5542 }
5543 if self.is_html_document {
5544 local_name.make_ascii_lowercase();
5545 }
5546 let name = LocalName::from(local_name);
5547 let value = AttrValue::String("".to_owned());
5548
5549 Ok(Attr::new(
5550 cx,
5551 self,
5552 name.clone(),
5553 value,
5554 name,
5555 ns!(),
5556 None,
5557 None,
5558 ))
5559 }
5560
5561 fn CreateAttributeNS(
5563 &self,
5564 cx: &mut JSContext,
5565 namespace: Option<DOMString>,
5566 qualified_name: DOMString,
5567 ) -> Fallible<DomRoot<Attr>> {
5568 let context = domname::Context::Attribute;
5571 let (namespace, prefix, local_name) =
5572 domname::validate_and_extract(namespace, &qualified_name, context)?;
5573 let value = AttrValue::String("".to_owned());
5574 let qualified_name = LocalName::from(qualified_name);
5575 Ok(Attr::new(
5576 cx,
5577 self,
5578 local_name,
5579 value,
5580 qualified_name,
5581 namespace,
5582 prefix,
5583 None,
5584 ))
5585 }
5586
5587 fn CreateDocumentFragment(&self, cx: &mut JSContext) -> DomRoot<DocumentFragment> {
5589 DocumentFragment::new(cx, self)
5590 }
5591
5592 fn CreateTextNode(&self, cx: &mut JSContext, data: DOMString) -> DomRoot<Text> {
5594 Text::new(cx, data, self)
5595 }
5596
5597 fn CreateCDATASection(
5599 &self,
5600 cx: &mut JSContext,
5601 data: DOMString,
5602 ) -> Fallible<DomRoot<CDATASection>> {
5603 if self.is_html_document {
5605 return Err(Error::NotSupported(None));
5606 }
5607
5608 if data.contains("]]>") {
5610 return Err(Error::InvalidCharacter(None));
5611 }
5612
5613 Ok(CDATASection::new(cx, data, self))
5615 }
5616
5617 fn CreateComment(&self, cx: &mut JSContext, data: DOMString) -> DomRoot<Comment> {
5619 Comment::new(cx, data, self, None)
5620 }
5621
5622 fn CreateProcessingInstruction(
5624 &self,
5625 cx: &mut JSContext,
5626 target: DOMString,
5627 data: DOMString,
5628 ) -> Fallible<DomRoot<ProcessingInstruction>> {
5629 if !matches_name_production(&target.str()) {
5631 return Err(Error::InvalidCharacter(None));
5632 }
5633
5634 if data.contains("?>") {
5636 return Err(Error::InvalidCharacter(None));
5637 }
5638
5639 Ok(ProcessingInstruction::new(cx, target, data, self))
5641 }
5642
5643 fn ImportNode(
5645 &self,
5646 cx: &mut JSContext,
5647 node: &Node,
5648 options: BooleanOrImportNodeOptions,
5649 ) -> Fallible<DomRoot<Node>> {
5650 if node.is::<Document>() || node.is::<ShadowRoot>() {
5652 return Err(Error::NotSupported(None));
5653 }
5654 let (subtree, registry) = match options {
5656 BooleanOrImportNodeOptions::Boolean(boolean) => (boolean.into(), None),
5659 BooleanOrImportNodeOptions::ImportNodeOptions(options) => {
5661 let subtree = (!options.selfOnly).into();
5663 let registry = if let Some(registry) = options.customElementRegistry {
5665 let this_registry = self
5668 .custom_element_registry()
5669 .expect("Document must have a custom element registry");
5670 if !registry.is_scoped() && registry != this_registry {
5671 return Err(Error::NotSupported(Some(
5672 "Imported customElementRegistry is not scoped and does not match existing registry.".into()
5673 )));
5674 }
5675 Some(registry)
5676 } else {
5677 None
5678 };
5679 (subtree, registry)
5680 },
5681 };
5682 let registry = registry
5685 .or_else(|| CustomElementRegistry::lookup_a_custom_element_registry(self.upcast()));
5686
5687 Ok(Node::clone(cx, node, Some(self), subtree, registry))
5690 }
5691
5692 fn AdoptNode(&self, cx: &mut JSContext, node: &Node) -> Fallible<DomRoot<Node>> {
5694 if node.is::<Document>() {
5696 return Err(Error::NotSupported(None));
5697 }
5698
5699 if node.is::<ShadowRoot>() {
5701 return Err(Error::HierarchyRequest(None));
5702 }
5703
5704 Node::adopt(cx, node, self);
5706
5707 Ok(DomRoot::from_ref(node))
5709 }
5710
5711 fn CreateEvent(
5713 &self,
5714 cx: &mut JSContext,
5715 mut interface: DOMString,
5716 ) -> Fallible<DomRoot<Event>> {
5717 interface.make_ascii_lowercase();
5718 match &*interface.str() {
5719 "beforeunloadevent" => Ok(DomRoot::upcast(BeforeUnloadEvent::new_uninitialized(
5720 cx,
5721 &self.window,
5722 ))),
5723 "compositionevent" | "textevent" => Ok(DomRoot::upcast(
5724 CompositionEvent::new_uninitialized(cx, &self.window),
5725 )),
5726 "customevent" => Ok(DomRoot::upcast(CustomEvent::new_uninitialized(
5727 cx,
5728 self.window.upcast(),
5729 ))),
5730 "events" | "event" | "htmlevents" | "svgevents" => {
5733 Ok(Event::new_uninitialized(cx, self.window.upcast()))
5734 },
5735 "focusevent" => Ok(DomRoot::upcast(FocusEvent::new_uninitialized(
5736 cx,
5737 &self.window,
5738 ))),
5739 "hashchangeevent" => Ok(DomRoot::upcast(HashChangeEvent::new_uninitialized(
5740 cx,
5741 &self.window,
5742 ))),
5743 "keyboardevent" => Ok(DomRoot::upcast(KeyboardEvent::new_uninitialized(
5744 cx,
5745 &self.window,
5746 ))),
5747 "messageevent" => Ok(DomRoot::upcast(MessageEvent::new_uninitialized(
5748 cx,
5749 self.window.upcast(),
5750 ))),
5751 "mouseevent" | "mouseevents" => Ok(DomRoot::upcast(MouseEvent::new_uninitialized(
5752 cx,
5753 &self.window,
5754 ))),
5755 "storageevent" => Ok(DomRoot::upcast(StorageEvent::new_uninitialized(
5756 cx,
5757 &self.window,
5758 "".into(),
5759 ))),
5760 "touchevent" => {
5761 let touches = TouchList::new(cx, &self.window, &[]);
5762 let changed_touches = TouchList::new(cx, &self.window, &[]);
5763 let target_touches = TouchList::new(cx, &self.window, &[]);
5764
5765 Ok(DomRoot::upcast(DomTouchEvent::new_uninitialized(
5766 cx,
5767 &self.window,
5768 &touches,
5769 &changed_touches,
5770 &target_touches,
5771 )))
5772 },
5773 "uievent" | "uievents" => Ok(DomRoot::upcast(UIEvent::new_uninitialized(
5774 cx,
5775 &self.window,
5776 ))),
5777 _ => Err(Error::NotSupported(None)),
5778 }
5779 }
5780
5781 fn LastModified(&self) -> DOMString {
5783 DOMString::from(self.last_modified.as_ref().cloned().unwrap_or_else(|| {
5784 Local::now().format("%m/%d/%Y %H:%M:%S").to_string()
5790 }))
5791 }
5792
5793 fn CreateRange(&self, cx: &mut JSContext) -> DomRoot<Range> {
5795 Range::new_with_doc(cx, self, None)
5796 }
5797
5798 fn CreateNodeIterator(
5800 &self,
5801 cx: &mut js::context::JSContext,
5802 root: &Node,
5803 what_to_show: u32,
5804 filter: Option<Rc<NodeFilter>>,
5805 ) -> DomRoot<NodeIterator> {
5806 NodeIterator::new(cx, self, root, what_to_show, filter)
5807 }
5808
5809 fn CreateTreeWalker(
5811 &self,
5812 cx: &mut JSContext,
5813 root: &Node,
5814 what_to_show: u32,
5815 filter: Option<Rc<NodeFilter>>,
5816 ) -> DomRoot<TreeWalker> {
5817 TreeWalker::new(cx, self, root, what_to_show, filter)
5818 }
5819
5820 fn Title(&self) -> DOMString {
5822 self.title().unwrap_or_else(|| DOMString::from(""))
5823 }
5824
5825 fn SetTitle(&self, cx: &mut JSContext, title: DOMString) {
5827 let root = match self.GetDocumentElement() {
5828 Some(root) => root,
5829 None => return,
5830 };
5831
5832 let node = if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
5833 let elem = root
5834 .upcast::<Node>()
5835 .child_elements_unrooted(cx.no_gc())
5836 .find(|node| {
5837 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
5838 });
5839 match elem {
5840 Some(elem) => UnrootedDom::upcast::<Node>(elem).as_rooted(),
5841 None => {
5842 let name = QualName::new(None, ns!(svg), local_name!("title"));
5843 let elem = Element::create(
5844 cx,
5845 name,
5846 None,
5847 self,
5848 ElementCreator::ScriptCreated,
5849 CustomElementCreationMode::Synchronous,
5850 None,
5851 );
5852 let parent = root.upcast::<Node>();
5853 let child = elem.upcast::<Node>();
5854 parent
5855 .InsertBefore(cx, child, parent.GetFirstChild().as_deref())
5856 .unwrap()
5857 },
5858 }
5859 } else if root.namespace() == &ns!(html) {
5860 let elem = root
5861 .upcast::<Node>()
5862 .traverse_preorder_non_rooting(cx.no_gc(), ShadowIncluding::No)
5863 .find(|node| node.is::<HTMLTitleElement>());
5864 match elem {
5865 Some(elem) => elem.as_rooted(),
5866 None => match self.GetHead() {
5867 Some(head) => {
5868 let name = QualName::new(None, ns!(html), local_name!("title"));
5869 let elem = Element::create(
5870 cx,
5871 name,
5872 None,
5873 self,
5874 ElementCreator::ScriptCreated,
5875 CustomElementCreationMode::Synchronous,
5876 None,
5877 );
5878 head.upcast::<Node>()
5879 .AppendChild(cx, elem.upcast())
5880 .unwrap()
5881 },
5882 None => return,
5883 },
5884 }
5885 } else {
5886 return;
5887 };
5888
5889 node.set_text_content_for_element(cx, Some(title));
5890 }
5891
5892 fn GetHead(&self) -> Option<DomRoot<HTMLHeadElement>> {
5894 self.get_html_element()
5895 .and_then(|root| root.upcast::<Node>().children().find_map(DomRoot::downcast))
5896 }
5897
5898 fn GetCurrentScript(&self) -> Option<DomRoot<HTMLScriptElement>> {
5900 self.current_script.get()
5901 }
5902
5903 fn GetBody(&self) -> Option<DomRoot<HTMLElement>> {
5905 self.get_html_element().and_then(|root| {
5908 let node = root.upcast::<Node>();
5909 node.children()
5910 .find(|child| {
5911 matches!(
5912 child.type_id(),
5913 NodeTypeId::Element(ElementTypeId::HTMLElement(
5914 HTMLElementTypeId::HTMLBodyElement,
5915 )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
5916 HTMLElementTypeId::HTMLFrameSetElement,
5917 ))
5918 )
5919 })
5920 .map(|node| DomRoot::downcast(node).unwrap())
5921 })
5922 }
5923
5924 fn SetBody(&self, cx: &mut JSContext, new_body: Option<&HTMLElement>) -> ErrorResult {
5926 let new_body = match new_body {
5928 Some(new_body) => new_body,
5929 None => return Err(Error::HierarchyRequest(None)),
5930 };
5931
5932 let node = new_body.upcast::<Node>();
5933 match node.type_id() {
5934 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) |
5935 NodeTypeId::Element(ElementTypeId::HTMLElement(
5936 HTMLElementTypeId::HTMLFrameSetElement,
5937 )) => {},
5938 _ => return Err(Error::HierarchyRequest(None)),
5939 }
5940
5941 let old_body = self.GetBody();
5943 if old_body.as_deref() == Some(new_body) {
5944 return Ok(());
5945 }
5946
5947 match (self.GetDocumentElement(), &old_body) {
5948 (Some(ref root), Some(child)) => {
5951 let root = root.upcast::<Node>();
5952 root.ReplaceChild(cx, new_body.upcast(), child.upcast())
5953 .map(|_| ())
5954 },
5955
5956 (None, _) => Err(Error::HierarchyRequest(None)),
5958
5959 (Some(ref root), &None) => {
5962 let root = root.upcast::<Node>();
5963 root.AppendChild(cx, new_body.upcast()).map(|_| ())
5964 },
5965 }
5966 }
5967
5968 fn GetElementsByName(&self, cx: &mut JSContext, name: DOMString) -> DomRoot<NodeList> {
5970 NodeList::new_elements_by_name_list(cx, self.window(), self, name)
5971 }
5972
5973 fn Images(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
5975 self.images.or_init(|| {
5976 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
5977 element.is::<HTMLImageElement>()
5978 })
5979 })
5980 }
5981
5982 fn Embeds(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
5984 self.embeds.or_init(|| {
5985 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
5986 element.is::<HTMLEmbedElement>()
5987 })
5988 })
5989 }
5990
5991 fn Plugins(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
5993 self.Embeds(cx)
5994 }
5995
5996 fn Links(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
5998 self.links.or_init(|| {
5999 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6000 (element.is::<HTMLAnchorElement>() || element.is::<HTMLAreaElement>()) &&
6001 element.has_attribute(&local_name!("href"))
6002 })
6003 })
6004 }
6005
6006 fn Forms(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6008 self.forms.or_init(|| {
6009 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6010 element.is::<HTMLFormElement>()
6011 })
6012 })
6013 }
6014
6015 fn Scripts(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6017 self.scripts.or_init(|| {
6018 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6019 element.is::<HTMLScriptElement>()
6020 })
6021 })
6022 }
6023
6024 fn Anchors(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6026 self.anchors.or_init(|| {
6027 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6028 element.is::<HTMLAnchorElement>() && element.has_attribute(&local_name!("href"))
6029 })
6030 })
6031 }
6032
6033 fn Applets(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6035 self.applets
6036 .or_init(|| HTMLCollection::always_empty(cx, &self.window, self.upcast()))
6037 }
6038
6039 fn GetLocation(&self, cx: &mut JSContext) -> Option<DomRoot<Location>> {
6041 if self.is_fully_active() {
6042 Some(self.window.Location(cx))
6043 } else {
6044 None
6045 }
6046 }
6047
6048 fn Children(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6050 HTMLCollection::children(cx, &self.window, self.upcast())
6051 }
6052
6053 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
6055 self.upcast::<Node>().child_elements().next()
6056 }
6057
6058 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
6060 self.upcast::<Node>()
6061 .rev_children()
6062 .find_map(DomRoot::downcast)
6063 }
6064
6065 fn ChildElementCount(&self) -> u32 {
6067 self.upcast::<Node>().child_elements().count() as u32
6068 }
6069
6070 fn Prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6072 self.upcast::<Node>().prepend(cx, nodes)
6073 }
6074
6075 fn Append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6077 self.upcast::<Node>().append(cx, nodes)
6078 }
6079
6080 fn ReplaceChildren(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6082 self.upcast::<Node>().replace_children(cx, nodes)
6083 }
6084
6085 fn MoveBefore(&self, cx: &mut JSContext, node: &Node, child: Option<&Node>) -> ErrorResult {
6087 self.upcast::<Node>().move_before(cx, node, child)
6088 }
6089
6090 fn QuerySelector(
6092 &self,
6093 cx: &mut JSContext,
6094 selectors: DOMString,
6095 ) -> Fallible<Option<DomRoot<Element>>> {
6096 self.upcast::<Node>().query_selector(cx.no_gc(), selectors)
6097 }
6098
6099 fn QuerySelectorAll(
6101 &self,
6102 cx: &mut JSContext,
6103 selectors: DOMString,
6104 ) -> Fallible<DomRoot<NodeList>> {
6105 self.upcast::<Node>().query_selector_all(cx, selectors)
6106 }
6107
6108 fn ReadyState(&self) -> DocumentReadyState {
6110 self.ready_state.get()
6111 }
6112
6113 fn GetDefaultView(&self) -> Option<DomRoot<Window>> {
6115 if self.has_browsing_context {
6116 Some(DomRoot::from_ref(&*self.window))
6117 } else {
6118 None
6119 }
6120 }
6121
6122 fn GetCookie(&self) -> Fallible<DOMString> {
6124 if self.is_cookie_averse() {
6125 return Ok(DOMString::new());
6126 }
6127
6128 if !self.origin().is_tuple() {
6129 return Err(Error::Security(None));
6130 }
6131
6132 let url = self.url();
6133 let (tx, rx) =
6134 profile_generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
6135 let _ = self
6136 .window
6137 .as_global_scope()
6138 .resource_threads()
6139 .send(GetCookieStringForUrl(url, tx, NonHTTP));
6140 let cookies = rx.recv().unwrap();
6141 Ok(cookies.map_or(DOMString::new(), DOMString::from))
6142 }
6143
6144 fn SetCookie(&self, cookie: DOMString) -> ErrorResult {
6146 if self.is_cookie_averse() {
6147 return Ok(());
6148 }
6149
6150 if !self.origin().is_tuple() {
6151 return Err(Error::Security(None));
6152 }
6153
6154 if !cookie.is_valid_for_cookie() {
6155 return Ok(());
6156 }
6157
6158 let cookies = if let Some(cookie) = Cookie::parse(cookie.to_string()).ok().map(Serde) {
6159 vec![cookie]
6160 } else {
6161 vec![]
6162 };
6163
6164 let _ = self
6165 .window
6166 .as_global_scope()
6167 .resource_threads()
6168 .send(SetCookiesForUrl(self.url(), cookies, NonHTTP));
6169 Ok(())
6170 }
6171
6172 fn BgColor(&self) -> DOMString {
6174 self.get_body_attribute(&local_name!("bgcolor"))
6175 }
6176
6177 fn SetBgColor(&self, cx: &mut JSContext, value: DOMString) {
6179 self.set_body_attribute(cx, &local_name!("bgcolor"), value)
6180 }
6181
6182 fn FgColor(&self) -> DOMString {
6184 self.get_body_attribute(&local_name!("text"))
6185 }
6186
6187 fn SetFgColor(&self, cx: &mut JSContext, value: DOMString) {
6189 self.set_body_attribute(cx, &local_name!("text"), value)
6190 }
6191
6192 fn NamedGetter(&self, cx: &mut JSContext, name: DOMString) -> Option<NamedPropertyValue> {
6194 if name.is_empty() {
6195 return None;
6196 }
6197 let name = Atom::from(name);
6198
6199 let elements_with_name = self.get_elements_with_name(cx, &name);
6202 let name_iter = elements_with_name
6203 .iter()
6204 .filter(|elem| is_named_element_with_name_attribute(elem));
6205 let elements_with_id = self.id_map.get_all(cx.no_gc(), self.upcast(), &name);
6206 let id_iter = elements_with_id
6207 .iter()
6208 .filter(|elem| is_named_element_with_id_attribute(elem));
6209 let mut elements = name_iter.chain(id_iter);
6210
6211 let first = elements.next()?;
6218 if elements.all(|other| first == other) {
6219 if let Some(nested_window_proxy) = first
6220 .downcast::<HTMLIFrameElement>()
6221 .and_then(|iframe| iframe.GetContentWindow())
6222 {
6223 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
6224 }
6225
6226 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
6228 }
6229
6230 #[derive(JSTraceable, MallocSizeOf)]
6233 struct DocumentNamedGetter {
6234 #[no_trace]
6235 name: Atom,
6236 }
6237 impl CollectionFilter for DocumentNamedGetter {
6238 fn filter(&self, elem: &Element, _root: &Node) -> bool {
6239 let type_ = match elem.upcast::<Node>().type_id() {
6240 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
6241 _ => return false,
6242 };
6243 match type_ {
6244 HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
6245 elem.get_name().as_ref() == Some(&self.name)
6246 },
6247 HTMLElementTypeId::HTMLImageElement => elem.get_name().is_some_and(|name| {
6248 name == *self.name ||
6249 !name.is_empty() && elem.get_id().as_ref() == Some(&self.name)
6250 }),
6251 _ => false,
6255 }
6256 }
6257 }
6258 let collection = HTMLCollection::create(
6259 cx,
6260 self.window(),
6261 self.upcast(),
6262 Box::new(DocumentNamedGetter { name }),
6263 );
6264 Some(NamedPropertyValue::HTMLCollection(collection))
6265 }
6266
6267 fn SupportedPropertyNames(&self, no_gc: &NoGC) -> Vec<DOMString> {
6269 let mut names_with_first_named_element_map = HashMap::new();
6270 self.name_map
6271 .for_each(no_gc, self.upcast(), |name, elements| {
6272 if name.is_empty() {
6273 return;
6274 }
6275 let mut name_iter = elements
6276 .iter()
6277 .filter(|elem| is_named_element_with_name_attribute(elem));
6278 if let Some(first) = name_iter.next() {
6279 names_with_first_named_element_map.insert(name.clone(), first.as_rooted());
6280 }
6281 });
6282
6283 self.id_map.for_each(no_gc, self.upcast(), |id, elements| {
6284 if id.is_empty() {
6285 return;
6286 }
6287 let mut id_iter = elements
6288 .iter()
6289 .filter(|elem| is_named_element_with_id_attribute(elem));
6290 if let Some(first) = id_iter.next() {
6291 match names_with_first_named_element_map.entry(id.clone()) {
6292 Vacant(entry) => drop(entry.insert(first.as_rooted())),
6293 Occupied(mut entry) => {
6294 if first.upcast::<Node>().is_before(entry.get().upcast()) {
6295 *entry.get_mut() = first.as_rooted();
6296 }
6297 },
6298 }
6299 }
6300 });
6301
6302 let mut names_with_first_named_element_vec: Vec<_> =
6303 names_with_first_named_element_map.into_iter().collect();
6304 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
6305 if a.1 == b.1 {
6306 a.0.cmp(&b.0)
6309 } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
6310 Ordering::Less
6311 } else {
6312 Ordering::Greater
6313 }
6314 });
6315
6316 names_with_first_named_element_vec
6317 .into_iter()
6318 .map(|(k, _)| DOMString::from(&*k))
6319 .collect()
6320 }
6321
6322 fn Clear(&self) {
6324 }
6326
6327 fn CaptureEvents(&self) {
6329 }
6331
6332 fn ReleaseEvents(&self) {
6334 }
6336
6337 global_event_handlers!();
6339
6340 event_handler!(
6342 readystatechange,
6343 GetOnreadystatechange,
6344 SetOnreadystatechange
6345 );
6346
6347 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
6349 self.document_or_shadow_root.element_from_point(
6350 self.upcast(),
6351 x,
6352 y,
6353 self.GetDocumentElement(),
6354 self.has_browsing_context,
6355 )
6356 }
6357
6358 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
6360 self.document_or_shadow_root.elements_from_point(
6361 self.upcast(),
6362 x,
6363 y,
6364 self.GetDocumentElement(),
6365 self.has_browsing_context,
6366 )
6367 }
6368
6369 fn GetScrollingElement(&self) -> Option<DomRoot<Element>> {
6371 if self.quirks_mode() == QuirksMode::Quirks {
6373 if let Some(ref body) = self.GetBody() {
6375 let e = body.upcast::<Element>();
6376 if !e.is_potentially_scrollable_body_for_scrolling_element() {
6380 return Some(DomRoot::from_ref(e));
6381 }
6382 }
6383
6384 return None;
6386 }
6387
6388 self.GetDocumentElement()
6391 }
6392
6393 fn Open(
6395 &self,
6396 cx: &mut JSContext,
6397 _unused1: Option<DOMString>,
6398 _unused2: Option<DOMString>,
6399 ) -> Fallible<DomRoot<Document>> {
6400 if !self.is_html_document() {
6402 return Err(Error::InvalidState(None));
6403 }
6404
6405 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6408 return Err(Error::InvalidState(None));
6409 }
6410
6411 let entry_responsible_document = GlobalScope::entry().as_window().Document();
6413
6414 if !self
6417 .origin()
6418 .same_origin(&entry_responsible_document.origin())
6419 {
6420 return Err(Error::Security(None));
6421 }
6422
6423 if self
6426 .active_parser()
6427 .is_some_and(|parser| parser.script_nesting_level() > 0)
6428 {
6429 return Ok(DomRoot::from_ref(self));
6430 }
6431
6432 if self.is_prompting_or_unloading() {
6434 return Ok(DomRoot::from_ref(self));
6435 }
6436
6437 if self.active_parser_was_aborted.get() {
6439 return Ok(DomRoot::from_ref(self));
6440 }
6441
6442 self.window().set_navigation_start();
6446
6447 if self.has_browsing_context() {
6451 self.abort(cx);
6454 }
6455
6456 for node in self
6459 .upcast::<Node>()
6460 .traverse_preorder_non_rooting(cx.no_gc(), ShadowIncluding::Yes)
6461 {
6462 node.upcast::<EventTarget>().remove_all_listeners();
6463 }
6464
6465 if self.window.Document() == DomRoot::from_ref(self) {
6468 self.window.upcast::<EventTarget>().remove_all_listeners();
6469 }
6470
6471 Node::replace_all(cx, None, self.upcast::<Node>());
6473
6474 if self.is_fully_active() {
6481 let mut new_url = entry_responsible_document.url();
6483
6484 if entry_responsible_document != DomRoot::from_ref(self) {
6486 new_url.set_fragment(None);
6487 }
6488
6489 self.set_url(new_url);
6492 }
6493
6494 self.is_initial_about_blank.set(false);
6496
6497 if self.iframe_load_in_progress.get() {
6500 self.mute_iframe_load.set(true);
6501 }
6502
6503 self.set_quirks_mode(QuirksMode::NoQuirks);
6505
6506 let resource_threads = self.window.as_global_scope().resource_threads().clone();
6512 *self.loader.borrow_mut() =
6513 DocumentLoader::new_with_threads(resource_threads, Some(self.url()));
6514 ServoParser::parse_html_script_input(cx, self, self.url());
6515
6516 self.ready_state.set(DocumentReadyState::Loading);
6522
6523 Ok(DomRoot::from_ref(self))
6525 }
6526
6527 fn Open_(
6529 &self,
6530 cx: &mut JSContext,
6531 url: USVString,
6532 target: DOMString,
6533 features: DOMString,
6534 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
6535 self.browsing_context()
6536 .ok_or(Error::InvalidAccess(None))?
6537 .open(cx, url, target, features)
6538 }
6539
6540 fn Write(&self, cx: &mut JSContext, text: Vec<TrustedHTMLOrString>) -> ErrorResult {
6542 self.write(cx, text, false, "Document", "write")
6545 }
6546
6547 fn Writeln(&self, cx: &mut JSContext, text: Vec<TrustedHTMLOrString>) -> ErrorResult {
6549 self.write(cx, text, true, "Document", "writeln")
6552 }
6553
6554 fn Close(&self, cx: &mut JSContext) -> ErrorResult {
6556 if !self.is_html_document() {
6557 return Err(Error::InvalidState(None));
6559 }
6560
6561 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6564 return Err(Error::InvalidState(None));
6565 }
6566
6567 let parser = match self.get_current_parser() {
6569 Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
6570 _ => {
6571 return Ok(());
6572 },
6573 };
6574
6575 parser.close(cx);
6577
6578 Ok(())
6579 }
6580
6581 fn ExecCommand(
6583 &self,
6584 cx: &mut JSContext,
6585 command_id: DOMString,
6586 _show_ui: bool,
6587 value: TrustedHTMLOrString,
6588 ) -> Fallible<bool> {
6589 let value = if command_id == "insertHTML" {
6590 TrustedHTML::get_trusted_type_compliant_string(
6591 cx,
6592 self.window.as_global_scope(),
6593 value,
6594 "Document execCommand",
6595 )?
6596 } else {
6597 match value {
6598 TrustedHTMLOrString::TrustedHTML(trusted_html) => trusted_html.data().clone(),
6599 TrustedHTMLOrString::String(value) => value,
6600 }
6601 };
6602
6603 Ok(self.exec_command_for_command_id(cx, command_id, value))
6604 }
6605
6606 fn QueryCommandEnabled(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
6608 self.check_support_and_enabled(cx, &command_id).is_some()
6610 }
6611
6612 fn QueryCommandSupported(&self, command_id: DOMString) -> bool {
6614 self.is_command_supported(command_id)
6618 }
6619
6620 fn QueryCommandIndeterm(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
6622 self.is_command_indeterminate(cx, command_id)
6623 }
6624
6625 fn QueryCommandState(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
6627 self.command_state_for_command(cx, command_id)
6628 }
6629
6630 fn QueryCommandValue(&self, cx: &mut JSContext, command_id: DOMString) -> DOMString {
6632 self.command_value_for_command(cx, command_id)
6633 }
6634
6635 event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror);
6637
6638 event_handler!(
6640 fullscreenchange,
6641 GetOnfullscreenchange,
6642 SetOnfullscreenchange
6643 );
6644
6645 fn FullscreenEnabled(&self) -> bool {
6647 self.get_allow_fullscreen()
6648 }
6649
6650 fn Fullscreen(&self) -> bool {
6652 self.fullscreen_element.get().is_some()
6653 }
6654
6655 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
6657 DocumentOrShadowRoot::get_fullscreen_element(&self.node, self.fullscreen_element.get())
6658 }
6659
6660 fn ExitFullscreen(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
6662 self.exit_fullscreen(cx)
6663 }
6664
6665 fn ServoGetMediaControls(&self, id: DOMString) -> Fallible<DomRoot<ShadowRoot>> {
6669 match self.media_controls.borrow().get(&*id.str()) {
6670 Some(m) => Ok(DomRoot::from_ref(m)),
6671 None => Err(Error::InvalidAccess(None)),
6672 }
6673 }
6674
6675 fn GetSelection(&self, cx: &mut JSContext) -> Option<DomRoot<Selection>> {
6677 if self.has_browsing_context {
6678 Some(self.selection.or_init(|| Selection::new(cx, self)))
6679 } else {
6680 None
6681 }
6682 }
6683
6684 fn Fonts(&self, cx: &mut JSContext) -> DomRoot<FontFaceSet> {
6686 self.fonts
6687 .or_init(|| FontFaceSet::new(cx, &self.global(), None))
6688 }
6689
6690 fn Hidden(&self) -> bool {
6692 self.visibility_state.get() == DocumentVisibilityState::Hidden
6693 }
6694
6695 fn VisibilityState(&self) -> DocumentVisibilityState {
6697 self.visibility_state.get()
6698 }
6699
6700 fn CreateExpression(
6701 &self,
6702 cx: &mut JSContext,
6703 expression: DOMString,
6704 resolver: Option<Rc<XPathNSResolver>>,
6705 ) -> Fallible<DomRoot<crate::dom::types::XPathExpression>> {
6706 let parsed_expression =
6707 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
6708 Ok(XPathExpression::new(
6709 cx,
6710 &self.window,
6711 None,
6712 parsed_expression,
6713 ))
6714 }
6715
6716 fn CreateNSResolver(&self, cx: &mut JSContext, node_resolver: &Node) -> DomRoot<Node> {
6717 let global = self.global();
6718 let window = global.as_window();
6719 let evaluator = XPathEvaluator::new(cx, window, None);
6720 XPathEvaluatorMethods::<crate::DomTypeHolder>::CreateNSResolver(&*evaluator, node_resolver)
6721 }
6722
6723 fn Evaluate(
6724 &self,
6725 cx: &mut JSContext,
6726 expression: DOMString,
6727 context_node: &Node,
6728 resolver: Option<Rc<XPathNSResolver>>,
6729 result_type: u16,
6730 result: Option<&crate::dom::types::XPathResult>,
6731 ) -> Fallible<DomRoot<crate::dom::types::XPathResult>> {
6732 let parsed_expression =
6733 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
6734 XPathExpression::new(cx, &self.window, None, parsed_expression).evaluate_internal(
6735 cx,
6736 context_node,
6737 result_type,
6738 result,
6739 )
6740 }
6741
6742 fn AdoptedStyleSheets(&self, cx: &mut JSContext, retval: MutableHandleValue) {
6744 self.adopted_stylesheets_frozen_types.get_or_init(
6745 cx,
6746 || {
6747 self.adopted_stylesheets
6748 .borrow()
6749 .clone()
6750 .iter()
6751 .map(|sheet| sheet.as_rooted())
6752 .collect()
6753 },
6754 retval,
6755 );
6756 }
6757
6758 fn SetAdoptedStyleSheets(&self, cx: &mut JSContext, val: HandleValue) -> ErrorResult {
6760 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
6761 cx,
6762 &self.adopted_stylesheets,
6763 val,
6764 &StyleSheetListOwner::Document(Dom::from_ref(self)),
6765 );
6766
6767 if result.is_ok() {
6768 self.adopted_stylesheets_frozen_types.clear()
6769 }
6770
6771 result
6772 }
6773
6774 fn Timeline(&self) -> DomRoot<DocumentTimeline> {
6775 self.timeline.as_rooted()
6776 }
6777}
6778
6779fn update_with_current_instant(marker: &Cell<Option<CrossProcessInstant>>) {
6780 if marker.get().is_none() {
6781 marker.set(Some(CrossProcessInstant::now()))
6782 }
6783}
6784
6785#[derive(JSTraceable, MallocSizeOf)]
6786pub(crate) enum AnimationFrameCallback {
6787 DevtoolsFramerateTick {
6788 actor_name: String,
6789 },
6790 FrameRequestCallback {
6791 #[conditional_malloc_size_of]
6792 callback: Rc<FrameRequestCallback>,
6793 },
6794}
6795
6796impl AnimationFrameCallback {
6797 fn call(&self, cx: &mut JSContext, document: &Document, now: f64) {
6798 match *self {
6799 AnimationFrameCallback::DevtoolsFramerateTick { ref actor_name } => {
6800 let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name.clone(), now);
6801 let devtools_sender = document.window().as_global_scope().devtools_chan().unwrap();
6802 devtools_sender.send(msg).unwrap();
6803 },
6804 AnimationFrameCallback::FrameRequestCallback { ref callback } => {
6805 let _ = callback.Call__(cx, Finite::wrap(now), ExceptionHandling::Report);
6808 },
6809 }
6810 }
6811}
6812
6813#[derive(Default, JSTraceable, MallocSizeOf)]
6814#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
6815struct PendingInOrderScriptVec {
6816 scripts: DomRefCell<VecDeque<PendingScript>>,
6817}
6818
6819impl PendingInOrderScriptVec {
6820 fn is_empty(&self) -> bool {
6821 self.scripts.borrow().is_empty()
6822 }
6823
6824 fn push(&self, element: &HTMLScriptElement) {
6825 self.scripts
6826 .borrow_mut()
6827 .push_back(PendingScript::new(element));
6828 }
6829
6830 fn loaded(&self, element: &HTMLScriptElement, result: ScriptResult) {
6831 let mut scripts = self.scripts.borrow_mut();
6832 let entry = scripts
6833 .iter_mut()
6834 .find(|entry| &*entry.element == element)
6835 .unwrap();
6836 entry.loaded(result);
6837 }
6838
6839 fn take_next_ready_to_be_executed(&self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
6840 let mut scripts = self.scripts.borrow_mut();
6841 let pair = scripts.front_mut()?.take_result()?;
6842 scripts.pop_front();
6843 Some(pair)
6844 }
6845
6846 fn clear(&self) {
6847 *self.scripts.borrow_mut() = Default::default();
6848 }
6849}
6850
6851#[derive(JSTraceable, MallocSizeOf)]
6852#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
6853struct PendingScript {
6854 element: Dom<HTMLScriptElement>,
6855 load: Option<ScriptResult>,
6857}
6858
6859impl PendingScript {
6860 fn new(element: &HTMLScriptElement) -> Self {
6861 Self {
6862 element: Dom::from_ref(element),
6863 load: None,
6864 }
6865 }
6866
6867 fn new_with_load(element: &HTMLScriptElement, load: Option<ScriptResult>) -> Self {
6868 Self {
6869 element: Dom::from_ref(element),
6870 load,
6871 }
6872 }
6873
6874 fn loaded(&mut self, result: ScriptResult) {
6875 assert!(self.load.is_none());
6876 self.load = Some(result);
6877 }
6878
6879 fn take_result(&mut self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
6880 self.load
6881 .take()
6882 .map(|result| (DomRoot::from_ref(&*self.element), result))
6883 }
6884}
6885
6886fn is_named_element_with_name_attribute(elem: &Element) -> bool {
6887 let type_ = match elem.upcast::<Node>().type_id() {
6888 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
6889 _ => return false,
6890 };
6891 match type_ {
6892 HTMLElementTypeId::HTMLFormElement |
6893 HTMLElementTypeId::HTMLIFrameElement |
6894 HTMLElementTypeId::HTMLImageElement => true,
6895 _ => false,
6899 }
6900}
6901
6902fn is_named_element_with_id_attribute(elem: &Element) -> bool {
6903 elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty())
6907}
6908
6909impl DocumentHelpers for Document {
6910 fn ensure_safe_to_run_script_or_layout(&self) {
6911 Document::ensure_safe_to_run_script_or_layout(self)
6912 }
6913}
6914
6915pub(crate) struct SameoriginAncestorNavigablesIterator {
6919 document: DomRoot<Document>,
6920}
6921
6922impl SameoriginAncestorNavigablesIterator {
6923 pub(crate) fn new(document: DomRoot<Document>) -> Self {
6924 Self { document }
6925 }
6926}
6927
6928impl Iterator for SameoriginAncestorNavigablesIterator {
6929 type Item = DomRoot<Document>;
6930
6931 fn next(&mut self) -> Option<Self::Item> {
6932 let window_proxy = self.document.browsing_context()?;
6933 self.document = window_proxy.parent()?.document()?;
6934 Some(self.document.clone())
6935 }
6936}
6937
6938pub(crate) struct SameOriginDescendantNavigablesIterator {
6943 stack: Vec<Box<dyn Iterator<Item = DomRoot<HTMLIFrameElement>>>>,
6944}
6945
6946impl SameOriginDescendantNavigablesIterator {
6947 pub(crate) fn new(document: &Document) -> Self {
6948 let iframes: Vec<DomRoot<HTMLIFrameElement>> = document.iframes().iter().collect();
6949 Self {
6950 stack: vec![Box::new(iframes.into_iter())],
6951 }
6952 }
6953
6954 fn get_next_iframe(&mut self) -> Option<DomRoot<HTMLIFrameElement>> {
6955 let mut cur_iframe = self.stack.last_mut()?.next();
6956 while cur_iframe.is_none() {
6957 self.stack.pop();
6958 cur_iframe = self.stack.last_mut()?.next();
6959 }
6960 cur_iframe
6961 }
6962}
6963
6964impl Iterator for SameOriginDescendantNavigablesIterator {
6965 type Item = DomRoot<Document>;
6966
6967 fn next(&mut self) -> Option<Self::Item> {
6968 while let Some(iframe) = self.get_next_iframe() {
6969 let Some(pipeline_id) = iframe.pipeline_id() else {
6970 continue;
6971 };
6972
6973 if let Some(document) = ScriptThread::find_document(pipeline_id) {
6974 let child_iframes: Vec<DomRoot<HTMLIFrameElement>> =
6975 document.iframes().iter().collect();
6976 self.stack.push(Box::new(child_iframes.into_iter()));
6977 return Some(document);
6978 } else {
6979 continue;
6980 };
6981 }
6982 None
6983 }
6984}