1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::cell::{Cell, RefCell};
8use std::cmp::Ordering;
9use std::collections::hash_map::Entry::{Occupied, Vacant};
10use std::collections::{HashMap, HashSet, VecDeque};
11use std::default::Default;
12use std::ops::Deref;
13use std::rc::Rc;
14use std::str::FromStr;
15use std::sync::{Arc as StdArc, LazyLock};
16use std::time::Duration;
17
18use bitflags::bitflags;
19use chrono::Local;
20use content_security_policy::sandboxing_directive::SandboxingFlagSet;
21use content_security_policy::{CspList, Policy as CspPolicy, PolicyDisposition};
22use cookie::Cookie;
23use data_url::mime::Mime;
24use devtools_traits::ScriptToDevtoolsControlMsg;
25use dom_struct::dom_struct;
26use embedder_traits::{
27 AllowOrDeny, AnimationState, CustomHandlersAutomationMode, EmbedderMsg, Image, LoadStatus,
28 Theme,
29};
30use encoding_rs::{Encoding, UTF_8};
31use html5ever::{LocalName, QualName, local_name, ns};
32use hyper_serde::Serde;
33use indexmap::IndexSet;
34use js::context::{JSContext, NoGC};
35use js::jsapi::JSObject;
36use js::realm::CurrentRealm;
37use js::rust::{HandleObject, HandleValue, MutableHandleValue};
38use layout_api::{
39 LCPCandidate, PendingRestyle, ReflowGoal, ReflowPhasesRun, ReflowStatistics, RestyleReason,
40 ScrollContainerQueryFlags, TrustedNodeAddress,
41};
42use malloc_size_of::MallocSizeOfOps;
43use metrics::{InteractiveFlag, InteractiveWindow, ProgressiveWebMetrics};
44use net_traits::CookieSource::NonHTTP;
45use net_traits::CoreResourceMsg::{GetCookieStringForUrl, SetCookiesForUrl};
46use net_traits::image_cache::ImageCache;
47use net_traits::policy_container::PolicyContainer;
48use net_traits::pub_domains::is_pub_domain;
49use net_traits::request::{
50 InsecureRequestsPolicy, PreloadId, PreloadKey, PreloadedResources, RequestBuilder,
51};
52use net_traits::{ReferrerPolicy, ResourceFetchTiming};
53use percent_encoding::percent_decode;
54use profile_traits::mem::{Report, ReportKind};
55use profile_traits::time::TimerMetadataFrameType;
56use profile_traits::{generic_channel as profile_generic_channel, path};
57use regex::bytes::Regex;
58use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
59use script_bindings::callback::{RootedCallback, ThisReflector};
60use script_bindings::cell::{DomRefCell, Ref, RefMut};
61use script_bindings::interfaces::DocumentHelpers;
62use script_bindings::reflector::reflect_dom_object_with_proto;
63use script_bindings::trace::CustomTraceable;
64use script_traits::{DocumentActivity, ProgressiveWebMetricType};
65use servo_arc::Arc;
66use servo_base::cross_process_instant::CrossProcessInstant;
67use servo_base::generic_channel::GenericSend;
68use servo_base::id::{LCPCandidateID, PipelineId, WebViewId};
69use servo_base::{Epoch, generic_channel};
70use servo_config::pref;
71use servo_constellation_traits::{
72 NavigationHistoryBehavior, PaintMetricEvent, ScriptToConstellationMessage,
73};
74use servo_media::{ClientContextId, ServoMedia};
75use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
76use style::attr::AttrValue;
77use style::context::QuirksMode;
78use style::dom::OpaqueNode;
79use style::invalidation::element::restyle_hints::RestyleHint;
80use style::selector_parser::Snapshot;
81use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
82use style::str::{split_html_space_chars, str_join};
83use style::stylesheet_set::DocumentStylesheetSet;
84use style::stylesheets::{Origin, OriginSet, Stylesheet};
85use style::stylist::Stylist;
86use stylo_atoms::Atom;
87use time::Duration as TimeDuration;
88use url::{Host, Position};
89
90use crate::css::stylesheet_loader::StylesheetContextId;
91use crate::css::stylesheet_set::StylesheetSetRef;
92use crate::dom::animationtimeline::AnimationTimeline;
93use crate::dom::attr::Attr;
94use crate::dom::beforeunloadevent::BeforeUnloadEvent;
95use crate::dom::bindings::callback::ExceptionHandling;
96use crate::dom::bindings::codegen::Bindings::AnimationFrameProviderBinding::FrameRequestCallback;
97use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEvent_Binding::BeforeUnloadEventMethods;
98use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
99 DocumentMethods, DocumentReadyState, DocumentVisibilityState, NamedPropertyValue,
100};
101use crate::dom::bindings::codegen::Bindings::ElementBinding::ScrollLogicalPosition;
102use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
103use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElement_Binding::HTMLIFrameElementMethods;
104#[cfg(any(feature = "webxr", feature = "gamepad"))]
105use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
106use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
107use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
108use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
109use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionName;
110use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
111 SetHTMLOptions, SetHTMLUnsafeOptions,
112};
113use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, WindowMethods};
114use crate::dom::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMethods;
115use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
116use crate::dom::bindings::codegen::UnionTypes::{
117 BooleanOrImportNodeOptions, NodeOrString, StringOrElementCreationOptions, TrustedHTMLOrString,
118};
119use crate::dom::bindings::domname::{
120 self, is_valid_attribute_local_name, is_valid_element_local_name, namespace_from_domstring,
121};
122use crate::dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible};
123use crate::dom::bindings::frozenarray::CachedFrozenArray;
124use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
125use crate::dom::bindings::num::Finite;
126use crate::dom::bindings::refcounted::Trusted;
127use crate::dom::bindings::reflector::DomGlobal;
128use crate::dom::bindings::root::{
129 Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout, ToLayoutOptional, UnrootedDom,
130};
131use crate::dom::bindings::str::{DOMString, USVString};
132use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
133use crate::dom::bindings::weakref::DOMTracker;
134use crate::dom::bindings::xmlname::matches_name_production;
135use crate::dom::cdatasection::CDATASection;
136use crate::dom::comment::Comment;
137use crate::dom::compositionevent::CompositionEvent;
138use crate::dom::css::cssstylesheet::CSSStyleSheet;
139use crate::dom::css::fontfaceset::FontFaceSet;
140use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
141use crate::dom::customelementregistry::{CustomElementReactionStack, CustomElementRegistry};
142use crate::dom::customevent::CustomEvent;
143use crate::dom::document::accessibility_data::AccessibilityData;
144use crate::dom::document::animation_manager::AnimationManager;
145use crate::dom::document::focus::{DocumentFocusHandler, FocusableArea};
146use crate::dom::document::iframe_collection::IFrameCollection;
147use crate::dom::document::tree_ordered_index_map::TreeOrderedIndexMap;
148use crate::dom::document::websocket::WebSocket;
149use crate::dom::document_embedder_controls::DocumentEmbedderControls;
150use crate::dom::document_event_handler::DocumentEventHandler;
151use crate::dom::documentfragment::DocumentFragment;
152use crate::dom::documentorshadowroot::{
153 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
154};
155use crate::dom::documenttimeline::DocumentTimeline;
156use crate::dom::documenttype::DocumentType;
157use crate::dom::domimplementation::DOMImplementation;
158use crate::dom::domstringlist::DOMStringList;
159use crate::dom::element::attributes::storage::AttrRef;
160use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
161use crate::dom::event::{Event, EventBubbles, EventCancelable};
162use crate::dom::eventtarget::EventTarget;
163use crate::dom::execcommand::basecommand::{CommandName, DefaultSingleLineContainerName};
164use crate::dom::execcommand::execcommands::DocumentExecCommandSupport;
165use crate::dom::focusevent::FocusEvent;
166use crate::dom::globalscope::GlobalScope;
167use crate::dom::hashchangeevent::HashChangeEvent;
168use crate::dom::history::History;
169use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
170use crate::dom::html::htmlareaelement::HTMLAreaElement;
171use crate::dom::html::htmlbaseelement::HTMLBaseElement;
172use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
173use crate::dom::html::htmlelement::HTMLElement;
174use crate::dom::html::htmlembedelement::HTMLEmbedElement;
175use crate::dom::html::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
176use crate::dom::html::htmlheadelement::HTMLHeadElement;
177use crate::dom::html::htmlhtmlelement::HTMLHtmlElement;
178use crate::dom::html::htmliframeelement::HTMLIFrameElement;
179use crate::dom::html::htmlimageelement::HTMLImageElement;
180use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult};
181use crate::dom::html::htmltitleelement::HTMLTitleElement;
182use crate::dom::htmldetailselement::DetailsNameGroups;
183use crate::dom::intersectionobserver::IntersectionObserver;
184use crate::dom::iterators::ShadowIncluding;
185use crate::dom::keyboardevent::KeyboardEvent;
186use crate::dom::largestcontentfulpaint::LargestContentfulPaint;
187use crate::dom::location::Location;
188use crate::dom::messageevent::MessageEvent;
189use crate::dom::mouseevent::MouseEvent;
190use crate::dom::node::focus::FocusTrigger;
191use crate::dom::node::treewalker::TreeWalker;
192use crate::dom::node::virtualmethods::vtable_for;
193use crate::dom::node::{Node, NodeDamage, NodeFlags, NodeTraits};
194use crate::dom::nodeiterator::NodeIterator;
195use crate::dom::nodelist::NodeList;
196use crate::dom::pagetransitionevent::PageTransitionEvent;
197use crate::dom::performance::performanceentry::PerformanceEntry;
198use crate::dom::performance::performancepainttiming::PerformancePaintTiming;
199use crate::dom::processinginstruction::ProcessingInstruction;
200use crate::dom::promise::Promise;
201use crate::dom::range::Range;
202use crate::dom::resizeobserver::{ResizeObservationDepth, ResizeObserver};
203use crate::dom::sanitizer::Sanitizer;
204use crate::dom::selection::Selection;
205use crate::dom::servoparser::ServoParser;
206use crate::dom::shadowroot::ShadowRoot;
207use crate::dom::storageevent::StorageEvent;
208use crate::dom::text::Text;
209use crate::dom::textevent::TextEvent;
210use crate::dom::touchevent::TouchEvent as DomTouchEvent;
211use crate::dom::touchlist::TouchList;
212use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
213use crate::dom::types::{HTMLCanvasElement, VisibilityStateEntry};
214use crate::dom::uievent::UIEvent;
215use crate::dom::window::Window;
216use crate::dom::window::scrolling_box::{ScrollAxisState, ScrollingBox};
217use crate::dom::windowproxy::WindowProxy;
218use crate::dom::xpathevaluator::XPathEvaluator;
219use crate::dom::xpathexpression::XPathExpression;
220use crate::dom::{FlatTreeParent, WeakRangeVec};
221use crate::event_loop::document_loader::{DocumentLoader, LoadType};
222use crate::event_loop::script_thread::{ScriptThread, SharedRwLocks};
223use crate::event_loop::timers::{OneshotTimerCallback, OneshotTimers};
224use crate::fetch::fetch::{DeferredFetchRecordInvokeState, FetchCanceller};
225use crate::fetch::network_listener::FetchResponseListener;
226use crate::mime::{APPLICATION, CHARSET};
227use crate::modules::script_module::{ModuleRequest, ModuleStatus};
228use crate::navigation::navigate;
229use crate::runtime::script_runtime::compute_size;
230use crate::tasks::task::NonSendTaskBox;
231use crate::tasks::task_manager::TaskManager;
232use crate::tasks::task_source::TaskSourceName;
233use crate::xpath::parse_expression;
234
235#[derive(Clone, Copy, PartialEq)]
236pub(crate) enum FireMouseEventType {
237 Move,
238 Over,
239 Out,
240 Enter,
241 Leave,
242}
243
244impl FireMouseEventType {
245 pub(crate) fn as_str(&self) -> &str {
246 match *self {
247 FireMouseEventType::Move => "mousemove",
248 FireMouseEventType::Over => "mouseover",
249 FireMouseEventType::Out => "mouseout",
250 FireMouseEventType::Enter => "mouseenter",
251 FireMouseEventType::Leave => "mouseleave",
252 }
253 }
254}
255
256#[derive(JSTraceable, MallocSizeOf)]
257pub(crate) struct RefreshRedirectDue {
258 #[no_trace]
259 pub(crate) url: ServoUrl,
260 pub(crate) from_meta_element: bool,
262}
263
264#[derive(JSTraceable, MallocSizeOf)]
268#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
269struct LCPCandidateAndElement {
270 element: Dom<Element>,
271 #[no_trace]
272 candidate: LCPCandidate,
273}
274
275impl RefreshRedirectDue {
276 pub(crate) fn invoke(self, cx: &mut JSContext, global: &GlobalScope) {
278 let window = global
279 .downcast::<Window>()
280 .expect("Queued a RefreshRedirectDue on a non-Window globalscope");
281
282 if self.from_meta_element &&
289 window.Document().has_active_sandboxing_flag(
290 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
291 )
292 {
293 return;
294 }
295 let load_data = window.load_data_for_document(self.url, window.pipeline_id());
296 navigate(
297 cx,
298 window,
299 NavigationHistoryBehavior::Replace,
300 false,
301 load_data,
302 );
303 }
304}
305
306#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
307pub(crate) enum IsHTMLDocument {
308 HTMLDocument,
309 NonHTMLDocument,
310}
311
312#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
313pub(crate) enum TheEndLoadingPhase {
314 #[default]
315 Initial,
316 ProcessingDeferredScripts,
317 ProcessingAsSoonAsPossibleScripts,
318 WaitingForLoadEventBlockers,
319 Done,
320}
321
322#[derive(JSTraceable, MallocSizeOf)]
324pub(crate) enum DeclarativeRefresh {
325 PendingLoad {
326 #[no_trace]
327 url: ServoUrl,
328 time: u64,
329 from_meta_element: bool,
331 },
332 CreatedAfterLoad,
333}
334
335#[derive(JSTraceable, MallocSizeOf, PartialEq)]
336#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
337struct PendingScrollEvent {
338 target: Dom<EventTarget>,
340 #[no_trace]
342 event: Atom,
343}
344
345impl PendingScrollEvent {
346 fn equivalent(&self, target: &EventTarget, event: &Atom) -> bool {
347 &*self.target == target && self.event == *event
348 }
349}
350
351#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf)]
354pub(crate) struct RenderingUpdateReason(u8);
355
356bitflags! {
357 impl RenderingUpdateReason: u8 {
358 const ResizeObserverStartedObservingTarget = 1 << 0;
361 const IntersectionObserverStartedObservingTarget = 1 << 1;
364 const FontReadyPromiseFulfilled = 1 << 2;
368 }
369}
370
371#[derive(Clone, Debug, Default, MallocSizeOf)]
373pub(crate) struct NavigationTiming {
374 pub(crate) dom_loading: Cell<Option<CrossProcessInstant>>,
375 pub(crate) navigation_start: Cell<Option<CrossProcessInstant>>,
377 pub(crate) unload_event_start: Cell<Option<CrossProcessInstant>>,
379 pub(crate) unload_event_end: Cell<Option<CrossProcessInstant>>,
381 pub(crate) dom_interactive: Cell<Option<CrossProcessInstant>>,
383 pub(crate) dom_content_loaded_event_start: Cell<Option<CrossProcessInstant>>,
385 pub(crate) dom_content_loaded_event_end: Cell<Option<CrossProcessInstant>>,
387 pub(crate) dom_complete: Cell<Option<CrossProcessInstant>>,
389 pub(crate) load_event_start: Cell<Option<CrossProcessInstant>>,
391 pub(crate) load_event_end: Cell<Option<CrossProcessInstant>>,
393 pub(crate) top_level_dom_complete: Cell<Option<CrossProcessInstant>>,
395}
396
397#[dom_struct]
399pub(crate) struct Document {
400 node: Node,
401 document_or_shadow_root: DocumentOrShadowRoot,
402 window: Dom<Window>,
403 implementation: MutNullableDom<DOMImplementation>,
404 #[ignore_malloc_size_of = "type from external crate"]
405 #[no_trace]
406 content_type: Mime,
407 last_modified: Option<String>,
408 #[no_trace]
409 encoding: Cell<&'static Encoding>,
410 has_browsing_context: bool,
411 is_html_document: bool,
412 #[no_trace]
413 activity: Cell<DocumentActivity>,
414 #[no_trace]
416 url: DomRefCell<ServoUrl>,
417 #[no_trace]
419 about_base_url: DomRefCell<Option<ServoUrl>>,
420 #[ignore_malloc_size_of = "defined in selectors"]
421 #[no_trace]
422 quirks_mode: Cell<QuirksMode>,
423 event_handler: DocumentEventHandler,
425 focus_handler: DocumentFocusHandler,
427 embedder_controls: DocumentEmbedderControls,
429 id_map: TreeOrderedIndexMap,
430 name_map: TreeOrderedIndexMap,
431 tag_map: DomRefCell<HashMapTracedValues<LocalName, Dom<HTMLCollection>, FxBuildHasher>>,
432 tagns_map: DomRefCell<HashMapTracedValues<QualName, Dom<HTMLCollection>, FxBuildHasher>>,
433 classes_map: DomRefCell<HashMapTracedValues<Vec<Atom>, Dom<HTMLCollection>>>,
434 images: MutNullableDom<HTMLCollection>,
435 embeds: MutNullableDom<HTMLCollection>,
436 links: MutNullableDom<HTMLCollection>,
437 forms: MutNullableDom<HTMLCollection>,
438 scripts: MutNullableDom<HTMLCollection>,
439 anchors: MutNullableDom<HTMLCollection>,
440 applets: MutNullableDom<HTMLCollection>,
441 iframes: RefCell<IFrameCollection>,
443 #[no_trace]
447 shared_style_locks: SharedRwLocks,
448 #[custom_trace]
450 stylesheets: DomRefCell<DocumentStylesheetSet<ServoStylesheetInDocument>>,
451 stylesheet_list: MutNullableDom<StyleSheetList>,
452 ready_state: Cell<DocumentReadyState>,
453 current_script: MutNullableDom<HTMLScriptElement>,
455 #[no_trace]
456 current_the_end_loading_phase: Cell<TheEndLoadingPhase>,
457 pending_parsing_blocking_script: DomRefCell<Option<PendingScript>>,
459 script_blocking_stylesheet_set: DomRefCell<IndexSet<StylesheetContextId>>,
462 render_blocking_element_count: Cell<u32>,
465 deferred_scripts: PendingInOrderScriptVec,
467 asap_in_order_scripts_list: PendingInOrderScriptVec,
469 asap_scripts_set: DomRefCell<Vec<Dom<HTMLScriptElement>>>,
471 animation_frame_ident: Cell<u32>,
474 animation_frame_list: DomRefCell<VecDeque<(u32, Option<AnimationFrameCallback>)>>,
477 running_animation_callbacks: Cell<bool>,
482 loader: DomRefCell<DocumentLoader>,
484 current_parser: MutNullableDom<ServoParser>,
486 base_element: MutNullableDom<HTMLBaseElement>,
488 target_base_element: MutNullableDom<HTMLBaseElement>,
490 appropriate_template_contents_owner_document: MutNullableDom<Document>,
493 pending_restyles: DomRefCell<FxHashMap<Dom<Element>, NoTrace<PendingRestyle>>>,
496 #[no_trace]
500 needs_restyle: Cell<RestyleReason>,
501 #[no_trace]
503 origin: DomRefCell<MutableOrigin>,
504 referrer: Option<String>,
506 target_element: MutNullableDom<Element>,
508 #[no_trace]
510 policy_container: DomRefCell<PolicyContainer>,
511 #[no_trace]
513 preloaded_resources: DomRefCell<PreloadedResources>,
514 ignore_destructive_writes_counter: Cell<u32>,
516 ignore_opens_during_unload_counter: Cell<u32>,
518 spurious_animation_frames: Cell<u8>,
522
523 fullscreen_element: MutNullableDom<Element>,
525 form_id_listener_map:
532 DomRefCell<HashMapTracedValues<Atom, HashSet<Dom<Element>>, FxBuildHasher>>,
533 #[no_trace]
534 interactive_time: DomRefCell<ProgressiveWebMetrics>,
535 #[no_trace]
536 tti_window: DomRefCell<InteractiveWindow>,
537 canceller: FetchCanceller,
539 throw_on_dynamic_markup_insertion_counter: Cell<u64>,
541 page_showing: Cell<bool>,
543 salvageable: Cell<bool>,
545 active_parser_was_aborted: Cell<bool>,
547 fired_unload: Cell<bool>,
549 responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>,
551
552 #[no_trace]
555 #[conditional_malloc_size_of]
556 navigation_timing: Rc<NavigationTiming>,
557
558 #[no_trace]
560 resource_fetch_timing: RefCell<Option<ResourceFetchTiming>>,
561
562 script_and_layout_blockers: Cell<u32>,
564 #[ignore_malloc_size_of = "Measuring trait objects is hard"]
566 delayed_tasks: DomRefCell<Vec<Box<dyn NonSendTaskBox>>>,
567 completely_loaded: Cell<bool>,
569 shadow_roots: DomRefCell<HashSet<Dom<ShadowRoot>>>,
571 shadow_roots_styles_changed: Cell<bool>,
573 media_controls: DomRefCell<HashMap<String, Dom<ShadowRoot>>>,
579 dirty_canvases: DomRefCell<Vec<Dom<HTMLCanvasElement>>>,
582 has_pending_animated_image_update: Cell<bool>,
584 selection: MutNullableDom<Selection>,
586 timeline: Dom<DocumentTimeline>,
589 animation_manager: AnimationManager,
591 dirty_root: MutNullableDom<Element>,
593 declarative_refresh: DomRefCell<Option<DeclarativeRefresh>>,
595 resize_observers: DomRefCell<Vec<Dom<ResizeObserver>>>,
604 fonts: MutNullableDom<FontFaceSet>,
607 visibility_state: Cell<DocumentVisibilityState>,
609 status_code: Option<u16>,
611 is_initial_about_blank: Cell<bool>,
613 allow_declarative_shadow_roots: Cell<bool>,
615 #[no_trace]
617 inherited_insecure_requests_policy: Cell<Option<InsecureRequestsPolicy>>,
618 has_trustworthy_ancestor_origin: Cell<bool>,
620 intersection_observer_task_queued: Cell<bool>,
622 intersection_observers: DomRefCell<Vec<Dom<IntersectionObserver>>>,
634 highlighted_dom_node: MutNullableDom<Node>,
636 lcp_candidates: DomRefCell<HashMapTracedValues<LCPCandidateID, LCPCandidateAndElement>>,
638 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
641 #[ignore_malloc_size_of = "mozjs"]
643 adopted_stylesheets_frozen_types: CachedFrozenArray,
644 pending_scroll_events: DomRefCell<Vec<PendingScrollEvent>>,
648 rendering_update_reasons: Cell<RenderingUpdateReason>,
650 waiting_on_canvas_image_updates: Cell<bool>,
654 root_removal_noted: Cell<bool>,
656 #[no_trace]
664 current_rendering_epoch: Cell<Epoch>,
665 #[conditional_malloc_size_of]
667 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
668 #[no_trace]
669 active_sandboxing_flag_set: Cell<SandboxingFlagSet>,
671 #[no_trace]
672 creation_sandboxing_flag_set: Cell<SandboxingFlagSet>,
679 #[no_trace]
681 favicon: RefCell<Option<Image>>,
682
683 websockets: DOMTracker<WebSocket>,
685
686 details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
688
689 #[no_trace]
691 protocol_handler_automation_mode: RefCell<CustomHandlersAutomationMode>,
692
693 layout_animations_test_enabled: bool,
695
696 #[no_trace]
698 state_override: DomRefCell<FxHashMap<CommandName, bool>>,
699
700 #[no_trace]
702 value_override: DomRefCell<FxHashMap<CommandName, DOMString>>,
703
704 #[no_trace]
706 default_single_line_container_name: Cell<DefaultSingleLineContainerName>,
707
708 css_styling_flag: Cell<bool>,
710
711 accessibility_data: DomRefCell<AccessibilityData>,
713
714 iframe_load_in_progress: Cell<bool>,
716 mute_iframe_load: Cell<bool>,
718
719 timers: OneshotTimers,
722
723 #[no_trace]
724 pipeline_id: PipelineId,
725
726 #[conditional_malloc_size_of]
728 task_manager: Rc<TaskManager>,
729
730 #[ignore_malloc_size_of = "ImageCache"]
731 #[no_trace]
732 image_cache: StdArc<dyn ImageCache>,
733
734 history: MutNullableDom<History>,
736
737 #[no_trace]
739 theme: Cell<Option<Theme>>,
740
741 default_language: DomRefCell<Option<String>>,
743
744 window_detached: Cell<bool>,
747
748 ancestor_origins_list: MutNullableDom<DOMStringList>,
750
751 #[no_trace]
753 internal_ancestor_origin_objects_list: RefCell<Option<Vec<ImmutableOrigin>>>,
754
755 live_ranges: WeakRangeVec,
758
759 #[ignore_malloc_size_of = "mozjs"]
762 module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
763}
764
765impl Document {
766 pub(crate) fn module_map(
767 &self,
768 ) -> &DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>> {
769 &self.module_map
770 }
771
772 pub(crate) fn history(&self, cx: &mut JSContext) -> DomRoot<History> {
773 self.history.or_init(|| History::new(cx, &self.window))
774 }
775
776 pub(crate) fn image_cache(&self) -> StdArc<dyn ImageCache> {
777 self.image_cache.clone()
778 }
779
780 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
781 self.task_manager.clone()
782 }
783
784 pub(crate) fn timers(&self) -> &OneshotTimers {
785 &self.timers
786 }
787
788 pub(crate) fn pipeline_id(&self) -> PipelineId {
789 self.pipeline_id
790 }
791
792 fn fully_exit_fullscreen(&self, cx: &mut JSContext) {
794 if self.fullscreen_element().is_none() {
797 return;
798 };
799
800 let _ = self.exit_fullscreen(cx);
805 }
806
807 fn unloading_cleanup_steps(&self, cx: &mut JSContext) {
809 self.fully_exit_fullscreen(cx);
812
813 if self.close_outstanding_websockets() {
816 self.salvageable.set(false);
818 }
819
820 if !self.salvageable.get() && !self.window_detached() {
825 let global_scope = self.window.as_global_scope();
826
827 global_scope.close_event_sources();
829
830 let msg = ScriptToConstellationMessage::DiscardDocument;
835 let _ = global_scope.script_to_constellation_chan().send(msg);
836 }
837 }
838
839 pub(crate) fn track_websocket(&self, websocket: &WebSocket) {
840 self.websockets.track(websocket);
841 }
842
843 fn close_outstanding_websockets(&self) -> bool {
844 let mut closed_any_websocket = false;
845 self.websockets.for_each(|websocket: DomRoot<WebSocket>| {
846 if websocket.make_disappear() {
847 closed_any_websocket = true;
848 }
849 });
850 closed_any_websocket
851 }
852
853 fn document_element_changed(&self) {
854 if self.GetDocumentElement().is_some() {
855 self.root_removal_noted.set(false);
858 } else if !self.root_removal_noted.get() {
859 self.add_restyle_reason(RestyleReason::DOMChanged);
862 self.root_removal_noted.set(true);
863 }
864 }
865
866 pub(crate) fn note_dirty_element(&self, no_gc: &NoGC, element: &Element) {
881 let node = element.upcast::<Node>();
882
883 debug_assert!(*node.owner_doc() == *self);
884 if !node.is_connected() {
885 return;
886 }
887
888 let parent_element = match node.parent_in_flat_tree(no_gc) {
889 FlatTreeParent::Parent(parent) => UnrootedDom::downcast::<Element>(parent),
890 FlatTreeParent::NotInFlatTree | FlatTreeParent::RootNode => return,
891 };
892
893 if let Some(parent_element) = parent_element {
897 if !parent_element.is_styled() {
900 return;
901 }
902 if parent_element.is_display_none() {
905 return;
906 }
907 }
908
909 let Some(old_dirty_root) = self.dirty_root.get() else {
910 node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
911 self.set_dirty_root(no_gc, Some(element));
912 return;
913 };
914
915 let old_dirty_root_node = old_dirty_root.upcast::<Node>();
916 for ancestor in element
917 .upcast::<Node>()
918 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
919 {
920 if !ancestor.is::<Element>() {
922 break;
923 }
924
925 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
926 return;
927 }
928
929 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
930
931 if old_dirty_root_node == &**ancestor {
935 return;
936 }
937 }
938
939 let common_element_ancestor = old_dirty_root_node
940 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
941 .skip(1) .find_map(|ancestor| {
943 let element = ancestor.downcast::<Element>().map(DomRoot::from_ref)?;
945 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
946 return Some(element);
947 }
948 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
949 None
950 });
951
952 let Some(new_dirty_root) = common_element_ancestor else {
956 let new_dirty_root = self.GetDocumentElement();
957 if let Some(new_dirty_root) = new_dirty_root.as_ref() {
958 new_dirty_root
959 .upcast::<Node>()
960 .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
961 }
962 self.set_dirty_root(no_gc, new_dirty_root.as_deref());
963 return;
964 };
965
966 for ancestor in new_dirty_root
970 .upcast::<Node>()
971 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
972 .skip(1)
973 {
974 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
975 }
976
977 self.set_dirty_root(no_gc, Some(&*new_dirty_root));
978 }
979
980 fn set_dirty_root(&self, no_gc: &NoGC, new_dirty_root: Option<&Element>) {
981 debug_assert!(new_dirty_root.as_ref().is_none_or(|new_dirty_root| {
983 new_dirty_root
984 .upcast::<Node>()
985 .inclusive_ancestors_in_flat_tree_unrooted(no_gc)
986 .skip(1)
987 .all(|node| !node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS))
988 }));
989 self.dirty_root.set(new_dirty_root);
990 }
991
992 pub(crate) fn take_dirty_root(&self) -> Option<DomRoot<Element>> {
993 self.dirty_root.take()
994 }
995
996 #[inline]
997 pub(crate) fn loader(&self) -> Ref<'_, DocumentLoader> {
998 self.loader.borrow()
999 }
1000
1001 #[inline]
1002 pub(crate) fn loader_mut(&self) -> RefMut<'_, DocumentLoader> {
1003 self.loader.borrow_mut()
1004 }
1005
1006 #[inline]
1007 pub(crate) fn has_browsing_context(&self) -> bool {
1008 self.has_browsing_context
1009 }
1010
1011 #[inline]
1013 pub(crate) fn browsing_context(&self) -> Option<DomRoot<WindowProxy>> {
1014 if self.has_browsing_context {
1015 self.window.undiscarded_window_proxy()
1016 } else {
1017 None
1018 }
1019 }
1020
1021 pub(crate) fn webview_id(&self) -> WebViewId {
1022 self.window.webview_id()
1023 }
1024
1025 #[inline]
1026 pub(crate) fn window(&self) -> &Window {
1027 &self.window
1028 }
1029
1030 #[inline]
1031 pub(crate) fn is_html_document(&self) -> bool {
1032 self.is_html_document
1033 }
1034
1035 pub(crate) fn is_xhtml_document(&self) -> bool {
1036 self.content_type.matches(APPLICATION, "xhtml+xml")
1037 }
1038
1039 pub(crate) fn is_fully_active(&self) -> bool {
1041 self.is_active() &&
1045 (self.window.is_top_level() || self.activity.get() == DocumentActivity::FullyActive)
1046 }
1047
1048 pub(crate) fn is_active(&self) -> bool {
1050 self.browsing_context().is_some() &&
1056 !self.window_detached() &&
1057 self.activity.get() != DocumentActivity::Inactive
1058 }
1059
1060 #[inline]
1061 pub(crate) fn current_rendering_epoch(&self) -> Epoch {
1062 self.current_rendering_epoch.get()
1063 }
1064
1065 #[inline]
1067 pub(crate) fn selection(&self) -> Option<DomRoot<Selection>> {
1068 self.selection.get()
1069 }
1070
1071 pub(crate) fn set_activity(&self, cx: &mut JSContext, activity: DocumentActivity) {
1072 assert!(self.has_browsing_context);
1074 if activity == self.activity.get() {
1075 return;
1076 }
1077
1078 self.activity.set(activity);
1080 let media = ServoMedia::get();
1081 let pipeline_id = self.window().pipeline_id();
1082 let client_context_id =
1083 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
1084
1085 if activity != DocumentActivity::FullyActive {
1086 if !self.window_detached() {
1087 self.window().suspend(cx);
1088 }
1089 media.suspend(&client_context_id);
1090 return;
1091 }
1092
1093 if self.window_detached() {
1094 return;
1095 }
1096
1097 self.title_changed();
1098 self.notify_embedder_favicon();
1099 self.dirty_all_nodes(cx.no_gc());
1100 self.window().resume(cx);
1101 media.resume(&client_context_id);
1102
1103 if self.ready_state.get() != DocumentReadyState::Complete {
1104 return;
1105 }
1106
1107 let document = Trusted::new(self);
1111 self.owner_global()
1112 .task_manager()
1113 .dom_manipulation_task_source()
1114 .queue(task!(fire_pageshow_event: move |cx| {
1115 let document = document.root();
1116 let window = document.window();
1117 if document.page_showing.get() {
1119 return;
1120 }
1121 document.page_showing.set(true);
1123 document.update_visibility_state(cx, DocumentVisibilityState::Visible);
1125 let event = PageTransitionEvent::new(
1128 cx,
1129 window,
1130 atom!("pageshow"),
1131 false, false, true, );
1135 let event = event.upcast::<Event>();
1136 event.set_trusted(true);
1137 window.dispatch_event_with_target_override(cx, event);
1138 }))
1139 }
1140
1141 pub(crate) fn origin(&self) -> Ref<'_, MutableOrigin> {
1142 self.origin.borrow()
1143 }
1144
1145 pub(crate) fn mark_as_internal(&self) {
1148 *self.origin.borrow_mut() = MutableOrigin::new(ImmutableOrigin::new_opaque());
1149 self.window().update_jsprincipals_from_document(self);
1150 }
1151
1152 pub(crate) fn set_protocol_handler_automation_mode(&self, mode: CustomHandlersAutomationMode) {
1153 *self.protocol_handler_automation_mode.borrow_mut() = mode;
1154 }
1155
1156 pub(crate) fn url(&self) -> ServoUrl {
1158 self.url.borrow().clone()
1159 }
1160
1161 pub(crate) fn set_url(&self, url: ServoUrl) {
1162 *self.url.borrow_mut() = url;
1163 }
1164
1165 pub(crate) fn about_base_url(&self) -> Option<ServoUrl> {
1166 self.about_base_url.borrow().clone()
1167 }
1168
1169 pub(crate) fn set_about_base_url(&self, about_base_url: Option<ServoUrl>) {
1170 *self.about_base_url.borrow_mut() = about_base_url;
1171 }
1172
1173 pub(crate) fn fallback_base_url(&self) -> ServoUrl {
1175 let document_url = self.url();
1176 if document_url.as_str() == "about:srcdoc" {
1178 return self
1181 .about_base_url()
1182 .expect("about:srcdoc page should always have an about base URL");
1183 }
1184
1185 if document_url.matches_about_blank() &&
1188 let Some(about_base_url) = self.about_base_url()
1189 {
1190 return about_base_url;
1191 }
1192
1193 document_url
1195 }
1196
1197 pub(crate) fn base_url(&self) -> ServoUrl {
1199 match self.base_element() {
1200 None => self.fallback_base_url(),
1202 Some(base) => base.frozen_base_url(),
1204 }
1205 }
1206
1207 pub(crate) fn add_restyle_reason(&self, reason: RestyleReason) {
1208 self.needs_restyle.set(self.needs_restyle.get() | reason)
1209 }
1210
1211 pub(crate) fn clear_restyle_reasons(&self) {
1212 self.needs_restyle.set(RestyleReason::empty());
1213 }
1214
1215 pub(crate) fn stylesheets_changed_since_last_reflow(&self) -> bool {
1216 self.stylesheets.borrow().has_changed()
1217 }
1218
1219 pub(crate) fn restyle_reason(&self, no_gc: &NoGC) -> RestyleReason {
1220 let mut condition = self.needs_restyle.get();
1221 if self.stylesheets_changed_since_last_reflow() {
1222 condition.insert(RestyleReason::StylesheetsChanged);
1223 }
1224
1225 if let Some(root) = self.get_document_element_unrooted(no_gc) &&
1229 root.has_dirty_descendants()
1230 {
1231 condition.insert(RestyleReason::DOMChanged);
1232 }
1233
1234 if !self.pending_restyles.borrow().is_empty() {
1235 condition.insert(RestyleReason::PendingRestyles);
1236 }
1237
1238 condition
1239 }
1240
1241 pub(crate) fn base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1243 self.base_element.get()
1244 }
1245
1246 pub(crate) fn target_base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1248 self.target_base_element.get()
1249 }
1250
1251 pub(crate) fn refresh_base_element(&self, cx: &mut JSContext) {
1253 if let Some(base_element) = self.base_element.get() {
1254 base_element.clear_frozen_base_url();
1255 }
1256 let new_base_element = self
1257 .upcast::<Node>()
1258 .traverse_preorder(ShadowIncluding::No)
1259 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1260 .find(|element| {
1261 element
1262 .upcast::<Element>()
1263 .has_attribute(&local_name!("href"))
1264 });
1265 if let Some(ref new_base_element) = new_base_element {
1266 new_base_element.set_frozen_base_url(cx);
1267 }
1268 self.base_element.set(new_base_element.as_deref());
1269
1270 let new_target_base_element = self
1271 .upcast::<Node>()
1272 .traverse_preorder(ShadowIncluding::No)
1273 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1274 .next();
1275 self.target_base_element
1276 .set(new_target_base_element.as_deref());
1277 }
1278
1279 pub(crate) fn quirks_mode(&self) -> QuirksMode {
1280 self.quirks_mode.get()
1281 }
1282
1283 pub(crate) fn set_quirks_mode(&self, new_mode: QuirksMode) {
1284 let old_mode = self.quirks_mode.replace(new_mode);
1285
1286 if old_mode != new_mode {
1287 self.window.layout_mut().set_quirks_mode(new_mode);
1288 }
1289 }
1290
1291 pub(crate) fn encoding(&self) -> &'static Encoding {
1292 self.encoding.get()
1293 }
1294
1295 pub(crate) fn set_encoding(&self, encoding: &'static Encoding) {
1296 self.encoding.set(encoding);
1297 }
1298
1299 pub(crate) fn content_and_heritage_changed(&self, no_gc: &NoGC, node: &Node) {
1300 if node.is::<Document>() {
1301 self.document_element_changed();
1302 }
1303
1304 node.dirty(no_gc, NodeDamage::ContentOrHeritage);
1309 }
1310
1311 pub(crate) fn unregister_element_id(&self, cx: &mut JSContext, id: &Atom) {
1313 self.id_map.remove(id);
1314 self.reset_form_owner_for_listeners(cx, id);
1315 }
1316
1317 pub(crate) fn register_element_id(&self, cx: &mut JSContext, element: &Element, id: &Atom) {
1319 self.id_map.add(id, element);
1320 self.reset_form_owner_for_listeners(cx, id);
1321 }
1322
1323 pub(crate) fn unregister_element_name(&self, name: &Atom) {
1325 self.name_map.remove(name);
1326 }
1327
1328 pub(crate) fn register_element_name(&self, element: &Element, name: &Atom) {
1330 self.name_map.add(name, element);
1331 }
1332
1333 pub(crate) fn register_form_id_listener<T: ?Sized + FormControl>(
1334 &self,
1335 id: DOMString,
1336 listener: &T,
1337 ) {
1338 let mut map = self.form_id_listener_map.borrow_mut();
1339 let listener = listener.to_element();
1340 let set = map.entry(Atom::from(id)).or_default();
1341 set.insert(Dom::from_ref(listener));
1342 }
1343
1344 pub(crate) fn unregister_form_id_listener<T: ?Sized + FormControl>(
1345 &self,
1346 id: DOMString,
1347 listener: &T,
1348 ) {
1349 let mut map = self.form_id_listener_map.borrow_mut();
1350 if let Occupied(mut entry) = map.entry(Atom::from(id)) {
1351 entry
1352 .get_mut()
1353 .remove(&Dom::from_ref(listener.to_element()));
1354 if entry.get().is_empty() {
1355 entry.remove();
1356 }
1357 }
1358 }
1359
1360 fn find_a_potential_indicated_element(
1362 &self,
1363 cx: &mut JSContext,
1364 fragment: &str,
1365 ) -> Option<DomRoot<Element>> {
1366 self.get_element_by_id(cx.no_gc(), &Atom::from(fragment))
1370 .or_else(|| self.get_anchor_by_name(cx, fragment))
1374 }
1375
1376 fn select_indicated_part(&self, cx: &mut JSContext, fragment: &str) -> Option<DomRoot<Node>> {
1379 if fragment.is_empty() {
1389 return Some(DomRoot::from_ref(self.upcast()));
1390 }
1391 if let Some(potential_indicated_element) =
1393 self.find_a_potential_indicated_element(cx, fragment)
1394 {
1395 return Some(DomRoot::upcast(potential_indicated_element));
1397 }
1398 let fragment_bytes = percent_decode(fragment.as_bytes());
1400 let Ok(decoded_fragment) = fragment_bytes.decode_utf8() else {
1402 return None;
1403 };
1404 if let Some(potential_indicated_element) =
1406 self.find_a_potential_indicated_element(cx, &decoded_fragment)
1407 {
1408 return Some(DomRoot::upcast(potential_indicated_element));
1410 }
1411 if decoded_fragment.eq_ignore_ascii_case("top") {
1413 return Some(DomRoot::from_ref(self.upcast()));
1414 }
1415 None
1417 }
1418
1419 pub(crate) fn scroll_to_the_fragment(&self, cx: &mut JSContext, fragment: &str) {
1421 let Some(indicated_part) = self.select_indicated_part(cx, fragment) else {
1426 self.set_target_element(None);
1427 return;
1428 };
1429 if *indicated_part == *self.upcast() {
1431 self.set_target_element(None);
1433 self.window.scroll(cx, 0.0, 0.0, ScrollBehavior::Instant);
1438 return;
1440 }
1441 let Some(target) = indicated_part.downcast::<Element>() else {
1444 unreachable!("Indicated part should always be an element");
1446 };
1447 self.set_target_element(Some(target));
1449 target.scroll_into_view_with_options(
1453 cx,
1454 ScrollBehavior::Auto,
1455 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Start),
1456 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Nearest),
1457 None,
1458 None,
1459 );
1460
1461 indicated_part.run_the_focusing_steps(
1464 cx,
1465 Some(FocusableArea::Viewport),
1466 FocusTrigger::Other,
1467 );
1468
1469 self.focus_handler()
1471 .set_sequential_focus_navigation_starting_point(target.upcast());
1472 }
1473
1474 fn get_anchor_by_name(&self, cx: &mut JSContext, name: &str) -> Option<DomRoot<Element>> {
1475 let document_element = self.GetDocumentElement()?;
1476 self.name_map
1477 .get_all(cx.no_gc(), document_element.upcast(), &Atom::from(name))
1478 .iter()
1479 .find(|element| element.is::<HTMLAnchorElement>())
1480 .map(|element| DomRoot::from_ref(&**element))
1481 }
1482
1483 pub(crate) fn notify_embedder_of_load_completion(&self) {
1484 if self.window().is_top_level() {
1485 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1486 self.webview_id(),
1487 LoadStatus::Complete,
1488 ));
1489 }
1490 }
1491
1492 pub(crate) fn set_document_readiness_to_loading_for_initialization(&self) {
1497 if self.is_initial_about_blank() {
1501 return;
1502 }
1503
1504 update_with_current_instant(&self.navigation_timing.dom_loading);
1508
1509 if self.window.is_top_level() {
1510 let webview_id = self.webview_id();
1511 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1512 webview_id,
1513 LoadStatus::Started,
1514 ));
1515 self.send_to_embedder(EmbedderMsg::Status(webview_id, None));
1516 }
1517
1518 self.ready_state.set(DocumentReadyState::Loading);
1519 }
1520
1521 pub(crate) fn update_the_current_document_readiness(
1523 &self,
1524 cx: &mut JSContext,
1525 state: DocumentReadyState,
1526 ) {
1527 if self.ready_state.get() == state {
1529 return;
1530 }
1531
1532 self.ready_state.set(state);
1534
1535 match state {
1540 DocumentReadyState::Loading => {},
1541 DocumentReadyState::Complete => {
1542 self.notify_embedder_of_load_completion();
1545
1546 update_with_current_instant(&self.navigation_timing.dom_complete);
1551 },
1552 DocumentReadyState::Interactive => {
1553 update_with_current_instant(&self.navigation_timing.dom_interactive)
1558 },
1559 };
1560
1561 self.upcast::<EventTarget>()
1563 .fire_event(cx, atom!("readystatechange"));
1564 }
1565
1566 pub(crate) fn scripting_enabled(&self) -> bool {
1569 self.has_browsing_context() &&
1572 !self.has_active_sandboxing_flag(
1576 SandboxingFlagSet::SANDBOXED_SCRIPTS_BROWSING_CONTEXT_FLAG,
1577 )
1578 }
1579
1580 pub(crate) fn title_changed(&self) {
1582 if self.browsing_context().is_some() {
1583 self.send_title_to_embedder();
1584 let title = String::from(self.Title());
1585 self.window
1586 .send_to_constellation(ScriptToConstellationMessage::TitleChanged(
1587 self.window.pipeline_id(),
1588 title.clone(),
1589 ));
1590 if let Some(chan) = self.window.as_global_scope().devtools_chan() {
1591 let _ = chan.send(ScriptToDevtoolsControlMsg::TitleChanged(
1592 self.window.pipeline_id(),
1593 title,
1594 ));
1595 }
1596 }
1597 }
1598
1599 fn title(&self) -> Option<DOMString> {
1603 let title = self.GetDocumentElement().and_then(|root| {
1604 if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
1605 root.upcast::<Node>()
1607 .child_elements()
1608 .find(|node| {
1609 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
1610 })
1611 .map(DomRoot::upcast::<Node>)
1612 } else {
1613 root.upcast::<Node>()
1615 .traverse_preorder(ShadowIncluding::No)
1616 .find(|node| node.is::<HTMLTitleElement>())
1617 }
1618 });
1619
1620 title.map(|title| {
1621 let value = title.child_text_content();
1623 DOMString::from(str_join(value.str().split_html_space_characters(), " "))
1624 })
1625 }
1626
1627 pub(crate) fn send_title_to_embedder(&self) {
1629 let window = self.window();
1630 if window.is_top_level() {
1631 let title = self.title().map(String::from);
1632 self.send_to_embedder(EmbedderMsg::ChangePageTitle(self.webview_id(), title));
1633 }
1634 }
1635
1636 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
1637 let window = self.window();
1638 window.send_to_embedder(msg);
1639 }
1640
1641 pub(crate) fn dirty_all_nodes(&self, no_gc: &NoGC) {
1642 let root = match self.GetDocumentElement() {
1643 Some(root) => root,
1644 None => return,
1645 };
1646 for node in root
1647 .upcast::<Node>()
1648 .traverse_preorder_non_rooting(no_gc, ShadowIncluding::Yes)
1649 {
1650 node.dirty(no_gc, NodeDamage::Other)
1651 }
1652 }
1653
1654 pub(crate) fn run_the_scroll_steps(&self, cx: &mut JSContext) {
1656 let boxes_that_were_scrolled: Vec<_> = self
1664 .pending_scroll_events
1665 .borrow()
1666 .iter()
1667 .filter_map(|pending_event| {
1668 if &*pending_event.event == "scroll" {
1669 Some(pending_event.target.as_rooted())
1670 } else {
1671 None
1672 }
1673 })
1674 .collect();
1675
1676 for target in boxes_that_were_scrolled.into_iter() {
1677 let Some(element) = target.downcast::<Element>() else {
1683 continue;
1684 };
1685 let document = element.owner_document();
1686
1687 let mut pending_scroll_events = document.pending_scroll_events.borrow_mut();
1694 let event = "scrollend".into();
1695 if pending_scroll_events
1696 .iter()
1697 .any(|existing| existing.equivalent(&target, &event))
1698 {
1699 continue;
1700 }
1701
1702 pending_scroll_events.push(PendingScrollEvent {
1704 target: target.as_traced(),
1705 event: "scrollend".into(),
1706 });
1707 }
1708
1709 rooted_vec!(let pending_scroll_events <- self.pending_scroll_events.take().into_iter());
1712 for pending_event in pending_scroll_events.iter() {
1713 let event = pending_event.event.clone();
1716 if pending_event.target.is::<Document>() {
1717 pending_event.target.fire_bubbling_event(cx, event);
1718 }
1719 else {
1728 pending_event.target.fire_event(cx, event);
1729 }
1730 }
1731
1732 }
1735
1736 pub(crate) fn handle_viewport_scroll_event(&self) {
1741 self.finish_handle_scroll_event(self.upcast());
1753 }
1754
1755 pub(crate) fn finish_handle_scroll_event(&self, event_target: &EventTarget) {
1760 let event = "scroll".into();
1763 if self
1764 .pending_scroll_events
1765 .borrow()
1766 .iter()
1767 .any(|existing| existing.equivalent(event_target, &event))
1768 {
1769 return;
1770 }
1771
1772 self.pending_scroll_events
1775 .borrow_mut()
1776 .push(PendingScrollEvent {
1777 target: Dom::from_ref(event_target),
1778 event: "scroll".into(),
1779 });
1780 }
1781
1782 pub(crate) fn node_from_nodes_and_strings(
1784 &self,
1785 cx: &mut JSContext,
1786 mut nodes: Vec<NodeOrString>,
1787 ) -> Fallible<DomRoot<Node>> {
1788 if nodes.len() == 1 {
1789 Ok(match nodes.pop().unwrap() {
1790 NodeOrString::Node(node) => node,
1791 NodeOrString::String(string) => DomRoot::upcast(self.CreateTextNode(cx, string)),
1792 })
1793 } else {
1794 let fragment = DomRoot::upcast::<Node>(self.CreateDocumentFragment(cx));
1795 for node in nodes {
1796 match node {
1797 NodeOrString::Node(node) => {
1798 fragment.AppendChild(cx, &node)?;
1799 },
1800 NodeOrString::String(string) => {
1801 let node = DomRoot::upcast::<Node>(self.CreateTextNode(cx, string));
1802 fragment.AppendChild(cx, &node).unwrap();
1805 },
1806 }
1807 }
1808 Ok(fragment)
1809 }
1810 }
1811
1812 pub(crate) fn get_body_attribute(&self, local_name: &LocalName) -> DOMString {
1813 match self.GetBody() {
1814 Some(ref body) if body.is_body_element() => {
1815 body.upcast::<Element>().get_string_attribute(local_name)
1816 },
1817 _ => DOMString::new(),
1818 }
1819 }
1820
1821 pub(crate) fn set_body_attribute(
1822 &self,
1823 cx: &mut JSContext,
1824 local_name: &LocalName,
1825 value: DOMString,
1826 ) {
1827 if let Some(ref body) = self.GetBody().filter(|elem| elem.is_body_element()) {
1828 let body = body.upcast::<Element>();
1829 let value = body.parse_attribute(&ns!(), local_name, value);
1830 body.set_attribute(cx, local_name, value);
1831 }
1832 }
1833
1834 pub(crate) fn set_current_script(&self, script: Option<&HTMLScriptElement>) {
1835 self.current_script.set(script);
1836 }
1837
1838 pub(crate) fn has_a_stylesheet_that_is_blocking_scripts(&self) -> bool {
1840 !self.script_blocking_stylesheet_set.borrow().is_empty()
1841 }
1842
1843 pub(crate) fn add_script_blocking_stylesheet(&self, id: StylesheetContextId) {
1844 self.script_blocking_stylesheet_set.borrow_mut().insert(id);
1845 }
1846
1847 pub(crate) fn remove_script_blocking_stylesheet(&self, id: StylesheetContextId) {
1848 self.script_blocking_stylesheet_set
1849 .borrow_mut()
1850 .shift_remove(&id);
1851 }
1852
1853 pub(crate) fn render_blocking_element_count(&self) -> u32 {
1854 self.render_blocking_element_count.get()
1855 }
1856
1857 pub(crate) fn increment_render_blocking_element_count(&self) {
1859 assert!(self.allows_adding_render_blocking_elements());
1866 let count_cell = &self.render_blocking_element_count;
1867 count_cell.set(count_cell.get() + 1);
1868 }
1869
1870 pub(crate) fn decrement_render_blocking_element_count(&self) {
1872 let count_cell = &self.render_blocking_element_count;
1878 assert!(count_cell.get() > 0);
1879 count_cell.set(count_cell.get() - 1);
1880 }
1881
1882 pub(crate) fn allows_adding_render_blocking_elements(&self) -> bool {
1884 self.is_html_document && self.GetBody().is_none()
1887 }
1888
1889 pub(crate) fn is_render_blocked(&self) -> bool {
1891 self.render_blocking_element_count() > 0
1895 }
1900
1901 pub(crate) fn invalidate_stylesheets(&self, no_gc: &NoGC) {
1902 self.stylesheets.borrow_mut().force_dirty(OriginSet::all());
1903
1904 if let Some(element) = self.GetDocumentElement() {
1908 element.upcast::<Node>().dirty(no_gc, NodeDamage::Style);
1909 }
1910 }
1911
1912 pub(crate) fn has_active_request_animation_frame_callbacks(&self) -> bool {
1915 !self.animation_frame_list.borrow().is_empty()
1916 }
1917
1918 pub(crate) fn request_animation_frame(&self, callback: AnimationFrameCallback) -> u32 {
1920 let ident = self.animation_frame_ident.get() + 1;
1921 self.animation_frame_ident.set(ident);
1922
1923 let had_animation_frame_callbacks;
1924 {
1925 let mut animation_frame_list = self.animation_frame_list.borrow_mut();
1926 had_animation_frame_callbacks = !animation_frame_list.is_empty();
1927 animation_frame_list.push_back((ident, Some(callback)));
1928 }
1929
1930 if !self.running_animation_callbacks.get() && !had_animation_frame_callbacks {
1936 self.window().send_to_constellation(
1937 ScriptToConstellationMessage::ChangeRunningAnimationsState(
1938 AnimationState::AnimationCallbacksPresent,
1939 ),
1940 );
1941 }
1942
1943 ident
1944 }
1945
1946 pub(crate) fn cancel_animation_frame(&self, ident: u32) {
1948 let mut list = self.animation_frame_list.borrow_mut();
1949 if let Some(pair) = list.iter_mut().find(|pair| pair.0 == ident) {
1950 pair.1 = None;
1951 }
1952 }
1953
1954 pub(crate) fn run_the_animation_frame_callbacks(&self, cx: &mut CurrentRealm) {
1956 self.running_animation_callbacks.set(true);
1957 let timing = self.global().performance(cx).Now();
1958
1959 let num_callbacks = self.animation_frame_list.borrow().len();
1960 for _ in 0..num_callbacks {
1961 let (_, maybe_callback) = self.animation_frame_list.borrow_mut().pop_front().unwrap();
1962 if let Some(callback) = maybe_callback {
1963 callback.call(cx, self, *timing);
1964 }
1965 }
1966 self.running_animation_callbacks.set(false);
1967
1968 if self.animation_frame_list.borrow().is_empty() {
1969 self.window().send_to_constellation(
1970 ScriptToConstellationMessage::ChangeRunningAnimationsState(
1971 AnimationState::AnimationCallbacksAbsent,
1972 ),
1973 );
1974 }
1975 }
1976
1977 pub(crate) fn policy_container(&self) -> Ref<'_, PolicyContainer> {
1978 self.policy_container.borrow()
1979 }
1980
1981 pub(crate) fn set_policy_container(&self, policy_container: PolicyContainer) {
1982 *self.policy_container.borrow_mut() = policy_container;
1983 }
1984
1985 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
1986 self.policy_container.borrow_mut().set_csp_list(csp_list);
1987 }
1988
1989 pub(crate) fn enforce_csp_policy(&self, policy: CspPolicy) {
1991 let mut csp_list = self.get_csp_list().clone().unwrap_or(CspList(vec![]));
1993 csp_list.push(policy);
1994 self.policy_container
1995 .borrow_mut()
1996 .set_csp_list(Some(csp_list));
1997 }
1998
1999 pub(crate) fn get_csp_list(&self) -> Ref<'_, Option<CspList>> {
2000 Ref::map(self.policy_container.borrow(), |policy_container| {
2001 &policy_container.csp_list
2002 })
2003 }
2004
2005 pub(crate) fn preloaded_resources(&self) -> std::cell::Ref<'_, PreloadedResources> {
2006 self.preloaded_resources.borrow()
2007 }
2008
2009 pub(crate) fn insert_preloaded_resource(&self, key: PreloadKey, preload_id: PreloadId) {
2010 self.preloaded_resources
2011 .borrow_mut()
2012 .insert(key, preload_id);
2013 }
2014
2015 pub(crate) fn fetch_blocking<Listener: FetchResponseListener>(
2016 &self,
2017 load: LoadType,
2018 request: RequestBuilder,
2019 listener: Listener,
2020 ) {
2021 self.loader_mut().add_blocking_load(load);
2022 self.fetch_background(request, listener);
2023 }
2024
2025 pub(crate) fn fetch_background<Listener: FetchResponseListener>(
2026 &self,
2027 request_builder: RequestBuilder,
2028 listener: Listener,
2029 ) {
2030 let networking_task_source = self
2031 .owner_global()
2032 .task_manager()
2033 .networking_task_source()
2034 .to_sendable();
2035 self.window()
2036 .as_global_scope()
2037 .fetch(request_builder, listener, networking_task_source);
2038 }
2039
2040 fn deferred_fetch_control_document(&self) -> DomRoot<Document> {
2042 match self.window().window_proxy().frame_element() {
2043 None => DomRoot::from_ref(self),
2046 Some(container) => container.owner_document().deferred_fetch_control_document(),
2048 }
2049 }
2050
2051 pub(crate) fn available_deferred_fetch_quota(&self, origin: ImmutableOrigin) -> isize {
2053 let control_document = self.deferred_fetch_control_document();
2055 let navigable = control_document.window();
2057 let is_top_level = navigable.is_top_level();
2060 let deferred_fetch_allowed = true;
2064 let deferred_fetch_minimal_allowed = true;
2068 let mut quota = match is_top_level {
2070 true if !deferred_fetch_allowed => 0,
2072 true if !deferred_fetch_minimal_allowed => 640 * 1024,
2074 true => 512 * 1024,
2076 _ if deferred_fetch_allowed => 0,
2080 _ if deferred_fetch_minimal_allowed => 8 * 1024,
2084 _ => 0,
2086 } as isize;
2087 let mut quota_for_request_origin = 64 * 1024_isize;
2089 let deferred_fetches = navigable.as_global_scope().fetch_group().deferred_fetches();
2098 for deferred_fetch in deferred_fetches {
2099 if deferred_fetch.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
2101 continue;
2102 }
2103 let request_length = deferred_fetch.request.total_request_length();
2105 quota -= request_length as isize;
2107 if deferred_fetch.request.url().origin() == origin {
2110 quota_for_request_origin -= request_length as isize;
2111 }
2112 }
2113 if quota <= 0 {
2115 return 0;
2116 }
2117 if quota < quota_for_request_origin {
2119 return quota;
2120 }
2121 quota_for_request_origin
2123 }
2124
2125 pub(crate) fn update_document_for_history_step_application(
2127 &self,
2128 old_url: &ServoUrl,
2129 new_url: &ServoUrl,
2130 ) {
2131 if old_url.as_url()[Position::BeforeFragment..] !=
2161 new_url.as_url()[Position::BeforeFragment..]
2162 {
2163 let window = Trusted::new(self.owner_window().deref());
2164 let old_url = old_url.to_string();
2165 let new_url = new_url.to_string();
2166 self.owner_global()
2167 .task_manager()
2168 .dom_manipulation_task_source()
2169 .queue(task!(hashchange_event: move |cx| {
2170 let window = window.root();
2171 HashChangeEvent::new(
2172 cx,
2173 &window,
2174 atom!("hashchange"),
2175 false,
2176 false,
2177 old_url,
2178 new_url,
2179 )
2180 .upcast::<Event>()
2181 .fire(cx, window.upcast());
2182 }));
2183 }
2184 }
2185
2186 pub(crate) fn finish_load_for_dropped_blocker(&self, load: LoadType) {
2187 let this = Trusted::new(self);
2188 self.owner_global()
2189 .task_manager()
2190 .dom_manipulation_task_source()
2191 .queue(task!(check_finished_load: move |cx| {
2192 this.root().finish_load(load, cx);
2193 }));
2194 }
2195
2196 pub(crate) fn finish_load(&self, load: LoadType, cx: &mut JSContext) {
2199 debug!("Document got finish_load: {:?}", load);
2201 self.loader.borrow_mut().finish_load(&load);
2202
2203 match load {
2204 LoadType::Stylesheet(_) => {
2205 self.process_pending_parsing_blocking_script(cx);
2208
2209 self.process_deferred_scripts(cx);
2211 },
2212 LoadType::PageSource(_) => {
2213 if self.has_browsing_context && self.is_fully_active() {
2216 self.window().allow_layout_if_necessary(cx);
2217 }
2218
2219 self.process_deferred_scripts(cx);
2224 },
2225 _ => {},
2226 }
2227
2228 let document = Trusted::new(self);
2230 self.owner_global()
2231 .task_manager()
2232 .dom_manipulation_task_source()
2233 .queue(task!(wait_for_load_blockers: move |cx| {
2234 document.root().wait_until_load_blockers_have_resolved(cx);
2235 }));
2236 }
2237
2238 pub(crate) fn check_if_unloading_is_cancelled(
2240 &self,
2241 cx: &mut JSContext,
2242 recursive_flag: bool,
2243 ) -> bool {
2244 self.incr_ignore_opens_during_unload_counter();
2247 let beforeunload_event = BeforeUnloadEvent::new(
2249 cx,
2250 &self.window,
2251 atom!("beforeunload"),
2252 EventBubbles::Bubbles,
2253 EventCancelable::Cancelable,
2254 );
2255 let event = beforeunload_event.upcast::<Event>();
2256 event.set_trusted(true);
2257 let event_target = self.window.upcast::<EventTarget>();
2258 let has_listeners = event_target.has_listeners_for(&atom!("beforeunload"));
2259 self.window.dispatch_event_with_target_override(cx, event);
2260 if has_listeners {
2263 self.salvageable.set(false);
2264 }
2265 let mut can_unload = true;
2266 let default_prevented = event.DefaultPrevented();
2268 let return_value_not_empty = !event
2269 .downcast::<BeforeUnloadEvent>()
2270 .unwrap()
2271 .ReturnValue()
2272 .is_empty();
2273 if default_prevented || return_value_not_empty {
2274 let (chan, port) = generic_channel::channel().expect("Failed to create IPC channel!");
2275 let msg = EmbedderMsg::AllowUnload(self.webview_id(), chan);
2276 self.send_to_embedder(msg);
2277 can_unload = port.recv().unwrap() == AllowOrDeny::Allow;
2278 }
2279 if !recursive_flag {
2281 let iframes: Vec<_> = self.iframes().iter().collect();
2284 for iframe in &iframes {
2285 let document = iframe.owner_document();
2287 can_unload = document.check_if_unloading_is_cancelled(cx, true);
2288 if !document.salvageable() {
2289 self.salvageable.set(false);
2290 }
2291 if !can_unload {
2292 break;
2293 }
2294 }
2295 }
2296 self.decr_ignore_opens_during_unload_counter();
2298 can_unload
2299 }
2300
2301 pub(crate) fn unload(&self, cx: &mut JSContext, recursive_flag: bool) {
2303 if self.window_detached() {
2304 return;
2305 }
2306
2307 self.incr_ignore_opens_during_unload_counter();
2310 if self.page_showing.get() {
2312 self.page_showing.set(false);
2314 let event = PageTransitionEvent::new(
2317 cx,
2318 &self.window,
2319 atom!("pagehide"),
2320 false, false, self.salvageable.get(), );
2324 let event = event.upcast::<Event>();
2325 event.set_trusted(true);
2326 self.window.dispatch_event_with_target_override(cx, event);
2327 self.update_visibility_state(cx, DocumentVisibilityState::Hidden);
2329 }
2330 if !self.fired_unload.get() {
2332 let event = Event::new(
2333 cx,
2334 self.window.upcast(),
2335 atom!("unload"),
2336 EventBubbles::Bubbles,
2337 EventCancelable::Cancelable,
2338 );
2339 event.set_trusted(true);
2340 let event_target = self.window.upcast::<EventTarget>();
2341 let has_listeners = event_target.has_listeners_for(&atom!("unload"));
2342 self.window.dispatch_event_with_target_override(cx, &event);
2343 self.fired_unload.set(true);
2344 if has_listeners {
2346 self.salvageable.set(false);
2347 }
2348 }
2349 if !recursive_flag {
2353 let iframes: Vec<_> = self.iframes().iter().collect();
2356 for iframe in &iframes {
2357 let document = iframe.owner_document();
2359 document.unload(cx, true);
2360 if !document.salvageable() {
2361 self.salvageable.set(false);
2362 }
2363 }
2364 }
2365
2366 self.unloading_cleanup_steps(cx);
2368
2369 self.window.as_global_scope().clean_up_all_file_resources();
2371
2372 self.decr_ignore_opens_during_unload_counter();
2374
2375 }
2378
2379 fn completely_finish_loading(&self) {
2381 self.completely_loaded.set(true);
2386 self.notify_constellation_load();
2395
2396 if let Some(DeclarativeRefresh::PendingLoad {
2405 url,
2406 time,
2407 from_meta_element,
2408 }) = &*self.declarative_refresh.borrow()
2409 {
2410 self.window.as_global_scope().schedule_callback(
2411 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
2412 url: url.clone(),
2413 from_meta_element: *from_meta_element,
2414 }),
2415 Duration::from_secs(*time),
2416 );
2417 }
2418 }
2419
2420 fn queue_document_completion(&self, cx: &mut JSContext) {
2422 assert!(!self.is_initial_about_blank());
2426
2427 self.loader.borrow_mut().inhibit_events();
2428
2429 debug!("Document loads are complete.");
2434 let document = Trusted::new(self);
2435 self.owner_global()
2436 .task_manager()
2437 .dom_manipulation_task_source()
2438 .queue(task!(fire_load_event: move |cx| {
2439 let document = document.root();
2440 let window = document.window();
2442 if !window.is_alive() || document.window_detached() {
2443 return;
2444 }
2445
2446 document.update_the_current_document_readiness(cx, DocumentReadyState::Complete);
2448
2449 if document.browsing_context().is_none() {
2451 return;
2452 }
2453
2454 update_with_current_instant(&document.navigation_timing.load_event_start);
2456
2457 let load_event = Event::new(
2459 cx,
2460 window.upcast(),
2461 atom!("load"),
2462 EventBubbles::DoesNotBubble,
2463 EventCancelable::NotCancelable,
2464 );
2465 load_event.set_trusted(true);
2466 debug!("About to dispatch load for {:?}", document.url());
2467 window.dispatch_event_with_target_override(cx, &load_event);
2468
2469 update_with_current_instant(&document.navigation_timing.load_event_end);
2479
2480 document.page_showing.set(true);
2485
2486 let page_show_event = PageTransitionEvent::new(
2488 cx,
2489 window,
2490 atom!("pageshow"),
2491 false, false, false, );
2495 let page_show_event = page_show_event.upcast::<Event>();
2496 page_show_event.set_trusted(true);
2497 page_show_event.fire(cx, window.upcast());
2498
2499 document.completely_finish_loading();
2501
2502 if let Some(fragment) = document.url().fragment() {
2506 document.scroll_to_the_fragment(cx, fragment);
2507 }
2508 }));
2509
2510 #[cfg(feature = "webxr")]
2525 if pref!(dom_webxr_sessionavailable) && self.window.is_top_level() {
2526 self.window.Navigator(cx).Xr(cx).dispatch_sessionavailable();
2527 }
2528 }
2529
2530 pub(crate) fn completely_loaded(&self) -> bool {
2531 self.completely_loaded.get()
2532 }
2533
2534 pub(crate) fn start_the_end_loading_phase(&self) {
2535 if self.is_initial_about_blank() {
2536 self.current_the_end_loading_phase
2538 .set(TheEndLoadingPhase::Done);
2539 } else {
2540 self.current_the_end_loading_phase
2541 .set(TheEndLoadingPhase::ProcessingDeferredScripts);
2542 }
2543 }
2544
2545 pub(crate) fn set_pending_parsing_blocking_script(
2547 &self,
2548 script: &HTMLScriptElement,
2549 load: Option<ScriptResult>,
2550 ) {
2551 assert!(!self.has_pending_parsing_blocking_script());
2552 *self.pending_parsing_blocking_script.borrow_mut() =
2553 Some(PendingScript::new_with_load(script, load));
2554 }
2555
2556 pub(crate) fn has_pending_parsing_blocking_script(&self) -> bool {
2558 self.pending_parsing_blocking_script.borrow().is_some()
2559 }
2560
2561 pub(crate) fn pending_parsing_blocking_script_loaded(
2563 &self,
2564 element: &HTMLScriptElement,
2565 result: ScriptResult,
2566 cx: &mut JSContext,
2567 ) {
2568 {
2569 let mut blocking_script = self.pending_parsing_blocking_script.borrow_mut();
2570 let entry = blocking_script.as_mut().unwrap();
2571 assert!(&*entry.element == element);
2572 entry.loaded(result);
2573 }
2574 self.process_pending_parsing_blocking_script(cx);
2575 }
2576
2577 fn process_pending_parsing_blocking_script(&self, cx: &mut JSContext) {
2578 if self.has_a_stylesheet_that_is_blocking_scripts() {
2579 return;
2580 }
2581 let pair = self
2582 .pending_parsing_blocking_script
2583 .borrow_mut()
2584 .as_mut()
2585 .and_then(PendingScript::take_result);
2586 if let Some((element, result)) = pair {
2587 *self.pending_parsing_blocking_script.borrow_mut() = None;
2588 self.get_current_parser()
2589 .unwrap()
2590 .resume_with_pending_parsing_blocking_script(cx, &element, result);
2591 }
2592 }
2593
2594 pub(crate) fn add_asap_script(&self, script: &HTMLScriptElement) {
2596 self.asap_scripts_set
2597 .borrow_mut()
2598 .push(Dom::from_ref(script));
2599 }
2600
2601 pub(crate) fn asap_script_loaded(
2604 &self,
2605 cx: &mut JSContext,
2606 element: &HTMLScriptElement,
2607 result: ScriptResult,
2608 ) {
2609 {
2610 let mut scripts = self.asap_scripts_set.borrow_mut();
2611 let idx = scripts
2612 .iter()
2613 .position(|entry| &**entry == element)
2614 .unwrap();
2615 scripts.swap_remove(idx);
2616 }
2617 element.execute(cx, result);
2618 self.wait_until_asap_scripts_have_executed();
2619 }
2620
2621 pub(crate) fn push_asap_in_order_script(&self, script: &HTMLScriptElement) {
2623 self.asap_in_order_scripts_list.push(script);
2624 }
2625
2626 pub(crate) fn asap_in_order_script_loaded(
2629 &self,
2630 cx: &mut JSContext,
2631 element: &HTMLScriptElement,
2632 result: ScriptResult,
2633 ) {
2634 self.asap_in_order_scripts_list.loaded(element, result);
2635 while let Some((element, result)) = self
2636 .asap_in_order_scripts_list
2637 .take_next_ready_to_be_executed()
2638 {
2639 element.execute(cx, result);
2640 }
2641
2642 self.wait_until_asap_scripts_have_executed();
2643 }
2644
2645 pub(crate) fn add_deferred_script(&self, script: &HTMLScriptElement) {
2647 self.deferred_scripts.push(script);
2648 }
2649
2650 pub(crate) fn deferred_script_loaded(
2653 &self,
2654 cx: &mut JSContext,
2655 element: &HTMLScriptElement,
2656 result: ScriptResult,
2657 ) {
2658 self.deferred_scripts.loaded(element, result);
2659 self.process_deferred_scripts(cx);
2660 }
2661
2662 fn process_deferred_scripts(&self, cx: &mut JSContext) {
2664 if self.current_the_end_loading_phase.get() != TheEndLoadingPhase::ProcessingDeferredScripts
2665 {
2666 return;
2667 }
2668
2669 loop {
2673 if self.has_a_stylesheet_that_is_blocking_scripts() {
2674 return;
2675 }
2676 if let Some((element, result)) = self.deferred_scripts.take_next_ready_to_be_executed()
2679 {
2680 element.execute(cx, result);
2682 } else {
2683 break;
2684 }
2685 }
2686 if self.deferred_scripts.is_empty() {
2688 self.current_the_end_loading_phase
2689 .set(TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts);
2690 self.dispatch_dom_content_loaded();
2691 }
2692 }
2693
2694 fn dispatch_dom_content_loaded(&self) {
2696 assert_ne!(
2697 self.ReadyState(),
2698 DocumentReadyState::Complete,
2699 "Complete before DOMContentLoaded?"
2700 );
2701
2702 let document = Trusted::new(self);
2705 self.owner_global()
2706 .task_manager()
2707 .dom_manipulation_task_source()
2708 .queue(task!(fire_dom_content_loaded_event: move |cx| {
2709 let document = document.root();
2712 update_with_current_instant(&document.navigation_timing.dom_content_loaded_event_start);
2713 document.upcast::<EventTarget>().fire_bubbling_event(cx, atom!("DOMContentLoaded"));
2715 update_with_current_instant(&document.navigation_timing.dom_content_loaded_event_end);
2718 }));
2727
2728 self.interactive_time
2730 .borrow()
2731 .maybe_set_tti(InteractiveFlag::DOMContentLoaded);
2732
2733 self.wait_until_asap_scripts_have_executed();
2734 }
2735
2736 fn has_finished_all_asap_scripts(&self) -> bool {
2737 self.asap_scripts_set.borrow().is_empty() && self.asap_in_order_scripts_list.is_empty()
2738 }
2739
2740 fn wait_until_asap_scripts_have_executed(&self) {
2742 if self.current_the_end_loading_phase.get() !=
2743 TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts
2744 {
2745 return;
2746 }
2747 if self.has_finished_all_asap_scripts() {
2750 let document = Trusted::new(self);
2751 self.owner_global()
2752 .task_manager()
2753 .dom_manipulation_task_source()
2754 .queue(task!(transition_away_from_asap_scripts: move |cx| {
2755 let document = document.root();
2756 if document.current_the_end_loading_phase.get() !=
2759 TheEndLoadingPhase::ProcessingAsSoonAsPossibleScripts
2760 {
2761 return;
2762 }
2763 if !document.has_finished_all_asap_scripts() {
2765 return;
2766 }
2767 document.current_the_end_loading_phase
2768 .set(TheEndLoadingPhase::WaitingForLoadEventBlockers);
2769 document.wait_until_load_blockers_have_resolved(cx);
2770 }));
2771 }
2772 }
2773
2774 pub(crate) fn wait_until_load_blockers_have_resolved(&self, cx: &mut JSContext) {
2776 if self.current_the_end_loading_phase.get() !=
2777 TheEndLoadingPhase::WaitingForLoadEventBlockers
2778 {
2779 return;
2780 }
2781 {
2783 let loader = self.loader.borrow();
2784
2785 if self
2787 .navigation_timing
2788 .top_level_dom_complete
2789 .get()
2790 .is_none() &&
2791 loader.is_only_blocked_by_iframes()
2792 {
2793 update_with_current_instant(&self.navigation_timing.top_level_dom_complete);
2794 }
2795
2796 let not_ready_for_load = loader.is_blocked() || loader.events_inhibited();
2797 if not_ready_for_load {
2798 return;
2799 }
2800 }
2801
2802 self.current_the_end_loading_phase
2803 .set(TheEndLoadingPhase::Done);
2804 self.queue_document_completion(cx);
2805 }
2806
2807 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut JSContext) {
2809 if !self.is_fully_active() {
2811 self.salvageable.set(false);
2816 }
2820 for exited_iframe in self.iframes().iter() {
2833 debug!("Destroying nested iframe document");
2834 exited_iframe.destroy_document_and_its_descendants(cx);
2835 }
2836 self.destroy(cx);
2841 }
2844
2845 pub(crate) fn destroy(&self, cx: &mut JSContext) {
2847 let exited_window = self.window();
2848 self.abort(cx);
2850 self.salvageable.set(false);
2852 self.unloading_cleanup_steps(cx);
2862
2863 exited_window
2866 .as_global_scope()
2867 .task_manager()
2868 .cancel_all_tasks_and_ignore_future_tasks();
2869
2870 exited_window.discard_browsing_context();
2872
2873 exited_window
2880 .as_global_scope()
2881 .disable_owned_worker_animation_frame_providers();
2882
2883 }
2887
2888 fn active_parser(&self) -> Option<DomRoot<ServoParser>> {
2890 self.get_current_parser()
2893 .filter(|parser| !(parser.has_stopped() || parser.has_aborted()))
2894 }
2895
2896 pub(crate) fn abort(&self, cx: &mut JSContext) {
2898 self.loader.borrow_mut().inhibit_events();
2900
2901 self.script_blocking_stylesheet_set.borrow_mut().clear();
2910 *self.pending_parsing_blocking_script.borrow_mut() = None;
2911 *self.asap_scripts_set.borrow_mut() = vec![];
2912 self.asap_in_order_scripts_list.clear();
2913 self.deferred_scripts.clear();
2914
2915 let global = self.window.as_global_scope();
2916 let loads_cancelled = global.fetch_group_mut().terminate(global);
2917 let event_sources_canceled = global.close_event_sources();
2918
2919 if loads_cancelled || event_sources_canceled {
2920 self.salvageable.set(false);
2922 };
2923
2924 self.owner_global()
2929 .task_manager()
2930 .cancel_pending_tasks_for_source(TaskSourceName::Networking);
2931
2932 if let Some(parser) = self.active_parser() {
2937 self.active_parser_was_aborted.set(true);
2939 parser.abort(cx);
2941 self.salvageable.set(false);
2943 }
2944 }
2945
2946 pub(crate) fn abort_a_document_and_its_descendants(&self, cx: &mut JSContext) {
2948 for iframe in self.iframes().iter() {
2956 if let Some(descendant_document) = iframe.GetContentDocument() {
2957 let trusted_descendant_document = Trusted::new(&*descendant_document);
2958 let document = Trusted::new(self);
2959 descendant_document
2960 .owner_global()
2961 .task_manager()
2962 .navigation_and_traversal_task_source()
2963 .queue(task!(abort_iframe_document: move |cx| {
2964 let descendant_document = trusted_descendant_document.root();
2965 descendant_document.abort(cx);
2967 if !descendant_document.salvageable.get() {
2969 document.root().salvageable.set(false);
2970 }
2971 }));
2972 }
2973 }
2974
2975 self.abort(cx);
2977 }
2978
2979 pub(crate) fn notify_constellation_load(&self) {
2980 self.window()
2981 .send_to_constellation(ScriptToConstellationMessage::LoadComplete);
2982 }
2983
2984 pub(crate) fn set_current_parser(&self, script: Option<&ServoParser>) {
2985 self.current_parser.set(script);
2986 }
2987
2988 pub(crate) fn get_current_parser(&self) -> Option<DomRoot<ServoParser>> {
2989 self.current_parser.get()
2990 }
2991
2992 pub(crate) fn get_current_parser_line(&self) -> u32 {
2993 self.get_current_parser()
2994 .map(|parser| parser.get_current_line())
2995 .unwrap_or(0)
2996 }
2997
2998 pub(crate) fn set_ancestor_origins_list(&self, ancestor_origins_list: &DOMStringList) {
3000 self.ancestor_origins_list.set(Some(ancestor_origins_list));
3001 }
3002
3003 pub(crate) fn ancestor_origins_list(&self) -> Option<DomRoot<DOMStringList>> {
3005 self.ancestor_origins_list.get()
3006 }
3007
3008 pub(crate) fn set_internal_ancestor_origin_objects_list(
3010 &self,
3011 internal_ancestor_origin_objects_list: Vec<ImmutableOrigin>,
3012 ) {
3013 *self.internal_ancestor_origin_objects_list.borrow_mut() =
3014 Some(internal_ancestor_origin_objects_list);
3015 }
3016
3017 pub(crate) fn internal_ancestor_origin_objects_list(
3019 &self,
3020 ) -> Ref<'_, Option<Vec<ImmutableOrigin>>> {
3021 self.internal_ancestor_origin_objects_list.borrow()
3022 }
3023
3024 pub(crate) fn iframes(&self) -> Ref<'_, IFrameCollection> {
3027 self.iframes.borrow()
3028 }
3029
3030 pub(crate) fn iframes_mut(&self) -> RefMut<'_, IFrameCollection> {
3033 self.iframes.borrow_mut()
3034 }
3035
3036 pub(crate) fn set_navigation_start(&self, navigation_start: CrossProcessInstant) {
3037 self.interactive_time
3038 .borrow_mut()
3039 .set_navigation_start(navigation_start);
3040 }
3041
3042 pub(crate) fn get_interactive_metrics(&self) -> Ref<'_, ProgressiveWebMetrics> {
3043 self.interactive_time.borrow()
3044 }
3045
3046 pub(crate) fn has_recorded_tti_metric(&self) -> bool {
3047 self.get_interactive_metrics().get_tti().is_some()
3048 }
3049
3050 pub(crate) fn start_tti(&self) {
3051 if self.get_interactive_metrics().needs_tti() {
3052 self.tti_window.borrow_mut().start_window();
3053 }
3054 }
3055
3056 pub(crate) fn record_tti_if_necessary(&self) {
3060 if self.has_recorded_tti_metric() {
3061 return;
3062 }
3063 if self.tti_window.borrow().needs_check() {
3064 self.get_interactive_metrics()
3065 .maybe_set_tti(InteractiveFlag::TimeToInteractive(
3066 self.tti_window.borrow().get_start(),
3067 ));
3068 }
3069 }
3070
3071 pub(crate) fn is_cookie_averse(&self) -> bool {
3073 !self.has_browsing_context || !url_has_network_scheme(&self.url())
3074 }
3075
3076 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
3077 self.document_or_shadow_root.custom_element_registry()
3078 }
3079
3080 pub(crate) fn set_custom_element_registry(&self, registry: &CustomElementRegistry) {
3081 self.document_or_shadow_root
3082 .set_custom_element_registry(Some(registry));
3083 }
3084
3085 pub(crate) fn effective_global_custom_element_registry(
3087 &self,
3088 ) -> Option<DomRoot<CustomElementRegistry>> {
3089 let document_custom_element_registry = self.custom_element_registry();
3092 if CustomElementRegistry::is_a_global_element_registry(
3093 document_custom_element_registry.as_deref(),
3094 ) {
3095 return document_custom_element_registry;
3096 }
3097 None
3099 }
3100
3101 pub(crate) fn teardown_custom_element_registry(&self) {
3104 if let Some(custom_elements) = self.custom_element_registry() {
3105 custom_elements.teardown();
3106 }
3107 }
3108
3109 pub(crate) fn increment_throw_on_dynamic_markup_insertion_counter(&self) {
3110 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
3111 self.throw_on_dynamic_markup_insertion_counter
3112 .set(counter + 1);
3113 }
3114
3115 pub(crate) fn decrement_throw_on_dynamic_markup_insertion_counter(&self) {
3116 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
3117 self.throw_on_dynamic_markup_insertion_counter
3118 .set(counter - 1);
3119 }
3120
3121 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
3122 for image in self.responsive_images.borrow().iter() {
3123 image.react_to_environment_changes(cx);
3124 }
3125 }
3126
3127 pub(crate) fn register_responsive_image(&self, img: &HTMLImageElement) {
3128 self.responsive_images.borrow_mut().push(Dom::from_ref(img));
3129 }
3130
3131 pub(crate) fn unregister_responsive_image(&self, img: &HTMLImageElement) {
3132 let index = self
3133 .responsive_images
3134 .borrow()
3135 .iter()
3136 .position(|x| **x == *img);
3137 if let Some(i) = index {
3138 self.responsive_images.borrow_mut().remove(i);
3139 }
3140 }
3141
3142 pub(crate) fn register_media_controls(&self, id: &str, controls: &ShadowRoot) {
3143 let did_have_these_media_controls = self
3144 .media_controls
3145 .borrow_mut()
3146 .insert(id.to_string(), Dom::from_ref(controls))
3147 .is_some();
3148 debug_assert!(
3149 !did_have_these_media_controls,
3150 "Trying to register known media controls"
3151 );
3152 }
3153
3154 pub(crate) fn unregister_media_controls(&self, id: &str) {
3155 let did_have_these_media_controls = self.media_controls.borrow_mut().remove(id).is_some();
3156 debug_assert!(
3157 did_have_these_media_controls,
3158 "Trying to unregister unknown media controls"
3159 );
3160 }
3161
3162 pub(crate) fn mark_canvas_as_dirty(&self, canvas: &Dom<HTMLCanvasElement>) {
3163 let mut dirty_canvases = self.dirty_canvases.borrow_mut();
3164 if dirty_canvases
3165 .iter()
3166 .any(|dirty_canvas| dirty_canvas == canvas)
3167 {
3168 return;
3169 }
3170 dirty_canvases.push(canvas.clone());
3171 }
3172
3173 pub(crate) fn needs_rendering_update(&self, no_gc: &NoGC) -> bool {
3177 if !self.is_fully_active() {
3178 return false;
3179 }
3180 if !self.window().layout_blocked() &&
3181 (!self.restyle_reason(no_gc).is_empty() ||
3182 self.window().layout().needs_new_display_list() ||
3183 self.window().layout().force_accessibility_update())
3184 {
3185 return true;
3186 }
3187 if !self.rendering_update_reasons.get().is_empty() {
3188 return true;
3189 }
3190 if self.event_handler.has_pending_input_events() {
3191 return true;
3192 }
3193 if self.has_pending_scroll_events() {
3194 return true;
3195 }
3196 if self.window().has_unhandled_resize_event() {
3197 return true;
3198 }
3199 if self.has_pending_animated_image_update.get() || !self.dirty_canvases.borrow().is_empty()
3200 {
3201 return true;
3202 }
3203 if self.window().has_pending_media_query_evaluation() {
3204 return true;
3205 }
3206 if self
3207 .selection()
3208 .is_some_and(|selection| selection.visible_selection_dirty())
3209 {
3210 return true;
3211 }
3212
3213 false
3214 }
3215
3216 pub(crate) fn update_the_rendering(
3224 &self,
3225 cx: &mut JSContext,
3226 ) -> (ReflowPhasesRun, ReflowStatistics) {
3227 assert!(!self.is_render_blocked());
3228
3229 let mut phases = ReflowPhasesRun::empty();
3230 if self.has_pending_animated_image_update.get() {
3231 self.animation_manager.update_active_image_animation_frames(
3232 &self.window,
3233 self.current_animation_timeline_value(),
3234 );
3235 self.has_pending_animated_image_update.set(false);
3236 phases.insert(ReflowPhasesRun::UpdatedImageData);
3237 }
3238
3239 self.current_rendering_epoch
3240 .set(self.current_rendering_epoch.get().next());
3241 let current_rendering_epoch = self.current_rendering_epoch.get();
3242
3243 let image_keys: Vec<_> = self
3245 .dirty_canvases
3246 .borrow_mut()
3247 .drain(..)
3248 .filter_map(|canvas| canvas.update_rendering(current_rendering_epoch))
3249 .collect();
3250
3251 let pipeline_id = self.window().pipeline_id();
3254 if !image_keys.is_empty() {
3255 phases.insert(ReflowPhasesRun::UpdatedImageData);
3256 self.waiting_on_canvas_image_updates.set(true);
3257 self.window().paint_api().delay_new_frame_for_canvas(
3258 self.webview_id(),
3259 self.window().pipeline_id(),
3260 current_rendering_epoch,
3261 image_keys,
3262 );
3263 }
3264
3265 let (reflow_phases, statistics) = self.window().reflow(cx, ReflowGoal::UpdateTheRendering);
3266 let phases = phases.union(reflow_phases);
3267
3268 self.window().paint_api().update_epoch(
3269 self.webview_id(),
3270 pipeline_id,
3271 current_rendering_epoch,
3272 );
3273
3274 (phases, statistics)
3275 }
3276
3277 pub(crate) fn handle_no_longer_waiting_on_asynchronous_image_updates(&self) {
3278 self.waiting_on_canvas_image_updates.set(false);
3279 }
3280
3281 pub(crate) fn waiting_on_canvas_image_updates(&self) -> bool {
3282 self.waiting_on_canvas_image_updates.get()
3283 }
3284
3285 pub(crate) fn maybe_fulfill_font_ready_promise(&self, cx: &mut JSContext) -> bool {
3295 if !self.is_fully_active() {
3296 return false;
3297 }
3298
3299 let fonts = self.Fonts(cx);
3300 if !fonts.waiting_to_fullfill_promise() {
3301 return false;
3302 }
3303 if self.window().font_context().web_fonts_still_loading() != 0 {
3304 return false;
3305 }
3306 if self.ReadyState() != DocumentReadyState::Complete {
3307 return false;
3308 }
3309 if !self.restyle_reason(cx.no_gc()).is_empty() {
3310 return false;
3311 }
3312 if !self.rendering_update_reasons.get().is_empty() {
3313 return false;
3314 }
3315
3316 let result = fonts.fulfill_ready_promise_if_needed(cx);
3317
3318 if result {
3322 self.add_rendering_update_reason(RenderingUpdateReason::FontReadyPromiseFulfilled);
3323 }
3324
3325 result
3326 }
3327
3328 pub(crate) fn id_map(&self) -> &TreeOrderedIndexMap {
3329 &self.id_map
3330 }
3331
3332 pub(crate) fn add_resize_observer(&self, resize_observer: &ResizeObserver) {
3334 self.resize_observers
3335 .borrow_mut()
3336 .push(Dom::from_ref(resize_observer));
3337 }
3338
3339 pub(crate) fn gather_active_resize_observations_at_depth(
3342 &self,
3343 no_gc: &NoGC,
3344 depth: &ResizeObservationDepth,
3345 ) -> bool {
3346 let mut has_active_resize_observations = false;
3347 for observer in self.resize_observers.borrow_mut().iter_mut() {
3348 observer.gather_active_resize_observations_at_depth(
3349 no_gc,
3350 depth,
3351 &mut has_active_resize_observations,
3352 );
3353 }
3354 has_active_resize_observations
3355 }
3356
3357 #[expect(clippy::redundant_iter_cloned)]
3359 pub(crate) fn broadcast_active_resize_observations(
3360 &self,
3361 cx: &mut JSContext,
3362 ) -> ResizeObservationDepth {
3363 let mut shallowest = ResizeObservationDepth::max();
3364 let iterator: Vec<DomRoot<ResizeObserver>> = self
3368 .resize_observers
3369 .borrow()
3370 .iter()
3371 .cloned()
3372 .map(|obs| DomRoot::from_ref(&*obs))
3373 .collect();
3374 for observer in iterator {
3375 observer.broadcast_active_resize_observations(cx, &mut shallowest);
3376 }
3377 shallowest
3378 }
3379
3380 pub(crate) fn has_skipped_resize_observations(&self) -> bool {
3382 self.resize_observers
3383 .borrow()
3384 .iter()
3385 .any(|observer| observer.has_skipped_resize_observations())
3386 }
3387
3388 pub(crate) fn deliver_resize_loop_error_notification(&self, cx: &mut JSContext) {
3390 let error_info: ErrorInfo = crate::dom::bindings::error::ErrorInfo {
3391 message: "ResizeObserver loop completed with undelivered notifications.".to_string(),
3392 ..Default::default()
3393 };
3394 self.window
3395 .as_global_scope()
3396 .report_an_error(cx, error_info, HandleValue::null());
3397 }
3398
3399 pub(crate) fn status_code(&self) -> Option<u16> {
3400 self.status_code
3401 }
3402
3403 pub(crate) fn encoding_parse_a_url(&self, url: &str) -> Result<ServoUrl, url::ParseError> {
3405 let encoding = self.encoding.get();
3411
3412 let base_url = self.base_url();
3418
3419 url::Url::options()
3421 .base_url(Some(base_url.as_url()))
3422 .encoding_override(Some(&|input| {
3423 servo_url::encoding::encode_as_url_query_string(input, encoding)
3424 }))
3425 .parse(url)
3426 .map(ServoUrl::from)
3427 }
3428
3429 pub(crate) fn allowed_to_use_feature(&self, _feature: PermissionName) -> bool {
3431 if !self.has_browsing_context {
3433 return false;
3434 }
3435
3436 if !self.is_fully_active() {
3438 return false;
3439 }
3440
3441 true
3447 }
3448
3449 pub(crate) fn add_intersection_observer(&self, intersection_observer: &IntersectionObserver) {
3452 self.intersection_observers
3453 .borrow_mut()
3454 .push(Dom::from_ref(intersection_observer));
3455 }
3456
3457 pub(crate) fn remove_intersection_observer(
3461 &self,
3462 intersection_observer: &IntersectionObserver,
3463 ) {
3464 self.intersection_observers
3465 .borrow_mut()
3466 .retain(|observer| *observer != intersection_observer)
3467 }
3468
3469 pub(crate) fn update_intersection_observer_steps(
3471 &self,
3472 cx: &mut JSContext,
3473 time: CrossProcessInstant,
3474 ) {
3475 if self.intersection_observers.borrow().is_empty() {
3476 return;
3477 }
3478 self.window()
3480 .reflow_for_non_flushing_update_the_rendering_queries(cx);
3481
3482 for intersection_observer in &*self.intersection_observers.borrow() {
3484 self.update_single_intersection_observer_steps(cx, intersection_observer, time);
3485 }
3486 }
3487
3488 fn update_single_intersection_observer_steps(
3490 &self,
3491 cx: &mut JSContext,
3492 intersection_observer: &IntersectionObserver,
3493 time: CrossProcessInstant,
3494 ) {
3495 let root_bounds = intersection_observer.root_intersection_rectangle();
3498
3499 intersection_observer.update_intersection_observations_steps(cx, self, time, root_bounds);
3503 }
3504
3505 pub(crate) fn notify_intersection_observers(&self, cx: &mut JSContext) {
3507 self.intersection_observer_task_queued.set(false);
3510
3511 rooted_vec!(let notify_list <- self.intersection_observers.clone().take().into_iter());
3516
3517 for intersection_observer in notify_list.iter() {
3520 intersection_observer.invoke_callback_if_necessary(cx);
3522 }
3523 }
3524
3525 pub(crate) fn queue_an_intersection_observer_task(&self) {
3527 if self.intersection_observer_task_queued.get() {
3530 return;
3531 }
3532
3533 self.intersection_observer_task_queued.set(true);
3536
3537 let document = Trusted::new(self);
3541 self.owner_global()
3542 .task_manager()
3543 .intersection_observer_task_source()
3544 .queue(task!(notify_intersection_observers: move |cx| {
3545 document.root().notify_intersection_observers(cx);
3546 }));
3547 }
3548
3549 pub(crate) fn store_lcp_candidate(&self, candidate: LCPCandidate, element: &Element) {
3550 self.lcp_candidates.borrow_mut().insert(
3551 candidate.id,
3552 LCPCandidateAndElement {
3553 element: Dom::from_ref(element),
3554 candidate,
3555 },
3556 );
3557 }
3558
3559 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
3560 pub(crate) fn handle_paint_metric(&self, cx: &mut JSContext, event: PaintMetricEvent) {
3561 let metrics = self.interactive_time.borrow();
3562 let entry = match event {
3563 PaintMetricEvent::FirstPaint(metric_value, first_reflow) => {
3564 metrics.set_first_paint(metric_value, first_reflow);
3565 DomRoot::upcast::<PerformanceEntry>(PerformancePaintTiming::new(
3566 cx,
3567 self.window.as_global_scope(),
3568 ProgressiveWebMetricType::FirstPaint,
3569 metric_value,
3570 ))
3571 },
3572 PaintMetricEvent::FirstContentfulPaint(metric_value, first_reflow) => {
3573 metrics.set_first_contentful_paint(metric_value, first_reflow);
3574 DomRoot::upcast::<PerformanceEntry>(PerformancePaintTiming::new(
3575 cx,
3576 self.window.as_global_scope(),
3577 ProgressiveWebMetricType::FirstContentfulPaint,
3578 metric_value,
3579 ))
3580 },
3581 PaintMetricEvent::LargestContentfulPaint(metric_value, id) => {
3582 let candidate = self.lcp_candidates.borrow_mut().remove(&id);
3583 let (element, area, url) = match candidate {
3584 Some(stored_candidate) => (
3585 Some(stored_candidate.element),
3586 stored_candidate.candidate.area,
3587 stored_candidate.candidate.url,
3588 ),
3589 None => (None, 0, None),
3590 };
3591 metrics.set_largest_contentful_paint(id, metric_value);
3592 DomRoot::upcast::<PerformanceEntry>(LargestContentfulPaint::new(
3593 cx,
3594 self.window.as_global_scope(),
3595 metric_value,
3596 area,
3597 url,
3598 element.as_deref(),
3599 ))
3600 },
3601 };
3602 self.window.Performance(cx).queue_entry(&entry);
3603 }
3604
3605 fn write(
3607 &self,
3608 cx: &mut JSContext,
3609 text: Vec<TrustedHTMLOrString>,
3610 line_feed: bool,
3611 containing_class: &str,
3612 field: &str,
3613 ) -> ErrorResult {
3614 let mut strings: Vec<String> = Vec::with_capacity(text.len());
3616 let mut is_trusted = true;
3618 for value in text {
3620 match value {
3621 TrustedHTMLOrString::TrustedHTML(trusted_html) => {
3623 strings.push(trusted_html.to_string());
3624 },
3625 TrustedHTMLOrString::String(str_) => {
3626 is_trusted = false;
3628 strings.push(str_.into());
3630 },
3631 };
3632 }
3633 let mut string = itertools::join(strings, "");
3634 if !is_trusted {
3638 string = TrustedHTML::get_trusted_type_compliant_string(
3639 cx,
3640 &self.global(),
3641 TrustedHTMLOrString::String(string.into()),
3642 &format!("{} {}", containing_class, field),
3643 )?
3644 .str()
3645 .to_owned();
3646 }
3647 if line_feed {
3649 string.push('\n');
3650 }
3651 if !self.is_html_document() {
3653 return Err(Error::InvalidState(Some(
3654 "Document must be a HTML document".into(),
3655 )));
3656 }
3657
3658 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
3661 return Err(Error::InvalidState(Some(
3662 "A custom element constructor attempted to open, close or write to this document"
3663 .into(),
3664 )));
3665 }
3666
3667 if self.active_parser_was_aborted.get() {
3669 return Ok(());
3670 }
3671
3672 let parser = match self.get_current_parser() {
3673 Some(ref parser) if parser.can_write() => DomRoot::from_ref(&**parser),
3674 _ => {
3676 if self.is_prompting_or_unloading() ||
3679 self.ignore_destructive_writes_counter.get() > 0
3680 {
3681 return Ok(());
3682 }
3683 self.Open(cx, None, None)?;
3685 self.get_current_parser().unwrap()
3686 },
3687 };
3688
3689 parser.write(cx, string.into());
3691
3692 Ok(())
3693 }
3694
3695 pub(crate) fn details_name_groups<'a: 'b, 'b>(
3696 &'a self,
3697 no_gc: &'b NoGC,
3698 ) -> RefMut<'b, DetailsNameGroups> {
3699 RefMut::map(
3700 self.details_name_groups.safe_borrow_mut(no_gc),
3701 |details_name_groups| details_name_groups.get_or_insert_default(),
3702 )
3703 }
3704
3705 pub(crate) fn accessibility_data_mut(&self) -> RefMut<'_, AccessibilityData> {
3706 self.accessibility_data.borrow_mut()
3707 }
3708
3709 pub(crate) fn accessibility_active(&self) -> bool {
3710 self.window().layout().accessibility_active()
3711 }
3712
3713 pub(crate) fn rooted_nodes_for_accessibility_integrity_check(
3714 &self,
3715 ) -> Option<FxHashSet<OpaqueNode>> {
3716 if !self.accessibility_active() {
3717 return None;
3718 }
3719
3720 let mut accessibility_data = self.accessibility_data_mut();
3721
3722 if pref!(expensive_accessibility_test_assertions_enabled) {
3723 return Some(accessibility_data.unroot_and_drain_all_removed_nodes());
3724 }
3725
3726 accessibility_data.unroot_all_removed_nodes();
3727 None
3728 }
3729
3730 pub(crate) fn get_document_element_unrooted<'a>(
3731 &self,
3732 no_gc: &'a NoGC,
3733 ) -> Option<UnrootedDom<'a, Element>> {
3734 self.upcast::<Node>().child_elements_unrooted(no_gc).next()
3735 }
3736
3737 pub(crate) fn collect_reports(
3738 &self,
3739 reports: &mut Vec<Report>,
3740 ops: &mut MallocSizeOfOps,
3741 ) -> HashSet<*const JSObject> {
3742 let mut computed_objects = HashSet::new();
3743 let mut sizes = DocumentSizes::default();
3744
3745 for node in self
3746 .upcast::<Node>()
3747 .traverse_preorder(ShadowIncluding::Yes)
3748 {
3749 let size = compute_size(node.jsobject(), ops, &computed_objects, None);
3750
3751 match node.type_id() {
3752 NodeTypeId::Element(_) => {
3753 sizes.element_nodes_size += size;
3754
3755 let element = node.downcast::<Element>().expect("node must be Element");
3756 for attr in element.attrs().borrow().iter() {
3757 if let Some(attr) = attr.as_attr() {
3758 let size = compute_size(
3759 attr.upcast::<Node>().jsobject(),
3760 ops,
3761 &computed_objects,
3762 None,
3763 );
3764 sizes.attribute_nodes_size += size;
3765 computed_objects.insert(attr.upcast::<Node>().jsobject());
3766 }
3767 }
3768 },
3769 NodeTypeId::CharacterData(_) => sizes.text_nodes_size += size,
3770 _ => sizes.other_nodes_size += size,
3771 };
3772
3773 computed_objects.insert(node.jsobject());
3774 }
3775
3776 let prefix = format!("url({})", self.url());
3777 reports.push(Report {
3778 path: path![prefix, "js", "dom", "element-nodes"],
3779 kind: ReportKind::ExplicitJemallocHeapSize,
3780 size: sizes.element_nodes_size,
3781 });
3782 reports.push(Report {
3783 path: path![prefix, "js", "dom", "text-nodes"],
3784 kind: ReportKind::ExplicitJemallocHeapSize,
3785 size: sizes.text_nodes_size,
3786 });
3787 reports.push(Report {
3788 path: path![prefix, "js", "dom", "attribute-nodes"],
3789 kind: ReportKind::ExplicitJemallocHeapSize,
3790 size: sizes.attribute_nodes_size,
3791 });
3792 reports.push(Report {
3793 path: path![prefix, "js", "dom", "other-nodes"],
3794 kind: ReportKind::ExplicitJemallocHeapSize,
3795 size: sizes.other_nodes_size,
3796 });
3797
3798 computed_objects
3799 }
3800
3801 pub(crate) fn live_ranges(&self) -> &WeakRangeVec {
3803 &self.live_ranges
3804 }
3805}
3806
3807#[derive(Default)]
3809struct DocumentSizes {
3810 element_nodes_size: usize,
3811 text_nodes_size: usize,
3812 attribute_nodes_size: usize,
3813 other_nodes_size: usize,
3814}
3815
3816impl<'dom> LayoutDom<'dom, Document> {
3817 #[inline]
3818 pub(crate) fn is_html_document_for_layout(&self) -> bool {
3819 self.unsafe_get().is_html_document
3820 }
3821
3822 #[inline]
3823 pub(crate) fn quirks_mode(self) -> QuirksMode {
3824 self.unsafe_get().quirks_mode.get()
3825 }
3826
3827 #[inline]
3828 pub(crate) fn shared_style_locks(self) -> &'dom SharedRwLocks {
3829 self.unsafe_get().shared_style_locks()
3830 }
3831
3832 #[inline]
3833 pub(crate) fn flush_shadow_root_stylesheets_if_necessary(
3834 self,
3835 stylist: &mut Stylist,
3836 guard: &SharedRwLockReadGuard,
3837 ) {
3838 (*self.unsafe_get()).flush_shadow_root_stylesheets_if_necessary_for_layout(stylist, guard)
3839 }
3840
3841 pub(crate) fn elements_with_id(self, id: &Atom) -> &[LayoutDom<'dom, Element>] {
3842 self.unsafe_get().id_map.get_all_for_layout(id)
3843 }
3844
3845 #[expect(unsafe_code)]
3846 pub(crate) fn url_for_layout(self) -> ServoUrl {
3847 unsafe { self.unsafe_get().url.borrow_for_layout() }.clone()
3848 }
3849
3850 #[expect(unsafe_code)]
3851 pub(crate) fn visible_selection_for_layout(&self) -> Option<LayoutDom<'dom, Selection>> {
3852 unsafe { self.unsafe_get().selection.to_layout() }
3853 }
3854
3855 #[expect(unsafe_code)]
3856 pub(crate) fn default_language_for_layout(&self) -> Option<&'dom str> {
3857 unsafe { self.unsafe_get().default_language.borrow_for_layout() }.as_deref()
3858 }
3859}
3860
3861pub(crate) fn get_registrable_domain_suffix_of_or_is_equal_to(
3865 host_suffix_string: &str,
3866 original_host: Host,
3867) -> Option<Host> {
3868 if host_suffix_string.is_empty() {
3870 return None;
3871 }
3872
3873 let host = match Host::parse(host_suffix_string) {
3875 Ok(host) => host,
3876 Err(_) => return None,
3877 };
3878
3879 if host != original_host {
3881 let host = match host {
3883 Host::Domain(ref host) => host,
3884 _ => return None,
3885 };
3886 let original_host = match original_host {
3887 Host::Domain(ref original_host) => original_host,
3888 _ => return None,
3889 };
3890
3891 let index = original_host.len().checked_sub(host.len())?;
3893 let (prefix, suffix) = original_host.split_at(index);
3894
3895 if !prefix.ends_with('.') {
3896 return None;
3897 }
3898 if suffix != host {
3899 return None;
3900 }
3901
3902 if is_pub_domain(host) {
3904 return None;
3905 }
3906 }
3907
3908 Some(host)
3910}
3911
3912fn url_has_network_scheme(url: &ServoUrl) -> bool {
3914 matches!(url.scheme(), "ftp" | "http" | "https")
3915}
3916
3917#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
3918pub(crate) enum HasBrowsingContext {
3919 No,
3920 Yes,
3921}
3922
3923impl Document {
3924 #[allow(clippy::too_many_arguments)]
3925 pub(crate) fn new_inherited(
3926 window: &Window,
3927 has_browsing_context: HasBrowsingContext,
3928 url: Option<ServoUrl>,
3929 about_base_url: Option<ServoUrl>,
3930 origin: MutableOrigin,
3931 is_html_document: IsHTMLDocument,
3932 content_type: Option<Mime>,
3933 last_modified: Option<String>,
3934 activity: DocumentActivity,
3935 doc_loader: DocumentLoader,
3936 referrer: Option<String>,
3937 status_code: Option<u16>,
3938 canceller: FetchCanceller,
3939 is_initial_about_blank: bool,
3940 allow_declarative_shadow_roots: bool,
3941 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
3942 has_trustworthy_ancestor_origin: bool,
3943 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
3944 creation_sandboxing_flag_set: SandboxingFlagSet,
3945 timeline: &DocumentTimeline,
3946 pipeline_id: PipelineId,
3947 image_cache: StdArc<dyn ImageCache>,
3948 ) -> Document {
3949 let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
3950
3951 let frame_type = match window.is_top_level() {
3952 true => TimerMetadataFrameType::RootWindow,
3953 false => TimerMetadataFrameType::IFrame,
3954 };
3955 let interactive_time = ProgressiveWebMetrics::new(
3956 window.time_profiler_chan().clone(),
3957 url.clone(),
3958 frame_type,
3959 );
3960
3961 let content_type = content_type.unwrap_or_else(|| {
3962 match is_html_document {
3963 IsHTMLDocument::HTMLDocument => "text/html",
3965 IsHTMLDocument::NonHTMLDocument => "application/xml",
3967 }
3968 .parse()
3969 .unwrap()
3970 });
3971
3972 let encoding = content_type
3973 .get_parameter(CHARSET)
3974 .and_then(|charset| Encoding::for_label(charset.as_bytes()))
3975 .unwrap_or(UTF_8);
3976
3977 let has_focus = window.parent_info().is_none();
3978 let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
3979 let shared_style_locks = window.script_thread().shared_style_locks().clone();
3980 let quirks_mode = if is_initial_about_blank {
3984 QuirksMode::Quirks
3985 } else {
3986 QuirksMode::NoQuirks
3988 };
3989
3990 Document {
3991 node: Node::new_document_node(),
3992 document_or_shadow_root: DocumentOrShadowRoot::new(window),
3993 window: Dom::from_ref(window),
3994 has_browsing_context,
3995 implementation: Default::default(),
3996 content_type,
3997 last_modified,
3998 url: DomRefCell::new(url),
3999 about_base_url: DomRefCell::new(about_base_url),
4000 quirks_mode: Cell::new(quirks_mode),
4001 event_handler: DocumentEventHandler::new(window),
4002 focus_handler: DocumentFocusHandler::new(window, has_focus),
4003 embedder_controls: DocumentEmbedderControls::new(window),
4004 id_map: TreeOrderedIndexMap::id(),
4005 name_map: TreeOrderedIndexMap::name(),
4006 encoding: Cell::new(encoding),
4008 is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
4009 activity: Cell::new(activity),
4010 tag_map: DomRefCell::new(HashMapTracedValues::new_fx()),
4011 tagns_map: DomRefCell::new(HashMapTracedValues::new_fx()),
4012 classes_map: DomRefCell::new(HashMapTracedValues::new()),
4013 images: Default::default(),
4014 embeds: Default::default(),
4015 links: Default::default(),
4016 forms: Default::default(),
4017 scripts: Default::default(),
4018 anchors: Default::default(),
4019 applets: Default::default(),
4020 iframes: RefCell::new(IFrameCollection::new()),
4021 shared_style_locks,
4022 stylesheets: DomRefCell::new(DocumentStylesheetSet::new()),
4023 stylesheet_list: MutNullableDom::new(None),
4024 ready_state: Cell::new(DocumentReadyState::Complete),
4027 current_script: Default::default(),
4028 current_the_end_loading_phase: Default::default(),
4029 pending_parsing_blocking_script: Default::default(),
4030 script_blocking_stylesheet_set: Default::default(),
4031 render_blocking_element_count: Default::default(),
4032 deferred_scripts: Default::default(),
4033 asap_in_order_scripts_list: Default::default(),
4034 asap_scripts_set: Default::default(),
4035 animation_frame_ident: Cell::new(0),
4036 animation_frame_list: DomRefCell::new(VecDeque::new()),
4037 running_animation_callbacks: Cell::new(false),
4038 loader: DomRefCell::new(doc_loader),
4039 current_parser: Default::default(),
4040 base_element: Default::default(),
4041 target_base_element: Default::default(),
4042 ancestor_origins_list: Default::default(),
4043 internal_ancestor_origin_objects_list: Default::default(),
4044 appropriate_template_contents_owner_document: Default::default(),
4045 pending_restyles: DomRefCell::new(FxHashMap::default()),
4046 needs_restyle: Cell::new(RestyleReason::DOMChanged),
4047 origin: DomRefCell::new(origin),
4048 referrer,
4049 target_element: MutNullableDom::new(None),
4050 policy_container: DomRefCell::new(PolicyContainer::default()),
4051 preloaded_resources: Default::default(),
4052 ignore_destructive_writes_counter: Default::default(),
4053 ignore_opens_during_unload_counter: Default::default(),
4054 spurious_animation_frames: Cell::new(0),
4055 fullscreen_element: MutNullableDom::new(None),
4056 form_id_listener_map: Default::default(),
4057 interactive_time: DomRefCell::new(interactive_time),
4058 tti_window: DomRefCell::new(InteractiveWindow::default()),
4059 canceller,
4060 throw_on_dynamic_markup_insertion_counter: Cell::new(0),
4061 page_showing: Cell::new(false),
4062 salvageable: Cell::new(true),
4063 active_parser_was_aborted: Cell::new(false),
4064 fired_unload: Cell::new(false),
4065 responsive_images: Default::default(),
4066 navigation_timing: Default::default(),
4067 resource_fetch_timing: RefCell::new(None),
4068 completely_loaded: Cell::new(false),
4069 script_and_layout_blockers: Cell::new(0),
4070 delayed_tasks: Default::default(),
4071 shadow_roots: DomRefCell::new(HashSet::new()),
4072 shadow_roots_styles_changed: Cell::new(false),
4073 media_controls: DomRefCell::new(HashMap::new()),
4074 dirty_canvases: DomRefCell::new(Default::default()),
4075 has_pending_animated_image_update: Cell::new(false),
4076 selection: MutNullableDom::new(None),
4077 timeline: Dom::from_ref(timeline),
4078 animation_manager: AnimationManager::new(),
4079 dirty_root: Default::default(),
4080 declarative_refresh: Default::default(),
4081 resize_observers: Default::default(),
4082 fonts: Default::default(),
4083 visibility_state: Cell::new(DocumentVisibilityState::Visible),
4087 status_code,
4088 is_initial_about_blank: Cell::new(is_initial_about_blank),
4089 allow_declarative_shadow_roots: Cell::new(allow_declarative_shadow_roots),
4090 inherited_insecure_requests_policy: Cell::new(inherited_insecure_requests_policy),
4091 has_trustworthy_ancestor_origin: Cell::new(has_trustworthy_ancestor_origin),
4092 intersection_observer_task_queued: Cell::new(false),
4093 intersection_observers: Default::default(),
4094 highlighted_dom_node: Default::default(),
4095 lcp_candidates: DomRefCell::new(Default::default()),
4096 adopted_stylesheets: Default::default(),
4097 adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
4098 pending_scroll_events: Default::default(),
4099 rendering_update_reasons: Default::default(),
4100 waiting_on_canvas_image_updates: Cell::new(false),
4101 root_removal_noted: Cell::new(true),
4102 current_rendering_epoch: Default::default(),
4103 custom_element_reaction_stack,
4104 active_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
4105 creation_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
4106 favicon: RefCell::new(None),
4107 websockets: DOMTracker::new(),
4108 details_name_groups: Default::default(),
4109 protocol_handler_automation_mode: Default::default(),
4110 layout_animations_test_enabled: pref!(layout_animations_test_enabled),
4111 state_override: Default::default(),
4112 value_override: Default::default(),
4113 default_single_line_container_name: Default::default(),
4114 css_styling_flag: Default::default(),
4115 accessibility_data: Default::default(),
4116 iframe_load_in_progress: Default::default(),
4117 mute_iframe_load: Default::default(),
4118 timers: OneshotTimers::new(window.upcast()),
4119 pipeline_id,
4120 task_manager: Rc::new(TaskManager::new(
4121 Some(window.event_loop_sender()),
4122 pipeline_id,
4123 None,
4124 )),
4125 image_cache,
4126 history: Default::default(),
4127 theme: Default::default(),
4128 default_language: Default::default(),
4129 window_detached: Default::default(),
4130 live_ranges: Default::default(),
4131 module_map: Default::default(),
4132 }
4133 }
4134
4135 pub(crate) fn detach_window(&self) {
4136 self.window_detached.set(true);
4137 }
4138
4139 pub(crate) fn window_detached(&self) -> bool {
4140 self.window_detached.get()
4141 }
4142
4143 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
4145 if let Some(csp_list) = self.get_csp_list().as_ref() {
4146 for policy in &csp_list.0 {
4147 if policy.contains_a_directive_whose_name_is("upgrade-insecure-requests") &&
4148 policy.disposition == PolicyDisposition::Enforce
4149 {
4150 return InsecureRequestsPolicy::Upgrade;
4151 }
4152 }
4153 }
4154
4155 self.inherited_insecure_requests_policy
4156 .get()
4157 .unwrap_or(InsecureRequestsPolicy::DoNotUpgrade)
4158 }
4159
4160 pub(crate) fn event_handler(&self) -> &DocumentEventHandler {
4162 &self.event_handler
4163 }
4164
4165 pub(crate) fn focus_handler(&self) -> &DocumentFocusHandler {
4167 &self.focus_handler
4168 }
4169
4170 pub(crate) fn embedder_controls(&self) -> &DocumentEmbedderControls {
4172 &self.embedder_controls
4173 }
4174
4175 fn has_pending_scroll_events(&self) -> bool {
4178 !self.pending_scroll_events.borrow().is_empty()
4179 }
4180
4181 pub(crate) fn add_rendering_update_reason(&self, reason: RenderingUpdateReason) {
4184 self.rendering_update_reasons
4185 .set(self.rendering_update_reasons.get().union(reason));
4186 }
4187
4188 pub(crate) fn clear_rendering_update_reasons(&self) {
4190 self.rendering_update_reasons
4191 .set(RenderingUpdateReason::empty())
4192 }
4193
4194 pub(crate) fn add_script_and_layout_blocker(&self) {
4201 self.script_and_layout_blockers
4202 .set(self.script_and_layout_blockers.get() + 1);
4203 }
4204
4205 pub(crate) fn remove_script_and_layout_blocker(&self, cx: &mut JSContext) {
4209 assert!(self.script_and_layout_blockers.get() > 0);
4210 self.script_and_layout_blockers
4211 .set(self.script_and_layout_blockers.get() - 1);
4212 while self.script_and_layout_blockers.get() == 0 && !self.delayed_tasks.borrow().is_empty()
4213 {
4214 let task = self.delayed_tasks.borrow_mut().remove(0);
4215 task.run_box(cx);
4216 }
4217 }
4218
4219 pub(crate) fn add_delayed_task<T: 'static + NonSendTaskBox>(&self, task: T) {
4221 self.delayed_tasks.borrow_mut().push(Box::new(task));
4222 }
4223
4224 pub(crate) fn is_safe_to_run_script_or_layout(&self) -> bool {
4227 self.script_and_layout_blockers.get() == 0
4228 }
4229
4230 pub(crate) fn ensure_safe_to_run_script_or_layout(&self) {
4233 assert!(
4234 self.is_safe_to_run_script_or_layout(),
4235 "Attempt to use script or layout while DOM not in a stable state"
4236 );
4237 }
4238
4239 #[allow(clippy::too_many_arguments)]
4240 pub(crate) fn new(
4241 cx: &mut JSContext,
4242 window: &Window,
4243 has_browsing_context: HasBrowsingContext,
4244 url: Option<ServoUrl>,
4245 about_base_url: Option<ServoUrl>,
4246 origin: MutableOrigin,
4247 doctype: IsHTMLDocument,
4248 content_type: Option<Mime>,
4249 last_modified: Option<String>,
4250 activity: DocumentActivity,
4251 doc_loader: DocumentLoader,
4252 referrer: Option<String>,
4253 status_code: Option<u16>,
4254 canceller: FetchCanceller,
4255 is_initial_about_blank: bool,
4256 allow_declarative_shadow_roots: bool,
4257 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4258 has_trustworthy_ancestor_origin: bool,
4259 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4260 creation_sandboxing_flag_set: SandboxingFlagSet,
4261 pipeline_id: PipelineId,
4262 image_cache: StdArc<dyn ImageCache>,
4263 ) -> DomRoot<Document> {
4264 Self::new_with_proto(
4265 cx,
4266 window,
4267 None,
4268 has_browsing_context,
4269 url,
4270 about_base_url,
4271 origin,
4272 doctype,
4273 content_type,
4274 last_modified,
4275 activity,
4276 doc_loader,
4277 referrer,
4278 status_code,
4279 canceller,
4280 is_initial_about_blank,
4281 allow_declarative_shadow_roots,
4282 inherited_insecure_requests_policy,
4283 has_trustworthy_ancestor_origin,
4284 custom_element_reaction_stack,
4285 creation_sandboxing_flag_set,
4286 pipeline_id,
4287 image_cache,
4288 )
4289 }
4290
4291 #[allow(clippy::too_many_arguments)]
4292 fn new_with_proto(
4293 cx: &mut JSContext,
4294 window: &Window,
4295 proto: Option<HandleObject>,
4296 has_browsing_context: HasBrowsingContext,
4297 url: Option<ServoUrl>,
4298 about_base_url: Option<ServoUrl>,
4299 origin: MutableOrigin,
4300 doctype: IsHTMLDocument,
4301 content_type: Option<Mime>,
4302 last_modified: Option<String>,
4303 activity: DocumentActivity,
4304 doc_loader: DocumentLoader,
4305 referrer: Option<String>,
4306 status_code: Option<u16>,
4307 canceller: FetchCanceller,
4308 is_initial_about_blank: bool,
4309 allow_declarative_shadow_roots: bool,
4310 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
4311 has_trustworthy_ancestor_origin: bool,
4312 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
4313 creation_sandboxing_flag_set: SandboxingFlagSet,
4314 pipeline_id: PipelineId,
4315 image_cache: StdArc<dyn ImageCache>,
4316 ) -> DomRoot<Document> {
4317 let timeline = DocumentTimeline::new(cx, window);
4318 let document = reflect_dom_object_with_proto(
4319 cx,
4320 Box::new(Document::new_inherited(
4321 window,
4322 has_browsing_context,
4323 url,
4324 about_base_url,
4325 origin,
4326 doctype,
4327 content_type,
4328 last_modified,
4329 activity,
4330 doc_loader,
4331 referrer,
4332 status_code,
4333 canceller,
4334 is_initial_about_blank,
4335 allow_declarative_shadow_roots,
4336 inherited_insecure_requests_policy,
4337 has_trustworthy_ancestor_origin,
4338 custom_element_reaction_stack,
4339 creation_sandboxing_flag_set,
4340 &timeline,
4341 pipeline_id,
4342 image_cache,
4343 )),
4344 window,
4345 proto,
4346 );
4347 {
4348 let node = document.upcast::<Node>();
4349 node.set_owner_doc(&document);
4350 }
4351 document
4352 }
4353
4354 pub(crate) fn get_redirect_count(&self) -> u16 {
4355 self.resource_fetch_timing()
4356 .as_ref()
4357 .map_or(0, |resource_fetch_timing| {
4358 resource_fetch_timing.redirect_count
4359 })
4360 }
4361
4362 pub(crate) fn set_resource_fetch_timing(&self, timing: ResourceFetchTiming) {
4363 self.resource_fetch_timing.replace(Some(timing));
4364 }
4365
4366 pub(crate) fn resource_fetch_timing(&self) -> Ref<'_, Option<ResourceFetchTiming>> {
4367 self.resource_fetch_timing.borrow()
4368 }
4369
4370 pub(crate) fn navigation_timing(&self) -> Rc<NavigationTiming> {
4371 self.navigation_timing.clone()
4372 }
4373
4374 pub(crate) fn performance_timing_attribute(
4375 &self,
4376 name: &str,
4377 ) -> Fallible<Option<CrossProcessInstant>> {
4378 Ok(match name {
4379 "unloadEventStart" => self.navigation_timing().unload_event_start.get(),
4380 "unloadEventEnd" => self.navigation_timing().unload_event_end.get(),
4381 "domInteractive" => self.navigation_timing().dom_interactive.get(),
4382 "domContentLoadedEventStart" => self
4383 .navigation_timing()
4384 .dom_content_loaded_event_start
4385 .get(),
4386 "domContentLoadedEventEnd" => {
4387 self.navigation_timing().dom_content_loaded_event_end.get()
4388 },
4389 "domComplete" => self.navigation_timing().dom_complete.get(),
4390 "loadEventStart" => self.navigation_timing().load_event_start.get(),
4391 "loadEventEnd" => self.navigation_timing().load_event_end.get(),
4392 "redirectStart" | "redirectEnd" | "secureConnectionStart" | "responseEnd" => self
4393 .resource_fetch_timing()
4394 .as_ref()
4395 .and_then(|resource_fetch_timing| match name {
4396 "redirectStart" => resource_fetch_timing.redirect_start,
4397 "redirectEnd" => resource_fetch_timing.redirect_end,
4398 "secureConnectionStart" => resource_fetch_timing.secure_connection_start,
4399 "responseEnd" => resource_fetch_timing.response_end,
4400 _ => None,
4401 }),
4402 _ => {
4403 return Err(Error::Operation(Some(format!(
4404 "{name} hasn't been implemented."
4405 ))));
4406 },
4407 })
4408 }
4409
4410 pub(crate) fn elements_by_name_count(&self, name: &DOMString) -> u32 {
4411 if name.is_empty() {
4412 return 0;
4413 }
4414 self.count_node_list(|n| Document::is_element_in_get_by_name(n, name))
4415 }
4416
4417 pub(crate) fn nth_element_by_name<'a>(
4418 &self,
4419 no_gc: &'a NoGC,
4420 index: u32,
4421 name: &DOMString,
4422 ) -> Option<UnrootedDom<'a, Node>> {
4423 if name.is_empty() {
4424 return None;
4425 }
4426 self.nth_in_node_list(no_gc, index, |n| {
4427 Document::is_element_in_get_by_name(n, name)
4428 })
4429 }
4430
4431 fn is_element_in_get_by_name(node: &Node, name: &DOMString) -> bool {
4434 let element = match node.downcast::<Element>() {
4435 Some(element) => element,
4436 None => return false,
4437 };
4438 if element.namespace() != &ns!(html) {
4439 return false;
4440 }
4441 element.get_name().is_some_and(|n| &*n == name)
4442 }
4443
4444 fn count_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> u32 {
4445 let doc = self.GetDocumentElement();
4446 let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
4447 maybe_node
4448 .iter()
4449 .flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
4450 .filter(|node| callback(node))
4451 .count() as u32
4452 }
4453
4454 fn nth_in_node_list<'a, F: Fn(&Node) -> bool>(
4455 &self,
4456 no_gc: &'a NoGC,
4457 index: u32,
4458 callback: F,
4459 ) -> Option<UnrootedDom<'a, Node>> {
4460 let doc = self.get_document_element_unrooted(no_gc)?;
4461 doc.upcast::<Node>()
4462 .traverse_preorder_non_rooting(no_gc, ShadowIncluding::No)
4463 .filter(|node| callback(node))
4464 .nth(index as usize)
4465 }
4466
4467 fn get_html_element(&self) -> Option<DomRoot<HTMLHtmlElement>> {
4468 self.GetDocumentElement().and_then(DomRoot::downcast)
4469 }
4470
4471 pub(crate) fn shared_style_locks(&self) -> &SharedRwLocks {
4473 &self.shared_style_locks
4474 }
4475
4476 pub(crate) fn style_shared_author_lock(&self) -> &SharedRwLock {
4478 &self.shared_style_locks.author
4479 }
4480
4481 pub(crate) fn flush_stylesheets_for_reflow(&self) -> bool {
4483 let mut stylesheets = self.stylesheets.borrow_mut();
4490 let have_changed = stylesheets.has_changed();
4491 stylesheets.flush_without_invalidation();
4492 have_changed
4493 }
4494
4495 pub(crate) fn salvageable(&self) -> bool {
4496 self.salvageable.get()
4497 }
4498
4499 pub(crate) fn make_document_unsalvageable(&self) {
4501 self.salvageable.set(false);
4507 }
4508
4509 pub(crate) fn appropriate_template_contents_owner_document(
4511 &self,
4512 cx: &mut JSContext,
4513 ) -> DomRoot<Document> {
4514 self.appropriate_template_contents_owner_document
4515 .or_init(|| {
4516 let doctype = if self.is_html_document {
4517 IsHTMLDocument::HTMLDocument
4518 } else {
4519 IsHTMLDocument::NonHTMLDocument
4520 };
4521 let new_doc = Document::new(
4522 cx,
4523 self.window(),
4524 HasBrowsingContext::No,
4525 None,
4526 None,
4527 MutableOrigin::new(ImmutableOrigin::new_opaque()),
4529 doctype,
4530 None,
4531 None,
4532 DocumentActivity::Inactive,
4533 DocumentLoader::new(&self.loader()),
4534 None,
4535 None,
4536 Default::default(),
4537 false,
4538 self.allow_declarative_shadow_roots(),
4539 Some(self.insecure_requests_policy()),
4540 self.has_trustworthy_ancestor_or_current_origin(),
4541 self.custom_element_reaction_stack.clone(),
4542 self.creation_sandboxing_flag_set(),
4543 self.pipeline_id(),
4544 self.image_cache.clone(),
4545 );
4546 new_doc
4547 .appropriate_template_contents_owner_document
4548 .set(Some(&new_doc));
4549 new_doc
4550 })
4551 }
4552
4553 pub(crate) fn get_element_by_id(&self, no_gc: &NoGC, id: &Atom) -> Option<DomRoot<Element>> {
4554 self.id_map.get(no_gc, self.upcast(), id)
4555 }
4556
4557 pub(crate) fn ensure_pending_restyle(&self, el: &Element) -> RefMut<'_, PendingRestyle> {
4558 let map = self.pending_restyles.borrow_mut();
4559 RefMut::map(map, |m| {
4560 &mut m
4561 .entry(Dom::from_ref(el))
4562 .or_insert_with(|| NoTrace(PendingRestyle::default()))
4563 .0
4564 })
4565 }
4566
4567 pub(crate) fn element_attr_will_change(&self, el: &Element, attr: AttrRef<'_>) {
4568 let mut entry = self.ensure_pending_restyle(el);
4574 if entry.snapshot.is_none() {
4575 entry.snapshot = Some(Snapshot::new());
4576 }
4577 if attr.local_name() == &local_name!("style") {
4578 entry.hint.insert(RestyleHint::RESTYLE_STYLE_ATTRIBUTE);
4579 }
4580
4581 if vtable_for(el.upcast()).attribute_affects_presentational_hints(attr) ||
4582 el.check_style_on_self_or_eager_pseudos(|style| {
4583 if let Some(ref attribute_references) = style.attribute_references {
4584 return attribute_references.contains_key(attr.local_name());
4585 }
4586 false
4587 })
4588 {
4589 entry.hint.insert(RestyleHint::RESTYLE_SELF);
4590 }
4591
4592 let snapshot = entry.snapshot.as_mut().unwrap();
4593 if attr.local_name() == &local_name!("id") {
4594 if snapshot.id_changed {
4595 return;
4596 }
4597 snapshot.id_changed = true;
4598 } else if attr.local_name() == &local_name!("class") {
4599 if snapshot.class_changed {
4600 return;
4601 }
4602 snapshot.class_changed = true;
4603 } else {
4604 snapshot.other_attributes_changed = true;
4605 }
4606 let local_name = style::LocalName::cast(attr.local_name());
4607 if !snapshot.changed_attrs.contains(local_name) {
4608 snapshot.changed_attrs.push(local_name.clone());
4609 }
4610 if snapshot.attrs.is_none() {
4611 let attrs = el
4612 .attrs()
4613 .borrow()
4614 .iter()
4615 .map(|attr| (attr.as_identifier(), attr.value().clone()))
4616 .collect();
4617 snapshot.attrs = Some(attrs);
4618 }
4619 }
4620
4621 pub(crate) fn set_referrer_policy(&self, policy: ReferrerPolicy) {
4622 self.policy_container
4623 .borrow_mut()
4624 .set_referrer_policy(policy);
4625 }
4626
4627 pub(crate) fn get_referrer_policy(&self) -> ReferrerPolicy {
4628 self.policy_container.borrow().get_referrer_policy()
4629 }
4630
4631 pub(crate) fn set_target_element(&self, node: Option<&Element>) {
4632 if let Some(ref element) = self.target_element.get() {
4633 element.set_target_state(false);
4634 }
4635
4636 self.target_element.set(node);
4637
4638 if let Some(ref element) = self.target_element.get() {
4639 element.set_target_state(true);
4640 }
4641 }
4642
4643 pub(crate) fn incr_ignore_destructive_writes_counter(&self) {
4644 self.ignore_destructive_writes_counter
4645 .set(self.ignore_destructive_writes_counter.get() + 1);
4646 }
4647
4648 pub(crate) fn decr_ignore_destructive_writes_counter(&self) {
4649 self.ignore_destructive_writes_counter
4650 .set(self.ignore_destructive_writes_counter.get() - 1);
4651 }
4652
4653 pub(crate) fn is_prompting_or_unloading(&self) -> bool {
4654 self.ignore_opens_during_unload_counter.get() > 0
4655 }
4656
4657 fn incr_ignore_opens_during_unload_counter(&self) {
4658 self.ignore_opens_during_unload_counter
4659 .set(self.ignore_opens_during_unload_counter.get() + 1);
4660 }
4661
4662 fn decr_ignore_opens_during_unload_counter(&self) {
4663 self.ignore_opens_during_unload_counter
4664 .set(self.ignore_opens_during_unload_counter.get() - 1);
4665 }
4666
4667 pub(crate) fn set_fullscreen_element(&self, element: Option<&Element>) {
4668 self.fullscreen_element.set(element);
4669 }
4670
4671 fn reset_form_owner_for_listeners(&self, cx: &mut JSContext, id: &Atom) {
4672 let map = self.form_id_listener_map.borrow();
4673 if let Some(listeners) = map.get(id) {
4674 for listener in listeners {
4675 listener
4676 .as_maybe_form_control()
4677 .expect("Element must be a form control")
4678 .reset_form_owner(cx);
4679 }
4680 }
4681 }
4682
4683 pub(crate) fn register_shadow_root(&self, shadow_root: &ShadowRoot) {
4684 self.shadow_roots
4685 .borrow_mut()
4686 .insert(Dom::from_ref(shadow_root));
4687 self.invalidate_shadow_roots_stylesheets();
4688 }
4689
4690 pub(crate) fn unregister_shadow_root(&self, shadow_root: &ShadowRoot) {
4691 let mut shadow_roots = self.shadow_roots.borrow_mut();
4692 shadow_roots.remove(&Dom::from_ref(shadow_root));
4693 }
4694
4695 pub(crate) fn invalidate_shadow_roots_stylesheets(&self) {
4696 self.shadow_roots_styles_changed.set(true);
4697 }
4698
4699 pub(crate) fn flush_shadow_root_stylesheets_if_necessary_for_layout(
4700 &self,
4701 stylist: &mut Stylist,
4702 guard: &SharedRwLockReadGuard,
4703 ) {
4704 if !self.shadow_roots_styles_changed.get() {
4705 return;
4706 }
4707 #[expect(unsafe_code)]
4708 unsafe {
4709 for shadow_root in self.shadow_roots.borrow_for_layout().iter() {
4710 let layout: LayoutDom<'_, _> = shadow_root.to_layout();
4711 layout.flush_stylesheets_for_layout(stylist, guard);
4712 }
4713 }
4714 self.shadow_roots_styles_changed.set(false);
4715 }
4716
4717 pub(crate) fn stylesheet_count(&self) -> usize {
4718 self.stylesheets.borrow().len()
4719 }
4720
4721 pub(crate) fn stylesheet_at(
4722 &self,
4723 cx: &mut JSContext,
4724 index: usize,
4725 ) -> Option<DomRoot<CSSStyleSheet>> {
4726 let stylesheets = self.stylesheets.borrow();
4727
4728 stylesheets
4729 .get(Origin::Author, index)
4730 .and_then(|s| s.owner.get_cssom_object(cx))
4731 }
4732
4733 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(
4740 &self,
4741 no_gc: &NoGC,
4742 owner_node: &Element,
4743 sheet: Arc<Stylesheet>,
4744 ) {
4745 let insertion_point = {
4746 let stylesheets = &mut *self.stylesheets.borrow_mut();
4747
4748 stylesheets
4750 .iter()
4751 .map(|(sheet, _origin)| sheet)
4752 .find(|sheet_in_doc| {
4753 match &sheet_in_doc.owner {
4754 StylesheetSource::Element(other_node) => owner_node
4755 .upcast::<Node>()
4756 .is_before(no_gc, other_node.upcast()),
4757 StylesheetSource::Constructed(_) => true,
4760 }
4761 })
4762 .cloned()
4763 };
4764
4765 if self.has_browsing_context() {
4766 self.add_stylesheet_to_stylist(
4767 sheet.clone(),
4768 insertion_point.as_ref().map(|s| s.sheet.clone()),
4769 );
4770 }
4771
4772 let stylesheets = &mut *self.stylesheets.borrow_mut();
4773 DocumentOrShadowRoot::add_stylesheet(
4774 StylesheetSource::Element(Dom::from_ref(owner_node)),
4775 StylesheetSetRef::Document(stylesheets),
4776 sheet,
4777 insertion_point,
4778 self.style_shared_author_lock(),
4779 );
4780 }
4781
4782 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
4787 pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
4788 debug_assert!(cssom_stylesheet.is_constructed());
4789
4790 let sheet = cssom_stylesheet.style_stylesheet().clone();
4791 let insertion_point = {
4792 let stylesheets = &mut *self.stylesheets.borrow_mut();
4793
4794 stylesheets
4795 .iter()
4796 .last()
4797 .map(|(sheet, _origin)| sheet)
4798 .cloned()
4799 };
4800
4801 if self.has_browsing_context() {
4802 self.add_stylesheet_to_stylist(
4803 sheet.clone(),
4804 insertion_point.as_ref().map(|s| s.sheet.clone()),
4805 );
4806 }
4807
4808 let stylesheets = &mut *self.stylesheets.borrow_mut();
4809 DocumentOrShadowRoot::add_stylesheet(
4810 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
4811 StylesheetSetRef::Document(stylesheets),
4812 sheet,
4813 insertion_point,
4814 self.style_shared_author_lock(),
4815 );
4816 }
4817
4818 pub(crate) fn add_stylesheet_to_stylist(
4819 &self,
4820 stylesheet: Arc<Stylesheet>,
4821 before_stylesheet: Option<Arc<Stylesheet>>,
4822 ) {
4823 self.window
4824 .layout_mut()
4825 .add_stylesheet(stylesheet, before_stylesheet);
4826 }
4827
4828 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, stylesheet: &Arc<Stylesheet>) {
4831 if self.has_browsing_context() {
4832 self.window
4833 .layout_mut()
4834 .remove_stylesheet(stylesheet.clone());
4835 }
4836
4837 DocumentOrShadowRoot::remove_stylesheet(
4838 owner,
4839 stylesheet,
4840 StylesheetSetRef::Document(&mut *self.stylesheets.borrow_mut()),
4841 )
4842 }
4843
4844 pub(crate) fn get_elements_with_id(
4845 &self,
4846 cx: &mut JSContext,
4847 id: &Atom,
4848 ) -> Ref<'_, [Dom<Element>]> {
4849 self.id_map.get_all(cx.no_gc(), self.upcast(), id)
4850 }
4851
4852 pub(crate) fn get_elements_with_name(
4853 &self,
4854 cx: &mut JSContext,
4855 name: &Atom,
4856 ) -> Ref<'_, [Dom<Element>]> {
4857 self.name_map.get_all(cx.no_gc(), self.upcast(), name)
4858 }
4859
4860 pub(crate) fn drain_pending_restyles(
4861 &self,
4862 no_gc: &NoGC,
4863 ) -> Vec<(TrustedNodeAddress, PendingRestyle)> {
4864 self.pending_restyles
4865 .borrow_mut()
4866 .drain()
4867 .filter_map(|(element, restyle)| {
4868 let node = element.upcast::<Node>();
4869 if !node.get_flag(NodeFlags::IS_CONNECTED) {
4870 return None;
4871 }
4872 element.note_dirty_descendants(no_gc);
4873 Some((node.to_trusted_node_address(), restyle.0))
4874 })
4875 .collect()
4876 }
4877
4878 pub(crate) fn advance_animation_timeline_for_testing(&self, delta: TimeDuration) {
4879 self.timeline.advance_specific(delta);
4880 let current_timeline_value = self.current_animation_timeline_value();
4881 self.animation_manager
4882 .update_for_new_timeline_value(&self.window, current_timeline_value);
4883 }
4884
4885 pub(crate) fn maybe_mark_animating_nodes_as_dirty(&self, no_gc: &NoGC) {
4886 let current_timeline_value = self.current_animation_timeline_value();
4887 self.animation_manager
4888 .mark_animating_nodes_as_dirty(no_gc, current_timeline_value);
4889 }
4890
4891 pub(crate) fn current_animation_timeline_value(&self) -> f64 {
4892 self.timeline
4893 .upcast::<AnimationTimeline>()
4894 .current_time_in_seconds()
4895 }
4896
4897 pub(crate) fn animation_manager(&self) -> &AnimationManager {
4898 &self.animation_manager
4899 }
4900
4901 pub(crate) fn update_animations_post_reflow(&self) {
4902 let current_timeline_value = self.current_animation_timeline_value();
4903 self.animation_manager
4904 .do_post_reflow_update(&self.window, current_timeline_value);
4905 }
4906
4907 pub(crate) fn cancel_animations_for_node(&self, node: &Node) {
4908 self.animation_manager.cancel_animations_for_node(node);
4909 }
4910
4911 pub(crate) fn remove_style_and_layout_data_from_subtree(
4915 &self,
4916 no_gc: &NoGC,
4917 subtree_root: &Node,
4918 ) {
4919 for node in subtree_root.traverse_preorder_non_rooting(no_gc, ShadowIncluding::Yes) {
4920 self.clean_up_style_and_layout_data_for_node(&node);
4921 }
4922 }
4923
4924 pub(crate) fn clean_up_style_and_layout_data_for_node(&self, node: &Node) {
4925 node.clear_layout_data();
4926 if let Some(element) = node.downcast::<Element>() {
4927 element.clean_up_style_data();
4928
4929 if self.dirty_root == Some(element) {
4934 self.dirty_root.clear();
4935 }
4936 }
4937
4938 node.set_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION, false);
4939 node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false);
4940 }
4941
4942 pub(crate) fn update_animations_and_send_events(&self, cx: &mut CurrentRealm) {
4944 if !self.layout_animations_test_enabled {
4946 self.timeline.update(self.window());
4947 }
4948
4949 let current_timeline_value = self.current_animation_timeline_value();
4956 self.animation_manager
4957 .update_for_new_timeline_value(&self.window, current_timeline_value);
4958 self.maybe_mark_animating_nodes_as_dirty(cx.no_gc());
4959
4960 self.window().perform_a_microtask_checkpoint(cx);
4962
4963 self.animation_manager()
4965 .send_pending_events(self.window(), cx);
4966 }
4967
4968 pub(crate) fn set_has_pending_animated_image_update(&self) {
4969 self.has_pending_animated_image_update.set(true);
4970 }
4971
4972 pub(crate) fn shared_declarative_refresh_steps(&self, content: &[u8], from_meta_element: bool) {
4974 if self.will_declaratively_refresh() {
4976 return;
4977 }
4978
4979 static REFRESH_REGEX: LazyLock<Regex> = LazyLock::new(|| {
4981 Regex::new(
4985 r#"(?xs)
4986 ^
4987 \s* # 3
4988 ((?<time>[0-9]+)|\.) # 5-6
4989 [0-9.]* # 8
4990 (
4991 (
4992 (\s*;|\s*,|\s) # 10.3
4993 \s* # 10.4
4994 )
4995 (
4996 (
4997 (U|u)(R|r)(L|l) # 11.2-11.4
4998 \s*=\s* # 11.5-11.7
4999 )?
5000 ('(?<url1>[^']*)'(?s-u:.)*|"(?<url2>[^"]*)"(?s-u:.)*|['"]?(?<url3>(?s-u:.)*)) # 11.8 - 11.10
5001 |
5002 (?<url4>(?s-u:.)*)
5003 )
5004 )?
5005 $
5006 "#,
5007 )
5008 .unwrap()
5009 });
5010
5011 let mut url_record = self.url();
5013 let captures = if let Some(captures) = REFRESH_REGEX.captures(content) {
5014 captures
5015 } else {
5016 return;
5017 };
5018 let time = if let Some(time_string) = captures.name("time") {
5019 u64::from_str(&String::from_utf8_lossy(time_string.as_bytes())).unwrap_or(0)
5020 } else {
5021 0
5022 };
5023 let captured_url = captures.name("url1").or(captures
5024 .name("url2")
5025 .or(captures.name("url3").or(captures.name("url4"))));
5026
5027 if let Some(url_match) = captured_url {
5029 url_record = if let Ok(url) = ServoUrl::parse_with_base(
5030 Some(&url_record),
5031 &String::from_utf8_lossy(url_match.as_bytes()),
5032 ) {
5033 info!("Refresh to {}", url.debug_compact());
5034 url
5035 } else {
5036 return;
5038 };
5039 if url_record.scheme() == "javascript" {
5041 return;
5042 }
5043 }
5044 if self.completely_loaded() {
5046 self.window.as_global_scope().schedule_callback(
5047 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
5048 url: url_record,
5049 from_meta_element,
5050 }),
5051 Duration::from_secs(time),
5052 );
5053 self.set_declarative_refresh(DeclarativeRefresh::CreatedAfterLoad);
5054 } else {
5055 self.set_declarative_refresh(DeclarativeRefresh::PendingLoad {
5056 url: url_record,
5057 time,
5058 from_meta_element,
5059 });
5060 }
5061 }
5062
5063 pub(crate) fn will_declaratively_refresh(&self) -> bool {
5064 self.declarative_refresh.borrow().is_some()
5065 }
5066 pub(crate) fn set_declarative_refresh(&self, refresh: DeclarativeRefresh) {
5067 *self.declarative_refresh.borrow_mut() = Some(refresh);
5068 }
5069
5070 fn update_visibility_state(
5072 &self,
5073 cx: &mut JSContext,
5074 visibility_state: DocumentVisibilityState,
5075 ) {
5076 if self.visibility_state.get() == visibility_state {
5078 return;
5079 }
5080 self.visibility_state.set(visibility_state);
5082 let entry = VisibilityStateEntry::new(
5085 cx,
5086 &self.global(),
5087 visibility_state,
5088 CrossProcessInstant::now(),
5089 );
5090 self.window
5091 .Performance(cx)
5092 .queue_entry(entry.upcast::<PerformanceEntry>());
5093
5094 #[cfg(feature = "gamepad")]
5105 if visibility_state == DocumentVisibilityState::Hidden {
5106 self.window
5107 .Navigator(cx)
5108 .GetGamepads(cx)
5109 .unwrap_or_default()
5110 .iter_mut()
5111 .for_each(|gamepad| {
5112 if let Some(g) = gamepad {
5113 g.vibration_actuator().handle_visibility_change();
5114 }
5115 });
5116 }
5117
5118 self.upcast::<EventTarget>()
5120 .fire_bubbling_event(cx, atom!("visibilitychange"));
5121 }
5122
5123 pub(crate) fn is_initial_about_blank(&self) -> bool {
5125 self.is_initial_about_blank.get()
5126 }
5127
5128 pub(crate) fn allow_declarative_shadow_roots(&self) -> bool {
5130 self.allow_declarative_shadow_roots.get()
5131 }
5132
5133 pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
5134 self.has_trustworthy_ancestor_origin.get()
5135 }
5136
5137 pub(crate) fn has_trustworthy_ancestor_or_current_origin(&self) -> bool {
5138 self.has_trustworthy_ancestor_origin.get() ||
5139 self.origin().immutable().is_potentially_trustworthy()
5140 }
5141
5142 pub(crate) fn highlight_dom_node(&self, node: Option<&Node>) {
5143 self.highlighted_dom_node.set(node);
5144 self.add_restyle_reason(RestyleReason::HighlightedDOMNodeChanged);
5145 }
5146
5147 pub(crate) fn highlighted_dom_node(&self) -> Option<DomRoot<Node>> {
5148 self.highlighted_dom_node.get()
5149 }
5150
5151 pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
5152 self.custom_element_reaction_stack.clone()
5153 }
5154
5155 pub(crate) fn active_sandboxing_flag_set(&self) -> SandboxingFlagSet {
5156 self.active_sandboxing_flag_set.get()
5157 }
5158
5159 pub(crate) fn has_active_sandboxing_flag(&self, flag: SandboxingFlagSet) -> bool {
5160 self.active_sandboxing_flag_set.get().contains(flag)
5161 }
5162
5163 pub(crate) fn set_active_sandboxing_flag_set(&self, flags: SandboxingFlagSet) {
5164 self.active_sandboxing_flag_set.set(flags)
5165 }
5166
5167 pub(crate) fn creation_sandboxing_flag_set(&self) -> SandboxingFlagSet {
5168 self.creation_sandboxing_flag_set.get()
5169 }
5170
5171 pub(crate) fn creation_sandboxing_flag_set_considering_parent_iframe(
5172 &self,
5173 ) -> SandboxingFlagSet {
5174 self.window()
5175 .window_proxy()
5176 .frame_element()
5177 .and_then(|element| element.downcast::<HTMLIFrameElement>())
5178 .map(HTMLIFrameElement::sandboxing_flag_set)
5179 .unwrap_or_else(|| self.creation_sandboxing_flag_set())
5180 }
5181
5182 pub(crate) fn viewport_scrolling_box(&self, flags: ScrollContainerQueryFlags) -> ScrollingBox {
5183 self.window()
5184 .scrolling_box_query(None, flags)
5185 .expect("We should always have a ScrollingBox for the Viewport")
5186 }
5187
5188 pub(crate) fn notify_embedder_favicon(&self) {
5189 if let Some(ref image) = *self.favicon.borrow() {
5190 self.send_to_embedder(EmbedderMsg::NewFavicon(self.webview_id(), image.clone()));
5191 }
5192 }
5193
5194 pub(crate) fn set_favicon(&self, favicon: Image) {
5195 *self.favicon.borrow_mut() = Some(favicon);
5196 self.notify_embedder_favicon();
5197 }
5198
5199 pub(crate) fn fullscreen_element(&self) -> Option<DomRoot<Element>> {
5200 self.fullscreen_element.get()
5201 }
5202
5203 pub(crate) fn state_override(&self, command_name: &CommandName) -> Option<bool> {
5205 self.state_override.borrow().get(command_name).copied()
5206 }
5207
5208 pub(crate) fn set_state_override(&self, command_name: CommandName, state: Option<bool>) {
5210 if let Some(state) = state {
5211 self.state_override.borrow_mut().insert(command_name, state);
5212 } else {
5213 self.value_override.borrow_mut().remove(&command_name);
5214 }
5215 }
5216
5217 pub(crate) fn value_override(&self, command_name: &CommandName) -> Option<DOMString> {
5219 self.value_override.borrow().get(command_name).cloned()
5220 }
5221
5222 pub(crate) fn set_value_override(&self, command_name: CommandName, value: Option<DOMString>) {
5224 if let Some(value) = value {
5225 self.value_override.borrow_mut().insert(command_name, value);
5226 } else {
5227 self.value_override.borrow_mut().remove(&command_name);
5228 }
5229 }
5230
5231 pub(crate) fn clear_command_overrides(&self) {
5234 self.state_override.borrow_mut().clear();
5235 self.value_override.borrow_mut().clear();
5236 }
5237
5238 pub(crate) fn default_single_line_container_name(&self) -> DefaultSingleLineContainerName {
5240 self.default_single_line_container_name.get()
5241 }
5242
5243 pub(crate) fn set_default_single_line_container_name(
5245 &self,
5246 value: DefaultSingleLineContainerName,
5247 ) {
5248 self.default_single_line_container_name.set(value)
5249 }
5250
5251 pub(crate) fn css_styling_flag(&self) -> bool {
5253 self.css_styling_flag.get()
5254 }
5255
5256 pub(crate) fn set_css_styling_flag(&self, value: bool) {
5258 self.css_styling_flag.set(value)
5259 }
5260
5261 pub(crate) fn mute_iframe_load_flag(&self) -> bool {
5262 self.mute_iframe_load.get()
5263 }
5264
5265 pub(crate) fn set_iframe_load_in_progress(&self, value: bool) {
5266 self.iframe_load_in_progress.set(value)
5267 }
5268
5269 pub(crate) fn theme(&self) -> Option<Theme> {
5270 self.theme.get()
5271 }
5272
5273 pub(crate) fn set_theme(&self, new_theme: Option<Theme>) {
5274 self.theme.set(new_theme);
5275 self.window.refresh_theme();
5276 }
5277
5278 pub(crate) fn default_language(&self) -> Option<String> {
5279 self.default_language.borrow().clone()
5280 }
5281
5282 pub(crate) fn set_default_language(&self, new_language: Option<String>) {
5283 *self.default_language.borrow_mut() = new_language;
5284 }
5285}
5286
5287impl DocumentMethods<crate::DomTypeHolder> for Document {
5288 fn Constructor(
5290 cx: &mut JSContext,
5291 window: &Window,
5292 proto: Option<HandleObject>,
5293 ) -> Fallible<DomRoot<Document>> {
5294 let doc = window.Document();
5296 let docloader = DocumentLoader::new(&doc.loader());
5297 Ok(Document::new_with_proto(
5298 cx,
5299 window,
5300 proto,
5301 HasBrowsingContext::No,
5302 None,
5303 None,
5304 doc.origin().clone(),
5305 IsHTMLDocument::NonHTMLDocument,
5306 None,
5307 None,
5308 DocumentActivity::Inactive,
5309 docloader,
5310 None,
5311 None,
5312 Default::default(),
5313 false,
5314 doc.allow_declarative_shadow_roots(),
5315 Some(doc.insecure_requests_policy()),
5316 doc.has_trustworthy_ancestor_or_current_origin(),
5317 doc.custom_element_reaction_stack(),
5318 doc.active_sandboxing_flag_set.get(),
5319 doc.pipeline_id(),
5320 doc.image_cache(),
5321 ))
5322 }
5323
5324 fn ParseHTMLUnsafe(
5326 cx: &mut JSContext,
5327 window: &Window,
5328 s: TrustedHTMLOrString,
5329 options: &SetHTMLUnsafeOptions,
5330 ) -> Fallible<DomRoot<Self>> {
5331 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
5335 cx,
5336 window.as_global_scope(),
5337 s,
5338 "Document parseHTMLUnsafe",
5339 )?;
5340
5341 let url = window.get_url();
5342 let doc = window.Document();
5343 let loader = DocumentLoader::new(&doc.loader());
5344
5345 let content_type = "text/html"
5346 .parse()
5347 .expect("Supported type is not a MIME type");
5348 let document = Document::new(
5351 cx,
5352 window,
5353 HasBrowsingContext::No,
5354 Some(ServoUrl::parse("about:blank").unwrap()),
5355 None,
5356 doc.origin().clone(),
5357 IsHTMLDocument::HTMLDocument,
5358 Some(content_type),
5359 None,
5360 DocumentActivity::Inactive,
5361 loader,
5362 None,
5363 None,
5364 Default::default(),
5365 false,
5366 true,
5367 Some(doc.insecure_requests_policy()),
5368 doc.has_trustworthy_ancestor_or_current_origin(),
5369 doc.custom_element_reaction_stack(),
5370 doc.creation_sandboxing_flag_set(),
5371 doc.pipeline_id(),
5372 doc.image_cache(),
5373 );
5374 ServoParser::parse_html_document(cx, &document, Some(compliant_html), url, None, None);
5376
5377 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, false)?;
5380
5381 sanitizer.sanitize(cx, document.upcast(), false)?;
5383
5384 document.update_the_current_document_readiness(cx, DocumentReadyState::Complete);
5386 Ok(document)
5387 }
5388
5389 fn ParseHTML(
5391 cx: &mut JSContext,
5392 window: &Window,
5393 html: DOMString,
5394 options: &SetHTMLOptions,
5395 ) -> Fallible<DomRoot<Document>> {
5396 let url = window.get_url();
5399 let doc = window.Document();
5400 let loader = DocumentLoader::new(&doc.loader());
5401 let content_type = "text/html"
5402 .parse()
5403 .expect("Supported type is not a MIME type");
5404 let document = Document::new(
5405 cx,
5406 window,
5407 HasBrowsingContext::No,
5408 Some(ServoUrl::parse("about:blank").unwrap()),
5409 None,
5410 doc.origin().clone(),
5411 IsHTMLDocument::HTMLDocument,
5412 Some(content_type),
5413 None,
5414 DocumentActivity::Inactive,
5415 loader,
5416 None,
5417 None,
5418 Default::default(),
5419 false,
5420 true,
5421 Some(doc.insecure_requests_policy()),
5422 doc.has_trustworthy_ancestor_or_current_origin(),
5423 doc.custom_element_reaction_stack(),
5424 doc.creation_sandboxing_flag_set(),
5425 doc.pipeline_id(),
5426 doc.image_cache(),
5427 );
5428
5429 ServoParser::parse_html_document(cx, &document, Some(html), url, None, None);
5431
5432 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, true)?;
5435
5436 sanitizer.sanitize(cx, document.upcast(), true)?;
5438
5439 Ok(document)
5441 }
5442
5443 fn StyleSheets(&self, cx: &mut JSContext) -> DomRoot<StyleSheetList> {
5445 self.stylesheet_list.or_init(|| {
5446 StyleSheetList::new(
5447 cx,
5448 &self.window,
5449 StyleSheetListOwner::Document(Dom::from_ref(self)),
5450 )
5451 })
5452 }
5453
5454 fn Implementation(&self, cx: &mut JSContext) -> DomRoot<DOMImplementation> {
5456 self.implementation
5457 .or_init(|| DOMImplementation::new(cx, self))
5458 }
5459
5460 fn URL(&self) -> USVString {
5462 USVString(String::from(self.url().as_str()))
5463 }
5464
5465 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
5467 self.document_or_shadow_root.active_element(self.upcast())
5468 }
5469
5470 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
5472 self.custom_element_registry()
5473 }
5474
5475 fn HasFocus(&self) -> bool {
5477 if self.window().parent_info().is_none() {
5499 self.is_fully_active()
5501 } else {
5502 self.is_fully_active() && self.focus_handler.has_focus()
5504 }
5505 }
5506
5507 fn Domain(&self) -> DOMString {
5509 match self.origin().effective_domain() {
5511 None => DOMString::new(),
5513 Some(Host::Domain(domain)) => DOMString::from(domain),
5515 Some(host) => DOMString::from(host.to_string()),
5516 }
5517 }
5518
5519 fn SetDomain(&self, value: DOMString) -> ErrorResult {
5521 if !self.has_browsing_context {
5523 return Err(Error::Security(Some(
5524 "Document has no browsing context".into(),
5525 )));
5526 }
5527
5528 if self.has_active_sandboxing_flag(
5531 SandboxingFlagSet::SANDBOXED_DOCUMENT_DOMAIN_BROWSING_CONTEXT_FLAG,
5532 ) {
5533 return Err(Error::Security(Some(
5534 "Sandboxed document cannot set its domain".into(),
5535 )));
5536 }
5537
5538 let effective_domain = match self.origin().effective_domain() {
5540 Some(effective_domain) => effective_domain,
5541 None => return Err(Error::Security(Some("Document's origin is opaque".into()))),
5543 };
5544
5545 let host =
5547 match get_registrable_domain_suffix_of_or_is_equal_to(&value.str(), effective_domain) {
5548 None => return Err(Error::Security(Some("Provided domain is not a registrable domain suffix and is not equal to document's effective domain".into()))),
5549 Some(host) => host,
5550 };
5551
5552 self.origin().set_domain(host);
5557
5558 Ok(())
5559 }
5560
5561 fn Referrer(&self) -> DOMString {
5563 match self.referrer {
5564 Some(ref referrer) => DOMString::from(referrer.to_string()),
5565 None => DOMString::new(),
5566 }
5567 }
5568
5569 fn DocumentURI(&self) -> USVString {
5571 self.URL()
5572 }
5573
5574 fn CompatMode(&self) -> DOMString {
5576 DOMString::from(match self.quirks_mode.get() {
5577 QuirksMode::LimitedQuirks | QuirksMode::NoQuirks => "CSS1Compat",
5578 QuirksMode::Quirks => "BackCompat",
5579 })
5580 }
5581
5582 fn CharacterSet(&self) -> DOMString {
5584 DOMString::from_static(self.encoding.get().name())
5585 }
5586
5587 fn Charset(&self) -> DOMString {
5589 self.CharacterSet()
5590 }
5591
5592 fn InputEncoding(&self) -> DOMString {
5594 self.CharacterSet()
5595 }
5596
5597 fn ContentType(&self) -> DOMString {
5599 DOMString::from(self.content_type.to_string())
5600 }
5601
5602 fn GetDoctype(&self) -> Option<DomRoot<DocumentType>> {
5604 self.upcast::<Node>().children().find_map(DomRoot::downcast)
5605 }
5606
5607 fn GetDocumentElement(&self) -> Option<DomRoot<Element>> {
5609 self.upcast::<Node>().child_elements().next()
5610 }
5611
5612 fn GetElementsByTagName(
5614 &self,
5615 cx: &mut JSContext,
5616 qualified_name: DOMString,
5617 ) -> DomRoot<HTMLCollection> {
5618 let qualified_name = LocalName::from(qualified_name);
5619 if let Some(entry) = self.tag_map.borrow_mut().get(&qualified_name) {
5620 return DomRoot::from_ref(entry);
5621 }
5622 let result = HTMLCollection::by_qualified_name(
5623 cx,
5624 &self.window,
5625 self.upcast(),
5626 qualified_name.clone(),
5627 );
5628 self.tag_map
5629 .borrow_mut()
5630 .insert(qualified_name, Dom::from_ref(&*result));
5631 result
5632 }
5633
5634 fn GetElementsByTagNameNS(
5636 &self,
5637 cx: &mut JSContext,
5638 maybe_ns: Option<DOMString>,
5639 tag_name: DOMString,
5640 ) -> DomRoot<HTMLCollection> {
5641 let ns = namespace_from_domstring(maybe_ns);
5642 let local = LocalName::from(tag_name);
5643 let qname = QualName::new(None, ns, local);
5644 if let Some(collection) = self.tagns_map.borrow().get(&qname) {
5645 return DomRoot::from_ref(collection);
5646 }
5647 let result =
5648 HTMLCollection::by_qual_tag_name(cx, &self.window, self.upcast(), qname.clone());
5649 self.tagns_map
5650 .borrow_mut()
5651 .insert(qname, Dom::from_ref(&*result));
5652 result
5653 }
5654
5655 fn GetElementsByClassName(
5657 &self,
5658 cx: &mut JSContext,
5659 classes: DOMString,
5660 ) -> DomRoot<HTMLCollection> {
5661 let class_atoms: Vec<Atom> = split_html_space_chars(&classes.str())
5662 .map(Atom::from)
5663 .collect();
5664 if let Some(collection) = self.classes_map.borrow().get(&class_atoms) {
5665 return DomRoot::from_ref(collection);
5666 }
5667 let result = HTMLCollection::by_atomic_class_name(
5668 cx,
5669 &self.window,
5670 self.upcast(),
5671 class_atoms.clone(),
5672 );
5673 self.classes_map
5674 .borrow_mut()
5675 .insert(class_atoms, Dom::from_ref(&*result));
5676 result
5677 }
5678
5679 fn GetElementById(
5681 &self,
5682 cx: &js::context::JSContext,
5683 id: DOMString,
5684 ) -> Option<DomRoot<Element>> {
5685 self.get_element_by_id(cx, &Atom::from(id))
5686 }
5687
5688 fn CreateElement(
5690 &self,
5691 cx: &mut JSContext,
5692 mut local_name: DOMString,
5693 options: StringOrElementCreationOptions,
5694 ) -> Fallible<DomRoot<Element>> {
5695 if !is_valid_element_local_name(&local_name.str()) {
5697 return Err(Error::InvalidCharacter(Some(
5698 "Provided element local name is invalid".into(),
5699 )));
5700 }
5701
5702 if self.is_html_document {
5704 local_name.make_ascii_lowercase();
5705 }
5706
5707 let ns = if self.is_html_document || self.is_xhtml_document() {
5709 ns!(html)
5710 } else {
5711 ns!()
5712 };
5713 let name = QualName::new(None, ns, LocalName::from(local_name));
5714
5715 let is = match options {
5716 StringOrElementCreationOptions::String(_) => None,
5717 StringOrElementCreationOptions::ElementCreationOptions(options) => {
5718 options.is.as_ref().map(LocalName::from)
5719 },
5720 };
5721 Ok(Element::create(
5722 cx,
5723 name,
5724 is,
5725 self,
5726 ElementCreator::ScriptCreated,
5727 CustomElementCreationMode::Synchronous,
5728 None,
5729 ))
5730 }
5731
5732 fn CreateElementNS(
5734 &self,
5735 cx: &mut JSContext,
5736 namespace: Option<DOMString>,
5737 qualified_name: DOMString,
5738 options: StringOrElementCreationOptions,
5739 ) -> Fallible<DomRoot<Element>> {
5740 let context = domname::Context::Element;
5743 let (namespace, prefix, local_name) =
5744 domname::validate_and_extract(namespace, &qualified_name, context)?;
5745
5746 let name = QualName::new(prefix, namespace, local_name);
5749 let is = match options {
5750 StringOrElementCreationOptions::String(_) => None,
5751 StringOrElementCreationOptions::ElementCreationOptions(options) => {
5752 options.is.as_ref().map(LocalName::from)
5753 },
5754 };
5755
5756 Ok(Element::create(
5758 cx,
5759 name,
5760 is,
5761 self,
5762 ElementCreator::ScriptCreated,
5763 CustomElementCreationMode::Synchronous,
5764 None,
5765 ))
5766 }
5767
5768 fn CreateAttribute(
5770 &self,
5771 cx: &mut JSContext,
5772 mut local_name: DOMString,
5773 ) -> Fallible<DomRoot<Attr>> {
5774 if !is_valid_attribute_local_name(&local_name.str()) {
5776 return Err(Error::InvalidCharacter(Some(
5777 "Provided local name is invalid".into(),
5778 )));
5779 }
5780
5781 if self.is_html_document {
5783 local_name.make_ascii_lowercase();
5784 }
5785 let name = LocalName::from(local_name);
5786 let value = AttrValue::String(String::new());
5787
5788 Ok(Attr::new(
5789 cx,
5790 self,
5791 name.clone(),
5792 value,
5793 name,
5794 ns!(),
5795 None,
5796 None,
5797 ))
5798 }
5799
5800 fn CreateAttributeNS(
5802 &self,
5803 cx: &mut JSContext,
5804 namespace: Option<DOMString>,
5805 qualified_name: DOMString,
5806 ) -> Fallible<DomRoot<Attr>> {
5807 let context = domname::Context::Attribute;
5810 let (namespace, prefix, local_name) =
5811 domname::validate_and_extract(namespace, &qualified_name, context)?;
5812 let value = AttrValue::String(String::new());
5813 let qualified_name = LocalName::from(qualified_name);
5814 Ok(Attr::new(
5815 cx,
5816 self,
5817 local_name,
5818 value,
5819 qualified_name,
5820 namespace,
5821 prefix,
5822 None,
5823 ))
5824 }
5825
5826 fn CreateDocumentFragment(&self, cx: &mut JSContext) -> DomRoot<DocumentFragment> {
5828 DocumentFragment::new(cx, self)
5829 }
5830
5831 fn CreateTextNode(&self, cx: &mut JSContext, data: DOMString) -> DomRoot<Text> {
5833 Text::new(cx, data, self)
5834 }
5835
5836 fn CreateCDATASection(
5838 &self,
5839 cx: &mut JSContext,
5840 data: DOMString,
5841 ) -> Fallible<DomRoot<CDATASection>> {
5842 if self.is_html_document {
5844 return Err(Error::NotSupported(Some(
5845 "Document must be an XML document".into(),
5846 )));
5847 }
5848
5849 if data.contains("]]>") {
5851 return Err(Error::InvalidCharacter(Some(
5852 "CDATA section cannot include `]]>`".into(),
5853 )));
5854 }
5855
5856 Ok(CDATASection::new(cx, data, self))
5858 }
5859
5860 fn CreateComment(&self, cx: &mut JSContext, data: DOMString) -> DomRoot<Comment> {
5862 Comment::new(cx, data, self, None)
5863 }
5864
5865 fn CreateProcessingInstruction(
5867 &self,
5868 cx: &mut JSContext,
5869 target: DOMString,
5870 data: DOMString,
5871 ) -> Fallible<DomRoot<ProcessingInstruction>> {
5872 if !matches_name_production(&target.str()) {
5874 return Err(Error::InvalidCharacter(Some(
5875 "Target name provided is invalid".into(),
5876 )));
5877 }
5878
5879 if data.contains("?>") {
5881 return Err(Error::InvalidCharacter(Some(
5882 "Processing instruction's data cannot contain `>?`".into(),
5883 )));
5884 }
5885
5886 Ok(ProcessingInstruction::new(cx, target, data, self))
5888 }
5889
5890 fn ImportNode(
5892 &self,
5893 cx: &mut JSContext,
5894 node: &Node,
5895 options: BooleanOrImportNodeOptions,
5896 ) -> Fallible<DomRoot<Node>> {
5897 if node.is::<Document>() || node.is::<ShadowRoot>() {
5899 return Err(Error::NotSupported(Some(
5900 "Node cannot be a document or shadow root".into(),
5901 )));
5902 }
5903 let (subtree, registry) = match options {
5905 BooleanOrImportNodeOptions::Boolean(boolean) => (boolean.into(), None),
5908 BooleanOrImportNodeOptions::ImportNodeOptions(options) => {
5910 let subtree = (!options.selfOnly).into();
5912 let registry = if let Some(registry) = options.customElementRegistry {
5914 let this_registry = self
5917 .custom_element_registry()
5918 .expect("Document must have a custom element registry");
5919 if !registry.is_scoped() && registry != this_registry {
5920 return Err(Error::NotSupported(Some(
5921 "Imported customElementRegistry is not scoped and does not match existing registry.".into()
5922 )));
5923 }
5924 Some(registry)
5925 } else {
5926 None
5927 };
5928 (subtree, registry)
5929 },
5930 };
5931 let registry = registry
5934 .or_else(|| CustomElementRegistry::lookup_a_custom_element_registry(self.upcast()));
5935
5936 Ok(Node::clone(cx, node, Some(self), subtree, registry))
5939 }
5940
5941 fn AdoptNode(&self, cx: &mut JSContext, node: &Node) -> Fallible<DomRoot<Node>> {
5943 if node.is::<Document>() {
5945 return Err(Error::NotSupported(Some(
5946 "Node cannot be a document".into(),
5947 )));
5948 }
5949
5950 if node.is::<ShadowRoot>() {
5952 return Err(Error::HierarchyRequest(Some(
5953 "Node cannot be a shadow root".into(),
5954 )));
5955 }
5956
5957 Node::adopt(cx, node, self);
5959
5960 Ok(DomRoot::from_ref(node))
5962 }
5963
5964 fn CreateEvent(
5966 &self,
5967 cx: &mut JSContext,
5968 mut interface: DOMString,
5969 ) -> Fallible<DomRoot<Event>> {
5970 interface.make_ascii_lowercase();
5971 match &*interface.str() {
5972 "beforeunloadevent" => Ok(DomRoot::upcast(BeforeUnloadEvent::new_uninitialized(
5973 cx,
5974 &self.window,
5975 ))),
5976 "compositionevent" => Ok(DomRoot::upcast(CompositionEvent::new_uninitialized(
5977 cx,
5978 &self.window,
5979 ))),
5980 "customevent" => Ok(DomRoot::upcast(CustomEvent::new_uninitialized(
5981 cx,
5982 self.window.upcast(),
5983 ))),
5984 "events" | "event" | "htmlevents" | "svgevents" => {
5987 Ok(Event::new_uninitialized(cx, self.window.upcast()))
5988 },
5989 "focusevent" => Ok(DomRoot::upcast(FocusEvent::new_uninitialized(
5990 cx,
5991 &self.window,
5992 ))),
5993 "hashchangeevent" => Ok(DomRoot::upcast(HashChangeEvent::new_uninitialized(
5994 cx,
5995 &self.window,
5996 ))),
5997 "keyboardevent" => Ok(DomRoot::upcast(KeyboardEvent::new_uninitialized(
5998 cx,
5999 &self.window,
6000 ))),
6001 "messageevent" => Ok(DomRoot::upcast(MessageEvent::new_uninitialized(
6002 cx,
6003 self.window.upcast(),
6004 ))),
6005 "mouseevent" | "mouseevents" => Ok(DomRoot::upcast(MouseEvent::new_uninitialized(
6006 cx,
6007 &self.window,
6008 ))),
6009 "storageevent" => Ok(DomRoot::upcast(StorageEvent::new_uninitialized(
6010 cx,
6011 &self.window,
6012 DOMString::new(),
6013 ))),
6014 "textevent" => Ok(DomRoot::upcast(TextEvent::new_uninitialized(
6015 cx,
6016 &self.window,
6017 ))),
6018 "touchevent" => {
6019 let touches = TouchList::new(cx, &self.window, &[]);
6020 let changed_touches = TouchList::new(cx, &self.window, &[]);
6021 let target_touches = TouchList::new(cx, &self.window, &[]);
6022
6023 Ok(DomRoot::upcast(DomTouchEvent::new_uninitialized(
6024 cx,
6025 &self.window,
6026 &touches,
6027 &changed_touches,
6028 &target_touches,
6029 )))
6030 },
6031 "uievent" | "uievents" => Ok(DomRoot::upcast(UIEvent::new_uninitialized(
6032 cx,
6033 &self.window,
6034 ))),
6035 _ => Err(Error::NotSupported(Some(
6036 "Interface is not supported".into(),
6037 ))),
6038 }
6039 }
6040
6041 fn LastModified(&self) -> DOMString {
6043 DOMString::from(self.last_modified.as_ref().cloned().unwrap_or_else(|| {
6044 Local::now().format("%m/%d/%Y %H:%M:%S").to_string()
6050 }))
6051 }
6052
6053 fn CreateRange(&self, cx: &mut JSContext) -> DomRoot<Range> {
6055 Range::new_with_doc(cx, self, None)
6056 }
6057
6058 fn CreateNodeIterator(
6060 &self,
6061 cx: &mut js::context::JSContext,
6062 root: &Node,
6063 what_to_show: u32,
6064 filter: Option<Rc<NodeFilter>>,
6065 ) -> DomRoot<NodeIterator> {
6066 NodeIterator::new(cx, self, root, what_to_show, filter)
6067 }
6068
6069 fn CreateTreeWalker(
6071 &self,
6072 cx: &mut JSContext,
6073 root: &Node,
6074 what_to_show: u32,
6075 filter: Option<Rc<NodeFilter>>,
6076 ) -> DomRoot<TreeWalker> {
6077 TreeWalker::new(cx, self, root, what_to_show, filter)
6078 }
6079
6080 fn Title(&self) -> DOMString {
6082 self.title().unwrap_or_default()
6083 }
6084
6085 fn SetTitle(&self, cx: &mut JSContext, title: DOMString) {
6087 let root = match self.GetDocumentElement() {
6088 Some(root) => root,
6089 None => return,
6090 };
6091
6092 let node = if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
6095 let elem = root
6098 .upcast::<Node>()
6099 .child_elements_unrooted(cx.no_gc())
6100 .find(|node| {
6101 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
6102 });
6103 match elem {
6104 Some(elem) => UnrootedDom::upcast::<Node>(elem).as_rooted(),
6105 None => {
6107 let name = QualName::new(None, ns!(svg), local_name!("title"));
6110 let elem = Element::create(
6111 cx,
6112 name,
6113 None,
6114 self,
6115 ElementCreator::ScriptCreated,
6116 CustomElementCreationMode::Synchronous,
6117 None,
6118 );
6119
6120 let parent = root.upcast::<Node>();
6122 let child = elem.upcast::<Node>();
6123 parent
6124 .InsertBefore(cx, child, parent.GetFirstChild().as_deref())
6125 .unwrap()
6126 },
6127 }
6128 }
6129 else if root.namespace() == &ns!(html) {
6131 let elem = root
6132 .upcast::<Node>()
6133 .traverse_preorder_non_rooting(cx.no_gc(), ShadowIncluding::No)
6134 .find(|node| node.is::<HTMLTitleElement>());
6135 match elem {
6136 Some(elem) => elem.as_rooted(),
6138 None => match self.GetHead() {
6140 Some(head) => {
6141 let name = QualName::new(None, ns!(html), local_name!("title"));
6144 let elem = Element::create(
6145 cx,
6146 name,
6147 None,
6148 self,
6149 ElementCreator::ScriptCreated,
6150 CustomElementCreationMode::Synchronous,
6151 None,
6152 );
6153
6154 head.upcast::<Node>()
6156 .AppendChild(cx, elem.upcast())
6157 .unwrap()
6158 },
6159 None => return,
6161 },
6162 }
6163 }
6164 else {
6166 return;
6168 };
6169
6170 node.set_text_content_for_element(cx, Some(title));
6175 }
6176
6177 fn GetHead(&self) -> Option<DomRoot<HTMLHeadElement>> {
6179 self.get_html_element()
6180 .and_then(|root| root.upcast::<Node>().children().find_map(DomRoot::downcast))
6181 }
6182
6183 fn GetCurrentScript(&self) -> Option<DomRoot<HTMLScriptElement>> {
6185 self.current_script.get()
6186 }
6187
6188 fn GetBody(&self) -> Option<DomRoot<HTMLElement>> {
6190 self.get_html_element().and_then(|root| {
6193 let node = root.upcast::<Node>();
6194 node.children()
6195 .find(|child| {
6196 matches!(
6197 child.type_id(),
6198 NodeTypeId::Element(ElementTypeId::HTMLElement(
6199 HTMLElementTypeId::HTMLBodyElement,
6200 )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
6201 HTMLElementTypeId::HTMLFrameSetElement,
6202 ))
6203 )
6204 })
6205 .map(|node| DomRoot::downcast(node).unwrap())
6206 })
6207 }
6208
6209 fn SetBody(&self, cx: &mut JSContext, new_body: Option<&HTMLElement>) -> ErrorResult {
6211 let new_body = match new_body {
6213 Some(new_body) => new_body,
6214 None => {
6215 return Err(Error::HierarchyRequest(Some(
6216 "HTML element provided is neither a body nor a frameset element".into(),
6217 )));
6218 },
6219 };
6220
6221 let node = new_body.upcast::<Node>();
6222 match node.type_id() {
6223 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) |
6224 NodeTypeId::Element(ElementTypeId::HTMLElement(
6225 HTMLElementTypeId::HTMLFrameSetElement,
6226 )) => {},
6227 _ => {
6228 return Err(Error::HierarchyRequest(Some(
6229 "HTML element provided is neither a body nor a frameset element".into(),
6230 )));
6231 },
6232 }
6233
6234 let old_body = self.GetBody();
6236 if old_body.as_deref() == Some(new_body) {
6237 return Ok(());
6238 }
6239
6240 match (self.GetDocumentElement(), &old_body) {
6241 (Some(ref root), Some(child)) => {
6244 let root = root.upcast::<Node>();
6245 root.ReplaceChild(cx, new_body.upcast(), child.upcast())
6246 .map(|_| ())
6247 },
6248
6249 (None, _) => Err(Error::HierarchyRequest(Some(
6251 "Document element is missing".into(),
6252 ))),
6253
6254 (Some(ref root), &None) => {
6257 let root = root.upcast::<Node>();
6258 root.AppendChild(cx, new_body.upcast()).map(|_| ())
6259 },
6260 }
6261 }
6262
6263 fn GetElementsByName(&self, cx: &mut JSContext, name: DOMString) -> DomRoot<NodeList> {
6265 NodeList::new_elements_by_name_list(cx, self.window(), self, name)
6266 }
6267
6268 fn Images(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6270 self.images.or_init(|| {
6271 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6272 element.is::<HTMLImageElement>()
6273 })
6274 })
6275 }
6276
6277 fn Embeds(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6279 self.embeds.or_init(|| {
6280 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6281 element.is::<HTMLEmbedElement>()
6282 })
6283 })
6284 }
6285
6286 fn Plugins(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6288 self.Embeds(cx)
6289 }
6290
6291 fn Links(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6293 self.links.or_init(|| {
6294 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6295 (element.is::<HTMLAnchorElement>() || element.is::<HTMLAreaElement>()) &&
6296 element.has_attribute(&local_name!("href"))
6297 })
6298 })
6299 }
6300
6301 fn Forms(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6303 self.forms.or_init(|| {
6304 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6305 element.is::<HTMLFormElement>()
6306 })
6307 })
6308 }
6309
6310 fn Scripts(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6312 self.scripts.or_init(|| {
6313 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6314 element.is::<HTMLScriptElement>()
6315 })
6316 })
6317 }
6318
6319 fn Anchors(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6321 self.anchors.or_init(|| {
6322 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
6323 element.is::<HTMLAnchorElement>() && element.has_attribute(&local_name!("href"))
6324 })
6325 })
6326 }
6327
6328 fn Applets(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6330 self.applets
6331 .or_init(|| HTMLCollection::always_empty(cx, &self.window, self.upcast()))
6332 }
6333
6334 fn GetLocation(&self, cx: &mut JSContext) -> Option<DomRoot<Location>> {
6336 if self.is_fully_active() {
6337 Some(self.window.Location(cx))
6338 } else {
6339 None
6340 }
6341 }
6342
6343 fn Children(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
6345 HTMLCollection::children(cx, &self.window, self.upcast())
6346 }
6347
6348 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
6350 self.upcast::<Node>().child_elements().next()
6351 }
6352
6353 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
6355 self.upcast::<Node>()
6356 .rev_children()
6357 .find_map(DomRoot::downcast)
6358 }
6359
6360 fn ChildElementCount(&self) -> u32 {
6362 self.upcast::<Node>().child_elements().count() as u32
6363 }
6364
6365 fn Prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6367 self.upcast::<Node>().prepend(cx, nodes)
6368 }
6369
6370 fn Append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6372 self.upcast::<Node>().append(cx, nodes)
6373 }
6374
6375 fn ReplaceChildren(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
6377 self.upcast::<Node>().replace_children(cx, nodes)
6378 }
6379
6380 fn MoveBefore(&self, cx: &mut JSContext, node: &Node, child: Option<&Node>) -> ErrorResult {
6382 self.upcast::<Node>().move_before(cx, node, child)
6383 }
6384
6385 fn QuerySelector(
6387 &self,
6388 cx: &mut JSContext,
6389 selectors: DOMString,
6390 ) -> Fallible<Option<DomRoot<Element>>> {
6391 self.upcast::<Node>().query_selector(cx.no_gc(), selectors)
6392 }
6393
6394 fn QuerySelectorAll(
6396 &self,
6397 cx: &mut JSContext,
6398 selectors: DOMString,
6399 ) -> Fallible<DomRoot<NodeList>> {
6400 self.upcast::<Node>().query_selector_all(cx, selectors)
6401 }
6402
6403 fn ReadyState(&self) -> DocumentReadyState {
6405 self.ready_state.get()
6406 }
6407
6408 fn GetDefaultView(&self) -> Option<DomRoot<Window>> {
6410 if self.has_browsing_context {
6411 Some(DomRoot::from_ref(&*self.window))
6412 } else {
6413 None
6414 }
6415 }
6416
6417 fn GetCookie(&self) -> Fallible<DOMString> {
6419 if self.is_cookie_averse() {
6420 return Ok(DOMString::new());
6421 }
6422
6423 if !self.origin().is_tuple() {
6424 return Err(Error::Security(Some("Document's origin is opaque".into())));
6425 }
6426
6427 let url = self.url();
6428 let (tx, rx) =
6429 profile_generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
6430 let _ = self
6431 .window
6432 .as_global_scope()
6433 .resource_threads()
6434 .send(GetCookieStringForUrl(url, tx, NonHTTP));
6435 let cookies = rx.recv().unwrap();
6436 Ok(cookies.map_or(DOMString::new(), DOMString::from))
6437 }
6438
6439 fn SetCookie(&self, cookie: DOMString) -> ErrorResult {
6441 if self.is_cookie_averse() {
6442 return Ok(());
6443 }
6444
6445 if !self.origin().is_tuple() {
6446 return Err(Error::Security(Some("Document's origin is opaque".into())));
6447 }
6448
6449 if !cookie.is_valid_for_cookie() {
6450 return Ok(());
6451 }
6452
6453 let cookies = if let Some(cookie) = Cookie::parse(cookie.to_string()).ok().map(Serde) {
6454 vec![cookie]
6455 } else {
6456 vec![]
6457 };
6458
6459 let _ = self
6460 .window
6461 .as_global_scope()
6462 .resource_threads()
6463 .send(SetCookiesForUrl(self.url(), cookies, NonHTTP));
6464 Ok(())
6465 }
6466
6467 fn BgColor(&self) -> DOMString {
6469 self.get_body_attribute(&local_name!("bgcolor"))
6470 }
6471
6472 fn SetBgColor(&self, cx: &mut JSContext, value: DOMString) {
6474 self.set_body_attribute(cx, &local_name!("bgcolor"), value)
6475 }
6476
6477 fn FgColor(&self) -> DOMString {
6479 self.get_body_attribute(&local_name!("text"))
6480 }
6481
6482 fn SetFgColor(&self, cx: &mut JSContext, value: DOMString) {
6484 self.set_body_attribute(cx, &local_name!("text"), value)
6485 }
6486
6487 fn NamedGetter(&self, cx: &mut JSContext, name: DOMString) -> Option<NamedPropertyValue> {
6489 if name.is_empty() {
6490 return None;
6491 }
6492 let name = Atom::from(name);
6493
6494 let elements_with_name = self.get_elements_with_name(cx, &name);
6497 let name_iter = elements_with_name
6498 .iter()
6499 .filter(|elem| is_named_element_with_name_attribute(elem));
6500 let elements_with_id = self.id_map.get_all(cx.no_gc(), self.upcast(), &name);
6501 let id_iter = elements_with_id
6502 .iter()
6503 .filter(|elem| is_named_element_with_id_attribute(elem));
6504 let mut elements = name_iter.chain(id_iter);
6505
6506 let first = elements.next()?;
6513 if elements.all(|other| first == other) {
6514 if let Some(nested_window_proxy) = first
6515 .downcast::<HTMLIFrameElement>()
6516 .and_then(|iframe| iframe.GetContentWindow())
6517 {
6518 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
6519 }
6520
6521 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
6523 }
6524
6525 #[derive(JSTraceable, MallocSizeOf)]
6528 struct DocumentNamedGetter {
6529 #[no_trace]
6530 name: Atom,
6531 }
6532 impl CollectionFilter for DocumentNamedGetter {
6533 fn filter(&self, elem: &Element, _root: &Node) -> bool {
6534 let type_ = match elem.upcast::<Node>().type_id() {
6535 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
6536 _ => return false,
6537 };
6538 match type_ {
6539 HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
6540 elem.get_name().as_ref() == Some(&self.name)
6541 },
6542 HTMLElementTypeId::HTMLImageElement => elem.get_name().is_some_and(|name| {
6543 name == *self.name ||
6544 !name.is_empty() && elem.get_id().as_ref() == Some(&self.name)
6545 }),
6546 _ => false,
6550 }
6551 }
6552 }
6553 let collection = HTMLCollection::create(
6554 cx,
6555 self.window(),
6556 self.upcast(),
6557 Box::new(DocumentNamedGetter { name }),
6558 );
6559 Some(NamedPropertyValue::HTMLCollection(collection))
6560 }
6561
6562 fn SupportedPropertyNames(&self, no_gc: &NoGC) -> Vec<DOMString> {
6564 let mut names_with_first_named_element_map = HashMap::new();
6565 self.name_map
6566 .for_each(no_gc, self.upcast(), |name, elements| {
6567 if name.is_empty() {
6568 return;
6569 }
6570 let mut name_iter = elements
6571 .iter()
6572 .filter(|elem| is_named_element_with_name_attribute(elem));
6573 if let Some(first) = name_iter.next() {
6574 names_with_first_named_element_map.insert(name.clone(), first.as_rooted());
6575 }
6576 });
6577
6578 self.id_map.for_each(no_gc, self.upcast(), |id, elements| {
6579 if id.is_empty() {
6580 return;
6581 }
6582 let mut id_iter = elements
6583 .iter()
6584 .filter(|elem| is_named_element_with_id_attribute(elem));
6585 if let Some(first) = id_iter.next() {
6586 match names_with_first_named_element_map.entry(id.clone()) {
6587 Vacant(entry) => drop(entry.insert(first.as_rooted())),
6588 Occupied(mut entry) => {
6589 if first
6590 .upcast::<Node>()
6591 .is_before(no_gc, entry.get().upcast())
6592 {
6593 *entry.get_mut() = first.as_rooted();
6594 }
6595 },
6596 }
6597 }
6598 });
6599
6600 let mut names_with_first_named_element_vec: Vec<_> =
6601 names_with_first_named_element_map.into_iter().collect();
6602 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
6603 if a.1 == b.1 {
6604 a.0.cmp(&b.0)
6607 } else if a.1.upcast::<Node>().is_before(no_gc, b.1.upcast::<Node>()) {
6608 Ordering::Less
6609 } else {
6610 Ordering::Greater
6611 }
6612 });
6613
6614 names_with_first_named_element_vec
6615 .into_iter()
6616 .map(|(k, _)| DOMString::from(&*k))
6617 .collect()
6618 }
6619
6620 fn Clear(&self) {
6622 }
6624
6625 fn CaptureEvents(&self) {
6627 }
6629
6630 fn ReleaseEvents(&self) {
6632 }
6634
6635 global_event_handlers!();
6637
6638 event_handler!(
6640 readystatechange,
6641 GetOnreadystatechange,
6642 SetOnreadystatechange
6643 );
6644
6645 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
6647 self.document_or_shadow_root.element_from_point(
6648 self.upcast(),
6649 x,
6650 y,
6651 self.GetDocumentElement(),
6652 self.has_browsing_context,
6653 )
6654 }
6655
6656 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
6658 self.document_or_shadow_root.elements_from_point(
6659 self.upcast(),
6660 x,
6661 y,
6662 self.GetDocumentElement(),
6663 self.has_browsing_context,
6664 )
6665 }
6666
6667 fn GetScrollingElement(&self) -> Option<DomRoot<Element>> {
6669 if self.quirks_mode() == QuirksMode::Quirks {
6671 if let Some(ref body) = self.GetBody() {
6673 let e = body.upcast::<Element>();
6674 if !e.is_potentially_scrollable_body_for_scrolling_element() {
6678 return Some(DomRoot::from_ref(e));
6679 }
6680 }
6681
6682 return None;
6684 }
6685
6686 self.GetDocumentElement()
6689 }
6690
6691 fn Open(
6693 &self,
6694 cx: &mut JSContext,
6695 _unused1: Option<DOMString>,
6696 _unused2: Option<DOMString>,
6697 ) -> Fallible<DomRoot<Document>> {
6698 if !self.is_html_document() {
6700 return Err(Error::InvalidState(Some(
6701 "Document must be a HTML document".into(),
6702 )));
6703 }
6704
6705 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6708 return Err(Error::InvalidState(Some(
6709 "A custom element constructor attempted to open, close or write to this document"
6710 .into(),
6711 )));
6712 }
6713
6714 let entry_responsible_document = GlobalScope::entry().as_window().Document();
6716
6717 if !self
6720 .origin()
6721 .same_origin(&entry_responsible_document.origin())
6722 {
6723 return Err(Error::Security(Some(
6724 "Document's origin is not the same as entry global's document origin".into(),
6725 )));
6726 }
6727
6728 if self
6731 .active_parser()
6732 .is_some_and(|parser| parser.script_nesting_level() > 0)
6733 {
6734 return Ok(DomRoot::from_ref(self));
6735 }
6736
6737 if self.is_prompting_or_unloading() {
6739 return Ok(DomRoot::from_ref(self));
6740 }
6741
6742 if self.active_parser_was_aborted.get() {
6744 return Ok(DomRoot::from_ref(self));
6745 }
6746
6747 self.window().set_navigation_start();
6751
6752 if self.has_browsing_context() {
6756 self.abort(cx);
6759 }
6760
6761 for node in self
6764 .upcast::<Node>()
6765 .traverse_preorder_non_rooting(cx.no_gc(), ShadowIncluding::Yes)
6766 {
6767 node.upcast::<EventTarget>().remove_all_listeners();
6768 }
6769
6770 if self.window.Document() == DomRoot::from_ref(self) {
6773 self.window.upcast::<EventTarget>().remove_all_listeners();
6774 }
6775
6776 Node::replace_all(cx, None, self.upcast::<Node>());
6778
6779 if self.is_fully_active() {
6786 let mut new_url = entry_responsible_document.url();
6788
6789 if entry_responsible_document != DomRoot::from_ref(self) {
6791 new_url.set_fragment(None);
6792 }
6793
6794 self.set_url(new_url);
6797 }
6798
6799 self.is_initial_about_blank.set(false);
6801
6802 if self.iframe_load_in_progress.get() {
6805 self.mute_iframe_load.set(true);
6806 }
6807
6808 self.set_quirks_mode(QuirksMode::NoQuirks);
6810
6811 let resource_threads = self.window.as_global_scope().resource_threads().clone();
6817 *self.loader.borrow_mut() =
6818 DocumentLoader::new_with_threads(resource_threads, Some(self.url()));
6819 ServoParser::parse_html_script_input(cx, self, self.url());
6820
6821 self.update_the_current_document_readiness(cx, DocumentReadyState::Loading);
6827
6828 Ok(DomRoot::from_ref(self))
6830 }
6831
6832 fn Open_(
6834 &self,
6835 cx: &mut JSContext,
6836 url: USVString,
6837 target: DOMString,
6838 features: DOMString,
6839 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
6840 self.browsing_context()
6841 .ok_or(Error::InvalidAccess(Some(
6842 "Document is not fully active".into(),
6843 )))?
6844 .open(cx, url, target, features)
6845 }
6846
6847 fn Write(&self, cx: &mut JSContext, text: Vec<TrustedHTMLOrString>) -> ErrorResult {
6849 self.write(cx, text, false, "Document", "write")
6852 }
6853
6854 fn Writeln(&self, cx: &mut JSContext, text: Vec<TrustedHTMLOrString>) -> ErrorResult {
6856 self.write(cx, text, true, "Document", "writeln")
6859 }
6860
6861 fn Close(&self, cx: &mut JSContext) -> ErrorResult {
6863 if !self.is_html_document() {
6864 return Err(Error::InvalidState(Some(
6866 "Document must be a HTML document".into(),
6867 )));
6868 }
6869
6870 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6873 return Err(Error::InvalidState(Some(
6874 "A custom element constructor attempted to open, close or write to this document"
6875 .into(),
6876 )));
6877 }
6878
6879 let parser = match self.get_current_parser() {
6881 Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
6882 _ => {
6883 return Ok(());
6884 },
6885 };
6886
6887 parser.close(cx);
6889
6890 Ok(())
6891 }
6892
6893 fn ExecCommand(
6895 &self,
6896 cx: &mut JSContext,
6897 command_id: DOMString,
6898 _show_ui: bool,
6899 value: TrustedHTMLOrString,
6900 ) -> Fallible<bool> {
6901 let value = if command_id == "insertHTML" {
6902 TrustedHTML::get_trusted_type_compliant_string(
6903 cx,
6904 self.window.as_global_scope(),
6905 value,
6906 "Document execCommand",
6907 )?
6908 } else {
6909 match value {
6910 TrustedHTMLOrString::TrustedHTML(trusted_html) => trusted_html.data().clone(),
6911 TrustedHTMLOrString::String(value) => value,
6912 }
6913 };
6914
6915 Ok(self.exec_command_for_command_id(cx, command_id, value))
6916 }
6917
6918 fn QueryCommandEnabled(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
6920 self.check_support_and_enabled(cx, &command_id).is_some()
6922 }
6923
6924 fn QueryCommandSupported(&self, command_id: DOMString) -> bool {
6926 self.is_command_supported(command_id)
6930 }
6931
6932 fn QueryCommandIndeterm(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
6934 self.is_command_indeterminate(cx, command_id)
6935 }
6936
6937 fn QueryCommandState(&self, cx: &mut JSContext, command_id: DOMString) -> bool {
6939 self.command_state_for_command(cx, command_id)
6940 }
6941
6942 fn QueryCommandValue(&self, cx: &mut JSContext, command_id: DOMString) -> DOMString {
6944 self.command_value_for_command(cx, command_id)
6945 }
6946
6947 event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror);
6949
6950 event_handler!(
6952 fullscreenchange,
6953 GetOnfullscreenchange,
6954 SetOnfullscreenchange
6955 );
6956
6957 fn FullscreenEnabled(&self) -> bool {
6959 self.get_allow_fullscreen()
6960 }
6961
6962 fn Fullscreen(&self) -> bool {
6964 self.fullscreen_element.get().is_some()
6965 }
6966
6967 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
6969 DocumentOrShadowRoot::get_fullscreen_element(&self.node, self.fullscreen_element.get())
6970 }
6971
6972 fn ExitFullscreen(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
6974 self.exit_fullscreen(cx)
6975 }
6976
6977 fn ServoGetMediaControls(&self, id: DOMString) -> Fallible<DomRoot<ShadowRoot>> {
6981 match self.media_controls.borrow().get(&*id.str()) {
6982 Some(m) => Ok(DomRoot::from_ref(m)),
6983 None => Err(Error::InvalidAccess(Some(
6984 "No registered media controls exist with provided id".into(),
6985 ))),
6986 }
6987 }
6988
6989 fn GetSelection(&self, cx: &mut JSContext) -> Option<DomRoot<Selection>> {
6991 if self.has_browsing_context {
6992 Some(self.selection.or_init(|| Selection::new(cx, self)))
6993 } else {
6994 None
6995 }
6996 }
6997
6998 fn Fonts(&self, cx: &mut JSContext) -> DomRoot<FontFaceSet> {
7000 self.fonts
7001 .or_init(|| FontFaceSet::new(cx, &self.global(), None))
7002 }
7003
7004 fn Hidden(&self) -> bool {
7006 self.visibility_state.get() == DocumentVisibilityState::Hidden
7007 }
7008
7009 fn VisibilityState(&self) -> DocumentVisibilityState {
7011 self.visibility_state.get()
7012 }
7013
7014 fn CreateExpression(
7015 &self,
7016 cx: &mut JSContext,
7017 expression: DOMString,
7018 resolver: Option<RootedCallback<XPathNSResolver>>,
7019 ) -> Fallible<DomRoot<crate::dom::types::XPathExpression>> {
7020 let parsed_expression =
7021 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
7022 Ok(XPathExpression::new(
7023 cx,
7024 &self.window,
7025 None,
7026 parsed_expression,
7027 ))
7028 }
7029
7030 fn CreateNSResolver(&self, cx: &mut JSContext, node_resolver: &Node) -> DomRoot<Node> {
7031 let global = self.global();
7032 let window = global.as_window();
7033 let evaluator = XPathEvaluator::new(cx, window, None);
7034 XPathEvaluatorMethods::<crate::DomTypeHolder>::CreateNSResolver(&*evaluator, node_resolver)
7035 }
7036
7037 fn Evaluate(
7038 &self,
7039 cx: &mut JSContext,
7040 expression: DOMString,
7041 context_node: &Node,
7042 resolver: Option<RootedCallback<XPathNSResolver>>,
7043 result_type: u16,
7044 result: Option<&crate::dom::types::XPathResult>,
7045 ) -> Fallible<DomRoot<crate::dom::types::XPathResult>> {
7046 let parsed_expression =
7047 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
7048 XPathExpression::new(cx, &self.window, None, parsed_expression).evaluate_internal(
7049 cx,
7050 context_node,
7051 result_type,
7052 result,
7053 )
7054 }
7055
7056 fn AdoptedStyleSheets(&self, cx: &mut JSContext, retval: MutableHandleValue) {
7058 self.adopted_stylesheets_frozen_types.get_or_init(
7059 cx,
7060 || {
7061 self.adopted_stylesheets
7062 .borrow()
7063 .clone()
7064 .iter()
7065 .map(|sheet| sheet.as_rooted())
7066 .collect()
7067 },
7068 retval,
7069 );
7070 }
7071
7072 fn SetAdoptedStyleSheets(&self, cx: &mut JSContext, val: HandleValue) -> ErrorResult {
7074 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
7075 cx,
7076 &self.adopted_stylesheets,
7077 val,
7078 &StyleSheetListOwner::Document(Dom::from_ref(self)),
7079 );
7080
7081 if result.is_ok() {
7082 self.adopted_stylesheets_frozen_types.clear()
7083 }
7084
7085 result
7086 }
7087
7088 fn Timeline(&self) -> DomRoot<DocumentTimeline> {
7089 self.timeline.as_rooted()
7090 }
7091}
7092
7093fn update_with_current_instant(marker: &Cell<Option<CrossProcessInstant>>) {
7094 if marker.get().is_none() {
7095 marker.set(Some(CrossProcessInstant::now()))
7096 }
7097}
7098
7099#[derive(JSTraceable, MallocSizeOf)]
7100pub(crate) enum AnimationFrameCallback {
7101 DevtoolsFramerateTick {
7102 actor_name: String,
7103 },
7104 FrameRequestCallback {
7105 #[conditional_malloc_size_of]
7106 callback: Rc<FrameRequestCallback>,
7107 },
7108}
7109
7110impl AnimationFrameCallback {
7111 fn call(&self, cx: &mut JSContext, document: &Document, now: f64) {
7112 match *self {
7113 AnimationFrameCallback::DevtoolsFramerateTick { ref actor_name } => {
7114 let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name.clone(), now);
7115 let devtools_sender = document.window().as_global_scope().devtools_chan().unwrap();
7116 devtools_sender.send(msg).unwrap();
7117 },
7118 AnimationFrameCallback::FrameRequestCallback { ref callback } => {
7119 let _ = callback.Call__(cx, Finite::wrap(now), ExceptionHandling::Report);
7122 },
7123 }
7124 }
7125}
7126
7127#[derive(Default, JSTraceable, MallocSizeOf)]
7128#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
7129struct PendingInOrderScriptVec {
7130 scripts: DomRefCell<VecDeque<PendingScript>>,
7131}
7132
7133impl PendingInOrderScriptVec {
7134 fn is_empty(&self) -> bool {
7135 self.scripts.borrow().is_empty()
7136 }
7137
7138 fn push(&self, element: &HTMLScriptElement) {
7139 self.scripts
7140 .borrow_mut()
7141 .push_back(PendingScript::new(element));
7142 }
7143
7144 fn loaded(&self, element: &HTMLScriptElement, result: ScriptResult) {
7145 let mut scripts = self.scripts.borrow_mut();
7146 let entry = scripts
7147 .iter_mut()
7148 .find(|entry| &*entry.element == element)
7149 .unwrap();
7150 entry.loaded(result);
7151 }
7152
7153 fn take_next_ready_to_be_executed(&self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
7154 let mut scripts = self.scripts.borrow_mut();
7155 let pair = scripts.front_mut()?.take_result()?;
7156 scripts.pop_front();
7157 Some(pair)
7158 }
7159
7160 fn clear(&self) {
7161 *self.scripts.borrow_mut() = Default::default();
7162 }
7163}
7164
7165#[derive(JSTraceable, MallocSizeOf)]
7166#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
7167struct PendingScript {
7168 element: Dom<HTMLScriptElement>,
7169 load: Option<ScriptResult>,
7171}
7172
7173impl PendingScript {
7174 fn new(element: &HTMLScriptElement) -> Self {
7175 Self {
7176 element: Dom::from_ref(element),
7177 load: None,
7178 }
7179 }
7180
7181 fn new_with_load(element: &HTMLScriptElement, load: Option<ScriptResult>) -> Self {
7182 Self {
7183 element: Dom::from_ref(element),
7184 load,
7185 }
7186 }
7187
7188 fn loaded(&mut self, result: ScriptResult) {
7189 assert!(self.load.is_none());
7190 self.load = Some(result);
7191 }
7192
7193 fn take_result(&mut self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
7194 self.load
7195 .take()
7196 .map(|result| (DomRoot::from_ref(&*self.element), result))
7197 }
7198}
7199
7200fn is_named_element_with_name_attribute(elem: &Element) -> bool {
7201 let type_ = match elem.upcast::<Node>().type_id() {
7202 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
7203 _ => return false,
7204 };
7205 match type_ {
7206 HTMLElementTypeId::HTMLFormElement |
7207 HTMLElementTypeId::HTMLIFrameElement |
7208 HTMLElementTypeId::HTMLImageElement => true,
7209 _ => false,
7213 }
7214}
7215
7216fn is_named_element_with_id_attribute(elem: &Element) -> bool {
7217 elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty())
7221}
7222
7223impl DocumentHelpers for Document {
7224 fn ensure_safe_to_run_script_or_layout(&self) {
7225 Document::ensure_safe_to_run_script_or_layout(self)
7226 }
7227}
7228
7229pub(crate) struct SameoriginAncestorNavigablesIterator {
7233 document: DomRoot<Document>,
7234}
7235
7236impl SameoriginAncestorNavigablesIterator {
7237 pub(crate) fn new(document: DomRoot<Document>) -> Self {
7238 Self { document }
7239 }
7240}
7241
7242impl Iterator for SameoriginAncestorNavigablesIterator {
7243 type Item = DomRoot<Document>;
7244
7245 fn next(&mut self) -> Option<Self::Item> {
7246 let window_proxy = self.document.browsing_context()?;
7247 self.document = window_proxy.parent()?.document()?;
7248 Some(self.document.clone())
7249 }
7250}
7251
7252pub(crate) struct SameOriginDescendantNavigablesIterator {
7257 stack: Vec<Box<dyn Iterator<Item = DomRoot<HTMLIFrameElement>>>>,
7258}
7259
7260impl SameOriginDescendantNavigablesIterator {
7261 pub(crate) fn new(document: &Document) -> Self {
7262 let iframes: Vec<DomRoot<HTMLIFrameElement>> = document.iframes().iter().collect();
7263 Self {
7264 stack: vec![Box::new(iframes.into_iter())],
7265 }
7266 }
7267
7268 fn get_next_iframe(&mut self) -> Option<DomRoot<HTMLIFrameElement>> {
7269 let mut cur_iframe = self.stack.last_mut()?.next();
7270 while cur_iframe.is_none() {
7271 self.stack.pop();
7272 cur_iframe = self.stack.last_mut()?.next();
7273 }
7274 cur_iframe
7275 }
7276}
7277
7278impl Iterator for SameOriginDescendantNavigablesIterator {
7279 type Item = DomRoot<Document>;
7280
7281 fn next(&mut self) -> Option<Self::Item> {
7282 while let Some(iframe) = self.get_next_iframe() {
7283 let Some(pipeline_id) = iframe.pipeline_id() else {
7284 continue;
7285 };
7286
7287 if let Some(document) = ScriptThread::find_document(pipeline_id) {
7288 let child_iframes: Vec<DomRoot<HTMLIFrameElement>> =
7289 document.iframes().iter().collect();
7290 self.stack.push(Box::new(child_iframes.into_iter()));
7291 return Some(document);
7292 } else {
7293 continue;
7294 };
7295 }
7296 None
7297 }
7298}