1use std::borrow::Cow;
8use std::cell::{Cell, LazyCell};
9use std::default::Default;
10use std::ops::Deref;
11use std::rc::Rc;
12use std::str::FromStr;
13use std::sync::atomic::{AtomicUsize, Ordering};
14use std::{fmt, mem};
15
16use app_units::Au;
17use bitflags::bitflags;
18use cssparser::match_ignore_ascii_case;
19use devtools_traits::{AttrInfo, DomMutation, ScriptToDevtoolsControlMsg};
20use dom_struct::dom_struct;
21use euclid::Rect;
22use html5ever::serialize::TraversalScope;
23use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
24use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns};
25use js::context::JSContext;
26use js::jsapi::{Heap, JSAutoRealm};
27use js::jsval::JSVal;
28use js::rust::HandleObject;
29use layout_api::{LayoutDamage, ScrollContainerQueryFlags};
30use net_traits::ReferrerPolicy;
31use net_traits::request::CorsSettings;
32use selectors::Element as SelectorsElement;
33use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
34use selectors::bloom::{BLOOM_HASH_MASK, BloomFilter};
35use selectors::matching::{ElementSelectorFlags, MatchingContext};
36use selectors::sink::Push;
37use servo_arc::Arc;
38use style::applicable_declarations::ApplicableDeclarationBlock;
39use style::attr::{AttrValue, LengthOrPercentageOrAuto};
40use style::context::QuirksMode;
41use style::invalidation::element::restyle_hints::RestyleHint;
42use style::properties::longhands::{
43 self, background_image, border_spacing, font_family, font_size,
44};
45use style::properties::{
46 ComputedValues, Importance, PropertyDeclaration, PropertyDeclarationBlock,
47 parse_style_attribute,
48};
49use style::rule_tree::CascadeLevel;
50use style::selector_parser::{
51 NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser, Snapshot,
52 extended_filtering,
53};
54use style::shared_lock::Locked;
55use style::stylesheets::layer_rule::LayerOrder;
56use style::stylesheets::{CssRuleType, UrlExtraData};
57use style::values::computed::Overflow;
58use style::values::generics::NonNegative;
59use style::values::generics::position::PreferredRatio;
60use style::values::generics::ratio::Ratio;
61use style::values::{AtomIdent, AtomString, CSSFloat, computed, specified};
62use style::{ArcSlice, CaseSensitivityExt, dom_apis, thread_state};
63use style_traits::CSSPixel;
64use stylo_atoms::Atom;
65use stylo_dom::ElementState;
66use xml5ever::serialize::TraversalScope::{
67 ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode,
68};
69
70use crate::conversions::Convert;
71use crate::dom::activation::Activatable;
72use crate::dom::attr::{Attr, AttrHelpersForLayout, is_relevant_attribute};
73use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut};
74use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
75use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
76use crate::dom::bindings::codegen::Bindings::ElementBinding::{
77 ElementMethods, GetHTMLOptions, ScrollIntoViewContainer, ScrollLogicalPosition, ShadowRootInit,
78};
79use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
80use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
81use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
82use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
83use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
84 ShadowRootMethods, ShadowRootMode, SlotAssignmentMode,
85};
86use crate::dom::bindings::codegen::Bindings::WindowBinding::{
87 ScrollBehavior, ScrollToOptions, WindowMethods,
88};
89use crate::dom::bindings::codegen::UnionTypes::{
90 BooleanOrScrollIntoViewOptions, NodeOrString, TrustedHTMLOrNullIsEmptyString,
91 TrustedHTMLOrString,
92 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString,
93 TrustedScriptURLOrUSVString,
94};
95use crate::dom::bindings::conversions::DerivedFrom;
96use crate::dom::bindings::domname::{
97 self, is_valid_attribute_local_name, namespace_from_domstring,
98};
99use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
100use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
101use crate::dom::bindings::num::Finite;
102use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
103use crate::dom::bindings::reflector::DomObject;
104use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout};
105use crate::dom::bindings::str::{DOMString, USVString};
106use crate::dom::characterdata::CharacterData;
107use crate::dom::create::create_element;
108use crate::dom::csp::{CspReporting, InlineCheckType, SourcePosition};
109use crate::dom::customelementregistry::{
110 CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementRegistry,
111 CustomElementState, is_valid_custom_element_name,
112};
113use crate::dom::document::{Document, LayoutDocumentHelpers};
114use crate::dom::documentfragment::DocumentFragment;
115use crate::dom::domrect::DOMRect;
116use crate::dom::domrectlist::DOMRectList;
117use crate::dom::domtokenlist::DOMTokenList;
118use crate::dom::elementinternals::ElementInternals;
119use crate::dom::event::{EventBubbles, EventCancelable, EventComposed};
120use crate::dom::eventtarget::EventTarget;
121use crate::dom::globalscope::GlobalScope;
122use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
123use crate::dom::html::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
124use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
125use crate::dom::html::htmlcollection::HTMLCollection;
126use crate::dom::html::htmlelement::HTMLElement;
127use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
128use crate::dom::html::htmlfontelement::{HTMLFontElement, HTMLFontElementLayoutHelpers};
129use crate::dom::html::htmlformelement::FormControlElementHelpers;
130use crate::dom::html::htmlhrelement::{HTMLHRElement, HTMLHRLayoutHelpers, SizePresentationalHint};
131use crate::dom::html::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods};
132use crate::dom::html::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
133use crate::dom::html::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
134use crate::dom::html::htmllabelelement::HTMLLabelElement;
135use crate::dom::html::htmllegendelement::HTMLLegendElement;
136use crate::dom::html::htmllinkelement::HTMLLinkElement;
137use crate::dom::html::htmlobjectelement::HTMLObjectElement;
138use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
139use crate::dom::html::htmloutputelement::HTMLOutputElement;
140use crate::dom::html::htmlscriptelement::HTMLScriptElement;
141use crate::dom::html::htmlselectelement::HTMLSelectElement;
142use crate::dom::html::htmlslotelement::{HTMLSlotElement, Slottable};
143use crate::dom::html::htmlstyleelement::HTMLStyleElement;
144use crate::dom::html::htmltablecellelement::{
145 HTMLTableCellElement, HTMLTableCellElementLayoutHelpers,
146};
147use crate::dom::html::htmltablecolelement::{
148 HTMLTableColElement, HTMLTableColElementLayoutHelpers,
149};
150use crate::dom::html::htmltableelement::{HTMLTableElement, HTMLTableElementLayoutHelpers};
151use crate::dom::html::htmltablerowelement::{
152 HTMLTableRowElement, HTMLTableRowElementLayoutHelpers,
153};
154use crate::dom::html::htmltablesectionelement::{
155 HTMLTableSectionElement, HTMLTableSectionElementLayoutHelpers,
156};
157use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
158use crate::dom::html::htmltextareaelement::{
159 HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers,
160};
161use crate::dom::html::htmlvideoelement::{HTMLVideoElement, LayoutHTMLVideoElementHelpers};
162use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserverRegistration};
163use crate::dom::mutationobserver::{Mutation, MutationObserver};
164use crate::dom::namednodemap::NamedNodeMap;
165use crate::dom::node::{
166 BindContext, ChildrenMutation, CloneChildrenFlag, IsShadowTree, LayoutNodeHelpers, Node,
167 NodeDamage, NodeFlags, NodeTraits, ShadowIncluding, UnbindContext,
168};
169use crate::dom::nodelist::NodeList;
170use crate::dom::promise::Promise;
171use crate::dom::range::Range;
172use crate::dom::raredata::ElementRareData;
173use crate::dom::scrolling_box::{ScrollAxisState, ScrollingBox};
174use crate::dom::servoparser::ServoParser;
175use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
176use crate::dom::text::Text;
177use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
178use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
179use crate::dom::validation::Validatable;
180use crate::dom::validitystate::ValidationFlags;
181use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
182use crate::script_runtime::CanGc;
183use crate::script_thread::ScriptThread;
184use crate::stylesheet_loader::StylesheetOwner;
185use crate::task::TaskOnce;
186
187#[dom_struct]
193pub struct Element {
194 node: Node,
195 #[no_trace]
196 local_name: LocalName,
197 tag_name: TagName,
198 #[no_trace]
199 namespace: Namespace,
200 #[no_trace]
201 prefix: DomRefCell<Option<Prefix>>,
202 attrs: DomRefCell<Vec<Dom<Attr>>>,
203 #[no_trace]
204 id_attribute: DomRefCell<Option<Atom>>,
205 #[no_trace]
207 is: DomRefCell<Option<LocalName>>,
208 #[conditional_malloc_size_of]
209 #[no_trace]
210 style_attribute: DomRefCell<Option<Arc<Locked<PropertyDeclarationBlock>>>>,
211 attr_list: MutNullableDom<NamedNodeMap>,
212 class_list: MutNullableDom<DOMTokenList>,
213 #[no_trace]
214 state: Cell<ElementState>,
215 selector_flags: AtomicUsize,
218 rare_data: DomRefCell<Option<Box<ElementRareData>>>,
219}
220
221impl fmt::Debug for Element {
222 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
223 write!(f, "<{}", self.local_name)?;
224 if let Some(ref id) = *self.id_attribute.borrow() {
225 write!(f, " id={}", id)?;
226 }
227 write!(f, ">")
228 }
229}
230
231#[derive(MallocSizeOf, PartialEq)]
232pub(crate) enum ElementCreator {
233 ParserCreated(u64),
234 ScriptCreated,
235}
236
237pub(crate) enum CustomElementCreationMode {
238 Synchronous,
239 Asynchronous,
240}
241
242impl ElementCreator {
243 pub(crate) fn is_parser_created(&self) -> bool {
244 match *self {
245 ElementCreator::ParserCreated(_) => true,
246 ElementCreator::ScriptCreated => false,
247 }
248 }
249 pub(crate) fn return_line_number(&self) -> u64 {
250 match *self {
251 ElementCreator::ParserCreated(l) => l,
252 ElementCreator::ScriptCreated => 1,
253 }
254 }
255}
256
257pub(crate) enum AdjacentPosition {
258 BeforeBegin,
259 AfterEnd,
260 AfterBegin,
261 BeforeEnd,
262}
263
264impl FromStr for AdjacentPosition {
265 type Err = Error;
266
267 fn from_str(position: &str) -> Result<Self, Self::Err> {
268 match_ignore_ascii_case! { position,
269 "beforebegin" => Ok(AdjacentPosition::BeforeBegin),
270 "afterbegin" => Ok(AdjacentPosition::AfterBegin),
271 "beforeend" => Ok(AdjacentPosition::BeforeEnd),
272 "afterend" => Ok(AdjacentPosition::AfterEnd),
273 _ => Err(Error::Syntax(None))
274 }
275 }
276}
277
278impl Element {
282 pub(crate) fn create(
283 name: QualName,
284 is: Option<LocalName>,
285 document: &Document,
286 creator: ElementCreator,
287 mode: CustomElementCreationMode,
288 proto: Option<HandleObject>,
289 can_gc: CanGc,
290 ) -> DomRoot<Element> {
291 create_element(name, is, document, creator, mode, proto, can_gc)
292 }
293
294 pub(crate) fn new_inherited(
295 local_name: LocalName,
296 namespace: Namespace,
297 prefix: Option<Prefix>,
298 document: &Document,
299 ) -> Element {
300 Element::new_inherited_with_state(
301 ElementState::empty(),
302 local_name,
303 namespace,
304 prefix,
305 document,
306 )
307 }
308
309 pub(crate) fn new_inherited_with_state(
310 state: ElementState,
311 local_name: LocalName,
312 namespace: Namespace,
313 prefix: Option<Prefix>,
314 document: &Document,
315 ) -> Element {
316 Element {
317 node: Node::new_inherited(document),
318 local_name,
319 tag_name: TagName::new(),
320 namespace,
321 prefix: DomRefCell::new(prefix),
322 attrs: DomRefCell::new(vec![]),
323 id_attribute: DomRefCell::new(None),
324 is: DomRefCell::new(None),
325 style_attribute: DomRefCell::new(None),
326 attr_list: Default::default(),
327 class_list: Default::default(),
328 state: Cell::new(state),
329 selector_flags: Default::default(),
330 rare_data: Default::default(),
331 }
332 }
333
334 pub(crate) fn new(
335 local_name: LocalName,
336 namespace: Namespace,
337 prefix: Option<Prefix>,
338 document: &Document,
339 proto: Option<HandleObject>,
340 can_gc: CanGc,
341 ) -> DomRoot<Element> {
342 Node::reflect_node_with_proto(
343 Box::new(Element::new_inherited(
344 local_name, namespace, prefix, document,
345 )),
346 document,
347 proto,
348 can_gc,
349 )
350 }
351
352 fn rare_data(&self) -> Ref<'_, Option<Box<ElementRareData>>> {
353 self.rare_data.borrow()
354 }
355
356 fn rare_data_mut(&self) -> RefMut<'_, Option<Box<ElementRareData>>> {
357 self.rare_data.borrow_mut()
358 }
359
360 fn ensure_rare_data(&self) -> RefMut<'_, Box<ElementRareData>> {
361 let mut rare_data = self.rare_data.borrow_mut();
362 if rare_data.is_none() {
363 *rare_data = Some(Default::default());
364 }
365 RefMut::map(rare_data, |rare_data| rare_data.as_mut().unwrap())
366 }
367
368 pub(crate) fn restyle(&self, damage: NodeDamage) {
369 let doc = self.node.owner_doc();
370 let mut restyle = doc.ensure_pending_restyle(self);
371
372 restyle.hint.insert(RestyleHint::RESTYLE_SELF);
375
376 match damage {
377 NodeDamage::Style => {},
378 NodeDamage::ContentOrHeritage => {
379 doc.note_node_with_dirty_descendants(self.upcast());
380 restyle
381 .damage
382 .insert(LayoutDamage::descendant_has_box_damage());
383 },
384 NodeDamage::Other => {
385 doc.note_node_with_dirty_descendants(self.upcast());
386 restyle.damage.insert(RestyleDamage::reconstruct());
387 },
388 }
389 }
390
391 pub(crate) fn set_is(&self, is: LocalName) {
392 *self.is.borrow_mut() = Some(is);
393 }
394
395 pub(crate) fn get_is(&self) -> Option<LocalName> {
397 self.is.borrow().clone()
398 }
399
400 pub(crate) fn set_initial_custom_element_state_to_uncustomized(&self) {
409 let mut state = self.state.get();
410 state.insert(ElementState::DEFINED);
411 self.state.set(state);
412 }
413
414 pub(crate) fn set_custom_element_state(&self, state: CustomElementState) {
416 if state != CustomElementState::Uncustomized {
418 self.ensure_rare_data().custom_element_state = state;
419 }
420
421 let in_defined_state = matches!(
422 state,
423 CustomElementState::Uncustomized | CustomElementState::Custom
424 );
425 self.set_state(ElementState::DEFINED, in_defined_state)
426 }
427
428 pub(crate) fn get_custom_element_state(&self) -> CustomElementState {
429 if let Some(rare_data) = self.rare_data().as_ref() {
430 return rare_data.custom_element_state;
431 }
432 CustomElementState::Uncustomized
433 }
434
435 pub(crate) fn is_custom(&self) -> bool {
437 self.get_custom_element_state() == CustomElementState::Custom
438 }
439
440 pub(crate) fn set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>) {
441 self.ensure_rare_data().custom_element_definition = Some(definition);
442 }
443
444 pub(crate) fn get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>> {
445 self.rare_data().as_ref()?.custom_element_definition.clone()
446 }
447
448 pub(crate) fn clear_custom_element_definition(&self) {
449 self.ensure_rare_data().custom_element_definition = None;
450 }
451
452 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
453 pub(crate) fn push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>) {
454 self.ensure_rare_data()
455 .custom_element_reaction_queue
456 .push(CustomElementReaction::Callback(function, args));
457 }
458
459 pub(crate) fn push_upgrade_reaction(&self, definition: Rc<CustomElementDefinition>) {
460 self.ensure_rare_data()
461 .custom_element_reaction_queue
462 .push(CustomElementReaction::Upgrade(definition));
463 }
464
465 pub(crate) fn clear_reaction_queue(&self) {
466 if let Some(ref mut rare_data) = *self.rare_data_mut() {
467 rare_data.custom_element_reaction_queue.clear();
468 }
469 }
470
471 pub(crate) fn invoke_reactions(&self, can_gc: CanGc) {
472 loop {
473 rooted_vec!(let mut reactions);
474 match *self.rare_data_mut() {
475 Some(ref mut data) => {
476 mem::swap(&mut *reactions, &mut data.custom_element_reaction_queue)
477 },
478 None => break,
479 };
480
481 if reactions.is_empty() {
482 break;
483 }
484
485 for reaction in reactions.iter() {
486 reaction.invoke(self, can_gc);
487 }
488
489 reactions.clear();
490 }
491 }
492
493 pub(crate) fn style(&self) -> Option<Arc<ComputedValues>> {
496 self.upcast::<Node>().style()
497 }
498
499 pub(crate) fn has_css_layout_box(&self) -> bool {
501 self.style()
502 .is_some_and(|s| !s.get_box().clone_display().is_none())
503 }
504
505 pub(crate) fn is_potentially_scrollable_body(&self) -> bool {
507 self.is_potentially_scrollable_body_shared_logic(false)
508 }
509
510 pub(crate) fn is_potentially_scrollable_body_for_scrolling_element(&self) -> bool {
512 self.is_potentially_scrollable_body_shared_logic(true)
513 }
514
515 fn is_potentially_scrollable_body_shared_logic(
517 &self,
518 treat_overflow_clip_on_parent_as_hidden: bool,
519 ) -> bool {
520 let node = self.upcast::<Node>();
521 debug_assert!(
522 node.owner_doc().GetBody().as_deref() == self.downcast::<HTMLElement>(),
523 "Called is_potentially_scrollable_body on element that is not the <body>"
524 );
525
526 if !self.has_css_layout_box() {
530 return false;
531 }
532
533 if let Some(parent) = node.GetParentElement() {
536 if let Some(style) = parent.style() {
537 let mut overflow_x = style.get_box().clone_overflow_x();
538 let mut overflow_y = style.get_box().clone_overflow_y();
539
540 if treat_overflow_clip_on_parent_as_hidden {
543 if overflow_x == Overflow::Clip {
544 overflow_x = Overflow::Hidden;
545 }
546 if overflow_y == Overflow::Clip {
547 overflow_y = Overflow::Hidden;
548 }
549 }
550
551 if !overflow_x.is_scrollable() && !overflow_y.is_scrollable() {
552 return false;
553 }
554 };
555 }
556
557 if let Some(style) = self.style() {
560 if !style.get_box().clone_overflow_x().is_scrollable() &&
561 !style.get_box().clone_overflow_y().is_scrollable()
562 {
563 return false;
564 }
565 };
566
567 true
568 }
569
570 pub(crate) fn establishes_scroll_container(&self) -> bool {
573 self.upcast::<Node>()
575 .effective_overflow()
576 .is_some_and(|overflow| overflow.establishes_scroll_container())
577 }
578
579 pub(crate) fn has_overflow(&self) -> bool {
580 self.ScrollHeight() > self.ClientHeight() || self.ScrollWidth() > self.ClientWidth()
581 }
582
583 fn has_scrolling_box(&self) -> bool {
591 self.has_css_layout_box() && self.establishes_scroll_container() && self.has_overflow()
592 }
593
594 pub(crate) fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
595 self.rare_data()
596 .as_ref()?
597 .shadow_root
598 .as_ref()
599 .map(|sr| DomRoot::from_ref(&**sr))
600 }
601
602 pub(crate) fn is_shadow_host(&self) -> bool {
603 self.shadow_root().is_some()
604 }
605
606 #[allow(clippy::too_many_arguments)]
608 pub(crate) fn attach_shadow(
609 &self,
610 is_ua_widget: IsUserAgentWidget,
611 mode: ShadowRootMode,
612 clonable: bool,
613 serializable: bool,
614 delegates_focus: bool,
615 slot_assignment_mode: SlotAssignmentMode,
616 can_gc: CanGc,
617 ) -> Fallible<DomRoot<ShadowRoot>> {
618 if self.namespace != ns!(html) {
621 return Err(Error::NotSupported(Some(
622 "Cannot attach shadow roots to elements with non-HTML namespaces".to_owned(),
623 )));
624 }
625
626 if !is_valid_shadow_host_name(self.local_name()) {
629 if is_ua_widget != IsUserAgentWidget::Yes {
631 let error_message = format!(
632 "Cannot attach shadow roots to <{}> elements",
633 *self.local_name()
634 );
635 return Err(Error::NotSupported(Some(error_message)));
636 }
637 }
638
639 if is_valid_custom_element_name(self.local_name()) || self.get_is().is_some() {
642 let definition = self.get_custom_element_definition();
646 if definition.is_some_and(|definition| definition.disable_shadow) {
649 let error_message = format!(
650 "The custom element constructor of <{}> disabled attachment of shadow roots",
651 self.local_name()
652 );
653 return Err(Error::NotSupported(Some(error_message)));
654 }
655 }
656
657 if let Some(current_shadow_root) = self.shadow_root() {
660 if !current_shadow_root.is_declarative() ||
664 current_shadow_root.shadow_root_mode() != mode
665 {
666 return Err(Error::NotSupported(Some(
667 "Cannot attach a second shadow root to the same element".into(),
668 )));
669 }
670
671 for child in current_shadow_root.upcast::<Node>().children() {
673 child.remove_self(can_gc);
674 }
675
676 current_shadow_root.set_declarative(false);
678
679 return Ok(current_shadow_root);
681 }
682
683 let shadow_root = ShadowRoot::new(
690 self,
691 &self.node.owner_doc(),
692 mode,
693 slot_assignment_mode,
694 clonable,
695 is_ua_widget,
696 can_gc,
697 );
698
699 let node = self.upcast::<Node>();
703 node.remove_layout_boxes_from_subtree();
704
705 shadow_root.set_delegates_focus(delegates_focus);
707
708 if matches!(
711 self.get_custom_element_state(),
712 CustomElementState::Precustomized | CustomElementState::Custom
713 ) {
714 shadow_root.set_available_to_element_internals(true);
715 }
716
717 shadow_root.set_declarative(false);
719
720 shadow_root.set_serializable(serializable);
722
723 self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
725 shadow_root
726 .upcast::<Node>()
727 .set_containing_shadow_root(Some(&shadow_root));
728
729 let bind_context = BindContext::new(self.upcast(), IsShadowTree::Yes);
730 shadow_root.bind_to_tree(&bind_context, can_gc);
731
732 node.dirty(NodeDamage::Other);
733
734 Ok(shadow_root)
735 }
736
737 pub(crate) fn attach_ua_shadow_root(
753 &self,
754 use_ua_widget_styling: bool,
755 can_gc: CanGc,
756 ) -> DomRoot<ShadowRoot> {
757 let root = self
758 .attach_shadow(
759 IsUserAgentWidget::Yes,
760 ShadowRootMode::Closed,
761 false,
762 false,
763 false,
764 SlotAssignmentMode::Manual,
765 can_gc,
766 )
767 .expect("Attaching UA shadow root failed");
768
769 root.upcast::<Node>()
770 .set_in_ua_widget(use_ua_widget_styling);
771 root
772 }
773
774 pub(crate) fn is_translate_enabled(&self) -> bool {
776 let name = &local_name!("translate");
777 if self.has_attribute(name) {
778 let attribute = self.get_string_attribute(name);
779 match_ignore_ascii_case! { &*attribute.str(),
780 "yes" | "" => return true,
781 "no" => return false,
782 _ => {},
783 }
784 }
785 if let Some(parent) = self.upcast::<Node>().GetParentNode() {
786 if let Some(elem) = parent.downcast::<Element>() {
787 return elem.is_translate_enabled();
788 }
789 }
790 true
791 }
792
793 pub(crate) fn directionality(&self) -> String {
795 self.downcast::<HTMLElement>()
796 .and_then(|html_element| html_element.directionality())
797 .unwrap_or_else(|| {
798 let node = self.upcast::<Node>();
799 node.parent_directionality()
800 })
801 }
802
803 pub(crate) fn is_root(&self) -> bool {
804 match self.node.GetParentNode() {
805 None => false,
806 Some(node) => node.is::<Document>(),
807 }
808 }
809
810 pub(crate) fn registered_intersection_observers_mut(
813 &self,
814 ) -> RefMut<'_, Vec<IntersectionObserverRegistration>> {
815 RefMut::map(self.ensure_rare_data(), |rare_data| {
816 &mut rare_data.registered_intersection_observers
817 })
818 }
819
820 pub(crate) fn registered_intersection_observers(
821 &self,
822 ) -> Option<Ref<'_, Vec<IntersectionObserverRegistration>>> {
823 let rare_data: Ref<'_, _> = self.rare_data.borrow();
824
825 if rare_data.is_none() {
826 return None;
827 }
828 Some(Ref::map(rare_data, |rare_data| {
829 &rare_data
830 .as_ref()
831 .unwrap()
832 .registered_intersection_observers
833 }))
834 }
835
836 pub(crate) fn get_intersection_observer_registration(
837 &self,
838 observer: &IntersectionObserver,
839 ) -> Option<Ref<'_, IntersectionObserverRegistration>> {
840 if let Some(registrations) = self.registered_intersection_observers() {
841 registrations
842 .iter()
843 .position(|reg_obs| reg_obs.observer == observer)
844 .map(|index| Ref::map(registrations, |registrations| ®istrations[index]))
845 } else {
846 None
847 }
848 }
849
850 pub(crate) fn add_initial_intersection_observer_registration(
852 &self,
853 observer: &IntersectionObserver,
854 ) {
855 self.ensure_rare_data()
856 .registered_intersection_observers
857 .push(IntersectionObserverRegistration::new_initial(observer));
858 }
859
860 pub(crate) fn remove_intersection_observer(&self, observer: &IntersectionObserver) {
862 self.ensure_rare_data()
863 .registered_intersection_observers
864 .retain(|reg_obs| *reg_obs.observer != *observer)
865 }
866
867 pub(crate) fn scrolling_box(&self, flags: ScrollContainerQueryFlags) -> Option<ScrollingBox> {
870 self.owner_window()
871 .scrolling_box_query(Some(self.upcast()), flags)
872 }
873
874 pub(crate) fn scroll_into_view_with_options(
876 &self,
877 behavior: ScrollBehavior,
878 block: ScrollAxisState,
879 inline: ScrollAxisState,
880 container: Option<&Element>,
881 inner_target_rect: Option<Rect<Au, CSSPixel>>,
882 ) {
883 let get_target_rect = || match inner_target_rect {
884 None => self.upcast::<Node>().border_box().unwrap_or_default(),
885 Some(inner_target_rect) => inner_target_rect.translate(
886 self.upcast::<Node>()
887 .content_box()
888 .unwrap_or_default()
889 .origin
890 .to_vector(),
891 ),
892 };
893
894 let mut parent_scrolling_box = self.scrolling_box(ScrollContainerQueryFlags::empty());
897 while let Some(scrolling_box) = parent_scrolling_box {
898 parent_scrolling_box = scrolling_box.parent();
899
900 let position =
911 scrolling_box.determine_scroll_into_view_position(block, inline, get_target_rect());
912
913 if position != scrolling_box.scroll_position() {
918 scrolling_box.scroll_to(position, behavior);
929 }
930
931 if container.is_some_and(|container| {
935 let container_node = container.upcast::<Node>();
936 scrolling_box
937 .node()
938 .is_shadow_including_inclusive_ancestor_of(container_node)
939 }) {
940 return;
941 }
942 }
943
944 let window_proxy = self.owner_window().window_proxy();
945 let Some(frame_element) = window_proxy.frame_element() else {
946 return;
947 };
948
949 let inner_target_rect = Some(get_target_rect());
950 let parent_window = frame_element.owner_window();
951 let cx = GlobalScope::get_cx();
952 let _ac = JSAutoRealm::new(*cx, *parent_window.reflector().get_jsobject());
953 frame_element.scroll_into_view_with_options(
954 behavior,
955 block,
956 inline,
957 None,
958 inner_target_rect,
959 )
960 }
961
962 pub(crate) fn ensure_contenteditable_selection_range(
963 &self,
964 document: &Document,
965 can_gc: CanGc,
966 ) -> DomRoot<Range> {
967 self.ensure_rare_data()
968 .contenteditable_selection_range
969 .or_init(|| Range::new_with_doc(document, None, can_gc))
970 }
971}
972
973#[inline]
975pub(crate) fn is_valid_shadow_host_name(name: &LocalName) -> bool {
976 if is_valid_custom_element_name(name) {
979 return true;
980 }
981
982 matches!(
985 name,
986 &local_name!("article") |
987 &local_name!("aside") |
988 &local_name!("blockquote") |
989 &local_name!("body") |
990 &local_name!("div") |
991 &local_name!("footer") |
992 &local_name!("h1") |
993 &local_name!("h2") |
994 &local_name!("h3") |
995 &local_name!("h4") |
996 &local_name!("h5") |
997 &local_name!("h6") |
998 &local_name!("header") |
999 &local_name!("main") |
1000 &local_name!("nav") |
1001 &local_name!("p") |
1002 &local_name!("section") |
1003 &local_name!("span")
1004 )
1005}
1006
1007#[inline]
1008pub(crate) fn get_attr_for_layout<'dom>(
1009 elem: LayoutDom<'dom, Element>,
1010 namespace: &Namespace,
1011 name: &LocalName,
1012) -> Option<&'dom AttrValue> {
1013 elem.attrs()
1014 .iter()
1015 .find(|attr| name == attr.local_name() && namespace == attr.namespace())
1016 .map(|attr| attr.value())
1017}
1018
1019pub(crate) trait LayoutElementHelpers<'dom> {
1020 fn attrs(self) -> &'dom [LayoutDom<'dom, Attr>];
1021 fn has_class_or_part_for_layout(
1022 self,
1023 name: &AtomIdent,
1024 attr_name: &LocalName,
1025 case_sensitivity: CaseSensitivity,
1026 ) -> bool;
1027 fn get_classes_for_layout(self) -> Option<&'dom [Atom]>;
1028 fn get_parts_for_layout(self) -> Option<&'dom [Atom]>;
1029
1030 fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
1031 where
1032 V: Push<ApplicableDeclarationBlock>;
1033 fn get_span(self) -> Option<u32>;
1034 fn get_colspan(self) -> Option<u32>;
1035 fn get_rowspan(self) -> Option<u32>;
1036 fn is_html_element(&self) -> bool;
1037 fn id_attribute(self) -> *const Option<Atom>;
1038 fn style_attribute(self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>>;
1039 fn local_name(self) -> &'dom LocalName;
1040 fn namespace(self) -> &'dom Namespace;
1041 fn get_lang_attr_val_for_layout(self) -> Option<&'dom str>;
1042 fn get_lang_for_layout(self) -> String;
1043 fn get_state_for_layout(self) -> ElementState;
1044 fn insert_selector_flags(self, flags: ElementSelectorFlags);
1045 fn get_selector_flags(self) -> ElementSelectorFlags;
1046 fn get_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>>;
1048 fn get_attr_for_layout(
1049 self,
1050 namespace: &Namespace,
1051 name: &LocalName,
1052 ) -> Option<&'dom AttrValue>;
1053 fn get_attr_val_for_layout(self, namespace: &Namespace, name: &LocalName) -> Option<&'dom str>;
1054 fn get_attr_vals_for_layout(self, name: &LocalName) -> impl Iterator<Item = &'dom AttrValue>;
1055 fn each_custom_state_for_layout(self, allback: impl FnMut(&AtomIdent));
1056}
1057
1058impl LayoutDom<'_, Element> {
1059 pub(super) fn focus_state(self) -> bool {
1060 self.unsafe_get().state.get().contains(ElementState::FOCUS)
1061 }
1062}
1063
1064impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> {
1065 #[expect(unsafe_code)]
1066 #[inline]
1067 fn attrs(self) -> &'dom [LayoutDom<'dom, Attr>] {
1068 unsafe { LayoutDom::to_layout_slice(self.unsafe_get().attrs.borrow_for_layout()) }
1069 }
1070
1071 #[inline]
1072 fn has_class_or_part_for_layout(
1073 self,
1074 name: &AtomIdent,
1075 attr_name: &LocalName,
1076 case_sensitivity: CaseSensitivity,
1077 ) -> bool {
1078 get_attr_for_layout(self, &ns!(), attr_name).is_some_and(|attr| {
1079 attr.as_tokens()
1080 .iter()
1081 .any(|atom| case_sensitivity.eq_atom(atom, name))
1082 })
1083 }
1084
1085 #[inline]
1086 fn get_classes_for_layout(self) -> Option<&'dom [Atom]> {
1087 get_attr_for_layout(self, &ns!(), &local_name!("class")).map(|attr| attr.as_tokens())
1088 }
1089
1090 fn get_parts_for_layout(self) -> Option<&'dom [Atom]> {
1091 get_attr_for_layout(self, &ns!(), &local_name!("part")).map(|attr| attr.as_tokens())
1092 }
1093
1094 fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
1095 where
1096 V: Push<ApplicableDeclarationBlock>,
1097 {
1098 let mut property_declaration_block = None;
1099 let mut push = |declaration| {
1100 property_declaration_block
1101 .get_or_insert_with(PropertyDeclarationBlock::default)
1102 .push(declaration, Importance::Normal);
1103 };
1104
1105 if let Some(lang) = self.get_lang_attr_val_for_layout() {
1108 push(PropertyDeclaration::XLang(specified::XLang(Atom::from(
1109 lang.to_owned(),
1110 ))));
1111 }
1112
1113 let bgcolor = if let Some(this) = self.downcast::<HTMLBodyElement>() {
1114 this.get_background_color()
1115 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1116 this.get_background_color()
1117 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1118 this.get_background_color()
1119 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1120 this.get_background_color()
1121 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1122 this.get_background_color()
1123 } else {
1124 None
1125 };
1126
1127 if let Some(color) = bgcolor {
1128 push(PropertyDeclaration::BackgroundColor(
1129 specified::Color::from_absolute_color(color),
1130 ));
1131 }
1132
1133 if is_element_affected_by_legacy_background_presentational_hint(
1134 self.namespace(),
1135 self.local_name(),
1136 ) {
1137 if let Some(url) = self
1138 .get_attr_for_layout(&ns!(), &local_name!("background"))
1139 .and_then(AttrValue::as_resolved_url)
1140 .cloned()
1141 {
1142 push(PropertyDeclaration::BackgroundImage(
1143 background_image::SpecifiedValue(
1144 vec![specified::Image::for_cascade(url)].into(),
1145 ),
1146 ));
1147 }
1148 }
1149
1150 let color = if let Some(this) = self.downcast::<HTMLFontElement>() {
1151 this.get_color()
1152 } else if let Some(this) = self.downcast::<HTMLBodyElement>() {
1153 this.get_color()
1155 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1156 this.get_color()
1158 } else {
1159 None
1160 };
1161
1162 if let Some(color) = color {
1163 push(PropertyDeclaration::Color(
1164 longhands::color::SpecifiedValue(specified::Color::from_absolute_color(color)),
1165 ));
1166 }
1167
1168 let font_face = self
1169 .downcast::<HTMLFontElement>()
1170 .and_then(HTMLFontElementLayoutHelpers::get_face);
1171 if let Some(font_face) = font_face {
1172 push(PropertyDeclaration::FontFamily(
1173 font_family::SpecifiedValue::Values(computed::font::FontFamilyList {
1174 list: ArcSlice::from_iter(
1175 HTMLFontElement::parse_face_attribute(font_face).into_iter(),
1176 ),
1177 }),
1178 ));
1179 }
1180
1181 let font_size = self
1182 .downcast::<HTMLFontElement>()
1183 .and_then(HTMLFontElementLayoutHelpers::get_size);
1184 if let Some(font_size) = font_size {
1185 push(PropertyDeclaration::FontSize(
1186 font_size::SpecifiedValue::from_html_size(font_size as u8),
1187 ));
1188 }
1189
1190 let cellspacing = self
1191 .downcast::<HTMLTableElement>()
1192 .and_then(HTMLTableElementLayoutHelpers::get_cellspacing);
1193 if let Some(cellspacing) = cellspacing {
1194 let width_value = specified::Length::from_px(cellspacing as f32);
1195 push(PropertyDeclaration::BorderSpacing(Box::new(
1196 border_spacing::SpecifiedValue::new(width_value.clone().into(), width_value.into()),
1197 )));
1198 }
1199
1200 let size = self
1206 .downcast::<HTMLInputElement>()
1207 .and_then(|input_element| {
1208 match self.get_attr_val_for_layout(&ns!(), &local_name!("type")) {
1210 Some("hidden") | Some("range") | Some("color") | Some("checkbox") |
1211 Some("radio") | Some("file") | Some("submit") | Some("image") |
1212 Some("reset") | Some("button") => None,
1213 _ => match input_element.size_for_layout() {
1215 0 => None,
1216 s => Some(s as i32),
1217 },
1218 }
1219 });
1220
1221 if let Some(size) = size {
1222 let value =
1223 specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(size));
1224 push(PropertyDeclaration::Width(
1225 specified::Size::LengthPercentage(NonNegative(
1226 specified::LengthPercentage::Length(value),
1227 )),
1228 ));
1229 }
1230
1231 let width = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1232 this.get_width()
1233 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1234 this.get_width()
1235 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1236 this.get_width()
1237 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1238 this.get_width()
1239 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1240 this.get_width()
1241 } else if let Some(this) = self.downcast::<HTMLTableColElement>() {
1242 this.get_width()
1243 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1244 this.get_width()
1246 } else {
1247 LengthOrPercentageOrAuto::Auto
1248 };
1249
1250 match width {
1252 LengthOrPercentageOrAuto::Auto => {},
1253 LengthOrPercentageOrAuto::Percentage(percentage) => {
1254 let width_value = specified::Size::LengthPercentage(NonNegative(
1255 specified::LengthPercentage::Percentage(computed::Percentage(percentage)),
1256 ));
1257 push(PropertyDeclaration::Width(width_value));
1258 },
1259 LengthOrPercentageOrAuto::Length(length) => {
1260 let width_value = specified::Size::LengthPercentage(NonNegative(
1261 specified::LengthPercentage::Length(specified::NoCalcLength::Absolute(
1262 specified::AbsoluteLength::Px(length.to_f32_px()),
1263 )),
1264 ));
1265 push(PropertyDeclaration::Width(width_value));
1266 },
1267 }
1268
1269 let height = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1270 this.get_height()
1271 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1272 this.get_height()
1273 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1274 this.get_height()
1275 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1276 this.get_height()
1277 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1278 this.get_height()
1279 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1280 this.get_height()
1281 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1282 this.get_height()
1283 } else {
1284 LengthOrPercentageOrAuto::Auto
1285 };
1286
1287 match height {
1288 LengthOrPercentageOrAuto::Auto => {},
1289 LengthOrPercentageOrAuto::Percentage(percentage) => {
1290 let height_value = specified::Size::LengthPercentage(NonNegative(
1291 specified::LengthPercentage::Percentage(computed::Percentage(percentage)),
1292 ));
1293 push(PropertyDeclaration::Height(height_value));
1294 },
1295 LengthOrPercentageOrAuto::Length(length) => {
1296 let height_value = specified::Size::LengthPercentage(NonNegative(
1297 specified::LengthPercentage::Length(specified::NoCalcLength::Absolute(
1298 specified::AbsoluteLength::Px(length.to_f32_px()),
1299 )),
1300 ));
1301 push(PropertyDeclaration::Height(height_value));
1302 },
1303 }
1304
1305 if self.downcast::<HTMLImageElement>().is_some() ||
1308 self.downcast::<HTMLVideoElement>().is_some()
1309 {
1310 if let LengthOrPercentageOrAuto::Length(width) = width {
1311 if let LengthOrPercentageOrAuto::Length(height) = height {
1312 let width_value = NonNegative(specified::Number::new(width.to_f32_px()));
1313 let height_value = NonNegative(specified::Number::new(height.to_f32_px()));
1314 let aspect_ratio = specified::position::AspectRatio {
1315 auto: true,
1316 ratio: PreferredRatio::Ratio(Ratio(width_value, height_value)),
1317 };
1318 push(PropertyDeclaration::AspectRatio(aspect_ratio));
1319 }
1320 }
1321 }
1322
1323 let cols = self
1324 .downcast::<HTMLTextAreaElement>()
1325 .map(LayoutHTMLTextAreaElementHelpers::get_cols);
1326 if let Some(cols) = cols {
1327 let cols = cols as i32;
1328 if cols > 0 {
1329 let value =
1335 specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(cols));
1336 push(PropertyDeclaration::Width(
1337 specified::Size::LengthPercentage(NonNegative(
1338 specified::LengthPercentage::Length(value),
1339 )),
1340 ));
1341 }
1342 }
1343
1344 let rows = self
1345 .downcast::<HTMLTextAreaElement>()
1346 .map(LayoutHTMLTextAreaElementHelpers::get_rows);
1347 if let Some(rows) = rows {
1348 let rows = rows as i32;
1349 if rows > 0 {
1350 let value = specified::NoCalcLength::FontRelative(
1354 specified::FontRelativeLength::Em(rows as CSSFloat),
1355 );
1356 push(PropertyDeclaration::Height(
1357 specified::Size::LengthPercentage(NonNegative(
1358 specified::LengthPercentage::Length(value),
1359 )),
1360 ));
1361 }
1362 }
1363
1364 let border = self
1365 .downcast::<HTMLTableElement>()
1366 .and_then(|table| table.get_border());
1367 if let Some(border) = border {
1368 let width_value = specified::BorderSideWidth::from_px(border as f32);
1369 push(PropertyDeclaration::BorderTopWidth(width_value.clone()));
1370 push(PropertyDeclaration::BorderLeftWidth(width_value.clone()));
1371 push(PropertyDeclaration::BorderBottomWidth(width_value.clone()));
1372 push(PropertyDeclaration::BorderRightWidth(width_value));
1373 }
1374
1375 if let Some(cellpadding) = self
1376 .downcast::<HTMLTableCellElement>()
1377 .and_then(|this| this.get_table())
1378 .and_then(|table| table.get_cellpadding())
1379 {
1380 let cellpadding = NonNegative(specified::LengthPercentage::Length(
1381 specified::NoCalcLength::from_px(cellpadding as f32),
1382 ));
1383 push(PropertyDeclaration::PaddingTop(cellpadding.clone()));
1384 push(PropertyDeclaration::PaddingLeft(cellpadding.clone()));
1385 push(PropertyDeclaration::PaddingBottom(cellpadding.clone()));
1386 push(PropertyDeclaration::PaddingRight(cellpadding));
1387 }
1388
1389 if let Some(size_info) = self
1391 .downcast::<HTMLHRElement>()
1392 .and_then(|hr_element| hr_element.get_size_info())
1393 {
1394 match size_info {
1395 SizePresentationalHint::SetHeightTo(height) => {
1396 push(PropertyDeclaration::Height(height));
1397 },
1398 SizePresentationalHint::SetAllBorderWidthValuesTo(border_width) => {
1399 push(PropertyDeclaration::BorderLeftWidth(border_width.clone()));
1400 push(PropertyDeclaration::BorderRightWidth(border_width.clone()));
1401 push(PropertyDeclaration::BorderTopWidth(border_width.clone()));
1402 push(PropertyDeclaration::BorderBottomWidth(border_width));
1403 },
1404 SizePresentationalHint::SetBottomBorderWidthToZero => {
1405 push(PropertyDeclaration::BorderBottomWidth(
1406 specified::border::BorderSideWidth::from_px(0.),
1407 ));
1408 },
1409 }
1410 }
1411
1412 let Some(property_declaration_block) = property_declaration_block else {
1413 return;
1414 };
1415
1416 let document = self.upcast::<Node>().owner_doc_for_layout();
1417 let shared_lock = document.style_shared_lock();
1418 hints.push(ApplicableDeclarationBlock::from_declarations(
1419 Arc::new(shared_lock.wrap(property_declaration_block)),
1420 CascadeLevel::PresHints,
1421 LayerOrder::root(),
1422 ));
1423 }
1424
1425 fn get_span(self) -> Option<u32> {
1426 self.downcast::<HTMLTableColElement>()
1428 .and_then(|element| element.get_span())
1429 }
1430
1431 fn get_colspan(self) -> Option<u32> {
1432 self.downcast::<HTMLTableCellElement>()
1434 .and_then(|element| element.get_colspan())
1435 }
1436
1437 fn get_rowspan(self) -> Option<u32> {
1438 self.downcast::<HTMLTableCellElement>()
1440 .and_then(|element| element.get_rowspan())
1441 }
1442
1443 #[inline]
1444 fn is_html_element(&self) -> bool {
1445 *self.namespace() == ns!(html)
1446 }
1447
1448 #[expect(unsafe_code)]
1449 fn id_attribute(self) -> *const Option<Atom> {
1450 unsafe { (self.unsafe_get()).id_attribute.borrow_for_layout() }
1451 }
1452
1453 #[expect(unsafe_code)]
1454 fn style_attribute(self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>> {
1455 unsafe { (self.unsafe_get()).style_attribute.borrow_for_layout() }
1456 }
1457
1458 fn local_name(self) -> &'dom LocalName {
1459 &(self.unsafe_get()).local_name
1460 }
1461
1462 fn namespace(self) -> &'dom Namespace {
1463 &(self.unsafe_get()).namespace
1464 }
1465
1466 fn get_lang_attr_val_for_layout(self) -> Option<&'dom str> {
1467 if let Some(attr) = self.get_attr_val_for_layout(&ns!(xml), &local_name!("lang")) {
1468 return Some(attr);
1469 }
1470 if let Some(attr) = self.get_attr_val_for_layout(&ns!(), &local_name!("lang")) {
1471 return Some(attr);
1472 }
1473 None
1474 }
1475
1476 fn get_lang_for_layout(self) -> String {
1477 let mut current_node = Some(self.upcast::<Node>());
1478 while let Some(node) = current_node {
1479 current_node = node.composed_parent_node_ref();
1480 match node.downcast::<Element>() {
1481 Some(elem) => {
1482 if let Some(attr) = elem.get_lang_attr_val_for_layout() {
1483 return attr.to_owned();
1484 }
1485 },
1486 None => continue,
1487 }
1488 }
1489 String::new()
1492 }
1493
1494 #[inline]
1495 fn get_state_for_layout(self) -> ElementState {
1496 (self.unsafe_get()).state.get()
1497 }
1498
1499 #[inline]
1500 fn insert_selector_flags(self, flags: ElementSelectorFlags) {
1501 debug_assert!(thread_state::get().is_layout());
1502 self.unsafe_get().insert_selector_flags(flags);
1503 }
1504
1505 #[inline]
1506 fn get_selector_flags(self) -> ElementSelectorFlags {
1507 self.unsafe_get().get_selector_flags()
1508 }
1509
1510 #[inline]
1511 #[expect(unsafe_code)]
1512 fn get_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
1513 unsafe {
1514 self.unsafe_get()
1515 .rare_data
1516 .borrow_for_layout()
1517 .as_ref()?
1518 .shadow_root
1519 .as_ref()
1520 .map(|sr| sr.to_layout())
1521 }
1522 }
1523
1524 #[inline]
1525 fn get_attr_for_layout(
1526 self,
1527 namespace: &Namespace,
1528 name: &LocalName,
1529 ) -> Option<&'dom AttrValue> {
1530 get_attr_for_layout(self, namespace, name)
1531 }
1532
1533 #[inline]
1534 fn get_attr_val_for_layout(self, namespace: &Namespace, name: &LocalName) -> Option<&'dom str> {
1535 get_attr_for_layout(self, namespace, name).map(|attr| &**attr)
1536 }
1537
1538 #[inline]
1539 fn get_attr_vals_for_layout(self, name: &LocalName) -> impl Iterator<Item = &'dom AttrValue> {
1540 self.attrs().iter().filter_map(move |attr| {
1541 if name == attr.local_name() {
1542 Some(attr.value())
1543 } else {
1544 None
1545 }
1546 })
1547 }
1548
1549 #[expect(unsafe_code)]
1550 fn each_custom_state_for_layout(self, mut callback: impl FnMut(&AtomIdent)) {
1551 let rare_data = unsafe { self.unsafe_get().rare_data.borrow_for_layout() };
1552 let Some(rare_data) = rare_data.as_ref() else {
1553 return;
1554 };
1555 let Some(element_internals) = rare_data.element_internals.as_ref() else {
1556 return;
1557 };
1558
1559 let element_internals = unsafe { element_internals.to_layout() };
1560 if let Some(states) = element_internals.unsafe_get().custom_states_for_layout() {
1561 for state in unsafe { states.unsafe_get().set_for_layout().iter() } {
1562 callback(&AtomIdent::from(&*state.str()));
1564 }
1565 }
1566 }
1567}
1568
1569impl Element {
1570 pub(crate) fn is_html_element(&self) -> bool {
1571 self.namespace == ns!(html)
1572 }
1573
1574 pub(crate) fn html_element_in_html_document(&self) -> bool {
1575 self.is_html_element() && self.upcast::<Node>().is_in_html_doc()
1576 }
1577
1578 pub(crate) fn local_name(&self) -> &LocalName {
1579 &self.local_name
1580 }
1581
1582 pub(crate) fn parsed_name(&self, mut name: DOMString) -> LocalName {
1583 if self.html_element_in_html_document() {
1584 name.make_ascii_lowercase();
1585 }
1586 LocalName::from(name)
1587 }
1588
1589 pub(crate) fn namespace(&self) -> &Namespace {
1590 &self.namespace
1591 }
1592
1593 pub(crate) fn prefix(&self) -> Ref<'_, Option<Prefix>> {
1594 self.prefix.borrow()
1595 }
1596
1597 pub(crate) fn set_prefix(&self, prefix: Option<Prefix>) {
1598 *self.prefix.borrow_mut() = prefix;
1599 }
1600
1601 pub(crate) fn set_custom_element_registry(
1602 &self,
1603 registry: Option<DomRoot<CustomElementRegistry>>,
1604 ) {
1605 self.ensure_rare_data().custom_element_registry = registry.as_deref().map(Dom::from_ref);
1606 }
1607
1608 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
1609 self.rare_data()
1610 .as_ref()?
1611 .custom_element_registry
1612 .as_deref()
1613 .map(DomRoot::from_ref)
1614 }
1615
1616 pub(crate) fn attrs(&self) -> Ref<'_, [Dom<Attr>]> {
1617 Ref::map(self.attrs.borrow(), |attrs| &**attrs)
1618 }
1619
1620 pub(crate) fn locate_namespace(&self, prefix: Option<DOMString>) -> Namespace {
1622 let namespace_prefix = prefix.clone().map(|s| Prefix::from(&*s.str()));
1623
1624 if namespace_prefix == Some(namespace_prefix!("xml")) {
1626 return ns!(xml);
1627 }
1628
1629 if namespace_prefix == Some(namespace_prefix!("xmlns")) {
1631 return ns!(xmlns);
1632 }
1633
1634 let prefix = prefix.map(LocalName::from);
1635
1636 let inclusive_ancestor_elements = self
1637 .upcast::<Node>()
1638 .inclusive_ancestors(ShadowIncluding::No)
1639 .filter_map(DomRoot::downcast::<Self>);
1640
1641 for element in inclusive_ancestor_elements {
1644 if element.namespace() != &ns!() &&
1646 element.prefix().as_ref().map(|p| &**p) == prefix.as_deref()
1647 {
1648 return element.namespace().clone();
1649 }
1650
1651 let attr = Ref::filter_map(self.attrs(), |attrs| {
1656 attrs.iter().find(|attr| {
1657 if attr.namespace() != &ns!(xmlns) {
1658 return false;
1659 }
1660 match (attr.prefix(), prefix.as_ref()) {
1661 (Some(&namespace_prefix!("xmlns")), Some(prefix)) => {
1662 attr.local_name() == prefix
1663 },
1664 (None, None) => attr.local_name() == &local_name!("xmlns"),
1665 _ => false,
1666 }
1667 })
1668 })
1669 .ok();
1670
1671 if let Some(attr) = attr {
1672 return (**attr.value()).into();
1673 }
1674 }
1675
1676 ns!()
1677 }
1678
1679 pub(crate) fn name_attribute(&self) -> Option<Atom> {
1680 self.rare_data().as_ref()?.name_attribute.clone()
1681 }
1682
1683 pub(crate) fn style_attribute(
1684 &self,
1685 ) -> &DomRefCell<Option<Arc<Locked<PropertyDeclarationBlock>>>> {
1686 &self.style_attribute
1687 }
1688
1689 pub(crate) fn summarize(&self) -> Vec<AttrInfo> {
1690 self.attrs
1691 .borrow()
1692 .iter()
1693 .map(|attr| attr.summarize())
1694 .collect()
1695 }
1696
1697 pub(crate) fn is_void(&self) -> bool {
1698 if self.namespace != ns!(html) {
1699 return false;
1700 }
1701 match self.local_name {
1702 local_name!("area") |
1705 local_name!("base") |
1706 local_name!("basefont") |
1707 local_name!("bgsound") |
1708 local_name!("br") |
1709 local_name!("col") |
1710 local_name!("embed") |
1711 local_name!("frame") |
1712 local_name!("hr") |
1713 local_name!("img") |
1714 local_name!("input") |
1715 local_name!("keygen") |
1716 local_name!("link") |
1717 local_name!("meta") |
1718 local_name!("param") |
1719 local_name!("source") |
1720 local_name!("track") |
1721 local_name!("wbr") => true,
1722 _ => false,
1723 }
1724 }
1725
1726 pub(crate) fn root_element(&self) -> DomRoot<Element> {
1727 if self.node.is_in_a_document_tree() {
1728 self.upcast::<Node>()
1729 .owner_doc()
1730 .GetDocumentElement()
1731 .unwrap()
1732 } else {
1733 self.upcast::<Node>()
1734 .inclusive_ancestors(ShadowIncluding::No)
1735 .filter_map(DomRoot::downcast)
1736 .last()
1737 .expect("We know inclusive_ancestors will return `self` which is an element")
1738 }
1739 }
1740
1741 pub(crate) fn lookup_prefix(&self, namespace: Namespace) -> Option<DOMString> {
1743 for node in self
1744 .upcast::<Node>()
1745 .inclusive_ancestors(ShadowIncluding::No)
1746 {
1747 let element = node.downcast::<Element>()?;
1748 if *element.namespace() == namespace {
1750 if let Some(prefix) = element.GetPrefix() {
1751 return Some(prefix);
1752 }
1753 }
1754
1755 for attr in element.attrs.borrow().iter() {
1757 if attr.prefix() == Some(&namespace_prefix!("xmlns")) &&
1758 **attr.value() == *namespace
1759 {
1760 return Some(attr.LocalName());
1761 }
1762 }
1763 }
1764 None
1765 }
1766
1767 pub(crate) fn is_document_element(&self) -> bool {
1769 if let Some(document_element) = self.owner_document().GetDocumentElement() {
1770 *document_element == *self
1771 } else {
1772 false
1773 }
1774 }
1775
1776 pub(crate) fn is_active_element(&self) -> bool {
1778 if let Some(active_element) = self.owner_document().GetActiveElement() {
1779 *active_element == *self
1780 } else {
1781 false
1782 }
1783 }
1784
1785 fn is_editing_host(&self) -> bool {
1786 self.downcast::<HTMLElement>()
1787 .is_some_and(|element| element.IsContentEditable())
1788 }
1789
1790 fn focusable_area_kind(&self) -> FocusableAreaKind {
1801 let node: &Node = self.upcast();
1803 if !node.is_connected() || !self.has_css_layout_box() || self.is_actually_disabled() {
1804 return Default::default();
1805 }
1806
1807 match self.explicitly_set_tab_index() {
1820 Some(tab_index) if tab_index < 0 => return FocusableAreaKind::Click,
1829 Some(_) => return FocusableAreaKind::Click | FocusableAreaKind::Sequential,
1830 None => {},
1831 }
1832
1833 let is_focusable_area_due_to_type = match node.type_id() {
1839 NodeTypeId::Element(ElementTypeId::HTMLElement(
1841 HTMLElementTypeId::HTMLAnchorElement,
1842 )) => self.has_attribute(&local_name!("href")),
1843
1844 NodeTypeId::Element(ElementTypeId::HTMLElement(
1852 HTMLElementTypeId::HTMLInputElement |
1853 HTMLElementTypeId::HTMLButtonElement |
1854 HTMLElementTypeId::HTMLSelectElement |
1855 HTMLElementTypeId::HTMLTextAreaElement |
1856 HTMLElementTypeId::HTMLIFrameElement,
1857 )) => true,
1858 _ => {
1859 self.downcast::<HTMLElement>()
1864 .is_some_and(|html_element| html_element.is_a_summary_for_its_parent_details()) ||
1865 self.is_editing_host() ||
1866 self.get_string_attribute(&local_name!("draggable")) == "true"
1867 },
1868 };
1869
1870 if is_focusable_area_due_to_type {
1871 return FocusableAreaKind::Click | FocusableAreaKind::Sequential;
1872 }
1873
1874 if self
1880 .upcast::<Node>()
1881 .effective_overflow()
1882 .is_some_and(|axes_overflow| {
1883 (matches!(axes_overflow.x, Overflow::Auto | Overflow::Scroll) &&
1886 self.ScrollWidth() > self.ClientWidth()) ||
1887 (matches!(axes_overflow.y, Overflow::Auto | Overflow::Scroll) &&
1888 self.ScrollHeight() > self.ClientHeight())
1889 })
1890 {
1891 return FocusableAreaKind::Sequential;
1892 }
1893
1894 Default::default()
1895 }
1896
1897 pub(crate) fn is_sequentially_focusable(&self) -> bool {
1899 self.focusable_area_kind()
1900 .contains(FocusableAreaKind::Sequential)
1901 }
1902
1903 pub(crate) fn is_click_focusable(&self) -> bool {
1905 self.focusable_area_kind()
1906 .contains(FocusableAreaKind::Click)
1907 }
1908
1909 pub(crate) fn is_focusable_area(&self) -> bool {
1911 !self.focusable_area_kind().is_empty()
1912 }
1913
1914 pub(crate) fn find_click_focusable_shadow_host_if_necessary(&self) -> Option<DomRoot<Element>> {
1919 if self.is_click_focusable() {
1920 return Some(DomRoot::from_ref(self));
1921 }
1922
1923 if self.upcast::<Node>().implemented_pseudo_element() ==
1924 Some(PseudoElement::ServoTextControlInnerEditor)
1925 {
1926 let containing_shadow_host = self
1928 .containing_shadow_root()
1929 .map(|root| root.Host())
1930 .expect("Text control inner shadow DOM should always have a shadow host.");
1931 if !containing_shadow_host.is_click_focusable() {
1932 return None;
1933 }
1934 return Some(containing_shadow_host);
1935 }
1936
1937 None
1938 }
1939
1940 pub(crate) fn is_actually_disabled(&self) -> bool {
1941 let node = self.upcast::<Node>();
1942 match node.type_id() {
1943 NodeTypeId::Element(ElementTypeId::HTMLElement(
1944 HTMLElementTypeId::HTMLButtonElement,
1945 )) |
1946 NodeTypeId::Element(ElementTypeId::HTMLElement(
1947 HTMLElementTypeId::HTMLInputElement,
1948 )) |
1949 NodeTypeId::Element(ElementTypeId::HTMLElement(
1950 HTMLElementTypeId::HTMLSelectElement,
1951 )) |
1952 NodeTypeId::Element(ElementTypeId::HTMLElement(
1953 HTMLElementTypeId::HTMLTextAreaElement,
1954 )) |
1955 NodeTypeId::Element(ElementTypeId::HTMLElement(
1956 HTMLElementTypeId::HTMLOptionElement,
1957 )) => self.disabled_state(),
1958 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
1959 self.downcast::<HTMLElement>()
1960 .unwrap()
1961 .is_form_associated_custom_element() &&
1962 self.disabled_state()
1963 },
1964 _ => false,
1969 }
1970 }
1971
1972 #[allow(clippy::too_many_arguments)]
1973 pub(crate) fn push_new_attribute(
1974 &self,
1975 local_name: LocalName,
1976 value: AttrValue,
1977 name: LocalName,
1978 namespace: Namespace,
1979 prefix: Option<Prefix>,
1980 reason: AttributeMutationReason,
1981 can_gc: CanGc,
1982 ) {
1983 let attr = Attr::new(
1984 &self.node.owner_doc(),
1985 local_name,
1986 value,
1987 name,
1988 namespace,
1989 prefix,
1990 Some(self),
1991 can_gc,
1992 );
1993 self.push_attribute(&attr, reason, can_gc);
1994 }
1995
1996 fn handle_attribute_changes(
1998 &self,
1999 attr: &Attr,
2000 old_value: Option<&AttrValue>,
2001 new_value: Option<DOMString>,
2002 reason: AttributeMutationReason,
2003 can_gc: CanGc,
2004 ) {
2005 let old_value_string = old_value.map(|old_value| DOMString::from(&**old_value));
2006 let name = attr.local_name().clone();
2009 let namespace = attr.namespace().clone();
2010 let mutation = LazyCell::new(|| Mutation::Attribute {
2011 name: name.clone(),
2012 namespace: namespace.clone(),
2013 old_value: old_value_string.clone(),
2014 });
2015 MutationObserver::queue_a_mutation_record(&self.node, mutation);
2016
2017 let has_new_value = new_value.is_some();
2019
2020 if self.is_custom() {
2023 let reaction = CallbackReaction::AttributeChanged(
2024 attr.local_name().clone(),
2025 old_value_string,
2026 new_value,
2027 attr.namespace().clone(),
2028 );
2029 ScriptThread::enqueue_callback_reaction(self, reaction, None);
2030 }
2031
2032 if is_relevant_attribute(attr.namespace(), attr.local_name()) {
2034 let attribute_mutation = if has_new_value {
2035 AttributeMutation::Set(old_value, reason)
2036 } else {
2037 AttributeMutation::Removed
2038 };
2039 vtable_for(self.upcast()).attribute_mutated(attr, attribute_mutation, can_gc);
2040 }
2041 }
2042
2043 pub(crate) fn change_attribute(&self, attr: &Attr, mut value: AttrValue, can_gc: CanGc) {
2045 let old_value = &attr.value().clone();
2049 self.will_mutate_attr(attr);
2051 attr.swap_value(&mut value);
2052 let new_value = DOMString::from(&**attr.value());
2056 self.handle_attribute_changes(
2057 attr,
2058 Some(old_value),
2059 Some(new_value),
2060 AttributeMutationReason::Directly,
2061 can_gc,
2062 );
2063 }
2064
2065 pub(crate) fn push_attribute(
2067 &self,
2068 attr: &Attr,
2069 reason: AttributeMutationReason,
2070 can_gc: CanGc,
2071 ) {
2072 assert!(attr.GetOwnerElement().as_deref() == Some(self));
2076 assert!(attr.upcast::<Node>().owner_doc() == self.node.owner_doc());
2080 self.will_mutate_attr(attr);
2082 self.attrs.borrow_mut().push(Dom::from_ref(attr));
2083 let new_value = DOMString::from(&**attr.value());
2087 self.handle_attribute_changes(attr, None, Some(new_value), reason, can_gc);
2088 }
2089
2090 pub(crate) fn get_attribute_with_namespace(
2096 &self,
2097 namespace: &Namespace,
2098 local_name: &LocalName,
2099 ) -> Option<DomRoot<Attr>> {
2100 self.attrs
2101 .borrow()
2102 .iter()
2103 .find(|attr| attr.local_name() == local_name && attr.namespace() == namespace)
2104 .map(|js| DomRoot::from_ref(&**js))
2105 }
2106
2107 pub(crate) fn get_attribute(&self, local_name: &LocalName) -> Option<DomRoot<Attr>> {
2112 debug_assert_eq!(
2113 *local_name,
2114 local_name.to_ascii_lowercase(),
2115 "All namespace-less attribute accesses should use a lowercase ASCII name"
2116 );
2117 self.get_attribute_with_namespace(&ns!(), local_name)
2118 }
2119
2120 pub(crate) fn get_attribute_by_name(&self, name: DOMString) -> Option<DomRoot<Attr>> {
2122 let name = &self.parsed_name(name);
2123 let maybe_attribute = self
2124 .attrs
2125 .borrow()
2126 .iter()
2127 .find(|a| a.name() == name)
2128 .map(|js| DomRoot::from_ref(&**js));
2129 fn id_and_name_must_be_atoms(name: &LocalName, maybe_attr: &Option<DomRoot<Attr>>) -> bool {
2130 if *name == local_name!("id") || *name == local_name!("name") {
2131 match maybe_attr {
2132 None => true,
2133 Some(attr) => matches!(*attr.value(), AttrValue::Atom(_)),
2134 }
2135 } else {
2136 true
2137 }
2138 }
2139 debug_assert!(id_and_name_must_be_atoms(name, &maybe_attribute));
2140 maybe_attribute
2141 }
2142
2143 pub(crate) fn set_attribute_from_parser(
2144 &self,
2145 qname: QualName,
2146 value: DOMString,
2147 prefix: Option<Prefix>,
2148 can_gc: CanGc,
2149 ) {
2150 if self
2152 .attrs
2153 .borrow()
2154 .iter()
2155 .any(|a| *a.local_name() == qname.local && *a.namespace() == qname.ns)
2156 {
2157 return;
2158 }
2159
2160 let name = match prefix {
2161 None => qname.local.clone(),
2162 Some(ref prefix) => {
2163 let name = format!("{}:{}", &**prefix, &*qname.local);
2164 LocalName::from(name)
2165 },
2166 };
2167 let value = self.parse_attribute(&qname.ns, &qname.local, value);
2168 self.push_new_attribute(
2169 qname.local,
2170 value,
2171 name,
2172 qname.ns,
2173 prefix,
2174 AttributeMutationReason::ByParser,
2175 can_gc,
2176 );
2177 }
2178
2179 pub(crate) fn set_attribute(&self, name: &LocalName, value: AttrValue, can_gc: CanGc) {
2180 debug_assert_eq!(
2181 *name,
2182 name.to_ascii_lowercase(),
2183 "All attribute accesses should use a lowercase ASCII name"
2184 );
2185 debug_assert!(!name.contains(':'));
2186
2187 self.set_first_matching_attribute(
2188 name.clone(),
2189 value,
2190 name.clone(),
2191 ns!(),
2192 None,
2193 |attr| attr.local_name() == name,
2194 can_gc,
2195 );
2196 }
2197
2198 pub(crate) fn set_attribute_with_namespace(
2199 &self,
2200 cx: &mut js::context::JSContext,
2201 local_name: LocalName,
2202 value: AttrValue,
2203 name: LocalName,
2204 namespace: Namespace,
2205 prefix: Option<Prefix>,
2206 ) {
2207 self.set_first_matching_attribute(
2208 local_name.clone(),
2209 value,
2210 name,
2211 namespace.clone(),
2212 prefix,
2213 |attr| *attr.local_name() == local_name && *attr.namespace() == namespace,
2214 CanGc::from_cx(cx),
2215 );
2216 }
2217
2218 #[allow(clippy::too_many_arguments)]
2220 fn set_first_matching_attribute<F>(
2221 &self,
2222 local_name: LocalName,
2223 value: AttrValue,
2224 name: LocalName,
2225 namespace: Namespace,
2226 prefix: Option<Prefix>,
2227 find: F,
2228 can_gc: CanGc,
2229 ) where
2230 F: Fn(&Attr) -> bool,
2231 {
2232 let attr = self
2234 .attrs
2235 .borrow()
2236 .iter()
2237 .find(|attr| find(attr))
2238 .map(|js| DomRoot::from_ref(&**js));
2239 if let Some(attr) = attr {
2240 self.will_mutate_attr(&attr);
2242 self.change_attribute(&attr, value, can_gc);
2243 } else {
2244 self.push_new_attribute(
2249 local_name,
2250 value,
2251 name,
2252 namespace,
2253 prefix,
2254 AttributeMutationReason::Directly,
2255 can_gc,
2256 );
2257 };
2258 }
2259
2260 pub(crate) fn parse_attribute(
2261 &self,
2262 namespace: &Namespace,
2263 local_name: &LocalName,
2264 value: DOMString,
2265 ) -> AttrValue {
2266 if is_relevant_attribute(namespace, local_name) {
2267 vtable_for(self.upcast()).parse_plain_attribute(local_name, value)
2268 } else {
2269 AttrValue::String(value.into())
2270 }
2271 }
2272
2273 pub(crate) fn remove_attribute(
2274 &self,
2275 namespace: &Namespace,
2276 local_name: &LocalName,
2277 can_gc: CanGc,
2278 ) -> Option<DomRoot<Attr>> {
2279 self.remove_first_matching_attribute(
2280 |attr| attr.namespace() == namespace && attr.local_name() == local_name,
2281 can_gc,
2282 )
2283 }
2284
2285 pub(crate) fn remove_attribute_by_name(
2286 &self,
2287 name: &LocalName,
2288 can_gc: CanGc,
2289 ) -> Option<DomRoot<Attr>> {
2290 self.remove_first_matching_attribute(|attr| attr.name() == name, can_gc)
2291 }
2292
2293 fn remove_first_matching_attribute<F>(&self, find: F, can_gc: CanGc) -> Option<DomRoot<Attr>>
2295 where
2296 F: Fn(&Attr) -> bool,
2297 {
2298 let idx = self.attrs.borrow().iter().position(|attr| find(attr));
2299 idx.map(|idx| {
2300 let attr = DomRoot::from_ref(&*(*self.attrs.borrow())[idx]);
2301
2302 self.will_mutate_attr(&attr);
2304 self.attrs.borrow_mut().remove(idx);
2305 attr.set_owner(None);
2307 self.handle_attribute_changes(
2309 &attr,
2310 Some(&attr.value()),
2311 None,
2312 AttributeMutationReason::Directly,
2313 can_gc,
2314 );
2315
2316 attr
2317 })
2318 }
2319
2320 pub(crate) fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2321 self.get_attribute(&local_name!("class"))
2322 .is_some_and(|attr| {
2323 attr.value()
2324 .as_tokens()
2325 .iter()
2326 .any(|atom| case_sensitivity.eq_atom(name, atom))
2327 })
2328 }
2329
2330 pub(crate) fn is_part(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2331 self.get_attribute(&LocalName::from("part"))
2332 .is_some_and(|attr| {
2333 attr.value()
2334 .as_tokens()
2335 .iter()
2336 .any(|atom| case_sensitivity.eq_atom(name, atom))
2337 })
2338 }
2339
2340 pub(crate) fn set_atomic_attribute(
2341 &self,
2342 local_name: &LocalName,
2343 value: DOMString,
2344 can_gc: CanGc,
2345 ) {
2346 self.set_attribute(local_name, AttrValue::from_atomic(value.into()), can_gc);
2347 }
2348
2349 pub(crate) fn has_attribute(&self, local_name: &LocalName) -> bool {
2350 debug_assert_eq!(
2351 *local_name,
2352 local_name.to_ascii_lowercase(),
2353 "All attribute accesses should use a lowercase ASCII name"
2354 );
2355 debug_assert!(!local_name.contains(':'));
2356 self.attrs
2357 .borrow()
2358 .iter()
2359 .any(|attr| attr.local_name() == local_name && attr.namespace() == &ns!())
2360 }
2361
2362 pub(crate) fn set_bool_attribute(&self, local_name: &LocalName, value: bool, can_gc: CanGc) {
2363 if self.has_attribute(local_name) == value {
2364 return;
2365 }
2366 if value {
2367 self.set_string_attribute(local_name, DOMString::new(), can_gc);
2368 } else {
2369 self.remove_attribute(&ns!(), local_name, can_gc);
2370 }
2371 }
2372
2373 pub(crate) fn get_url_attribute(&self, local_name: &LocalName) -> USVString {
2374 let Some(attribute) = self.get_attribute(local_name) else {
2375 return Default::default();
2376 };
2377 let value = &**attribute.value();
2378 self.owner_document()
2380 .base_url()
2381 .join(value)
2382 .map(|parsed| USVString(parsed.into_string()))
2383 .unwrap_or_else(|_| USVString(value.to_owned()))
2384 }
2385
2386 pub(crate) fn set_url_attribute(
2387 &self,
2388 local_name: &LocalName,
2389 value: USVString,
2390 can_gc: CanGc,
2391 ) {
2392 self.set_attribute(local_name, AttrValue::String(value.to_string()), can_gc);
2393 }
2394
2395 pub(crate) fn get_trusted_type_url_attribute(
2396 &self,
2397 local_name: &LocalName,
2398 ) -> TrustedScriptURLOrUSVString {
2399 let Some(attribute) = self.get_attribute(local_name) else {
2400 return TrustedScriptURLOrUSVString::USVString(USVString::default());
2401 };
2402 let value = &**attribute.value();
2403 self.owner_document()
2405 .base_url()
2406 .join(value)
2407 .map(|parsed| TrustedScriptURLOrUSVString::USVString(USVString(parsed.into_string())))
2408 .unwrap_or_else(|_| TrustedScriptURLOrUSVString::USVString(USVString(value.to_owned())))
2409 }
2410
2411 pub(crate) fn get_trusted_html_attribute(&self, local_name: &LocalName) -> TrustedHTMLOrString {
2412 TrustedHTMLOrString::String(self.get_string_attribute(local_name))
2413 }
2414
2415 pub(crate) fn get_string_attribute(&self, local_name: &LocalName) -> DOMString {
2416 self.get_attribute(local_name)
2417 .map(|attribute| attribute.Value())
2418 .unwrap_or_default()
2419 }
2420
2421 pub(crate) fn set_string_attribute(
2422 &self,
2423 local_name: &LocalName,
2424 value: DOMString,
2425 can_gc: CanGc,
2426 ) {
2427 self.set_attribute(local_name, AttrValue::String(value.into()), can_gc);
2428 }
2429
2430 fn get_nullable_string_attribute(&self, local_name: &LocalName) -> Option<DOMString> {
2433 if self.has_attribute(local_name) {
2434 Some(self.get_string_attribute(local_name))
2435 } else {
2436 None
2437 }
2438 }
2439
2440 fn set_nullable_string_attribute(
2443 &self,
2444 cx: &mut JSContext,
2445 local_name: &LocalName,
2446 value: Option<DOMString>,
2447 ) {
2448 match value {
2449 Some(val) => {
2450 self.set_string_attribute(local_name, val, CanGc::from_cx(cx));
2451 },
2452 None => {
2453 self.remove_attribute(&ns!(), local_name, CanGc::from_cx(cx));
2454 },
2455 }
2456 }
2457
2458 pub(crate) fn get_tokenlist_attribute(&self, local_name: &LocalName) -> Vec<Atom> {
2459 self.get_attribute(local_name)
2460 .map(|attribute| attribute.value().as_tokens().to_vec())
2461 .unwrap_or_default()
2462 }
2463
2464 pub(crate) fn set_tokenlist_attribute(
2465 &self,
2466 local_name: &LocalName,
2467 value: DOMString,
2468 can_gc: CanGc,
2469 ) {
2470 self.set_attribute(
2471 local_name,
2472 AttrValue::from_serialized_tokenlist(value.into()),
2473 can_gc,
2474 );
2475 }
2476
2477 pub(crate) fn set_atomic_tokenlist_attribute(
2478 &self,
2479 local_name: &LocalName,
2480 tokens: Vec<Atom>,
2481 can_gc: CanGc,
2482 ) {
2483 self.set_attribute(local_name, AttrValue::from_atomic_tokens(tokens), can_gc);
2484 }
2485
2486 pub(crate) fn get_int_attribute(&self, local_name: &LocalName, default: i32) -> i32 {
2487 match self.get_attribute(local_name) {
2488 Some(ref attribute) => match *attribute.value() {
2489 AttrValue::Int(_, value) => value,
2490 _ => unreachable!("Expected an AttrValue::Int: implement parse_plain_attribute"),
2491 },
2492 None => default,
2493 }
2494 }
2495
2496 pub(crate) fn set_int_attribute(&self, local_name: &LocalName, value: i32, can_gc: CanGc) {
2497 self.set_attribute(local_name, AttrValue::Int(value.to_string(), value), can_gc);
2498 }
2499
2500 pub(crate) fn get_uint_attribute(&self, local_name: &LocalName, default: u32) -> u32 {
2501 match self.get_attribute(local_name) {
2502 Some(ref attribute) => match *attribute.value() {
2503 AttrValue::UInt(_, value) => value,
2504 _ => unreachable!("Expected an AttrValue::UInt: implement parse_plain_attribute"),
2505 },
2506 None => default,
2507 }
2508 }
2509 pub(crate) fn set_uint_attribute(&self, local_name: &LocalName, value: u32, can_gc: CanGc) {
2510 self.set_attribute(
2511 local_name,
2512 AttrValue::UInt(value.to_string(), value),
2513 can_gc,
2514 );
2515 }
2516
2517 pub(crate) fn will_mutate_attr(&self, attr: &Attr) {
2518 let node = self.upcast::<Node>();
2519 node.owner_doc().element_attr_will_change(self, attr);
2520 }
2521
2522 fn update_style_attribute(&self, attr: &Attr, mutation: AttributeMutation) {
2524 let doc = self.upcast::<Node>().owner_doc();
2525 *self.style_attribute.borrow_mut() = match mutation {
2527 AttributeMutation::Set(..) => {
2528 let is_declaration = matches!(*attr.value(), AttrValue::Declaration(..));
2534
2535 let block = if is_declaration {
2536 let mut value = AttrValue::String(String::new());
2537 attr.swap_value(&mut value);
2538 let (serialization, block) = match value {
2539 AttrValue::Declaration(s, b) => (s, b),
2540 _ => unreachable!(),
2541 };
2542 let mut value = AttrValue::String(serialization);
2543 attr.swap_value(&mut value);
2544 block
2545 } else {
2546 let win = self.owner_window();
2547 let source = &**attr.value();
2548 let global = &self.owner_global();
2549 if global
2554 .get_csp_list()
2555 .should_elements_inline_type_behavior_be_blocked(
2556 global,
2557 self,
2558 InlineCheckType::StyleAttribute,
2559 source,
2560 doc.get_current_parser_line(),
2561 )
2562 {
2563 return;
2564 }
2565 Arc::new(doc.style_shared_lock().wrap(parse_style_attribute(
2566 source,
2567 &UrlExtraData(doc.base_url().get_arc()),
2568 Some(win.css_error_reporter()),
2569 doc.quirks_mode(),
2570 CssRuleType::Style,
2571 )))
2572 };
2573
2574 Some(block)
2575 },
2576 AttributeMutation::Removed => None,
2577 };
2578 }
2579
2580 fn set_attribute_node(
2584 &self,
2585 cx: &mut js::context::JSContext,
2586 attr: &Attr,
2587 ) -> Fallible<Option<DomRoot<Attr>>> {
2588 let verified_value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
2592 self.namespace(),
2593 self.local_name(),
2594 attr.local_name(),
2595 Some(attr.namespace()),
2596 TrustedTypeOrString::String(attr.Value()),
2597 &self.owner_global(),
2598 CanGc::from_cx(cx),
2599 )?;
2600
2601 if let Some(owner) = attr.GetOwnerElement() {
2604 if &*owner != self {
2605 return Err(Error::InUseAttribute(None));
2606 }
2607 }
2608
2609 let vtable = vtable_for(self.upcast());
2610
2611 attr.swap_value(
2617 &mut vtable.parse_plain_attribute(attr.local_name(), verified_value.clone()),
2618 );
2619
2620 let position = self.attrs.borrow().iter().position(|old_attr| {
2622 attr.namespace() == old_attr.namespace() && attr.local_name() == old_attr.local_name()
2623 });
2624
2625 let old_attr = if let Some(position) = position {
2626 let old_attr = DomRoot::from_ref(&*self.attrs.borrow()[position]);
2627
2628 if &*old_attr == attr {
2630 return Ok(Some(DomRoot::from_ref(attr)));
2631 }
2632
2633 self.will_mutate_attr(attr);
2643 self.attrs.borrow_mut()[position] = Dom::from_ref(attr);
2644 attr.set_owner(Some(self));
2646 attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2648 old_attr.set_owner(None);
2650 self.handle_attribute_changes(
2652 attr,
2653 Some(&old_attr.value()),
2654 Some(verified_value),
2655 AttributeMutationReason::Directly,
2656 CanGc::from_cx(cx),
2657 );
2658
2659 Some(old_attr)
2660 } else {
2661 attr.set_owner(Some(self));
2663 attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2664 self.push_attribute(attr, AttributeMutationReason::Directly, CanGc::from_cx(cx));
2665
2666 None
2667 };
2668
2669 Ok(old_attr)
2671 }
2672
2673 pub(crate) fn update_nonce_internal_slot(&self, nonce: String) {
2675 self.ensure_rare_data().cryptographic_nonce = nonce;
2676 }
2677
2678 pub(crate) fn nonce_value(&self) -> String {
2680 match self.rare_data().as_ref() {
2681 None => String::new(),
2682 Some(rare_data) => rare_data.cryptographic_nonce.clone(),
2683 }
2684 }
2685
2686 pub(crate) fn update_nonce_post_connection(&self) {
2688 if !self.upcast::<Node>().is_connected_with_browsing_context() {
2691 return;
2692 }
2693 let global = self.owner_global();
2694 let csp_list = match global.get_csp_list() {
2696 None => return,
2697 Some(csp_list) => csp_list,
2698 };
2699 if !csp_list.contains_a_header_delivered_content_security_policy() ||
2702 self.get_string_attribute(&local_name!("nonce")).is_empty()
2703 {
2704 return;
2705 }
2706 let nonce = self.nonce_value();
2708 self.set_string_attribute(&local_name!("nonce"), "".into(), CanGc::note());
2710 self.update_nonce_internal_slot(nonce);
2712 }
2713
2714 pub(crate) fn is_nonceable(&self) -> bool {
2716 if !self.has_attribute(&local_name!("nonce")) {
2718 return false;
2719 }
2720 if self.downcast::<HTMLScriptElement>().is_some() {
2722 for attr in self.attrs().iter() {
2723 let attr_name = attr.name().to_ascii_lowercase();
2726 if attr_name.contains("<script") || attr_name.contains("<style") {
2727 return false;
2728 }
2729 let attr_value = attr.value().to_ascii_lowercase();
2732 if attr_value.contains("<script") || attr_value.contains("<style") {
2733 return false;
2734 }
2735 }
2736 }
2737 true
2742 }
2743
2744 pub(crate) fn insert_adjacent(
2746 &self,
2747 where_: AdjacentPosition,
2748 node: &Node,
2749 can_gc: CanGc,
2750 ) -> Fallible<Option<DomRoot<Node>>> {
2751 let self_node = self.upcast::<Node>();
2752 match where_ {
2753 AdjacentPosition::BeforeBegin => {
2754 if let Some(parent) = self_node.GetParentNode() {
2755 Node::pre_insert(node, &parent, Some(self_node), can_gc).map(Some)
2756 } else {
2757 Ok(None)
2758 }
2759 },
2760 AdjacentPosition::AfterBegin => Node::pre_insert(
2761 node,
2762 self_node,
2763 self_node.GetFirstChild().as_deref(),
2764 can_gc,
2765 )
2766 .map(Some),
2767 AdjacentPosition::BeforeEnd => {
2768 Node::pre_insert(node, self_node, None, can_gc).map(Some)
2769 },
2770 AdjacentPosition::AfterEnd => {
2771 if let Some(parent) = self_node.GetParentNode() {
2772 Node::pre_insert(node, &parent, self_node.GetNextSibling().as_deref(), can_gc)
2773 .map(Some)
2774 } else {
2775 Ok(None)
2776 }
2777 },
2778 }
2779 }
2780
2781 pub(crate) fn scroll(&self, x: f64, y: f64, behavior: ScrollBehavior) {
2786 let x = if x.is_finite() { x } else { 0.0 } as f32;
2788 let y = if y.is_finite() { y } else { 0.0 } as f32;
2789
2790 let node = self.upcast::<Node>();
2791
2792 let doc = node.owner_doc();
2794
2795 if !doc.is_fully_active() {
2797 return;
2798 }
2799
2800 let win = match doc.GetDefaultView() {
2802 None => return,
2803 Some(win) => win,
2804 };
2805
2806 if *self.root_element() == *self {
2808 if doc.quirks_mode() != QuirksMode::Quirks {
2809 win.scroll(x, y, behavior);
2810 }
2811
2812 return;
2813 }
2814
2815 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
2817 doc.quirks_mode() == QuirksMode::Quirks &&
2818 !self.is_potentially_scrollable_body()
2819 {
2820 win.scroll(x, y, behavior);
2821 return;
2822 }
2823
2824 if !self.has_scrolling_box() {
2826 return;
2827 }
2828
2829 win.scroll_an_element(self, x, y, behavior);
2831 }
2832
2833 pub(crate) fn parse_fragment(
2835 &self,
2836 markup: DOMString,
2837 cx: &mut js::context::JSContext,
2838 ) -> Fallible<DomRoot<DocumentFragment>> {
2839 let new_children = ServoParser::parse_html_fragment(self, markup, false, cx);
2842 let context_document = {
2845 if let Some(template) = self.downcast::<HTMLTemplateElement>() {
2846 template
2847 .Content(CanGc::from_cx(cx))
2848 .upcast::<Node>()
2849 .owner_doc()
2850 } else {
2851 self.owner_document()
2852 }
2853 };
2854 let fragment = DocumentFragment::new(&context_document, CanGc::from_cx(cx));
2855 for child in new_children {
2857 fragment
2858 .upcast::<Node>()
2859 .AppendChild(&child, CanGc::from_cx(cx))
2860 .unwrap();
2861 }
2862 Ok(fragment)
2864 }
2865
2866 pub(crate) fn fragment_parsing_context(
2869 cx: &mut JSContext,
2870 owner_doc: &Document,
2871 element: Option<&Self>,
2872 ) -> DomRoot<Self> {
2873 match element {
2875 Some(elem)
2876 if elem.local_name() != &local_name!("html") ||
2880 !elem.html_element_in_html_document() =>
2881 {
2882 DomRoot::from_ref(elem)
2883 },
2884 _ => Element::create(
2887 QualName::new(None, ns!(html), local_name!("body")),
2888 None,
2889 owner_doc,
2890 ElementCreator::ScriptCreated,
2891 CustomElementCreationMode::Asynchronous,
2892 None,
2893 CanGc::from_cx(cx),
2894 ),
2895 }
2896 }
2897
2898 pub(crate) fn fullscreen_element_ready_check(&self) -> bool {
2900 if !self.is_connected() {
2901 return false;
2902 }
2903 self.owner_document().get_allow_fullscreen()
2904 }
2905
2906 pub(crate) fn is_in_same_home_subtree<T>(&self, other: &T) -> bool
2908 where
2909 T: DerivedFrom<Element> + DomObject,
2910 {
2911 let other = other.upcast::<Element>();
2912 self.root_element() == other.root_element()
2913 }
2914
2915 pub(crate) fn get_id(&self) -> Option<Atom> {
2916 self.id_attribute.borrow().clone()
2917 }
2918
2919 pub(crate) fn get_name(&self) -> Option<Atom> {
2920 self.rare_data().as_ref()?.name_attribute.clone()
2921 }
2922
2923 pub(crate) fn get_element_internals(&self) -> Option<DomRoot<ElementInternals>> {
2924 self.rare_data()
2925 .as_ref()?
2926 .element_internals
2927 .as_ref()
2928 .map(|sr| DomRoot::from_ref(&**sr))
2929 }
2930
2931 pub(crate) fn ensure_element_internals(&self, can_gc: CanGc) -> DomRoot<ElementInternals> {
2932 let mut rare_data = self.ensure_rare_data();
2933 DomRoot::from_ref(rare_data.element_internals.get_or_insert_with(|| {
2934 let elem = self
2935 .downcast::<HTMLElement>()
2936 .expect("ensure_element_internals should only be called for an HTMLElement");
2937 Dom::from_ref(&*ElementInternals::new(elem, can_gc))
2938 }))
2939 }
2940
2941 pub(crate) fn outer_html(&self, can_gc: CanGc) -> Fallible<DOMString> {
2942 match self.GetOuterHTML(can_gc)? {
2943 TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => Ok(str),
2944 TrustedHTMLOrNullIsEmptyString::TrustedHTML(_) => unreachable!(),
2945 }
2946 }
2947
2948 pub(crate) fn compute_source_position(&self, line_number: u32) -> SourcePosition {
2949 SourcePosition {
2950 source_file: self.owner_global().get_url().to_string(),
2951 line_number: line_number + 2,
2952 column_number: 0,
2953 }
2954 }
2955
2956 pub(crate) fn explicitly_set_tab_index(&self) -> Option<i32> {
2957 if self.has_attribute(&local_name!("tabindex")) {
2958 Some(self.get_int_attribute(&local_name!("tabindex"), 0))
2959 } else {
2960 None
2961 }
2962 }
2963
2964 pub(crate) fn tab_index(&self) -> i32 {
2966 if let Some(tab_index) = self.explicitly_set_tab_index() {
2972 return tab_index;
2973 }
2974
2975 if matches!(
2981 self.upcast::<Node>().type_id(),
2982 NodeTypeId::Element(ElementTypeId::HTMLElement(
2983 HTMLElementTypeId::HTMLAnchorElement |
2984 HTMLElementTypeId::HTMLAreaElement |
2985 HTMLElementTypeId::HTMLButtonElement |
2986 HTMLElementTypeId::HTMLFrameElement |
2987 HTMLElementTypeId::HTMLIFrameElement |
2988 HTMLElementTypeId::HTMLInputElement |
2989 HTMLElementTypeId::HTMLObjectElement |
2990 HTMLElementTypeId::HTMLSelectElement |
2991 HTMLElementTypeId::HTMLTextAreaElement
2992 ))
2993 ) {
2994 return 0;
2995 }
2996 if self
2997 .downcast::<HTMLElement>()
2998 .is_some_and(|html_element| html_element.is_a_summary_for_its_parent_details())
2999 {
3000 return 0;
3001 }
3002
3003 -1
3004 }
3005
3006 #[inline]
3007 fn insert_selector_flags(&self, flags: ElementSelectorFlags) {
3008 self.selector_flags
3009 .fetch_or(flags.bits(), Ordering::Relaxed);
3010 }
3011
3012 #[inline]
3013 fn get_selector_flags(&self) -> ElementSelectorFlags {
3014 ElementSelectorFlags::from_bits_retain(self.selector_flags.load(Ordering::Relaxed))
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.borrow().as_ref().map(|p| DOMString::from(&**p))
3033 }
3034
3035 fn TagName(&self) -> DOMString {
3037 let name = self.tag_name.or_init(|| {
3038 let qualified_name = match *self.prefix.borrow() {
3039 Some(ref prefix) => Cow::Owned(format!("{}:{}", &**prefix, &*self.local_name)),
3040 None => Cow::Borrowed(&*self.local_name),
3041 };
3042 if self.html_element_in_html_document() {
3043 LocalName::from(qualified_name.to_ascii_uppercase())
3044 } else {
3045 LocalName::from(qualified_name)
3046 }
3047 });
3048 DOMString::from(&*name)
3049 }
3050
3051 fn Id(&self) -> DOMString {
3055 self.get_string_attribute(&local_name!("id"))
3056 }
3057
3058 fn SetId(&self, id: DOMString, can_gc: CanGc) {
3060 self.set_atomic_attribute(&local_name!("id"), id, can_gc);
3061 }
3062
3063 fn ClassName(&self) -> DOMString {
3065 self.get_string_attribute(&local_name!("class"))
3066 }
3067
3068 fn SetClassName(&self, class: DOMString, can_gc: CanGc) {
3070 self.set_tokenlist_attribute(&local_name!("class"), class, can_gc);
3071 }
3072
3073 fn ClassList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
3075 self.class_list
3076 .or_init(|| DOMTokenList::new(self, &local_name!("class"), None, can_gc))
3077 }
3078
3079 make_getter!(Slot, "slot");
3081
3082 make_setter!(SetSlot, "slot");
3084
3085 fn Attributes(&self, can_gc: CanGc) -> DomRoot<NamedNodeMap> {
3087 self.attr_list
3088 .or_init(|| NamedNodeMap::new(&self.owner_window(), self, can_gc))
3089 }
3090
3091 fn HasAttributes(&self) -> bool {
3093 !self.attrs.borrow().is_empty()
3094 }
3095
3096 fn GetAttributeNames(&self) -> Vec<DOMString> {
3098 self.attrs.borrow().iter().map(|attr| attr.Name()).collect()
3099 }
3100
3101 fn GetAttribute(&self, name: DOMString) -> Option<DOMString> {
3103 self.GetAttributeNode(name).map(|s| s.Value())
3104 }
3105
3106 fn GetAttributeNS(
3108 &self,
3109 namespace: Option<DOMString>,
3110 local_name: DOMString,
3111 ) -> Option<DOMString> {
3112 self.GetAttributeNodeNS(namespace, local_name)
3113 .map(|attr| attr.Value())
3114 }
3115
3116 fn GetAttributeNode(&self, name: DOMString) -> Option<DomRoot<Attr>> {
3118 self.get_attribute_by_name(name)
3119 }
3120
3121 fn GetAttributeNodeNS(
3123 &self,
3124 namespace: Option<DOMString>,
3125 local_name: DOMString,
3126 ) -> Option<DomRoot<Attr>> {
3127 let namespace = &namespace_from_domstring(namespace);
3128 self.get_attribute_with_namespace(namespace, &LocalName::from(local_name))
3129 }
3130
3131 fn ToggleAttribute(
3133 &self,
3134 cx: &mut js::context::JSContext,
3135 name: DOMString,
3136 force: Option<bool>,
3137 ) -> Fallible<bool> {
3138 if !is_valid_attribute_local_name(&name.str()) {
3141 return Err(Error::InvalidCharacter(None));
3142 }
3143
3144 let attribute = self.GetAttribute(name.clone());
3146
3147 let name = self.parsed_name(name);
3149 match attribute {
3150 None => match force {
3152 None | Some(true) => {
3154 self.set_first_matching_attribute(
3155 name.clone(),
3156 AttrValue::String(String::new()),
3157 name.clone(),
3158 ns!(),
3159 None,
3160 |attr| *attr.name() == name,
3161 CanGc::from_cx(cx),
3162 );
3163 Ok(true)
3164 },
3165 Some(false) => Ok(false),
3167 },
3168 Some(_index) => match force {
3169 None | Some(false) => {
3171 self.remove_attribute_by_name(&name, CanGc::from_cx(cx));
3172 Ok(false)
3173 },
3174 Some(true) => Ok(true),
3176 },
3177 }
3178 }
3179
3180 fn SetAttribute(
3182 &self,
3183 cx: &mut js::context::JSContext,
3184 name: DOMString,
3185 value: TrustedTypeOrString,
3186 ) -> ErrorResult {
3187 if !is_valid_attribute_local_name(&name.str()) {
3190 return Err(Error::InvalidCharacter(None));
3191 }
3192
3193 let name = self.parsed_name(name);
3196
3197 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3201 self.namespace(),
3202 self.local_name(),
3203 &name,
3204 None,
3205 value,
3206 &self.owner_global(),
3207 CanGc::from_cx(cx),
3208 )?;
3209
3210 let value = self.parse_attribute(&ns!(), &name, value);
3215 self.set_first_matching_attribute(
3216 name.clone(),
3217 value,
3218 name.clone(),
3219 ns!(),
3220 None,
3221 |attr| *attr.name() == name,
3222 CanGc::from_cx(cx),
3223 );
3224 Ok(())
3225 }
3226
3227 fn SetAttributeNS(
3229 &self,
3230 cx: &mut js::context::JSContext,
3231 namespace: Option<DOMString>,
3232 qualified_name: DOMString,
3233 value: TrustedTypeOrString,
3234 ) -> ErrorResult {
3235 let (namespace, prefix, local_name) =
3237 domname::validate_and_extract(namespace, &qualified_name, domname::Context::Element)?;
3238 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3241 self.namespace(),
3242 self.local_name(),
3243 &local_name,
3244 Some(&namespace),
3245 value,
3246 &self.owner_global(),
3247 CanGc::from_cx(cx),
3248 )?;
3249 let value = self.parse_attribute(&namespace, &local_name, value);
3251 self.set_attribute_with_namespace(
3252 cx,
3253 local_name,
3254 value,
3255 LocalName::from(qualified_name),
3256 namespace,
3257 prefix,
3258 );
3259 Ok(())
3260 }
3261
3262 fn SetAttributeNode(
3264 &self,
3265 cx: &mut js::context::JSContext,
3266 attr: &Attr,
3267 ) -> Fallible<Option<DomRoot<Attr>>> {
3268 self.set_attribute_node(cx, attr)
3269 }
3270
3271 fn SetAttributeNodeNS(
3273 &self,
3274 cx: &mut js::context::JSContext,
3275 attr: &Attr,
3276 ) -> Fallible<Option<DomRoot<Attr>>> {
3277 self.set_attribute_node(cx, attr)
3278 }
3279
3280 fn RemoveAttribute(&self, name: DOMString, can_gc: CanGc) {
3282 let name = self.parsed_name(name);
3283 self.remove_attribute_by_name(&name, can_gc);
3284 }
3285
3286 fn RemoveAttributeNS(
3288 &self,
3289 cx: &mut js::context::JSContext,
3290 namespace: Option<DOMString>,
3291 local_name: DOMString,
3292 ) {
3293 let namespace = namespace_from_domstring(namespace);
3294 let local_name = LocalName::from(local_name);
3295 self.remove_attribute(&namespace, &local_name, CanGc::from_cx(cx));
3296 }
3297
3298 fn RemoveAttributeNode(
3300 &self,
3301 cx: &mut js::context::JSContext,
3302 attr: &Attr,
3303 ) -> Fallible<DomRoot<Attr>> {
3304 self.remove_first_matching_attribute(|a| a == attr, CanGc::from_cx(cx))
3305 .ok_or(Error::NotFound(None))
3306 }
3307
3308 fn HasAttribute(&self, name: DOMString) -> bool {
3310 self.GetAttribute(name).is_some()
3311 }
3312
3313 fn HasAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> bool {
3315 self.GetAttributeNS(namespace, local_name).is_some()
3316 }
3317
3318 fn GetElementsByTagName(&self, localname: DOMString, can_gc: CanGc) -> DomRoot<HTMLCollection> {
3320 let window = self.owner_window();
3321 HTMLCollection::by_qualified_name(
3322 &window,
3323 self.upcast(),
3324 LocalName::from(localname),
3325 can_gc,
3326 )
3327 }
3328
3329 fn GetElementsByTagNameNS(
3331 &self,
3332 maybe_ns: Option<DOMString>,
3333 localname: DOMString,
3334 can_gc: CanGc,
3335 ) -> DomRoot<HTMLCollection> {
3336 let window = self.owner_window();
3337 HTMLCollection::by_tag_name_ns(&window, self.upcast(), localname, maybe_ns, can_gc)
3338 }
3339
3340 fn GetElementsByClassName(&self, classes: DOMString, can_gc: CanGc) -> DomRoot<HTMLCollection> {
3342 let window = self.owner_window();
3343 HTMLCollection::by_class_name(&window, self.upcast(), classes, can_gc)
3344 }
3345
3346 fn GetClientRects(&self, can_gc: CanGc) -> DomRoot<DOMRectList> {
3348 let win = self.owner_window();
3349 let raw_rects = self.upcast::<Node>().border_boxes();
3350 let rects: Vec<DomRoot<DOMRect>> = raw_rects
3351 .map(|rect| {
3352 DOMRect::new(
3353 win.upcast(),
3354 rect.origin.x.to_f64_px(),
3355 rect.origin.y.to_f64_px(),
3356 rect.size.width.to_f64_px(),
3357 rect.size.height.to_f64_px(),
3358 can_gc,
3359 )
3360 })
3361 .collect();
3362 DOMRectList::new(&win, rects, can_gc)
3363 }
3364
3365 fn GetBoundingClientRect(&self, can_gc: CanGc) -> DomRoot<DOMRect> {
3367 let win = self.owner_window();
3368 let rect = self.upcast::<Node>().border_box().unwrap_or_default();
3369 DOMRect::new(
3370 win.upcast(),
3371 rect.origin.x.to_f64_px(),
3372 rect.origin.y.to_f64_px(),
3373 rect.size.width.to_f64_px(),
3374 rect.size.height.to_f64_px(),
3375 can_gc,
3376 )
3377 }
3378
3379 fn Scroll(&self, options: &ScrollToOptions) {
3381 let left = options.left.unwrap_or(self.ScrollLeft());
3383 let top = options.top.unwrap_or(self.ScrollTop());
3384 self.scroll(left, top, options.parent.behavior);
3385 }
3386
3387 fn Scroll_(&self, x: f64, y: f64) {
3389 self.scroll(x, y, ScrollBehavior::Auto);
3390 }
3391
3392 fn ScrollTo(&self, options: &ScrollToOptions) {
3394 self.Scroll(options);
3395 }
3396
3397 fn ScrollTo_(&self, x: f64, y: f64) {
3399 self.Scroll_(x, y);
3400 }
3401
3402 fn ScrollBy(&self, options: &ScrollToOptions) {
3404 let delta_left = options.left.unwrap_or(0.0f64);
3406 let delta_top = options.top.unwrap_or(0.0f64);
3407 let left = self.ScrollLeft();
3408 let top = self.ScrollTop();
3409 self.scroll(left + delta_left, top + delta_top, options.parent.behavior);
3410 }
3411
3412 fn ScrollBy_(&self, x: f64, y: f64) {
3414 let left = self.ScrollLeft();
3415 let top = self.ScrollTop();
3416 self.scroll(left + x, top + y, ScrollBehavior::Auto);
3417 }
3418
3419 fn ScrollTop(&self) -> f64 {
3421 let node = self.upcast::<Node>();
3422
3423 let doc = node.owner_doc();
3425
3426 if !doc.is_fully_active() {
3428 return 0.0;
3429 }
3430
3431 let win = match doc.GetDefaultView() {
3433 None => return 0.0,
3434 Some(win) => win,
3435 };
3436
3437 if self.is_document_element() {
3439 if doc.quirks_mode() == QuirksMode::Quirks {
3440 return 0.0;
3441 }
3442
3443 return win.ScrollY() as f64;
3445 }
3446
3447 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3449 doc.quirks_mode() == QuirksMode::Quirks &&
3450 !self.is_potentially_scrollable_body()
3451 {
3452 return win.ScrollY() as f64;
3453 }
3454
3455 if !self.has_css_layout_box() {
3457 return 0.0;
3458 }
3459
3460 let point = win.scroll_offset_query(node);
3462 point.y.abs() as f64
3463 }
3464
3465 fn SetScrollTop(&self, y_: f64) {
3468 let behavior = ScrollBehavior::Auto;
3469
3470 let y = if y_.is_finite() { y_ } else { 0.0 } as f32;
3472
3473 let node = self.upcast::<Node>();
3474
3475 let doc = node.owner_doc();
3477
3478 if !doc.is_fully_active() {
3480 return;
3481 }
3482
3483 let win = match doc.GetDefaultView() {
3485 None => return,
3486 Some(win) => win,
3487 };
3488
3489 if self.is_document_element() {
3491 if doc.quirks_mode() != QuirksMode::Quirks {
3492 win.scroll(win.ScrollX() as f32, y, behavior);
3493 }
3494
3495 return;
3496 }
3497
3498 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3500 doc.quirks_mode() == QuirksMode::Quirks &&
3501 !self.is_potentially_scrollable_body()
3502 {
3503 win.scroll(win.ScrollX() as f32, y, behavior);
3504 return;
3505 }
3506
3507 if !self.has_scrolling_box() {
3509 return;
3510 }
3511
3512 win.scroll_an_element(self, self.ScrollLeft() as f32, y, behavior);
3514 }
3515
3516 fn ScrollLeft(&self) -> f64 {
3518 let node = self.upcast::<Node>();
3519
3520 let doc = node.owner_doc();
3522
3523 if !doc.is_fully_active() {
3525 return 0.0;
3526 }
3527
3528 let win = match doc.GetDefaultView() {
3530 None => return 0.0,
3531 Some(win) => win,
3532 };
3533
3534 if self.is_document_element() {
3536 if doc.quirks_mode() != QuirksMode::Quirks {
3537 return win.ScrollX() as f64;
3539 }
3540
3541 return 0.0;
3542 }
3543
3544 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3546 doc.quirks_mode() == QuirksMode::Quirks &&
3547 !self.is_potentially_scrollable_body()
3548 {
3549 return win.ScrollX() as f64;
3550 }
3551
3552 if !self.has_css_layout_box() {
3554 return 0.0;
3555 }
3556
3557 let point = win.scroll_offset_query(node);
3559 point.x.abs() as f64
3560 }
3561
3562 fn SetScrollLeft(&self, x: f64) {
3564 let behavior = ScrollBehavior::Auto;
3565
3566 let x = if x.is_finite() { x } else { 0.0 } as f32;
3568
3569 let node = self.upcast::<Node>();
3570
3571 let doc = node.owner_doc();
3573
3574 if !doc.is_fully_active() {
3576 return;
3577 }
3578
3579 let win = match doc.GetDefaultView() {
3581 None => return,
3582 Some(win) => win,
3583 };
3584
3585 if self.is_document_element() {
3587 if doc.quirks_mode() == QuirksMode::Quirks {
3588 return;
3589 }
3590
3591 win.scroll(x, win.ScrollY() as f32, behavior);
3592 return;
3593 }
3594
3595 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3597 doc.quirks_mode() == QuirksMode::Quirks &&
3598 !self.is_potentially_scrollable_body()
3599 {
3600 win.scroll(x, win.ScrollY() as f32, behavior);
3601 return;
3602 }
3603
3604 if !self.has_scrolling_box() {
3606 return;
3607 }
3608
3609 win.scroll_an_element(self, x, self.ScrollTop() as f32, behavior);
3611 }
3612
3613 fn ScrollIntoView(&self, arg: BooleanOrScrollIntoViewOptions) {
3615 let (behavior, block, inline, container) = match arg {
3616 BooleanOrScrollIntoViewOptions::Boolean(true) => (
3618 ScrollBehavior::Auto, ScrollLogicalPosition::Start, ScrollLogicalPosition::Nearest, None, ),
3623 BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(options) => (
3626 options.parent.behavior,
3627 options.block,
3628 options.inline,
3629 if options.container == ScrollIntoViewContainer::Nearest {
3632 Some(self)
3633 } else {
3634 None
3635 },
3636 ),
3637 BooleanOrScrollIntoViewOptions::Boolean(false) => (
3639 ScrollBehavior::Auto,
3640 ScrollLogicalPosition::End,
3641 ScrollLogicalPosition::Nearest,
3642 None,
3643 ),
3644 };
3645
3646 if !self.has_css_layout_box() {
3649 return;
3650 }
3651
3652 self.scroll_into_view_with_options(
3654 behavior,
3655 ScrollAxisState::new_always_scroll_position(block),
3656 ScrollAxisState::new_always_scroll_position(inline),
3657 container,
3658 None,
3659 );
3660
3661 }
3664
3665 fn ScrollWidth(&self) -> i32 {
3667 self.upcast::<Node>().scroll_area().size.width
3668 }
3669
3670 fn ScrollHeight(&self) -> i32 {
3672 self.upcast::<Node>().scroll_area().size.height
3673 }
3674
3675 fn ClientTop(&self) -> i32 {
3677 self.client_rect().origin.y
3678 }
3679
3680 fn ClientLeft(&self) -> i32 {
3682 self.client_rect().origin.x
3683 }
3684
3685 fn ClientWidth(&self) -> i32 {
3687 self.client_rect().size.width
3688 }
3689
3690 fn ClientHeight(&self) -> i32 {
3692 self.client_rect().size.height
3693 }
3694
3695 fn CurrentCSSZoom(&self) -> Finite<f64> {
3697 let window = self.owner_window();
3698 Finite::wrap(window.current_css_zoom_query(self.upcast::<Node>()) as f64)
3699 }
3700
3701 fn SetHTMLUnsafe(
3703 &self,
3704 cx: &mut js::context::JSContext,
3705 html: TrustedHTMLOrString,
3706 ) -> ErrorResult {
3707 let html = TrustedHTML::get_trusted_script_compliant_string(
3711 &self.owner_global(),
3712 html,
3713 "Element setHTMLUnsafe",
3714 CanGc::from_cx(cx),
3715 )?;
3716 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3718 DomRoot::upcast(template.Content(CanGc::from_cx(cx)))
3719 } else {
3720 DomRoot::from_ref(self.upcast())
3721 };
3722
3723 Node::unsafely_set_html(&target, self, html, cx);
3725 Ok(())
3726 }
3727
3728 fn GetHTML(&self, options: &GetHTMLOptions, can_gc: CanGc) -> DOMString {
3730 self.upcast::<Node>().html_serialize(
3733 TraversalScope::ChildrenOnly(None),
3734 options.serializableShadowRoots,
3735 options.shadowRoots.clone(),
3736 can_gc,
3737 )
3738 }
3739
3740 fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3742 let qname = QualName::new(
3743 self.prefix().clone(),
3744 self.namespace().clone(),
3745 self.local_name().clone(),
3746 );
3747
3748 let result = if self.owner_document().is_html_document() {
3751 self.upcast::<Node>()
3752 .html_serialize(ChildrenOnly(Some(qname)), false, vec![], can_gc)
3753 } else {
3754 self.upcast::<Node>()
3755 .xml_serialize(XmlChildrenOnly(Some(qname)))?
3756 };
3757
3758 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3759 }
3760
3761 fn SetInnerHTML(
3763 &self,
3764 cx: &mut js::context::JSContext,
3765 value: TrustedHTMLOrNullIsEmptyString,
3766 ) -> ErrorResult {
3767 let value = TrustedHTML::get_trusted_script_compliant_string(
3771 &self.owner_global(),
3772 value.convert(),
3773 "Element innerHTML",
3774 CanGc::from_cx(cx),
3775 )?;
3776 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3778 DomRoot::upcast(template.Content(CanGc::from_cx(cx)))
3781 } else {
3782 DomRoot::from_ref(self.upcast())
3784 };
3785
3786 if !self.node.has_weird_parser_insertion_mode() &&
3789 value.len() < 100 &&
3790 !value
3791 .as_bytes()
3792 .iter()
3793 .any(|c| matches!(*c, b'&' | b'\0' | b'<' | b'\r'))
3794 {
3795 return Node::SetTextContent(&target, Some(value), CanGc::from_cx(cx));
3796 }
3797
3798 let frag = self.parse_fragment(value, cx)?;
3801
3802 Node::replace_all(Some(frag.upcast()), &target, CanGc::from_cx(cx));
3804 Ok(())
3805 }
3806
3807 fn GetOuterHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3809 let result = if self.owner_document().is_html_document() {
3812 self.upcast::<Node>()
3813 .html_serialize(IncludeNode, false, vec![], can_gc)
3814 } else {
3815 self.upcast::<Node>().xml_serialize(XmlIncludeNode)?
3816 };
3817
3818 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3819 }
3820
3821 fn SetOuterHTML(
3823 &self,
3824 cx: &mut js::context::JSContext,
3825 value: TrustedHTMLOrNullIsEmptyString,
3826 ) -> ErrorResult {
3827 let value = TrustedHTML::get_trusted_script_compliant_string(
3831 &self.owner_global(),
3832 value.convert(),
3833 "Element outerHTML",
3834 CanGc::from_cx(cx),
3835 )?;
3836 let context_document = self.owner_document();
3837 let context_node = self.upcast::<Node>();
3838 let context_parent = match context_node.GetParentNode() {
3840 None => {
3841 return Ok(());
3844 },
3845 Some(parent) => parent,
3846 };
3847
3848 let parent = match context_parent.type_id() {
3849 NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed(None)),
3851
3852 NodeTypeId::DocumentFragment(_) => {
3855 let body_elem = Element::create(
3856 QualName::new(None, ns!(html), local_name!("body")),
3857 None,
3858 &context_document,
3859 ElementCreator::ScriptCreated,
3860 CustomElementCreationMode::Synchronous,
3861 None,
3862 CanGc::from_cx(cx),
3863 );
3864 DomRoot::upcast(body_elem)
3865 },
3866 _ => context_node.GetParentElement().unwrap(),
3867 };
3868
3869 let frag = parent.parse_fragment(value, cx)?;
3872 context_parent.ReplaceChild(frag.upcast(), context_node, CanGc::from_cx(cx))?;
3874 Ok(())
3875 }
3876
3877 fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
3879 self.upcast::<Node>()
3880 .preceding_siblings()
3881 .find_map(DomRoot::downcast)
3882 }
3883
3884 fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
3886 self.upcast::<Node>()
3887 .following_siblings()
3888 .find_map(DomRoot::downcast)
3889 }
3890
3891 fn Children(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
3893 let window = self.owner_window();
3894 HTMLCollection::children(&window, self.upcast(), can_gc)
3895 }
3896
3897 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
3899 self.upcast::<Node>().child_elements().next()
3900 }
3901
3902 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
3904 self.upcast::<Node>()
3905 .rev_children()
3906 .find_map(DomRoot::downcast::<Element>)
3907 }
3908
3909 fn ChildElementCount(&self) -> u32 {
3911 self.upcast::<Node>().child_elements().count() as u32
3912 }
3913
3914 fn Prepend(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3916 self.upcast::<Node>().prepend(nodes, can_gc)
3917 }
3918
3919 fn Append(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3921 self.upcast::<Node>().append(nodes, can_gc)
3922 }
3923
3924 fn ReplaceChildren(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3926 self.upcast::<Node>().replace_children(nodes, can_gc)
3927 }
3928
3929 fn MoveBefore(&self, node: &Node, child: Option<&Node>, can_gc: CanGc) -> ErrorResult {
3931 self.upcast::<Node>().move_before(node, child, can_gc)
3932 }
3933
3934 fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
3936 let root = self.upcast::<Node>();
3937 root.query_selector(selectors)
3938 }
3939
3940 fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>> {
3942 let root = self.upcast::<Node>();
3943 root.query_selector_all(selectors)
3944 }
3945
3946 fn Before(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3948 self.upcast::<Node>().before(nodes, can_gc)
3949 }
3950
3951 fn After(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3953 self.upcast::<Node>().after(nodes, can_gc)
3954 }
3955
3956 fn ReplaceWith(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3958 self.upcast::<Node>().replace_with(nodes, can_gc)
3959 }
3960
3961 fn Remove(&self, can_gc: CanGc) {
3963 self.upcast::<Node>().remove_self(can_gc);
3964 }
3965
3966 fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
3968 let doc = self.owner_document();
3969 let url = doc.url();
3970 let selectors = match SelectorParser::parse_author_origin_no_namespace(
3971 &selectors.str(),
3972 &UrlExtraData(url.get_arc()),
3973 ) {
3974 Err(_) => return Err(Error::Syntax(None)),
3975 Ok(selectors) => selectors,
3976 };
3977
3978 let quirks_mode = doc.quirks_mode();
3979 let element = DomRoot::from_ref(self);
3980
3981 Ok(dom_apis::element_matches(
3982 &SelectorWrapper::Borrowed(&element),
3983 &selectors,
3984 quirks_mode,
3985 ))
3986 }
3987
3988 fn WebkitMatchesSelector(&self, selectors: DOMString) -> Fallible<bool> {
3990 self.Matches(selectors)
3991 }
3992
3993 fn Closest(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
3995 let doc = self.owner_document();
3996 let url = doc.url();
3997 let selectors = match SelectorParser::parse_author_origin_no_namespace(
3998 &selectors.str(),
3999 &UrlExtraData(url.get_arc()),
4000 ) {
4001 Err(_) => return Err(Error::Syntax(None)),
4002 Ok(selectors) => selectors,
4003 };
4004
4005 let quirks_mode = doc.quirks_mode();
4006 Ok(dom_apis::element_closest(
4007 SelectorWrapper::Owned(DomRoot::from_ref(self)),
4008 &selectors,
4009 quirks_mode,
4010 )
4011 .map(SelectorWrapper::into_owned))
4012 }
4013
4014 fn InsertAdjacentElement(
4016 &self,
4017 where_: DOMString,
4018 element: &Element,
4019 can_gc: CanGc,
4020 ) -> Fallible<Option<DomRoot<Element>>> {
4021 let where_ = where_.parse::<AdjacentPosition>()?;
4022 let inserted_node = self.insert_adjacent(where_, element.upcast(), can_gc)?;
4023 Ok(inserted_node.map(|node| DomRoot::downcast(node).unwrap()))
4024 }
4025
4026 fn InsertAdjacentText(&self, where_: DOMString, data: DOMString, can_gc: CanGc) -> ErrorResult {
4028 let text = Text::new(data, &self.owner_document(), can_gc);
4030
4031 let where_ = where_.parse::<AdjacentPosition>()?;
4033 self.insert_adjacent(where_, text.upcast(), can_gc)
4034 .map(|_| ())
4035 }
4036
4037 fn InsertAdjacentHTML(
4039 &self,
4040 cx: &mut js::context::JSContext,
4041 position: DOMString,
4042 text: TrustedHTMLOrString,
4043 ) -> ErrorResult {
4044 let text = TrustedHTML::get_trusted_script_compliant_string(
4048 &self.owner_global(),
4049 text,
4050 "Element insertAdjacentHTML",
4051 CanGc::from_cx(cx),
4052 )?;
4053 let position = position.parse::<AdjacentPosition>()?;
4054
4055 let context = match position {
4058 AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => {
4061 match self.upcast::<Node>().GetParentNode() {
4062 Some(ref node) if node.is::<Document>() => {
4064 return Err(Error::NoModificationAllowed(None));
4065 },
4066 None => return Err(Error::NoModificationAllowed(None)),
4067 Some(node) => node,
4069 }
4070 },
4071 AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => {
4074 DomRoot::from_ref(self.upcast::<Node>())
4076 },
4077 };
4078
4079 let context = Element::fragment_parsing_context(
4081 cx,
4082 &context.owner_doc(),
4083 context.downcast::<Element>(),
4084 );
4085
4086 let fragment = context.parse_fragment(text, cx)?;
4089
4090 self.insert_adjacent(position, fragment.upcast(), CanGc::from_cx(cx))
4092 .map(|_| ())
4093 }
4094
4095 fn EnterFormalActivationState(&self) -> ErrorResult {
4097 match self.as_maybe_activatable() {
4098 Some(a) => {
4099 a.enter_formal_activation_state();
4100 Ok(())
4101 },
4102 None => Err(Error::NotSupported(None)),
4103 }
4104 }
4105
4106 fn ExitFormalActivationState(&self) -> ErrorResult {
4107 match self.as_maybe_activatable() {
4108 Some(a) => {
4109 a.exit_formal_activation_state();
4110 Ok(())
4111 },
4112 None => Err(Error::NotSupported(None)),
4113 }
4114 }
4115
4116 fn RequestFullscreen(&self, can_gc: CanGc) -> Rc<Promise> {
4118 let doc = self.owner_document();
4119 doc.enter_fullscreen(self, can_gc)
4120 }
4121
4122 fn AttachShadow(&self, init: &ShadowRootInit, can_gc: CanGc) -> Fallible<DomRoot<ShadowRoot>> {
4124 let shadow_root = self.attach_shadow(
4127 IsUserAgentWidget::No,
4128 init.mode,
4129 init.clonable,
4130 init.serializable,
4131 init.delegatesFocus,
4132 init.slotAssignment,
4133 can_gc,
4134 )?;
4135
4136 Ok(shadow_root)
4138 }
4139
4140 fn GetShadowRoot(&self) -> Option<DomRoot<ShadowRoot>> {
4142 let shadow_or_none = self.shadow_root();
4144
4145 let shadow = shadow_or_none?;
4147 if shadow.Mode() == ShadowRootMode::Closed {
4148 return None;
4149 }
4150
4151 Some(shadow)
4153 }
4154
4155 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
4157 self.custom_element_registry()
4159 }
4160
4161 fn GetRole(&self) -> Option<DOMString> {
4162 self.get_nullable_string_attribute(&local_name!("role"))
4163 }
4164
4165 fn SetRole(&self, cx: &mut JSContext, value: Option<DOMString>) {
4166 self.set_nullable_string_attribute(cx, &local_name!("role"), value);
4167 }
4168
4169 fn GetAriaAtomic(&self) -> Option<DOMString> {
4170 self.get_nullable_string_attribute(&local_name!("aria-atomic"))
4171 }
4172
4173 fn SetAriaAtomic(&self, cx: &mut JSContext, value: Option<DOMString>) {
4174 self.set_nullable_string_attribute(cx, &local_name!("aria-atomic"), value);
4175 }
4176
4177 fn GetAriaAutoComplete(&self) -> Option<DOMString> {
4178 self.get_nullable_string_attribute(&local_name!("aria-autocomplete"))
4179 }
4180
4181 fn SetAriaAutoComplete(&self, cx: &mut JSContext, value: Option<DOMString>) {
4182 self.set_nullable_string_attribute(cx, &local_name!("aria-autocomplete"), value);
4183 }
4184
4185 fn GetAriaBrailleLabel(&self) -> Option<DOMString> {
4186 self.get_nullable_string_attribute(&local_name!("aria-braillelabel"))
4187 }
4188
4189 fn SetAriaBrailleLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4190 self.set_nullable_string_attribute(cx, &local_name!("aria-braillelabel"), value);
4191 }
4192
4193 fn GetAriaBrailleRoleDescription(&self) -> Option<DOMString> {
4194 self.get_nullable_string_attribute(&local_name!("aria-brailleroledescription"))
4195 }
4196
4197 fn SetAriaBrailleRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4198 self.set_nullable_string_attribute(cx, &local_name!("aria-brailleroledescription"), value);
4199 }
4200
4201 fn GetAriaBusy(&self) -> Option<DOMString> {
4202 self.get_nullable_string_attribute(&local_name!("aria-busy"))
4203 }
4204
4205 fn SetAriaBusy(&self, cx: &mut JSContext, value: Option<DOMString>) {
4206 self.set_nullable_string_attribute(cx, &local_name!("aria-busy"), value);
4207 }
4208
4209 fn GetAriaChecked(&self) -> Option<DOMString> {
4210 self.get_nullable_string_attribute(&local_name!("aria-checked"))
4211 }
4212
4213 fn SetAriaChecked(&self, cx: &mut JSContext, value: Option<DOMString>) {
4214 self.set_nullable_string_attribute(cx, &local_name!("aria-checked"), value);
4215 }
4216
4217 fn GetAriaColCount(&self) -> Option<DOMString> {
4218 self.get_nullable_string_attribute(&local_name!("aria-colcount"))
4219 }
4220
4221 fn SetAriaColCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4222 self.set_nullable_string_attribute(cx, &local_name!("aria-colcount"), value);
4223 }
4224
4225 fn GetAriaColIndex(&self) -> Option<DOMString> {
4226 self.get_nullable_string_attribute(&local_name!("aria-colindex"))
4227 }
4228
4229 fn SetAriaColIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4230 self.set_nullable_string_attribute(cx, &local_name!("aria-colindex"), value);
4231 }
4232
4233 fn GetAriaColIndexText(&self) -> Option<DOMString> {
4234 self.get_nullable_string_attribute(&local_name!("aria-colindextext"))
4235 }
4236
4237 fn SetAriaColIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4238 self.set_nullable_string_attribute(cx, &local_name!("aria-colindextext"), value);
4239 }
4240
4241 fn GetAriaColSpan(&self) -> Option<DOMString> {
4242 self.get_nullable_string_attribute(&local_name!("aria-colspan"))
4243 }
4244
4245 fn SetAriaColSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4246 self.set_nullable_string_attribute(cx, &local_name!("aria-colspan"), value);
4247 }
4248
4249 fn GetAriaCurrent(&self) -> Option<DOMString> {
4250 self.get_nullable_string_attribute(&local_name!("aria-current"))
4251 }
4252
4253 fn SetAriaCurrent(&self, cx: &mut JSContext, value: Option<DOMString>) {
4254 self.set_nullable_string_attribute(cx, &local_name!("aria-current"), value);
4255 }
4256
4257 fn GetAriaDescription(&self) -> Option<DOMString> {
4258 self.get_nullable_string_attribute(&local_name!("aria-description"))
4259 }
4260
4261 fn SetAriaDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4262 self.set_nullable_string_attribute(cx, &local_name!("aria-description"), value);
4263 }
4264
4265 fn GetAriaDisabled(&self) -> Option<DOMString> {
4266 self.get_nullable_string_attribute(&local_name!("aria-disabled"))
4267 }
4268
4269 fn SetAriaDisabled(&self, cx: &mut JSContext, value: Option<DOMString>) {
4270 self.set_nullable_string_attribute(cx, &local_name!("aria-disabled"), value);
4271 }
4272
4273 fn GetAriaExpanded(&self) -> Option<DOMString> {
4274 self.get_nullable_string_attribute(&local_name!("aria-expanded"))
4275 }
4276
4277 fn SetAriaExpanded(&self, cx: &mut JSContext, value: Option<DOMString>) {
4278 self.set_nullable_string_attribute(cx, &local_name!("aria-expanded"), value);
4279 }
4280
4281 fn GetAriaHasPopup(&self) -> Option<DOMString> {
4282 self.get_nullable_string_attribute(&local_name!("aria-haspopup"))
4283 }
4284
4285 fn SetAriaHasPopup(&self, cx: &mut JSContext, value: Option<DOMString>) {
4286 self.set_nullable_string_attribute(cx, &local_name!("aria-haspopup"), value);
4287 }
4288
4289 fn GetAriaHidden(&self) -> Option<DOMString> {
4290 self.get_nullable_string_attribute(&local_name!("aria-hidden"))
4291 }
4292
4293 fn SetAriaHidden(&self, cx: &mut JSContext, value: Option<DOMString>) {
4294 self.set_nullable_string_attribute(cx, &local_name!("aria-hidden"), value);
4295 }
4296
4297 fn GetAriaInvalid(&self) -> Option<DOMString> {
4298 self.get_nullable_string_attribute(&local_name!("aria-invalid"))
4299 }
4300
4301 fn SetAriaInvalid(&self, cx: &mut JSContext, value: Option<DOMString>) {
4302 self.set_nullable_string_attribute(cx, &local_name!("aria-invalid"), value);
4303 }
4304
4305 fn GetAriaKeyShortcuts(&self) -> Option<DOMString> {
4306 self.get_nullable_string_attribute(&local_name!("aria-keyshortcuts"))
4307 }
4308
4309 fn SetAriaKeyShortcuts(&self, cx: &mut JSContext, value: Option<DOMString>) {
4310 self.set_nullable_string_attribute(cx, &local_name!("aria-keyshortcuts"), value);
4311 }
4312
4313 fn GetAriaLabel(&self) -> Option<DOMString> {
4314 self.get_nullable_string_attribute(&local_name!("aria-label"))
4315 }
4316
4317 fn SetAriaLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4318 self.set_nullable_string_attribute(cx, &local_name!("aria-label"), value);
4319 }
4320
4321 fn GetAriaLevel(&self) -> Option<DOMString> {
4322 self.get_nullable_string_attribute(&local_name!("aria-level"))
4323 }
4324
4325 fn SetAriaLevel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4326 self.set_nullable_string_attribute(cx, &local_name!("aria-level"), value);
4327 }
4328
4329 fn GetAriaLive(&self) -> Option<DOMString> {
4330 self.get_nullable_string_attribute(&local_name!("aria-live"))
4331 }
4332
4333 fn SetAriaLive(&self, cx: &mut JSContext, value: Option<DOMString>) {
4334 self.set_nullable_string_attribute(cx, &local_name!("aria-live"), value);
4335 }
4336
4337 fn GetAriaModal(&self) -> Option<DOMString> {
4338 self.get_nullable_string_attribute(&local_name!("aria-modal"))
4339 }
4340
4341 fn SetAriaModal(&self, cx: &mut JSContext, value: Option<DOMString>) {
4342 self.set_nullable_string_attribute(cx, &local_name!("aria-modal"), value);
4343 }
4344
4345 fn GetAriaMultiLine(&self) -> Option<DOMString> {
4346 self.get_nullable_string_attribute(&local_name!("aria-multiline"))
4347 }
4348
4349 fn SetAriaMultiLine(&self, cx: &mut JSContext, value: Option<DOMString>) {
4350 self.set_nullable_string_attribute(cx, &local_name!("aria-multiline"), value);
4351 }
4352
4353 fn GetAriaMultiSelectable(&self) -> Option<DOMString> {
4354 self.get_nullable_string_attribute(&local_name!("aria-multiselectable"))
4355 }
4356
4357 fn SetAriaMultiSelectable(&self, cx: &mut JSContext, value: Option<DOMString>) {
4358 self.set_nullable_string_attribute(cx, &local_name!("aria-multiselectable"), value);
4359 }
4360
4361 fn GetAriaOrientation(&self) -> Option<DOMString> {
4362 self.get_nullable_string_attribute(&local_name!("aria-orientation"))
4363 }
4364
4365 fn SetAriaOrientation(&self, cx: &mut JSContext, value: Option<DOMString>) {
4366 self.set_nullable_string_attribute(cx, &local_name!("aria-orientation"), value);
4367 }
4368
4369 fn GetAriaPlaceholder(&self) -> Option<DOMString> {
4370 self.get_nullable_string_attribute(&local_name!("aria-placeholder"))
4371 }
4372
4373 fn SetAriaPlaceholder(&self, cx: &mut JSContext, value: Option<DOMString>) {
4374 self.set_nullable_string_attribute(cx, &local_name!("aria-placeholder"), value);
4375 }
4376
4377 fn GetAriaPosInSet(&self) -> Option<DOMString> {
4378 self.get_nullable_string_attribute(&local_name!("aria-posinset"))
4379 }
4380
4381 fn SetAriaPosInSet(&self, cx: &mut JSContext, value: Option<DOMString>) {
4382 self.set_nullable_string_attribute(cx, &local_name!("aria-posinset"), value);
4383 }
4384
4385 fn GetAriaPressed(&self) -> Option<DOMString> {
4386 self.get_nullable_string_attribute(&local_name!("aria-pressed"))
4387 }
4388
4389 fn SetAriaPressed(&self, cx: &mut JSContext, value: Option<DOMString>) {
4390 self.set_nullable_string_attribute(cx, &local_name!("aria-pressed"), value);
4391 }
4392
4393 fn GetAriaReadOnly(&self) -> Option<DOMString> {
4394 self.get_nullable_string_attribute(&local_name!("aria-readonly"))
4395 }
4396
4397 fn SetAriaReadOnly(&self, cx: &mut JSContext, value: Option<DOMString>) {
4398 self.set_nullable_string_attribute(cx, &local_name!("aria-readonly"), value);
4399 }
4400
4401 fn GetAriaRelevant(&self) -> Option<DOMString> {
4402 self.get_nullable_string_attribute(&local_name!("aria-relevant"))
4403 }
4404
4405 fn SetAriaRelevant(&self, cx: &mut JSContext, value: Option<DOMString>) {
4406 self.set_nullable_string_attribute(cx, &local_name!("aria-relevant"), value);
4407 }
4408
4409 fn GetAriaRequired(&self) -> Option<DOMString> {
4410 self.get_nullable_string_attribute(&local_name!("aria-required"))
4411 }
4412
4413 fn SetAriaRequired(&self, cx: &mut JSContext, value: Option<DOMString>) {
4414 self.set_nullable_string_attribute(cx, &local_name!("aria-required"), value);
4415 }
4416
4417 fn GetAriaRoleDescription(&self) -> Option<DOMString> {
4418 self.get_nullable_string_attribute(&local_name!("aria-roledescription"))
4419 }
4420
4421 fn SetAriaRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4422 self.set_nullable_string_attribute(cx, &local_name!("aria-roledescription"), value);
4423 }
4424
4425 fn GetAriaRowCount(&self) -> Option<DOMString> {
4426 self.get_nullable_string_attribute(&local_name!("aria-rowcount"))
4427 }
4428
4429 fn SetAriaRowCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4430 self.set_nullable_string_attribute(cx, &local_name!("aria-rowcount"), value);
4431 }
4432
4433 fn GetAriaRowIndex(&self) -> Option<DOMString> {
4434 self.get_nullable_string_attribute(&local_name!("aria-rowindex"))
4435 }
4436
4437 fn SetAriaRowIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4438 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindex"), value);
4439 }
4440
4441 fn GetAriaRowIndexText(&self) -> Option<DOMString> {
4442 self.get_nullable_string_attribute(&local_name!("aria-rowindextext"))
4443 }
4444
4445 fn SetAriaRowIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4446 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindextext"), value);
4447 }
4448
4449 fn GetAriaRowSpan(&self) -> Option<DOMString> {
4450 self.get_nullable_string_attribute(&local_name!("aria-rowspan"))
4451 }
4452
4453 fn SetAriaRowSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4454 self.set_nullable_string_attribute(cx, &local_name!("aria-rowspan"), value);
4455 }
4456
4457 fn GetAriaSelected(&self) -> Option<DOMString> {
4458 self.get_nullable_string_attribute(&local_name!("aria-selected"))
4459 }
4460
4461 fn SetAriaSelected(&self, cx: &mut JSContext, value: Option<DOMString>) {
4462 self.set_nullable_string_attribute(cx, &local_name!("aria-selected"), value);
4463 }
4464
4465 fn GetAriaSetSize(&self) -> Option<DOMString> {
4466 self.get_nullable_string_attribute(&local_name!("aria-setsize"))
4467 }
4468
4469 fn SetAriaSetSize(&self, cx: &mut JSContext, value: Option<DOMString>) {
4470 self.set_nullable_string_attribute(cx, &local_name!("aria-setsize"), value);
4471 }
4472
4473 fn GetAriaSort(&self) -> Option<DOMString> {
4474 self.get_nullable_string_attribute(&local_name!("aria-sort"))
4475 }
4476
4477 fn SetAriaSort(&self, cx: &mut JSContext, value: Option<DOMString>) {
4478 self.set_nullable_string_attribute(cx, &local_name!("aria-sort"), value);
4479 }
4480
4481 fn GetAriaValueMax(&self) -> Option<DOMString> {
4482 self.get_nullable_string_attribute(&local_name!("aria-valuemax"))
4483 }
4484
4485 fn SetAriaValueMax(&self, cx: &mut JSContext, value: Option<DOMString>) {
4486 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemax"), value);
4487 }
4488
4489 fn GetAriaValueMin(&self) -> Option<DOMString> {
4490 self.get_nullable_string_attribute(&local_name!("aria-valuemin"))
4491 }
4492
4493 fn SetAriaValueMin(&self, cx: &mut JSContext, value: Option<DOMString>) {
4494 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemin"), value);
4495 }
4496
4497 fn GetAriaValueNow(&self) -> Option<DOMString> {
4498 self.get_nullable_string_attribute(&local_name!("aria-valuenow"))
4499 }
4500
4501 fn SetAriaValueNow(&self, cx: &mut JSContext, value: Option<DOMString>) {
4502 self.set_nullable_string_attribute(cx, &local_name!("aria-valuenow"), value);
4503 }
4504
4505 fn GetAriaValueText(&self) -> Option<DOMString> {
4506 self.get_nullable_string_attribute(&local_name!("aria-valuetext"))
4507 }
4508
4509 fn SetAriaValueText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4510 self.set_nullable_string_attribute(cx, &local_name!("aria-valuetext"), value);
4511 }
4512
4513 fn GetAssignedSlot(&self) -> Option<DomRoot<HTMLSlotElement>> {
4515 let cx = GlobalScope::get_cx();
4516
4517 rooted!(in(*cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4520 slottable.find_a_slot(true)
4521 }
4522
4523 fn Part(&self) -> DomRoot<DOMTokenList> {
4525 self.ensure_rare_data()
4526 .part
4527 .or_init(|| DOMTokenList::new(self, &local_name!("part"), None, CanGc::note()))
4528 }
4529}
4530
4531impl VirtualMethods for Element {
4532 fn super_type(&self) -> Option<&dyn VirtualMethods> {
4533 Some(self.upcast::<Node>() as &dyn VirtualMethods)
4534 }
4535
4536 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
4537 if attr.local_name() == &local_name!("lang") {
4539 return true;
4540 }
4541
4542 self.super_type()
4543 .unwrap()
4544 .attribute_affects_presentational_hints(attr)
4545 }
4546
4547 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
4548 self.super_type()
4549 .unwrap()
4550 .attribute_mutated(attr, mutation, can_gc);
4551 let node = self.upcast::<Node>();
4552 let doc = node.owner_doc();
4553 match *attr.local_name() {
4554 local_name!("style") => self.update_style_attribute(attr, mutation),
4555 local_name!("id") => {
4556 *self.id_attribute.borrow_mut() = mutation.new_value(attr).and_then(|value| {
4558 let value = value.as_atom();
4559 if value != &atom!("") {
4560 Some(value.clone())
4562 } else {
4563 None
4565 }
4566 });
4567
4568 let containing_shadow_root = self.containing_shadow_root();
4569 if node.is_in_a_document_tree() || node.is_in_a_shadow_tree() {
4570 let value = attr.value().as_atom().clone();
4571 match mutation {
4572 AttributeMutation::Set(old_value, _) => {
4573 if let Some(old_value) = old_value {
4574 let old_value = old_value.as_atom().clone();
4575 if let Some(ref shadow_root) = containing_shadow_root {
4576 shadow_root.unregister_element_id(self, old_value, can_gc);
4577 } else {
4578 doc.unregister_element_id(self, old_value, can_gc);
4579 }
4580 }
4581 if value != atom!("") {
4582 if let Some(ref shadow_root) = containing_shadow_root {
4583 shadow_root.register_element_id(self, value, can_gc);
4584 } else {
4585 doc.register_element_id(self, value, can_gc);
4586 }
4587 }
4588 },
4589 AttributeMutation::Removed => {
4590 if value != atom!("") {
4591 if let Some(ref shadow_root) = containing_shadow_root {
4592 shadow_root.unregister_element_id(self, value, can_gc);
4593 } else {
4594 doc.unregister_element_id(self, value, can_gc);
4595 }
4596 }
4597 },
4598 }
4599 }
4600 },
4601 local_name!("name") => {
4602 self.ensure_rare_data().name_attribute =
4604 mutation.new_value(attr).and_then(|value| {
4605 let value = value.as_atom();
4606 if value != &atom!("") {
4607 Some(value.clone())
4608 } else {
4609 None
4610 }
4611 });
4612 if node.is_connected() && node.containing_shadow_root().is_none() {
4615 let value = attr.value().as_atom().clone();
4616 match mutation {
4617 AttributeMutation::Set(old_value, _) => {
4618 if let Some(old_value) = old_value {
4619 let old_value = old_value.as_atom().clone();
4620 doc.unregister_element_name(self, old_value);
4621 }
4622 if value != atom!("") {
4623 doc.register_element_name(self, value);
4624 }
4625 },
4626 AttributeMutation::Removed => {
4627 if value != atom!("") {
4628 doc.unregister_element_name(self, value);
4629 }
4630 },
4631 }
4632 }
4633 },
4634 local_name!("slot") => {
4635 let cx = GlobalScope::get_cx();
4637
4638 rooted!(in(*cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4639
4640 if let Some(assigned_slot) = slottable.assigned_slot() {
4642 assigned_slot.assign_slottables();
4643 }
4644 slottable.assign_a_slot();
4645 },
4646 _ => {
4647 if attr.namespace() == &ns!() && attr.local_name() == &local_name!("src") {
4650 node.dirty(NodeDamage::Other);
4651 }
4652 },
4653 };
4654
4655 if self
4658 .upcast::<Node>()
4659 .get_flag(NodeFlags::USES_ATTR_IN_CONTENT_ATTRIBUTE)
4660 {
4661 node.dirty(NodeDamage::ContentOrHeritage);
4662 }
4663
4664 node.rev_version();
4668
4669 let global = self.owner_global();
4671 if global.live_devtools_updates() {
4672 if let Some(sender) = global.devtools_chan() {
4673 let pipeline_id = global.pipeline_id();
4674 if ScriptThread::devtools_want_updates_for_node(pipeline_id, self.upcast()) {
4675 let devtools_message = ScriptToDevtoolsControlMsg::DomMutation(
4676 pipeline_id,
4677 DomMutation::AttributeModified {
4678 node: self.upcast::<Node>().unique_id(pipeline_id),
4679 attribute_name: attr.local_name().to_string(),
4680 new_value: mutation.new_value(attr).map(|value| value.to_string()),
4681 },
4682 );
4683 sender.send(devtools_message).unwrap();
4684 }
4685 }
4686 }
4687 }
4688
4689 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
4690 match *name {
4691 local_name!("id") => AttrValue::Atom(value.into()),
4692 local_name!("name") => AttrValue::Atom(value.into()),
4693 local_name!("class") | local_name!("part") => {
4694 AttrValue::from_serialized_tokenlist(value.into())
4695 },
4696 local_name!("exportparts") => AttrValue::from_shadow_parts(value.into()),
4697 local_name!("tabindex") => AttrValue::from_i32(value.into(), -1),
4698 _ => self
4699 .super_type()
4700 .unwrap()
4701 .parse_plain_attribute(name, value),
4702 }
4703 }
4704
4705 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
4706 if let Some(s) = self.super_type() {
4707 s.bind_to_tree(context, can_gc);
4708 }
4709
4710 if let Some(f) = self.as_maybe_form_control() {
4711 f.bind_form_control_to_tree(can_gc);
4712 }
4713
4714 let doc = self.owner_document();
4715
4716 if let Some(ref shadow_root) = self.shadow_root() {
4717 shadow_root.bind_to_tree(context, can_gc);
4718 }
4719
4720 if !context.is_in_tree() {
4721 return;
4722 }
4723
4724 if let Some(ref id) = *self.id_attribute.borrow() {
4725 if let Some(shadow_root) = self.containing_shadow_root() {
4726 shadow_root.register_element_id(self, id.clone(), can_gc);
4727 } else {
4728 doc.register_element_id(self, id.clone(), can_gc);
4729 }
4730 }
4731 if let Some(ref name) = self.name_attribute() {
4732 if self.containing_shadow_root().is_none() {
4733 doc.register_element_name(self, name.clone());
4734 }
4735 }
4736
4737 doc.increment_dom_count();
4739 }
4740
4741 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
4742 self.super_type().unwrap().unbind_from_tree(context, can_gc);
4743
4744 if let Some(f) = self.as_maybe_form_control() {
4745 f.unbind_form_control_from_tree(can_gc);
4749 }
4750
4751 if !context.tree_is_in_a_document_tree && !context.tree_is_in_a_shadow_tree {
4752 return;
4753 }
4754
4755 let doc = self.owner_document();
4756
4757 let fullscreen = doc.fullscreen_element();
4758 if fullscreen.as_deref() == Some(self) {
4759 doc.exit_fullscreen(can_gc);
4760 }
4761 if let Some(ref value) = *self.id_attribute.borrow() {
4762 if let Some(ref shadow_root) = self.containing_shadow_root() {
4763 if !self.upcast::<Node>().is_in_a_shadow_tree() {
4766 shadow_root.unregister_element_id(self, value.clone(), can_gc);
4767 }
4768 } else {
4769 doc.unregister_element_id(self, value.clone(), can_gc);
4770 }
4771 }
4772 if let Some(ref value) = self.name_attribute() {
4773 if self.containing_shadow_root().is_none() {
4774 doc.unregister_element_name(self, value.clone());
4775 }
4776 }
4777 doc.decrement_dom_count();
4779 }
4780
4781 fn children_changed(&self, mutation: &ChildrenMutation, can_gc: CanGc) {
4782 if let Some(s) = self.super_type() {
4783 s.children_changed(mutation, can_gc);
4784 }
4785
4786 let flags = self.get_selector_flags();
4787 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
4788 self.upcast::<Node>().dirty(NodeDamage::Other);
4790 } else {
4791 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
4792 if let Some(next_child) = mutation.next_child() {
4793 for child in next_child.inclusively_following_siblings() {
4794 if child.is::<Element>() {
4795 child.dirty(NodeDamage::Other);
4796 }
4797 }
4798 }
4799 }
4800 if flags.intersects(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {
4801 if let Some(child) = mutation.modified_edge_element() {
4802 child.dirty(NodeDamage::Other);
4803 }
4804 }
4805 }
4806 }
4807
4808 fn adopting_steps(&self, old_doc: &Document, can_gc: CanGc) {
4809 self.super_type().unwrap().adopting_steps(old_doc, can_gc);
4810
4811 if self.owner_document().is_html_document() != old_doc.is_html_document() {
4812 self.tag_name.clear();
4813 }
4814 }
4815
4816 fn post_connection_steps(&self, cx: &mut js::context::JSContext) {
4817 if let Some(s) = self.super_type() {
4818 s.post_connection_steps(cx);
4819 }
4820
4821 self.update_nonce_post_connection();
4822 }
4823
4824 fn cloning_steps(
4826 &self,
4827 cx: &mut JSContext,
4828 copy: &Node,
4829 maybe_doc: Option<&Document>,
4830 clone_children: CloneChildrenFlag,
4831 ) {
4832 if let Some(s) = self.super_type() {
4833 s.cloning_steps(cx, copy, maybe_doc, clone_children);
4834 }
4835 let elem = copy.downcast::<Element>().unwrap();
4836 if let Some(rare_data) = self.rare_data().as_ref() {
4837 elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone());
4838 }
4839 }
4840}
4841
4842#[derive(Clone, PartialEq)]
4843pub enum SelectorWrapper<'a> {
4848 Borrowed(&'a DomRoot<Element>),
4849 Owned(DomRoot<Element>),
4850}
4851
4852impl fmt::Debug for SelectorWrapper<'_> {
4853 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4854 self.deref().fmt(f)
4855 }
4856}
4857
4858impl Deref for SelectorWrapper<'_> {
4859 type Target = DomRoot<Element>;
4860
4861 fn deref(&self) -> &Self::Target {
4862 match self {
4863 SelectorWrapper::Owned(r) => r,
4864 SelectorWrapper::Borrowed(r) => r,
4865 }
4866 }
4867}
4868
4869impl SelectorWrapper<'_> {
4870 fn into_owned(self) -> DomRoot<Element> {
4871 match self {
4872 SelectorWrapper::Owned(r) => r,
4873 SelectorWrapper::Borrowed(r) => r.clone(),
4874 }
4875 }
4876}
4877
4878impl SelectorsElement for SelectorWrapper<'_> {
4879 type Impl = SelectorImpl;
4880
4881 #[expect(unsafe_code)]
4882 fn opaque(&self) -> ::selectors::OpaqueElement {
4883 ::selectors::OpaqueElement::new(unsafe { &*self.reflector().get_jsobject().get() })
4884 }
4885
4886 fn parent_element(&self) -> Option<Self> {
4887 self.upcast::<Node>()
4888 .GetParentElement()
4889 .map(SelectorWrapper::Owned)
4890 }
4891
4892 fn parent_node_is_shadow_root(&self) -> bool {
4893 match self.upcast::<Node>().GetParentNode() {
4894 None => false,
4895 Some(node) => node.is::<ShadowRoot>(),
4896 }
4897 }
4898
4899 fn containing_shadow_host(&self) -> Option<Self> {
4900 self.containing_shadow_root()
4901 .map(|shadow_root| shadow_root.Host())
4902 .map(SelectorWrapper::Owned)
4903 }
4904
4905 fn is_pseudo_element(&self) -> bool {
4906 false
4907 }
4908
4909 fn match_pseudo_element(
4910 &self,
4911 _pseudo: &PseudoElement,
4912 _context: &mut MatchingContext<Self::Impl>,
4913 ) -> bool {
4914 false
4915 }
4916
4917 fn prev_sibling_element(&self) -> Option<Self> {
4918 self.node
4919 .preceding_siblings()
4920 .find_map(DomRoot::downcast)
4921 .map(SelectorWrapper::Owned)
4922 }
4923
4924 fn next_sibling_element(&self) -> Option<Self> {
4925 self.node
4926 .following_siblings()
4927 .find_map(DomRoot::downcast)
4928 .map(SelectorWrapper::Owned)
4929 }
4930
4931 fn first_element_child(&self) -> Option<Self> {
4932 self.GetFirstElementChild().map(SelectorWrapper::Owned)
4933 }
4934
4935 fn attr_matches(
4936 &self,
4937 ns: &NamespaceConstraint<&style::Namespace>,
4938 local_name: &style::LocalName,
4939 operation: &AttrSelectorOperation<&AtomString>,
4940 ) -> bool {
4941 match *ns {
4942 NamespaceConstraint::Specific(ns) => self
4943 .get_attribute_with_namespace(ns, local_name)
4944 .is_some_and(|attr| attr.value().eval_selector(operation)),
4945 NamespaceConstraint::Any => self.attrs.borrow().iter().any(|attr| {
4946 *attr.local_name() == **local_name && attr.value().eval_selector(operation)
4947 }),
4948 }
4949 }
4950
4951 fn is_root(&self) -> bool {
4952 Element::is_root(self)
4953 }
4954
4955 fn is_empty(&self) -> bool {
4956 self.node.children().all(|node| {
4957 !node.is::<Element>() &&
4958 match node.downcast::<Text>() {
4959 None => true,
4960 Some(text) => text.upcast::<CharacterData>().data().is_empty(),
4961 }
4962 })
4963 }
4964
4965 fn has_local_name(&self, local_name: &LocalName) -> bool {
4966 Element::local_name(self) == local_name
4967 }
4968
4969 fn has_namespace(&self, ns: &Namespace) -> bool {
4970 Element::namespace(self) == ns
4971 }
4972
4973 fn is_same_type(&self, other: &Self) -> bool {
4974 Element::local_name(self) == Element::local_name(other) &&
4975 Element::namespace(self) == Element::namespace(other)
4976 }
4977
4978 fn match_non_ts_pseudo_class(
4979 &self,
4980 pseudo_class: &NonTSPseudoClass,
4981 _: &mut MatchingContext<Self::Impl>,
4982 ) -> bool {
4983 match *pseudo_class {
4984 NonTSPseudoClass::Link | NonTSPseudoClass::AnyLink => self.is_link(),
4986 NonTSPseudoClass::Visited => false,
4987
4988 NonTSPseudoClass::ServoNonZeroBorder => match self.downcast::<HTMLTableElement>() {
4989 None => false,
4990 Some(this) => match this.get_border() {
4991 None | Some(0) => false,
4992 Some(_) => true,
4993 },
4994 },
4995
4996 NonTSPseudoClass::CustomState(ref state) => self.has_custom_state(&state.0),
4997
4998 NonTSPseudoClass::Lang(ref lang) => {
5003 extended_filtering(&self.upcast::<Node>().get_lang().unwrap_or_default(), lang)
5004 },
5005
5006 NonTSPseudoClass::ReadOnly => {
5007 !Element::state(self).contains(NonTSPseudoClass::ReadWrite.state_flag())
5008 },
5009
5010 NonTSPseudoClass::Active |
5011 NonTSPseudoClass::Autofill |
5012 NonTSPseudoClass::Checked |
5013 NonTSPseudoClass::Default |
5014 NonTSPseudoClass::Defined |
5015 NonTSPseudoClass::Disabled |
5016 NonTSPseudoClass::Enabled |
5017 NonTSPseudoClass::Focus |
5018 NonTSPseudoClass::FocusVisible |
5019 NonTSPseudoClass::FocusWithin |
5020 NonTSPseudoClass::Fullscreen |
5021 NonTSPseudoClass::Hover |
5022 NonTSPseudoClass::InRange |
5023 NonTSPseudoClass::Indeterminate |
5024 NonTSPseudoClass::Invalid |
5025 NonTSPseudoClass::Modal |
5026 NonTSPseudoClass::MozMeterOptimum |
5027 NonTSPseudoClass::MozMeterSubOptimum |
5028 NonTSPseudoClass::MozMeterSubSubOptimum |
5029 NonTSPseudoClass::Open |
5030 NonTSPseudoClass::Optional |
5031 NonTSPseudoClass::OutOfRange |
5032 NonTSPseudoClass::PlaceholderShown |
5033 NonTSPseudoClass::PopoverOpen |
5034 NonTSPseudoClass::ReadWrite |
5035 NonTSPseudoClass::Required |
5036 NonTSPseudoClass::Target |
5037 NonTSPseudoClass::UserInvalid |
5038 NonTSPseudoClass::UserValid |
5039 NonTSPseudoClass::Valid => Element::state(self).contains(pseudo_class.state_flag()),
5040 }
5041 }
5042
5043 fn is_link(&self) -> bool {
5044 let node = self.upcast::<Node>();
5046 match node.type_id() {
5047 NodeTypeId::Element(ElementTypeId::HTMLElement(
5049 HTMLElementTypeId::HTMLAnchorElement,
5050 )) |
5051 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
5052 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
5053 self.has_attribute(&local_name!("href"))
5054 },
5055 _ => false,
5056 }
5057 }
5058
5059 fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
5060 self.id_attribute
5061 .borrow()
5062 .as_ref()
5063 .is_some_and(|atom| case_sensitivity.eq_atom(id, atom))
5064 }
5065
5066 fn is_part(&self, name: &AtomIdent) -> bool {
5067 Element::is_part(self, name, CaseSensitivity::CaseSensitive)
5068 }
5069
5070 fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
5071 None
5072 }
5073
5074 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
5075 Element::has_class(self, name, case_sensitivity)
5076 }
5077
5078 fn is_html_element_in_html_document(&self) -> bool {
5079 self.html_element_in_html_document()
5080 }
5081
5082 fn is_html_slot_element(&self) -> bool {
5083 self.is_html_element() && self.local_name() == &local_name!("slot")
5084 }
5085
5086 fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
5087 let self_flags = flags.for_self();
5089 if !self_flags.is_empty() {
5090 #[expect(unsafe_code)]
5091 unsafe {
5092 Dom::from_ref(&***self)
5093 .to_layout()
5094 .insert_selector_flags(self_flags);
5095 }
5096 }
5097
5098 let parent_flags = flags.for_parent();
5100 if !parent_flags.is_empty() {
5101 if let Some(p) = self.parent_element() {
5102 #[expect(unsafe_code)]
5103 unsafe {
5104 Dom::from_ref(&**p)
5105 .to_layout()
5106 .insert_selector_flags(parent_flags);
5107 }
5108 }
5109 }
5110 }
5111
5112 fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool {
5113 let mut f = |hash| filter.insert_hash(hash & BLOOM_HASH_MASK);
5114
5115 f(Element::local_name(self).get_hash());
5118 f(Element::namespace(self).get_hash());
5119
5120 if let Some(ref id) = *self.id_attribute.borrow() {
5121 f(id.get_hash());
5122 }
5123
5124 if let Some(attr) = self.get_attribute(&local_name!("class")) {
5125 for class in attr.value().as_tokens() {
5126 f(AtomIdent::cast(class).get_hash());
5127 }
5128 }
5129
5130 for attr in self.attrs.borrow().iter() {
5131 let name = style::values::GenericAtomIdent::cast(attr.local_name());
5132 if !style::bloom::is_attr_name_excluded_from_filter(name) {
5133 f(name.get_hash());
5134 }
5135 }
5136
5137 true
5138 }
5139
5140 fn has_custom_state(&self, name: &AtomIdent) -> bool {
5141 let mut has_state = false;
5142 self.each_custom_state(|state| has_state |= state == name);
5143
5144 has_state
5145 }
5146}
5147
5148impl Element {
5149 fn each_custom_state<F>(&self, callback: F)
5150 where
5151 F: FnMut(&AtomIdent),
5152 {
5153 self.get_element_internals()
5154 .and_then(|internals| internals.custom_states())
5155 .inspect(|states| states.for_each_state(callback));
5156 }
5157
5158 pub(crate) fn client_rect(&self) -> Rect<i32, CSSPixel> {
5159 let doc = self.node.owner_doc();
5160
5161 if let Some(rect) = self
5162 .rare_data()
5163 .as_ref()
5164 .and_then(|data| data.client_rect.as_ref())
5165 .and_then(|rect| rect.get().ok())
5166 {
5167 if doc.restyle_reason().is_empty() {
5168 return rect;
5169 }
5170 }
5171
5172 let mut rect = self.upcast::<Node>().client_rect();
5173 let in_quirks_mode = doc.quirks_mode() == QuirksMode::Quirks;
5174
5175 if (in_quirks_mode && doc.GetBody().as_deref() == self.downcast::<HTMLElement>()) ||
5176 (!in_quirks_mode && self.is_document_element())
5177 {
5178 rect.size = doc.window().viewport_details().size.round().to_i32();
5179 }
5180
5181 self.ensure_rare_data().client_rect = Some(self.owner_window().cache_layout_value(rect));
5182 rect
5183 }
5184
5185 pub(crate) fn as_maybe_activatable(&self) -> Option<&dyn Activatable> {
5186 let element = match self.upcast::<Node>().type_id() {
5187 NodeTypeId::Element(ElementTypeId::HTMLElement(
5188 HTMLElementTypeId::HTMLInputElement,
5189 )) => {
5190 let element = self.downcast::<HTMLInputElement>().unwrap();
5191 Some(element as &dyn Activatable)
5192 },
5193 NodeTypeId::Element(ElementTypeId::HTMLElement(
5194 HTMLElementTypeId::HTMLButtonElement,
5195 )) => {
5196 let element = self.downcast::<HTMLButtonElement>().unwrap();
5197 Some(element as &dyn Activatable)
5198 },
5199 NodeTypeId::Element(ElementTypeId::HTMLElement(
5200 HTMLElementTypeId::HTMLAnchorElement,
5201 )) => {
5202 let element = self.downcast::<HTMLAnchorElement>().unwrap();
5203 Some(element as &dyn Activatable)
5204 },
5205 NodeTypeId::Element(ElementTypeId::HTMLElement(
5206 HTMLElementTypeId::HTMLLabelElement,
5207 )) => {
5208 let element = self.downcast::<HTMLLabelElement>().unwrap();
5209 Some(element as &dyn Activatable)
5210 },
5211 NodeTypeId::Element(ElementTypeId::HTMLElement(
5212 HTMLElementTypeId::HTMLSelectElement,
5213 )) => {
5214 let element = self.downcast::<HTMLSelectElement>().unwrap();
5215 Some(element as &dyn Activatable)
5216 },
5217 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
5218 let element = self.downcast::<HTMLElement>().unwrap();
5219 Some(element as &dyn Activatable)
5220 },
5221 _ => None,
5222 };
5223 element.and_then(|elem| {
5224 if elem.is_instance_activatable() {
5225 Some(elem)
5226 } else {
5227 None
5228 }
5229 })
5230 }
5231
5232 pub(crate) fn as_stylesheet_owner(&self) -> Option<&dyn StylesheetOwner> {
5233 if let Some(s) = self.downcast::<HTMLStyleElement>() {
5234 return Some(s as &dyn StylesheetOwner);
5235 }
5236
5237 if let Some(l) = self.downcast::<HTMLLinkElement>() {
5238 return Some(l as &dyn StylesheetOwner);
5239 }
5240
5241 None
5242 }
5243
5244 pub(crate) fn as_maybe_validatable(&self) -> Option<&dyn Validatable> {
5246 match self.upcast::<Node>().type_id() {
5247 NodeTypeId::Element(ElementTypeId::HTMLElement(
5248 HTMLElementTypeId::HTMLInputElement,
5249 )) => {
5250 let element = self.downcast::<HTMLInputElement>().unwrap();
5251 Some(element as &dyn Validatable)
5252 },
5253 NodeTypeId::Element(ElementTypeId::HTMLElement(
5254 HTMLElementTypeId::HTMLButtonElement,
5255 )) => {
5256 let element = self.downcast::<HTMLButtonElement>().unwrap();
5257 Some(element as &dyn Validatable)
5258 },
5259 NodeTypeId::Element(ElementTypeId::HTMLElement(
5260 HTMLElementTypeId::HTMLObjectElement,
5261 )) => {
5262 let element = self.downcast::<HTMLObjectElement>().unwrap();
5263 Some(element as &dyn Validatable)
5264 },
5265 NodeTypeId::Element(ElementTypeId::HTMLElement(
5266 HTMLElementTypeId::HTMLSelectElement,
5267 )) => {
5268 let element = self.downcast::<HTMLSelectElement>().unwrap();
5269 Some(element as &dyn Validatable)
5270 },
5271 NodeTypeId::Element(ElementTypeId::HTMLElement(
5272 HTMLElementTypeId::HTMLTextAreaElement,
5273 )) => {
5274 let element = self.downcast::<HTMLTextAreaElement>().unwrap();
5275 Some(element as &dyn Validatable)
5276 },
5277 NodeTypeId::Element(ElementTypeId::HTMLElement(
5278 HTMLElementTypeId::HTMLFieldSetElement,
5279 )) => {
5280 let element = self.downcast::<HTMLFieldSetElement>().unwrap();
5281 Some(element as &dyn Validatable)
5282 },
5283 NodeTypeId::Element(ElementTypeId::HTMLElement(
5284 HTMLElementTypeId::HTMLOutputElement,
5285 )) => {
5286 let element = self.downcast::<HTMLOutputElement>().unwrap();
5287 Some(element as &dyn Validatable)
5288 },
5289 _ => None,
5290 }
5291 }
5292
5293 pub(crate) fn is_invalid(&self, needs_update: bool, can_gc: CanGc) -> bool {
5294 if let Some(validatable) = self.as_maybe_validatable() {
5295 if needs_update {
5296 validatable
5297 .validity_state(can_gc)
5298 .perform_validation_and_update(ValidationFlags::all(), can_gc);
5299 }
5300 return validatable.is_instance_validatable() &&
5301 !validatable.satisfies_constraints(can_gc);
5302 }
5303
5304 if let Some(internals) = self.get_element_internals() {
5305 return internals.is_invalid(can_gc);
5306 }
5307 false
5308 }
5309
5310 pub(crate) fn is_instance_validatable(&self) -> bool {
5311 if let Some(validatable) = self.as_maybe_validatable() {
5312 return validatable.is_instance_validatable();
5313 }
5314 if let Some(internals) = self.get_element_internals() {
5315 return internals.is_instance_validatable();
5316 }
5317 false
5318 }
5319
5320 pub(crate) fn init_state_for_internals(&self) {
5321 self.set_enabled_state(true);
5322 self.set_state(ElementState::VALID, true);
5323 self.set_state(ElementState::INVALID, false);
5324 }
5325
5326 pub(crate) fn click_in_progress(&self) -> bool {
5327 self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
5328 }
5329
5330 pub(crate) fn set_click_in_progress(&self, click: bool) {
5331 self.upcast::<Node>()
5332 .set_flag(NodeFlags::CLICK_IN_PROGRESS, click)
5333 }
5334
5335 pub(crate) fn nearest_activable_element(&self) -> Option<DomRoot<Element>> {
5337 match self.as_maybe_activatable() {
5338 Some(el) => Some(DomRoot::from_ref(el.as_element())),
5339 None => {
5340 let node = self.upcast::<Node>();
5341 for node in node.ancestors() {
5342 if let Some(node) = node.downcast::<Element>() {
5343 if node.as_maybe_activatable().is_some() {
5344 return Some(DomRoot::from_ref(node));
5345 }
5346 }
5347 }
5348 None
5349 },
5350 }
5351 }
5352
5353 pub fn state(&self) -> ElementState {
5354 self.state.get()
5355 }
5356
5357 pub(crate) fn set_state(&self, which: ElementState, value: bool) {
5358 let mut state = self.state.get();
5359 let previous_state = state;
5360 if value {
5361 state.insert(which);
5362 } else {
5363 state.remove(which);
5364 }
5365
5366 if previous_state == state {
5367 return;
5369 }
5370
5371 {
5374 let document = self.owner_document();
5375 let mut entry = document.ensure_pending_restyle(self);
5376 if entry.snapshot.is_none() {
5377 entry.snapshot = Some(Snapshot::new());
5378 }
5379 let snapshot = entry.snapshot.as_mut().unwrap();
5380 if snapshot.state.is_none() {
5381 snapshot.state = Some(self.state());
5382 }
5383 }
5384
5385 self.state.set(state);
5386 }
5387
5388 pub(crate) fn set_active_state(&self, value: bool) {
5390 self.set_state(ElementState::ACTIVE, value);
5391
5392 if let Some(parent) = self.upcast::<Node>().GetParentElement() {
5393 parent.set_active_state(value);
5394 }
5395 }
5396
5397 pub(crate) fn focus_state(&self) -> bool {
5398 self.state.get().contains(ElementState::FOCUS)
5399 }
5400
5401 pub(crate) fn set_focus_state(&self, value: bool) {
5402 self.set_state(ElementState::FOCUS, value);
5403 }
5404
5405 pub(crate) fn hover_state(&self) -> bool {
5406 self.state.get().contains(ElementState::HOVER)
5407 }
5408
5409 pub(crate) fn set_hover_state(&self, value: bool) {
5410 self.set_state(ElementState::HOVER, value);
5411 }
5412
5413 pub(crate) fn enabled_state(&self) -> bool {
5414 self.state.get().contains(ElementState::ENABLED)
5415 }
5416
5417 pub(crate) fn set_enabled_state(&self, value: bool) {
5418 self.set_state(ElementState::ENABLED, value)
5419 }
5420
5421 pub(crate) fn disabled_state(&self) -> bool {
5422 self.state.get().contains(ElementState::DISABLED)
5423 }
5424
5425 pub(crate) fn set_disabled_state(&self, value: bool) {
5426 self.set_state(ElementState::DISABLED, value)
5427 }
5428
5429 pub(crate) fn read_write_state(&self) -> bool {
5430 self.state.get().contains(ElementState::READWRITE)
5431 }
5432
5433 pub(crate) fn set_read_write_state(&self, value: bool) {
5434 self.set_state(ElementState::READWRITE, value)
5435 }
5436
5437 pub(crate) fn open_state(&self) -> bool {
5438 self.state.get().contains(ElementState::OPEN)
5439 }
5440
5441 pub(crate) fn set_open_state(&self, value: bool) {
5442 self.set_state(ElementState::OPEN, value);
5443 }
5444
5445 pub(crate) fn placeholder_shown_state(&self) -> bool {
5446 self.state.get().contains(ElementState::PLACEHOLDER_SHOWN)
5447 }
5448
5449 pub(crate) fn set_placeholder_shown_state(&self, value: bool) {
5450 self.set_state(ElementState::PLACEHOLDER_SHOWN, value);
5451 }
5452
5453 pub(crate) fn set_modal_state(&self, value: bool) {
5454 self.set_state(ElementState::MODAL, value);
5455 }
5456
5457 pub(crate) fn set_target_state(&self, value: bool) {
5458 self.set_state(ElementState::URLTARGET, value)
5459 }
5460
5461 pub(crate) fn set_fullscreen_state(&self, value: bool) {
5462 self.set_state(ElementState::FULLSCREEN, value)
5463 }
5464
5465 pub(crate) fn is_connected(&self) -> bool {
5467 self.upcast::<Node>().is_connected()
5468 }
5469
5470 pub(crate) fn cannot_navigate(&self) -> bool {
5472 let document = self.owner_document();
5473
5474 !document.is_fully_active() ||
5476 (
5477 !self.is::<HTMLAnchorElement>() && !self.is_connected()
5479 )
5480 }
5481}
5482
5483impl Element {
5484 pub(crate) fn check_ancestors_disabled_state_for_form_control(&self) {
5485 let node = self.upcast::<Node>();
5486 if self.disabled_state() {
5487 return;
5488 }
5489 for ancestor in node.ancestors() {
5490 if !ancestor.is::<HTMLFieldSetElement>() {
5491 continue;
5492 }
5493 if !ancestor.downcast::<Element>().unwrap().disabled_state() {
5494 continue;
5495 }
5496 if ancestor.is_parent_of(node) {
5497 self.set_disabled_state(true);
5498 self.set_enabled_state(false);
5499 return;
5500 }
5501 if let Some(ref legend) = ancestor.children().find(|n| n.is::<HTMLLegendElement>()) {
5502 if node.ancestors().any(|ancestor| ancestor == *legend) {
5504 continue;
5505 }
5506 }
5507 self.set_disabled_state(true);
5508 self.set_enabled_state(false);
5509 return;
5510 }
5511 }
5512
5513 pub(crate) fn check_parent_disabled_state_for_option(&self) {
5514 if self.disabled_state() {
5515 return;
5516 }
5517 let node = self.upcast::<Node>();
5518 if let Some(ref parent) = node.GetParentNode() {
5519 if parent.is::<HTMLOptGroupElement>() &&
5520 parent.downcast::<Element>().unwrap().disabled_state()
5521 {
5522 self.set_disabled_state(true);
5523 self.set_enabled_state(false);
5524 }
5525 }
5526 }
5527
5528 pub(crate) fn check_disabled_attribute(&self) {
5529 let has_disabled_attrib = self.has_attribute(&local_name!("disabled"));
5530 self.set_disabled_state(has_disabled_attrib);
5531 self.set_enabled_state(!has_disabled_attrib);
5532 }
5533
5534 pub(crate) fn update_read_write_state_from_readonly_attribute(&self) {
5535 let has_readonly_attribute = self.has_attribute(&local_name!("readonly"));
5536 self.set_read_write_state(has_readonly_attribute);
5537 }
5538}
5539
5540#[derive(Clone, Copy, PartialEq)]
5541pub(crate) enum AttributeMutationReason {
5542 ByCloning,
5543 ByParser,
5544 Directly,
5545}
5546
5547#[derive(Clone, Copy)]
5548pub(crate) enum AttributeMutation<'a> {
5549 Set(Option<&'a AttrValue>, AttributeMutationReason),
5552
5553 Removed,
5556}
5557
5558impl AttributeMutation<'_> {
5559 pub(crate) fn is_removal(&self) -> bool {
5560 match *self {
5561 AttributeMutation::Removed => true,
5562 AttributeMutation::Set(..) => false,
5563 }
5564 }
5565
5566 pub(crate) fn new_value<'b>(&self, attr: &'b Attr) -> Option<Ref<'b, AttrValue>> {
5567 match *self {
5568 AttributeMutation::Set(..) => Some(attr.value()),
5569 AttributeMutation::Removed => None,
5570 }
5571 }
5572}
5573
5574#[derive(JSTraceable, MallocSizeOf)]
5578struct TagName {
5579 #[no_trace]
5580 ptr: DomRefCell<Option<LocalName>>,
5581}
5582
5583impl TagName {
5584 fn new() -> TagName {
5585 TagName {
5586 ptr: DomRefCell::new(None),
5587 }
5588 }
5589
5590 fn or_init<F>(&self, cb: F) -> LocalName
5593 where
5594 F: FnOnce() -> LocalName,
5595 {
5596 match &mut *self.ptr.borrow_mut() {
5597 &mut Some(ref name) => name.clone(),
5598 ptr => {
5599 let name = cb();
5600 *ptr = Some(name.clone());
5601 name
5602 },
5603 }
5604 }
5605
5606 fn clear(&self) {
5609 *self.ptr.borrow_mut() = None;
5610 }
5611}
5612
5613pub(crate) struct ElementPerformFullscreenEnter {
5614 element: Trusted<Element>,
5615 document: Trusted<Document>,
5616 promise: TrustedPromise,
5617 error: bool,
5618}
5619
5620impl ElementPerformFullscreenEnter {
5621 pub(crate) fn new(
5622 element: Trusted<Element>,
5623 document: Trusted<Document>,
5624 promise: TrustedPromise,
5625 error: bool,
5626 ) -> Box<ElementPerformFullscreenEnter> {
5627 Box::new(ElementPerformFullscreenEnter {
5628 element,
5629 document,
5630 promise,
5631 error,
5632 })
5633 }
5634}
5635
5636impl TaskOnce for ElementPerformFullscreenEnter {
5637 fn run_once(self, cx: &mut js::context::JSContext) {
5639 let element = self.element.root();
5640 let promise = self.promise.root();
5641 let document = element.owner_document();
5642
5643 if self.document.root() != document ||
5652 !element.fullscreen_element_ready_check() ||
5653 self.error
5654 {
5655 document
5657 .upcast::<EventTarget>()
5658 .fire_event(atom!("fullscreenerror"), CanGc::from_cx(cx));
5659 promise.reject_error(
5660 Error::Type(c"fullscreen is not connected".to_owned()),
5661 CanGc::from_cx(cx),
5662 );
5663 return;
5664 }
5665
5666 element.set_fullscreen_state(true);
5669 document.set_fullscreen_element(Some(&element));
5670 document.upcast::<EventTarget>().fire_event_with_params(
5671 atom!("fullscreenchange"),
5672 EventBubbles::Bubbles,
5673 EventCancelable::NotCancelable,
5674 EventComposed::Composed,
5675 CanGc::from_cx(cx),
5676 );
5677
5678 promise.resolve_native(&(), CanGc::from_cx(cx));
5681 }
5682}
5683
5684pub(crate) struct ElementPerformFullscreenExit {
5685 element: Trusted<Element>,
5686 promise: TrustedPromise,
5687}
5688
5689impl ElementPerformFullscreenExit {
5690 pub(crate) fn new(
5691 element: Trusted<Element>,
5692 promise: TrustedPromise,
5693 ) -> Box<ElementPerformFullscreenExit> {
5694 Box::new(ElementPerformFullscreenExit { element, promise })
5695 }
5696}
5697
5698impl TaskOnce for ElementPerformFullscreenExit {
5699 fn run_once(self, cx: &mut js::context::JSContext) {
5701 let element = self.element.root();
5702 let document = element.owner_document();
5703 element.set_fullscreen_state(false);
5710 document.set_fullscreen_element(None);
5711 document.upcast::<EventTarget>().fire_event_with_params(
5712 atom!("fullscreenchange"),
5713 EventBubbles::Bubbles,
5714 EventCancelable::NotCancelable,
5715 EventComposed::Composed,
5716 CanGc::from_cx(cx),
5717 );
5718
5719 self.promise.root().resolve_native(&(), CanGc::from_cx(cx));
5722 }
5723}
5724
5725pub(crate) fn reflect_cross_origin_attribute(element: &Element) -> Option<DOMString> {
5727 element
5728 .get_attribute(&local_name!("crossorigin"))
5729 .map(|attribute| {
5730 let value = attribute.value().to_ascii_lowercase();
5731 if value == "anonymous" || value == "use-credentials" {
5732 DOMString::from(value)
5733 } else {
5734 DOMString::from("anonymous")
5735 }
5736 })
5737}
5738
5739pub(crate) fn set_cross_origin_attribute(
5740 cx: &mut JSContext,
5741 element: &Element,
5742 value: Option<DOMString>,
5743) {
5744 match value {
5745 Some(val) => {
5746 element.set_string_attribute(&local_name!("crossorigin"), val, CanGc::from_cx(cx))
5747 },
5748 None => {
5749 element.remove_attribute(&ns!(), &local_name!("crossorigin"), CanGc::from_cx(cx));
5750 },
5751 }
5752}
5753
5754pub(crate) fn reflect_referrer_policy_attribute(element: &Element) -> DOMString {
5756 element
5757 .get_attribute(&local_name!("referrerpolicy"))
5758 .map(|attribute| {
5759 let value = attribute.value().to_ascii_lowercase();
5760 if value == "no-referrer" ||
5761 value == "no-referrer-when-downgrade" ||
5762 value == "same-origin" ||
5763 value == "origin" ||
5764 value == "strict-origin" ||
5765 value == "origin-when-cross-origin" ||
5766 value == "strict-origin-when-cross-origin" ||
5767 value == "unsafe-url"
5768 {
5769 DOMString::from(value)
5770 } else {
5771 DOMString::new()
5772 }
5773 })
5774 .unwrap_or_default()
5775}
5776
5777pub(crate) fn referrer_policy_for_element(element: &Element) -> ReferrerPolicy {
5778 element
5779 .get_attribute(&local_name!("referrerpolicy"))
5780 .map(|attribute| ReferrerPolicy::from(&**attribute.value()))
5781 .unwrap_or(element.owner_document().get_referrer_policy())
5782}
5783
5784pub(crate) fn cors_setting_for_element(element: &Element) -> Option<CorsSettings> {
5785 element
5786 .get_attribute(&local_name!("crossorigin"))
5787 .map(|attribute| CorsSettings::from_enumerated_attribute(&attribute.value()))
5788}
5789
5790pub(crate) fn is_element_affected_by_legacy_background_presentational_hint(
5791 namespace: &Namespace,
5792 local_name: &LocalName,
5793) -> bool {
5794 *namespace == ns!(html) &&
5795 matches!(
5796 *local_name,
5797 local_name!("body") |
5798 local_name!("table") |
5799 local_name!("thead") |
5800 local_name!("tbody") |
5801 local_name!("tfoot") |
5802 local_name!("tr") |
5803 local_name!("td") |
5804 local_name!("th")
5805 )
5806}
5807
5808#[derive(Clone, Copy, Debug, Default)]
5810pub(crate) struct FocusableAreaKind(u8);
5811
5812bitflags! {
5813 impl FocusableAreaKind: u8 {
5814 const Click = 1 << 0;
5820 const Sequential = 1 << 1;
5826 }
5827}