1use std::cell::{Cell, RefCell};
6use std::cmp::Ordering;
7use std::collections::hash_map::Entry::{Occupied, Vacant};
8use std::collections::{HashMap, HashSet, VecDeque};
9use std::default::Default;
10use std::ops::Deref;
11use std::rc::Rc;
12use std::str::FromStr;
13use std::sync::{LazyLock, Mutex};
14use std::time::Duration;
15
16use bitflags::bitflags;
17use chrono::Local;
18use content_security_policy::sandboxing_directive::SandboxingFlagSet;
19use content_security_policy::{CspList, Policy as CspPolicy, PolicyDisposition};
20use cookie::Cookie;
21use data_url::mime::Mime;
22use devtools_traits::ScriptToDevtoolsControlMsg;
23use dom_struct::dom_struct;
24use embedder_traits::{
25 AllowOrDeny, AnimationState, CustomHandlersAutomationMode, EmbedderMsg, Image, LoadStatus,
26};
27use encoding_rs::{Encoding, UTF_8};
28use fonts::WebFontDocumentContext;
29use html5ever::{LocalName, Namespace, QualName, local_name, ns};
30use hyper_serde::Serde;
31use js::realm::CurrentRealm;
32use js::rust::{HandleObject, HandleValue, MutableHandleValue};
33use layout_api::{
34 PendingRestyle, ReflowGoal, ReflowPhasesRun, ReflowStatistics, RestyleReason,
35 ScrollContainerQueryFlags, TrustedNodeAddress,
36};
37use metrics::{InteractiveFlag, InteractiveWindow, ProgressiveWebMetrics};
38use net_traits::CookieSource::NonHTTP;
39use net_traits::CoreResourceMsg::{GetCookieStringForUrl, SetCookiesForUrl};
40use net_traits::policy_container::PolicyContainer;
41use net_traits::pub_domains::is_pub_domain;
42use net_traits::request::{
43 InsecureRequestsPolicy, PreloadId, PreloadKey, PreloadedResources, RequestBuilder,
44};
45use net_traits::{ReferrerPolicy, ResourceFetchTiming};
46use percent_encoding::percent_decode;
47use profile_traits::generic_channel as profile_generic_channel;
48use profile_traits::time::TimerMetadataFrameType;
49use regex::bytes::Regex;
50use rustc_hash::{FxBuildHasher, FxHashMap};
51use script_bindings::cell::{DomRefCell, Ref, RefMut};
52use script_bindings::interfaces::DocumentHelpers;
53use script_bindings::reflector::reflect_dom_object_with_proto;
54use script_bindings::script_runtime::JSContext;
55use script_traits::{DocumentActivity, ProgressiveWebMetricType};
56use servo_arc::Arc;
57use servo_base::cross_process_instant::CrossProcessInstant;
58use servo_base::generic_channel::GenericSend;
59use servo_base::id::WebViewId;
60use servo_base::{Epoch, generic_channel};
61use servo_config::pref;
62use servo_constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage};
63use servo_media::{ClientContextId, ServoMedia};
64use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
65use style::attr::AttrValue;
66use style::context::QuirksMode;
67use style::invalidation::element::restyle_hints::RestyleHint;
68use style::selector_parser::Snapshot;
69use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
70use style::str::{split_html_space_chars, str_join};
71use style::stylesheet_set::DocumentStylesheetSet;
72use style::stylesheets::{Origin, OriginSet, Stylesheet};
73use style::stylist::Stylist;
74use stylo_atoms::Atom;
75use time::Duration as TimeDuration;
76use url::{Host, Position};
77
78use crate::animations::Animations;
79use crate::document_loader::{DocumentLoader, LoadType};
80use crate::dom::animationtimeline::AnimationTimeline;
81use crate::dom::attr::Attr;
82use crate::dom::beforeunloadevent::BeforeUnloadEvent;
83use crate::dom::bindings::callback::ExceptionHandling;
84use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEvent_Binding::BeforeUnloadEventMethods;
85use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
86 DocumentMethods, DocumentReadyState, DocumentVisibilityState, NamedPropertyValue,
87};
88use crate::dom::bindings::codegen::Bindings::ElementBinding::ScrollLogicalPosition;
89use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
90use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElement_Binding::HTMLIFrameElementMethods;
91#[cfg(any(feature = "webxr", feature = "gamepad"))]
92use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
93use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
94use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
95use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
96use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionName;
97use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
98 SetHTMLOptions, SetHTMLUnsafeOptions,
99};
100use crate::dom::bindings::codegen::Bindings::WindowBinding::{
101 FrameRequestCallback, ScrollBehavior, WindowMethods,
102};
103use crate::dom::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMethods;
104use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
105use crate::dom::bindings::codegen::UnionTypes::{
106 BooleanOrImportNodeOptions, NodeOrString, StringOrElementCreationOptions, TrustedHTMLOrString,
107};
108use crate::dom::bindings::domname::{
109 self, is_valid_attribute_local_name, is_valid_element_local_name, namespace_from_domstring,
110};
111use crate::dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible};
112use crate::dom::bindings::frozenarray::CachedFrozenArray;
113use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
114use crate::dom::bindings::num::Finite;
115use crate::dom::bindings::refcounted::Trusted;
116use crate::dom::bindings::reflector::DomGlobal;
117use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout, UnrootedDom};
118use crate::dom::bindings::str::{DOMString, USVString};
119use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
120use crate::dom::bindings::weakref::DOMTracker;
121use crate::dom::bindings::xmlname::matches_name_production;
122use crate::dom::cdatasection::CDATASection;
123use crate::dom::comment::Comment;
124use crate::dom::compositionevent::CompositionEvent;
125use crate::dom::css::cssstylesheet::CSSStyleSheet;
126use crate::dom::css::fontfaceset::FontFaceSet;
127use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
128use crate::dom::customelementregistry::{
129 CustomElementDefinition, CustomElementReactionStack, CustomElementRegistry,
130};
131use crate::dom::customevent::CustomEvent;
132use crate::dom::document::focus::{DocumentFocusHandler, FocusableArea};
133use crate::dom::document_embedder_controls::DocumentEmbedderControls;
134use crate::dom::document_event_handler::DocumentEventHandler;
135use crate::dom::documentfragment::DocumentFragment;
136use crate::dom::documentorshadowroot::{
137 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
138};
139use crate::dom::documenttimeline::DocumentTimeline;
140use crate::dom::documenttype::DocumentType;
141use crate::dom::domimplementation::DOMImplementation;
142use crate::dom::element::attributes::storage::AttrRef;
143use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
144use crate::dom::event::{Event, EventBubbles, EventCancelable};
145use crate::dom::eventtarget::EventTarget;
146use crate::dom::execcommand::basecommand::{CommandName, DefaultSingleLineContainerName};
147use crate::dom::execcommand::execcommands::DocumentExecCommandSupport;
148use crate::dom::focusevent::FocusEvent;
149use crate::dom::globalscope::GlobalScope;
150use crate::dom::hashchangeevent::HashChangeEvent;
151use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
152use crate::dom::html::htmlareaelement::HTMLAreaElement;
153use crate::dom::html::htmlbaseelement::HTMLBaseElement;
154use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
155use crate::dom::html::htmlelement::HTMLElement;
156use crate::dom::html::htmlembedelement::HTMLEmbedElement;
157use crate::dom::html::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
158use crate::dom::html::htmlheadelement::HTMLHeadElement;
159use crate::dom::html::htmlhtmlelement::HTMLHtmlElement;
160use crate::dom::html::htmliframeelement::HTMLIFrameElement;
161use crate::dom::html::htmlimageelement::HTMLImageElement;
162use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult};
163use crate::dom::html::htmltitleelement::HTMLTitleElement;
164use crate::dom::htmldetailselement::DetailsNameGroups;
165use crate::dom::intersectionobserver::IntersectionObserver;
166use crate::dom::iterators::ShadowIncluding;
167use crate::dom::keyboardevent::KeyboardEvent;
168use crate::dom::largestcontentfulpaint::LargestContentfulPaint;
169use crate::dom::location::Location;
170use crate::dom::messageevent::MessageEvent;
171use crate::dom::mouseevent::MouseEvent;
172use crate::dom::node::{Node, NodeDamage, NodeFlags, NodeTraits};
173use crate::dom::nodeiterator::NodeIterator;
174use crate::dom::nodelist::NodeList;
175use crate::dom::pagetransitionevent::PageTransitionEvent;
176use crate::dom::performance::performanceentry::PerformanceEntry;
177use crate::dom::performance::performancepainttiming::PerformancePaintTiming;
178use crate::dom::processinginstruction::ProcessingInstruction;
179use crate::dom::promise::Promise;
180use crate::dom::range::Range;
181use crate::dom::resizeobserver::{ResizeObservationDepth, ResizeObserver};
182use crate::dom::sanitizer::Sanitizer;
183use crate::dom::scrolling_box::{ScrollAxisState, ScrollingBox};
184use crate::dom::selection::Selection;
185use crate::dom::servoparser::ServoParser;
186use crate::dom::shadowroot::ShadowRoot;
187use crate::dom::storageevent::StorageEvent;
188use crate::dom::text::Text;
189use crate::dom::touchevent::TouchEvent as DomTouchEvent;
190use crate::dom::touchlist::TouchList;
191use crate::dom::treewalker::TreeWalker;
192use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
193use crate::dom::types::{HTMLCanvasElement, VisibilityStateEntry};
194use crate::dom::uievent::UIEvent;
195use crate::dom::virtualmethods::vtable_for;
196use crate::dom::websocket::WebSocket;
197use crate::dom::window::Window;
198use crate::dom::windowproxy::WindowProxy;
199use crate::dom::xpathevaluator::XPathEvaluator;
200use crate::dom::xpathexpression::XPathExpression;
201use crate::fetch::{DeferredFetchRecordInvokeState, FetchCanceller};
202use crate::iframe_collection::IFrameCollection;
203use crate::image_animation::ImageAnimationManager;
204use crate::mime::{APPLICATION, CHARSET};
205use crate::navigation::navigate;
206use crate::network_listener::{FetchResponseListener, NetworkListener};
207use crate::script_runtime::CanGc;
208use crate::script_thread::{ScriptThread, SharedRwLocks};
209use crate::stylesheet_set::StylesheetSetRef;
210use crate::task::NonSendTaskBox;
211use crate::task_source::TaskSourceName;
212use crate::timers::OneshotTimerCallback;
213use crate::xpath::parse_expression;
214
215#[derive(Clone, Copy, PartialEq)]
216pub(crate) enum FireMouseEventType {
217 Move,
218 Over,
219 Out,
220 Enter,
221 Leave,
222}
223
224impl FireMouseEventType {
225 pub(crate) fn as_str(&self) -> &str {
226 match *self {
227 FireMouseEventType::Move => "mousemove",
228 FireMouseEventType::Over => "mouseover",
229 FireMouseEventType::Out => "mouseout",
230 FireMouseEventType::Enter => "mouseenter",
231 FireMouseEventType::Leave => "mouseleave",
232 }
233 }
234}
235
236#[derive(JSTraceable, MallocSizeOf)]
237pub(crate) struct RefreshRedirectDue {
238 #[no_trace]
239 pub(crate) url: ServoUrl,
240 #[ignore_malloc_size_of = "non-owning"]
241 pub(crate) window: DomRoot<Window>,
242 pub(crate) from_meta_element: bool,
244}
245impl RefreshRedirectDue {
246 pub(crate) fn invoke(self, cx: &mut js::context::JSContext) {
248 if self.from_meta_element &&
255 self.window.Document().has_active_sandboxing_flag(
256 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
257 )
258 {
259 return;
260 }
261 let load_data = self
262 .window
263 .load_data_for_document(self.url.clone(), self.window.pipeline_id());
264 navigate(
265 cx,
266 &self.window,
267 NavigationHistoryBehavior::Replace,
268 false,
269 load_data,
270 );
271 }
272}
273
274#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
275pub(crate) enum IsHTMLDocument {
276 HTMLDocument,
277 NonHTMLDocument,
278}
279
280#[derive(JSTraceable, MallocSizeOf)]
282pub(crate) enum DeclarativeRefresh {
283 PendingLoad {
284 #[no_trace]
285 url: ServoUrl,
286 time: u64,
287 from_meta_element: bool,
289 },
290 CreatedAfterLoad,
291}
292
293#[derive(JSTraceable, MallocSizeOf, PartialEq)]
294#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
295struct PendingScrollEvent {
296 target: Dom<EventTarget>,
298 #[no_trace]
300 event: Atom,
301}
302
303impl PendingScrollEvent {
304 fn equivalent(&self, target: &EventTarget, event: &Atom) -> bool {
305 &*self.target == target && self.event == *event
306 }
307}
308
309#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf)]
312pub(crate) struct RenderingUpdateReason(u8);
313
314bitflags! {
315 impl RenderingUpdateReason: u8 {
316 const ResizeObserverStartedObservingTarget = 1 << 0;
319 const IntersectionObserverStartedObservingTarget = 1 << 1;
322 const FontReadyPromiseFulfilled = 1 << 2;
326 }
327}
328
329#[dom_struct]
331pub(crate) struct Document {
332 node: Node,
333 document_or_shadow_root: DocumentOrShadowRoot,
334 window: Dom<Window>,
335 implementation: MutNullableDom<DOMImplementation>,
336 #[ignore_malloc_size_of = "type from external crate"]
337 #[no_trace]
338 content_type: Mime,
339 last_modified: Option<String>,
340 #[no_trace]
341 encoding: Cell<&'static Encoding>,
342 has_browsing_context: bool,
343 is_html_document: bool,
344 #[no_trace]
345 activity: Cell<DocumentActivity>,
346 #[no_trace]
348 url: DomRefCell<ServoUrl>,
349 #[no_trace]
351 about_base_url: DomRefCell<Option<ServoUrl>>,
352 #[ignore_malloc_size_of = "defined in selectors"]
353 #[no_trace]
354 quirks_mode: Cell<QuirksMode>,
355 event_handler: DocumentEventHandler,
357 focus_handler: DocumentFocusHandler,
359 embedder_controls: DocumentEmbedderControls,
361 id_map: DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>>,
364 name_map: DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>>,
365 tag_map: DomRefCell<HashMapTracedValues<LocalName, Dom<HTMLCollection>, FxBuildHasher>>,
366 tagns_map: DomRefCell<HashMapTracedValues<QualName, Dom<HTMLCollection>, FxBuildHasher>>,
367 classes_map: DomRefCell<HashMapTracedValues<Vec<Atom>, Dom<HTMLCollection>>>,
368 images: MutNullableDom<HTMLCollection>,
369 embeds: MutNullableDom<HTMLCollection>,
370 links: MutNullableDom<HTMLCollection>,
371 forms: MutNullableDom<HTMLCollection>,
372 scripts: MutNullableDom<HTMLCollection>,
373 anchors: MutNullableDom<HTMLCollection>,
374 applets: MutNullableDom<HTMLCollection>,
375 iframes: RefCell<IFrameCollection>,
377 #[no_trace]
381 shared_style_locks: SharedRwLocks,
382 #[custom_trace]
384 stylesheets: DomRefCell<DocumentStylesheetSet<ServoStylesheetInDocument>>,
385 stylesheet_list: MutNullableDom<StyleSheetList>,
386 ready_state: Cell<DocumentReadyState>,
387 domcontentloaded_dispatched: Cell<bool>,
389 current_script: MutNullableDom<HTMLScriptElement>,
391 pending_parsing_blocking_script: DomRefCell<Option<PendingScript>>,
393 script_blocking_stylesheets_count: Cell<u32>,
395 render_blocking_element_count: Cell<u32>,
398 deferred_scripts: PendingInOrderScriptVec,
400 asap_in_order_scripts_list: PendingInOrderScriptVec,
402 asap_scripts_set: DomRefCell<Vec<Dom<HTMLScriptElement>>>,
404 animation_frame_ident: Cell<u32>,
407 animation_frame_list: DomRefCell<VecDeque<(u32, Option<AnimationFrameCallback>)>>,
410 running_animation_callbacks: Cell<bool>,
415 loader: DomRefCell<DocumentLoader>,
417 current_parser: MutNullableDom<ServoParser>,
419 base_element: MutNullableDom<HTMLBaseElement>,
421 target_base_element: MutNullableDom<HTMLBaseElement>,
423 appropriate_template_contents_owner_document: MutNullableDom<Document>,
426 pending_restyles: DomRefCell<FxHashMap<Dom<Element>, NoTrace<PendingRestyle>>>,
429 #[no_trace]
433 needs_restyle: Cell<RestyleReason>,
434 #[no_trace]
437 dom_interactive: Cell<Option<CrossProcessInstant>>,
438 #[no_trace]
440 dom_content_loaded_event_start: Cell<Option<CrossProcessInstant>>,
441 #[no_trace]
443 dom_content_loaded_event_end: Cell<Option<CrossProcessInstant>>,
444 #[no_trace]
445 dom_complete: Cell<Option<CrossProcessInstant>>,
446 #[no_trace]
447 top_level_dom_complete: Cell<Option<CrossProcessInstant>>,
448 #[no_trace]
449 load_event_start: Cell<Option<CrossProcessInstant>>,
450 #[no_trace]
451 load_event_end: Cell<Option<CrossProcessInstant>>,
452 #[no_trace]
453 unload_event_start: Cell<Option<CrossProcessInstant>>,
454 #[no_trace]
455 unload_event_end: Cell<Option<CrossProcessInstant>>,
456
457 #[no_trace]
459 origin: DomRefCell<MutableOrigin>,
460 referrer: Option<String>,
462 target_element: MutNullableDom<Element>,
464 #[no_trace]
466 policy_container: DomRefCell<PolicyContainer>,
467 #[no_trace]
469 preloaded_resources: DomRefCell<PreloadedResources>,
470 ignore_destructive_writes_counter: Cell<u32>,
472 ignore_opens_during_unload_counter: Cell<u32>,
474 spurious_animation_frames: Cell<u8>,
478
479 fullscreen_element: MutNullableDom<Element>,
481 form_id_listener_map:
488 DomRefCell<HashMapTracedValues<Atom, HashSet<Dom<Element>>, FxBuildHasher>>,
489 #[no_trace]
490 interactive_time: DomRefCell<ProgressiveWebMetrics>,
491 #[no_trace]
492 tti_window: DomRefCell<InteractiveWindow>,
493 canceller: FetchCanceller,
495 throw_on_dynamic_markup_insertion_counter: Cell<u64>,
497 page_showing: Cell<bool>,
499 salvageable: Cell<bool>,
501 active_parser_was_aborted: Cell<bool>,
503 fired_unload: Cell<bool>,
505 responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>,
507
508 #[no_trace]
510 resource_fetch_timing: RefCell<Option<ResourceFetchTiming>>,
511
512 script_and_layout_blockers: Cell<u32>,
514 #[ignore_malloc_size_of = "Measuring trait objects is hard"]
516 delayed_tasks: DomRefCell<Vec<Box<dyn NonSendTaskBox>>>,
517 completely_loaded: Cell<bool>,
519 shadow_roots: DomRefCell<HashSet<Dom<ShadowRoot>>>,
521 shadow_roots_styles_changed: Cell<bool>,
523 media_controls: DomRefCell<HashMap<String, Dom<ShadowRoot>>>,
529 dirty_canvases: DomRefCell<Vec<Dom<HTMLCanvasElement>>>,
532 has_pending_animated_image_update: Cell<bool>,
534 selection: MutNullableDom<Selection>,
536 timeline: Dom<DocumentTimeline>,
539 animations: Animations,
541 image_animation_manager: DomRefCell<ImageAnimationManager>,
543 dirty_root: MutNullableDom<Element>,
545 declarative_refresh: DomRefCell<Option<DeclarativeRefresh>>,
547 resize_observers: DomRefCell<Vec<Dom<ResizeObserver>>>,
556 fonts: MutNullableDom<FontFaceSet>,
559 visibility_state: Cell<DocumentVisibilityState>,
561 status_code: Option<u16>,
563 is_initial_about_blank: Cell<bool>,
565 allow_declarative_shadow_roots: Cell<bool>,
567 #[no_trace]
569 inherited_insecure_requests_policy: Cell<Option<InsecureRequestsPolicy>>,
570 has_trustworthy_ancestor_origin: Cell<bool>,
572 intersection_observer_task_queued: Cell<bool>,
574 intersection_observers: DomRefCell<Vec<Dom<IntersectionObserver>>>,
586 highlighted_dom_node: MutNullableDom<Node>,
588 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
591 #[ignore_malloc_size_of = "mozjs"]
593 adopted_stylesheets_frozen_types: CachedFrozenArray,
594 pending_scroll_events: DomRefCell<Vec<PendingScrollEvent>>,
598 rendering_update_reasons: Cell<RenderingUpdateReason>,
600 waiting_on_canvas_image_updates: Cell<bool>,
604 root_removal_noted: Cell<bool>,
606 #[no_trace]
614 current_rendering_epoch: Cell<Epoch>,
615 #[conditional_malloc_size_of]
617 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
618 #[no_trace]
619 active_sandboxing_flag_set: Cell<SandboxingFlagSet>,
621 #[no_trace]
622 creation_sandboxing_flag_set: Cell<SandboxingFlagSet>,
629 #[no_trace]
631 favicon: RefCell<Option<Image>>,
632
633 websockets: DOMTracker<WebSocket>,
635
636 details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
638
639 #[no_trace]
641 protocol_handler_automation_mode: RefCell<CustomHandlersAutomationMode>,
642
643 layout_animations_test_enabled: bool,
645
646 #[no_trace]
648 state_override: DomRefCell<FxHashMap<CommandName, bool>>,
649
650 #[no_trace]
652 value_override: DomRefCell<FxHashMap<CommandName, DOMString>>,
653
654 #[no_trace]
656 default_single_line_container_name: Cell<DefaultSingleLineContainerName>,
657
658 css_styling_flag: Cell<bool>,
660}
661
662impl Document {
663 fn unloading_cleanup_steps(&self) {
665 if self.close_outstanding_websockets() {
668 self.salvageable.set(false);
670 }
671
672 if !self.salvageable.get() {
677 let global_scope = self.window.as_global_scope();
678
679 global_scope.close_event_sources();
681
682 let msg = ScriptToConstellationMessage::DiscardDocument;
687 let _ = global_scope.script_to_constellation_chan().send(msg);
688 }
689 }
690
691 pub(crate) fn track_websocket(&self, websocket: &WebSocket) {
692 self.websockets.track(websocket);
693 }
694
695 fn close_outstanding_websockets(&self) -> bool {
696 let mut closed_any_websocket = false;
697 self.websockets.for_each(|websocket: DomRoot<WebSocket>| {
698 if websocket.make_disappear() {
699 closed_any_websocket = true;
700 }
701 });
702 closed_any_websocket
703 }
704
705 pub(crate) fn note_node_with_dirty_descendants(&self, node: &Node) {
706 debug_assert!(*node.owner_doc() == *self);
707 if !node.is_connected() {
708 return;
709 }
710
711 let parent = match node.parent_in_flat_tree() {
712 Some(parent) => parent,
713 None => {
714 let Some(document_element) = self.GetDocumentElement() else {
717 if !self.root_removal_noted.get() {
719 self.add_restyle_reason(RestyleReason::DOMChanged);
720 self.root_removal_noted.set(true);
721 }
722 return;
723 };
724 self.root_removal_noted.set(false);
727
728 if let Some(dirty_root) = self.dirty_root.get() &&
729 dirty_root.is_connected()
730 {
731 for ancestor in dirty_root
734 .upcast::<Node>()
735 .inclusive_ancestors_in_flat_tree()
736 {
737 if ancestor.is::<Element>() {
738 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
739 }
740 }
741 }
742 self.dirty_root.set(Some(&document_element));
743 return;
744 },
745 };
746
747 if let Some(parent_element) = parent.downcast::<Element>() {
748 if !parent_element.is_styled() {
749 return;
750 }
751 if parent_element.is_display_none() {
752 return;
753 }
754 }
755
756 let element_parent: DomRoot<Element>;
757 let element = match node.downcast::<Element>() {
758 Some(element) => element,
759 None => {
760 match DomRoot::downcast::<Element>(parent) {
763 Some(parent) => {
764 element_parent = parent;
765 &element_parent
766 },
767 None => {
768 return;
772 },
773 }
774 },
775 };
776
777 let dirty_root = match self.dirty_root.get() {
778 Some(root) if root.is_connected() => root,
779 _ => {
780 element
781 .upcast::<Node>()
782 .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
783 self.dirty_root.set(Some(element));
784 return;
785 },
786 };
787
788 for ancestor in element.upcast::<Node>().inclusive_ancestors_in_flat_tree() {
789 if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
790 return;
791 }
792
793 if ancestor.is::<Element>() {
794 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
795 }
796 }
797
798 let new_dirty_root = element
799 .upcast::<Node>()
800 .common_ancestor_in_flat_tree(dirty_root.upcast())
801 .expect("Couldn't find common ancestor");
802
803 let mut has_dirty_descendants = true;
804 for ancestor in dirty_root
805 .upcast::<Node>()
806 .inclusive_ancestors_in_flat_tree()
807 {
808 ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, has_dirty_descendants);
809 has_dirty_descendants &= *ancestor != *new_dirty_root;
810 }
811
812 self.dirty_root
813 .set(Some(new_dirty_root.downcast::<Element>().unwrap()));
814 }
815
816 pub(crate) fn take_dirty_root(&self) -> Option<DomRoot<Element>> {
817 self.dirty_root.take()
818 }
819
820 #[inline]
821 pub(crate) fn loader(&self) -> Ref<'_, DocumentLoader> {
822 self.loader.borrow()
823 }
824
825 #[inline]
826 pub(crate) fn loader_mut(&self) -> RefMut<'_, DocumentLoader> {
827 self.loader.borrow_mut()
828 }
829
830 #[inline]
831 pub(crate) fn has_browsing_context(&self) -> bool {
832 self.has_browsing_context
833 }
834
835 #[inline]
837 pub(crate) fn browsing_context(&self) -> Option<DomRoot<WindowProxy>> {
838 if self.has_browsing_context {
839 self.window.undiscarded_window_proxy()
840 } else {
841 None
842 }
843 }
844
845 pub(crate) fn webview_id(&self) -> WebViewId {
846 self.window.webview_id()
847 }
848
849 #[inline]
850 pub(crate) fn window(&self) -> &Window {
851 &self.window
852 }
853
854 #[inline]
855 pub(crate) fn is_html_document(&self) -> bool {
856 self.is_html_document
857 }
858
859 pub(crate) fn is_xhtml_document(&self) -> bool {
860 self.content_type.matches(APPLICATION, "xhtml+xml")
861 }
862
863 pub(crate) fn is_fully_active(&self) -> bool {
864 self.activity.get() == DocumentActivity::FullyActive
865 }
866
867 pub(crate) fn is_active(&self) -> bool {
868 self.activity.get() != DocumentActivity::Inactive
869 }
870
871 #[inline]
872 pub(crate) fn current_rendering_epoch(&self) -> Epoch {
873 self.current_rendering_epoch.get()
874 }
875
876 pub(crate) fn set_activity(&self, cx: &mut js::context::JSContext, activity: DocumentActivity) {
877 assert!(self.has_browsing_context);
879 if activity == self.activity.get() {
880 return;
881 }
882
883 self.activity.set(activity);
885 let media = ServoMedia::get();
886 let pipeline_id = self.window().pipeline_id();
887 let client_context_id =
888 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
889
890 if activity != DocumentActivity::FullyActive {
891 self.window().suspend(cx);
892 media.suspend(&client_context_id);
893 return;
894 }
895
896 self.title_changed();
897 self.notify_embedder_favicon();
898 self.dirty_all_nodes();
899 self.window().resume(CanGc::from_cx(cx));
900 media.resume(&client_context_id);
901
902 if self.ready_state.get() != DocumentReadyState::Complete {
903 return;
904 }
905
906 let document = Trusted::new(self);
910 self.owner_global()
911 .task_manager()
912 .dom_manipulation_task_source()
913 .queue(task!(fire_pageshow_event: move |cx| {
914 let document = document.root();
915 let window = document.window();
916 if document.page_showing.get() {
918 return;
919 }
920 document.page_showing.set(true);
922 document.update_visibility_state(cx, DocumentVisibilityState::Visible);
924 let event = PageTransitionEvent::new(
927 window,
928 atom!("pageshow"),
929 false, false, true, CanGc::from_cx(cx),
933 );
934 let event = event.upcast::<Event>();
935 event.set_trusted(true);
936 window.dispatch_event_with_target_override(cx, event);
937 }))
938 }
939
940 pub(crate) fn origin(&self) -> Ref<'_, MutableOrigin> {
941 self.origin.borrow()
942 }
943
944 pub(crate) fn mark_as_internal(&self) {
947 *self.origin.borrow_mut() = MutableOrigin::new(ImmutableOrigin::new_opaque());
948 }
949
950 pub(crate) fn set_protocol_handler_automation_mode(&self, mode: CustomHandlersAutomationMode) {
951 *self.protocol_handler_automation_mode.borrow_mut() = mode;
952 }
953
954 pub(crate) fn url(&self) -> ServoUrl {
956 self.url.borrow().clone()
957 }
958
959 pub(crate) fn set_url(&self, url: ServoUrl) {
960 *self.url.borrow_mut() = url;
961 }
962
963 pub(crate) fn about_base_url(&self) -> Option<ServoUrl> {
964 self.about_base_url.borrow().clone()
965 }
966
967 pub(crate) fn set_about_base_url(&self, about_base_url: Option<ServoUrl>) {
968 *self.about_base_url.borrow_mut() = about_base_url;
969 }
970
971 pub(crate) fn fallback_base_url(&self) -> ServoUrl {
973 let document_url = self.url();
974 if document_url.as_str() == "about:srcdoc" {
976 return self
979 .about_base_url()
980 .expect("about:srcdoc page should always have an about base URL");
981 }
982
983 if document_url.matches_about_blank() &&
986 let Some(about_base_url) = self.about_base_url()
987 {
988 return about_base_url;
989 }
990
991 document_url
993 }
994
995 pub(crate) fn base_url(&self) -> ServoUrl {
997 match self.base_element() {
998 None => self.fallback_base_url(),
1000 Some(base) => base.frozen_base_url(),
1002 }
1003 }
1004
1005 pub(crate) fn add_restyle_reason(&self, reason: RestyleReason) {
1006 self.needs_restyle.set(self.needs_restyle.get() | reason)
1007 }
1008
1009 pub(crate) fn clear_restyle_reasons(&self) {
1010 self.needs_restyle.set(RestyleReason::empty());
1011 }
1012
1013 pub(crate) fn restyle_reason(&self) -> RestyleReason {
1014 let mut condition = self.needs_restyle.get();
1015 if self.stylesheets.borrow().has_changed() {
1016 condition.insert(RestyleReason::StylesheetsChanged);
1017 }
1018
1019 if let Some(root) = self.GetDocumentElement() &&
1023 root.upcast::<Node>().has_dirty_descendants()
1024 {
1025 condition.insert(RestyleReason::DOMChanged);
1026 }
1027
1028 if !self.pending_restyles.borrow().is_empty() {
1029 condition.insert(RestyleReason::PendingRestyles);
1030 }
1031
1032 condition
1033 }
1034
1035 pub(crate) fn base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1037 self.base_element.get()
1038 }
1039
1040 pub(crate) fn target_base_element(&self) -> Option<DomRoot<HTMLBaseElement>> {
1042 self.target_base_element.get()
1043 }
1044
1045 pub(crate) fn refresh_base_element(&self) {
1047 if let Some(base_element) = self.base_element.get() {
1048 base_element.clear_frozen_base_url();
1049 }
1050 let new_base_element = self
1051 .upcast::<Node>()
1052 .traverse_preorder(ShadowIncluding::No)
1053 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1054 .find(|element| {
1055 element
1056 .upcast::<Element>()
1057 .has_attribute(&local_name!("href"))
1058 });
1059 if let Some(ref new_base_element) = new_base_element {
1060 new_base_element.set_frozen_base_url();
1061 }
1062 self.base_element.set(new_base_element.as_deref());
1063
1064 let new_target_base_element = self
1065 .upcast::<Node>()
1066 .traverse_preorder(ShadowIncluding::No)
1067 .filter_map(DomRoot::downcast::<HTMLBaseElement>)
1068 .next();
1069 self.target_base_element
1070 .set(new_target_base_element.as_deref());
1071 }
1072
1073 pub(crate) fn quirks_mode(&self) -> QuirksMode {
1074 self.quirks_mode.get()
1075 }
1076
1077 pub(crate) fn set_quirks_mode(&self, new_mode: QuirksMode) {
1078 let old_mode = self.quirks_mode.replace(new_mode);
1079
1080 if old_mode != new_mode {
1081 self.window.layout_mut().set_quirks_mode(new_mode);
1082 }
1083 }
1084
1085 pub(crate) fn encoding(&self) -> &'static Encoding {
1086 self.encoding.get()
1087 }
1088
1089 pub(crate) fn set_encoding(&self, encoding: &'static Encoding) {
1090 self.encoding.set(encoding);
1091 }
1092
1093 pub(crate) fn content_and_heritage_changed(&self, node: &Node) {
1094 if node.is_connected() {
1095 node.note_dirty_descendants();
1096 }
1097
1098 node.dirty(NodeDamage::ContentOrHeritage);
1101 }
1102
1103 pub(crate) fn unregister_element_id(
1105 &self,
1106 cx: &mut js::context::JSContext,
1107 to_unregister: &Element,
1108 id: Atom,
1109 ) {
1110 self.document_or_shadow_root
1111 .unregister_named_element(&self.id_map, to_unregister, &id);
1112 self.reset_form_owner_for_listeners(cx, &id);
1113 }
1114
1115 pub(crate) fn register_element_id(
1117 &self,
1118 cx: &mut js::context::JSContext,
1119 element: &Element,
1120 id: Atom,
1121 ) {
1122 let root = self.GetDocumentElement().expect(
1123 "The element is in the document, so there must be a document \
1124 element.",
1125 );
1126 self.document_or_shadow_root.register_named_element(
1127 &self.id_map,
1128 element,
1129 &id,
1130 DomRoot::from_ref(root.upcast::<Node>()),
1131 );
1132 self.reset_form_owner_for_listeners(cx, &id);
1133 }
1134
1135 pub(crate) fn unregister_element_name(&self, to_unregister: &Element, name: Atom) {
1137 self.document_or_shadow_root
1138 .unregister_named_element(&self.name_map, to_unregister, &name);
1139 }
1140
1141 pub(crate) fn register_element_name(&self, element: &Element, name: Atom) {
1143 let root = self.GetDocumentElement().expect(
1144 "The element is in the document, so there must be a document \
1145 element.",
1146 );
1147 self.document_or_shadow_root.register_named_element(
1148 &self.name_map,
1149 element,
1150 &name,
1151 DomRoot::from_ref(root.upcast::<Node>()),
1152 );
1153 }
1154
1155 pub(crate) fn register_form_id_listener<T: ?Sized + FormControl>(
1156 &self,
1157 id: DOMString,
1158 listener: &T,
1159 ) {
1160 let mut map = self.form_id_listener_map.borrow_mut();
1161 let listener = listener.to_element();
1162 let set = map.entry(Atom::from(id)).or_default();
1163 set.insert(Dom::from_ref(listener));
1164 }
1165
1166 pub(crate) fn unregister_form_id_listener<T: ?Sized + FormControl>(
1167 &self,
1168 id: DOMString,
1169 listener: &T,
1170 ) {
1171 let mut map = self.form_id_listener_map.borrow_mut();
1172 if let Occupied(mut entry) = map.entry(Atom::from(id)) {
1173 entry
1174 .get_mut()
1175 .remove(&Dom::from_ref(listener.to_element()));
1176 if entry.get().is_empty() {
1177 entry.remove();
1178 }
1179 }
1180 }
1181
1182 fn find_a_potential_indicated_element(&self, fragment: &str) -> Option<DomRoot<Element>> {
1184 self.get_element_by_id(&Atom::from(fragment))
1188 .or_else(|| self.get_anchor_by_name(fragment))
1192 }
1193
1194 fn select_indicated_part(&self, fragment: &str) -> Option<DomRoot<Node>> {
1197 if fragment.is_empty() {
1207 return Some(DomRoot::from_ref(self.upcast()));
1208 }
1209 if let Some(potential_indicated_element) = self.find_a_potential_indicated_element(fragment)
1211 {
1212 return Some(DomRoot::upcast(potential_indicated_element));
1214 }
1215 let fragment_bytes = percent_decode(fragment.as_bytes());
1217 let Ok(decoded_fragment) = fragment_bytes.decode_utf8() else {
1219 return None;
1220 };
1221 if let Some(potential_indicated_element) =
1223 self.find_a_potential_indicated_element(&decoded_fragment)
1224 {
1225 return Some(DomRoot::upcast(potential_indicated_element));
1227 }
1228 if decoded_fragment.eq_ignore_ascii_case("top") {
1230 return Some(DomRoot::from_ref(self.upcast()));
1231 }
1232 None
1234 }
1235
1236 pub(crate) fn scroll_to_the_fragment(&self, cx: &mut js::context::JSContext, fragment: &str) {
1238 let Some(indicated_part) = self.select_indicated_part(fragment) else {
1243 self.set_target_element(None);
1244 return;
1245 };
1246 if *indicated_part == *self.upcast() {
1248 self.set_target_element(None);
1250 self.window.scroll(cx, 0.0, 0.0, ScrollBehavior::Instant);
1255 return;
1257 }
1258 let Some(target) = indicated_part.downcast::<Element>() else {
1261 unreachable!("Indicated part should always be an element");
1263 };
1264 self.set_target_element(Some(target));
1266 target.scroll_into_view_with_options(
1270 cx,
1271 ScrollBehavior::Auto,
1272 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Start),
1273 ScrollAxisState::new_always_scroll_position(ScrollLogicalPosition::Nearest),
1274 None,
1275 None,
1276 );
1277
1278 indicated_part.run_the_focusing_steps(cx, Some(FocusableArea::Viewport));
1281
1282 self.focus_handler()
1284 .set_sequential_focus_navigation_starting_point(target.upcast());
1285 }
1286
1287 fn get_anchor_by_name(&self, name: &str) -> Option<DomRoot<Element>> {
1288 let name = Atom::from(name);
1289 self.name_map.borrow().get(&name).and_then(|elements| {
1290 elements
1291 .iter()
1292 .find(|e| e.is::<HTMLAnchorElement>())
1293 .map(|e| DomRoot::from_ref(&**e))
1294 })
1295 }
1296
1297 pub(crate) fn set_ready_state(
1299 &self,
1300 cx: &mut js::context::JSContext,
1301 state: DocumentReadyState,
1302 ) {
1303 match state {
1304 DocumentReadyState::Loading => {
1305 if self.window().is_top_level() {
1306 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1307 self.webview_id(),
1308 LoadStatus::Started,
1309 ));
1310 self.send_to_embedder(EmbedderMsg::Status(self.webview_id(), None));
1311 }
1312 },
1313 DocumentReadyState::Complete => {
1314 if self.window().is_top_level() {
1315 self.send_to_embedder(EmbedderMsg::NotifyLoadStatusChanged(
1316 self.webview_id(),
1317 LoadStatus::Complete,
1318 ));
1319 }
1320 update_with_current_instant(&self.dom_complete);
1321 },
1322 DocumentReadyState::Interactive => update_with_current_instant(&self.dom_interactive),
1323 };
1324
1325 self.ready_state.set(state);
1326
1327 self.upcast::<EventTarget>()
1328 .fire_event(cx, atom!("readystatechange"));
1329 }
1330
1331 pub(crate) fn scripting_enabled(&self) -> bool {
1334 self.has_browsing_context() &&
1337 !self.has_active_sandboxing_flag(
1341 SandboxingFlagSet::SANDBOXED_SCRIPTS_BROWSING_CONTEXT_FLAG,
1342 )
1343 }
1344
1345 pub(crate) fn title_changed(&self) {
1347 if self.browsing_context().is_some() {
1348 self.send_title_to_embedder();
1349 let title = String::from(self.Title());
1350 self.window
1351 .send_to_constellation(ScriptToConstellationMessage::TitleChanged(
1352 self.window.pipeline_id(),
1353 title.clone(),
1354 ));
1355 if let Some(chan) = self.window.as_global_scope().devtools_chan() {
1356 let _ = chan.send(ScriptToDevtoolsControlMsg::TitleChanged(
1357 self.window.pipeline_id(),
1358 title,
1359 ));
1360 }
1361 }
1362 }
1363
1364 fn title(&self) -> Option<DOMString> {
1368 let title = self.GetDocumentElement().and_then(|root| {
1369 if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
1370 root.upcast::<Node>()
1372 .child_elements()
1373 .find(|node| {
1374 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
1375 })
1376 .map(DomRoot::upcast::<Node>)
1377 } else {
1378 root.upcast::<Node>()
1380 .traverse_preorder(ShadowIncluding::No)
1381 .find(|node| node.is::<HTMLTitleElement>())
1382 }
1383 });
1384
1385 title.map(|title| {
1386 let value = title.child_text_content();
1388 DOMString::from(str_join(value.str().split_html_space_characters(), " "))
1389 })
1390 }
1391
1392 pub(crate) fn send_title_to_embedder(&self) {
1394 let window = self.window();
1395 if window.is_top_level() {
1396 let title = self.title().map(String::from);
1397 self.send_to_embedder(EmbedderMsg::ChangePageTitle(self.webview_id(), title));
1398 }
1399 }
1400
1401 pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) {
1402 let window = self.window();
1403 window.send_to_embedder(msg);
1404 }
1405
1406 pub(crate) fn dirty_all_nodes(&self) {
1407 let root = match self.GetDocumentElement() {
1408 Some(root) => root,
1409 None => return,
1410 };
1411 for node in root
1412 .upcast::<Node>()
1413 .traverse_preorder(ShadowIncluding::Yes)
1414 {
1415 node.dirty(NodeDamage::Other)
1416 }
1417 }
1418
1419 pub(crate) fn run_the_scroll_steps(&self, cx: &mut js::context::JSContext) {
1421 let boxes_that_were_scrolled: Vec<_> = self
1429 .pending_scroll_events
1430 .borrow()
1431 .iter()
1432 .filter_map(|pending_event| {
1433 if &*pending_event.event == "scroll" {
1434 Some(pending_event.target.as_rooted())
1435 } else {
1436 None
1437 }
1438 })
1439 .collect();
1440
1441 for target in boxes_that_were_scrolled.into_iter() {
1442 let Some(element) = target.downcast::<Element>() else {
1448 continue;
1449 };
1450 let document = element.owner_document();
1451
1452 let mut pending_scroll_events = document.pending_scroll_events.borrow_mut();
1459 let event = "scrollend".into();
1460 if pending_scroll_events
1461 .iter()
1462 .any(|existing| existing.equivalent(&target, &event))
1463 {
1464 continue;
1465 }
1466
1467 pending_scroll_events.push(PendingScrollEvent {
1469 target: target.as_traced(),
1470 event: "scrollend".into(),
1471 });
1472 }
1473
1474 rooted_vec!(let pending_scroll_events <- self.pending_scroll_events.take().into_iter());
1477 for pending_event in pending_scroll_events.iter() {
1478 let event = pending_event.event.clone();
1481 if pending_event.target.is::<Document>() {
1482 pending_event.target.fire_bubbling_event(cx, event);
1483 }
1484 else {
1493 pending_event.target.fire_event(cx, event);
1494 }
1495 }
1496
1497 }
1500
1501 pub(crate) fn handle_viewport_scroll_event(&self) {
1506 self.finish_handle_scroll_event(self.upcast());
1518 }
1519
1520 pub(crate) fn finish_handle_scroll_event(&self, event_target: &EventTarget) {
1525 let event = "scroll".into();
1528 if self
1529 .pending_scroll_events
1530 .borrow()
1531 .iter()
1532 .any(|existing| existing.equivalent(event_target, &event))
1533 {
1534 return;
1535 }
1536
1537 self.pending_scroll_events
1540 .borrow_mut()
1541 .push(PendingScrollEvent {
1542 target: Dom::from_ref(event_target),
1543 event: "scroll".into(),
1544 });
1545 }
1546
1547 pub(crate) fn node_from_nodes_and_strings(
1549 &self,
1550 cx: &mut js::context::JSContext,
1551 mut nodes: Vec<NodeOrString>,
1552 ) -> Fallible<DomRoot<Node>> {
1553 if nodes.len() == 1 {
1554 Ok(match nodes.pop().unwrap() {
1555 NodeOrString::Node(node) => node,
1556 NodeOrString::String(string) => DomRoot::upcast(self.CreateTextNode(cx, string)),
1557 })
1558 } else {
1559 let fragment = DomRoot::upcast::<Node>(self.CreateDocumentFragment(cx));
1560 for node in nodes {
1561 match node {
1562 NodeOrString::Node(node) => {
1563 fragment.AppendChild(cx, &node)?;
1564 },
1565 NodeOrString::String(string) => {
1566 let node = DomRoot::upcast::<Node>(self.CreateTextNode(cx, string));
1567 fragment.AppendChild(cx, &node).unwrap();
1570 },
1571 }
1572 }
1573 Ok(fragment)
1574 }
1575 }
1576
1577 pub(crate) fn get_body_attribute(&self, local_name: &LocalName) -> DOMString {
1578 match self.GetBody() {
1579 Some(ref body) if body.is_body_element() => {
1580 body.upcast::<Element>().get_string_attribute(local_name)
1581 },
1582 _ => DOMString::new(),
1583 }
1584 }
1585
1586 pub(crate) fn set_body_attribute(
1587 &self,
1588 cx: &mut js::context::JSContext,
1589 local_name: &LocalName,
1590 value: DOMString,
1591 ) {
1592 if let Some(ref body) = self.GetBody().filter(|elem| elem.is_body_element()) {
1593 let body = body.upcast::<Element>();
1594 let value = body.parse_attribute(&ns!(), local_name, value);
1595 body.set_attribute(cx, local_name, value);
1596 }
1597 }
1598
1599 pub(crate) fn set_current_script(&self, script: Option<&HTMLScriptElement>) {
1600 self.current_script.set(script);
1601 }
1602
1603 pub(crate) fn get_script_blocking_stylesheets_count(&self) -> u32 {
1604 self.script_blocking_stylesheets_count.get()
1605 }
1606
1607 pub(crate) fn increment_script_blocking_stylesheet_count(&self) {
1608 let count_cell = &self.script_blocking_stylesheets_count;
1609 count_cell.set(count_cell.get() + 1);
1610 }
1611
1612 pub(crate) fn decrement_script_blocking_stylesheet_count(&self) {
1613 let count_cell = &self.script_blocking_stylesheets_count;
1614 assert!(count_cell.get() > 0);
1615 count_cell.set(count_cell.get() - 1);
1616 }
1617
1618 pub(crate) fn render_blocking_element_count(&self) -> u32 {
1619 self.render_blocking_element_count.get()
1620 }
1621
1622 pub(crate) fn increment_render_blocking_element_count(&self) {
1624 assert!(self.allows_adding_render_blocking_elements());
1631 let count_cell = &self.render_blocking_element_count;
1632 count_cell.set(count_cell.get() + 1);
1633 }
1634
1635 pub(crate) fn decrement_render_blocking_element_count(&self) {
1637 let count_cell = &self.render_blocking_element_count;
1643 assert!(count_cell.get() > 0);
1644 count_cell.set(count_cell.get() - 1);
1645 }
1646
1647 pub(crate) fn allows_adding_render_blocking_elements(&self) -> bool {
1649 self.is_html_document && self.GetBody().is_none()
1652 }
1653
1654 pub(crate) fn is_render_blocked(&self) -> bool {
1656 self.render_blocking_element_count() > 0
1660 }
1665
1666 pub(crate) fn invalidate_stylesheets(&self) {
1667 self.stylesheets.borrow_mut().force_dirty(OriginSet::all());
1668
1669 if let Some(element) = self.GetDocumentElement() {
1673 element.upcast::<Node>().dirty(NodeDamage::Style);
1674 }
1675 }
1676
1677 pub(crate) fn has_active_request_animation_frame_callbacks(&self) -> bool {
1680 !self.animation_frame_list.borrow().is_empty()
1681 }
1682
1683 pub(crate) fn request_animation_frame(&self, callback: AnimationFrameCallback) -> u32 {
1685 let ident = self.animation_frame_ident.get() + 1;
1686 self.animation_frame_ident.set(ident);
1687
1688 let had_animation_frame_callbacks;
1689 {
1690 let mut animation_frame_list = self.animation_frame_list.borrow_mut();
1691 had_animation_frame_callbacks = !animation_frame_list.is_empty();
1692 animation_frame_list.push_back((ident, Some(callback)));
1693 }
1694
1695 if !self.running_animation_callbacks.get() && !had_animation_frame_callbacks {
1701 self.window().send_to_constellation(
1702 ScriptToConstellationMessage::ChangeRunningAnimationsState(
1703 AnimationState::AnimationCallbacksPresent,
1704 ),
1705 );
1706 }
1707
1708 ident
1709 }
1710
1711 pub(crate) fn cancel_animation_frame(&self, ident: u32) {
1713 let mut list = self.animation_frame_list.borrow_mut();
1714 if let Some(pair) = list.iter_mut().find(|pair| pair.0 == ident) {
1715 pair.1 = None;
1716 }
1717 }
1718
1719 pub(crate) fn run_the_animation_frame_callbacks(&self, cx: &mut CurrentRealm) {
1721 self.running_animation_callbacks.set(true);
1722 let timing = self.global().performance().Now();
1723
1724 let num_callbacks = self.animation_frame_list.borrow().len();
1725 for _ in 0..num_callbacks {
1726 let (_, maybe_callback) = self.animation_frame_list.borrow_mut().pop_front().unwrap();
1727 if let Some(callback) = maybe_callback {
1728 callback.call(cx, self, *timing);
1729 }
1730 }
1731 self.running_animation_callbacks.set(false);
1732
1733 if self.animation_frame_list.borrow().is_empty() {
1734 self.window().send_to_constellation(
1735 ScriptToConstellationMessage::ChangeRunningAnimationsState(
1736 AnimationState::NoAnimationCallbacksPresent,
1737 ),
1738 );
1739 }
1740 }
1741
1742 pub(crate) fn policy_container(&self) -> Ref<'_, PolicyContainer> {
1743 self.policy_container.borrow()
1744 }
1745
1746 pub(crate) fn set_policy_container(&self, policy_container: PolicyContainer) {
1747 *self.policy_container.borrow_mut() = policy_container;
1748 }
1749
1750 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
1751 self.policy_container.borrow_mut().set_csp_list(csp_list);
1752 }
1753
1754 pub(crate) fn enforce_csp_policy(&self, policy: CspPolicy) {
1756 let mut csp_list = self.get_csp_list().clone().unwrap_or(CspList(vec![]));
1758 csp_list.push(policy);
1759 self.policy_container
1760 .borrow_mut()
1761 .set_csp_list(Some(csp_list));
1762 }
1763
1764 pub(crate) fn get_csp_list(&self) -> Ref<'_, Option<CspList>> {
1765 Ref::map(self.policy_container.borrow(), |policy_container| {
1766 &policy_container.csp_list
1767 })
1768 }
1769
1770 pub(crate) fn preloaded_resources(&self) -> std::cell::Ref<'_, PreloadedResources> {
1771 self.preloaded_resources.borrow()
1772 }
1773
1774 pub(crate) fn insert_preloaded_resource(&self, key: PreloadKey, preload_id: PreloadId) {
1775 self.preloaded_resources
1776 .borrow_mut()
1777 .insert(key, preload_id);
1778 }
1779
1780 pub(crate) fn fetch<Listener: FetchResponseListener>(
1781 &self,
1782 load: LoadType,
1783 mut request: RequestBuilder,
1784 listener: Listener,
1785 ) {
1786 request = request
1787 .insecure_requests_policy(self.insecure_requests_policy())
1788 .has_trustworthy_ancestor_origin(self.has_trustworthy_ancestor_or_current_origin());
1789 let callback = NetworkListener {
1790 context: std::sync::Arc::new(Mutex::new(Some(listener))),
1791 task_source: self
1792 .owner_global()
1793 .task_manager()
1794 .networking_task_source()
1795 .into(),
1796 }
1797 .into_callback();
1798 self.loader_mut()
1799 .fetch_async_with_callback(load, request, callback);
1800 }
1801
1802 pub(crate) fn fetch_background<Listener: FetchResponseListener>(
1803 &self,
1804 mut request: RequestBuilder,
1805 listener: Listener,
1806 ) {
1807 request = request
1808 .insecure_requests_policy(self.insecure_requests_policy())
1809 .has_trustworthy_ancestor_origin(self.has_trustworthy_ancestor_or_current_origin());
1810 let callback = NetworkListener {
1811 context: std::sync::Arc::new(Mutex::new(Some(listener))),
1812 task_source: self
1813 .owner_global()
1814 .task_manager()
1815 .networking_task_source()
1816 .into(),
1817 }
1818 .into_callback();
1819 self.loader_mut().fetch_async_background(request, callback);
1820 }
1821
1822 fn deferred_fetch_control_document(&self) -> DomRoot<Document> {
1824 match self.window().window_proxy().frame_element() {
1825 None => DomRoot::from_ref(self),
1828 Some(container) => container.owner_document().deferred_fetch_control_document(),
1830 }
1831 }
1832
1833 pub(crate) fn available_deferred_fetch_quota(&self, origin: ImmutableOrigin) -> isize {
1835 let control_document = self.deferred_fetch_control_document();
1837 let navigable = control_document.window();
1839 let is_top_level = navigable.is_top_level();
1842 let deferred_fetch_allowed = true;
1846 let deferred_fetch_minimal_allowed = true;
1850 let mut quota = match is_top_level {
1852 true if !deferred_fetch_allowed => 0,
1854 true if !deferred_fetch_minimal_allowed => 640 * 1024,
1856 true => 512 * 1024,
1858 _ if deferred_fetch_allowed => 0,
1862 _ if deferred_fetch_minimal_allowed => 8 * 1024,
1866 _ => 0,
1868 } as isize;
1869 let mut quota_for_request_origin = 64 * 1024_isize;
1871 for deferred_fetch in navigable.as_global_scope().deferred_fetches() {
1880 if deferred_fetch.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
1882 continue;
1883 }
1884 let request_length = deferred_fetch.request.total_request_length();
1886 quota -= request_length as isize;
1888 if deferred_fetch.request.url().origin() == origin {
1891 quota_for_request_origin -= request_length as isize;
1892 }
1893 }
1894 if quota <= 0 {
1896 return 0;
1897 }
1898 if quota < quota_for_request_origin {
1900 return quota;
1901 }
1902 quota_for_request_origin
1904 }
1905
1906 pub(crate) fn update_document_for_history_step_application(
1908 &self,
1909 old_url: &ServoUrl,
1910 new_url: &ServoUrl,
1911 ) {
1912 if old_url.as_url()[Position::BeforeFragment..] !=
1942 new_url.as_url()[Position::BeforeFragment..]
1943 {
1944 let window = Trusted::new(self.owner_window().deref());
1945 let old_url = old_url.to_string();
1946 let new_url = new_url.to_string();
1947 self.owner_global()
1948 .task_manager()
1949 .dom_manipulation_task_source()
1950 .queue(task!(hashchange_event: move |cx| {
1951 let window = window.root();
1952 HashChangeEvent::new(
1953 &window,
1954 atom!("hashchange"),
1955 false,
1956 false,
1957 old_url,
1958 new_url,
1959 CanGc::from_cx(cx),
1960 )
1961 .upcast::<Event>()
1962 .fire(cx, window.upcast());
1963 }));
1964 }
1965 }
1966
1967 pub(crate) fn finish_load_for_dropped_blocker(&self, load: LoadType) {
1968 let this = Trusted::new(self);
1969 self.owner_global()
1970 .task_manager()
1971 .dom_manipulation_task_source()
1972 .queue(task!(check_finished_load: move |cx| {
1973 this.root().finish_load(load, cx);
1974 }));
1975 }
1976
1977 pub(crate) fn finish_load(&self, load: LoadType, cx: &mut js::context::JSContext) {
1980 debug!("Document got finish_load: {:?}", load);
1982 self.loader.borrow_mut().finish_load(&load);
1983
1984 match load {
1985 LoadType::Stylesheet(_) => {
1986 self.process_pending_parsing_blocking_script(cx);
1989
1990 self.process_deferred_scripts(cx);
1992 },
1993 LoadType::PageSource(_) => {
1994 if self.has_browsing_context && self.is_fully_active() {
1997 self.window().allow_layout_if_necessary(cx);
1998 }
1999
2000 self.process_deferred_scripts(cx);
2005 },
2006 _ => {},
2007 }
2008
2009 let loader = self.loader.borrow();
2016
2017 if self.top_level_dom_complete.get().is_none() && loader.is_only_blocked_by_iframes() {
2019 update_with_current_instant(&self.top_level_dom_complete);
2020 }
2021
2022 if loader.is_blocked() || loader.events_inhibited() {
2023 return;
2025 }
2026
2027 ScriptThread::mark_document_with_no_blocked_loads(self);
2028 }
2029
2030 pub(crate) fn check_if_unloading_is_cancelled(
2032 &self,
2033 cx: &mut js::context::JSContext,
2034 recursive_flag: bool,
2035 ) -> bool {
2036 self.incr_ignore_opens_during_unload_counter();
2039 let beforeunload_event = BeforeUnloadEvent::new(
2041 &self.window,
2042 atom!("beforeunload"),
2043 EventBubbles::Bubbles,
2044 EventCancelable::Cancelable,
2045 CanGc::from_cx(cx),
2046 );
2047 let event = beforeunload_event.upcast::<Event>();
2048 event.set_trusted(true);
2049 let event_target = self.window.upcast::<EventTarget>();
2050 let has_listeners = event_target.has_listeners_for(&atom!("beforeunload"));
2051 self.window.dispatch_event_with_target_override(cx, event);
2052 if has_listeners {
2055 self.salvageable.set(false);
2056 }
2057 let mut can_unload = true;
2058 let default_prevented = event.DefaultPrevented();
2060 let return_value_not_empty = !event
2061 .downcast::<BeforeUnloadEvent>()
2062 .unwrap()
2063 .ReturnValue()
2064 .is_empty();
2065 if default_prevented || return_value_not_empty {
2066 let (chan, port) = generic_channel::channel().expect("Failed to create IPC channel!");
2067 let msg = EmbedderMsg::AllowUnload(self.webview_id(), chan);
2068 self.send_to_embedder(msg);
2069 can_unload = port.recv().unwrap() == AllowOrDeny::Allow;
2070 }
2071 if !recursive_flag {
2073 let iframes: Vec<_> = self.iframes().iter().collect();
2076 for iframe in &iframes {
2077 let document = iframe.owner_document();
2079 can_unload = document.check_if_unloading_is_cancelled(cx, true);
2080 if !document.salvageable() {
2081 self.salvageable.set(false);
2082 }
2083 if !can_unload {
2084 break;
2085 }
2086 }
2087 }
2088 self.decr_ignore_opens_during_unload_counter();
2090 can_unload
2091 }
2092
2093 pub(crate) fn unload(&self, cx: &mut js::context::JSContext, recursive_flag: bool) {
2095 self.incr_ignore_opens_during_unload_counter();
2098 if self.page_showing.get() {
2100 self.page_showing.set(false);
2102 let event = PageTransitionEvent::new(
2105 &self.window,
2106 atom!("pagehide"),
2107 false, false, self.salvageable.get(), CanGc::from_cx(cx),
2111 );
2112 let event = event.upcast::<Event>();
2113 event.set_trusted(true);
2114 self.window.dispatch_event_with_target_override(cx, event);
2115 self.update_visibility_state(cx, DocumentVisibilityState::Hidden);
2117 }
2118 if !self.fired_unload.get() {
2120 let event = Event::new(
2121 self.window.upcast(),
2122 atom!("unload"),
2123 EventBubbles::Bubbles,
2124 EventCancelable::Cancelable,
2125 CanGc::from_cx(cx),
2126 );
2127 event.set_trusted(true);
2128 let event_target = self.window.upcast::<EventTarget>();
2129 let has_listeners = event_target.has_listeners_for(&atom!("unload"));
2130 self.window.dispatch_event_with_target_override(cx, &event);
2131 self.fired_unload.set(true);
2132 if has_listeners {
2134 self.salvageable.set(false);
2135 }
2136 }
2137 if !recursive_flag {
2141 let iframes: Vec<_> = self.iframes().iter().collect();
2144 for iframe in &iframes {
2145 let document = iframe.owner_document();
2147 document.unload(cx, true);
2148 if !document.salvageable() {
2149 self.salvageable.set(false);
2150 }
2151 }
2152 }
2153
2154 self.unloading_cleanup_steps();
2156
2157 self.window.as_global_scope().clean_up_all_file_resources();
2159
2160 self.decr_ignore_opens_during_unload_counter();
2162
2163 }
2166
2167 fn completely_finish_loading(&self) {
2169 self.completely_loaded.set(true);
2174 self.notify_constellation_load();
2183
2184 if let Some(DeclarativeRefresh::PendingLoad {
2193 url,
2194 time,
2195 from_meta_element,
2196 }) = &*self.declarative_refresh.borrow()
2197 {
2198 self.window.as_global_scope().schedule_callback(
2199 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
2200 window: DomRoot::from_ref(self.window()),
2201 url: url.clone(),
2202 from_meta_element: *from_meta_element,
2203 }),
2204 Duration::from_secs(*time),
2205 );
2206 }
2207 }
2208
2209 pub(crate) fn maybe_queue_document_completion(&self) {
2211 let is_in_delaying_load_events_mode = match self.window.undiscarded_window_proxy() {
2213 Some(window_proxy) => window_proxy.is_delaying_load_events_mode(),
2214 None => false,
2215 };
2216
2217 let not_ready_for_load = self.loader.borrow().is_blocked() ||
2222 !self.is_fully_active() ||
2223 is_in_delaying_load_events_mode ||
2224 self.loader.borrow().events_inhibited();
2227
2228 if not_ready_for_load {
2229 return;
2231 }
2232
2233 self.loader.borrow_mut().inhibit_events();
2234
2235 debug!("Document loads are complete.");
2240 let document = Trusted::new(self);
2241 self.owner_global()
2242 .task_manager()
2243 .dom_manipulation_task_source()
2244 .queue(task!(fire_load_event: move |cx| {
2245 let document = document.root();
2246 let window = document.window();
2248 if !window.is_alive() {
2249 return;
2250 }
2251
2252 document.set_ready_state(cx,DocumentReadyState::Complete);
2254
2255 if document.browsing_context().is_none() {
2257 return;
2258 }
2259
2260 update_with_current_instant(&document.load_event_start);
2262
2263 let load_event = Event::new(
2265 window.upcast(),
2266 atom!("load"),
2267 EventBubbles::DoesNotBubble,
2268 EventCancelable::NotCancelable,
2269 CanGc::from_cx(cx),
2270 );
2271 load_event.set_trusted(true);
2272 debug!("About to dispatch load for {:?}", document.url());
2273 window.dispatch_event_with_target_override(cx, &load_event);
2274
2275 update_with_current_instant(&document.load_event_end);
2285
2286 document.page_showing.set(true);
2291
2292 let page_show_event = PageTransitionEvent::new(
2294 window,
2295 atom!("pageshow"),
2296 false, false, false, CanGc::from_cx(cx),
2300 );
2301 let page_show_event = page_show_event.upcast::<Event>();
2302 page_show_event.set_trusted(true);
2303 page_show_event.fire(cx, window.upcast());
2304
2305 document.completely_finish_loading();
2307
2308 if let Some(fragment) = document.url().fragment() {
2312 document.scroll_to_the_fragment(cx, fragment);
2313 }
2314 }));
2315
2316 #[cfg(feature = "webxr")]
2331 if pref!(dom_webxr_sessionavailable) && self.window.is_top_level() {
2332 self.window.Navigator().Xr().dispatch_sessionavailable();
2333 }
2334 }
2335
2336 pub(crate) fn completely_loaded(&self) -> bool {
2337 self.completely_loaded.get()
2338 }
2339
2340 pub(crate) fn set_pending_parsing_blocking_script(
2342 &self,
2343 script: &HTMLScriptElement,
2344 load: Option<ScriptResult>,
2345 ) {
2346 assert!(!self.has_pending_parsing_blocking_script());
2347 *self.pending_parsing_blocking_script.borrow_mut() =
2348 Some(PendingScript::new_with_load(script, load));
2349 }
2350
2351 pub(crate) fn has_pending_parsing_blocking_script(&self) -> bool {
2353 self.pending_parsing_blocking_script.borrow().is_some()
2354 }
2355
2356 pub(crate) fn pending_parsing_blocking_script_loaded(
2358 &self,
2359 element: &HTMLScriptElement,
2360 result: ScriptResult,
2361 cx: &mut js::context::JSContext,
2362 ) {
2363 {
2364 let mut blocking_script = self.pending_parsing_blocking_script.borrow_mut();
2365 let entry = blocking_script.as_mut().unwrap();
2366 assert!(&*entry.element == element);
2367 entry.loaded(result);
2368 }
2369 self.process_pending_parsing_blocking_script(cx);
2370 }
2371
2372 fn process_pending_parsing_blocking_script(&self, cx: &mut js::context::JSContext) {
2373 if self.script_blocking_stylesheets_count.get() > 0 {
2374 return;
2375 }
2376 let pair = self
2377 .pending_parsing_blocking_script
2378 .borrow_mut()
2379 .as_mut()
2380 .and_then(PendingScript::take_result);
2381 if let Some((element, result)) = pair {
2382 *self.pending_parsing_blocking_script.borrow_mut() = None;
2383 self.get_current_parser()
2384 .unwrap()
2385 .resume_with_pending_parsing_blocking_script(cx, &element, result);
2386 }
2387 }
2388
2389 pub(crate) fn add_asap_script(&self, script: &HTMLScriptElement) {
2391 self.asap_scripts_set
2392 .borrow_mut()
2393 .push(Dom::from_ref(script));
2394 }
2395
2396 pub(crate) fn asap_script_loaded(
2399 &self,
2400 cx: &mut js::context::JSContext,
2401 element: &HTMLScriptElement,
2402 result: ScriptResult,
2403 ) {
2404 {
2405 let mut scripts = self.asap_scripts_set.borrow_mut();
2406 let idx = scripts
2407 .iter()
2408 .position(|entry| &**entry == element)
2409 .unwrap();
2410 scripts.swap_remove(idx);
2411 }
2412 element.execute(cx, result);
2413 }
2414
2415 pub(crate) fn push_asap_in_order_script(&self, script: &HTMLScriptElement) {
2417 self.asap_in_order_scripts_list.push(script);
2418 }
2419
2420 pub(crate) fn asap_in_order_script_loaded(
2423 &self,
2424 cx: &mut js::context::JSContext,
2425 element: &HTMLScriptElement,
2426 result: ScriptResult,
2427 ) {
2428 self.asap_in_order_scripts_list.loaded(element, result);
2429 while let Some((element, result)) = self
2430 .asap_in_order_scripts_list
2431 .take_next_ready_to_be_executed()
2432 {
2433 element.execute(cx, result);
2434 }
2435 }
2436
2437 pub(crate) fn add_deferred_script(&self, script: &HTMLScriptElement) {
2439 self.deferred_scripts.push(script);
2440 }
2441
2442 pub(crate) fn deferred_script_loaded(
2445 &self,
2446 cx: &mut js::context::JSContext,
2447 element: &HTMLScriptElement,
2448 result: ScriptResult,
2449 ) {
2450 self.deferred_scripts.loaded(element, result);
2451 self.process_deferred_scripts(cx);
2452 }
2453
2454 fn process_deferred_scripts(&self, cx: &mut js::context::JSContext) {
2456 if self.ready_state.get() != DocumentReadyState::Interactive {
2457 return;
2458 }
2459 loop {
2461 if self.script_blocking_stylesheets_count.get() > 0 {
2462 return;
2463 }
2464 if let Some((element, result)) = self.deferred_scripts.take_next_ready_to_be_executed()
2465 {
2466 element.execute(cx, result);
2467 } else {
2468 break;
2469 }
2470 }
2471 if self.deferred_scripts.is_empty() {
2472 self.maybe_dispatch_dom_content_loaded();
2474 }
2475 }
2476
2477 pub(crate) fn maybe_dispatch_dom_content_loaded(&self) {
2479 if self.domcontentloaded_dispatched.get() {
2480 return;
2481 }
2482 self.domcontentloaded_dispatched.set(true);
2483 assert_ne!(
2484 self.ReadyState(),
2485 DocumentReadyState::Complete,
2486 "Complete before DOMContentLoaded?"
2487 );
2488
2489 let document = Trusted::new(self);
2492 self.owner_global()
2493 .task_manager()
2494 .dom_manipulation_task_source()
2495 .queue(task!(fire_dom_content_loaded_event: move |cx| {
2496 let document = document.root();
2497
2498 update_with_current_instant(&document.dom_content_loaded_event_start);
2501
2502 document.upcast::<EventTarget>().fire_bubbling_event(cx, atom!("DOMContentLoaded"));
2505
2506 update_with_current_instant(&document.dom_content_loaded_event_end);
2509
2510 }));
2517
2518 self.interactive_time
2520 .borrow()
2521 .maybe_set_tti(InteractiveFlag::DOMContentLoaded);
2522
2523 }
2526
2527 pub(crate) fn destroy_document_and_its_descendants(&self, cx: &mut js::context::JSContext) {
2529 if !self.is_fully_active() {
2531 self.salvageable.set(false);
2536 }
2540 for exited_iframe in self.iframes().iter() {
2553 debug!("Destroying nested iframe document");
2554 exited_iframe.destroy_document_and_its_descendants(cx);
2555 }
2556 self.destroy(cx);
2561 }
2564
2565 pub(crate) fn destroy(&self, cx: &mut js::context::JSContext) {
2567 let exited_window = self.window();
2568 self.abort(cx);
2570 self.salvageable.set(false);
2572 self.unloading_cleanup_steps();
2582
2583 exited_window
2586 .as_global_scope()
2587 .task_manager()
2588 .cancel_all_tasks_and_ignore_future_tasks();
2589
2590 exited_window.discard_browsing_context();
2592
2593 }
2605
2606 fn terminate_fetch_group(&self) -> bool {
2608 let mut load_cancellers = self.loader.borrow_mut().cancel_all_loads();
2609
2610 for canceller in &mut load_cancellers {
2614 if !canceller.keep_alive() {
2615 canceller.terminate();
2616 }
2617 }
2618 self.owner_global().process_deferred_fetches();
2620
2621 !load_cancellers.is_empty()
2622 }
2623
2624 fn active_parser(&self) -> Option<DomRoot<ServoParser>> {
2626 self.get_current_parser()
2629 .filter(|parser| !(parser.has_stopped() || parser.has_aborted()))
2630 }
2631
2632 pub(crate) fn abort(&self, cx: &mut js::context::JSContext) {
2634 self.loader.borrow_mut().inhibit_events();
2636
2637 self.script_blocking_stylesheets_count.set(0);
2646 *self.pending_parsing_blocking_script.borrow_mut() = None;
2647 *self.asap_scripts_set.borrow_mut() = vec![];
2648 self.asap_in_order_scripts_list.clear();
2649 self.deferred_scripts.clear();
2650 let loads_cancelled = self.terminate_fetch_group();
2651 let event_sources_canceled = self.window.as_global_scope().close_event_sources();
2652 if loads_cancelled || event_sources_canceled {
2653 self.salvageable.set(false);
2655 };
2656
2657 self.owner_global()
2662 .task_manager()
2663 .cancel_pending_tasks_for_source(TaskSourceName::Networking);
2664
2665 if let Some(parser) = self.active_parser() {
2670 self.active_parser_was_aborted.set(true);
2672 parser.abort(cx);
2674 self.salvageable.set(false);
2676 }
2677 }
2678
2679 pub(crate) fn abort_a_document_and_its_descendants(&self, cx: &mut js::context::JSContext) {
2681 for iframe in self.iframes().iter() {
2689 if let Some(descendant_document) = iframe.GetContentDocument() {
2690 let trusted_descendant_document = Trusted::new(&*descendant_document);
2691 let document = Trusted::new(self);
2692 descendant_document
2693 .owner_global()
2694 .task_manager()
2695 .navigation_and_traversal_task_source()
2696 .queue(task!(abort_iframe_document: move |cx| {
2697 let descendant_document = trusted_descendant_document.root();
2698 descendant_document.abort(cx);
2700 if !descendant_document.salvageable.get() {
2702 document.root().salvageable.set(false);
2703 }
2704 }));
2705 }
2706 }
2707
2708 self.abort(cx);
2710 }
2711
2712 pub(crate) fn notify_constellation_load(&self) {
2713 self.window()
2714 .send_to_constellation(ScriptToConstellationMessage::LoadComplete);
2715 }
2716
2717 pub(crate) fn set_current_parser(&self, script: Option<&ServoParser>) {
2718 self.current_parser.set(script);
2719 }
2720
2721 pub(crate) fn get_current_parser(&self) -> Option<DomRoot<ServoParser>> {
2722 self.current_parser.get()
2723 }
2724
2725 pub(crate) fn get_current_parser_line(&self) -> u32 {
2726 self.get_current_parser()
2727 .map(|parser| parser.get_current_line())
2728 .unwrap_or(0)
2729 }
2730
2731 pub(crate) fn iframes(&self) -> Ref<'_, IFrameCollection> {
2734 self.iframes.borrow()
2735 }
2736
2737 pub(crate) fn iframes_mut(&self) -> RefMut<'_, IFrameCollection> {
2740 self.iframes.borrow_mut()
2741 }
2742
2743 pub(crate) fn get_dom_interactive(&self) -> Option<CrossProcessInstant> {
2744 self.dom_interactive.get()
2745 }
2746
2747 pub(crate) fn set_navigation_start(&self, navigation_start: CrossProcessInstant) {
2748 self.interactive_time
2749 .borrow_mut()
2750 .set_navigation_start(navigation_start);
2751 }
2752
2753 pub(crate) fn get_interactive_metrics(&self) -> Ref<'_, ProgressiveWebMetrics> {
2754 self.interactive_time.borrow()
2755 }
2756
2757 pub(crate) fn has_recorded_tti_metric(&self) -> bool {
2758 self.get_interactive_metrics().get_tti().is_some()
2759 }
2760
2761 pub(crate) fn get_dom_content_loaded_event_start(&self) -> Option<CrossProcessInstant> {
2762 self.dom_content_loaded_event_start.get()
2763 }
2764
2765 pub(crate) fn get_dom_content_loaded_event_end(&self) -> Option<CrossProcessInstant> {
2766 self.dom_content_loaded_event_end.get()
2767 }
2768
2769 pub(crate) fn get_dom_complete(&self) -> Option<CrossProcessInstant> {
2770 self.dom_complete.get()
2771 }
2772
2773 pub(crate) fn get_top_level_dom_complete(&self) -> Option<CrossProcessInstant> {
2774 self.top_level_dom_complete.get()
2775 }
2776
2777 pub(crate) fn get_load_event_start(&self) -> Option<CrossProcessInstant> {
2778 self.load_event_start.get()
2779 }
2780
2781 pub(crate) fn get_load_event_end(&self) -> Option<CrossProcessInstant> {
2782 self.load_event_end.get()
2783 }
2784
2785 pub(crate) fn get_unload_event_start(&self) -> Option<CrossProcessInstant> {
2786 self.unload_event_start.get()
2787 }
2788
2789 pub(crate) fn get_unload_event_end(&self) -> Option<CrossProcessInstant> {
2790 self.unload_event_end.get()
2791 }
2792
2793 pub(crate) fn start_tti(&self) {
2794 if self.get_interactive_metrics().needs_tti() {
2795 self.tti_window.borrow_mut().start_window();
2796 }
2797 }
2798
2799 pub(crate) fn record_tti_if_necessary(&self) {
2803 if self.has_recorded_tti_metric() {
2804 return;
2805 }
2806 if self.tti_window.borrow().needs_check() {
2807 self.get_interactive_metrics()
2808 .maybe_set_tti(InteractiveFlag::TimeToInteractive(
2809 self.tti_window.borrow().get_start(),
2810 ));
2811 }
2812 }
2813
2814 pub(crate) fn is_cookie_averse(&self) -> bool {
2816 !self.has_browsing_context || !url_has_network_scheme(&self.url())
2817 }
2818
2819 pub(crate) fn lookup_custom_element_definition(
2821 &self,
2822 namespace: &Namespace,
2823 local_name: &LocalName,
2824 is: Option<&LocalName>,
2825 ) -> Option<Rc<CustomElementDefinition>> {
2826 if *namespace != ns!(html) {
2828 return None;
2829 }
2830
2831 if !self.has_browsing_context {
2833 return None;
2834 }
2835
2836 let registry = self.window.CustomElements();
2838
2839 registry.lookup_definition(local_name, is)
2840 }
2841
2842 pub(crate) fn custom_element_registry(&self) -> DomRoot<CustomElementRegistry> {
2844 self.window.CustomElements()
2845 }
2846
2847 pub(crate) fn increment_throw_on_dynamic_markup_insertion_counter(&self) {
2848 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
2849 self.throw_on_dynamic_markup_insertion_counter
2850 .set(counter + 1);
2851 }
2852
2853 pub(crate) fn decrement_throw_on_dynamic_markup_insertion_counter(&self) {
2854 let counter = self.throw_on_dynamic_markup_insertion_counter.get();
2855 self.throw_on_dynamic_markup_insertion_counter
2856 .set(counter - 1);
2857 }
2858
2859 pub(crate) fn react_to_environment_changes(&self) {
2860 for image in self.responsive_images.borrow().iter() {
2861 image.react_to_environment_changes();
2862 }
2863 }
2864
2865 pub(crate) fn register_responsive_image(&self, img: &HTMLImageElement) {
2866 self.responsive_images.borrow_mut().push(Dom::from_ref(img));
2867 }
2868
2869 pub(crate) fn unregister_responsive_image(&self, img: &HTMLImageElement) {
2870 let index = self
2871 .responsive_images
2872 .borrow()
2873 .iter()
2874 .position(|x| **x == *img);
2875 if let Some(i) = index {
2876 self.responsive_images.borrow_mut().remove(i);
2877 }
2878 }
2879
2880 pub(crate) fn register_media_controls(&self, id: &str, controls: &ShadowRoot) {
2881 let did_have_these_media_controls = self
2882 .media_controls
2883 .borrow_mut()
2884 .insert(id.to_string(), Dom::from_ref(controls))
2885 .is_some();
2886 debug_assert!(
2887 !did_have_these_media_controls,
2888 "Trying to register known media controls"
2889 );
2890 }
2891
2892 pub(crate) fn unregister_media_controls(&self, id: &str) {
2893 let did_have_these_media_controls = self.media_controls.borrow_mut().remove(id).is_some();
2894 debug_assert!(
2895 did_have_these_media_controls,
2896 "Trying to unregister unknown media controls"
2897 );
2898 }
2899
2900 pub(crate) fn mark_canvas_as_dirty(&self, canvas: &Dom<HTMLCanvasElement>) {
2901 let mut dirty_canvases = self.dirty_canvases.borrow_mut();
2902 if dirty_canvases
2903 .iter()
2904 .any(|dirty_canvas| dirty_canvas == canvas)
2905 {
2906 return;
2907 }
2908 dirty_canvases.push(canvas.clone());
2909 }
2910
2911 pub(crate) fn needs_rendering_update(&self) -> bool {
2915 if !self.is_fully_active() {
2916 return false;
2917 }
2918 if !self.window().layout_blocked() &&
2919 (!self.restyle_reason().is_empty() ||
2920 self.window().layout().needs_new_display_list() ||
2921 self.window().layout().needs_accessibility_update())
2922 {
2923 return true;
2924 }
2925 if !self.rendering_update_reasons.get().is_empty() {
2926 return true;
2927 }
2928 if self.event_handler.has_pending_input_events() {
2929 return true;
2930 }
2931 if self.has_pending_scroll_events() {
2932 return true;
2933 }
2934 if self.window().has_unhandled_resize_event() {
2935 return true;
2936 }
2937 if self.has_pending_animated_image_update.get() || !self.dirty_canvases.borrow().is_empty()
2938 {
2939 return true;
2940 }
2941
2942 false
2943 }
2944
2945 pub(crate) fn update_the_rendering(
2953 &self,
2954 cx: &mut js::context::JSContext,
2955 ) -> (ReflowPhasesRun, ReflowStatistics) {
2956 assert!(!self.is_render_blocked());
2957
2958 let mut phases = ReflowPhasesRun::empty();
2959 if self.has_pending_animated_image_update.get() {
2960 self.image_animation_manager
2961 .borrow()
2962 .update_active_frames(&self.window, self.current_animation_timeline_value());
2963 self.has_pending_animated_image_update.set(false);
2964 phases.insert(ReflowPhasesRun::UpdatedImageData);
2965 }
2966
2967 self.current_rendering_epoch
2968 .set(self.current_rendering_epoch.get().next());
2969 let current_rendering_epoch = self.current_rendering_epoch.get();
2970
2971 let image_keys: Vec<_> = self
2973 .dirty_canvases
2974 .borrow_mut()
2975 .drain(..)
2976 .filter_map(|canvas| canvas.update_rendering(current_rendering_epoch))
2977 .collect();
2978
2979 let pipeline_id = self.window().pipeline_id();
2982 if !image_keys.is_empty() {
2983 phases.insert(ReflowPhasesRun::UpdatedImageData);
2984 self.waiting_on_canvas_image_updates.set(true);
2985 self.window().paint_api().delay_new_frame_for_canvas(
2986 self.webview_id(),
2987 self.window().pipeline_id(),
2988 current_rendering_epoch,
2989 image_keys,
2990 );
2991 }
2992
2993 let (reflow_phases, statistics) = self.window().reflow(cx, ReflowGoal::UpdateTheRendering);
2994 let phases = phases.union(reflow_phases);
2995
2996 self.window().paint_api().update_epoch(
2997 self.webview_id(),
2998 pipeline_id,
2999 current_rendering_epoch,
3000 );
3001
3002 (phases, statistics)
3003 }
3004
3005 pub(crate) fn handle_no_longer_waiting_on_asynchronous_image_updates(&self) {
3006 self.waiting_on_canvas_image_updates.set(false);
3007 }
3008
3009 pub(crate) fn waiting_on_canvas_image_updates(&self) -> bool {
3010 self.waiting_on_canvas_image_updates.get()
3011 }
3012
3013 pub(crate) fn maybe_fulfill_font_ready_promise(&self, cx: &mut js::context::JSContext) -> bool {
3023 if !self.is_fully_active() {
3024 return false;
3025 }
3026
3027 let fonts = self.Fonts(cx);
3028 if !fonts.waiting_to_fullfill_promise() {
3029 return false;
3030 }
3031 if self.window().font_context().web_fonts_still_loading() != 0 {
3032 return false;
3033 }
3034 if self.ReadyState() != DocumentReadyState::Complete {
3035 return false;
3036 }
3037 if !self.restyle_reason().is_empty() {
3038 return false;
3039 }
3040 if !self.rendering_update_reasons.get().is_empty() {
3041 return false;
3042 }
3043
3044 let result = fonts.fulfill_ready_promise_if_needed(cx);
3045
3046 if result {
3050 self.add_rendering_update_reason(RenderingUpdateReason::FontReadyPromiseFulfilled);
3051 }
3052
3053 result
3054 }
3055
3056 pub(crate) fn id_map(
3057 &self,
3058 ) -> Ref<'_, HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>> {
3059 self.id_map.borrow()
3060 }
3061
3062 pub(crate) fn name_map(
3063 &self,
3064 ) -> Ref<'_, HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>> {
3065 self.name_map.borrow()
3066 }
3067
3068 pub(crate) fn add_resize_observer(&self, resize_observer: &ResizeObserver) {
3070 self.resize_observers
3071 .borrow_mut()
3072 .push(Dom::from_ref(resize_observer));
3073 }
3074
3075 pub(crate) fn has_resize_observers(&self) -> bool {
3077 !self.resize_observers.borrow().is_empty()
3078 }
3079
3080 pub(crate) fn gather_active_resize_observations_at_depth(
3083 &self,
3084 depth: &ResizeObservationDepth,
3085 ) -> bool {
3086 let mut has_active_resize_observations = false;
3087 for observer in self.resize_observers.borrow_mut().iter_mut() {
3088 observer.gather_active_resize_observations_at_depth(
3089 depth,
3090 &mut has_active_resize_observations,
3091 );
3092 }
3093 has_active_resize_observations
3094 }
3095
3096 #[expect(clippy::redundant_iter_cloned)]
3098 pub(crate) fn broadcast_active_resize_observations(
3099 &self,
3100 cx: &mut js::context::JSContext,
3101 ) -> ResizeObservationDepth {
3102 let mut shallowest = ResizeObservationDepth::max();
3103 let iterator: Vec<DomRoot<ResizeObserver>> = self
3107 .resize_observers
3108 .borrow()
3109 .iter()
3110 .cloned()
3111 .map(|obs| DomRoot::from_ref(&*obs))
3112 .collect();
3113 for observer in iterator {
3114 observer.broadcast_active_resize_observations(cx, &mut shallowest);
3115 }
3116 shallowest
3117 }
3118
3119 pub(crate) fn has_skipped_resize_observations(&self) -> bool {
3121 self.resize_observers
3122 .borrow()
3123 .iter()
3124 .any(|observer| observer.has_skipped_resize_observations())
3125 }
3126
3127 pub(crate) fn deliver_resize_loop_error_notification(&self, cx: &mut js::context::JSContext) {
3129 let error_info: ErrorInfo = crate::dom::bindings::error::ErrorInfo {
3130 message: "ResizeObserver loop completed with undelivered notifications.".to_string(),
3131 ..Default::default()
3132 };
3133 self.window
3134 .as_global_scope()
3135 .report_an_error(cx, error_info, HandleValue::null());
3136 }
3137
3138 pub(crate) fn status_code(&self) -> Option<u16> {
3139 self.status_code
3140 }
3141
3142 pub(crate) fn encoding_parse_a_url(&self, url: &str) -> Result<ServoUrl, url::ParseError> {
3144 let encoding = self.encoding.get();
3150
3151 let base_url = self.base_url();
3157
3158 url::Url::options()
3160 .base_url(Some(base_url.as_url()))
3161 .encoding_override(Some(&|input| {
3162 servo_url::encoding::encode_as_url_query_string(input, encoding)
3163 }))
3164 .parse(url)
3165 .map(ServoUrl::from)
3166 }
3167
3168 pub(crate) fn allowed_to_use_feature(&self, _feature: PermissionName) -> bool {
3170 if !self.has_browsing_context {
3172 return false;
3173 }
3174
3175 if !self.is_fully_active() {
3177 return false;
3178 }
3179
3180 true
3186 }
3187
3188 pub(crate) fn add_intersection_observer(&self, intersection_observer: &IntersectionObserver) {
3191 self.intersection_observers
3192 .borrow_mut()
3193 .push(Dom::from_ref(intersection_observer));
3194 }
3195
3196 pub(crate) fn remove_intersection_observer(
3200 &self,
3201 intersection_observer: &IntersectionObserver,
3202 ) {
3203 self.intersection_observers
3204 .borrow_mut()
3205 .retain(|observer| *observer != intersection_observer)
3206 }
3207
3208 pub(crate) fn update_intersection_observer_steps(
3210 &self,
3211 cx: &mut js::context::JSContext,
3212 time: CrossProcessInstant,
3213 ) {
3214 for intersection_observer in &*self.intersection_observers.borrow() {
3216 self.update_single_intersection_observer_steps(cx, intersection_observer, time);
3217 }
3218 }
3219
3220 fn update_single_intersection_observer_steps(
3222 &self,
3223 cx: &mut js::context::JSContext,
3224 intersection_observer: &IntersectionObserver,
3225 time: CrossProcessInstant,
3226 ) {
3227 let root_bounds = intersection_observer.root_intersection_rectangle();
3230
3231 intersection_observer.update_intersection_observations_steps(cx, self, time, root_bounds);
3235 }
3236
3237 pub(crate) fn notify_intersection_observers(&self, cx: &mut js::context::JSContext) {
3239 self.intersection_observer_task_queued.set(false);
3242
3243 rooted_vec!(let notify_list <- self.intersection_observers.clone().take().into_iter());
3248
3249 for intersection_observer in notify_list.iter() {
3252 intersection_observer.invoke_callback_if_necessary(cx);
3254 }
3255 }
3256
3257 pub(crate) fn queue_an_intersection_observer_task(&self) {
3259 if self.intersection_observer_task_queued.get() {
3262 return;
3263 }
3264
3265 self.intersection_observer_task_queued.set(true);
3268
3269 let document = Trusted::new(self);
3273 self.owner_global()
3274 .task_manager()
3275 .intersection_observer_task_source()
3276 .queue(task!(notify_intersection_observers: move |cx| {
3277 document.root().notify_intersection_observers(cx);
3278 }));
3279 }
3280
3281 pub(crate) fn handle_paint_metric(
3282 &self,
3283 metric_type: ProgressiveWebMetricType,
3284 metric_value: CrossProcessInstant,
3285 first_reflow: bool,
3286 can_gc: CanGc,
3287 ) {
3288 let metrics = self.interactive_time.borrow();
3289 match metric_type {
3290 ProgressiveWebMetricType::FirstPaint |
3291 ProgressiveWebMetricType::FirstContentfulPaint => {
3292 let binding = PerformancePaintTiming::new(
3293 self.window.as_global_scope(),
3294 metric_type.clone(),
3295 metric_value,
3296 can_gc,
3297 );
3298 metrics.set_performance_paint_metric(metric_value, first_reflow, metric_type);
3299 let entry = binding.upcast::<PerformanceEntry>();
3300 self.window.Performance().queue_entry(entry);
3301 },
3302 ProgressiveWebMetricType::LargestContentfulPaint { area, url } => {
3303 let binding = LargestContentfulPaint::new(
3304 self.window.as_global_scope(),
3305 metric_value,
3306 area,
3307 url,
3308 can_gc,
3309 );
3310 metrics.set_largest_contentful_paint(metric_value, area);
3311 let entry = binding.upcast::<PerformanceEntry>();
3312 self.window.Performance().queue_entry(entry);
3313 },
3314 ProgressiveWebMetricType::TimeToInteractive => {
3315 unreachable!("Unexpected non-paint metric.")
3316 },
3317 }
3318 }
3319
3320 fn write(
3322 &self,
3323 cx: &mut js::context::JSContext,
3324 text: Vec<TrustedHTMLOrString>,
3325 line_feed: bool,
3326 containing_class: &str,
3327 field: &str,
3328 ) -> ErrorResult {
3329 let mut strings: Vec<String> = Vec::with_capacity(text.len());
3331 let mut is_trusted = true;
3333 for value in text {
3335 match value {
3336 TrustedHTMLOrString::TrustedHTML(trusted_html) => {
3338 strings.push(trusted_html.to_string());
3339 },
3340 TrustedHTMLOrString::String(str_) => {
3341 is_trusted = false;
3343 strings.push(str_.into());
3345 },
3346 };
3347 }
3348 let mut string = itertools::join(strings, "");
3349 if !is_trusted {
3353 string = TrustedHTML::get_trusted_type_compliant_string(
3354 cx,
3355 &self.global(),
3356 TrustedHTMLOrString::String(string.into()),
3357 &format!("{} {}", containing_class, field),
3358 )?
3359 .str()
3360 .to_owned();
3361 }
3362 if line_feed {
3364 string.push('\n');
3365 }
3366 if !self.is_html_document() {
3368 return Err(Error::InvalidState(None));
3369 }
3370
3371 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
3374 return Err(Error::InvalidState(None));
3375 }
3376
3377 if self.active_parser_was_aborted.get() {
3379 return Ok(());
3380 }
3381
3382 let parser = match self.get_current_parser() {
3383 Some(ref parser) if parser.can_write() => DomRoot::from_ref(&**parser),
3384 _ => {
3386 if self.is_prompting_or_unloading() ||
3389 self.ignore_destructive_writes_counter.get() > 0
3390 {
3391 return Ok(());
3392 }
3393 self.Open(cx, None, None)?;
3395 self.get_current_parser().unwrap()
3396 },
3397 };
3398
3399 parser.write(cx, string.into());
3401
3402 Ok(())
3403 }
3404
3405 pub(crate) fn details_name_groups(&self) -> RefMut<'_, DetailsNameGroups> {
3406 RefMut::map(
3407 self.details_name_groups.borrow_mut(),
3408 |details_name_groups| details_name_groups.get_or_insert_default(),
3409 )
3410 }
3411}
3412
3413#[derive(MallocSizeOf, PartialEq)]
3414pub(crate) enum DocumentSource {
3415 FromParser,
3416 NotFromParser,
3417}
3418
3419#[expect(unsafe_code)]
3420impl<'dom> LayoutDom<'dom, Document> {
3421 #[inline]
3422 pub(crate) fn is_html_document_for_layout(&self) -> bool {
3423 self.unsafe_get().is_html_document
3424 }
3425
3426 #[inline]
3427 pub(crate) fn quirks_mode(self) -> QuirksMode {
3428 self.unsafe_get().quirks_mode.get()
3429 }
3430
3431 #[inline]
3432 pub(crate) fn shared_style_locks(self) -> &'dom SharedRwLocks {
3433 self.unsafe_get().shared_style_locks()
3434 }
3435
3436 #[inline]
3437 pub(crate) fn flush_shadow_root_stylesheets_if_necessary(
3438 self,
3439 stylist: &mut Stylist,
3440 guard: &SharedRwLockReadGuard,
3441 ) {
3442 (*self.unsafe_get()).flush_shadow_root_stylesheets_if_necessary_for_layout(stylist, guard)
3443 }
3444
3445 #[inline]
3446 pub(crate) fn shadow_roots_styles_changed(self) -> bool {
3447 self.unsafe_get().shadow_roots_styles_changed.get()
3448 }
3449
3450 pub(crate) fn elements_with_id(self, id: &Atom) -> &[LayoutDom<'dom, Element>] {
3451 let id_map = unsafe { self.unsafe_get().id_map.borrow_for_layout() };
3452 let matching_elements = id_map.get(id).map(Vec::as_slice).unwrap_or_default();
3453 unsafe { LayoutDom::to_layout_slice(matching_elements) }
3454 }
3455}
3456
3457pub(crate) fn get_registrable_domain_suffix_of_or_is_equal_to(
3461 host_suffix_string: &str,
3462 original_host: Host,
3463) -> Option<Host> {
3464 if host_suffix_string.is_empty() {
3466 return None;
3467 }
3468
3469 let host = match Host::parse(host_suffix_string) {
3471 Ok(host) => host,
3472 Err(_) => return None,
3473 };
3474
3475 if host != original_host {
3477 let host = match host {
3479 Host::Domain(ref host) => host,
3480 _ => return None,
3481 };
3482 let original_host = match original_host {
3483 Host::Domain(ref original_host) => original_host,
3484 _ => return None,
3485 };
3486
3487 let index = original_host.len().checked_sub(host.len())?;
3489 let (prefix, suffix) = original_host.split_at(index);
3490
3491 if !prefix.ends_with('.') {
3492 return None;
3493 }
3494 if suffix != host {
3495 return None;
3496 }
3497
3498 if is_pub_domain(host) {
3500 return None;
3501 }
3502 }
3503
3504 Some(host)
3506}
3507
3508fn url_has_network_scheme(url: &ServoUrl) -> bool {
3510 matches!(url.scheme(), "ftp" | "http" | "https")
3511}
3512
3513#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
3514pub(crate) enum HasBrowsingContext {
3515 No,
3516 Yes,
3517}
3518
3519impl Document {
3520 #[allow(clippy::too_many_arguments)]
3521 pub(crate) fn new_inherited(
3522 window: &Window,
3523 has_browsing_context: HasBrowsingContext,
3524 url: Option<ServoUrl>,
3525 about_base_url: Option<ServoUrl>,
3526 origin: MutableOrigin,
3527 is_html_document: IsHTMLDocument,
3528 content_type: Option<Mime>,
3529 last_modified: Option<String>,
3530 activity: DocumentActivity,
3531 source: DocumentSource,
3532 doc_loader: DocumentLoader,
3533 referrer: Option<String>,
3534 status_code: Option<u16>,
3535 canceller: FetchCanceller,
3536 is_initial_about_blank: bool,
3537 allow_declarative_shadow_roots: bool,
3538 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
3539 has_trustworthy_ancestor_origin: bool,
3540 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
3541 creation_sandboxing_flag_set: SandboxingFlagSet,
3542 can_gc: CanGc,
3543 ) -> Document {
3544 let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
3545
3546 let (ready_state, domcontentloaded_dispatched) = if source == DocumentSource::FromParser {
3547 (DocumentReadyState::Loading, false)
3548 } else {
3549 (DocumentReadyState::Complete, true)
3550 };
3551
3552 let frame_type = match window.is_top_level() {
3553 true => TimerMetadataFrameType::RootWindow,
3554 false => TimerMetadataFrameType::IFrame,
3555 };
3556 let interactive_time = ProgressiveWebMetrics::new(
3557 window.time_profiler_chan().clone(),
3558 url.clone(),
3559 frame_type,
3560 );
3561
3562 let content_type = content_type.unwrap_or_else(|| {
3563 match is_html_document {
3564 IsHTMLDocument::HTMLDocument => "text/html",
3566 IsHTMLDocument::NonHTMLDocument => "application/xml",
3568 }
3569 .parse()
3570 .unwrap()
3571 });
3572
3573 let encoding = content_type
3574 .get_parameter(CHARSET)
3575 .and_then(|charset| Encoding::for_label(charset.as_bytes()))
3576 .unwrap_or(UTF_8);
3577
3578 let has_focus = window.parent_info().is_none();
3579 let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
3580 let shared_style_locks = window.script_thread().shared_style_locks().clone();
3581
3582 Document {
3583 node: Node::new_document_node(),
3584 document_or_shadow_root: DocumentOrShadowRoot::new(window),
3585 window: Dom::from_ref(window),
3586 has_browsing_context,
3587 implementation: Default::default(),
3588 content_type,
3589 last_modified,
3590 url: DomRefCell::new(url),
3591 about_base_url: DomRefCell::new(about_base_url),
3592 quirks_mode: Cell::new(QuirksMode::NoQuirks),
3594 event_handler: DocumentEventHandler::new(window),
3595 focus_handler: DocumentFocusHandler::new(window, has_focus),
3596 embedder_controls: DocumentEmbedderControls::new(window),
3597 id_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3598 name_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3599 encoding: Cell::new(encoding),
3601 is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
3602 activity: Cell::new(activity),
3603 tag_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3604 tagns_map: DomRefCell::new(HashMapTracedValues::new_fx()),
3605 classes_map: DomRefCell::new(HashMapTracedValues::new()),
3606 images: Default::default(),
3607 embeds: Default::default(),
3608 links: Default::default(),
3609 forms: Default::default(),
3610 scripts: Default::default(),
3611 anchors: Default::default(),
3612 applets: Default::default(),
3613 iframes: RefCell::new(IFrameCollection::new()),
3614 shared_style_locks,
3615 stylesheets: DomRefCell::new(DocumentStylesheetSet::new()),
3616 stylesheet_list: MutNullableDom::new(None),
3617 ready_state: Cell::new(ready_state),
3618 domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched),
3619
3620 current_script: Default::default(),
3621 pending_parsing_blocking_script: Default::default(),
3622 script_blocking_stylesheets_count: Default::default(),
3623 render_blocking_element_count: Default::default(),
3624 deferred_scripts: Default::default(),
3625 asap_in_order_scripts_list: Default::default(),
3626 asap_scripts_set: Default::default(),
3627 animation_frame_ident: Cell::new(0),
3628 animation_frame_list: DomRefCell::new(VecDeque::new()),
3629 running_animation_callbacks: Cell::new(false),
3630 loader: DomRefCell::new(doc_loader),
3631 current_parser: Default::default(),
3632 base_element: Default::default(),
3633 target_base_element: Default::default(),
3634 appropriate_template_contents_owner_document: Default::default(),
3635 pending_restyles: DomRefCell::new(FxHashMap::default()),
3636 needs_restyle: Cell::new(RestyleReason::DOMChanged),
3637 dom_interactive: Cell::new(Default::default()),
3638 dom_content_loaded_event_start: Cell::new(Default::default()),
3639 dom_content_loaded_event_end: Cell::new(Default::default()),
3640 dom_complete: Cell::new(Default::default()),
3641 top_level_dom_complete: Cell::new(Default::default()),
3642 load_event_start: Cell::new(Default::default()),
3643 load_event_end: Cell::new(Default::default()),
3644 unload_event_start: Cell::new(Default::default()),
3645 unload_event_end: Cell::new(Default::default()),
3646 origin: DomRefCell::new(origin),
3647 referrer,
3648 target_element: MutNullableDom::new(None),
3649 policy_container: DomRefCell::new(PolicyContainer::default()),
3650 preloaded_resources: Default::default(),
3651 ignore_destructive_writes_counter: Default::default(),
3652 ignore_opens_during_unload_counter: Default::default(),
3653 spurious_animation_frames: Cell::new(0),
3654 fullscreen_element: MutNullableDom::new(None),
3655 form_id_listener_map: Default::default(),
3656 interactive_time: DomRefCell::new(interactive_time),
3657 tti_window: DomRefCell::new(InteractiveWindow::default()),
3658 canceller,
3659 throw_on_dynamic_markup_insertion_counter: Cell::new(0),
3660 page_showing: Cell::new(false),
3661 salvageable: Cell::new(true),
3662 active_parser_was_aborted: Cell::new(false),
3663 fired_unload: Cell::new(false),
3664 responsive_images: Default::default(),
3665 resource_fetch_timing: RefCell::new(None),
3666 completely_loaded: Cell::new(false),
3667 script_and_layout_blockers: Cell::new(0),
3668 delayed_tasks: Default::default(),
3669 shadow_roots: DomRefCell::new(HashSet::new()),
3670 shadow_roots_styles_changed: Cell::new(false),
3671 media_controls: DomRefCell::new(HashMap::new()),
3672 dirty_canvases: DomRefCell::new(Default::default()),
3673 has_pending_animated_image_update: Cell::new(false),
3674 selection: MutNullableDom::new(None),
3675 timeline: DocumentTimeline::new(window, can_gc).as_traced(),
3676 animations: Animations::new(),
3677 image_animation_manager: DomRefCell::new(ImageAnimationManager::default()),
3678 dirty_root: Default::default(),
3679 declarative_refresh: Default::default(),
3680 resize_observers: Default::default(),
3681 fonts: Default::default(),
3682 visibility_state: Cell::new(DocumentVisibilityState::Hidden),
3683 status_code,
3684 is_initial_about_blank: Cell::new(is_initial_about_blank),
3685 allow_declarative_shadow_roots: Cell::new(allow_declarative_shadow_roots),
3686 inherited_insecure_requests_policy: Cell::new(inherited_insecure_requests_policy),
3687 has_trustworthy_ancestor_origin: Cell::new(has_trustworthy_ancestor_origin),
3688 intersection_observer_task_queued: Cell::new(false),
3689 intersection_observers: Default::default(),
3690 highlighted_dom_node: Default::default(),
3691 adopted_stylesheets: Default::default(),
3692 adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
3693 pending_scroll_events: Default::default(),
3694 rendering_update_reasons: Default::default(),
3695 waiting_on_canvas_image_updates: Cell::new(false),
3696 root_removal_noted: Cell::new(true),
3697 current_rendering_epoch: Default::default(),
3698 custom_element_reaction_stack,
3699 active_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
3700 creation_sandboxing_flag_set: Cell::new(creation_sandboxing_flag_set),
3701 favicon: RefCell::new(None),
3702 websockets: DOMTracker::new(),
3703 details_name_groups: Default::default(),
3704 protocol_handler_automation_mode: Default::default(),
3705 layout_animations_test_enabled: pref!(layout_animations_test_enabled),
3706 state_override: Default::default(),
3707 value_override: Default::default(),
3708 default_single_line_container_name: Default::default(),
3709 css_styling_flag: Default::default(),
3710 }
3711 }
3712
3713 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
3715 if let Some(csp_list) = self.get_csp_list().as_ref() {
3716 for policy in &csp_list.0 {
3717 if policy.contains_a_directive_whose_name_is("upgrade-insecure-requests") &&
3718 policy.disposition == PolicyDisposition::Enforce
3719 {
3720 return InsecureRequestsPolicy::Upgrade;
3721 }
3722 }
3723 }
3724
3725 self.inherited_insecure_requests_policy
3726 .get()
3727 .unwrap_or(InsecureRequestsPolicy::DoNotUpgrade)
3728 }
3729
3730 pub(crate) fn event_handler(&self) -> &DocumentEventHandler {
3732 &self.event_handler
3733 }
3734
3735 pub(crate) fn focus_handler(&self) -> &DocumentFocusHandler {
3737 &self.focus_handler
3738 }
3739
3740 pub(crate) fn embedder_controls(&self) -> &DocumentEmbedderControls {
3742 &self.embedder_controls
3743 }
3744
3745 fn has_pending_scroll_events(&self) -> bool {
3748 !self.pending_scroll_events.borrow().is_empty()
3749 }
3750
3751 pub(crate) fn add_rendering_update_reason(&self, reason: RenderingUpdateReason) {
3754 self.rendering_update_reasons
3755 .set(self.rendering_update_reasons.get().union(reason));
3756 }
3757
3758 pub(crate) fn clear_rendering_update_reasons(&self) {
3760 self.rendering_update_reasons
3761 .set(RenderingUpdateReason::empty())
3762 }
3763
3764 pub(crate) fn add_script_and_layout_blocker(&self) {
3771 self.script_and_layout_blockers
3772 .set(self.script_and_layout_blockers.get() + 1);
3773 }
3774
3775 pub(crate) fn remove_script_and_layout_blocker(&self, cx: &mut js::context::JSContext) {
3779 assert!(self.script_and_layout_blockers.get() > 0);
3780 self.script_and_layout_blockers
3781 .set(self.script_and_layout_blockers.get() - 1);
3782 while self.script_and_layout_blockers.get() == 0 && !self.delayed_tasks.borrow().is_empty()
3783 {
3784 let task = self.delayed_tasks.borrow_mut().remove(0);
3785 task.run_box(cx);
3786 }
3787 }
3788
3789 pub(crate) fn add_delayed_task<T: 'static + NonSendTaskBox>(&self, task: T) {
3791 self.delayed_tasks.borrow_mut().push(Box::new(task));
3792 }
3793
3794 pub(crate) fn ensure_safe_to_run_script_or_layout(&self) {
3797 assert_eq!(
3798 self.script_and_layout_blockers.get(),
3799 0,
3800 "Attempt to use script or layout while DOM not in a stable state"
3801 );
3802 }
3803
3804 #[allow(clippy::too_many_arguments)]
3805 pub(crate) fn new(
3806 window: &Window,
3807 has_browsing_context: HasBrowsingContext,
3808 url: Option<ServoUrl>,
3809 about_base_url: Option<ServoUrl>,
3810 origin: MutableOrigin,
3811 doctype: IsHTMLDocument,
3812 content_type: Option<Mime>,
3813 last_modified: Option<String>,
3814 activity: DocumentActivity,
3815 source: DocumentSource,
3816 doc_loader: DocumentLoader,
3817 referrer: Option<String>,
3818 status_code: Option<u16>,
3819 canceller: FetchCanceller,
3820 is_initial_about_blank: bool,
3821 allow_declarative_shadow_roots: bool,
3822 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
3823 has_trustworthy_ancestor_origin: bool,
3824 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
3825 creation_sandboxing_flag_set: SandboxingFlagSet,
3826 can_gc: CanGc,
3827 ) -> DomRoot<Document> {
3828 Self::new_with_proto(
3829 window,
3830 None,
3831 has_browsing_context,
3832 url,
3833 about_base_url,
3834 origin,
3835 doctype,
3836 content_type,
3837 last_modified,
3838 activity,
3839 source,
3840 doc_loader,
3841 referrer,
3842 status_code,
3843 canceller,
3844 is_initial_about_blank,
3845 allow_declarative_shadow_roots,
3846 inherited_insecure_requests_policy,
3847 has_trustworthy_ancestor_origin,
3848 custom_element_reaction_stack,
3849 creation_sandboxing_flag_set,
3850 can_gc,
3851 )
3852 }
3853
3854 #[allow(clippy::too_many_arguments)]
3855 fn new_with_proto(
3856 window: &Window,
3857 proto: Option<HandleObject>,
3858 has_browsing_context: HasBrowsingContext,
3859 url: Option<ServoUrl>,
3860 about_base_url: Option<ServoUrl>,
3861 origin: MutableOrigin,
3862 doctype: IsHTMLDocument,
3863 content_type: Option<Mime>,
3864 last_modified: Option<String>,
3865 activity: DocumentActivity,
3866 source: DocumentSource,
3867 doc_loader: DocumentLoader,
3868 referrer: Option<String>,
3869 status_code: Option<u16>,
3870 canceller: FetchCanceller,
3871 is_initial_about_blank: bool,
3872 allow_declarative_shadow_roots: bool,
3873 inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
3874 has_trustworthy_ancestor_origin: bool,
3875 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
3876 creation_sandboxing_flag_set: SandboxingFlagSet,
3877 can_gc: CanGc,
3878 ) -> DomRoot<Document> {
3879 let document = reflect_dom_object_with_proto(
3880 Box::new(Document::new_inherited(
3881 window,
3882 has_browsing_context,
3883 url,
3884 about_base_url,
3885 origin,
3886 doctype,
3887 content_type,
3888 last_modified,
3889 activity,
3890 source,
3891 doc_loader,
3892 referrer,
3893 status_code,
3894 canceller,
3895 is_initial_about_blank,
3896 allow_declarative_shadow_roots,
3897 inherited_insecure_requests_policy,
3898 has_trustworthy_ancestor_origin,
3899 custom_element_reaction_stack,
3900 creation_sandboxing_flag_set,
3901 can_gc,
3902 )),
3903 window,
3904 proto,
3905 can_gc,
3906 );
3907 {
3908 let node = document.upcast::<Node>();
3909 node.set_owner_doc(&document);
3910 }
3911 document
3912 }
3913
3914 pub(crate) fn get_redirect_count(&self) -> u16 {
3915 self.resource_fetch_timing
3916 .borrow()
3917 .as_ref()
3918 .map_or(0, |resource_fetch_timing| {
3919 resource_fetch_timing.redirect_count
3920 })
3921 }
3922
3923 pub(crate) fn set_resource_fetch_timing(&self, timing: ResourceFetchTiming) {
3924 self.resource_fetch_timing.replace(Some(timing));
3925 }
3926
3927 pub(crate) fn performance_timing_attribute(
3928 &self,
3929 name: &str,
3930 ) -> Fallible<Option<CrossProcessInstant>> {
3931 Ok(match name {
3932 "unloadEventStart" => self.get_unload_event_start(),
3933 "unloadEventEnd" => self.get_unload_event_end(),
3934 "domInteractive" => self.get_dom_interactive(),
3935 "domContentLoadedEventStart" => self.get_dom_content_loaded_event_start(),
3936 "domContentLoadedEventEnd" => self.get_dom_content_loaded_event_end(),
3937 "domComplete" => self.get_dom_complete(),
3938 "loadEventStart" => self.get_load_event_start(),
3939 "loadEventEnd" => self.get_load_event_end(),
3940 "redirectStart" | "redirectEnd" | "secureConnectionStart" | "responseEnd" => self
3941 .resource_fetch_timing
3942 .borrow()
3943 .as_ref()
3944 .and_then(|resource_fetch_timing| match name {
3945 "redirectStart" => resource_fetch_timing.redirect_start,
3946 "redirectEnd" => resource_fetch_timing.redirect_end,
3947 "secureConnectionStart" => resource_fetch_timing.secure_connection_start,
3948 "responseEnd" => resource_fetch_timing.response_end,
3949 _ => None,
3950 }),
3951 _ => {
3952 return Err(Error::Operation(Some(format!(
3953 "{name} hasn't been implemented."
3954 ))));
3955 },
3956 })
3957 }
3958
3959 pub(crate) fn elements_by_name_count(&self, name: &DOMString) -> u32 {
3960 if name.is_empty() {
3961 return 0;
3962 }
3963 self.count_node_list(|n| Document::is_element_in_get_by_name(n, name))
3964 }
3965
3966 pub(crate) fn nth_element_by_name(
3967 &self,
3968 index: u32,
3969 name: &DOMString,
3970 ) -> Option<DomRoot<Node>> {
3971 if name.is_empty() {
3972 return None;
3973 }
3974 self.nth_in_node_list(index, |n| Document::is_element_in_get_by_name(n, name))
3975 }
3976
3977 fn is_element_in_get_by_name(node: &Node, name: &DOMString) -> bool {
3980 let element = match node.downcast::<Element>() {
3981 Some(element) => element,
3982 None => return false,
3983 };
3984 if element.namespace() != &ns!(html) {
3985 return false;
3986 }
3987 element.get_name().is_some_and(|n| &*n == name)
3988 }
3989
3990 fn count_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> u32 {
3991 let doc = self.GetDocumentElement();
3992 let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
3993 maybe_node
3994 .iter()
3995 .flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
3996 .filter(|node| callback(node))
3997 .count() as u32
3998 }
3999
4000 fn nth_in_node_list<F: Fn(&Node) -> bool>(
4001 &self,
4002 index: u32,
4003 callback: F,
4004 ) -> Option<DomRoot<Node>> {
4005 let doc = self.GetDocumentElement();
4006 let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
4007 maybe_node
4008 .iter()
4009 .flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
4010 .filter(|node| callback(node))
4011 .nth(index as usize)
4012 .map(|n| DomRoot::from_ref(&*n))
4013 }
4014
4015 fn get_html_element(&self) -> Option<DomRoot<HTMLHtmlElement>> {
4016 self.GetDocumentElement().and_then(DomRoot::downcast)
4017 }
4018
4019 pub(crate) fn shared_style_locks(&self) -> &SharedRwLocks {
4021 &self.shared_style_locks
4022 }
4023
4024 pub(crate) fn style_shared_author_lock(&self) -> &SharedRwLock {
4026 &self.shared_style_locks.author
4027 }
4028
4029 pub(crate) fn flush_stylesheets_for_reflow(&self) -> bool {
4031 let mut stylesheets = self.stylesheets.borrow_mut();
4038 let have_changed = stylesheets.has_changed();
4039 stylesheets.flush_without_invalidation();
4040 have_changed
4041 }
4042
4043 pub(crate) fn salvageable(&self) -> bool {
4044 self.salvageable.get()
4045 }
4046
4047 pub(crate) fn make_document_unsalvageable(&self) {
4049 self.salvageable.set(false);
4055 }
4056
4057 pub(crate) fn appropriate_template_contents_owner_document(
4059 &self,
4060 can_gc: CanGc,
4061 ) -> DomRoot<Document> {
4062 self.appropriate_template_contents_owner_document
4063 .or_init(|| {
4064 let doctype = if self.is_html_document {
4065 IsHTMLDocument::HTMLDocument
4066 } else {
4067 IsHTMLDocument::NonHTMLDocument
4068 };
4069 let new_doc = Document::new(
4070 self.window(),
4071 HasBrowsingContext::No,
4072 None,
4073 None,
4074 MutableOrigin::new(ImmutableOrigin::new_opaque()),
4076 doctype,
4077 None,
4078 None,
4079 DocumentActivity::Inactive,
4080 DocumentSource::NotFromParser,
4081 DocumentLoader::new(&self.loader()),
4082 None,
4083 None,
4084 Default::default(),
4085 false,
4086 self.allow_declarative_shadow_roots(),
4087 Some(self.insecure_requests_policy()),
4088 self.has_trustworthy_ancestor_or_current_origin(),
4089 self.custom_element_reaction_stack.clone(),
4090 self.creation_sandboxing_flag_set(),
4091 can_gc,
4092 );
4093 new_doc
4094 .appropriate_template_contents_owner_document
4095 .set(Some(&new_doc));
4096 new_doc
4097 })
4098 }
4099
4100 pub(crate) fn get_element_by_id(&self, id: &Atom) -> Option<DomRoot<Element>> {
4101 self.id_map
4102 .borrow()
4103 .get(id)
4104 .map(|elements| DomRoot::from_ref(&*elements[0]))
4105 }
4106
4107 pub(crate) fn ensure_pending_restyle(&self, el: &Element) -> RefMut<'_, PendingRestyle> {
4108 let map = self.pending_restyles.borrow_mut();
4109 RefMut::map(map, |m| {
4110 &mut m
4111 .entry(Dom::from_ref(el))
4112 .or_insert_with(|| NoTrace(PendingRestyle::default()))
4113 .0
4114 })
4115 }
4116
4117 pub(crate) fn element_attr_will_change(&self, el: &Element, attr: AttrRef<'_>) {
4118 let mut entry = self.ensure_pending_restyle(el);
4124 if entry.snapshot.is_none() {
4125 entry.snapshot = Some(Snapshot::new());
4126 }
4127 if attr.local_name() == &local_name!("style") {
4128 entry.hint.insert(RestyleHint::RESTYLE_STYLE_ATTRIBUTE);
4129 }
4130
4131 if vtable_for(el.upcast()).attribute_affects_presentational_hints(attr) {
4132 entry.hint.insert(RestyleHint::RESTYLE_SELF);
4133 }
4134
4135 let snapshot = entry.snapshot.as_mut().unwrap();
4136 if attr.local_name() == &local_name!("id") {
4137 if snapshot.id_changed {
4138 return;
4139 }
4140 snapshot.id_changed = true;
4141 } else if attr.local_name() == &local_name!("class") {
4142 if snapshot.class_changed {
4143 return;
4144 }
4145 snapshot.class_changed = true;
4146 } else {
4147 snapshot.other_attributes_changed = true;
4148 }
4149 let local_name = style::LocalName::cast(attr.local_name());
4150 if !snapshot.changed_attrs.contains(local_name) {
4151 snapshot.changed_attrs.push(local_name.clone());
4152 }
4153 if snapshot.attrs.is_none() {
4154 let attrs = el
4155 .attrs()
4156 .borrow()
4157 .iter()
4158 .map(|attr| (attr.identifier().clone(), attr.value().clone()))
4159 .collect();
4160 snapshot.attrs = Some(attrs);
4161 }
4162 }
4163
4164 pub(crate) fn set_referrer_policy(&self, policy: ReferrerPolicy) {
4165 self.policy_container
4166 .borrow_mut()
4167 .set_referrer_policy(policy);
4168 }
4169
4170 pub(crate) fn get_referrer_policy(&self) -> ReferrerPolicy {
4171 self.policy_container.borrow().get_referrer_policy()
4172 }
4173
4174 pub(crate) fn set_target_element(&self, node: Option<&Element>) {
4175 if let Some(ref element) = self.target_element.get() {
4176 element.set_target_state(false);
4177 }
4178
4179 self.target_element.set(node);
4180
4181 if let Some(ref element) = self.target_element.get() {
4182 element.set_target_state(true);
4183 }
4184 }
4185
4186 pub(crate) fn incr_ignore_destructive_writes_counter(&self) {
4187 self.ignore_destructive_writes_counter
4188 .set(self.ignore_destructive_writes_counter.get() + 1);
4189 }
4190
4191 pub(crate) fn decr_ignore_destructive_writes_counter(&self) {
4192 self.ignore_destructive_writes_counter
4193 .set(self.ignore_destructive_writes_counter.get() - 1);
4194 }
4195
4196 pub(crate) fn is_prompting_or_unloading(&self) -> bool {
4197 self.ignore_opens_during_unload_counter.get() > 0
4198 }
4199
4200 fn incr_ignore_opens_during_unload_counter(&self) {
4201 self.ignore_opens_during_unload_counter
4202 .set(self.ignore_opens_during_unload_counter.get() + 1);
4203 }
4204
4205 fn decr_ignore_opens_during_unload_counter(&self) {
4206 self.ignore_opens_during_unload_counter
4207 .set(self.ignore_opens_during_unload_counter.get() - 1);
4208 }
4209
4210 pub(crate) fn set_fullscreen_element(&self, element: Option<&Element>) {
4211 self.fullscreen_element.set(element);
4212 }
4213
4214 fn reset_form_owner_for_listeners(&self, cx: &mut js::context::JSContext, id: &Atom) {
4215 let map = self.form_id_listener_map.borrow();
4216 if let Some(listeners) = map.get(id) {
4217 for listener in listeners {
4218 listener
4219 .as_maybe_form_control()
4220 .expect("Element must be a form control")
4221 .reset_form_owner(cx);
4222 }
4223 }
4224 }
4225
4226 pub(crate) fn register_shadow_root(&self, shadow_root: &ShadowRoot) {
4227 self.shadow_roots
4228 .borrow_mut()
4229 .insert(Dom::from_ref(shadow_root));
4230 self.invalidate_shadow_roots_stylesheets();
4231 }
4232
4233 pub(crate) fn unregister_shadow_root(&self, shadow_root: &ShadowRoot) {
4234 let mut shadow_roots = self.shadow_roots.borrow_mut();
4235 shadow_roots.remove(&Dom::from_ref(shadow_root));
4236 }
4237
4238 pub(crate) fn invalidate_shadow_roots_stylesheets(&self) {
4239 self.shadow_roots_styles_changed.set(true);
4240 }
4241
4242 pub(crate) fn shadow_roots_styles_changed(&self) -> bool {
4243 self.shadow_roots_styles_changed.get()
4244 }
4245
4246 pub(crate) fn flush_shadow_root_stylesheets_if_necessary_for_layout(
4247 &self,
4248 stylist: &mut Stylist,
4249 guard: &SharedRwLockReadGuard,
4250 ) {
4251 if !self.shadow_roots_styles_changed.get() {
4252 return;
4253 }
4254 #[expect(unsafe_code)]
4255 unsafe {
4256 for shadow_root in self.shadow_roots.borrow_for_layout().iter() {
4257 shadow_root
4258 .to_layout()
4259 .flush_stylesheets_for_layout(stylist, guard);
4260 }
4261 }
4262 self.shadow_roots_styles_changed.set(false);
4263 }
4264
4265 pub(crate) fn stylesheet_count(&self) -> usize {
4266 self.stylesheets.borrow().len()
4267 }
4268
4269 pub(crate) fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
4270 let stylesheets = self.stylesheets.borrow();
4271
4272 stylesheets
4273 .get(Origin::Author, index)
4274 .and_then(|s| s.owner.get_cssom_object())
4275 }
4276
4277 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) {
4284 let stylesheets = &mut *self.stylesheets.borrow_mut();
4285
4286 let insertion_point = stylesheets
4288 .iter()
4289 .map(|(sheet, _origin)| sheet)
4290 .find(|sheet_in_doc| {
4291 match &sheet_in_doc.owner {
4292 StylesheetSource::Element(other_node) => {
4293 owner_node.upcast::<Node>().is_before(other_node.upcast())
4294 },
4295 StylesheetSource::Constructed(_) => true,
4298 }
4299 })
4300 .cloned();
4301
4302 if self.has_browsing_context() {
4303 let document_context = self.window.web_font_context();
4304
4305 self.window.layout_mut().add_stylesheet(
4306 sheet.clone(),
4307 insertion_point.as_ref().map(|s| s.sheet.clone()),
4308 &document_context,
4309 );
4310 }
4311
4312 DocumentOrShadowRoot::add_stylesheet(
4313 StylesheetSource::Element(Dom::from_ref(owner_node)),
4314 StylesheetSetRef::Document(stylesheets),
4315 sheet,
4316 insertion_point,
4317 self.style_shared_author_lock(),
4318 );
4319 }
4320
4321 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
4326 pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
4327 debug_assert!(cssom_stylesheet.is_constructed());
4328
4329 let stylesheets = &mut *self.stylesheets.borrow_mut();
4330 let sheet = cssom_stylesheet.style_stylesheet().clone();
4331
4332 let insertion_point = stylesheets
4333 .iter()
4334 .last()
4335 .map(|(sheet, _origin)| sheet)
4336 .cloned();
4337
4338 if self.has_browsing_context() {
4339 self.window.layout_mut().add_stylesheet(
4340 sheet.clone(),
4341 insertion_point.as_ref().map(|s| s.sheet.clone()),
4342 &self.window.web_font_context(),
4343 );
4344 }
4345
4346 DocumentOrShadowRoot::add_stylesheet(
4347 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
4348 StylesheetSetRef::Document(stylesheets),
4349 sheet,
4350 insertion_point,
4351 self.style_shared_author_lock(),
4352 );
4353 }
4354
4355 pub(crate) fn load_web_fonts_from_stylesheet(
4357 &self,
4358 stylesheet: &Arc<Stylesheet>,
4359 document_context: &WebFontDocumentContext,
4360 ) {
4361 self.window
4362 .layout()
4363 .load_web_fonts_from_stylesheet(stylesheet, document_context);
4364 }
4365
4366 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, stylesheet: &Arc<Stylesheet>) {
4369 if self.has_browsing_context() {
4370 self.window
4371 .layout_mut()
4372 .remove_stylesheet(stylesheet.clone());
4373 }
4374
4375 DocumentOrShadowRoot::remove_stylesheet(
4376 owner,
4377 stylesheet,
4378 StylesheetSetRef::Document(&mut *self.stylesheets.borrow_mut()),
4379 )
4380 }
4381
4382 pub(crate) fn get_elements_with_id(&self, id: &Atom) -> Ref<'_, [Dom<Element>]> {
4383 Ref::map(self.id_map.borrow(), |map| {
4384 map.get(id).map(|vec| &**vec).unwrap_or_default()
4385 })
4386 }
4387
4388 pub(crate) fn get_elements_with_name(&self, name: &Atom) -> Ref<'_, [Dom<Element>]> {
4389 Ref::map(self.name_map.borrow(), |map| {
4390 map.get(name).map(|vec| &**vec).unwrap_or_default()
4391 })
4392 }
4393
4394 pub(crate) fn drain_pending_restyles(&self) -> Vec<(TrustedNodeAddress, PendingRestyle)> {
4395 self.pending_restyles
4396 .borrow_mut()
4397 .drain()
4398 .filter_map(|(elem, restyle)| {
4399 let node = elem.upcast::<Node>();
4400 if !node.get_flag(NodeFlags::IS_CONNECTED) {
4401 return None;
4402 }
4403 node.note_dirty_descendants();
4404 Some((node.to_trusted_node_address(), restyle.0))
4405 })
4406 .collect()
4407 }
4408
4409 pub(crate) fn advance_animation_timeline_for_testing(&self, delta: TimeDuration) {
4410 self.timeline.advance_specific(delta);
4411 let current_timeline_value = self.current_animation_timeline_value();
4412 self.animations
4413 .update_for_new_timeline_value(&self.window, current_timeline_value);
4414 }
4415
4416 pub(crate) fn maybe_mark_animating_nodes_as_dirty(&self) {
4417 let current_timeline_value = self.current_animation_timeline_value();
4418 self.animations
4419 .mark_animating_nodes_as_dirty(current_timeline_value);
4420 }
4421
4422 pub(crate) fn current_animation_timeline_value(&self) -> f64 {
4423 self.timeline
4424 .upcast::<AnimationTimeline>()
4425 .current_time_in_seconds()
4426 }
4427
4428 pub(crate) fn animations(&self) -> &Animations {
4429 &self.animations
4430 }
4431
4432 pub(crate) fn update_animations_post_reflow(&self) {
4433 let current_timeline_value = self.current_animation_timeline_value();
4434 self.animations
4435 .do_post_reflow_update(&self.window, current_timeline_value);
4436 self.image_animation_manager
4437 .borrow_mut()
4438 .do_post_reflow_update(&self.window, current_timeline_value);
4439 }
4440
4441 pub(crate) fn cancel_animations_for_node(&self, node: &Node) {
4442 self.animations.cancel_animations_for_node(node);
4443 self.image_animation_manager
4444 .borrow_mut()
4445 .cancel_animations_for_node(node);
4446 }
4447
4448 pub(crate) fn update_animations_and_send_events(&self, cx: &mut CurrentRealm) {
4450 if !self.layout_animations_test_enabled {
4452 self.timeline.update(self.window());
4453 }
4454
4455 let current_timeline_value = self.current_animation_timeline_value();
4462 self.animations
4463 .update_for_new_timeline_value(&self.window, current_timeline_value);
4464 self.maybe_mark_animating_nodes_as_dirty();
4465
4466 self.window().perform_a_microtask_checkpoint(cx);
4468
4469 self.animations().send_pending_events(self.window(), cx);
4471 }
4472
4473 pub(crate) fn image_animation_manager(&self) -> Ref<'_, ImageAnimationManager> {
4474 self.image_animation_manager.borrow()
4475 }
4476
4477 pub(crate) fn set_has_pending_animated_image_update(&self) {
4478 self.has_pending_animated_image_update.set(true);
4479 }
4480
4481 pub(crate) fn shared_declarative_refresh_steps(&self, content: &[u8], from_meta_element: bool) {
4483 if self.will_declaratively_refresh() {
4485 return;
4486 }
4487
4488 static REFRESH_REGEX: LazyLock<Regex> = LazyLock::new(|| {
4490 Regex::new(
4494 r#"(?xs)
4495 ^
4496 \s* # 3
4497 ((?<time>[0-9]+)|\.) # 5-6
4498 [0-9.]* # 8
4499 (
4500 (
4501 (\s*;|\s*,|\s) # 10.3
4502 \s* # 10.4
4503 )
4504 (
4505 (
4506 (U|u)(R|r)(L|l) # 11.2-11.4
4507 \s*=\s* # 11.5-11.7
4508 )?
4509 ('(?<url1>[^']*)'(?s-u:.)*|"(?<url2>[^"]*)"(?s-u:.)*|['"]?(?<url3>(?s-u:.)*)) # 11.8 - 11.10
4510 |
4511 (?<url4>(?s-u:.)*)
4512 )
4513 )?
4514 $
4515 "#,
4516 )
4517 .unwrap()
4518 });
4519
4520 let mut url_record = self.url();
4522 let captures = if let Some(captures) = REFRESH_REGEX.captures(content) {
4523 captures
4524 } else {
4525 return;
4526 };
4527 let time = if let Some(time_string) = captures.name("time") {
4528 u64::from_str(&String::from_utf8_lossy(time_string.as_bytes())).unwrap_or(0)
4529 } else {
4530 0
4531 };
4532 let captured_url = captures.name("url1").or(captures
4533 .name("url2")
4534 .or(captures.name("url3").or(captures.name("url4"))));
4535
4536 if let Some(url_match) = captured_url {
4538 url_record = if let Ok(url) = ServoUrl::parse_with_base(
4539 Some(&url_record),
4540 &String::from_utf8_lossy(url_match.as_bytes()),
4541 ) {
4542 info!("Refresh to {}", url.debug_compact());
4543 url
4544 } else {
4545 return;
4547 };
4548 if url_record.scheme() == "javascript" {
4550 return;
4551 }
4552 }
4553 if self.completely_loaded() {
4555 self.window.as_global_scope().schedule_callback(
4556 OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
4557 window: DomRoot::from_ref(self.window()),
4558 url: url_record,
4559 from_meta_element,
4560 }),
4561 Duration::from_secs(time),
4562 );
4563 self.set_declarative_refresh(DeclarativeRefresh::CreatedAfterLoad);
4564 } else {
4565 self.set_declarative_refresh(DeclarativeRefresh::PendingLoad {
4566 url: url_record,
4567 time,
4568 from_meta_element,
4569 });
4570 }
4571 }
4572
4573 pub(crate) fn will_declaratively_refresh(&self) -> bool {
4574 self.declarative_refresh.borrow().is_some()
4575 }
4576 pub(crate) fn set_declarative_refresh(&self, refresh: DeclarativeRefresh) {
4577 *self.declarative_refresh.borrow_mut() = Some(refresh);
4578 }
4579
4580 fn update_visibility_state(
4582 &self,
4583 cx: &mut js::context::JSContext,
4584 visibility_state: DocumentVisibilityState,
4585 ) {
4586 if self.visibility_state.get() == visibility_state {
4588 return;
4589 }
4590 self.visibility_state.set(visibility_state);
4592 let entry = VisibilityStateEntry::new(
4595 &self.global(),
4596 visibility_state,
4597 CrossProcessInstant::now(),
4598 CanGc::from_cx(cx),
4599 );
4600 self.window
4601 .Performance()
4602 .queue_entry(entry.upcast::<PerformanceEntry>());
4603
4604 #[cfg(feature = "gamepad")]
4615 if visibility_state == DocumentVisibilityState::Hidden {
4616 self.window
4617 .Navigator()
4618 .GetGamepads()
4619 .iter_mut()
4620 .for_each(|gamepad| {
4621 if let Some(g) = gamepad {
4622 g.vibration_actuator().handle_visibility_change();
4623 }
4624 });
4625 }
4626
4627 self.upcast::<EventTarget>()
4629 .fire_bubbling_event(cx, atom!("visibilitychange"));
4630 }
4631
4632 pub(crate) fn is_initial_about_blank(&self) -> bool {
4634 self.is_initial_about_blank.get()
4635 }
4636
4637 pub(crate) fn allow_declarative_shadow_roots(&self) -> bool {
4639 self.allow_declarative_shadow_roots.get()
4640 }
4641
4642 pub(crate) fn has_trustworthy_ancestor_origin(&self) -> bool {
4643 self.has_trustworthy_ancestor_origin.get()
4644 }
4645
4646 pub(crate) fn has_trustworthy_ancestor_or_current_origin(&self) -> bool {
4647 self.has_trustworthy_ancestor_origin.get() ||
4648 self.origin().immutable().is_potentially_trustworthy()
4649 }
4650
4651 pub(crate) fn highlight_dom_node(&self, node: Option<&Node>) {
4652 self.highlighted_dom_node.set(node);
4653 self.add_restyle_reason(RestyleReason::HighlightedDOMNodeChanged);
4654 }
4655
4656 pub(crate) fn highlighted_dom_node(&self) -> Option<DomRoot<Node>> {
4657 self.highlighted_dom_node.get()
4658 }
4659
4660 pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
4661 self.custom_element_reaction_stack.clone()
4662 }
4663
4664 pub(crate) fn active_sandboxing_flag_set(&self) -> SandboxingFlagSet {
4665 self.active_sandboxing_flag_set.get()
4666 }
4667
4668 pub(crate) fn has_active_sandboxing_flag(&self, flag: SandboxingFlagSet) -> bool {
4669 self.active_sandboxing_flag_set.get().contains(flag)
4670 }
4671
4672 pub(crate) fn set_active_sandboxing_flag_set(&self, flags: SandboxingFlagSet) {
4673 self.active_sandboxing_flag_set.set(flags)
4674 }
4675
4676 pub(crate) fn creation_sandboxing_flag_set(&self) -> SandboxingFlagSet {
4677 self.creation_sandboxing_flag_set.get()
4678 }
4679
4680 pub(crate) fn creation_sandboxing_flag_set_considering_parent_iframe(
4681 &self,
4682 ) -> SandboxingFlagSet {
4683 self.window()
4684 .window_proxy()
4685 .frame_element()
4686 .and_then(|element| element.downcast::<HTMLIFrameElement>())
4687 .map(HTMLIFrameElement::sandboxing_flag_set)
4688 .unwrap_or_else(|| self.creation_sandboxing_flag_set())
4689 }
4690
4691 pub(crate) fn viewport_scrolling_box(&self, flags: ScrollContainerQueryFlags) -> ScrollingBox {
4692 self.window()
4693 .scrolling_box_query(None, flags)
4694 .expect("We should always have a ScrollingBox for the Viewport")
4695 }
4696
4697 pub(crate) fn notify_embedder_favicon(&self) {
4698 if let Some(ref image) = *self.favicon.borrow() {
4699 self.send_to_embedder(EmbedderMsg::NewFavicon(self.webview_id(), image.clone()));
4700 }
4701 }
4702
4703 pub(crate) fn set_favicon(&self, favicon: Image) {
4704 *self.favicon.borrow_mut() = Some(favicon);
4705 self.notify_embedder_favicon();
4706 }
4707
4708 pub(crate) fn fullscreen_element(&self) -> Option<DomRoot<Element>> {
4709 self.fullscreen_element.get()
4710 }
4711
4712 pub(crate) fn state_override(&self, command_name: &CommandName) -> Option<bool> {
4714 self.state_override.borrow().get(command_name).copied()
4715 }
4716
4717 pub(crate) fn set_state_override(&self, command_name: CommandName, state: Option<bool>) {
4719 if let Some(state) = state {
4720 self.state_override.borrow_mut().insert(command_name, state);
4721 } else {
4722 self.value_override.borrow_mut().remove(&command_name);
4723 }
4724 }
4725
4726 pub(crate) fn value_override(&self, command_name: &CommandName) -> Option<DOMString> {
4728 self.value_override.borrow().get(command_name).cloned()
4729 }
4730
4731 pub(crate) fn set_value_override(&self, command_name: CommandName, value: Option<DOMString>) {
4733 if let Some(value) = value {
4734 self.value_override.borrow_mut().insert(command_name, value);
4735 } else {
4736 self.value_override.borrow_mut().remove(&command_name);
4737 }
4738 }
4739
4740 pub(crate) fn clear_command_overrides(&self) {
4743 self.state_override.borrow_mut().clear();
4744 self.value_override.borrow_mut().clear();
4745 }
4746
4747 pub(crate) fn default_single_line_container_name(&self) -> DefaultSingleLineContainerName {
4749 self.default_single_line_container_name.get()
4750 }
4751
4752 pub(crate) fn set_default_single_line_container_name(
4754 &self,
4755 value: DefaultSingleLineContainerName,
4756 ) {
4757 self.default_single_line_container_name.set(value)
4758 }
4759
4760 pub(crate) fn css_styling_flag(&self) -> bool {
4762 self.css_styling_flag.get()
4763 }
4764
4765 pub(crate) fn set_css_styling_flag(&self, value: bool) {
4767 self.css_styling_flag.set(value)
4768 }
4769}
4770
4771impl DocumentMethods<crate::DomTypeHolder> for Document {
4772 fn Constructor(
4774 window: &Window,
4775 proto: Option<HandleObject>,
4776 can_gc: CanGc,
4777 ) -> Fallible<DomRoot<Document>> {
4778 let doc = window.Document();
4780 let docloader = DocumentLoader::new(&doc.loader());
4781 Ok(Document::new_with_proto(
4782 window,
4783 proto,
4784 HasBrowsingContext::No,
4785 None,
4786 None,
4787 doc.origin().clone(),
4788 IsHTMLDocument::NonHTMLDocument,
4789 None,
4790 None,
4791 DocumentActivity::Inactive,
4792 DocumentSource::NotFromParser,
4793 docloader,
4794 None,
4795 None,
4796 Default::default(),
4797 false,
4798 doc.allow_declarative_shadow_roots(),
4799 Some(doc.insecure_requests_policy()),
4800 doc.has_trustworthy_ancestor_or_current_origin(),
4801 doc.custom_element_reaction_stack(),
4802 doc.active_sandboxing_flag_set.get(),
4803 can_gc,
4804 ))
4805 }
4806
4807 fn ParseHTMLUnsafe(
4809 cx: &mut js::context::JSContext,
4810 window: &Window,
4811 s: TrustedHTMLOrString,
4812 options: &SetHTMLUnsafeOptions,
4813 ) -> Fallible<DomRoot<Self>> {
4814 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
4818 cx,
4819 window.as_global_scope(),
4820 s,
4821 "Document parseHTMLUnsafe",
4822 )?;
4823
4824 let url = window.get_url();
4825 let doc = window.Document();
4826 let loader = DocumentLoader::new(&doc.loader());
4827
4828 let content_type = "text/html"
4829 .parse()
4830 .expect("Supported type is not a MIME type");
4831 let document = Document::new(
4834 window,
4835 HasBrowsingContext::No,
4836 Some(ServoUrl::parse("about:blank").unwrap()),
4837 None,
4838 doc.origin().clone(),
4839 IsHTMLDocument::HTMLDocument,
4840 Some(content_type),
4841 None,
4842 DocumentActivity::Inactive,
4843 DocumentSource::FromParser,
4844 loader,
4845 None,
4846 None,
4847 Default::default(),
4848 false,
4849 true,
4850 Some(doc.insecure_requests_policy()),
4851 doc.has_trustworthy_ancestor_or_current_origin(),
4852 doc.custom_element_reaction_stack(),
4853 doc.creation_sandboxing_flag_set(),
4854 CanGc::from_cx(cx),
4855 );
4856 ServoParser::parse_html_document(cx, &document, Some(compliant_html), url, None, None);
4858
4859 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, false)?;
4862
4863 sanitizer.sanitize(cx, document.upcast(), false)?;
4865
4866 document.set_ready_state(cx, DocumentReadyState::Complete);
4868 Ok(document)
4869 }
4870
4871 fn ParseHTML(
4873 cx: &mut js::context::JSContext,
4874 window: &Window,
4875 html: DOMString,
4876 options: &SetHTMLOptions,
4877 ) -> Fallible<DomRoot<Document>> {
4878 let url = window.get_url();
4881 let doc = window.Document();
4882 let loader = DocumentLoader::new(&doc.loader());
4883 let content_type = "text/html"
4884 .parse()
4885 .expect("Supported type is not a MIME type");
4886 let document = Document::new(
4887 window,
4888 HasBrowsingContext::No,
4889 Some(ServoUrl::parse("about:blank").unwrap()),
4890 None,
4891 doc.origin().clone(),
4892 IsHTMLDocument::HTMLDocument,
4893 Some(content_type),
4894 None,
4895 DocumentActivity::Inactive,
4896 DocumentSource::FromParser,
4897 loader,
4898 None,
4899 None,
4900 Default::default(),
4901 false,
4902 true,
4903 Some(doc.insecure_requests_policy()),
4904 doc.has_trustworthy_ancestor_or_current_origin(),
4905 doc.custom_element_reaction_stack(),
4906 doc.creation_sandboxing_flag_set(),
4907 CanGc::from_cx(cx),
4908 );
4909
4910 ServoParser::parse_html_document(cx, &document, Some(html), url, None, None);
4912
4913 let sanitizer = Sanitizer::get_sanitizer_instance_from_options(cx, window, options, true)?;
4916
4917 sanitizer.sanitize(cx, document.upcast(), true)?;
4919
4920 Ok(document)
4922 }
4923
4924 fn StyleSheets(&self, can_gc: CanGc) -> DomRoot<StyleSheetList> {
4926 self.stylesheet_list.or_init(|| {
4927 StyleSheetList::new(
4928 &self.window,
4929 StyleSheetListOwner::Document(Dom::from_ref(self)),
4930 can_gc,
4931 )
4932 })
4933 }
4934
4935 fn Implementation(&self, can_gc: CanGc) -> DomRoot<DOMImplementation> {
4937 self.implementation
4938 .or_init(|| DOMImplementation::new(self, can_gc))
4939 }
4940
4941 fn URL(&self) -> USVString {
4943 USVString(String::from(self.url().as_str()))
4944 }
4945
4946 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
4948 self.document_or_shadow_root.active_element(self.upcast())
4949 }
4950
4951 fn HasFocus(&self) -> bool {
4953 if self.window().parent_info().is_none() {
4975 self.is_fully_active()
4977 } else {
4978 self.is_fully_active() && self.focus_handler.has_focus()
4980 }
4981 }
4982
4983 fn Domain(&self) -> DOMString {
4985 match self.origin().effective_domain() {
4987 None => DOMString::new(),
4989 Some(Host::Domain(domain)) => DOMString::from(domain),
4991 Some(host) => DOMString::from(host.to_string()),
4992 }
4993 }
4994
4995 fn SetDomain(&self, value: DOMString) -> ErrorResult {
4997 if !self.has_browsing_context {
4999 return Err(Error::Security(None));
5000 }
5001
5002 if self.has_active_sandboxing_flag(
5005 SandboxingFlagSet::SANDBOXED_DOCUMENT_DOMAIN_BROWSING_CONTEXT_FLAG,
5006 ) {
5007 return Err(Error::Security(None));
5008 }
5009
5010 let effective_domain = match self.origin().effective_domain() {
5012 Some(effective_domain) => effective_domain,
5013 None => return Err(Error::Security(None)),
5015 };
5016
5017 let host =
5019 match get_registrable_domain_suffix_of_or_is_equal_to(&value.str(), effective_domain) {
5020 None => return Err(Error::Security(None)),
5021 Some(host) => host,
5022 };
5023
5024 self.origin().set_domain(host);
5029
5030 Ok(())
5031 }
5032
5033 fn Referrer(&self) -> DOMString {
5035 match self.referrer {
5036 Some(ref referrer) => DOMString::from(referrer.to_string()),
5037 None => DOMString::new(),
5038 }
5039 }
5040
5041 fn DocumentURI(&self) -> USVString {
5043 self.URL()
5044 }
5045
5046 fn CompatMode(&self) -> DOMString {
5048 DOMString::from(match self.quirks_mode.get() {
5049 QuirksMode::LimitedQuirks | QuirksMode::NoQuirks => "CSS1Compat",
5050 QuirksMode::Quirks => "BackCompat",
5051 })
5052 }
5053
5054 fn CharacterSet(&self) -> DOMString {
5056 DOMString::from(self.encoding.get().name())
5057 }
5058
5059 fn Charset(&self) -> DOMString {
5061 self.CharacterSet()
5062 }
5063
5064 fn InputEncoding(&self) -> DOMString {
5066 self.CharacterSet()
5067 }
5068
5069 fn ContentType(&self) -> DOMString {
5071 DOMString::from(self.content_type.to_string())
5072 }
5073
5074 fn GetDoctype(&self) -> Option<DomRoot<DocumentType>> {
5076 self.upcast::<Node>().children().find_map(DomRoot::downcast)
5077 }
5078
5079 fn GetDocumentElement(&self) -> Option<DomRoot<Element>> {
5081 self.upcast::<Node>().child_elements().next()
5082 }
5083
5084 fn GetElementsByTagName(
5086 &self,
5087 cx: &mut js::context::JSContext,
5088 qualified_name: DOMString,
5089 ) -> DomRoot<HTMLCollection> {
5090 let qualified_name = LocalName::from(qualified_name);
5091 if let Some(entry) = self.tag_map.borrow_mut().get(&qualified_name) {
5092 return DomRoot::from_ref(entry);
5093 }
5094 let result = HTMLCollection::by_qualified_name(
5095 cx,
5096 &self.window,
5097 self.upcast(),
5098 qualified_name.clone(),
5099 );
5100 self.tag_map
5101 .borrow_mut()
5102 .insert(qualified_name, Dom::from_ref(&*result));
5103 result
5104 }
5105
5106 fn GetElementsByTagNameNS(
5108 &self,
5109 cx: &mut js::context::JSContext,
5110 maybe_ns: Option<DOMString>,
5111 tag_name: DOMString,
5112 ) -> DomRoot<HTMLCollection> {
5113 let ns = namespace_from_domstring(maybe_ns);
5114 let local = LocalName::from(tag_name);
5115 let qname = QualName::new(None, ns, local);
5116 if let Some(collection) = self.tagns_map.borrow().get(&qname) {
5117 return DomRoot::from_ref(collection);
5118 }
5119 let result =
5120 HTMLCollection::by_qual_tag_name(cx, &self.window, self.upcast(), qname.clone());
5121 self.tagns_map
5122 .borrow_mut()
5123 .insert(qname, Dom::from_ref(&*result));
5124 result
5125 }
5126
5127 fn GetElementsByClassName(
5129 &self,
5130 cx: &mut js::context::JSContext,
5131 classes: DOMString,
5132 ) -> DomRoot<HTMLCollection> {
5133 let class_atoms: Vec<Atom> = split_html_space_chars(&classes.str())
5134 .map(Atom::from)
5135 .collect();
5136 if let Some(collection) = self.classes_map.borrow().get(&class_atoms) {
5137 return DomRoot::from_ref(collection);
5138 }
5139 let result = HTMLCollection::by_atomic_class_name(
5140 cx,
5141 &self.window,
5142 self.upcast(),
5143 class_atoms.clone(),
5144 );
5145 self.classes_map
5146 .borrow_mut()
5147 .insert(class_atoms, Dom::from_ref(&*result));
5148 result
5149 }
5150
5151 fn GetElementById(&self, id: DOMString) -> Option<DomRoot<Element>> {
5153 self.get_element_by_id(&Atom::from(id))
5154 }
5155
5156 fn CreateElement(
5158 &self,
5159 cx: &mut js::context::JSContext,
5160 mut local_name: DOMString,
5161 options: StringOrElementCreationOptions,
5162 ) -> Fallible<DomRoot<Element>> {
5163 if !is_valid_element_local_name(&local_name.str()) {
5166 debug!("Not a valid element name");
5167 return Err(Error::InvalidCharacter(None));
5168 }
5169
5170 if self.is_html_document {
5171 local_name.make_ascii_lowercase();
5172 }
5173
5174 let ns = if self.is_html_document || self.is_xhtml_document() {
5175 ns!(html)
5176 } else {
5177 ns!()
5178 };
5179
5180 let name = QualName::new(None, ns, LocalName::from(local_name));
5181 let is = match options {
5182 StringOrElementCreationOptions::String(_) => None,
5183 StringOrElementCreationOptions::ElementCreationOptions(options) => {
5184 options.is.as_ref().map(LocalName::from)
5185 },
5186 };
5187 Ok(Element::create(
5188 cx,
5189 name,
5190 is,
5191 self,
5192 ElementCreator::ScriptCreated,
5193 CustomElementCreationMode::Synchronous,
5194 None,
5195 ))
5196 }
5197
5198 fn CreateElementNS(
5200 &self,
5201 cx: &mut js::context::JSContext,
5202 namespace: Option<DOMString>,
5203 qualified_name: DOMString,
5204 options: StringOrElementCreationOptions,
5205 ) -> Fallible<DomRoot<Element>> {
5206 let context = domname::Context::Element;
5209 let (namespace, prefix, local_name) =
5210 domname::validate_and_extract(namespace, &qualified_name, context)?;
5211
5212 let name = QualName::new(prefix, namespace, local_name);
5215 let is = match options {
5216 StringOrElementCreationOptions::String(_) => None,
5217 StringOrElementCreationOptions::ElementCreationOptions(options) => {
5218 options.is.as_ref().map(LocalName::from)
5219 },
5220 };
5221
5222 Ok(Element::create(
5224 cx,
5225 name,
5226 is,
5227 self,
5228 ElementCreator::ScriptCreated,
5229 CustomElementCreationMode::Synchronous,
5230 None,
5231 ))
5232 }
5233
5234 fn CreateAttribute(
5236 &self,
5237 cx: &mut js::context::JSContext,
5238 mut local_name: DOMString,
5239 ) -> Fallible<DomRoot<Attr>> {
5240 if !is_valid_attribute_local_name(&local_name.str()) {
5243 debug!("Not a valid attribute name");
5244 return Err(Error::InvalidCharacter(None));
5245 }
5246 if self.is_html_document {
5247 local_name.make_ascii_lowercase();
5248 }
5249 let name = LocalName::from(local_name);
5250 let value = AttrValue::String("".to_owned());
5251
5252 Ok(Attr::new(
5253 cx,
5254 self,
5255 name.clone(),
5256 value,
5257 name,
5258 ns!(),
5259 None,
5260 None,
5261 ))
5262 }
5263
5264 fn CreateAttributeNS(
5266 &self,
5267 cx: &mut js::context::JSContext,
5268 namespace: Option<DOMString>,
5269 qualified_name: DOMString,
5270 ) -> Fallible<DomRoot<Attr>> {
5271 let context = domname::Context::Attribute;
5274 let (namespace, prefix, local_name) =
5275 domname::validate_and_extract(namespace, &qualified_name, context)?;
5276 let value = AttrValue::String("".to_owned());
5277 let qualified_name = LocalName::from(qualified_name);
5278 Ok(Attr::new(
5279 cx,
5280 self,
5281 local_name,
5282 value,
5283 qualified_name,
5284 namespace,
5285 prefix,
5286 None,
5287 ))
5288 }
5289
5290 fn CreateDocumentFragment(&self, cx: &mut js::context::JSContext) -> DomRoot<DocumentFragment> {
5292 DocumentFragment::new(cx, self)
5293 }
5294
5295 fn CreateTextNode(&self, cx: &mut js::context::JSContext, data: DOMString) -> DomRoot<Text> {
5297 Text::new(cx, data, self)
5298 }
5299
5300 fn CreateCDATASection(
5302 &self,
5303 cx: &mut js::context::JSContext,
5304 data: DOMString,
5305 ) -> Fallible<DomRoot<CDATASection>> {
5306 if self.is_html_document {
5308 return Err(Error::NotSupported(None));
5309 }
5310
5311 if data.contains("]]>") {
5313 return Err(Error::InvalidCharacter(None));
5314 }
5315
5316 Ok(CDATASection::new(cx, data, self))
5318 }
5319
5320 fn CreateComment(&self, cx: &mut js::context::JSContext, data: DOMString) -> DomRoot<Comment> {
5322 Comment::new(cx, data, self, None)
5323 }
5324
5325 fn CreateProcessingInstruction(
5327 &self,
5328 cx: &mut js::context::JSContext,
5329 target: DOMString,
5330 data: DOMString,
5331 ) -> Fallible<DomRoot<ProcessingInstruction>> {
5332 if !matches_name_production(&target.str()) {
5334 return Err(Error::InvalidCharacter(None));
5335 }
5336
5337 if data.contains("?>") {
5339 return Err(Error::InvalidCharacter(None));
5340 }
5341
5342 Ok(ProcessingInstruction::new(cx, target, data, self))
5344 }
5345
5346 fn ImportNode(
5348 &self,
5349 cx: &mut js::context::JSContext,
5350 node: &Node,
5351 options: BooleanOrImportNodeOptions,
5352 ) -> Fallible<DomRoot<Node>> {
5353 if node.is::<Document>() || node.is::<ShadowRoot>() {
5355 return Err(Error::NotSupported(None));
5356 }
5357 let (subtree, registry) = match options {
5359 BooleanOrImportNodeOptions::Boolean(boolean) => (boolean.into(), None),
5362 BooleanOrImportNodeOptions::ImportNodeOptions(options) => {
5364 let subtree = (!options.selfOnly).into();
5366 let registry = options.customElementRegistry;
5368 (subtree, registry)
5372 },
5373 };
5374 let registry = registry
5377 .or_else(|| CustomElementRegistry::lookup_a_custom_element_registry(self.upcast()));
5378
5379 Ok(Node::clone(cx, node, Some(self), subtree, registry))
5382 }
5383
5384 fn AdoptNode(&self, cx: &mut js::context::JSContext, node: &Node) -> Fallible<DomRoot<Node>> {
5386 if node.is::<Document>() {
5388 return Err(Error::NotSupported(None));
5389 }
5390
5391 if node.is::<ShadowRoot>() {
5393 return Err(Error::HierarchyRequest(None));
5394 }
5395
5396 Node::adopt(cx, node, self);
5398
5399 Ok(DomRoot::from_ref(node))
5401 }
5402
5403 fn CreateEvent(
5405 &self,
5406 cx: &mut js::context::JSContext,
5407 mut interface: DOMString,
5408 ) -> Fallible<DomRoot<Event>> {
5409 interface.make_ascii_lowercase();
5410 match &*interface.str() {
5411 "beforeunloadevent" => Ok(DomRoot::upcast(BeforeUnloadEvent::new_uninitialized(
5412 &self.window,
5413 CanGc::from_cx(cx),
5414 ))),
5415 "compositionevent" | "textevent" => Ok(DomRoot::upcast(
5416 CompositionEvent::new_uninitialized(&self.window, CanGc::from_cx(cx)),
5417 )),
5418 "customevent" => Ok(DomRoot::upcast(CustomEvent::new_uninitialized(
5419 self.window.upcast(),
5420 CanGc::from_cx(cx),
5421 ))),
5422 "events" | "event" | "htmlevents" | "svgevents" => Ok(Event::new_uninitialized(
5425 self.window.upcast(),
5426 CanGc::from_cx(cx),
5427 )),
5428 "focusevent" => Ok(DomRoot::upcast(FocusEvent::new_uninitialized(
5429 &self.window,
5430 CanGc::from_cx(cx),
5431 ))),
5432 "hashchangeevent" => Ok(DomRoot::upcast(HashChangeEvent::new_uninitialized(
5433 &self.window,
5434 CanGc::from_cx(cx),
5435 ))),
5436 "keyboardevent" => Ok(DomRoot::upcast(KeyboardEvent::new_uninitialized(
5437 cx,
5438 &self.window,
5439 ))),
5440 "messageevent" => Ok(DomRoot::upcast(MessageEvent::new_uninitialized(
5441 self.window.upcast(),
5442 CanGc::from_cx(cx),
5443 ))),
5444 "mouseevent" | "mouseevents" => Ok(DomRoot::upcast(MouseEvent::new_uninitialized(
5445 cx,
5446 &self.window,
5447 ))),
5448 "storageevent" => Ok(DomRoot::upcast(StorageEvent::new_uninitialized(
5449 &self.window,
5450 "".into(),
5451 CanGc::from_cx(cx),
5452 ))),
5453 "touchevent" => Ok(DomRoot::upcast(DomTouchEvent::new_uninitialized(
5454 &self.window,
5455 &TouchList::new(&self.window, &[], CanGc::from_cx(cx)),
5456 &TouchList::new(&self.window, &[], CanGc::from_cx(cx)),
5457 &TouchList::new(&self.window, &[], CanGc::from_cx(cx)),
5458 CanGc::from_cx(cx),
5459 ))),
5460 "uievent" | "uievents" => Ok(DomRoot::upcast(UIEvent::new_uninitialized(
5461 &self.window,
5462 CanGc::from_cx(cx),
5463 ))),
5464 _ => Err(Error::NotSupported(None)),
5465 }
5466 }
5467
5468 fn LastModified(&self) -> DOMString {
5470 DOMString::from(self.last_modified.as_ref().cloned().unwrap_or_else(|| {
5471 Local::now().format("%m/%d/%Y %H:%M:%S").to_string()
5477 }))
5478 }
5479
5480 fn CreateRange(&self, cx: &mut js::context::JSContext) -> DomRoot<Range> {
5482 Range::new_with_doc(self, None, CanGc::from_cx(cx))
5483 }
5484
5485 fn CreateNodeIterator(
5487 &self,
5488 root: &Node,
5489 what_to_show: u32,
5490 filter: Option<Rc<NodeFilter>>,
5491 can_gc: CanGc,
5492 ) -> DomRoot<NodeIterator> {
5493 NodeIterator::new(self, root, what_to_show, filter, can_gc)
5494 }
5495
5496 fn CreateTreeWalker(
5498 &self,
5499 root: &Node,
5500 what_to_show: u32,
5501 filter: Option<Rc<NodeFilter>>,
5502 ) -> DomRoot<TreeWalker> {
5503 TreeWalker::new(self, root, what_to_show, filter)
5504 }
5505
5506 fn Title(&self) -> DOMString {
5508 self.title().unwrap_or_else(|| DOMString::from(""))
5509 }
5510
5511 fn SetTitle(&self, cx: &mut js::context::JSContext, title: DOMString) {
5513 let root = match self.GetDocumentElement() {
5514 Some(root) => root,
5515 None => return,
5516 };
5517
5518 let node = if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") {
5519 let elem = root
5520 .upcast::<Node>()
5521 .child_elements_unrooted(cx.no_gc())
5522 .find(|node| {
5523 node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title")
5524 });
5525 match elem {
5526 Some(elem) => UnrootedDom::upcast::<Node>(elem).as_rooted(),
5527 None => {
5528 let name = QualName::new(None, ns!(svg), local_name!("title"));
5529 let elem = Element::create(
5530 cx,
5531 name,
5532 None,
5533 self,
5534 ElementCreator::ScriptCreated,
5535 CustomElementCreationMode::Synchronous,
5536 None,
5537 );
5538 let parent = root.upcast::<Node>();
5539 let child = elem.upcast::<Node>();
5540 parent
5541 .InsertBefore(cx, child, parent.GetFirstChild().as_deref())
5542 .unwrap()
5543 },
5544 }
5545 } else if root.namespace() == &ns!(html) {
5546 let elem = root
5547 .upcast::<Node>()
5548 .traverse_preorder_non_rooting(cx.no_gc(), ShadowIncluding::No)
5549 .find(|node| node.is::<HTMLTitleElement>());
5550 match elem {
5551 Some(elem) => elem.as_rooted(),
5552 None => match self.GetHead() {
5553 Some(head) => {
5554 let name = QualName::new(None, ns!(html), local_name!("title"));
5555 let elem = Element::create(
5556 cx,
5557 name,
5558 None,
5559 self,
5560 ElementCreator::ScriptCreated,
5561 CustomElementCreationMode::Synchronous,
5562 None,
5563 );
5564 head.upcast::<Node>()
5565 .AppendChild(cx, elem.upcast())
5566 .unwrap()
5567 },
5568 None => return,
5569 },
5570 }
5571 } else {
5572 return;
5573 };
5574
5575 node.set_text_content_for_element(cx, Some(title));
5576 }
5577
5578 fn GetHead(&self) -> Option<DomRoot<HTMLHeadElement>> {
5580 self.get_html_element()
5581 .and_then(|root| root.upcast::<Node>().children().find_map(DomRoot::downcast))
5582 }
5583
5584 fn GetCurrentScript(&self) -> Option<DomRoot<HTMLScriptElement>> {
5586 self.current_script.get()
5587 }
5588
5589 fn GetBody(&self) -> Option<DomRoot<HTMLElement>> {
5591 self.get_html_element().and_then(|root| {
5594 let node = root.upcast::<Node>();
5595 node.children()
5596 .find(|child| {
5597 matches!(
5598 child.type_id(),
5599 NodeTypeId::Element(ElementTypeId::HTMLElement(
5600 HTMLElementTypeId::HTMLBodyElement,
5601 )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
5602 HTMLElementTypeId::HTMLFrameSetElement,
5603 ))
5604 )
5605 })
5606 .map(|node| DomRoot::downcast(node).unwrap())
5607 })
5608 }
5609
5610 fn SetBody(
5612 &self,
5613 cx: &mut js::context::JSContext,
5614 new_body: Option<&HTMLElement>,
5615 ) -> ErrorResult {
5616 let new_body = match new_body {
5618 Some(new_body) => new_body,
5619 None => return Err(Error::HierarchyRequest(None)),
5620 };
5621
5622 let node = new_body.upcast::<Node>();
5623 match node.type_id() {
5624 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) |
5625 NodeTypeId::Element(ElementTypeId::HTMLElement(
5626 HTMLElementTypeId::HTMLFrameSetElement,
5627 )) => {},
5628 _ => return Err(Error::HierarchyRequest(None)),
5629 }
5630
5631 let old_body = self.GetBody();
5633 if old_body.as_deref() == Some(new_body) {
5634 return Ok(());
5635 }
5636
5637 match (self.GetDocumentElement(), &old_body) {
5638 (Some(ref root), Some(child)) => {
5641 let root = root.upcast::<Node>();
5642 root.ReplaceChild(cx, new_body.upcast(), child.upcast())
5643 .map(|_| ())
5644 },
5645
5646 (None, _) => Err(Error::HierarchyRequest(None)),
5648
5649 (Some(ref root), &None) => {
5652 let root = root.upcast::<Node>();
5653 root.AppendChild(cx, new_body.upcast()).map(|_| ())
5654 },
5655 }
5656 }
5657
5658 fn GetElementsByName(&self, name: DOMString, can_gc: CanGc) -> DomRoot<NodeList> {
5660 NodeList::new_elements_by_name_list(self.window(), self, name, can_gc)
5661 }
5662
5663 fn Images(&self, cx: &mut js::context::JSContext) -> DomRoot<HTMLCollection> {
5665 self.images.or_init(|| {
5666 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
5667 element.is::<HTMLImageElement>()
5668 })
5669 })
5670 }
5671
5672 fn Embeds(&self, cx: &mut js::context::JSContext) -> DomRoot<HTMLCollection> {
5674 self.embeds.or_init(|| {
5675 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
5676 element.is::<HTMLEmbedElement>()
5677 })
5678 })
5679 }
5680
5681 fn Plugins(&self, cx: &mut js::context::JSContext) -> DomRoot<HTMLCollection> {
5683 self.Embeds(cx)
5684 }
5685
5686 fn Links(&self, cx: &mut js::context::JSContext) -> DomRoot<HTMLCollection> {
5688 self.links.or_init(|| {
5689 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
5690 (element.is::<HTMLAnchorElement>() || element.is::<HTMLAreaElement>()) &&
5691 element.has_attribute(&local_name!("href"))
5692 })
5693 })
5694 }
5695
5696 fn Forms(&self, cx: &mut js::context::JSContext) -> DomRoot<HTMLCollection> {
5698 self.forms.or_init(|| {
5699 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
5700 element.is::<HTMLFormElement>()
5701 })
5702 })
5703 }
5704
5705 fn Scripts(&self, cx: &mut js::context::JSContext) -> DomRoot<HTMLCollection> {
5707 self.scripts.or_init(|| {
5708 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
5709 element.is::<HTMLScriptElement>()
5710 })
5711 })
5712 }
5713
5714 fn Anchors(&self, cx: &mut js::context::JSContext) -> DomRoot<HTMLCollection> {
5716 self.anchors.or_init(|| {
5717 HTMLCollection::new_with_filter_fn(cx, &self.window, self.upcast(), |element, _| {
5718 element.is::<HTMLAnchorElement>() && element.has_attribute(&local_name!("href"))
5719 })
5720 })
5721 }
5722
5723 fn Applets(&self, cx: &mut js::context::JSContext) -> DomRoot<HTMLCollection> {
5725 self.applets
5726 .or_init(|| HTMLCollection::always_empty(cx, &self.window, self.upcast()))
5727 }
5728
5729 fn GetLocation(&self, cx: &mut js::context::JSContext) -> Option<DomRoot<Location>> {
5731 if self.is_fully_active() {
5732 Some(self.window.Location(cx))
5733 } else {
5734 None
5735 }
5736 }
5737
5738 fn Children(&self, cx: &mut js::context::JSContext) -> DomRoot<HTMLCollection> {
5740 HTMLCollection::children(cx, &self.window, self.upcast())
5741 }
5742
5743 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
5745 self.upcast::<Node>().child_elements().next()
5746 }
5747
5748 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
5750 self.upcast::<Node>()
5751 .rev_children()
5752 .find_map(DomRoot::downcast)
5753 }
5754
5755 fn ChildElementCount(&self) -> u32 {
5757 self.upcast::<Node>().child_elements().count() as u32
5758 }
5759
5760 fn Prepend(&self, cx: &mut js::context::JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
5762 self.upcast::<Node>().prepend(cx, nodes)
5763 }
5764
5765 fn Append(&self, cx: &mut js::context::JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
5767 self.upcast::<Node>().append(cx, nodes)
5768 }
5769
5770 fn ReplaceChildren(
5772 &self,
5773 cx: &mut js::context::JSContext,
5774 nodes: Vec<NodeOrString>,
5775 ) -> ErrorResult {
5776 self.upcast::<Node>().replace_children(cx, nodes)
5777 }
5778
5779 fn MoveBefore(
5781 &self,
5782 cx: &mut js::context::JSContext,
5783 node: &Node,
5784 child: Option<&Node>,
5785 ) -> ErrorResult {
5786 self.upcast::<Node>().move_before(cx, node, child)
5787 }
5788
5789 fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
5791 self.upcast::<Node>().query_selector(selectors)
5792 }
5793
5794 fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>> {
5796 self.upcast::<Node>().query_selector_all(selectors)
5797 }
5798
5799 fn ReadyState(&self) -> DocumentReadyState {
5801 self.ready_state.get()
5802 }
5803
5804 fn GetDefaultView(&self) -> Option<DomRoot<Window>> {
5806 if self.has_browsing_context {
5807 Some(DomRoot::from_ref(&*self.window))
5808 } else {
5809 None
5810 }
5811 }
5812
5813 fn GetCookie(&self) -> Fallible<DOMString> {
5815 if self.is_cookie_averse() {
5816 return Ok(DOMString::new());
5817 }
5818
5819 if !self.origin().is_tuple() {
5820 return Err(Error::Security(None));
5821 }
5822
5823 let url = self.url();
5824 let (tx, rx) =
5825 profile_generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
5826 let _ = self
5827 .window
5828 .as_global_scope()
5829 .resource_threads()
5830 .send(GetCookieStringForUrl(url, tx, NonHTTP));
5831 let cookies = rx.recv().unwrap();
5832 Ok(cookies.map_or(DOMString::new(), DOMString::from))
5833 }
5834
5835 fn SetCookie(&self, cookie: DOMString) -> ErrorResult {
5837 if self.is_cookie_averse() {
5838 return Ok(());
5839 }
5840
5841 if !self.origin().is_tuple() {
5842 return Err(Error::Security(None));
5843 }
5844
5845 if !cookie.is_valid_for_cookie() {
5846 return Ok(());
5847 }
5848
5849 let cookies = if let Some(cookie) = Cookie::parse(cookie.to_string()).ok().map(Serde) {
5850 vec![cookie]
5851 } else {
5852 vec![]
5853 };
5854
5855 let _ = self
5856 .window
5857 .as_global_scope()
5858 .resource_threads()
5859 .send(SetCookiesForUrl(self.url(), cookies, NonHTTP));
5860 Ok(())
5861 }
5862
5863 fn BgColor(&self) -> DOMString {
5865 self.get_body_attribute(&local_name!("bgcolor"))
5866 }
5867
5868 fn SetBgColor(&self, cx: &mut js::context::JSContext, value: DOMString) {
5870 self.set_body_attribute(cx, &local_name!("bgcolor"), value)
5871 }
5872
5873 fn FgColor(&self) -> DOMString {
5875 self.get_body_attribute(&local_name!("text"))
5876 }
5877
5878 fn SetFgColor(&self, cx: &mut js::context::JSContext, value: DOMString) {
5880 self.set_body_attribute(cx, &local_name!("text"), value)
5881 }
5882
5883 fn NamedGetter(
5885 &self,
5886 cx: &mut js::context::JSContext,
5887 name: DOMString,
5888 ) -> Option<NamedPropertyValue> {
5889 if name.is_empty() {
5890 return None;
5891 }
5892 let name = Atom::from(name);
5893
5894 let elements_with_name = self.get_elements_with_name(&name);
5897 let name_iter = elements_with_name
5898 .iter()
5899 .filter(|elem| is_named_element_with_name_attribute(elem));
5900 let elements_with_id = self.get_elements_with_id(&name);
5901 let id_iter = elements_with_id
5902 .iter()
5903 .filter(|elem| is_named_element_with_id_attribute(elem));
5904 let mut elements = name_iter.chain(id_iter);
5905
5906 let first = elements.next()?;
5913 if elements.all(|other| first == other) {
5914 if let Some(nested_window_proxy) = first
5915 .downcast::<HTMLIFrameElement>()
5916 .and_then(|iframe| iframe.GetContentWindow())
5917 {
5918 return Some(NamedPropertyValue::WindowProxy(nested_window_proxy));
5919 }
5920
5921 return Some(NamedPropertyValue::Element(DomRoot::from_ref(first)));
5923 }
5924
5925 #[derive(JSTraceable, MallocSizeOf)]
5928 struct DocumentNamedGetter {
5929 #[no_trace]
5930 name: Atom,
5931 }
5932 impl CollectionFilter for DocumentNamedGetter {
5933 fn filter(&self, elem: &Element, _root: &Node) -> bool {
5934 let type_ = match elem.upcast::<Node>().type_id() {
5935 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
5936 _ => return false,
5937 };
5938 match type_ {
5939 HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
5940 elem.get_name().as_ref() == Some(&self.name)
5941 },
5942 HTMLElementTypeId::HTMLImageElement => elem.get_name().is_some_and(|name| {
5943 name == *self.name ||
5944 !name.is_empty() && elem.get_id().as_ref() == Some(&self.name)
5945 }),
5946 _ => false,
5950 }
5951 }
5952 }
5953 let collection = HTMLCollection::create(
5954 cx,
5955 self.window(),
5956 self.upcast(),
5957 Box::new(DocumentNamedGetter { name }),
5958 );
5959 Some(NamedPropertyValue::HTMLCollection(collection))
5960 }
5961
5962 fn SupportedPropertyNames(&self) -> Vec<DOMString> {
5964 let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
5965
5966 let name_map = self.name_map.borrow();
5967 for (name, elements) in &(name_map).0 {
5968 if name.is_empty() {
5969 continue;
5970 }
5971 let mut name_iter = elements
5972 .iter()
5973 .filter(|elem| is_named_element_with_name_attribute(elem));
5974 if let Some(first) = name_iter.next() {
5975 names_with_first_named_element_map.insert(name, first);
5976 }
5977 }
5978 let id_map = self.id_map.borrow();
5979 for (id, elements) in &(id_map).0 {
5980 if id.is_empty() {
5981 continue;
5982 }
5983 let mut id_iter = elements
5984 .iter()
5985 .filter(|elem| is_named_element_with_id_attribute(elem));
5986 if let Some(first) = id_iter.next() {
5987 match names_with_first_named_element_map.entry(id) {
5988 Vacant(entry) => drop(entry.insert(first)),
5989 Occupied(mut entry) => {
5990 if first.upcast::<Node>().is_before(entry.get().upcast()) {
5991 *entry.get_mut() = first;
5992 }
5993 },
5994 }
5995 }
5996 }
5997
5998 let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
5999 names_with_first_named_element_map
6000 .iter()
6001 .map(|(k, v)| (*k, *v))
6002 .collect();
6003 names_with_first_named_element_vec.sort_unstable_by(|a, b| {
6004 if a.1 == b.1 {
6005 a.0.cmp(b.0)
6008 } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
6009 Ordering::Less
6010 } else {
6011 Ordering::Greater
6012 }
6013 });
6014
6015 names_with_first_named_element_vec
6016 .iter()
6017 .map(|(k, _v)| DOMString::from(&***k))
6018 .collect()
6019 }
6020
6021 fn Clear(&self) {
6023 }
6025
6026 fn CaptureEvents(&self) {
6028 }
6030
6031 fn ReleaseEvents(&self) {
6033 }
6035
6036 global_event_handlers!();
6038
6039 event_handler!(
6041 readystatechange,
6042 GetOnreadystatechange,
6043 SetOnreadystatechange
6044 );
6045
6046 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
6048 self.document_or_shadow_root.element_from_point(
6049 x,
6050 y,
6051 self.GetDocumentElement(),
6052 self.has_browsing_context,
6053 )
6054 }
6055
6056 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
6058 self.document_or_shadow_root.elements_from_point(
6059 x,
6060 y,
6061 self.GetDocumentElement(),
6062 self.has_browsing_context,
6063 )
6064 }
6065
6066 fn GetScrollingElement(&self) -> Option<DomRoot<Element>> {
6068 if self.quirks_mode() == QuirksMode::Quirks {
6070 if let Some(ref body) = self.GetBody() {
6072 let e = body.upcast::<Element>();
6073 if !e.is_potentially_scrollable_body_for_scrolling_element() {
6077 return Some(DomRoot::from_ref(e));
6078 }
6079 }
6080
6081 return None;
6083 }
6084
6085 self.GetDocumentElement()
6088 }
6089
6090 fn Open(
6092 &self,
6093 cx: &mut js::context::JSContext,
6094 _unused1: Option<DOMString>,
6095 _unused2: Option<DOMString>,
6096 ) -> Fallible<DomRoot<Document>> {
6097 if !self.is_html_document() {
6099 return Err(Error::InvalidState(None));
6100 }
6101
6102 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6105 return Err(Error::InvalidState(None));
6106 }
6107
6108 let entry_responsible_document = GlobalScope::entry().as_window().Document();
6110
6111 if !self
6114 .origin()
6115 .same_origin(&entry_responsible_document.origin())
6116 {
6117 return Err(Error::Security(None));
6118 }
6119
6120 if self
6123 .active_parser()
6124 .is_some_and(|parser| parser.script_nesting_level() > 0)
6125 {
6126 return Ok(DomRoot::from_ref(self));
6127 }
6128
6129 if self.is_prompting_or_unloading() {
6131 return Ok(DomRoot::from_ref(self));
6132 }
6133
6134 if self.active_parser_was_aborted.get() {
6136 return Ok(DomRoot::from_ref(self));
6137 }
6138
6139 self.window().set_navigation_start();
6143
6144 if self.has_browsing_context() {
6148 self.abort(cx);
6151 }
6152
6153 for node in self
6156 .upcast::<Node>()
6157 .traverse_preorder_non_rooting(cx.no_gc(), ShadowIncluding::Yes)
6158 {
6159 node.upcast::<EventTarget>().remove_all_listeners();
6160 }
6161
6162 if self.window.Document() == DomRoot::from_ref(self) {
6165 self.window.upcast::<EventTarget>().remove_all_listeners();
6166 }
6167
6168 Node::replace_all(cx, None, self.upcast::<Node>());
6170
6171 if self.is_fully_active() {
6178 let mut new_url = entry_responsible_document.url();
6180
6181 if entry_responsible_document != DomRoot::from_ref(self) {
6183 new_url.set_fragment(None);
6184 }
6185
6186 self.set_url(new_url);
6189 }
6190
6191 self.is_initial_about_blank.set(false);
6193
6194 self.set_quirks_mode(QuirksMode::NoQuirks);
6200
6201 let resource_threads = self.window.as_global_scope().resource_threads().clone();
6207 *self.loader.borrow_mut() =
6208 DocumentLoader::new_with_threads(resource_threads, Some(self.url()));
6209 ServoParser::parse_html_script_input(self, self.url());
6210
6211 self.ready_state.set(DocumentReadyState::Loading);
6217
6218 Ok(DomRoot::from_ref(self))
6220 }
6221
6222 fn Open_(
6224 &self,
6225 cx: &mut js::context::JSContext,
6226 url: USVString,
6227 target: DOMString,
6228 features: DOMString,
6229 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
6230 self.browsing_context()
6231 .ok_or(Error::InvalidAccess(None))?
6232 .open(cx, url, target, features)
6233 }
6234
6235 fn Write(
6237 &self,
6238 cx: &mut js::context::JSContext,
6239 text: Vec<TrustedHTMLOrString>,
6240 ) -> ErrorResult {
6241 self.write(cx, text, false, "Document", "write")
6244 }
6245
6246 fn Writeln(
6248 &self,
6249 cx: &mut js::context::JSContext,
6250 text: Vec<TrustedHTMLOrString>,
6251 ) -> ErrorResult {
6252 self.write(cx, text, true, "Document", "writeln")
6255 }
6256
6257 fn Close(&self, cx: &mut js::context::JSContext) -> ErrorResult {
6259 if !self.is_html_document() {
6260 return Err(Error::InvalidState(None));
6262 }
6263
6264 if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
6267 return Err(Error::InvalidState(None));
6268 }
6269
6270 let parser = match self.get_current_parser() {
6272 Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
6273 _ => {
6274 return Ok(());
6275 },
6276 };
6277
6278 parser.close(cx);
6280
6281 Ok(())
6282 }
6283
6284 fn ExecCommand(
6286 &self,
6287 cx: &mut js::context::JSContext,
6288 command_id: DOMString,
6289 _show_ui: bool,
6290 value: TrustedHTMLOrString,
6291 ) -> Fallible<bool> {
6292 let value = if command_id == "insertHTML" {
6293 TrustedHTML::get_trusted_type_compliant_string(
6294 cx,
6295 self.window.as_global_scope(),
6296 value,
6297 "Document execCommand",
6298 )?
6299 } else {
6300 match value {
6301 TrustedHTMLOrString::TrustedHTML(trusted_html) => trusted_html.data().clone(),
6302 TrustedHTMLOrString::String(value) => value,
6303 }
6304 };
6305
6306 Ok(self.exec_command_for_command_id(cx, command_id, value))
6307 }
6308
6309 fn QueryCommandEnabled(&self, cx: &mut js::context::JSContext, command_id: DOMString) -> bool {
6311 self.check_support_and_enabled(cx, &command_id).is_some()
6313 }
6314
6315 fn QueryCommandSupported(&self, command_id: DOMString) -> bool {
6317 self.is_command_supported(command_id)
6321 }
6322
6323 fn QueryCommandIndeterm(&self, cx: &mut js::context::JSContext, command_id: DOMString) -> bool {
6325 self.is_command_indeterminate(cx, command_id)
6326 }
6327
6328 fn QueryCommandState(&self, cx: &mut js::context::JSContext, command_id: DOMString) -> bool {
6330 self.command_state_for_command(cx, command_id)
6331 }
6332
6333 fn QueryCommandValue(
6335 &self,
6336 cx: &mut js::context::JSContext,
6337 command_id: DOMString,
6338 ) -> DOMString {
6339 self.command_value_for_command(cx, command_id)
6340 }
6341
6342 event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror);
6344
6345 event_handler!(
6347 fullscreenchange,
6348 GetOnfullscreenchange,
6349 SetOnfullscreenchange
6350 );
6351
6352 fn FullscreenEnabled(&self) -> bool {
6354 self.get_allow_fullscreen()
6355 }
6356
6357 fn Fullscreen(&self) -> bool {
6359 self.fullscreen_element.get().is_some()
6360 }
6361
6362 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
6364 DocumentOrShadowRoot::get_fullscreen_element(&self.node, self.fullscreen_element.get())
6365 }
6366
6367 fn ExitFullscreen(&self, can_gc: CanGc) -> Rc<Promise> {
6369 self.exit_fullscreen(can_gc)
6370 }
6371
6372 fn ServoGetMediaControls(&self, id: DOMString) -> Fallible<DomRoot<ShadowRoot>> {
6376 match self.media_controls.borrow().get(&*id.str()) {
6377 Some(m) => Ok(DomRoot::from_ref(m)),
6378 None => Err(Error::InvalidAccess(None)),
6379 }
6380 }
6381
6382 fn GetSelection(&self, cx: &mut js::context::JSContext) -> Option<DomRoot<Selection>> {
6384 if self.has_browsing_context {
6385 Some(self.selection.or_init(|| Selection::new(cx, self)))
6386 } else {
6387 None
6388 }
6389 }
6390
6391 fn Fonts(&self, cx: &mut js::context::JSContext) -> DomRoot<FontFaceSet> {
6393 self.fonts
6394 .or_init(|| FontFaceSet::new(cx, &self.global(), None))
6395 }
6396
6397 fn Hidden(&self) -> bool {
6399 self.visibility_state.get() == DocumentVisibilityState::Hidden
6400 }
6401
6402 fn VisibilityState(&self) -> DocumentVisibilityState {
6404 self.visibility_state.get()
6405 }
6406
6407 fn CreateExpression(
6408 &self,
6409 cx: &mut js::context::JSContext,
6410 expression: DOMString,
6411 resolver: Option<Rc<XPathNSResolver>>,
6412 ) -> Fallible<DomRoot<crate::dom::types::XPathExpression>> {
6413 let parsed_expression =
6414 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
6415 Ok(XPathExpression::new(
6416 cx,
6417 &self.window,
6418 None,
6419 parsed_expression,
6420 ))
6421 }
6422
6423 fn CreateNSResolver(
6424 &self,
6425 cx: &mut js::context::JSContext,
6426 node_resolver: &Node,
6427 ) -> DomRoot<Node> {
6428 let global = self.global();
6429 let window = global.as_window();
6430 let evaluator = XPathEvaluator::new(cx, window, None);
6431 XPathEvaluatorMethods::<crate::DomTypeHolder>::CreateNSResolver(&*evaluator, node_resolver)
6432 }
6433
6434 fn Evaluate(
6435 &self,
6436 cx: &mut js::context::JSContext,
6437 expression: DOMString,
6438 context_node: &Node,
6439 resolver: Option<Rc<XPathNSResolver>>,
6440 result_type: u16,
6441 result: Option<&crate::dom::types::XPathResult>,
6442 ) -> Fallible<DomRoot<crate::dom::types::XPathResult>> {
6443 let parsed_expression =
6444 parse_expression(cx, &expression.str(), resolver, self.is_html_document())?;
6445 XPathExpression::new(cx, &self.window, None, parsed_expression).evaluate_internal(
6446 cx,
6447 context_node,
6448 result_type,
6449 result,
6450 )
6451 }
6452
6453 fn AdoptedStyleSheets(&self, context: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
6455 self.adopted_stylesheets_frozen_types.get_or_init(
6456 || {
6457 self.adopted_stylesheets
6458 .borrow()
6459 .clone()
6460 .iter()
6461 .map(|sheet| sheet.as_rooted())
6462 .collect()
6463 },
6464 context,
6465 retval,
6466 can_gc,
6467 );
6468 }
6469
6470 fn SetAdoptedStyleSheets(
6472 &self,
6473 context: JSContext,
6474 val: HandleValue,
6475 can_gc: CanGc,
6476 ) -> ErrorResult {
6477 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
6478 context,
6479 self.adopted_stylesheets.borrow_mut().as_mut(),
6480 val,
6481 &StyleSheetListOwner::Document(Dom::from_ref(self)),
6482 can_gc,
6483 );
6484
6485 if result.is_ok() {
6487 self.adopted_stylesheets_frozen_types.clear()
6488 }
6489
6490 result
6491 }
6492
6493 fn Timeline(&self) -> DomRoot<DocumentTimeline> {
6494 self.timeline.as_rooted()
6495 }
6496}
6497
6498fn update_with_current_instant(marker: &Cell<Option<CrossProcessInstant>>) {
6499 if marker.get().is_none() {
6500 marker.set(Some(CrossProcessInstant::now()))
6501 }
6502}
6503
6504#[derive(JSTraceable, MallocSizeOf)]
6505pub(crate) enum AnimationFrameCallback {
6506 DevtoolsFramerateTick {
6507 actor_name: String,
6508 },
6509 FrameRequestCallback {
6510 #[conditional_malloc_size_of]
6511 callback: Rc<FrameRequestCallback>,
6512 },
6513}
6514
6515impl AnimationFrameCallback {
6516 fn call(&self, cx: &mut js::context::JSContext, document: &Document, now: f64) {
6517 match *self {
6518 AnimationFrameCallback::DevtoolsFramerateTick { ref actor_name } => {
6519 let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name.clone(), now);
6520 let devtools_sender = document.window().as_global_scope().devtools_chan().unwrap();
6521 devtools_sender.send(msg).unwrap();
6522 },
6523 AnimationFrameCallback::FrameRequestCallback { ref callback } => {
6524 let _ = callback.Call__(cx, Finite::wrap(now), ExceptionHandling::Report);
6527 },
6528 }
6529 }
6530}
6531
6532#[derive(Default, JSTraceable, MallocSizeOf)]
6533#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
6534struct PendingInOrderScriptVec {
6535 scripts: DomRefCell<VecDeque<PendingScript>>,
6536}
6537
6538impl PendingInOrderScriptVec {
6539 fn is_empty(&self) -> bool {
6540 self.scripts.borrow().is_empty()
6541 }
6542
6543 fn push(&self, element: &HTMLScriptElement) {
6544 self.scripts
6545 .borrow_mut()
6546 .push_back(PendingScript::new(element));
6547 }
6548
6549 fn loaded(&self, element: &HTMLScriptElement, result: ScriptResult) {
6550 let mut scripts = self.scripts.borrow_mut();
6551 let entry = scripts
6552 .iter_mut()
6553 .find(|entry| &*entry.element == element)
6554 .unwrap();
6555 entry.loaded(result);
6556 }
6557
6558 fn take_next_ready_to_be_executed(&self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
6559 let mut scripts = self.scripts.borrow_mut();
6560 let pair = scripts.front_mut()?.take_result()?;
6561 scripts.pop_front();
6562 Some(pair)
6563 }
6564
6565 fn clear(&self) {
6566 *self.scripts.borrow_mut() = Default::default();
6567 }
6568}
6569
6570#[derive(JSTraceable, MallocSizeOf)]
6571#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
6572struct PendingScript {
6573 element: Dom<HTMLScriptElement>,
6574 load: Option<ScriptResult>,
6576}
6577
6578impl PendingScript {
6579 fn new(element: &HTMLScriptElement) -> Self {
6580 Self {
6581 element: Dom::from_ref(element),
6582 load: None,
6583 }
6584 }
6585
6586 fn new_with_load(element: &HTMLScriptElement, load: Option<ScriptResult>) -> Self {
6587 Self {
6588 element: Dom::from_ref(element),
6589 load,
6590 }
6591 }
6592
6593 fn loaded(&mut self, result: ScriptResult) {
6594 assert!(self.load.is_none());
6595 self.load = Some(result);
6596 }
6597
6598 fn take_result(&mut self) -> Option<(DomRoot<HTMLScriptElement>, ScriptResult)> {
6599 self.load
6600 .take()
6601 .map(|result| (DomRoot::from_ref(&*self.element), result))
6602 }
6603}
6604
6605fn is_named_element_with_name_attribute(elem: &Element) -> bool {
6606 let type_ = match elem.upcast::<Node>().type_id() {
6607 NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
6608 _ => return false,
6609 };
6610 match type_ {
6611 HTMLElementTypeId::HTMLFormElement |
6612 HTMLElementTypeId::HTMLIFrameElement |
6613 HTMLElementTypeId::HTMLImageElement => true,
6614 _ => false,
6618 }
6619}
6620
6621fn is_named_element_with_id_attribute(elem: &Element) -> bool {
6622 elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty())
6626}
6627
6628impl DocumentHelpers for Document {
6629 fn ensure_safe_to_run_script_or_layout(&self) {
6630 Document::ensure_safe_to_run_script_or_layout(self)
6631 }
6632}
6633
6634pub(crate) struct SameoriginAncestorNavigablesIterator {
6638 document: DomRoot<Document>,
6639}
6640
6641impl SameoriginAncestorNavigablesIterator {
6642 pub(crate) fn new(document: DomRoot<Document>) -> Self {
6643 Self { document }
6644 }
6645}
6646
6647impl Iterator for SameoriginAncestorNavigablesIterator {
6648 type Item = DomRoot<Document>;
6649
6650 fn next(&mut self) -> Option<Self::Item> {
6651 let window_proxy = self.document.browsing_context()?;
6652 self.document = window_proxy.parent()?.document()?;
6653 Some(self.document.clone())
6654 }
6655}
6656
6657pub(crate) struct SameOriginDescendantNavigablesIterator {
6662 stack: Vec<Box<dyn Iterator<Item = DomRoot<HTMLIFrameElement>>>>,
6663}
6664
6665impl SameOriginDescendantNavigablesIterator {
6666 pub(crate) fn new(document: DomRoot<Document>) -> Self {
6667 let iframes: Vec<DomRoot<HTMLIFrameElement>> = document.iframes().iter().collect();
6668 Self {
6669 stack: vec![Box::new(iframes.into_iter())],
6670 }
6671 }
6672
6673 fn get_next_iframe(&mut self) -> Option<DomRoot<HTMLIFrameElement>> {
6674 let mut cur_iframe = self.stack.last_mut()?.next();
6675 while cur_iframe.is_none() {
6676 self.stack.pop();
6677 cur_iframe = self.stack.last_mut()?.next();
6678 }
6679 cur_iframe
6680 }
6681}
6682
6683impl Iterator for SameOriginDescendantNavigablesIterator {
6684 type Item = DomRoot<Document>;
6685
6686 fn next(&mut self) -> Option<Self::Item> {
6687 while let Some(iframe) = self.get_next_iframe() {
6688 let Some(pipeline_id) = iframe.pipeline_id() else {
6689 continue;
6690 };
6691
6692 if let Some(document) = ScriptThread::find_document(pipeline_id) {
6693 let child_iframes: Vec<DomRoot<HTMLIFrameElement>> =
6694 document.iframes().iter().collect();
6695 self.stack.push(Box::new(child_iframes.into_iter()));
6696 return Some(document);
6697 } else {
6698 continue;
6699 };
6700 }
6701 None
6702 }
6703}