1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
8
9use std::borrow::Cow;
10use std::cell::{Cell, LazyCell, RefCell};
11use std::default::Default;
12use std::rc::Rc;
13use std::str::FromStr;
14use std::sync::atomic::{AtomicUsize, Ordering};
15use std::{fmt, mem};
16
17use app_units::Au;
18use atomic_refcell::{AtomicRef, AtomicRefCell};
19use cssparser::match_ignore_ascii_case;
20use dom_struct::dom_struct;
21use euclid::Rect;
22use hashlink::LruCache;
23use html5ever::serialize::TraversalScope;
24use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
25use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns};
26use js::context::{JSContext, NoGC};
27use js::jsapi::{Heap, JSObject};
28use js::jsval::JSVal;
29use js::realm::CurrentRealm;
30use js::rust::HandleObject;
31use layout_api::{
32 ACCESSKIT_ROLE_TO_ARIA_ROLE, AccessibilityDamage, LayoutDamage, QueryMsg,
33 ScrollContainerQueryFlags, StyleData, with_layout_state,
34};
35use net_traits::ReferrerPolicy;
36use net_traits::request::{CorsSettings, CredentialsMode};
37use rustc_hash::FxBuildHasher;
38use script_bindings::callback::RootedCallback;
39use script_bindings::cell::{DomRefCell, Ref, RefMut};
40use script_bindings::codegen::GenericBindings::AnimationBinding::AnimationMethods;
41use script_bindings::codegen::GenericBindings::KeyframeEffectBinding::KeyframeEffectMethods;
42use script_bindings::dom::UnrootedDom;
43use script_bindings::domstring::TracedDOMString;
44use script_bindings::reflector::DomObject;
45use selectors::attr::CaseSensitivity;
46use selectors::matching::ElementSelectorFlags;
47use selectors::sink::Push;
48use servo_arc::Arc as ServoArc;
49use style::applicable_declarations::ApplicableDeclarationBlock;
50use style::attr::{AttrValue, LengthOrPercentageOrAuto};
51use style::context::QuirksMode;
52use style::invalidation::element::restyle_hints::RestyleHint;
53use style::properties::longhands::{
54 self, background_image, border_spacing, color, font_family, font_size,
55};
56use style::properties::{
57 ComputedValues, Importance, PropertyDeclaration, PropertyDeclarationBlock,
58 parse_style_attribute,
59};
60use style::rule_tree::{CascadeLevel, CascadeOrigin};
61use style::selector_parser::{RestyleDamage, SelectorParser, Snapshot};
62use style::shared_lock::Locked;
63use style::str::string_as_ascii_lowercase;
64use style::stylesheets::layer_rule::LayerOrder;
65use style::stylesheets::{CssRuleType, UrlExtraData};
66use style::values::computed::{Overflow, UserSelect};
67use style::values::generics::NonNegative;
68use style::values::generics::position::PreferredRatio;
69use style::values::generics::ratio::Ratio;
70use style::values::{AtomIdent, AtomString, CSSFloat, computed, specified};
71use style::{ArcSlice, CaseSensitivityExt, dom_apis, thread_state};
72use style_traits::CSSPixel;
73use stylo_atoms::Atom;
74use stylo_dom::ElementState;
75use xml5ever::serialize::TraversalScope::{
76 ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode,
77};
78
79use crate::conversions::Convert;
80use crate::css::stylesheet_loader::StylesheetOwner;
81use crate::dom::RootedPromise;
82use crate::dom::accessors::AttrStrRef;
83use crate::dom::activation::Activatable;
84use crate::dom::animation::Animation;
85use crate::dom::animations::keyframeeffect::KeyframeEffect;
86use crate::dom::attr::{Attr, is_relevant_attribute};
87use crate::dom::bindings::cell::AtomicSafeBorrowMut;
88use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
89use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
90use crate::dom::bindings::codegen::Bindings::ElementBinding::{
91 ElementMethods, GetHTMLOptions, ScrollIntoViewContainer, ScrollLogicalPosition, ShadowRootInit,
92};
93use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
94use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
95use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
96use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
97use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
98 SetHTMLOptions, SetHTMLUnsafeOptions,
99};
100use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
101 ShadowRootMethods, ShadowRootMode, SlotAssignmentMode,
102};
103use crate::dom::bindings::codegen::Bindings::WindowBinding::{
104 ScrollBehavior, ScrollToOptions, WindowMethods,
105};
106use crate::dom::bindings::codegen::UnionTypes::{
107 BooleanOrScrollIntoViewOptions, NodeOrString, TrustedHTMLOrNullIsEmptyString,
108 TrustedHTMLOrString,
109 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString,
110 UnrestrictedDoubleOrKeyframeAnimationOptions, UnrestrictedDoubleOrKeyframeEffectOptions,
111};
112use crate::dom::bindings::conversions::DerivedFrom;
113use crate::dom::bindings::domname::{
114 self, is_valid_attribute_local_name, namespace_from_domstring,
115};
116use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
117use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
118use crate::dom::bindings::num::Finite;
119use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout};
120use crate::dom::bindings::str::DOMString;
121use crate::dom::csp::{CspReporting, InlineCheckType, SourcePosition};
122use crate::dom::customelementregistry::{
123 CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementRegistry,
124 CustomElementState, is_valid_custom_element_name,
125};
126use crate::dom::document::Document;
127use crate::dom::documentfragment::DocumentFragment;
128use crate::dom::domrect::DOMRect;
129use crate::dom::domrectlist::DOMRectList;
130use crate::dom::domtokenlist::DOMTokenList;
131use crate::dom::element::attributes::storage::{
132 AttrName, AttrRef, AttrValueRef, AttributeEntry, AttributeStorage, ContentAttributeData,
133};
134use crate::dom::element::create::create_element;
135use crate::dom::eventtarget::EventTarget;
136use crate::dom::globalscope::GlobalScope;
137use crate::dom::html::form_controls::htmlinputelement::HTMLInputElement;
138use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
139use crate::dom::html::htmlareaelement::HTMLAreaElement;
140use crate::dom::html::htmlbodyelement::HTMLBodyElement;
141use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
142use crate::dom::html::htmlcollection::HTMLCollection;
143use crate::dom::html::htmlelement::HTMLElement;
144use crate::dom::html::htmlembedelement::HTMLEmbedElement;
145use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
146use crate::dom::html::htmlfontelement::HTMLFontElement;
147use crate::dom::html::htmlformelement::FormControlElementHelpers;
148use crate::dom::html::htmlhrelement::{HTMLHRElement, SizePresentationalHint};
149use crate::dom::html::htmliframeelement::HTMLIFrameElement;
150use crate::dom::html::htmlimageelement::HTMLImageElement;
151use crate::dom::html::htmllabelelement::HTMLLabelElement;
152use crate::dom::html::htmllegendelement::HTMLLegendElement;
153use crate::dom::html::htmllinkelement::HTMLLinkElement;
154use crate::dom::html::htmlmarqueeelement::HTMLMarqueeElement;
155use crate::dom::html::htmlobjectelement::HTMLObjectElement;
156use crate::dom::html::htmloutputelement::HTMLOutputElement;
157use crate::dom::html::htmlscriptelement::HTMLScriptElement;
158use crate::dom::html::htmlselectelement::HTMLSelectElement;
159use crate::dom::html::htmlslotelement::{HTMLSlotElement, Slottable};
160use crate::dom::html::htmlstyleelement::HTMLStyleElement;
161use crate::dom::html::htmltablecellelement::HTMLTableCellElement;
162use crate::dom::html::htmltablecolelement::HTMLTableColElement;
163use crate::dom::html::htmltableelement::HTMLTableElement;
164use crate::dom::html::htmltablerowelement::HTMLTableRowElement;
165use crate::dom::html::htmltablesectionelement::HTMLTableSectionElement;
166use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
167use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
168use crate::dom::html::htmlvideoelement::HTMLVideoElement;
169use crate::dom::html::internals::elementinternals::ElementInternals;
170use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserverRegistration};
171use crate::dom::iterators::ShadowIncluding;
172use crate::dom::mathmlelement::MathMLElement;
173use crate::dom::mutationobserver::{Mutation, MutationObserver};
174use crate::dom::namednodemap::NamedNodeMap;
175use crate::dom::node::virtualmethods::{VirtualMethods, vtable_for};
176use crate::dom::node::{
177 BindContext, ChildrenMutation, CloneChildrenFlag, IsShadowTree, Node, NodeDamage, NodeFlags,
178 NodeTraits, UnbindContext,
179};
180use crate::dom::nodelist::NodeList;
181use crate::dom::range::Range;
182use crate::dom::raredata::ElementRareData;
183use crate::dom::sanitizer::Sanitizer;
184use crate::dom::servoparser::ServoParser;
185use crate::dom::shadowroot::shadowroot::{IsUserAgentWidget, ShadowRoot};
186use crate::dom::svg::svgelement::SVGElement;
187use crate::dom::text::Text;
188use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
189use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
190use crate::dom::validation::Validatable;
191use crate::dom::validitystate::ValidationFlags;
192use crate::dom::window::Window;
193use crate::dom::window::scrolling_box::{ScrollAxisState, ScrollingBox};
194use crate::event_loop::script_thread::ScriptThread;
195use crate::layout_dom::ServoDangerousStyleElement;
196use crate::realms::enter_auto_realm;
197
198#[dom_struct]
204pub struct Element {
205 node: Node,
206 #[no_trace]
207 local_name: LocalName,
208 #[no_trace]
209 namespace: Namespace,
210 #[no_trace]
211 prefix: DomRefCell<Option<Prefix>>,
212 attrs: AttributeStorage,
213 #[no_trace]
214 id_attribute: AtomicRefCell<Option<Atom>>,
215 #[conditional_malloc_size_of]
216 #[no_trace]
217 style_attribute: DomRefCell<Option<ServoArc<Locked<PropertyDeclarationBlock>>>>,
218 attr_list: MutNullableDom<NamedNodeMap>,
219 class_list: MutNullableDom<DOMTokenList>,
220 #[no_trace]
221 state: Cell<ElementState>,
222 selector_flags: AtomicUsize,
225 rare_data: DomRefCell<Option<Box<ElementRareData>>>,
226
227 #[no_trace]
230 style_data: DomRefCell<Option<Box<StyleData>>>,
231}
232
233impl fmt::Debug for Element {
234 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
235 write!(f, "<{}", self.local_name)?;
236 if let Some(ref id) = *self.id_attribute.borrow() {
237 write!(f, " id={}", id)?;
238 }
239 write!(f, ">")
240 }
241}
242
243#[derive(MallocSizeOf, PartialEq)]
244pub(crate) enum ElementCreator {
245 ParserCreated(u64),
246 ScriptCreated,
247}
248
249pub(crate) enum CustomElementCreationMode {
250 Synchronous,
251 Asynchronous,
252}
253
254impl ElementCreator {
255 pub(crate) fn is_parser_created(&self) -> bool {
256 match *self {
257 ElementCreator::ParserCreated(_) => true,
258 ElementCreator::ScriptCreated => false,
259 }
260 }
261 pub(crate) fn return_line_number(&self) -> u64 {
262 match *self {
263 ElementCreator::ParserCreated(l) => l,
264 ElementCreator::ScriptCreated => 1,
265 }
266 }
267}
268
269pub(crate) enum AdjacentPosition {
270 BeforeBegin,
271 AfterEnd,
272 AfterBegin,
273 BeforeEnd,
274}
275
276impl FromStr for AdjacentPosition {
277 type Err = Error;
278
279 fn from_str(position: &str) -> Result<Self, Self::Err> {
280 match_ignore_ascii_case! { position,
281 "beforebegin" => Ok(AdjacentPosition::BeforeBegin),
282 "afterbegin" => Ok(AdjacentPosition::AfterBegin),
283 "beforeend" => Ok(AdjacentPosition::BeforeEnd),
284 "afterend" => Ok(AdjacentPosition::AfterEnd),
285 _ => Err(Error::Syntax(Some("Invalid position".into())))
286 }
287 }
288}
289
290impl Element {
294 pub(crate) fn create(
295 cx: &mut JSContext,
296 name: QualName,
297 is: Option<LocalName>,
298 document: &Document,
299 creator: ElementCreator,
300 mode: CustomElementCreationMode,
301 proto: Option<HandleObject>,
302 ) -> DomRoot<Element> {
303 create_element(cx, name, is, document, creator, mode, proto)
304 }
305
306 pub(crate) fn new_inherited(
307 local_name: LocalName,
308 namespace: Namespace,
309 prefix: Option<Prefix>,
310 document: &Document,
311 ) -> Element {
312 Element::new_inherited_with_state(
313 ElementState::empty(),
314 local_name,
315 namespace,
316 prefix,
317 document,
318 )
319 }
320
321 pub(crate) fn new_inherited_with_state(
322 state: ElementState,
323 local_name: LocalName,
324 namespace: Namespace,
325 prefix: Option<Prefix>,
326 document: &Document,
327 ) -> Element {
328 Element {
329 node: Node::new_inherited(document),
330 local_name,
331 namespace,
332 prefix: DomRefCell::new(prefix),
333 attrs: Default::default(),
334 id_attribute: Default::default(),
335 style_attribute: DomRefCell::new(None),
336 attr_list: Default::default(),
337 class_list: Default::default(),
338 state: Cell::new(state),
339 selector_flags: Default::default(),
340 rare_data: Default::default(),
341 style_data: Default::default(),
342 }
343 }
344
345 pub(crate) fn set_had_duplicate_attributes(&self, no_gc: &NoGC) {
346 self.ensure_rare_data(no_gc).had_duplicate_attributes = true;
347 }
348
349 pub(crate) fn new(
350 cx: &mut JSContext,
351 local_name: LocalName,
352 namespace: Namespace,
353 prefix: Option<Prefix>,
354 document: &Document,
355 proto: Option<HandleObject>,
356 ) -> DomRoot<Element> {
357 Node::reflect_node_with_proto(
358 cx,
359 Box::new(Element::new_inherited(
360 local_name, namespace, prefix, document,
361 )),
362 document,
363 proto,
364 )
365 }
366
367 fn rare_data(&self) -> Ref<'_, Option<Box<ElementRareData>>> {
368 self.rare_data.borrow()
369 }
370
371 fn rare_data_mut(&self) -> RefMut<'_, Option<Box<ElementRareData>>> {
372 self.rare_data.borrow_mut()
373 }
374
375 pub(crate) fn ensure_rare_data<'a: 'b, 'b>(
376 &'a self,
377 no_gc: &'b NoGC,
378 ) -> RefMut<'b, Box<ElementRareData>> {
379 let mut rare_data = self.rare_data.safe_borrow_mut(no_gc);
380 if rare_data.is_none() {
381 *rare_data = Some(Default::default());
382 }
383 RefMut::map(rare_data, |rare_data| rare_data.as_mut().unwrap())
384 }
385
386 pub(crate) fn clean_up_style_data(&self) {
387 self.style_data.borrow_mut().take();
388 }
389
390 pub(crate) fn restyle(&self, no_gc: &NoGC, damage: NodeDamage) {
391 let doc = self.node.owner_document_unrooted(no_gc);
392 let mut restyle = doc.ensure_pending_restyle(self);
393
394 restyle.hint.insert(RestyleHint::RESTYLE_SELF);
397
398 match damage {
399 NodeDamage::Style => {},
400 NodeDamage::ContentOrHeritage => {
401 doc.note_dirty_element(no_gc, self);
402 restyle
403 .damage
404 .insert(RestyleDamage::from(LayoutDamage::DescendantHasBoxDamage));
405 },
406 NodeDamage::Other => {
407 doc.note_dirty_element(no_gc, self);
408 restyle.damage.insert(RestyleDamage::reconstruct());
409 },
410 }
411 }
412
413 pub(crate) fn has_dirty_descendants(&self) -> bool {
414 self.upcast::<Node>()
415 .get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)
416 }
417
418 pub(crate) fn note_dirty_descendants(&self, no_gc: &NoGC) {
419 self.upcast::<Node>()
420 .owner_doc_unrooted(no_gc)
421 .note_dirty_element(no_gc, self);
422 }
423
424 pub(crate) fn set_is(&self, is: LocalName, no_gc: &NoGC) {
425 self.ensure_rare_data(no_gc).is_value = Some(is);
426 }
427
428 pub(crate) fn get_is(&self) -> Option<LocalName> {
430 self.rare_data().as_ref()?.is_value.clone()
431 }
432
433 pub(crate) fn set_initial_custom_element_state_to_uncustomized(&self) {
442 let mut state = self.state.get();
443 state.insert(ElementState::DEFINED);
444 self.state.set(state);
445 }
446
447 pub(crate) fn set_custom_element_state(&self, state: CustomElementState, no_gc: &NoGC) {
449 if state != CustomElementState::Uncustomized {
451 self.ensure_rare_data(no_gc).custom_element_state = state;
452 }
453
454 let in_defined_state = matches!(
455 state,
456 CustomElementState::Uncustomized | CustomElementState::Custom
457 );
458 self.set_state(no_gc, ElementState::DEFINED, in_defined_state)
459 }
460
461 pub(crate) fn get_custom_element_state(&self) -> CustomElementState {
462 if let Some(rare_data) = self.rare_data().as_ref() {
463 return rare_data.custom_element_state;
464 }
465 CustomElementState::Uncustomized
466 }
467
468 pub(crate) fn is_custom(&self) -> bool {
470 self.get_custom_element_state() == CustomElementState::Custom
471 }
472
473 pub(crate) fn set_custom_element_definition(
474 &self,
475 definition: Rc<CustomElementDefinition>,
476 no_gc: &NoGC,
477 ) {
478 self.ensure_rare_data(no_gc).custom_element_definition = Some(definition);
479 }
480
481 pub(crate) fn get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>> {
482 self.rare_data().as_ref()?.custom_element_definition.clone()
483 }
484
485 pub(crate) fn clear_custom_element_definition(&self, no_gc: &NoGC) {
486 self.ensure_rare_data(no_gc).custom_element_definition = None;
487 }
488
489 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
490 pub(crate) fn push_callback_reaction(
491 &self,
492 function: RootedCallback<Function>,
493 args: Box<[Heap<JSVal>]>,
494 no_gc: &NoGC,
495 ) {
496 self.ensure_rare_data(no_gc)
497 .custom_element_reaction_queue
498 .push(CustomElementReaction::Callback(function.to_traced(), args));
499 }
500
501 pub(crate) fn push_upgrade_reaction(
502 &self,
503 definition: Rc<CustomElementDefinition>,
504 no_gc: &NoGC,
505 ) {
506 self.ensure_rare_data(no_gc)
507 .custom_element_reaction_queue
508 .push(CustomElementReaction::Upgrade(definition));
509 }
510
511 pub(crate) fn clear_reaction_queue(&self) {
512 if let Some(ref mut rare_data) = *self.rare_data_mut() {
513 rare_data.custom_element_reaction_queue.clear();
514 }
515 }
516
517 pub(crate) fn invoke_reactions(&self, cx: &mut JSContext) {
518 loop {
519 rooted_vec!(let mut reactions);
520 match *self.rare_data_mut() {
521 Some(ref mut data) => {
522 mem::swap(&mut *reactions, &mut data.custom_element_reaction_queue)
523 },
524 None => break,
525 };
526
527 if reactions.is_empty() {
528 break;
529 }
530
531 for reaction in reactions.iter() {
532 reaction.invoke(cx, self);
533 }
534
535 reactions.clear();
536 }
537 }
538
539 pub(crate) fn has_css_layout_box(&self) -> bool {
541 self.style().is_some_and(|s| !s.get_display().is_none())
542 }
543
544 pub(crate) fn is_potentially_scrollable_body(&self) -> bool {
546 self.is_potentially_scrollable_body_shared_logic(false)
547 }
548
549 pub(crate) fn is_potentially_scrollable_body_for_scrolling_element(&self) -> bool {
551 self.is_potentially_scrollable_body_shared_logic(true)
552 }
553
554 fn is_potentially_scrollable_body_shared_logic(
556 &self,
557 treat_overflow_clip_on_parent_as_hidden: bool,
558 ) -> bool {
559 let node = self.upcast::<Node>();
560 debug_assert!(
561 node.owner_doc().GetBody().as_deref() == self.downcast::<HTMLElement>(),
562 "Called is_potentially_scrollable_body on element that is not the <body>"
563 );
564
565 if !self.has_css_layout_box() {
569 return false;
570 }
571
572 if let Some(parent) = node.GetParentElement() &&
575 let Some(style) = parent.style()
576 {
577 let mut overflow_x = style.get_box().get_overflow_x();
578 let mut overflow_y = style.get_box().get_overflow_y();
579
580 if treat_overflow_clip_on_parent_as_hidden {
583 if overflow_x == &Overflow::Clip {
584 overflow_x = &Overflow::Hidden;
585 }
586 if overflow_y == &Overflow::Clip {
587 overflow_y = &Overflow::Hidden;
588 }
589 }
590
591 if !overflow_x.is_scrollable() && !overflow_y.is_scrollable() {
592 return false;
593 }
594 };
595
596 if let Some(style) = self.style() &&
599 !style.get_box().get_overflow_x().is_scrollable() &&
600 !style.get_box().get_overflow_y().is_scrollable()
601 {
602 return false;
603 };
604
605 true
606 }
607
608 pub(crate) fn establishes_scroll_container(&self, cx: &mut JSContext) -> bool {
611 self.upcast::<Node>()
613 .effective_overflow(cx)
614 .is_some_and(|overflow| overflow.establishes_scroll_container())
615 }
616
617 pub(crate) fn establishes_scroll_container_without_reflow(&self) -> bool {
619 self.upcast::<Node>()
620 .effective_overflow_without_reflow()
621 .is_some_and(|overflow| overflow.establishes_scroll_container())
622 }
623
624 pub(crate) fn has_overflow(&self, cx: &mut JSContext) -> bool {
625 self.ScrollHeight(cx) > self.ClientHeight(cx) || self.ScrollWidth(cx) > self.ClientWidth(cx)
626 }
627
628 fn has_scrolling_box(&self, cx: &mut JSContext) -> bool {
636 self.has_css_layout_box() && self.establishes_scroll_container(cx) && self.has_overflow(cx)
637 }
638
639 pub(crate) fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
640 self.rare_data()
641 .as_ref()?
642 .shadow_root
643 .as_ref()
644 .map(|sr| DomRoot::from_ref(&**sr))
645 }
646
647 pub(crate) fn shadow_root_unrooted<'a>(
648 &self,
649 no_gc: &'a NoGC,
650 ) -> Option<UnrootedDom<'a, ShadowRoot>> {
651 self.rare_data()
652 .as_ref()?
653 .shadow_root
654 .as_ref()
655 .map(|shadow_root| shadow_root.as_unrooted(no_gc))
656 }
657
658 pub(crate) fn is_shadow_host(&self) -> bool {
659 self.shadow_root().is_some()
660 }
661
662 #[expect(clippy::too_many_arguments)]
664 pub(crate) fn attach_shadow(
665 &self,
666 cx: &mut JSContext,
667 is_ua_widget: IsUserAgentWidget,
668 mode: ShadowRootMode,
669 clonable: bool,
670 serializable: bool,
671 delegates_focus: bool,
672 slot_assignment_mode: SlotAssignmentMode,
673 ) -> Fallible<DomRoot<ShadowRoot>> {
674 if self.namespace != ns!(html) {
677 return Err(Error::NotSupported(Some(
678 "Cannot attach shadow roots to elements with non-HTML namespaces".to_owned(),
679 )));
680 }
681
682 if !is_valid_shadow_host_name(self.local_name()) {
685 if is_ua_widget != IsUserAgentWidget::Yes {
687 let error_message = format!(
688 "Cannot attach shadow roots to <{}> elements",
689 *self.local_name()
690 );
691 return Err(Error::NotSupported(Some(error_message)));
692 }
693 }
694
695 if is_valid_custom_element_name(self.local_name()) || self.get_is().is_some() {
698 let definition = self.get_custom_element_definition();
702 if definition.is_some_and(|definition| definition.disable_shadow) {
705 let error_message = format!(
706 "The custom element constructor of <{}> disabled attachment of shadow roots",
707 self.local_name()
708 );
709 return Err(Error::NotSupported(Some(error_message)));
710 }
711 }
712
713 if let Some(current_shadow_root) = self.shadow_root() {
716 if !current_shadow_root.is_declarative() ||
720 current_shadow_root.shadow_root_mode() != mode
721 {
722 return Err(Error::NotSupported(Some(
723 "Cannot attach a second shadow root to the same element".into(),
724 )));
725 }
726
727 for child in current_shadow_root.upcast::<Node>().children() {
729 child.remove_self(cx);
730 }
731
732 current_shadow_root.set_declarative(false);
734
735 return Ok(current_shadow_root);
737 }
738
739 let document = self.node.owner_doc();
746 let shadow_root = ShadowRoot::new(
747 cx,
748 self,
749 &document,
750 mode,
751 slot_assignment_mode,
752 clonable,
753 is_ua_widget,
754 );
755
756 let node = self.upcast::<Node>();
760 if node.is_connected() {
761 document.remove_style_and_layout_data_from_subtree(cx.no_gc(), node);
762 }
763 if let Some(selection) = self.owner_document().selection() &&
764 node.get_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION)
765 {
766 selection.set_visible_selection_dirty();
767 }
768
769 shadow_root.set_delegates_focus(delegates_focus);
771
772 if matches!(
775 self.get_custom_element_state(),
776 CustomElementState::Precustomized | CustomElementState::Custom
777 ) {
778 shadow_root.set_available_to_element_internals(true);
779 }
780
781 shadow_root.set_declarative(false);
783
784 shadow_root.set_serializable(serializable);
786
787 self.ensure_rare_data(cx.no_gc()).shadow_root = Some(Dom::from_ref(&*shadow_root));
789 shadow_root
790 .upcast::<Node>()
791 .set_containing_shadow_root(Some(&shadow_root));
792
793 let bind_context = BindContext::new(self.upcast(), IsShadowTree::Yes);
794 shadow_root.bind_to_tree(cx, &bind_context);
795
796 node.dirty(cx.no_gc(), NodeDamage::Other);
797
798 Ok(shadow_root)
799 }
800
801 pub(crate) fn is_translate_enabled(&self) -> bool {
803 let name = &local_name!("translate");
804 if self.has_attribute(name) {
805 let attribute = self.get_string_attribute(name);
806 match_ignore_ascii_case! { &*attribute.str(),
807 "yes" | "" => return true,
808 "no" => return false,
809 _ => {},
810 }
811 }
812 if let Some(parent) = self.upcast::<Node>().GetParentNode() &&
813 let Some(elem) = parent.downcast::<Element>()
814 {
815 return elem.is_translate_enabled();
816 }
817 true
818 }
819
820 pub(crate) fn directionality(&self) -> String {
822 self.downcast::<HTMLElement>()
823 .and_then(|html_element| html_element.directionality())
824 .unwrap_or_else(|| {
825 let node = self.upcast::<Node>();
826 node.parent_directionality()
827 })
828 }
829
830 pub(crate) fn is_root(&self) -> bool {
831 match self.node.GetParentNode() {
832 None => false,
833 Some(node) => node.is::<Document>(),
834 }
835 }
836
837 pub(crate) fn registered_intersection_observers_mut<'a: 'b, 'b>(
840 &'a self,
841 no_gc: &'b NoGC,
842 ) -> RefMut<'b, Vec<IntersectionObserverRegistration>> {
843 RefMut::map(self.ensure_rare_data(no_gc), |rare_data| {
844 &mut rare_data.registered_intersection_observers
845 })
846 }
847
848 pub(crate) fn registered_intersection_observers(
849 &self,
850 ) -> Option<Ref<'_, Vec<IntersectionObserverRegistration>>> {
851 let rare_data: Ref<'_, _> = self.rare_data.borrow();
852
853 if rare_data.is_none() {
854 return None;
855 }
856 Some(Ref::map(rare_data, |rare_data| {
857 &rare_data
858 .as_ref()
859 .unwrap()
860 .registered_intersection_observers
861 }))
862 }
863
864 pub(crate) fn get_intersection_observer_registration(
865 &self,
866 observer: &IntersectionObserver,
867 ) -> Option<Ref<'_, IntersectionObserverRegistration>> {
868 if let Some(registrations) = self.registered_intersection_observers() {
869 registrations
870 .iter()
871 .position(|reg_obs| reg_obs.observer == observer)
872 .map(|index| Ref::map(registrations, |registrations| ®istrations[index]))
873 } else {
874 None
875 }
876 }
877
878 pub(crate) fn add_initial_intersection_observer_registration(
880 &self,
881 observer: &IntersectionObserver,
882 no_gc: &NoGC,
883 ) {
884 self.ensure_rare_data(no_gc)
885 .registered_intersection_observers
886 .push(IntersectionObserverRegistration::new_initial(observer));
887 }
888
889 pub(crate) fn remove_intersection_observer(
891 &self,
892 observer: &IntersectionObserver,
893 no_gc: &NoGC,
894 ) {
895 self.ensure_rare_data(no_gc)
896 .registered_intersection_observers
897 .retain(|reg_obs| *reg_obs.observer != *observer)
898 }
899
900 pub(crate) fn scrolling_box(&self, flags: ScrollContainerQueryFlags) -> Option<ScrollingBox> {
903 self.owner_window()
904 .scrolling_box_query(Some(self.upcast()), flags)
905 }
906
907 pub(crate) fn scroll_into_view_with_options(
909 &self,
910 cx: &mut JSContext,
911 behavior: ScrollBehavior,
912 block: ScrollAxisState,
913 inline: ScrollAxisState,
914 container: Option<&Element>,
915 inner_target_rect: Option<Rect<Au, CSSPixel>>,
916 ) {
917 let get_target_rect = |cx: &mut JSContext| match inner_target_rect {
918 None => self.upcast::<Node>().border_box(cx).unwrap_or_default(),
919 Some(inner_target_rect) => inner_target_rect.translate(
920 self.upcast::<Node>()
921 .content_box(cx)
922 .unwrap_or_default()
923 .origin
924 .to_vector(),
925 ),
926 };
927
928 let mut parent_scrolling_box = self.scrolling_box(ScrollContainerQueryFlags::empty());
931 while let Some(scrolling_box) = parent_scrolling_box {
932 parent_scrolling_box = scrolling_box.parent();
933
934 let target_rect = get_target_rect(cx);
945 let position =
946 scrolling_box.determine_scroll_into_view_position(cx, block, inline, target_rect);
947
948 if position != scrolling_box.scroll_position(cx) {
953 scrolling_box.scroll_to(cx, position, behavior);
964 }
965
966 if container.is_some_and(|container| {
970 let container_node = container.upcast::<Node>();
971 scrolling_box
972 .node()
973 .is_shadow_including_inclusive_ancestor_of(container_node)
974 }) {
975 return;
976 }
977 }
978
979 let window_proxy = self.owner_window_unrooted(cx.no_gc()).window_proxy();
980 let Some(frame_element) = window_proxy.frame_element() else {
981 return;
982 };
983
984 let inner_target_rect = Some(get_target_rect(cx));
985 let mut realm = enter_auto_realm(cx, frame_element);
986 let cx = &mut realm;
987 frame_element.scroll_into_view_with_options(
988 cx,
989 behavior,
990 block,
991 inline,
992 None,
993 inner_target_rect,
994 )
995 }
996
997 pub(crate) fn ensure_contenteditable_selection_range(
998 &self,
999 cx: &mut JSContext,
1000 document: &Document,
1001 ) -> DomRoot<Range> {
1002 let Some(selection_range) = self
1003 .rare_data()
1004 .as_ref()
1005 .and_then(|data| data.contenteditable_selection_range.get())
1006 else {
1007 let range = Range::new_with_doc(cx, document, None);
1008 self.ensure_rare_data(cx.no_gc())
1009 .contenteditable_selection_range
1010 .set(Some(&*range));
1011 return range;
1012 };
1013 selection_range
1014 }
1015
1016 pub(crate) fn handle_scroll_event(&self) {
1021 let document = self.owner_document();
1023
1024 document.finish_handle_scroll_event(self.upcast());
1033 }
1034
1035 pub(crate) fn style(&self) -> Option<ServoArc<ComputedValues>> {
1036 self.owner_window()
1037 .layout_reflow_deprecated(QueryMsg::StyleQuery);
1038 self.style_without_reflow()
1039 }
1040
1041 pub(crate) fn style_without_reflow(&self) -> Option<ServoArc<ComputedValues>> {
1044 self.style_data
1045 .borrow()
1046 .as_ref()
1047 .map(|data| data.element_data.borrow().styles.primary().clone())
1048 }
1049
1050 pub(crate) fn is_styled(&self) -> bool {
1051 self.style_data.borrow().is_some()
1052 }
1053
1054 pub(crate) fn is_display_none(&self) -> bool {
1055 self.style_data.borrow().as_ref().is_none_or(|data| {
1056 data.element_data
1057 .borrow()
1058 .styles
1059 .primary()
1060 .get_box()
1061 .display
1062 .is_none()
1063 })
1064 }
1065
1066 pub(crate) fn computed_user_select(&self) -> Option<UserSelect> {
1071 Some(
1072 self.style_data
1073 .borrow()
1074 .as_ref()?
1075 .element_data
1076 .borrow()
1077 .styles
1078 .primary()
1079 .get_ui()
1080 .user_select,
1081 )
1082 }
1083
1084 pub(crate) fn check_style_on_self_or_eager_pseudos(
1085 &self,
1086 check_styles_fn: impl Fn(&ComputedValues) -> bool,
1087 ) -> bool {
1088 let style_data = self.style_data.borrow();
1089 let Some(data) = style_data.as_ref().map(|data| data.element_data.borrow()) else {
1090 return false;
1091 };
1092
1093 if check_styles_fn(data.styles.primary()) {
1094 return true;
1095 }
1096
1097 let mut pseudo_styles = data.styles.pseudos.as_array().iter();
1098 pseudo_styles.any(|style| style.as_deref().is_some_and(&check_styles_fn))
1099 }
1100}
1101
1102#[inline]
1104pub(crate) fn is_valid_shadow_host_name(name: &LocalName) -> bool {
1105 if is_valid_custom_element_name(name) {
1108 return true;
1109 }
1110
1111 matches!(
1114 name,
1115 &local_name!("article") |
1116 &local_name!("aside") |
1117 &local_name!("blockquote") |
1118 &local_name!("body") |
1119 &local_name!("div") |
1120 &local_name!("footer") |
1121 &local_name!("h1") |
1122 &local_name!("h2") |
1123 &local_name!("h3") |
1124 &local_name!("h4") |
1125 &local_name!("h5") |
1126 &local_name!("h6") |
1127 &local_name!("header") |
1128 &local_name!("main") |
1129 &local_name!("nav") |
1130 &local_name!("p") |
1131 &local_name!("section") |
1132 &local_name!("span")
1133 )
1134}
1135
1136#[inline]
1137#[expect(unsafe_code)]
1138pub(crate) fn get_attr_for_layout<'dom>(
1139 elem: LayoutDom<'dom, Element>,
1140 namespace: &Namespace,
1141 name: &LocalName,
1142) -> Option<&'dom AttrValue> {
1143 let storage = unsafe { elem.unsafe_get().attrs.borrow_for_layout() };
1144 storage
1145 .iter()
1146 .find(|e: &&AttributeEntry| {
1147 name == e.local_name_for_layout() && namespace == e.namespace_for_layout()
1148 })
1149 .map(|e: &AttributeEntry| e.value_for_layout())
1150}
1151
1152impl<'dom> LayoutDom<'dom, Element> {
1153 #[inline]
1154 pub(crate) fn is_root(&self) -> bool {
1155 self.upcast::<Node>()
1156 .parent_node_ref()
1157 .is_some_and(|parent| matches!(parent.type_id_for_layout(), NodeTypeId::Document(_)))
1158 }
1159
1160 pub(crate) fn is_body_element_of_html_element_root(&self) -> bool {
1162 if self.local_name() != &local_name!("body") {
1163 return false;
1164 }
1165 let Some(parent_node) = self.upcast::<Node>().parent_node_ref() else {
1166 return false;
1167 };
1168 let Some(parent_element) = parent_node.downcast::<Element>() else {
1169 return false;
1170 };
1171 parent_element.local_name() == &local_name!("html")
1172 }
1173
1174 #[expect(unsafe_code)]
1176 #[inline]
1177 pub(crate) fn each_attr_name_for_layout<F>(self, mut callback: F)
1178 where
1179 F: FnMut(&LocalName),
1180 {
1181 let storage = unsafe { self.unsafe_get().attrs.borrow_for_layout() };
1182 for entry in storage.iter() {
1183 callback(entry.local_name_for_layout());
1184 }
1185 }
1186
1187 #[inline]
1188 pub(crate) fn has_class_or_part_for_layout(
1189 self,
1190 name: &AtomIdent,
1191 attr_name: &LocalName,
1192 case_sensitivity: CaseSensitivity,
1193 ) -> bool {
1194 get_attr_for_layout(self, &ns!(), attr_name).is_some_and(|attr| {
1195 attr.as_tokens()
1196 .iter()
1197 .any(|atom| case_sensitivity.eq_atom(atom, name))
1198 })
1199 }
1200
1201 #[inline]
1202 pub(crate) fn get_classes_for_layout(self) -> Option<&'dom [Atom]> {
1203 get_attr_for_layout(self, &ns!(), &local_name!("class")).map(|attr| attr.as_tokens())
1204 }
1205
1206 #[inline]
1207 pub(crate) fn get_parts_for_layout(self) -> Option<&'dom [Atom]> {
1208 get_attr_for_layout(self, &ns!(), &local_name!("part")).map(|attr| attr.as_tokens())
1209 }
1210
1211 #[inline]
1212 pub(crate) fn dimension_attr_value(self, name: LocalName) -> LengthOrPercentageOrAuto {
1213 self.get_attr_for_layout(&ns!(), &name)
1214 .map(AttrValue::as_dimension)
1215 .cloned()
1216 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1217 }
1218
1219 #[inline]
1220 #[expect(unsafe_code)]
1221 pub(crate) fn style_data(self) -> Option<&'dom StyleData> {
1222 unsafe { self.unsafe_get().style_data.borrow_for_layout().as_deref() }
1223 }
1224
1225 #[inline]
1226 #[expect(unsafe_code)]
1227 pub(crate) unsafe fn initialize_style_data(self) {
1228 let data = unsafe { self.unsafe_get().style_data.borrow_mut_for_layout() };
1229 debug_assert!(data.is_none());
1230 *data = Some(Box::default());
1231 }
1232
1233 #[inline]
1234 #[expect(unsafe_code)]
1235 pub(crate) unsafe fn clear_style_data(self) {
1236 unsafe {
1237 self.unsafe_get().style_data.borrow_mut_for_layout().take();
1238 }
1239 }
1240
1241 #[inline]
1242 fn apply_property_for_length_percentage<Callback>(
1243 self,
1244 length_percentage: LengthOrPercentageOrAuto,
1245 callback: Callback,
1246 ) where
1247 Callback: FnOnce(specified::LengthPercentage),
1248 {
1249 match length_percentage {
1250 LengthOrPercentageOrAuto::Auto => {},
1251 LengthOrPercentageOrAuto::Percentage(percentage) => {
1252 let value = specified::LengthPercentage::Percentage(
1253 specified::NoCalcPercentage::new(percentage),
1254 );
1255 callback(value)
1256 },
1257 LengthOrPercentageOrAuto::Length(length) => {
1258 let value = specified::LengthPercentage::Length(specified::NoCalcLength::from_px(
1259 length.to_f32_px(),
1260 ));
1261 callback(value)
1262 },
1263 }
1264 }
1265
1266 pub(crate) fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
1267 where
1268 V: Push<ApplicableDeclarationBlock>,
1269 {
1270 let document = self.upcast::<Node>().owner_doc_for_layout();
1273 let mut property_declaration_block = None;
1274 let mut push = |declaration| {
1275 property_declaration_block
1276 .get_or_insert_with(PropertyDeclarationBlock::default)
1277 .push(declaration, Importance::Normal);
1278 };
1279
1280 if let Some(lang) = self.get_lang_attr_val_for_layout() {
1283 push(PropertyDeclaration::XLang(specified::XLang(Atom::from(
1284 lang.to_owned(),
1285 ))));
1286 }
1287
1288 let bgcolor = if let Some(this) = self.downcast::<HTMLBodyElement>() {
1289 this.get_background_color()
1290 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1291 this.get_background_color()
1292 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1293 this.get_background_color()
1294 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1295 this.get_background_color()
1296 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1297 this.get_background_color()
1298 } else {
1299 None
1300 };
1301
1302 if let Some(color) = bgcolor {
1303 push(PropertyDeclaration::BackgroundColor(
1304 specified::Color::from_absolute_color(color),
1305 ));
1306 }
1307
1308 if is_element_affected_by_legacy_background_presentational_hint(
1309 self.namespace(),
1310 self.local_name(),
1311 ) && let Some(url) = self
1312 .get_attr_for_layout(&ns!(), &local_name!("background"))
1313 .and_then(AttrValue::as_resolved_url)
1314 .cloned()
1315 {
1316 push(PropertyDeclaration::BackgroundImage(
1317 background_image::SpecifiedValue(vec![specified::Image::for_cascade(url)].into()),
1318 ));
1319 }
1320
1321 let color = if let Some(this) = self.downcast::<HTMLFontElement>() {
1322 this.get_color()
1323 } else if let Some(this) = self.downcast::<HTMLBodyElement>() {
1324 this.get_color()
1326 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1327 this.get_color()
1329 } else {
1330 None
1331 };
1332
1333 if let Some(color) = color {
1334 push(PropertyDeclaration::Color(
1335 longhands::color::SpecifiedValue(specified::Color::from_absolute_color(color)),
1336 ));
1337 }
1338
1339 let font_face = self
1340 .downcast::<HTMLFontElement>()
1341 .and_then(LayoutDom::get_face);
1342 if let Some(font_face) = font_face {
1343 push(PropertyDeclaration::FontFamily(
1344 font_family::SpecifiedValue::Values(computed::font::FontFamilyList {
1345 list: ArcSlice::from_iter(
1346 HTMLFontElement::parse_face_attribute(font_face).into_iter(),
1347 ),
1348 }),
1349 ));
1350 }
1351
1352 let font_size = self
1353 .downcast::<HTMLFontElement>()
1354 .and_then(LayoutDom::get_size);
1355 if let Some(font_size) = font_size {
1356 push(PropertyDeclaration::FontSize(
1357 font_size::SpecifiedValue::from_html_size(font_size as u8),
1358 ));
1359 }
1360
1361 let size = self
1367 .downcast::<HTMLInputElement>()
1368 .and_then(|input_element| {
1369 match self.get_attr_val_for_layout(&ns!(), &local_name!("type")) {
1371 Some("hidden") | Some("range") | Some("color") | Some("checkbox") |
1372 Some("radio") | Some("file") | Some("submit") | Some("image") |
1373 Some("reset") | Some("button") => None,
1374 _ => match input_element.size_for_layout() {
1376 0 => None,
1377 s => Some(s as i32),
1378 },
1379 }
1380 });
1381
1382 if let Some(size) = size {
1383 let value = specified::NoCalcLength::from_servo_character_width(size);
1384 push(PropertyDeclaration::Width(
1385 specified::Size::LengthPercentage(NonNegative(
1386 specified::LengthPercentage::Length(value),
1387 )),
1388 ));
1389 }
1390
1391 let width = if let Some(this) = self.downcast::<HTMLEmbedElement>() {
1392 this.width()
1393 } else if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1394 this.width()
1395 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1396 this.width()
1397 } else if let Some(this) = self.downcast::<HTMLInputElement>() {
1398 this.width()
1399 } else if let Some(this) = self.downcast::<HTMLMarqueeElement>() {
1400 this.width()
1401 } else if let Some(this) = self.downcast::<HTMLObjectElement>() {
1402 this.width()
1403 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1404 this.width()
1405 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1406 this.width()
1407 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1408 this.width()
1409 } else if let Some(this) = self.downcast::<HTMLTableColElement>() {
1410 this.width()
1411 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1412 this.width()
1414 } else {
1415 LengthOrPercentageOrAuto::Auto
1416 };
1417
1418 self.apply_property_for_length_percentage(width, |value| {
1419 push(PropertyDeclaration::Width(
1420 specified::Size::LengthPercentage(NonNegative(value)),
1421 ));
1422 });
1423
1424 let height = if let Some(this) = self.downcast::<HTMLEmbedElement>() {
1425 this.height()
1426 } else if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1427 this.height()
1428 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1429 this.height()
1430 } else if let Some(this) = self.downcast::<HTMLInputElement>() {
1431 this.height()
1432 } else if let Some(this) = self.downcast::<HTMLMarqueeElement>() {
1433 this.height()
1434 } else if let Some(this) = self.downcast::<HTMLObjectElement>() {
1435 this.height()
1436 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1437 this.height()
1438 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1439 this.height()
1440 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1441 this.height()
1442 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1443 this.height()
1444 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1445 this.height()
1446 } else {
1447 LengthOrPercentageOrAuto::Auto
1448 };
1449
1450 self.apply_property_for_length_percentage(height, |value| {
1451 push(PropertyDeclaration::Height(
1452 specified::Size::LengthPercentage(NonNegative(value)),
1453 ));
1454 });
1455
1456 let margin_right_left = if let Some(this) = self.downcast::<HTMLEmbedElement>() {
1457 this.margin_right_left()
1458 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1459 this.margin_right_left()
1460 } else if let Some(this) = self.downcast::<HTMLInputElement>() {
1461 this.margin_right_left()
1462 } else if let Some(this) = self.downcast::<HTMLMarqueeElement>() {
1463 this.margin_right_left()
1464 } else if let Some(this) = self.downcast::<HTMLObjectElement>() {
1465 this.margin_right_left()
1466 } else {
1467 LengthOrPercentageOrAuto::Auto
1468 };
1469
1470 self.apply_property_for_length_percentage(margin_right_left, |value| {
1471 let value = specified::Margin::LengthPercentage(value);
1472 push(PropertyDeclaration::MarginLeft(value.clone()));
1473 push(PropertyDeclaration::MarginRight(value));
1474 });
1475
1476 let margin_top_bottom = if let Some(this) = self.downcast::<HTMLEmbedElement>() {
1477 this.margin_top_bottom()
1478 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1479 this.margin_top_bottom()
1480 } else if let Some(this) = self.downcast::<HTMLInputElement>() {
1481 this.margin_top_bottom()
1482 } else if let Some(this) = self.downcast::<HTMLMarqueeElement>() {
1483 this.margin_top_bottom()
1484 } else if let Some(this) = self.downcast::<HTMLObjectElement>() {
1485 this.margin_top_bottom()
1486 } else {
1487 LengthOrPercentageOrAuto::Auto
1488 };
1489
1490 self.apply_property_for_length_percentage(margin_top_bottom, |value| {
1491 let value = specified::Margin::LengthPercentage(value);
1492 push(PropertyDeclaration::MarginTop(value.clone()));
1493 push(PropertyDeclaration::MarginBottom(value));
1494 });
1495
1496 if let Some(svg_element) = self.downcast::<SVGElement>() {
1497 svg_element.synthesize_presentational_hints(document, &mut push);
1498 }
1499
1500 if let Some(mathml_element) = self.downcast::<MathMLElement>() {
1501 mathml_element.synthesize_presentational_hints(document, &mut push);
1502 }
1503
1504 if (self.is::<HTMLImageElement>() || self.is::<HTMLVideoElement>()) &&
1507 let LengthOrPercentageOrAuto::Length(width) = width &&
1508 let LengthOrPercentageOrAuto::Length(height) = height
1509 {
1510 let width_value = NonNegative(specified::Number::new(width.to_f32_px()));
1511 let height_value = NonNegative(specified::Number::new(height.to_f32_px()));
1512 let aspect_ratio = specified::position::AspectRatio {
1513 auto: true,
1514 ratio: PreferredRatio::Ratio(Ratio(width_value, height_value)),
1515 };
1516 push(PropertyDeclaration::AspectRatio(Box::new(aspect_ratio)));
1517 }
1518
1519 let cols = self
1520 .downcast::<HTMLTextAreaElement>()
1521 .map(LayoutDom::get_cols);
1522 if let Some(cols) = cols {
1523 let cols = cols as i32;
1524 if cols > 0 {
1525 let value = specified::NoCalcLength::from_servo_character_width(cols);
1531 push(PropertyDeclaration::Width(
1532 specified::Size::LengthPercentage(NonNegative(
1533 specified::LengthPercentage::Length(value),
1534 )),
1535 ));
1536 }
1537 }
1538
1539 let rows = self
1540 .downcast::<HTMLTextAreaElement>()
1541 .map(LayoutDom::get_rows);
1542 if let Some(rows) = rows {
1543 let rows = rows as i32;
1544 if rows > 0 {
1545 let value = specified::NoCalcLength::from_em(rows as CSSFloat);
1549 push(PropertyDeclaration::Height(
1550 specified::Size::LengthPercentage(NonNegative(
1551 specified::LengthPercentage::Length(value),
1552 )),
1553 ));
1554 }
1555 }
1556
1557 if let Some(table) = self.downcast::<HTMLTableElement>() {
1558 if let Some(cellspacing) = table.get_cellspacing() {
1559 let width_value = specified::Length::from_px(cellspacing as f32);
1560 push(PropertyDeclaration::BorderSpacing(
1561 border_spacing::SpecifiedValue::new(
1562 width_value.clone().into(),
1563 width_value.into(),
1564 ),
1565 ));
1566 }
1567 if let Some(border) = table.get_border() {
1568 let width_value = specified::BorderSideWidth::from_px(border as f32);
1569 push(PropertyDeclaration::BorderTopWidth(width_value.clone()));
1570 push(PropertyDeclaration::BorderLeftWidth(width_value.clone()));
1571 push(PropertyDeclaration::BorderBottomWidth(width_value.clone()));
1572 push(PropertyDeclaration::BorderRightWidth(width_value));
1573 }
1574 if document.quirks_mode() == QuirksMode::Quirks {
1575 push(PropertyDeclaration::Color(color::SpecifiedValue(
1577 specified::Color::InheritFromBodyQuirk,
1578 )));
1579 }
1580 }
1581
1582 if let Some(cellpadding) = self
1583 .downcast::<HTMLTableCellElement>()
1584 .and_then(|this| this.get_table())
1585 .and_then(|table| table.get_cellpadding())
1586 {
1587 let cellpadding = NonNegative(specified::LengthPercentage::Length(
1588 specified::NoCalcLength::from_px(cellpadding as f32),
1589 ));
1590 push(PropertyDeclaration::PaddingTop(cellpadding.clone()));
1591 push(PropertyDeclaration::PaddingLeft(cellpadding.clone()));
1592 push(PropertyDeclaration::PaddingBottom(cellpadding.clone()));
1593 push(PropertyDeclaration::PaddingRight(cellpadding));
1594 }
1595
1596 if let Some(size_info) = self
1598 .downcast::<HTMLHRElement>()
1599 .and_then(|hr_element| hr_element.get_size_info())
1600 {
1601 match size_info {
1602 SizePresentationalHint::SetHeightTo(height) => {
1603 push(PropertyDeclaration::Height(height));
1604 },
1605 SizePresentationalHint::SetAllBorderWidthValuesTo(border_width) => {
1606 push(PropertyDeclaration::BorderLeftWidth(border_width.clone()));
1607 push(PropertyDeclaration::BorderRightWidth(border_width.clone()));
1608 push(PropertyDeclaration::BorderTopWidth(border_width.clone()));
1609 push(PropertyDeclaration::BorderBottomWidth(border_width));
1610 },
1611 SizePresentationalHint::SetBottomBorderWidthToZero => {
1612 push(PropertyDeclaration::BorderBottomWidth(
1613 specified::border::BorderSideWidth::from_px(0.),
1614 ));
1615 },
1616 }
1617 }
1618
1619 let Some(property_declaration_block) = property_declaration_block else {
1620 return;
1621 };
1622
1623 let shared_lock = &document.shared_style_locks().author;
1624 hints.push(ApplicableDeclarationBlock::from_declarations(
1625 ServoArc::new(shared_lock.wrap(property_declaration_block)),
1626 CascadeLevel::new(CascadeOrigin::PresHints),
1627 LayerOrder::root(),
1628 ));
1629 }
1630
1631 pub(crate) fn get_span(self) -> Option<u32> {
1632 self.downcast::<HTMLTableColElement>()
1634 .and_then(|element| element.get_span())
1635 }
1636
1637 pub(crate) fn get_colspan(self) -> Option<u32> {
1638 self.downcast::<HTMLTableCellElement>()
1640 .and_then(|element| element.get_colspan())
1641 }
1642
1643 pub(crate) fn get_rowspan(self) -> Option<u32> {
1644 self.downcast::<HTMLTableCellElement>()
1646 .and_then(|element| element.get_rowspan())
1647 }
1648
1649 #[inline]
1650 pub(crate) fn is_html_element(&self) -> bool {
1651 *self.namespace() == ns!(html)
1652 }
1653
1654 pub(crate) fn id_attribute(self) -> Option<AtomicRef<'dom, Atom>> {
1655 AtomicRef::filter_map(self.unsafe_get().id_attribute.borrow(), Option::as_ref)
1656 }
1657
1658 #[expect(unsafe_code)]
1663 pub(crate) unsafe fn id_attribute_unguarded(self) -> Option<&'dom Atom> {
1664 unsafe { (*self.unsafe_get().id_attribute.as_ptr()).as_ref() }
1665 }
1666
1667 #[expect(unsafe_code)]
1668 pub(crate) fn style_attribute(
1669 self,
1670 ) -> *const Option<ServoArc<Locked<PropertyDeclarationBlock>>> {
1671 unsafe { (self.unsafe_get()).style_attribute.borrow_for_layout() }
1672 }
1673
1674 pub(crate) fn local_name(self) -> &'dom LocalName {
1675 &(self.unsafe_get()).local_name
1676 }
1677
1678 pub(crate) fn namespace(self) -> &'dom Namespace {
1679 &(self.unsafe_get()).namespace
1680 }
1681
1682 pub(crate) fn get_lang_attr_val_for_layout(self) -> Option<&'dom str> {
1684 if let Some(attr) = self.get_attr_val_for_layout(&ns!(xml), &local_name!("lang")) {
1687 return Some(attr);
1688 }
1689 if self.is_html_element() || self.namespace() == &ns!(svg) {
1693 return self.get_attr_val_for_layout(&ns!(), &local_name!("lang"));
1694 }
1695 None
1696 }
1697
1698 pub(crate) fn get_lang_for_layout(self) -> AtomString {
1700 let mut current_node = Some(self.upcast::<Node>());
1703 while let Some(node) = current_node {
1704 current_node = node.composed_parent_node_ref();
1709 match node.downcast::<Element>() {
1710 Some(elem) => {
1711 if let Some(attr) = elem.get_lang_attr_val_for_layout() {
1712 return AtomString::from(attr);
1713 }
1714 },
1715 None => continue,
1716 }
1717 }
1718 let document = self.upcast::<Node>().owner_doc_for_layout();
1733 if let Some(document_language) = document.default_language_for_layout().as_deref() {
1734 return AtomString::from(document_language);
1735 }
1736 AtomString::default()
1737 }
1738
1739 #[inline]
1740 pub(crate) fn get_state_for_layout(self) -> ElementState {
1741 (self.unsafe_get()).state.get()
1742 }
1743
1744 #[inline]
1745 pub(crate) fn insert_selector_flags(self, flags: ElementSelectorFlags) {
1746 debug_assert!(thread_state::get().is_layout());
1747 self.unsafe_get().insert_selector_flags(flags);
1748 }
1749
1750 #[inline]
1751 pub(crate) fn get_selector_flags(self) -> ElementSelectorFlags {
1752 self.unsafe_get().get_selector_flags()
1753 }
1754
1755 #[inline]
1756 #[expect(unsafe_code)]
1757 pub(crate) fn get_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
1758 unsafe {
1759 self.unsafe_get()
1760 .rare_data
1761 .borrow_for_layout()
1762 .as_ref()?
1763 .shadow_root
1764 .as_ref()
1765 .map(|sr| sr.to_layout())
1766 }
1767 }
1768
1769 #[inline]
1770 pub(crate) fn get_attr_for_layout(
1771 self,
1772 namespace: &Namespace,
1773 name: &LocalName,
1774 ) -> Option<&'dom AttrValue> {
1775 get_attr_for_layout(self, namespace, name)
1776 }
1777
1778 #[inline]
1779 pub(crate) fn get_attr_val_for_layout(
1780 self,
1781 namespace: &Namespace,
1782 name: &LocalName,
1783 ) -> Option<&'dom str> {
1784 get_attr_for_layout(self, namespace, name).map(|attr| &**attr)
1785 }
1786
1787 #[inline]
1788 #[expect(unsafe_code)]
1789 pub(crate) fn get_attr_vals_for_layout(
1790 self,
1791 name: &LocalName,
1792 ) -> impl Iterator<Item = &'dom AttrValue> {
1793 let storage = unsafe { self.unsafe_get().attrs.borrow_for_layout() };
1794 storage
1795 .iter()
1796 .filter(move |e: &&AttributeEntry| name == e.local_name_for_layout())
1797 .map(|e: &AttributeEntry| e.value_for_layout())
1798 }
1799
1800 #[expect(unsafe_code)]
1801 pub(crate) fn each_custom_state_for_layout(self, mut callback: impl FnMut(&AtomIdent)) {
1802 let rare_data = unsafe { self.unsafe_get().rare_data.borrow_for_layout() };
1803 let Some(rare_data) = rare_data.as_ref() else {
1804 return;
1805 };
1806 let Some(element_internals) = rare_data.element_internals.as_ref() else {
1807 return;
1808 };
1809
1810 let element_internals: LayoutDom<'_, _> = unsafe { element_internals.to_layout() };
1811 if let Some(states) = element_internals.unsafe_get().custom_states_for_layout() {
1812 for state in states.unsafe_get().set_for_layout().iter() {
1813 callback(&AtomIdent::from(&*state.str()));
1815 }
1816 }
1817 }
1818}
1819
1820impl Element {
1821 pub(crate) fn is_html_element(&self) -> bool {
1822 self.namespace == ns!(html)
1823 }
1824
1825 pub(crate) fn is_svg_element(&self) -> bool {
1826 self.namespace == ns!(svg)
1827 }
1828
1829 pub(crate) fn html_element_in_html_document(&self) -> bool {
1830 self.is_html_element() && self.upcast::<Node>().is_in_html_doc()
1831 }
1832
1833 pub(crate) fn local_name(&self) -> &LocalName {
1834 &self.local_name
1835 }
1836
1837 pub(crate) fn parsed_name(&self, mut name: DOMString) -> LocalName {
1838 if self.html_element_in_html_document() {
1839 name.make_ascii_lowercase();
1840 }
1841 LocalName::from(name)
1842 }
1843
1844 pub(crate) fn namespace(&self) -> &Namespace {
1845 &self.namespace
1846 }
1847
1848 pub(crate) fn prefix(&self) -> Ref<'_, Option<Prefix>> {
1849 self.prefix.borrow()
1850 }
1851
1852 pub(crate) fn set_prefix(&self, prefix: Option<Prefix>) {
1853 *self.prefix.borrow_mut() = prefix;
1854 }
1855
1856 pub(crate) fn set_custom_element_registry(
1857 &self,
1858 registry: Option<&CustomElementRegistry>,
1859 no_gc: &NoGC,
1860 ) {
1861 self.ensure_rare_data(no_gc).custom_element_registry = registry.map(Dom::from_ref);
1862 }
1863
1864 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
1865 self.rare_data()
1866 .as_ref()?
1867 .custom_element_registry
1868 .as_deref()
1869 .map(DomRoot::from_ref)
1870 }
1871
1872 pub(crate) fn attrs(&self) -> &AttributeStorage {
1873 &self.attrs
1874 }
1875
1876 pub(crate) fn dom_attrs(&self, cx: &mut JSContext) -> &AttributeStorage {
1877 let len = self.attrs.borrow().len();
1879 for i in 0..len {
1880 self.attrs.ensure_dom(cx, i, self);
1881 }
1882 &self.attrs
1883 }
1884
1885 pub(crate) fn locate_namespace(&self, prefix: Option<DOMString>) -> Namespace {
1887 let namespace_prefix = prefix.as_ref().map(|s| Prefix::from(&*s.str()));
1888
1889 if namespace_prefix == Some(namespace_prefix!("xml")) {
1891 return ns!(xml);
1892 }
1893
1894 if namespace_prefix == Some(namespace_prefix!("xmlns")) {
1896 return ns!(xmlns);
1897 }
1898
1899 let prefix = prefix.map(LocalName::from);
1900
1901 let inclusive_ancestor_elements = self
1902 .upcast::<Node>()
1903 .inclusive_ancestors(ShadowIncluding::No)
1904 .filter_map(DomRoot::downcast::<Self>);
1905
1906 for element in inclusive_ancestor_elements {
1909 if element.namespace() != &ns!() &&
1911 element.prefix().as_ref().map(|p| &**p) == prefix.as_deref()
1912 {
1913 return element.namespace().clone();
1914 }
1915
1916 let found_ns = element.attrs.borrow().iter().find_map(|attr| {
1921 if attr.namespace() != &ns!(xmlns) {
1922 return None;
1923 }
1924 match (attr.prefix(), prefix.as_ref()) {
1925 (Some(&namespace_prefix!("xmlns")), Some(prefix)) => {
1926 if attr.local_name() == prefix {
1927 Some(Namespace::from(&**attr.value()))
1928 } else {
1929 None
1930 }
1931 },
1932 (None, None) => {
1933 if attr.local_name() == &local_name!("xmlns") {
1934 Some(Namespace::from(&**attr.value()))
1935 } else {
1936 None
1937 }
1938 },
1939 _ => None,
1940 }
1941 });
1942
1943 if let Some(ns) = found_ns {
1944 return ns;
1945 }
1946 }
1947
1948 ns!()
1949 }
1950
1951 pub(crate) fn name_attribute(&self) -> Option<Atom> {
1952 self.rare_data().as_ref()?.name_attribute.clone()
1953 }
1954
1955 pub(crate) fn style_attribute(
1956 &self,
1957 ) -> &DomRefCell<Option<ServoArc<Locked<PropertyDeclarationBlock>>>> {
1958 &self.style_attribute
1959 }
1960
1961 #[cfg(feature = "devtools")]
1962 pub(crate) fn summarize(&self) -> Vec<devtools_traits::AttrInfo> {
1963 self.attrs
1964 .borrow()
1965 .iter()
1966 .map(|attr| attr.summarize())
1967 .collect()
1968 }
1969
1970 pub(crate) fn is_void(&self) -> bool {
1971 if self.namespace != ns!(html) {
1972 return false;
1973 }
1974 match self.local_name {
1975 local_name!("area") |
1978 local_name!("base") |
1979 local_name!("basefont") |
1980 local_name!("bgsound") |
1981 local_name!("br") |
1982 local_name!("col") |
1983 local_name!("embed") |
1984 local_name!("frame") |
1985 local_name!("hr") |
1986 local_name!("img") |
1987 local_name!("input") |
1988 local_name!("keygen") |
1989 local_name!("link") |
1990 local_name!("meta") |
1991 local_name!("param") |
1992 local_name!("source") |
1993 local_name!("track") |
1994 local_name!("wbr") => true,
1995 _ => false,
1996 }
1997 }
1998
1999 pub(crate) fn root_element(&self) -> DomRoot<Element> {
2000 if self.node.is_in_a_document_tree() {
2001 self.upcast::<Node>()
2002 .owner_doc()
2003 .GetDocumentElement()
2004 .unwrap()
2005 } else {
2006 self.upcast::<Node>()
2007 .inclusive_ancestors(ShadowIncluding::No)
2008 .filter_map(DomRoot::downcast)
2009 .last()
2010 .expect("We know inclusive_ancestors will return `self` which is an element")
2011 }
2012 }
2013
2014 pub(crate) fn lookup_prefix(&self, namespace: Namespace) -> Option<DOMString> {
2016 for node in self
2017 .upcast::<Node>()
2018 .inclusive_ancestors(ShadowIncluding::No)
2019 {
2020 let element = node.downcast::<Element>()?;
2021 if *element.namespace() == namespace &&
2023 let Some(prefix) = element.GetPrefix()
2024 {
2025 return Some(prefix);
2026 }
2027
2028 for attr in element.attrs.borrow().iter() {
2030 if attr.prefix() == Some(&namespace_prefix!("xmlns")) &&
2031 **attr.value() == *namespace
2032 {
2033 return Some(TracedDOMString::from(attr.local_name().clone()).root());
2034 }
2035 }
2036 }
2037 None
2038 }
2039
2040 pub(crate) fn is_document_element(&self) -> bool {
2042 if let Some(document_element) = self.owner_document().GetDocumentElement() {
2043 *document_element == *self
2044 } else {
2045 false
2046 }
2047 }
2048
2049 pub(crate) fn is_active_element(&self) -> bool {
2051 if let Some(active_element) = self.owner_document().GetActiveElement() {
2052 *active_element == *self
2053 } else {
2054 false
2055 }
2056 }
2057
2058 pub(crate) fn is_actually_disabled(&self) -> bool {
2059 let node = self.upcast::<Node>();
2060 match node.type_id() {
2061 NodeTypeId::Element(ElementTypeId::HTMLElement(
2062 HTMLElementTypeId::HTMLButtonElement,
2063 )) |
2064 NodeTypeId::Element(ElementTypeId::HTMLElement(
2065 HTMLElementTypeId::HTMLInputElement,
2066 )) |
2067 NodeTypeId::Element(ElementTypeId::HTMLElement(
2068 HTMLElementTypeId::HTMLSelectElement,
2069 )) |
2070 NodeTypeId::Element(ElementTypeId::HTMLElement(
2071 HTMLElementTypeId::HTMLTextAreaElement,
2072 )) |
2073 NodeTypeId::Element(ElementTypeId::HTMLElement(
2074 HTMLElementTypeId::HTMLOptionElement,
2075 )) => self.disabled_state(),
2076 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
2077 self.downcast::<HTMLElement>()
2078 .unwrap()
2079 .is_form_associated_custom_element() &&
2080 self.disabled_state()
2081 },
2082 _ => false,
2087 }
2088 }
2089
2090 pub(crate) fn push_new_attribute(
2091 &self,
2092 cx: &mut JSContext,
2093 local_name: LocalName,
2094 value: AttrValue,
2095 name: LocalName,
2096 namespace: Namespace,
2097 prefix: Option<Prefix>,
2098 ) {
2099 let data = ContentAttributeData {
2104 identifier: AttrName::new(local_name, name, namespace, prefix),
2105 value,
2106 };
2107 let attr_ref = AttrRef::Raw(&data);
2108 self.will_mutate_attr(cx.no_gc(), attr_ref);
2109 self.attrs.push_raw(ContentAttributeData {
2111 identifier: data.identifier.clone(),
2112 value: data.value.clone(),
2113 });
2114 self.handle_attribute_changes(cx, attr_ref, None, Some(&*attr_ref.value()));
2116 }
2117
2118 fn handle_attribute_changes(
2120 &self,
2121 cx: &mut JSContext,
2122 attr: AttrRef<'_>,
2123 old_value: Option<&AttrValue>,
2124 new_value: Option<&AttrValue>,
2125 ) {
2126 let name = attr.local_name().clone();
2129 let namespace = attr.namespace().clone();
2130 let mutation = LazyCell::new(|| Mutation::Attribute {
2131 name: name.clone(),
2132 namespace: namespace.clone(),
2133 old_value: old_value.map(|old_value| DOMString::from(&**old_value)),
2134 });
2135 MutationObserver::queue_a_mutation_record(cx, &self.node, mutation);
2136
2137 let has_new_value = new_value.is_some();
2139
2140 if self.is_custom() {
2143 let reaction = CallbackReaction::AttributeChanged(
2144 attr.local_name().clone(),
2145 old_value,
2146 new_value,
2147 attr.namespace().clone(),
2148 );
2149 ScriptThread::enqueue_callback_reaction(cx, self, reaction, None);
2150 }
2151
2152 if is_relevant_attribute(attr.namespace(), attr.local_name()) {
2154 let attribute_mutation = if has_new_value {
2155 AttributeMutation::Set(old_value)
2156 } else {
2157 AttributeMutation::Removed
2158 };
2159 vtable_for(self.upcast()).attribute_mutated(cx, attr, attribute_mutation);
2160 }
2161 }
2162
2163 pub(crate) fn change_attribute(&self, cx: &mut JSContext, attr: &Attr, mut value: AttrValue) {
2165 let old_value = &attr.value().clone();
2169 self.will_mutate_attr(cx.no_gc(), AttrRef::Dom(attr));
2171 attr.swap_value(&mut value);
2172 self.handle_attribute_changes(
2176 cx,
2177 AttrRef::Dom(attr),
2178 Some(old_value),
2179 Some(&*attr.value()),
2180 );
2181 }
2182
2183 pub(crate) fn push_attribute(&self, cx: &mut JSContext, attr: &Attr) {
2185 assert!(attr.GetOwnerElement().as_deref() == Some(self));
2189 assert!(attr.upcast::<Node>().owner_doc() == self.node.owner_doc());
2193 self.will_mutate_attr(cx.no_gc(), AttrRef::Dom(attr));
2195 self.attrs.push_dom(attr);
2196 self.handle_attribute_changes(cx, AttrRef::Dom(attr), None, Some(&*attr.value()));
2200 }
2201
2202 pub(crate) fn with_attribute<R, F>(
2203 &self,
2204 namespace: &Namespace,
2205 local_name: &LocalName,
2206 map_func: F,
2207 ) -> Option<R>
2208 where
2209 F: FnOnce(AttrRef<'_>) -> R,
2210 {
2211 self.attrs
2212 .borrow()
2213 .iter()
2214 .find(|attribute| {
2215 attribute.local_name() == local_name && attribute.namespace() == namespace
2216 })
2217 .map(map_func)
2218 }
2219
2220 pub(crate) fn attribute_str_ref<'a>(
2221 &'a self,
2222 namespace: &Namespace,
2223 local_name: &LocalName,
2224 ) -> Option<AttrStrRef<'a>> {
2225 AttrStrRef::maybe_new(self.attrs.borrow(), namespace, local_name)
2226 }
2227
2228 pub(crate) fn get_attribute_with_namespace(
2234 &self,
2235 cx: &mut JSContext,
2236 namespace: &Namespace,
2237 local_name: &LocalName,
2238 ) -> Option<DomRoot<Attr>> {
2239 let idx = self
2240 .attrs
2241 .borrow()
2242 .iter()
2243 .position(|a| a.local_name() == local_name && a.namespace() == namespace)?;
2244 Some(self.attrs.ensure_dom(cx, idx, self))
2245 }
2246
2247 pub(crate) fn get_attribute_by_name(
2249 &self,
2250 cx: &mut JSContext,
2251 name: DOMString,
2252 ) -> Option<DomRoot<Attr>> {
2253 let name = &self.parsed_name(name);
2254 let idx = self.attrs.borrow().iter().position(|a| a.name() == name)?;
2255 let attr_dom = Some(self.attrs.ensure_dom(cx, idx, self));
2256 fn id_and_name_must_be_atoms(name: &LocalName, maybe_attr: &Option<DomRoot<Attr>>) -> bool {
2257 if *name == local_name!("id") || *name == local_name!("name") {
2258 match maybe_attr {
2259 None => true,
2260 Some(attr) => matches!(*attr.value(), AttrValue::Atom(_)),
2261 }
2262 } else {
2263 true
2264 }
2265 }
2266 debug_assert!(id_and_name_must_be_atoms(name, &attr_dom));
2267 attr_dom
2268 }
2269
2270 pub(crate) fn set_attribute_from_parser(
2271 &self,
2272 cx: &mut JSContext,
2273 qname: QualName,
2274 value: DOMString,
2275 ) {
2276 if self
2278 .attrs
2279 .borrow()
2280 .iter()
2281 .any(|a| *a.local_name() == qname.local && *a.namespace() == qname.ns)
2282 {
2283 return;
2284 }
2285
2286 let name = match qname.prefix {
2287 None => qname.local.clone(),
2288 Some(ref prefix) => {
2289 let name = format!("{}:{}", &**prefix, &*qname.local);
2290 LocalName::from(name)
2291 },
2292 };
2293 let value = self.parse_attribute(&qname.ns, &qname.local, value);
2294 self.push_new_attribute(cx, qname.local, value, name, qname.ns, qname.prefix);
2295 }
2296
2297 pub(crate) fn set_attribute(&self, cx: &mut JSContext, name: &LocalName, value: AttrValue) {
2298 debug_assert_eq!(
2299 *name,
2300 name.to_ascii_lowercase(),
2301 "All attribute accesses should use a lowercase ASCII name"
2302 );
2303 debug_assert!(!name.contains(':'));
2304
2305 self.set_first_matching_attribute(
2306 cx,
2307 name.clone(),
2308 value,
2309 name.clone(),
2310 ns!(),
2311 None,
2312 |attr| attr.local_name() == name,
2313 );
2314 }
2315
2316 pub(crate) fn set_attribute_with_namespace(
2317 &self,
2318 cx: &mut JSContext,
2319 local_name: LocalName,
2320 value: AttrValue,
2321 name: LocalName,
2322 namespace: Namespace,
2323 prefix: Option<Prefix>,
2324 ) {
2325 self.set_first_matching_attribute(
2326 cx,
2327 local_name.clone(),
2328 value,
2329 name,
2330 namespace.clone(),
2331 prefix,
2332 |attr| *attr.local_name() == local_name && *attr.namespace() == namespace,
2333 );
2334 }
2335
2336 #[expect(clippy::too_many_arguments)]
2338 fn set_first_matching_attribute<F>(
2339 &self,
2340 cx: &mut JSContext,
2341 local_name: LocalName,
2342 value: AttrValue,
2343 name: LocalName,
2344 namespace: Namespace,
2345 prefix: Option<Prefix>,
2346 find: F,
2347 ) where
2348 F: Fn(AttrRef<'_>) -> bool,
2349 {
2350 let found_idx = self.attrs.borrow().iter().position(find);
2353 if let Some(idx) = found_idx {
2354 let attr = self.attrs.ensure_dom(cx, idx, self);
2355 self.will_mutate_attr(cx.no_gc(), AttrRef::Dom(&attr));
2357 self.change_attribute(cx, &attr, value);
2358 } else {
2359 self.push_new_attribute(cx, local_name, value, name, namespace, prefix);
2364 };
2365 }
2366
2367 pub(crate) fn parse_attribute(
2368 &self,
2369 namespace: &Namespace,
2370 local_name: &LocalName,
2371 value: DOMString,
2372 ) -> AttrValue {
2373 if is_relevant_attribute(namespace, local_name) {
2374 vtable_for(self.upcast()).parse_plain_attribute(local_name, value)
2375 } else {
2376 AttrValue::String(value.into())
2377 }
2378 }
2379
2380 pub(crate) fn remove_attribute(
2381 &self,
2382 cx: &mut JSContext,
2383 namespace: &Namespace,
2384 local_name: &LocalName,
2385 ) -> Option<DomRoot<Attr>> {
2386 self.remove_first_matching_attribute(cx, |attr| {
2387 attr.namespace() == namespace && attr.local_name() == local_name
2388 })
2389 }
2390
2391 pub(crate) fn remove_attribute_by_name(
2392 &self,
2393 cx: &mut JSContext,
2394 name: &LocalName,
2395 ) -> Option<DomRoot<Attr>> {
2396 self.remove_first_matching_attribute(cx, |attr| attr.name() == name)
2397 }
2398
2399 fn remove_first_matching_attribute<F>(
2401 &self,
2402 cx: &mut JSContext,
2403 find: F,
2404 ) -> Option<DomRoot<Attr>>
2405 where
2406 F: Fn(AttrRef<'_>) -> bool,
2407 {
2408 let idx = self.attrs.borrow().iter().position(find);
2409 idx.map(|idx| {
2410 let attr = self.attrs.ensure_dom(cx, idx, self);
2411
2412 self.will_mutate_attr(cx.no_gc(), AttrRef::Dom(&attr));
2414 self.attrs.remove(idx);
2415 attr.set_owner(cx, None);
2417 self.handle_attribute_changes(cx, AttrRef::Dom(&attr), Some(&attr.value()), None);
2419
2420 attr
2421 })
2422 }
2423
2424 pub(crate) fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2425 self.any_tokenlist_attribute(&local_name!("class"), |atom| {
2426 case_sensitivity.eq_atom(name, atom)
2427 })
2428 }
2429
2430 pub(crate) fn has_attribute(&self, local_name: &LocalName) -> bool {
2431 debug_assert_eq!(
2432 *local_name,
2433 local_name.to_ascii_lowercase(),
2434 "All attribute accesses should use a lowercase ASCII name"
2435 );
2436 debug_assert!(!local_name.contains(':'));
2437 self.attrs
2438 .borrow()
2439 .iter()
2440 .any(|attr| attr.local_name() == local_name && attr.namespace() == &ns!())
2441 }
2442
2443 pub(crate) fn will_mutate_attr(&self, no_gc: &NoGC, attr: AttrRef<'_>) {
2444 self.upcast::<Node>()
2445 .owner_doc_unrooted(no_gc)
2446 .element_attr_will_change(self, attr);
2447 }
2448
2449 fn update_style_attribute(
2451 &self,
2452 cx: &mut JSContext,
2453 attr: AttrRef<'_>,
2454 mutation: AttributeMutation,
2455 ) {
2456 let doc = self.upcast::<Node>().owner_doc();
2457 *self.style_attribute.borrow_mut() = match mutation {
2459 AttributeMutation::Set(_) => {
2460 let value = attr.as_attr().map_or_else(
2461 || attr.value(),
2462 |attribute| AttrValueRef::Borrowed(attribute.value()),
2463 );
2464
2465 Some(match &*value {
2466 AttrValue::Declaration { block, .. } => block.clone(),
2467 _ => {
2468 let win = self.owner_window();
2469 let source = &**attr.value();
2470 let global = &self.owner_global();
2471 if global
2476 .get_csp_list()
2477 .should_elements_inline_type_behavior_be_blocked(
2478 cx,
2479 global,
2480 self,
2481 InlineCheckType::StyleAttribute,
2482 source,
2483 doc.get_current_parser_line(),
2484 )
2485 {
2486 return;
2487 }
2488 ServoArc::new(doc.style_shared_author_lock().wrap(parse_style_attribute(
2489 source,
2490 &UrlExtraData(doc.base_url().get_arc()),
2491 Some(win.css_error_reporter()),
2492 doc.quirks_mode(),
2493 CssRuleType::Style,
2494 )))
2495 },
2496 })
2497 },
2498 AttributeMutation::Removed => None,
2499 };
2500 }
2501
2502 fn set_attribute_node(
2506 &self,
2507 cx: &mut JSContext,
2508 attr: &Attr,
2509 ) -> Fallible<Option<DomRoot<Attr>>> {
2510 let verified_value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
2514 cx,
2515 self.namespace(),
2516 self.local_name(),
2517 attr.local_name(),
2518 Some(attr.namespace()),
2519 TrustedTypeOrString::String(attr.Value()),
2520 &self.owner_global(),
2521 )?;
2522
2523 if let Some(owner) = attr.GetOwnerElement() &&
2526 &*owner != self
2527 {
2528 return Err(Error::InUseAttribute(Some(
2529 "Attribute is already in use by another element".into(),
2530 )));
2531 }
2532
2533 let vtable = vtable_for(self.upcast());
2534
2535 attr.swap_value(
2541 &mut vtable.parse_plain_attribute(attr.local_name(), verified_value.clone()),
2542 );
2543
2544 let position = self.attrs.borrow().iter().position(|old_attr| {
2546 attr.namespace() == old_attr.namespace() && attr.local_name() == old_attr.local_name()
2547 });
2548
2549 let old_attr = if let Some(position) = position {
2550 let old_attr = self.attrs.ensure_dom(cx, position, self);
2551
2552 if &*old_attr == attr {
2554 return Ok(Some(DomRoot::from_ref(attr)));
2555 }
2556
2557 self.will_mutate_attr(cx.no_gc(), AttrRef::Dom(attr));
2567 self.attrs
2568 .set(position, AttributeEntry::Dom(Dom::from_ref(attr)));
2569 attr.set_owner(cx, Some(self));
2571 attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2573 old_attr.set_owner(cx, None);
2575 self.handle_attribute_changes(
2577 cx,
2578 AttrRef::Dom(attr),
2579 Some(&old_attr.value()),
2580 Some(&AttrValue::String(verified_value.into())),
2581 );
2582
2583 Some(old_attr)
2584 } else {
2585 attr.set_owner(cx, Some(self));
2587 attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2588 self.push_attribute(cx, attr);
2589
2590 None
2591 };
2592
2593 Ok(old_attr)
2595 }
2596
2597 pub(crate) fn update_nonce_internal_slot(&self, nonce: String, no_gc: &NoGC) {
2599 self.ensure_rare_data(no_gc).cryptographic_nonce = nonce;
2600 }
2601
2602 pub(crate) fn nonce_value(&self) -> String {
2604 match self.rare_data().as_ref() {
2605 None => String::new(),
2606 Some(rare_data) => rare_data.cryptographic_nonce.clone(),
2607 }
2608 }
2609
2610 pub(crate) fn update_nonce_post_connection(&self, cx: &mut JSContext) {
2612 if !self
2615 .upcast::<Node>()
2616 .is_connected_with_browsing_context(cx.no_gc())
2617 {
2618 return;
2619 }
2620 let global = self.owner_global();
2621 let csp_list = match global.get_csp_list() {
2623 None => return,
2624 Some(csp_list) => csp_list,
2625 };
2626 if !csp_list.contains_a_header_delivered_content_security_policy() ||
2629 self.get_string_attribute(&local_name!("nonce")).is_empty()
2630 {
2631 return;
2632 }
2633 let nonce = self.nonce_value();
2635 self.set_string_attribute(cx, &local_name!("nonce"), DOMString::new());
2637 self.update_nonce_internal_slot(nonce, cx.no_gc());
2639 }
2640
2641 pub(crate) fn is_nonceable(&self) -> bool {
2643 if !self.has_attribute(&local_name!("nonce")) {
2645 return false;
2646 }
2647 if self.is::<HTMLScriptElement>() {
2649 for attr in self.attrs().borrow().iter() {
2650 let attr_name = attr.name().to_ascii_lowercase();
2655 if attr_name.contains("<script") || attr_name.contains("<style") {
2656 return false;
2657 }
2658 let attr_value = attr.value();
2661 let attr_value = string_as_ascii_lowercase(&attr_value);
2662 if attr_value.contains("<script") || attr_value.contains("<style") {
2663 return false;
2664 }
2665 }
2666 }
2667 if self
2669 .rare_data()
2670 .as_ref()
2671 .is_some_and(|d| d.had_duplicate_attributes)
2672 {
2673 return false;
2674 }
2675 true
2677 }
2678
2679 pub(crate) fn insert_adjacent(
2681 &self,
2682 cx: &mut JSContext,
2683 where_: AdjacentPosition,
2684 node: &Node,
2685 ) -> Fallible<Option<DomRoot<Node>>> {
2686 let self_node = self.upcast::<Node>();
2687 match where_ {
2688 AdjacentPosition::BeforeBegin => {
2689 if let Some(parent) = self_node.GetParentNode() {
2690 Node::pre_insert(cx, node, &parent, Some(self_node)).map(Some)
2691 } else {
2692 Ok(None)
2693 }
2694 },
2695 AdjacentPosition::AfterBegin => {
2696 Node::pre_insert(cx, node, self_node, self_node.GetFirstChild().as_deref())
2697 .map(Some)
2698 },
2699 AdjacentPosition::BeforeEnd => Node::pre_insert(cx, node, self_node, None).map(Some),
2700 AdjacentPosition::AfterEnd => {
2701 if let Some(parent) = self_node.GetParentNode() {
2702 Node::pre_insert(cx, node, &parent, self_node.GetNextSibling().as_deref())
2703 .map(Some)
2704 } else {
2705 Ok(None)
2706 }
2707 },
2708 }
2709 }
2710
2711 pub(crate) fn scroll(&self, cx: &mut JSContext, x: f64, y: f64, behavior: ScrollBehavior) {
2713 let x = if x.is_finite() { x } else { 0.0 } as f32;
2718 let y = if y.is_finite() { y } else { 0.0 } as f32;
2719
2720 let node = self.upcast::<Node>();
2721
2722 let document = node.owner_doc();
2724
2725 if !document.is_fully_active() {
2729 return;
2730 }
2731
2732 let Some(window) = document.GetDefaultView() else {
2734 return;
2738 };
2739
2740 if *self.root_element() == *self {
2745 if document.quirks_mode() == QuirksMode::Quirks {
2746 return;
2747 }
2748
2749 window.scroll(cx, x, y, behavior);
2754 return;
2755 }
2756
2757 if document.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
2763 document.quirks_mode() == QuirksMode::Quirks &&
2764 !self.is_potentially_scrollable_body()
2765 {
2766 window.scroll(cx, x, y, behavior);
2767 return;
2768 }
2769
2770 if !self.has_scrolling_box(cx) {
2778 return;
2779 }
2780
2781 window.scroll_an_element(cx, self, x, y, behavior);
2786
2787 }
2790
2791 pub(crate) fn parse_fragment(
2793 &self,
2794 markup: DOMString,
2795 cx: &mut JSContext,
2796 ) -> Fallible<DomRoot<DocumentFragment>> {
2797 let new_children = ServoParser::parse_html_fragment(cx, self, markup, false);
2800 let context_document = {
2803 if let Some(template) = self.downcast::<HTMLTemplateElement>() {
2804 template.Content(cx).upcast::<Node>().owner_doc()
2805 } else {
2806 self.owner_document()
2807 }
2808 };
2809 let fragment = DocumentFragment::new(cx, &context_document);
2810 for child in new_children {
2812 fragment.upcast::<Node>().AppendChild(cx, &child).unwrap();
2813 }
2814 Ok(fragment)
2816 }
2817
2818 pub(crate) fn fragment_parsing_context(
2821 cx: &mut JSContext,
2822 owner_doc: &Document,
2823 element: Option<&Self>,
2824 ) -> DomRoot<Self> {
2825 match element {
2827 Some(elem)
2828 if elem.local_name() != &local_name!("html") ||
2832 !elem.html_element_in_html_document() =>
2833 {
2834 DomRoot::from_ref(elem)
2835 },
2836 _ => Element::create(
2839 cx,
2840 QualName::new(None, ns!(html), local_name!("body")),
2841 None,
2842 owner_doc,
2843 ElementCreator::ScriptCreated,
2844 CustomElementCreationMode::Asynchronous,
2845 None
2846 ),
2847 }
2848 }
2849
2850 pub(crate) fn is_in_same_home_subtree<T>(&self, other: &T) -> bool
2852 where
2853 T: DerivedFrom<Element> + DomObject,
2854 {
2855 let other = other.upcast::<Element>();
2856 self.root_element() == other.root_element()
2857 }
2858
2859 pub(crate) fn get_id(&self) -> Option<Atom> {
2860 self.id_attribute.borrow().clone()
2861 }
2862
2863 pub(crate) fn get_name(&self) -> Option<Atom> {
2864 self.rare_data().as_ref()?.name_attribute.clone()
2865 }
2866
2867 pub(crate) fn get_element_internals(&self) -> Option<DomRoot<ElementInternals>> {
2868 self.rare_data()
2869 .as_ref()?
2870 .element_internals
2871 .as_ref()
2872 .map(|sr| DomRoot::from_ref(&**sr))
2873 }
2874
2875 pub(crate) fn outer_html(&self, cx: &mut JSContext) -> Fallible<DOMString> {
2876 match self.GetOuterHTML(cx)? {
2877 TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => Ok(str),
2878 TrustedHTMLOrNullIsEmptyString::TrustedHTML(_) => unreachable!(),
2879 }
2880 }
2881
2882 pub(crate) fn compute_source_position(&self, line_number: u32) -> SourcePosition {
2883 SourcePosition {
2884 source_file: self.owner_global().get_url().to_string(),
2885 line_number: line_number + 2,
2886 column_number: 0,
2887 }
2888 }
2889
2890 pub(crate) fn explicitly_set_tab_index(&self) -> Option<i32> {
2891 if self.has_attribute(&local_name!("tabindex")) {
2892 Some(self.get_int_attribute(&local_name!("tabindex"), 0))
2893 } else {
2894 None
2895 }
2896 }
2897
2898 pub(crate) fn tab_index(&self) -> i32 {
2900 if let Some(tab_index) = self.explicitly_set_tab_index() {
2906 return tab_index;
2907 }
2908
2909 if matches!(
2915 self.upcast::<Node>().type_id(),
2916 NodeTypeId::Element(ElementTypeId::HTMLElement(
2917 HTMLElementTypeId::HTMLAnchorElement |
2918 HTMLElementTypeId::HTMLAreaElement |
2919 HTMLElementTypeId::HTMLButtonElement |
2920 HTMLElementTypeId::HTMLFrameElement |
2921 HTMLElementTypeId::HTMLIFrameElement |
2922 HTMLElementTypeId::HTMLInputElement |
2923 HTMLElementTypeId::HTMLObjectElement |
2924 HTMLElementTypeId::HTMLSelectElement |
2925 HTMLElementTypeId::HTMLTextAreaElement
2926 ))
2927 ) {
2928 return 0;
2929 }
2930 if self
2931 .downcast::<HTMLElement>()
2932 .is_some_and(|html_element| html_element.is_a_summary_for_its_parent_details())
2933 {
2934 return 0;
2935 }
2936
2937 -1
2938 }
2939
2940 #[inline]
2941 fn insert_selector_flags(&self, flags: ElementSelectorFlags) {
2942 self.selector_flags
2943 .fetch_or(flags.bits(), Ordering::Relaxed);
2944 }
2945
2946 #[inline]
2947 fn get_selector_flags(&self) -> ElementSelectorFlags {
2948 ElementSelectorFlags::from_bits_retain(self.selector_flags.load(Ordering::Relaxed))
2949 }
2950
2951 pub(crate) fn needs_preserved_style_attribute_after_change(&self) -> bool {
2956 self.owner_window().get_exists_mut_observer() ||
2963 self.get_custom_element_definition()
2964 .is_some_and(|custom_element_definition| {
2965 custom_element_definition.has_attribute_changed_callback()
2966 })
2967 }
2968
2969 pub(crate) fn register_current_id_and_name_attribute(&self, cx: &mut JSContext) {
2970 if let Some(shadow_root) = self.containing_shadow_root() {
2971 if let Some(ref id) = *self.id_attribute.borrow() {
2972 shadow_root.register_element_id(self, id);
2973 }
2974 } else {
2975 if let Some(ref id) = *self.id_attribute.borrow() {
2976 self.owner_document().register_element_id(cx, self, id);
2977 }
2978 if let Some(ref name) = self.name_attribute() {
2979 self.owner_document_unrooted(cx.no_gc())
2980 .register_element_name(self, name);
2981 }
2982 }
2983 }
2984
2985 pub(crate) fn unregister_current_id_and_name_attribute(&self, cx: &mut JSContext) {
2986 if let Some(shadow_root) = self.containing_shadow_root() {
2987 if self.upcast::<Node>().is_in_a_shadow_tree() {
2991 return;
2992 }
2993 if let Some(ref id) = *self.id_attribute.borrow() {
2994 shadow_root.unregister_element_id(id);
2995 }
2996 } else {
2997 if let Some(ref id) = *self.id_attribute.borrow() {
2998 self.owner_document().unregister_element_id(cx, id);
2999 }
3000 if let Some(ref name) = self.name_attribute() {
3001 self.owner_document_unrooted(cx.no_gc())
3002 .unregister_element_name(name);
3003 }
3004 }
3005 }
3006
3007 pub(crate) fn qualified_name(&self) -> Cow<'_, str> {
3011 match &*self.prefix.borrow() {
3012 Some(prefix) => Cow::Owned(format!("{}:{}", prefix, &*self.local_name)),
3013 None => Cow::Borrowed(&*self.local_name),
3014 }
3015 }
3016}
3017
3018impl ElementMethods<crate::DomTypeHolder> for Element {
3019 fn GetNamespaceURI(&self) -> Option<DOMString> {
3021 Node::namespace_to_string(self.namespace.clone())
3022 }
3023
3024 fn LocalName(&self) -> DOMString {
3026 DOMString::from(&*self.local_name)
3028 }
3029
3030 fn GetPrefix(&self) -> Option<DOMString> {
3032 self.prefix
3033 .borrow()
3034 .as_ref()
3035 .map(|p| TracedDOMString::from(p.clone()).root())
3036 }
3037
3038 fn TagName(&self) -> DOMString {
3040 let key = TagNameKey {
3044 prefix: self.prefix.borrow().clone(),
3045 local_name: self.local_name.clone(),
3046 html_element_in_html_document: self.html_element_in_html_document(),
3047 };
3048 let name = TAG_NAME_CACHE.with(|cache| {
3049 if let Some(name) = cache.borrow_mut().get(&key).cloned() {
3051 return name;
3052 }
3053 let qualified_name = self.qualified_name();
3055 let name = if key.html_element_in_html_document {
3059 LocalName::from(qualified_name.to_ascii_uppercase())
3060 } else {
3061 LocalName::from(&*qualified_name)
3062 };
3063 cache.borrow_mut().insert(key, name.clone());
3064 name
3065 });
3066 TracedDOMString::from(name).root()
3067 }
3068
3069 fn Id(&self) -> DOMString {
3073 self.get_string_attribute(&local_name!("id"))
3074 }
3075
3076 fn SetId(&self, cx: &mut JSContext, id: DOMString) {
3078 self.set_atomic_attribute(cx, &local_name!("id"), id);
3079 }
3080
3081 fn ClassName(&self) -> DOMString {
3083 self.get_string_attribute(&local_name!("class"))
3084 }
3085
3086 fn SetClassName(&self, cx: &mut JSContext, class: DOMString) {
3088 self.set_tokenlist_attribute(cx, &local_name!("class"), class);
3089 }
3090
3091 fn ClassList(&self, cx: &mut js::context::JSContext) -> DomRoot<DOMTokenList> {
3093 self.class_list
3094 .or_init(|| DOMTokenList::new(cx, self, &local_name!("class"), None))
3095 }
3096
3097 make_getter!(Slot, "slot");
3099
3100 make_setter!(SetSlot, "slot");
3102
3103 fn Attributes(&self, cx: &mut JSContext) -> DomRoot<NamedNodeMap> {
3105 self.attr_list
3106 .or_init(|| NamedNodeMap::new(cx, &self.owner_window(), self))
3107 }
3108
3109 fn HasAttributes(&self) -> bool {
3111 !self.attrs.borrow().is_empty()
3112 }
3113
3114 fn GetAttributeNames(&self) -> Vec<DOMString> {
3116 self.attrs
3117 .borrow()
3118 .iter()
3119 .map(|attr| TracedDOMString::from(attr.name().clone()).root())
3120 .collect()
3121 }
3122
3123 fn GetAttribute(&self, cx: &mut JSContext, name: DOMString) -> Option<DOMString> {
3125 self.GetAttributeNode(cx, name).map(|s| s.Value())
3126 }
3127
3128 fn GetAttributeNS(
3130 &self,
3131 cx: &mut JSContext,
3132 namespace: Option<DOMString>,
3133 local_name: DOMString,
3134 ) -> Option<DOMString> {
3135 self.GetAttributeNodeNS(cx, namespace, local_name)
3136 .map(|attr| attr.Value())
3137 }
3138
3139 fn GetAttributeNode(&self, cx: &mut JSContext, name: DOMString) -> Option<DomRoot<Attr>> {
3141 self.get_attribute_by_name(cx, name)
3142 }
3143
3144 fn GetAttributeNodeNS(
3146 &self,
3147 cx: &mut JSContext,
3148 namespace: Option<DOMString>,
3149 local_name: DOMString,
3150 ) -> Option<DomRoot<Attr>> {
3151 let namespace = &namespace_from_domstring(namespace);
3152 self.get_attribute_with_namespace(cx, namespace, &LocalName::from(local_name))
3153 }
3154
3155 fn ToggleAttribute(
3157 &self,
3158 cx: &mut JSContext,
3159 name: DOMString,
3160 force: Option<bool>,
3161 ) -> Fallible<bool> {
3162 if !is_valid_attribute_local_name(&name.str()) {
3165 return Err(Error::InvalidCharacter(Some(
3166 "Invalid attribute name".into(),
3167 )));
3168 }
3169
3170 let attribute = self.GetAttribute(cx, name.clone());
3172
3173 let name = self.parsed_name(name);
3175 match attribute {
3176 None => match force {
3178 None | Some(true) => {
3180 self.set_first_matching_attribute(
3181 cx,
3182 name.clone(),
3183 AttrValue::String(String::new()),
3184 name.clone(),
3185 ns!(),
3186 None,
3187 |attr| *attr.name() == name,
3188 );
3189 Ok(true)
3190 },
3191 Some(false) => Ok(false),
3193 },
3194 Some(_index) => match force {
3195 None | Some(false) => {
3197 self.remove_attribute_by_name(cx, &name);
3198 Ok(false)
3199 },
3200 Some(true) => Ok(true),
3202 },
3203 }
3204 }
3205
3206 fn SetAttribute(
3208 &self,
3209 cx: &mut JSContext,
3210 name: DOMString,
3211 value: TrustedTypeOrString,
3212 ) -> ErrorResult {
3213 if !is_valid_attribute_local_name(&name.str()) {
3216 return Err(Error::InvalidCharacter(Some(
3217 "Invalid attribute name".into(),
3218 )));
3219 }
3220
3221 let name = self.parsed_name(name);
3224
3225 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3229 cx,
3230 self.namespace(),
3231 self.local_name(),
3232 &name,
3233 None,
3234 value,
3235 &self.owner_global(),
3236 )?;
3237
3238 let value = self.parse_attribute(&ns!(), &name, value);
3243 self.set_first_matching_attribute(
3244 cx,
3245 name.clone(),
3246 value,
3247 name.clone(),
3248 ns!(),
3249 None,
3250 |attr| *attr.name() == name,
3251 );
3252 Ok(())
3253 }
3254
3255 fn SetAttributeNS(
3257 &self,
3258 cx: &mut JSContext,
3259 namespace: Option<DOMString>,
3260 qualified_name: DOMString,
3261 value: TrustedTypeOrString,
3262 ) -> ErrorResult {
3263 let (namespace, prefix, local_name) =
3265 domname::validate_and_extract(namespace, &qualified_name, domname::Context::Element)?;
3266 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3269 cx,
3270 self.namespace(),
3271 self.local_name(),
3272 &local_name,
3273 Some(&namespace),
3274 value,
3275 &self.owner_global(),
3276 )?;
3277 let value = self.parse_attribute(&namespace, &local_name, value);
3279 self.set_attribute_with_namespace(
3280 cx,
3281 local_name,
3282 value,
3283 LocalName::from(qualified_name),
3284 namespace,
3285 prefix,
3286 );
3287 Ok(())
3288 }
3289
3290 fn SetAttributeNode(&self, cx: &mut JSContext, attr: &Attr) -> Fallible<Option<DomRoot<Attr>>> {
3292 self.set_attribute_node(cx, attr)
3293 }
3294
3295 fn SetAttributeNodeNS(
3297 &self,
3298 cx: &mut JSContext,
3299 attr: &Attr,
3300 ) -> Fallible<Option<DomRoot<Attr>>> {
3301 self.set_attribute_node(cx, attr)
3302 }
3303
3304 fn RemoveAttribute(&self, cx: &mut JSContext, name: DOMString) {
3306 let name = self.parsed_name(name);
3307 self.remove_attribute_by_name(cx, &name);
3308 }
3309
3310 fn RemoveAttributeNS(
3312 &self,
3313 cx: &mut JSContext,
3314 namespace: Option<DOMString>,
3315 local_name: DOMString,
3316 ) {
3317 let namespace = namespace_from_domstring(namespace);
3318 let local_name = LocalName::from(local_name);
3319 self.remove_attribute(cx, &namespace, &local_name);
3320 }
3321
3322 fn RemoveAttributeNode(&self, cx: &mut JSContext, attr: &Attr) -> Fallible<DomRoot<Attr>> {
3324 self.remove_first_matching_attribute(cx, |a| a.as_attr().is_some_and(|a| a == attr))
3327 .ok_or(Error::NotFound(Some(
3328 "Attribute is not owned by this element".into(),
3329 )))
3330 }
3331
3332 fn HasAttribute(&self, cx: &mut JSContext, name: DOMString) -> bool {
3334 self.GetAttribute(cx, name).is_some()
3335 }
3336
3337 fn HasAttributeNS(
3339 &self,
3340 cx: &mut JSContext,
3341 namespace: Option<DOMString>,
3342 local_name: DOMString,
3343 ) -> bool {
3344 self.GetAttributeNS(cx, namespace, local_name).is_some()
3345 }
3346
3347 fn GetElementsByTagName(
3349 &self,
3350 cx: &mut JSContext,
3351 localname: DOMString,
3352 ) -> DomRoot<HTMLCollection> {
3353 let window = self.owner_window();
3354 HTMLCollection::by_qualified_name(cx, &window, self.upcast(), LocalName::from(localname))
3355 }
3356
3357 fn GetElementsByTagNameNS(
3359 &self,
3360 cx: &mut JSContext,
3361 maybe_ns: Option<DOMString>,
3362 localname: DOMString,
3363 ) -> DomRoot<HTMLCollection> {
3364 let window = self.owner_window();
3365 HTMLCollection::by_tag_name_ns(cx, &window, self.upcast(), localname, maybe_ns)
3366 }
3367
3368 fn GetElementsByClassName(
3370 &self,
3371 cx: &mut JSContext,
3372 classes: DOMString,
3373 ) -> DomRoot<HTMLCollection> {
3374 let window = self.owner_window();
3375 HTMLCollection::by_class_name(cx, &window, self.upcast(), classes)
3376 }
3377
3378 fn GetClientRects(&self, cx: &mut JSContext) -> DomRoot<DOMRectList> {
3380 let win = self.owner_window();
3381 let raw_rects = self.upcast::<Node>().border_boxes(cx);
3382 let rects: Vec<DomRoot<DOMRect>> = raw_rects
3383 .into_iter()
3384 .map(|rect| {
3385 DOMRect::new(
3386 cx,
3387 win.upcast(),
3388 rect.origin.x.to_f64_px(),
3389 rect.origin.y.to_f64_px(),
3390 rect.size.width.to_f64_px(),
3391 rect.size.height.to_f64_px(),
3392 )
3393 })
3394 .collect();
3395 DOMRectList::new(cx, &win, rects)
3396 }
3397
3398 fn GetBoundingClientRect(&self, cx: &mut JSContext) -> DomRoot<DOMRect> {
3400 let win = self.owner_window();
3401 let rect = self.upcast::<Node>().border_box(cx).unwrap_or_default();
3402 debug_assert!(rect.size.width.to_f64_px() >= 0.0 && rect.size.height.to_f64_px() >= 0.0);
3403 DOMRect::new(
3404 cx,
3405 win.upcast(),
3406 rect.origin.x.to_f64_px(),
3407 rect.origin.y.to_f64_px(),
3408 rect.size.width.to_f64_px(),
3409 rect.size.height.to_f64_px(),
3410 )
3411 }
3412
3413 fn Scroll(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3415 let left = options.left.unwrap_or(self.ScrollLeft(cx));
3417 let top = options.top.unwrap_or(self.ScrollTop(cx));
3418 self.scroll(cx, left, top, options.parent.behavior);
3419 }
3420
3421 fn Scroll_(&self, cx: &mut JSContext, x: f64, y: f64) {
3423 self.scroll(cx, x, y, ScrollBehavior::Auto);
3424 }
3425
3426 fn ScrollTo(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3428 self.Scroll(cx, options);
3429 }
3430
3431 fn ScrollTo_(&self, cx: &mut JSContext, x: f64, y: f64) {
3433 self.Scroll_(cx, x, y);
3434 }
3435
3436 fn ScrollBy(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3438 let delta_left = options.left.unwrap_or(0.0f64);
3440 let delta_top = options.top.unwrap_or(0.0f64);
3441 let left = self.ScrollLeft(cx);
3442 let top = self.ScrollTop(cx);
3443 self.scroll(
3444 cx,
3445 left + delta_left,
3446 top + delta_top,
3447 options.parent.behavior,
3448 );
3449 }
3450
3451 fn ScrollBy_(&self, cx: &mut JSContext, x: f64, y: f64) {
3453 let left = self.ScrollLeft(cx);
3454 let top = self.ScrollTop(cx);
3455 self.scroll(cx, left + x, top + y, ScrollBehavior::Auto);
3456 }
3457
3458 fn ScrollTop(&self, cx: &mut JSContext) -> f64 {
3460 let node = self.upcast::<Node>();
3461
3462 let document = node.owner_doc();
3464
3465 if !document.is_fully_active() {
3468 return 0.0;
3469 }
3470
3471 let Some(window) = document.GetDefaultView() else {
3473 return 0.0;
3475 };
3476
3477 if self.is_document_element() {
3478 if document.quirks_mode() == QuirksMode::Quirks {
3481 return 0.0;
3482 }
3483
3484 return window.ScrollY(cx) as f64;
3487 }
3488
3489 if document.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3493 document.quirks_mode() == QuirksMode::Quirks &&
3494 !self.is_potentially_scrollable_body()
3495 {
3496 return window.ScrollY(cx) as f64;
3497 }
3498
3499 if !self.has_css_layout_box() {
3502 return 0.0;
3503 }
3504
3505 let point = window.scroll_offset_query(cx, node);
3508 point.y.abs() as f64
3509 }
3510
3511 fn SetScrollTop(&self, cx: &mut JSContext, y_: f64) {
3513 let behavior = ScrollBehavior::Auto;
3514
3515 let y = if y_.is_finite() { y_ } else { 0.0 } as f32;
3518
3519 let node = self.upcast::<Node>();
3520
3521 let document = node.owner_doc();
3523
3524 if !document.is_fully_active() {
3526 return;
3527 }
3528
3529 let Some(window) = document.GetDefaultView() else {
3531 return;
3533 };
3534
3535 if self.is_document_element() {
3536 if document.quirks_mode() == QuirksMode::Quirks {
3539 return;
3540 }
3541
3542 let window_scroll_x = window.ScrollX(cx) as f32;
3546 window.scroll(cx, window_scroll_x, y, behavior);
3547 return;
3548 }
3549
3550 if document.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3555 document.quirks_mode() == QuirksMode::Quirks &&
3556 !self.is_potentially_scrollable_body()
3557 {
3558 let window_scroll_x = window.ScrollX(cx) as f32;
3559 window.scroll(cx, window_scroll_x, y, behavior);
3560 return;
3561 }
3562
3563 if !self.has_scrolling_box(cx) {
3567 return;
3568 }
3569
3570 let element_scroll_left = self.ScrollLeft(cx) as f32;
3573 window.scroll_an_element(cx, self, element_scroll_left, y, behavior);
3574 }
3575
3576 fn ScrollLeft(&self, cx: &mut JSContext) -> f64 {
3578 let node = self.upcast::<Node>();
3579
3580 let document = node.owner_doc();
3582
3583 if !document.is_fully_active() {
3586 return 0.0;
3587 }
3588
3589 let Some(window) = document.GetDefaultView() else {
3592 return 0.0;
3593 };
3594
3595 if self.is_document_element() {
3596 if document.quirks_mode() == QuirksMode::Quirks {
3599 return 0.0;
3600 }
3601
3602 return window.ScrollX(cx) as f64;
3604 }
3605
3606 if document.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3610 document.quirks_mode() == QuirksMode::Quirks &&
3611 !self.is_potentially_scrollable_body()
3612 {
3613 return window.ScrollX(cx) as f64;
3614 }
3615
3616 if !self.has_css_layout_box() {
3619 return 0.0;
3620 }
3621
3622 let point = window.scroll_offset_query(cx, node);
3625 point.x.abs() as f64
3626 }
3627
3628 fn SetScrollLeft(&self, cx: &mut JSContext, x: f64) {
3630 let behavior = ScrollBehavior::Auto;
3631
3632 let x = if x.is_finite() { x } else { 0.0 } as f32;
3635
3636 let node = self.upcast::<Node>();
3638 let document = node.owner_doc();
3639
3640 if !document.is_fully_active() {
3642 return;
3643 }
3644
3645 let Some(window) = document.GetDefaultView() else {
3647 return;
3649 };
3650
3651 if self.is_document_element() {
3652 if document.quirks_mode() == QuirksMode::Quirks {
3655 return;
3656 }
3657
3658 let window_scroll_y = window.ScrollY(cx) as f32;
3662 window.scroll(cx, x, window_scroll_y, behavior);
3663 return;
3664 }
3665
3666 if document.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3671 document.quirks_mode() == QuirksMode::Quirks &&
3672 !self.is_potentially_scrollable_body()
3673 {
3674 let window_scroll_y = window.ScrollY(cx) as f32;
3675 window.scroll(cx, x, window_scroll_y, behavior);
3676 return;
3677 }
3678
3679 if !self.has_scrolling_box(cx) {
3683 return;
3684 }
3685
3686 let element_scroll_top = self.ScrollTop(cx) as f32;
3689 window.scroll_an_element(cx, self, x, element_scroll_top, behavior);
3690 }
3691
3692 fn ScrollIntoView(&self, cx: &mut JSContext, arg: BooleanOrScrollIntoViewOptions) {
3694 let (behavior, block, inline, container) = match arg {
3695 BooleanOrScrollIntoViewOptions::Boolean(true) => (
3697 ScrollBehavior::Auto, ScrollLogicalPosition::Start, ScrollLogicalPosition::Nearest, None, ),
3702 BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(options) => (
3705 options.parent.behavior,
3706 options.block,
3707 options.inline,
3708 if options.container == ScrollIntoViewContainer::Nearest {
3711 Some(self)
3712 } else {
3713 None
3714 },
3715 ),
3716 BooleanOrScrollIntoViewOptions::Boolean(false) => (
3718 ScrollBehavior::Auto,
3719 ScrollLogicalPosition::End,
3720 ScrollLogicalPosition::Nearest,
3721 None,
3722 ),
3723 };
3724
3725 if !self.has_css_layout_box() {
3728 return;
3729 }
3730
3731 self.scroll_into_view_with_options(
3733 cx,
3734 behavior,
3735 ScrollAxisState::new_always_scroll_position(block),
3736 ScrollAxisState::new_always_scroll_position(inline),
3737 container,
3738 None,
3739 );
3740
3741 }
3744
3745 fn ScrollWidth(&self, cx: &mut JSContext) -> i32 {
3747 self.upcast::<Node>().scroll_area(cx).size.width
3748 }
3749
3750 fn ScrollHeight(&self, cx: &mut JSContext) -> i32 {
3752 self.upcast::<Node>().scroll_area(cx).size.height
3753 }
3754
3755 fn ClientTop(&self, cx: &mut JSContext) -> i32 {
3757 self.client_rect(cx).origin.y
3758 }
3759
3760 fn ClientLeft(&self, cx: &mut JSContext) -> i32 {
3762 self.client_rect(cx).origin.x
3763 }
3764
3765 fn ClientWidth(&self, cx: &mut JSContext) -> i32 {
3767 self.client_rect(cx).size.width
3768 }
3769
3770 fn ClientHeight(&self, cx: &mut JSContext) -> i32 {
3772 self.client_rect(cx).size.height
3773 }
3774
3775 fn CurrentCSSZoom(&self, cx: &mut JSContext) -> Finite<f64> {
3777 let window = self.owner_window();
3778 Finite::wrap(window.current_css_zoom_query(cx, self.upcast::<Node>()) as f64)
3779 }
3780
3781 fn SetHTMLUnsafe(
3783 &self,
3784 cx: &mut JSContext,
3785 html: TrustedHTMLOrString,
3786 options: &SetHTMLUnsafeOptions,
3787 ) -> ErrorResult {
3788 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
3792 cx,
3793 &self.owner_global(),
3794 html,
3795 "Element setHTMLUnsafe",
3796 )?;
3797 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3799 DomRoot::upcast(template.Content(cx))
3800 } else {
3801 DomRoot::from_ref(self.upcast())
3802 };
3803
3804 Sanitizer::set_and_filter_html(cx, &target, self, compliant_html, options, false)?;
3806
3807 Ok(())
3808 }
3809
3810 fn SetHTML(
3812 &self,
3813 cx: &mut JSContext,
3814 html: DOMString,
3815 options: &SetHTMLOptions,
3816 ) -> ErrorResult {
3817 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3819 DomRoot::upcast(template.Content(cx))
3820 } else {
3821 DomRoot::from_ref(self.upcast())
3822 };
3823
3824 Sanitizer::set_and_filter_html(cx, &target, self, html, options, true)
3826 }
3827
3828 fn GetHTML(&self, cx: &mut JSContext, options: &GetHTMLOptions) -> DOMString {
3830 self.upcast::<Node>().html_serialize(
3833 cx,
3834 TraversalScope::ChildrenOnly(None),
3835 options.serializableShadowRoots,
3836 options.shadowRoots.clone(),
3837 )
3838 }
3839
3840 fn GetInnerHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3842 let qname = QualName::new(
3843 self.prefix().clone(),
3844 self.namespace().clone(),
3845 self.local_name().clone(),
3846 );
3847
3848 let result = if self.owner_document().is_html_document() {
3851 self.upcast::<Node>()
3852 .html_serialize(cx, ChildrenOnly(Some(qname)), false, vec![])
3853 } else {
3854 self.upcast::<Node>()
3855 .xml_serialize(XmlChildrenOnly(Some(qname)))?
3856 };
3857
3858 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3859 }
3860
3861 fn SetInnerHTML(
3863 &self,
3864 cx: &mut JSContext,
3865 value: TrustedHTMLOrNullIsEmptyString,
3866 ) -> ErrorResult {
3867 let value = TrustedHTML::get_trusted_type_compliant_string(
3871 cx,
3872 &self.owner_global(),
3873 value.convert(),
3874 "Element innerHTML",
3875 )?;
3876 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3878 DomRoot::upcast(template.Content(cx))
3881 } else {
3882 DomRoot::from_ref(self.upcast())
3884 };
3885
3886 if !self.node.has_weird_parser_insertion_mode() &&
3889 value.len_utf8_or_latin1() < 100 &&
3890 !value
3891 .as_bytes(cx.no_gc())
3892 .iter()
3893 .any(|c| matches!(*c, b'&' | b'\0' | b'<' | b'\r'))
3894 {
3895 return Node::SetTextContent(&target, cx, Some(value));
3896 }
3897
3898 let frag = self.parse_fragment(value, cx)?;
3901
3902 Node::replace_all(cx, Some(frag.upcast()), &target);
3904 Ok(())
3905 }
3906
3907 fn GetOuterHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3909 let result = if self.owner_document().is_html_document() {
3912 self.upcast::<Node>()
3913 .html_serialize(cx, IncludeNode, false, vec![])
3914 } else {
3915 self.upcast::<Node>().xml_serialize(XmlIncludeNode)?
3916 };
3917
3918 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3919 }
3920
3921 fn SetOuterHTML(
3923 &self,
3924 cx: &mut JSContext,
3925 value: TrustedHTMLOrNullIsEmptyString,
3926 ) -> ErrorResult {
3927 let value = TrustedHTML::get_trusted_type_compliant_string(
3931 cx,
3932 &self.owner_global(),
3933 value.convert(),
3934 "Element outerHTML",
3935 )?;
3936 let context_document = self.owner_document();
3937 let context_node = self.upcast::<Node>();
3938 let context_parent = match context_node.GetParentNode() {
3940 None => {
3941 return Ok(());
3944 },
3945 Some(parent) => parent,
3946 };
3947
3948 let parent = match context_parent.type_id() {
3949 NodeTypeId::Document(_) => {
3951 return Err(Error::NoModificationAllowed(Some(
3952 "Parent is a Document".into(),
3953 )));
3954 },
3955
3956 NodeTypeId::DocumentFragment(_) => {
3959 let body_elem = Element::create(
3960 cx,
3961 QualName::new(None, ns!(html), local_name!("body")),
3962 None,
3963 &context_document,
3964 ElementCreator::ScriptCreated,
3965 CustomElementCreationMode::Synchronous,
3966 None,
3967 );
3968 DomRoot::upcast(body_elem)
3969 },
3970 _ => context_node.GetParentElement().unwrap(),
3971 };
3972
3973 let frag = parent.parse_fragment(value, cx)?;
3976 context_parent.ReplaceChild(cx, frag.upcast(), context_node)?;
3978 Ok(())
3979 }
3980
3981 fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
3983 self.upcast::<Node>()
3984 .preceding_siblings()
3985 .find_map(DomRoot::downcast)
3986 }
3987
3988 fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
3990 self.upcast::<Node>()
3991 .following_siblings()
3992 .find_map(DomRoot::downcast)
3993 }
3994
3995 fn Children(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
3997 let window = self.owner_window();
3998 HTMLCollection::children(cx, &window, self.upcast())
3999 }
4000
4001 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
4003 self.upcast::<Node>().child_elements().next()
4004 }
4005
4006 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
4008 self.upcast::<Node>()
4009 .rev_children()
4010 .find_map(DomRoot::downcast::<Element>)
4011 }
4012
4013 fn ChildElementCount(&self) -> u32 {
4015 self.upcast::<Node>().child_elements().count() as u32
4016 }
4017
4018 fn Prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
4020 self.upcast::<Node>().prepend(cx, nodes)
4021 }
4022
4023 fn Append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
4025 self.upcast::<Node>().append(cx, nodes)
4026 }
4027
4028 fn ReplaceChildren(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
4030 self.upcast::<Node>().replace_children(cx, nodes)
4031 }
4032
4033 fn MoveBefore(&self, cx: &mut JSContext, node: &Node, child: Option<&Node>) -> ErrorResult {
4035 self.upcast::<Node>().move_before(cx, node, child)
4036 }
4037
4038 fn QuerySelector(
4040 &self,
4041 cx: &mut JSContext,
4042 selectors: DOMString,
4043 ) -> Fallible<Option<DomRoot<Element>>> {
4044 let root = self.upcast::<Node>();
4045 root.query_selector(cx.no_gc(), selectors)
4046 }
4047
4048 fn QuerySelectorAll(
4050 &self,
4051 cx: &mut JSContext,
4052 selectors: DOMString,
4053 ) -> Fallible<DomRoot<NodeList>> {
4054 let root = self.upcast::<Node>();
4055 root.query_selector_all(cx, selectors)
4056 }
4057
4058 fn Before(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
4060 self.upcast::<Node>().before(cx, nodes)
4061 }
4062
4063 fn After(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
4065 self.upcast::<Node>().after(cx, nodes)
4066 }
4067
4068 fn ReplaceWith(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
4070 self.upcast::<Node>().replace_with(cx, nodes)
4071 }
4072
4073 fn Remove(&self, cx: &mut JSContext) {
4075 self.upcast::<Node>().remove_self(cx);
4076 }
4077
4078 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
4080 fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
4081 let document = self.owner_document();
4082 let url = document.url();
4083 let selectors = match SelectorParser::parse_author_origin_no_namespace(
4084 &selectors.str(),
4085 &UrlExtraData(url.get_arc()),
4086 ) {
4087 Err(_) => {
4088 return Err(Error::Syntax(
4089 format!("'{selectors}' is not a valid selector").into(),
4090 ));
4091 },
4092 Ok(selectors) => selectors,
4093 };
4094
4095 let traced_self = Dom::from_ref(self);
4097 let quirks_mode = document.quirks_mode();
4098 Ok(with_layout_state(|| {
4099 #[expect(unsafe_code)]
4100 let layout_element: LayoutDom<'_, _> = unsafe { traced_self.to_layout() };
4101 dom_apis::element_matches(
4102 &ServoDangerousStyleElement::from(layout_element.upcast()),
4103 &selectors,
4104 quirks_mode,
4105 )
4106 }))
4107 }
4108
4109 fn WebkitMatchesSelector(&self, selectors: DOMString) -> Fallible<bool> {
4111 self.Matches(selectors)
4112 }
4113
4114 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
4116 fn Closest(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
4117 let document = self.owner_document();
4118 let url = document.url();
4119 let selectors = match SelectorParser::parse_author_origin_no_namespace(
4120 &selectors.str(),
4121 &UrlExtraData(url.get_arc()),
4122 ) {
4123 Err(_) => return Err(Error::Syntax(Some("Invalid selector".into()))),
4124 Ok(selectors) => selectors,
4125 };
4126
4127 let traced_self = Dom::from_ref(self);
4129 let quirks_mode = document.quirks_mode();
4130 let closest_element = with_layout_state(|| {
4131 #[expect(unsafe_code)]
4132 let layout_element: LayoutDom<'_, _> = unsafe { traced_self.to_layout() };
4133 dom_apis::element_closest(
4134 ServoDangerousStyleElement::from(layout_element.upcast()),
4135 &selectors,
4136 quirks_mode,
4137 )
4138 });
4139 Ok(closest_element.map(ServoDangerousStyleElement::rooted))
4140 }
4141
4142 fn InsertAdjacentElement(
4144 &self,
4145 cx: &mut JSContext,
4146 where_: DOMString,
4147 element: &Element,
4148 ) -> Fallible<Option<DomRoot<Element>>> {
4149 let where_ = where_.parse::<AdjacentPosition>()?;
4150 let inserted_node = self.insert_adjacent(cx, where_, element.upcast())?;
4151 Ok(inserted_node.map(|node| DomRoot::downcast(node).unwrap()))
4152 }
4153
4154 fn InsertAdjacentText(
4156 &self,
4157 cx: &mut JSContext,
4158 where_: DOMString,
4159 data: DOMString,
4160 ) -> ErrorResult {
4161 let text = Text::new(cx, data, &self.owner_document());
4163
4164 let where_ = where_.parse::<AdjacentPosition>()?;
4166 self.insert_adjacent(cx, where_, text.upcast()).map(|_| ())
4167 }
4168
4169 fn InsertAdjacentHTML(
4171 &self,
4172 cx: &mut JSContext,
4173 position: DOMString,
4174 text: TrustedHTMLOrString,
4175 ) -> ErrorResult {
4176 let text = TrustedHTML::get_trusted_type_compliant_string(
4180 cx,
4181 &self.owner_global(),
4182 text,
4183 "Element insertAdjacentHTML",
4184 )?;
4185 let position = position.parse::<AdjacentPosition>()?;
4186
4187 let context = match position {
4190 AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => {
4193 match self.upcast::<Node>().GetParentNode() {
4194 Some(ref node) if node.is::<Document>() => {
4196 return Err(Error::NoModificationAllowed(Some(
4197 "Parent is a Document".into(),
4198 )));
4199 },
4200 None => {
4201 return Err(Error::NoModificationAllowed(Some(
4202 "Element has no parent".into(),
4203 )));
4204 },
4205 Some(node) => node,
4207 }
4208 },
4209 AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => {
4212 DomRoot::from_ref(self.upcast::<Node>())
4214 },
4215 };
4216
4217 let context = Element::fragment_parsing_context(
4219 cx,
4220 &context.owner_doc(),
4221 context.downcast::<Element>(),
4222 );
4223
4224 let fragment = context.parse_fragment(text, cx)?;
4227
4228 self.insert_adjacent(cx, position, fragment.upcast())
4230 .map(|_| ())
4231 }
4232
4233 fn EnterFormalActivationState(&self, cx: &JSContext) -> ErrorResult {
4235 match self.as_maybe_activatable() {
4236 Some(a) => {
4237 a.enter_formal_activation_state(cx.no_gc());
4238 Ok(())
4239 },
4240 None => Err(Error::NotSupported(Some(
4241 "Element is not activatable".into(),
4242 ))),
4243 }
4244 }
4245
4246 fn ExitFormalActivationState(&self, cx: &JSContext) -> ErrorResult {
4247 match self.as_maybe_activatable() {
4248 Some(a) => {
4249 a.exit_formal_activation_state(cx.no_gc());
4250 Ok(())
4251 },
4252 None => Err(Error::NotSupported(Some(
4253 "Element is not activatable".into(),
4254 ))),
4255 }
4256 }
4257
4258 fn RequestFullscreen(&self, cx: &mut CurrentRealm) -> RootedPromise {
4260 let doc = self.owner_document();
4261 doc.enter_fullscreen(cx, self)
4262 }
4263
4264 fn SetPointerCapture(&self, pointer_id: i32) -> ErrorResult {
4266 let document = self.owner_document();
4267 let event_handler = document.event_handler();
4268
4269 if !self.upcast::<Node>().is_connected() {
4280 return Err(Error::InvalidState(Some(
4281 "Can't capture pointer on an unconnected element".into(),
4282 )));
4283 }
4284
4285 if !event_handler.is_active_pointer(pointer_id) {
4295 return Ok(());
4296 }
4297
4298 event_handler.set_pending_pointer_capture(pointer_id, self);
4301
4302 Ok(())
4303 }
4304
4305 fn ReleasePointerCapture(&self, pointer_id: i32) -> ErrorResult {
4307 let document = self.owner_document();
4308 let event_handler = document.event_handler();
4309
4310 if !event_handler.is_active_pointer(pointer_id) {
4314 return Err(Error::NotFound(Some(
4315 "Can't release a pointer that is not active".into(),
4316 )));
4317 }
4318
4319 if !event_handler.has_pointer_capture(pointer_id, self) {
4322 return Ok(());
4323 }
4324
4325 event_handler.clear_pending_pointer_capture(pointer_id);
4327
4328 Ok(())
4329 }
4330
4331 fn HasPointerCapture(&self, pointer_id: i32) -> bool {
4333 let document = self.owner_document();
4334 let event_handler = document.event_handler();
4335 event_handler.has_pointer_capture(pointer_id, self)
4336 }
4337
4338 fn AttachShadow(
4340 &self,
4341 cx: &mut JSContext,
4342 init: &ShadowRootInit,
4343 ) -> Fallible<DomRoot<ShadowRoot>> {
4344 let shadow_root = self.attach_shadow(
4347 cx,
4348 IsUserAgentWidget::No,
4349 init.mode,
4350 init.clonable,
4351 init.serializable,
4352 init.delegatesFocus,
4353 init.slotAssignment,
4354 )?;
4355
4356 Ok(shadow_root)
4358 }
4359
4360 fn GetShadowRoot(&self) -> Option<DomRoot<ShadowRoot>> {
4362 let shadow_or_none = self.shadow_root();
4364
4365 let shadow = shadow_or_none?;
4367 if shadow.Mode() == ShadowRootMode::Closed {
4368 return None;
4369 }
4370
4371 Some(shadow)
4373 }
4374
4375 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
4377 self.custom_element_registry()
4379 }
4380
4381 fn GetRole(&self) -> Option<DOMString> {
4383 self.get_nullable_string_attribute(&local_name!("role"))
4384 }
4385
4386 fn SetRole(&self, cx: &mut JSContext, value: Option<DOMString>) {
4388 self.set_nullable_tokenlist_attribute(cx, &local_name!("role"), value);
4389 }
4390
4391 fn GetAriaAtomic(&self) -> Option<DOMString> {
4392 self.get_nullable_string_attribute(&local_name!("aria-atomic"))
4393 }
4394
4395 fn SetAriaAtomic(&self, cx: &mut JSContext, value: Option<DOMString>) {
4396 self.set_nullable_string_attribute(cx, &local_name!("aria-atomic"), value);
4397 }
4398
4399 fn GetAriaAutoComplete(&self) -> Option<DOMString> {
4400 self.get_nullable_string_attribute(&local_name!("aria-autocomplete"))
4401 }
4402
4403 fn SetAriaAutoComplete(&self, cx: &mut JSContext, value: Option<DOMString>) {
4404 self.set_nullable_string_attribute(cx, &local_name!("aria-autocomplete"), value);
4405 }
4406
4407 fn GetAriaBrailleLabel(&self) -> Option<DOMString> {
4408 self.get_nullable_string_attribute(&local_name!("aria-braillelabel"))
4409 }
4410
4411 fn SetAriaBrailleLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4412 self.set_nullable_string_attribute(cx, &local_name!("aria-braillelabel"), value);
4413 }
4414
4415 fn GetAriaBrailleRoleDescription(&self) -> Option<DOMString> {
4416 self.get_nullable_string_attribute(&local_name!("aria-brailleroledescription"))
4417 }
4418
4419 fn SetAriaBrailleRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4420 self.set_nullable_string_attribute(cx, &local_name!("aria-brailleroledescription"), value);
4421 }
4422
4423 fn GetAriaBusy(&self) -> Option<DOMString> {
4424 self.get_nullable_string_attribute(&local_name!("aria-busy"))
4425 }
4426
4427 fn SetAriaBusy(&self, cx: &mut JSContext, value: Option<DOMString>) {
4428 self.set_nullable_string_attribute(cx, &local_name!("aria-busy"), value);
4429 }
4430
4431 fn GetAriaChecked(&self) -> Option<DOMString> {
4432 self.get_nullable_string_attribute(&local_name!("aria-checked"))
4433 }
4434
4435 fn SetAriaChecked(&self, cx: &mut JSContext, value: Option<DOMString>) {
4436 self.set_nullable_string_attribute(cx, &local_name!("aria-checked"), value);
4437 }
4438
4439 fn GetAriaColCount(&self) -> Option<DOMString> {
4440 self.get_nullable_string_attribute(&local_name!("aria-colcount"))
4441 }
4442
4443 fn SetAriaColCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4444 self.set_nullable_string_attribute(cx, &local_name!("aria-colcount"), value);
4445 }
4446
4447 fn GetAriaColIndex(&self) -> Option<DOMString> {
4448 self.get_nullable_string_attribute(&local_name!("aria-colindex"))
4449 }
4450
4451 fn SetAriaColIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4452 self.set_nullable_string_attribute(cx, &local_name!("aria-colindex"), value);
4453 }
4454
4455 fn GetAriaColIndexText(&self) -> Option<DOMString> {
4456 self.get_nullable_string_attribute(&local_name!("aria-colindextext"))
4457 }
4458
4459 fn SetAriaColIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4460 self.set_nullable_string_attribute(cx, &local_name!("aria-colindextext"), value);
4461 }
4462
4463 fn GetAriaColSpan(&self) -> Option<DOMString> {
4464 self.get_nullable_string_attribute(&local_name!("aria-colspan"))
4465 }
4466
4467 fn SetAriaColSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4468 self.set_nullable_string_attribute(cx, &local_name!("aria-colspan"), value);
4469 }
4470
4471 fn GetAriaCurrent(&self) -> Option<DOMString> {
4472 self.get_nullable_string_attribute(&local_name!("aria-current"))
4473 }
4474
4475 fn SetAriaCurrent(&self, cx: &mut JSContext, value: Option<DOMString>) {
4476 self.set_nullable_string_attribute(cx, &local_name!("aria-current"), value);
4477 }
4478
4479 fn GetAriaDescription(&self) -> Option<DOMString> {
4480 self.get_nullable_string_attribute(&local_name!("aria-description"))
4481 }
4482
4483 fn SetAriaDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4484 self.set_nullable_string_attribute(cx, &local_name!("aria-description"), value);
4485 }
4486
4487 fn GetAriaDisabled(&self) -> Option<DOMString> {
4488 self.get_nullable_string_attribute(&local_name!("aria-disabled"))
4489 }
4490
4491 fn SetAriaDisabled(&self, cx: &mut JSContext, value: Option<DOMString>) {
4492 self.set_nullable_string_attribute(cx, &local_name!("aria-disabled"), value);
4493 }
4494
4495 fn GetAriaExpanded(&self) -> Option<DOMString> {
4496 self.get_nullable_string_attribute(&local_name!("aria-expanded"))
4497 }
4498
4499 fn SetAriaExpanded(&self, cx: &mut JSContext, value: Option<DOMString>) {
4500 self.set_nullable_string_attribute(cx, &local_name!("aria-expanded"), value);
4501 }
4502
4503 fn GetAriaHasPopup(&self) -> Option<DOMString> {
4504 self.get_nullable_string_attribute(&local_name!("aria-haspopup"))
4505 }
4506
4507 fn SetAriaHasPopup(&self, cx: &mut JSContext, value: Option<DOMString>) {
4508 self.set_nullable_string_attribute(cx, &local_name!("aria-haspopup"), value);
4509 }
4510
4511 fn GetAriaHidden(&self) -> Option<DOMString> {
4512 self.get_nullable_string_attribute(&local_name!("aria-hidden"))
4513 }
4514
4515 fn SetAriaHidden(&self, cx: &mut JSContext, value: Option<DOMString>) {
4516 self.set_nullable_string_attribute(cx, &local_name!("aria-hidden"), value);
4517 }
4518
4519 fn GetAriaInvalid(&self) -> Option<DOMString> {
4520 self.get_nullable_string_attribute(&local_name!("aria-invalid"))
4521 }
4522
4523 fn SetAriaInvalid(&self, cx: &mut JSContext, value: Option<DOMString>) {
4524 self.set_nullable_string_attribute(cx, &local_name!("aria-invalid"), value);
4525 }
4526
4527 fn GetAriaKeyShortcuts(&self) -> Option<DOMString> {
4528 self.get_nullable_string_attribute(&local_name!("aria-keyshortcuts"))
4529 }
4530
4531 fn SetAriaKeyShortcuts(&self, cx: &mut JSContext, value: Option<DOMString>) {
4532 self.set_nullable_string_attribute(cx, &local_name!("aria-keyshortcuts"), value);
4533 }
4534
4535 fn GetAriaLabel(&self) -> Option<DOMString> {
4536 self.get_nullable_string_attribute(&local_name!("aria-label"))
4537 }
4538
4539 fn SetAriaLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4540 self.set_nullable_string_attribute(cx, &local_name!("aria-label"), value);
4541 }
4542
4543 fn GetAriaLevel(&self) -> Option<DOMString> {
4544 self.get_nullable_string_attribute(&local_name!("aria-level"))
4545 }
4546
4547 fn SetAriaLevel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4548 self.set_nullable_string_attribute(cx, &local_name!("aria-level"), value);
4549 }
4550
4551 fn GetAriaLive(&self) -> Option<DOMString> {
4552 self.get_nullable_string_attribute(&local_name!("aria-live"))
4553 }
4554
4555 fn SetAriaLive(&self, cx: &mut JSContext, value: Option<DOMString>) {
4556 self.set_nullable_string_attribute(cx, &local_name!("aria-live"), value);
4557 }
4558
4559 fn GetAriaModal(&self) -> Option<DOMString> {
4560 self.get_nullable_string_attribute(&local_name!("aria-modal"))
4561 }
4562
4563 fn SetAriaModal(&self, cx: &mut JSContext, value: Option<DOMString>) {
4564 self.set_nullable_string_attribute(cx, &local_name!("aria-modal"), value);
4565 }
4566
4567 fn GetAriaMultiLine(&self) -> Option<DOMString> {
4568 self.get_nullable_string_attribute(&local_name!("aria-multiline"))
4569 }
4570
4571 fn SetAriaMultiLine(&self, cx: &mut JSContext, value: Option<DOMString>) {
4572 self.set_nullable_string_attribute(cx, &local_name!("aria-multiline"), value);
4573 }
4574
4575 fn GetAriaMultiSelectable(&self) -> Option<DOMString> {
4576 self.get_nullable_string_attribute(&local_name!("aria-multiselectable"))
4577 }
4578
4579 fn SetAriaMultiSelectable(&self, cx: &mut JSContext, value: Option<DOMString>) {
4580 self.set_nullable_string_attribute(cx, &local_name!("aria-multiselectable"), value);
4581 }
4582
4583 fn GetAriaOrientation(&self) -> Option<DOMString> {
4584 self.get_nullable_string_attribute(&local_name!("aria-orientation"))
4585 }
4586
4587 fn SetAriaOrientation(&self, cx: &mut JSContext, value: Option<DOMString>) {
4588 self.set_nullable_string_attribute(cx, &local_name!("aria-orientation"), value);
4589 }
4590
4591 fn GetAriaPlaceholder(&self) -> Option<DOMString> {
4592 self.get_nullable_string_attribute(&local_name!("aria-placeholder"))
4593 }
4594
4595 fn SetAriaPlaceholder(&self, cx: &mut JSContext, value: Option<DOMString>) {
4596 self.set_nullable_string_attribute(cx, &local_name!("aria-placeholder"), value);
4597 }
4598
4599 fn GetAriaPosInSet(&self) -> Option<DOMString> {
4600 self.get_nullable_string_attribute(&local_name!("aria-posinset"))
4601 }
4602
4603 fn SetAriaPosInSet(&self, cx: &mut JSContext, value: Option<DOMString>) {
4604 self.set_nullable_string_attribute(cx, &local_name!("aria-posinset"), value);
4605 }
4606
4607 fn GetAriaPressed(&self) -> Option<DOMString> {
4608 self.get_nullable_string_attribute(&local_name!("aria-pressed"))
4609 }
4610
4611 fn SetAriaPressed(&self, cx: &mut JSContext, value: Option<DOMString>) {
4612 self.set_nullable_string_attribute(cx, &local_name!("aria-pressed"), value);
4613 }
4614
4615 fn GetAriaReadOnly(&self) -> Option<DOMString> {
4616 self.get_nullable_string_attribute(&local_name!("aria-readonly"))
4617 }
4618
4619 fn SetAriaReadOnly(&self, cx: &mut JSContext, value: Option<DOMString>) {
4620 self.set_nullable_string_attribute(cx, &local_name!("aria-readonly"), value);
4621 }
4622
4623 fn GetAriaRelevant(&self) -> Option<DOMString> {
4624 self.get_nullable_string_attribute(&local_name!("aria-relevant"))
4625 }
4626
4627 fn SetAriaRelevant(&self, cx: &mut JSContext, value: Option<DOMString>) {
4628 self.set_nullable_string_attribute(cx, &local_name!("aria-relevant"), value);
4629 }
4630
4631 fn GetAriaRequired(&self) -> Option<DOMString> {
4632 self.get_nullable_string_attribute(&local_name!("aria-required"))
4633 }
4634
4635 fn SetAriaRequired(&self, cx: &mut JSContext, value: Option<DOMString>) {
4636 self.set_nullable_string_attribute(cx, &local_name!("aria-required"), value);
4637 }
4638
4639 fn GetAriaRoleDescription(&self) -> Option<DOMString> {
4640 self.get_nullable_string_attribute(&local_name!("aria-roledescription"))
4641 }
4642
4643 fn SetAriaRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4644 self.set_nullable_string_attribute(cx, &local_name!("aria-roledescription"), value);
4645 }
4646
4647 fn GetAriaRowCount(&self) -> Option<DOMString> {
4648 self.get_nullable_string_attribute(&local_name!("aria-rowcount"))
4649 }
4650
4651 fn SetAriaRowCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4652 self.set_nullable_string_attribute(cx, &local_name!("aria-rowcount"), value);
4653 }
4654
4655 fn GetAriaRowIndex(&self) -> Option<DOMString> {
4656 self.get_nullable_string_attribute(&local_name!("aria-rowindex"))
4657 }
4658
4659 fn SetAriaRowIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4660 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindex"), value);
4661 }
4662
4663 fn GetAriaRowIndexText(&self) -> Option<DOMString> {
4664 self.get_nullable_string_attribute(&local_name!("aria-rowindextext"))
4665 }
4666
4667 fn SetAriaRowIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4668 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindextext"), value);
4669 }
4670
4671 fn GetAriaRowSpan(&self) -> Option<DOMString> {
4672 self.get_nullable_string_attribute(&local_name!("aria-rowspan"))
4673 }
4674
4675 fn SetAriaRowSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4676 self.set_nullable_string_attribute(cx, &local_name!("aria-rowspan"), value);
4677 }
4678
4679 fn GetAriaSelected(&self) -> Option<DOMString> {
4680 self.get_nullable_string_attribute(&local_name!("aria-selected"))
4681 }
4682
4683 fn SetAriaSelected(&self, cx: &mut JSContext, value: Option<DOMString>) {
4684 self.set_nullable_string_attribute(cx, &local_name!("aria-selected"), value);
4685 }
4686
4687 fn GetAriaSetSize(&self) -> Option<DOMString> {
4688 self.get_nullable_string_attribute(&local_name!("aria-setsize"))
4689 }
4690
4691 fn SetAriaSetSize(&self, cx: &mut JSContext, value: Option<DOMString>) {
4692 self.set_nullable_string_attribute(cx, &local_name!("aria-setsize"), value);
4693 }
4694
4695 fn GetAriaSort(&self) -> Option<DOMString> {
4696 self.get_nullable_string_attribute(&local_name!("aria-sort"))
4697 }
4698
4699 fn SetAriaSort(&self, cx: &mut JSContext, value: Option<DOMString>) {
4700 self.set_nullable_string_attribute(cx, &local_name!("aria-sort"), value);
4701 }
4702
4703 fn GetAriaValueMax(&self) -> Option<DOMString> {
4704 self.get_nullable_string_attribute(&local_name!("aria-valuemax"))
4705 }
4706
4707 fn SetAriaValueMax(&self, cx: &mut JSContext, value: Option<DOMString>) {
4708 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemax"), value);
4709 }
4710
4711 fn GetAriaValueMin(&self) -> Option<DOMString> {
4712 self.get_nullable_string_attribute(&local_name!("aria-valuemin"))
4713 }
4714
4715 fn SetAriaValueMin(&self, cx: &mut JSContext, value: Option<DOMString>) {
4716 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemin"), value);
4717 }
4718
4719 fn GetAriaValueNow(&self) -> Option<DOMString> {
4720 self.get_nullable_string_attribute(&local_name!("aria-valuenow"))
4721 }
4722
4723 fn SetAriaValueNow(&self, cx: &mut JSContext, value: Option<DOMString>) {
4724 self.set_nullable_string_attribute(cx, &local_name!("aria-valuenow"), value);
4725 }
4726
4727 fn GetAriaValueText(&self) -> Option<DOMString> {
4728 self.get_nullable_string_attribute(&local_name!("aria-valuetext"))
4729 }
4730
4731 fn SetAriaValueText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4732 self.set_nullable_string_attribute(cx, &local_name!("aria-valuetext"), value);
4733 }
4734
4735 fn GetAssignedSlot(&self, cx: &JSContext) -> Option<DomRoot<HTMLSlotElement>> {
4737 rooted!(&in(cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4740 slottable.find_a_slot(cx.no_gc(), true)
4741 }
4742
4743 fn Part(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
4745 let Some(part) = self.rare_data().as_ref().and_then(|data| data.part.get()) else {
4746 let part = DOMTokenList::new(cx, self, &local_name!("part"), None);
4747 self.ensure_rare_data(cx.no_gc()).part.set(Some(&*part));
4748 return part;
4749 };
4750 part
4751 }
4752
4753 fn Animate(
4755 &self,
4756 cx: &mut JSContext,
4757 keyframes: *mut JSObject,
4758 options: UnrestrictedDoubleOrKeyframeAnimationOptions,
4759 ) -> DomRoot<Animation> {
4760 let window = self.owner_window();
4761
4762 let target = self;
4764
4765 let timeline =
4769 if let UnrestrictedDoubleOrKeyframeAnimationOptions::KeyframeAnimationOptions(options) =
4770 &options
4771 {
4772 options.timeline.clone().flatten()
4773 } else {
4774 None
4775 };
4776 let timeline = timeline.unwrap_or_else(|| {
4777 let document = self.owner_document();
4778 DomRoot::upcast(document.Timeline())
4779 });
4780
4781 let parent_options = match options {
4789 UnrestrictedDoubleOrKeyframeAnimationOptions::UnrestrictedDouble(value) => {
4790 UnrestrictedDoubleOrKeyframeEffectOptions::UnrestrictedDouble(value)
4791 },
4792 UnrestrictedDoubleOrKeyframeAnimationOptions::KeyframeAnimationOptions(options) => {
4793 UnrestrictedDoubleOrKeyframeEffectOptions::KeyframeEffectOptions(options.parent)
4794 },
4795 };
4796 let effect =
4797 KeyframeEffect::Constructor(cx, &window, None, Some(target), keyframes, parent_options);
4798
4799 let animation = Animation::Constructor(
4803 cx,
4804 &window,
4805 None,
4806 Some(effect.upcast()),
4807 Some(Some(&timeline)),
4808 );
4809
4810 animation
4817 }
4818}
4819
4820impl VirtualMethods for Element {
4821 fn super_type(&self) -> Option<&dyn VirtualMethods> {
4822 Some(self.upcast::<Node>() as &dyn VirtualMethods)
4823 }
4824
4825 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
4826 if attr.local_name() == &local_name!("lang") {
4828 return true;
4829 }
4830
4831 self.super_type()
4832 .unwrap()
4833 .attribute_affects_presentational_hints(attr)
4834 }
4835
4836 fn attribute_mutated(
4837 &self,
4838 cx: &mut JSContext,
4839 attr: AttrRef<'_>,
4840 mutation: AttributeMutation,
4841 ) {
4842 self.super_type()
4843 .unwrap()
4844 .attribute_mutated(cx, attr, mutation);
4845 let node = self.upcast::<Node>();
4846 match *attr.local_name() {
4847 ref name if name.starts_with("on") && EventTarget::is_content_event_handler(name) => {
4851 let evtarget = self.upcast::<EventTarget>();
4852 let event_name = &name[2..];
4853 match mutation {
4854 AttributeMutation::Set(_) => {
4856 let source = &**attr.value();
4857 let source_line = 1; evtarget.set_event_handler_uncompiled(
4859 cx,
4860 self.owner_window_unrooted(cx.no_gc()).get_url(),
4861 source_line,
4862 event_name,
4863 source,
4864 );
4865 },
4866 AttributeMutation::Removed => {
4868 evtarget
4869 .set_event_handler_common::<EventHandlerNonNull>(cx, event_name, None);
4870 },
4871 }
4872 },
4873 local_name!("style") => self.update_style_attribute(cx, attr, mutation),
4874 local_name!("id") => {
4875 let id = mutation.new_value(attr).and_then(|value| {
4877 let value = value.as_atom();
4878 if value != &atom!("") {
4879 Some(value.clone())
4881 } else {
4882 None
4884 }
4885 });
4886 *self.id_attribute.safe_borrow_mut(cx.no_gc()) = id;
4887
4888 let containing_shadow_root = self.containing_shadow_root();
4889 if node.is_in_a_document_tree() || node.is_in_a_shadow_tree() {
4890 let value = attr.value().as_atom().clone();
4891 let document = node.owner_document();
4892 match mutation {
4893 AttributeMutation::Set(old_value) => {
4894 if let Some(old_value) = old_value {
4895 let old_value = old_value.as_atom();
4896 if let Some(ref shadow_root) = containing_shadow_root {
4897 shadow_root.unregister_element_id(old_value);
4898 } else {
4899 document.unregister_element_id(cx, old_value);
4900 }
4901 }
4902 if value != atom!("") {
4903 if let Some(ref shadow_root) = containing_shadow_root {
4904 shadow_root.register_element_id(self, &value);
4905 } else {
4906 document.register_element_id(cx, self, &value);
4907 }
4908 }
4909 },
4910 AttributeMutation::Removed => {
4911 if value != atom!("") {
4912 if let Some(ref shadow_root) = containing_shadow_root {
4913 shadow_root.unregister_element_id(&value);
4914 } else {
4915 document.unregister_element_id(cx, &value);
4916 }
4917 }
4918 },
4919 }
4920 }
4921 },
4922 local_name!("name") => {
4923 self.ensure_rare_data(cx.no_gc()).name_attribute =
4925 mutation.new_value(attr).and_then(|value| {
4926 let value = value.as_atom();
4927 if value != &atom!("") {
4928 Some(value.clone())
4929 } else {
4930 None
4931 }
4932 });
4933 if node.is_connected() && node.containing_shadow_root().is_none() {
4936 let value = attr.value().as_atom().clone();
4937 let document = node.owner_document_unrooted(cx.no_gc());
4938 match mutation {
4939 AttributeMutation::Set(old_value) => {
4940 if let Some(old_value) = old_value {
4941 document.unregister_element_name(old_value.as_atom());
4942 }
4943 if value != atom!("") {
4944 document.register_element_name(self, &value);
4945 }
4946 },
4947 AttributeMutation::Removed => {
4948 if value != atom!("") {
4949 document.unregister_element_name(&value);
4950 }
4951 },
4952 }
4953 }
4954 },
4955 local_name!("slot") => {
4956 rooted!(&in(cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4958
4959 if let Some(assigned_slot) = slottable.assigned_slot() {
4961 assigned_slot.assign_slottables(cx);
4962 }
4963 slottable.assign_a_slot(cx);
4964 },
4965 local_name!("role") => {
4966 self.upcast::<Node>()
4967 .add_pending_accessibility_damage(cx.no_gc(), AccessibilityDamage::Node);
4968 },
4969 _ => {
4970 if attr.namespace() == &ns!() && attr.local_name() == &local_name!("src") {
4973 node.dirty(cx.no_gc(), NodeDamage::Other);
4974 }
4975 },
4976 };
4977
4978 if self
4981 .upcast::<Node>()
4982 .get_flag(NodeFlags::USES_ATTR_IN_CONTENT_ATTRIBUTE)
4983 {
4984 node.dirty(cx.no_gc(), NodeDamage::ContentOrHeritage);
4985 }
4986
4987 node.rev_version(cx.no_gc());
4991
4992 #[cfg(feature = "devtools")]
4994 {
4995 let window = self.owner_window_unrooted(cx.no_gc());
4996 if window.live_devtools_updates() {
4997 let global = window.upcast::<GlobalScope>();
4998 if let Some(sender) = global.devtools_chan() {
4999 let pipeline_id = global.pipeline_id();
5000 if ScriptThread::devtools_want_updates_for_node(pipeline_id, self.upcast()) {
5001 let devtools_message =
5002 devtools_traits::ScriptToDevtoolsControlMsg::DomMutation(
5003 pipeline_id,
5004 devtools_traits::DomMutation::AttributeModified {
5005 node: self.upcast::<Node>().unique_id(pipeline_id),
5006 attribute_name: attr.local_name().to_string(),
5007 new_value: mutation
5008 .new_value(attr)
5009 .map(|value| value.to_string()),
5010 },
5011 );
5012 sender.send(devtools_message).unwrap();
5013 }
5014 }
5015 }
5016 }
5017 }
5018
5019 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
5020 match *name {
5021 local_name!("id") => AttrValue::Atom(value.into()),
5022 local_name!("name") => AttrValue::Atom(value.into()),
5023 local_name!("class") | local_name!("part") | local_name!("role") => {
5024 AttrValue::from_serialized_tokenlist(value.into())
5025 },
5026 local_name!("exportparts") => AttrValue::from_shadow_parts(value.into()),
5027 local_name!("tabindex") => AttrValue::from_i32(value.into(), -1),
5028 _ => self
5029 .super_type()
5030 .unwrap()
5031 .parse_plain_attribute(name, value),
5032 }
5033 }
5034
5035 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
5036 if let Some(s) = self.super_type() {
5037 s.bind_to_tree(cx, context);
5038 }
5039
5040 if let Some(f) = self.as_maybe_form_control() {
5041 f.bind_form_control_to_tree(cx);
5042 }
5043
5044 if let Some(ref shadow_root) = self.shadow_root() {
5045 shadow_root.bind_to_tree(cx, context);
5046 }
5047
5048 if !context.is_in_tree() {
5049 return;
5050 }
5051
5052 self.register_current_id_and_name_attribute(cx);
5053 }
5054
5055 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
5056 self.super_type().unwrap().unbind_from_tree(cx, context);
5057
5058 if let Some(f) = self.as_maybe_form_control() {
5059 f.unbind_form_control_from_tree(cx);
5063 }
5064
5065 if !context.tree_is_in_a_document_tree && !context.tree_is_in_a_shadow_tree {
5066 return;
5067 }
5068
5069 let document = self.owner_document_unrooted(cx.no_gc());
5070 let fullscreen = document.fullscreen_element();
5071 if fullscreen.as_deref() == Some(self) {
5072 self.owner_document().exit_fullscreen(cx);
5073 }
5074
5075 self.unregister_current_id_and_name_attribute(cx);
5076 }
5077
5078 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
5079 if let Some(s) = self.super_type() {
5080 s.children_changed(cx, mutation);
5081 }
5082
5083 let flags = self.get_selector_flags();
5084 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
5085 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
5087 } else {
5088 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) &&
5089 let Some(next_child) = mutation.next_child()
5090 {
5091 for child in next_child.inclusively_following_siblings_unrooted(cx.no_gc()) {
5092 if child.is::<Element>() {
5093 child.dirty(cx.no_gc(), NodeDamage::Other);
5094 }
5095 }
5096 }
5097 if flags.intersects(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) &&
5098 let Some(child) = mutation.modified_edge_element(cx.no_gc())
5099 {
5100 child.dirty(cx.no_gc(), NodeDamage::Other);
5101 }
5102 }
5103 }
5104
5105 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
5106 self.super_type().unwrap().adopting_steps(cx, old_doc);
5107 }
5108
5109 fn post_connection_steps(&self, cx: &mut JSContext) {
5110 if let Some(s) = self.super_type() {
5111 s.post_connection_steps(cx);
5112 }
5113
5114 self.update_nonce_post_connection(cx);
5115 }
5116
5117 fn cloning_steps(
5119 &self,
5120 cx: &mut JSContext,
5121 copy: &Node,
5122 maybe_doc: Option<&Document>,
5123 clone_children: CloneChildrenFlag,
5124 ) {
5125 if let Some(s) = self.super_type() {
5126 s.cloning_steps(cx, copy, maybe_doc, clone_children);
5127 }
5128 let elem = copy.downcast::<Element>().unwrap();
5129 if let Some(rare_data) = self.rare_data().as_ref() {
5130 elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone(), cx.no_gc());
5131 }
5132 }
5133}
5134impl Element {
5135 pub(crate) fn client_rect(&self, cx: &mut JSContext) -> Rect<i32, CSSPixel> {
5136 let doc = self.node.owner_doc();
5137
5138 if let Some(rect) = self
5139 .rare_data()
5140 .as_ref()
5141 .and_then(|data| data.client_rect.as_ref())
5142 .and_then(|rect| rect.get().ok()) &&
5143 doc.restyle_reason(cx.no_gc()).is_empty()
5144 {
5145 return rect;
5146 }
5147
5148 let mut rect = self.upcast::<Node>().client_rect(cx);
5149 let in_quirks_mode = doc.quirks_mode() == QuirksMode::Quirks;
5150
5151 if (in_quirks_mode && doc.GetBody().as_deref() == self.downcast::<HTMLElement>()) ||
5152 (!in_quirks_mode && self.is_document_element())
5153 {
5154 rect.size = doc.window().viewport_details().size.round().to_i32();
5155 }
5156
5157 self.ensure_rare_data(cx.no_gc()).client_rect = Some(
5158 self.owner_window_unrooted(cx.no_gc())
5159 .cache_layout_value(rect),
5160 );
5161 rect
5162 }
5163
5164 pub(crate) fn as_maybe_activatable(&self) -> Option<&dyn Activatable> {
5165 let element = match self.upcast::<Node>().type_id() {
5166 NodeTypeId::Element(ElementTypeId::HTMLElement(
5167 HTMLElementTypeId::HTMLInputElement,
5168 )) => {
5169 let element = self.downcast::<HTMLInputElement>().unwrap();
5170 Some(element as &dyn Activatable)
5171 },
5172 NodeTypeId::Element(ElementTypeId::HTMLElement(
5173 HTMLElementTypeId::HTMLButtonElement,
5174 )) => {
5175 let element = self.downcast::<HTMLButtonElement>().unwrap();
5176 Some(element as &dyn Activatable)
5177 },
5178 NodeTypeId::Element(ElementTypeId::HTMLElement(
5179 HTMLElementTypeId::HTMLAnchorElement,
5180 )) => {
5181 let element = self.downcast::<HTMLAnchorElement>().unwrap();
5182 Some(element as &dyn Activatable)
5183 },
5184 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) => {
5185 let element = self.downcast::<HTMLAreaElement>().unwrap();
5186 Some(element as &dyn Activatable)
5187 },
5188 NodeTypeId::Element(ElementTypeId::HTMLElement(
5189 HTMLElementTypeId::HTMLLabelElement,
5190 )) => {
5191 let element = self.downcast::<HTMLLabelElement>().unwrap();
5192 Some(element as &dyn Activatable)
5193 },
5194 NodeTypeId::Element(ElementTypeId::HTMLElement(
5195 HTMLElementTypeId::HTMLSelectElement,
5196 )) => {
5197 let element = self.downcast::<HTMLSelectElement>().unwrap();
5198 Some(element as &dyn Activatable)
5199 },
5200 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
5201 let element = self.downcast::<HTMLElement>().unwrap();
5202 Some(element as &dyn Activatable)
5203 },
5204 _ => None,
5205 };
5206 element.filter(|elem| elem.is_instance_activatable())
5207 }
5208
5209 pub(crate) fn as_stylesheet_owner(&self) -> Option<&dyn StylesheetOwner> {
5210 if let Some(s) = self.downcast::<HTMLStyleElement>() {
5211 return Some(s as &dyn StylesheetOwner);
5212 }
5213
5214 if let Some(l) = self.downcast::<HTMLLinkElement>() {
5215 return Some(l as &dyn StylesheetOwner);
5216 }
5217
5218 None
5219 }
5220
5221 pub(crate) fn as_maybe_validatable(&self) -> Option<&dyn Validatable> {
5223 match self.upcast::<Node>().type_id() {
5224 NodeTypeId::Element(ElementTypeId::HTMLElement(
5225 HTMLElementTypeId::HTMLInputElement,
5226 )) => {
5227 let element = self.downcast::<HTMLInputElement>().unwrap();
5228 Some(element as &dyn Validatable)
5229 },
5230 NodeTypeId::Element(ElementTypeId::HTMLElement(
5231 HTMLElementTypeId::HTMLButtonElement,
5232 )) => {
5233 let element = self.downcast::<HTMLButtonElement>().unwrap();
5234 Some(element as &dyn Validatable)
5235 },
5236 NodeTypeId::Element(ElementTypeId::HTMLElement(
5237 HTMLElementTypeId::HTMLObjectElement,
5238 )) => {
5239 let element = self.downcast::<HTMLObjectElement>().unwrap();
5240 Some(element as &dyn Validatable)
5241 },
5242 NodeTypeId::Element(ElementTypeId::HTMLElement(
5243 HTMLElementTypeId::HTMLSelectElement,
5244 )) => {
5245 let element = self.downcast::<HTMLSelectElement>().unwrap();
5246 Some(element as &dyn Validatable)
5247 },
5248 NodeTypeId::Element(ElementTypeId::HTMLElement(
5249 HTMLElementTypeId::HTMLTextAreaElement,
5250 )) => {
5251 let element = self.downcast::<HTMLTextAreaElement>().unwrap();
5252 Some(element as &dyn Validatable)
5253 },
5254 NodeTypeId::Element(ElementTypeId::HTMLElement(
5255 HTMLElementTypeId::HTMLFieldSetElement,
5256 )) => {
5257 let element = self.downcast::<HTMLFieldSetElement>().unwrap();
5258 Some(element as &dyn Validatable)
5259 },
5260 NodeTypeId::Element(ElementTypeId::HTMLElement(
5261 HTMLElementTypeId::HTMLOutputElement,
5262 )) => {
5263 let element = self.downcast::<HTMLOutputElement>().unwrap();
5264 Some(element as &dyn Validatable)
5265 },
5266 _ => None,
5267 }
5268 }
5269
5270 pub(crate) fn is_invalid(&self, cx: &mut JSContext, needs_update: bool) -> bool {
5271 if let Some(validatable) = self.as_maybe_validatable() {
5272 if needs_update {
5273 validatable
5274 .validity_state(cx)
5275 .perform_validation_and_update(cx, ValidationFlags::all());
5276 }
5277 return validatable.is_instance_validatable(cx.no_gc()) &&
5278 !validatable.satisfies_constraints(cx);
5279 }
5280
5281 if let Some(internals) = self.get_element_internals() {
5282 return internals.is_invalid(cx);
5283 }
5284 false
5285 }
5286
5287 pub(crate) fn is_instance_validatable(&self, no_gc: &NoGC) -> bool {
5288 if let Some(validatable) = self.as_maybe_validatable() {
5289 return validatable.is_instance_validatable(no_gc);
5290 }
5291 if let Some(internals) = self.get_element_internals() {
5292 return internals.is_instance_validatable(no_gc);
5293 }
5294 false
5295 }
5296
5297 pub(crate) fn init_state_for_internals(&self, no_gc: &NoGC) {
5298 self.set_enabled_state(no_gc, true);
5299 self.set_state(no_gc, ElementState::VALID, true);
5300 self.set_state(no_gc, ElementState::INVALID, false);
5301 }
5302
5303 pub(crate) fn click_in_progress(&self) -> bool {
5304 self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
5305 }
5306
5307 pub(crate) fn set_click_in_progress(&self, click: bool) {
5308 self.upcast::<Node>()
5309 .set_flag(NodeFlags::CLICK_IN_PROGRESS, click)
5310 }
5311
5312 pub fn state(&self) -> ElementState {
5313 self.state.get()
5314 }
5315
5316 pub(crate) fn set_state(&self, no_gc: &NoGC, which: ElementState, value: bool) {
5317 let mut state = self.state.get();
5318 let previous_state = state;
5319 if value {
5320 state.insert(which);
5321 } else {
5322 state.remove(which);
5323 }
5324
5325 if previous_state == state {
5326 return;
5328 }
5329
5330 {
5333 let document = self.owner_document_unrooted(no_gc);
5334 let mut entry = document.ensure_pending_restyle(self);
5335 if entry.snapshot.is_none() {
5336 entry.snapshot = Some(Snapshot::new());
5337 }
5338 let snapshot = entry.snapshot.as_mut().unwrap();
5339 if snapshot.state.is_none() {
5340 snapshot.state = Some(self.state());
5341 }
5342 }
5343
5344 self.state.set(state);
5345 }
5346
5347 pub(crate) fn set_active_state(&self, no_gc: &NoGC, value: bool) {
5349 self.set_state(no_gc, ElementState::ACTIVE, value);
5350
5351 if let Some(parent) = self.upcast::<Node>().GetParentElement() {
5352 parent.set_active_state(no_gc, value);
5353 }
5354 }
5355
5356 pub(crate) fn focus_state(&self) -> bool {
5357 self.state.get().contains(ElementState::FOCUS)
5358 }
5359
5360 pub(crate) fn set_focus_state(&self, no_gc: &NoGC, value: bool) {
5361 self.set_state(no_gc, ElementState::FOCUS, value);
5362 }
5363
5364 pub(crate) fn set_focusring_state(&self, no_gc: &NoGC, value: bool) {
5365 self.set_state(no_gc, ElementState::FOCUSRING, value);
5366 }
5367
5368 pub(crate) fn set_hover_state(&self, no_gc: &NoGC, value: bool) {
5369 self.set_state(no_gc, ElementState::HOVER, value);
5370 }
5371
5372 pub(crate) fn enabled_state(&self) -> bool {
5373 self.state.get().contains(ElementState::ENABLED)
5374 }
5375
5376 pub(crate) fn set_enabled_state(&self, no_gc: &NoGC, value: bool) {
5377 self.set_state(no_gc, ElementState::ENABLED, value)
5378 }
5379
5380 pub(crate) fn disabled_state(&self) -> bool {
5381 self.state.get().contains(ElementState::DISABLED)
5382 }
5383
5384 pub(crate) fn set_disabled_state(&self, no_gc: &NoGC, value: bool) {
5385 self.set_state(no_gc, ElementState::DISABLED, value)
5386 }
5387
5388 pub(crate) fn read_write_state(&self) -> bool {
5389 self.state.get().contains(ElementState::READWRITE)
5390 }
5391
5392 pub(crate) fn set_read_write_state(&self, no_gc: &NoGC, value: bool) {
5393 self.set_state(no_gc, ElementState::READWRITE, value)
5394 }
5395
5396 pub(crate) fn set_open_state(&self, no_gc: &NoGC, value: bool) {
5397 self.set_state(no_gc, ElementState::OPEN, value);
5398 }
5399
5400 pub(crate) fn set_placeholder_shown_state(&self, no_gc: &NoGC, value: bool) {
5401 self.set_state(no_gc, ElementState::PLACEHOLDER_SHOWN, value);
5402 }
5403
5404 pub(crate) fn set_modal_state(&self, no_gc: &NoGC, value: bool) {
5405 self.set_state(no_gc, ElementState::MODAL, value);
5406 self.owner_document_unrooted(no_gc)
5407 .update_topmost_modal(no_gc);
5408 }
5409
5410 pub(crate) fn set_target_state(&self, no_gc: &NoGC, value: bool) {
5411 self.set_state(no_gc, ElementState::URLTARGET, value)
5412 }
5413
5414 pub(crate) fn set_fullscreen_state(&self, no_gc: &NoGC, value: bool) {
5415 self.set_state(no_gc, ElementState::FULLSCREEN, value)
5416 }
5417
5418 pub(crate) fn is_connected(&self) -> bool {
5420 self.upcast::<Node>().is_connected()
5421 }
5422
5423 pub(crate) fn cannot_navigate(&self) -> bool {
5425 let document = self.owner_document();
5429 !document.is_fully_active() || (!self.is::<HTMLAnchorElement>() && !self.is_connected())
5430 }
5431
5432 pub(crate) fn get_computed_role(&self, cx: &mut JSContext) -> Option<DOMString> {
5433 let accesskit_node = self
5434 .owner_window()
5435 .accesskit_node_query(cx, self.upcast::<Node>().to_trusted_node_address())?;
5436 let aria_role = ACCESSKIT_ROLE_TO_ARIA_ROLE.get(&accesskit_node.role())?;
5437 Some(TracedDOMString::from(aria_role.clone()).root())
5438 }
5439}
5440
5441impl Element {
5442 pub(crate) fn is_disabled_by_ancestor_fieldset(&self, no_gc: &NoGC) -> bool {
5447 let node = self.upcast::<Node>();
5448 let mut previous_ancestor = UnrootedDom::from_ref(node, no_gc);
5449 for ancestor in previous_ancestor.ancestors_unrooted(no_gc) {
5450 let child = std::mem::replace(&mut previous_ancestor, ancestor.clone());
5451 let Some(ancestor_element) = ancestor.downcast::<HTMLFieldSetElement>() else {
5452 continue;
5453 };
5454 if !ancestor_element.upcast::<Element>().disabled_state() {
5455 continue;
5456 }
5457 if !ancestor.is_parent_of(node) &&
5458 ancestor
5459 .children_unrooted(no_gc)
5460 .find(|node| node.is::<HTMLLegendElement>())
5461 .is_some_and(|fieldset| fieldset == child)
5462 {
5463 continue;
5464 }
5465
5466 return true;
5467 }
5468
5469 false
5470 }
5471
5472 pub(crate) fn check_ancestors_disabled_state_for_form_control(&self, no_gc: &NoGC) {
5473 if self.disabled_state() {
5474 return;
5475 }
5476 let disabled = self.is_disabled_by_ancestor_fieldset(no_gc);
5477 self.set_disabled_state(no_gc, disabled);
5478 self.set_enabled_state(no_gc, !disabled);
5479 }
5480
5481 pub(crate) fn check_disabled_attribute(&self, no_gc: &NoGC) {
5482 let has_disabled_attrib = self.has_attribute(&local_name!("disabled"));
5483 self.set_disabled_state(no_gc, has_disabled_attrib);
5484 self.set_enabled_state(no_gc, !has_disabled_attrib);
5485 }
5486
5487 pub(crate) fn update_read_write_state_from_readonly_attribute(&self, no_gc: &NoGC) {
5488 let has_readonly_attribute = self.has_attribute(&local_name!("readonly"));
5489 self.set_read_write_state(no_gc, has_readonly_attribute);
5490 }
5491}
5492
5493#[derive(Clone, Copy)]
5494pub(crate) enum AttributeMutation<'a> {
5495 Set(Option<&'a AttrValue>),
5498
5499 Removed,
5502}
5503
5504impl AttributeMutation<'_> {
5505 pub(crate) fn is_removal(&self) -> bool {
5506 match *self {
5507 AttributeMutation::Removed => true,
5508 AttributeMutation::Set(_) => false,
5509 }
5510 }
5511
5512 pub(crate) fn new_value<'b>(&self, attr: AttrRef<'b>) -> Option<AttrValueRef<'b>> {
5513 match *self {
5514 AttributeMutation::Set(_) => Some(attr.value()),
5515 AttributeMutation::Removed => None,
5516 }
5517 }
5518
5519 pub(crate) fn old_value(&self, attr: AttrRef<'_>) -> Option<String> {
5520 match *self {
5521 AttributeMutation::Set(old) => old.map(|value| value.to_string()),
5522 AttributeMutation::Removed => Some(attr.value().to_string()),
5523 }
5524 }
5525}
5526
5527#[derive(Eq, Hash, PartialEq)]
5531struct TagNameKey {
5532 prefix: Option<Prefix>,
5533 local_name: LocalName,
5534 html_element_in_html_document: bool,
5535}
5536
5537const TAG_NAME_CACHE_CAPACITY: usize = 128;
5539
5540thread_local! {
5541 static TAG_NAME_CACHE: RefCell<LruCache<TagNameKey, LocalName, FxBuildHasher>> =
5548 RefCell::new(LruCache::with_hasher(TAG_NAME_CACHE_CAPACITY, FxBuildHasher));
5549}
5550
5551pub(crate) fn reflect_cross_origin_attribute(element: &Element) -> Option<DOMString> {
5553 element
5554 .get_attribute_string_ref(&local_name!("crossorigin"))
5555 .map(|value| {
5556 DOMString::from_static(
5557 ["anonymous", "use-credentials"]
5558 .into_iter()
5559 .find(|keyword| value.eq_ignore_ascii_case(keyword))
5560 .unwrap_or("anonymous"),
5561 )
5562 })
5563}
5564
5565pub(crate) fn set_cross_origin_attribute(
5566 cx: &mut JSContext,
5567 element: &Element,
5568 value: Option<DOMString>,
5569) {
5570 match value {
5571 Some(val) => element.set_string_attribute(cx, &local_name!("crossorigin"), val),
5572 None => {
5573 element.remove_attribute(cx, &ns!(), &local_name!("crossorigin"));
5574 },
5575 }
5576}
5577
5578pub(crate) fn reflect_referrer_policy_attribute(element: &Element) -> DOMString {
5580 element
5581 .get_attribute_string_ref(&local_name!("referrerpolicy"))
5582 .map(|value| {
5583 DOMString::from(
5584 [
5585 "no-referrer",
5586 "no-referrer-when-downgrade",
5587 "same-origin",
5588 "origin",
5589 "strict-origin",
5590 "origin-when-cross-origin",
5591 "strict-origin-when-cross-origin",
5592 "unsafe-url",
5593 ]
5594 .into_iter()
5595 .find(|keyword| value.eq_ignore_ascii_case(keyword))
5596 .unwrap_or(""),
5597 )
5598 })
5599 .unwrap_or_default()
5600}
5601
5602pub(crate) fn referrer_policy_for_element(element: &Element) -> ReferrerPolicy {
5603 element
5604 .get_attribute_string_ref(&local_name!("referrerpolicy"))
5605 .map(|value| {
5606 let value = value.as_attr_ref().value();
5607 ReferrerPolicy::from(value.as_ref())
5608 })
5609 .unwrap_or(element.owner_document().get_referrer_policy())
5610}
5611
5612pub(crate) fn cors_setting_for_element(element: &Element) -> Option<CorsSettings> {
5613 element
5614 .get_attribute_string_ref(&local_name!("crossorigin"))
5615 .map(|value| {
5616 let value = value.as_attr_ref().value();
5617 CorsSettings::from_enumerated_attribute(&value)
5618 })
5619}
5620
5621pub(crate) fn cors_settings_attribute_credential_mode(element: &Element) -> CredentialsMode {
5623 element
5624 .get_attribute_string_ref(&local_name!("crossorigin"))
5625 .map(|value| {
5626 if value.eq_ignore_ascii_case("use-credentials") {
5627 CredentialsMode::Include
5628 } else {
5629 CredentialsMode::CredentialsSameOrigin
5631 }
5632 })
5633 .unwrap_or(CredentialsMode::CredentialsSameOrigin)
5635}
5636
5637pub(crate) fn is_element_affected_by_legacy_background_presentational_hint(
5638 namespace: &Namespace,
5639 local_name: &LocalName,
5640) -> bool {
5641 *namespace == ns!(html) &&
5642 matches!(
5643 *local_name,
5644 local_name!("body") |
5645 local_name!("table") |
5646 local_name!("thead") |
5647 local_name!("tbody") |
5648 local_name!("tfoot") |
5649 local_name!("tr") |
5650 local_name!("td") |
5651 local_name!("th")
5652 )
5653}
5654
5655impl script_bindings::callback::OwnerWindow<crate::DomTypeHolder> for Element {
5656 fn owner_window(&self) -> Option<DomRoot<Window>> {
5657 Some(NodeTraits::owner_window(self))
5658 }
5659}