script/dom/
element.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Element nodes.
6
7use 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::{fmt, mem};
14
15use app_units::Au;
16use cssparser::match_ignore_ascii_case;
17use devtools_traits::AttrInfo;
18use dom_struct::dom_struct;
19use euclid::default::{Rect, Size2D};
20use html5ever::serialize::TraversalScope;
21use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
22use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns};
23use js::jsapi::{Heap, JSAutoRealm};
24use js::jsval::JSVal;
25use js::rust::HandleObject;
26use layout_api::{LayoutDamage, ScrollContainerQueryFlags};
27use net_traits::ReferrerPolicy;
28use net_traits::request::CorsSettings;
29use selectors::Element as SelectorsElement;
30use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
31use selectors::bloom::{BLOOM_HASH_MASK, BloomFilter};
32use selectors::matching::{ElementSelectorFlags, MatchingContext};
33use selectors::sink::Push;
34use servo_arc::Arc;
35use style::applicable_declarations::ApplicableDeclarationBlock;
36use style::attr::{AttrValue, LengthOrPercentageOrAuto};
37use style::context::QuirksMode;
38use style::invalidation::element::restyle_hints::RestyleHint;
39use style::properties::longhands::{
40    self, background_image, border_spacing, font_family, font_size,
41};
42use style::properties::{
43    ComputedValues, Importance, PropertyDeclaration, PropertyDeclarationBlock,
44    parse_style_attribute,
45};
46use style::rule_tree::CascadeLevel;
47use style::selector_parser::{
48    NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser, Snapshot,
49    extended_filtering,
50};
51use style::shared_lock::Locked;
52use style::stylesheets::layer_rule::LayerOrder;
53use style::stylesheets::{CssRuleType, UrlExtraData};
54use style::values::computed::Overflow;
55use style::values::generics::NonNegative;
56use style::values::generics::position::PreferredRatio;
57use style::values::generics::ratio::Ratio;
58use style::values::{AtomIdent, AtomString, CSSFloat, computed, specified};
59use style::{ArcSlice, CaseSensitivityExt, dom_apis, thread_state};
60use stylo_atoms::Atom;
61use stylo_dom::ElementState;
62use xml5ever::serialize::TraversalScope::{
63    ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode,
64};
65
66use crate::conversions::Convert;
67use crate::dom::activation::Activatable;
68use crate::dom::attr::{Attr, AttrHelpersForLayout, is_relevant_attribute};
69use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut};
70use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
71use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
72use crate::dom::bindings::codegen::Bindings::ElementBinding::{
73    ElementMethods, GetHTMLOptions, ScrollIntoViewContainer, ScrollLogicalPosition, ShadowRootInit,
74};
75use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
76use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
77use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
78use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
79    ShadowRootMethods, ShadowRootMode, SlotAssignmentMode,
80};
81use crate::dom::bindings::codegen::Bindings::WindowBinding::{
82    ScrollBehavior, ScrollToOptions, WindowMethods,
83};
84use crate::dom::bindings::codegen::UnionTypes::{
85    BooleanOrScrollIntoViewOptions, NodeOrString, TrustedHTMLOrNullIsEmptyString,
86    TrustedHTMLOrString,
87    TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString,
88    TrustedScriptURLOrUSVString,
89};
90use crate::dom::bindings::conversions::DerivedFrom;
91use crate::dom::bindings::domname::{
92    self, is_valid_attribute_local_name, namespace_from_domstring,
93};
94use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
95use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
96use crate::dom::bindings::num::Finite;
97use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
98use crate::dom::bindings::reflector::DomObject;
99use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout};
100use crate::dom::bindings::str::{DOMString, USVString};
101use crate::dom::bindings::xmlname::matches_name_production;
102use crate::dom::characterdata::CharacterData;
103use crate::dom::create::create_element;
104use crate::dom::csp::{CspReporting, InlineCheckType, SourcePosition};
105use crate::dom::customelementregistry::{
106    CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementState,
107    is_valid_custom_element_name,
108};
109use crate::dom::document::{Document, LayoutDocumentHelpers};
110use crate::dom::documentfragment::DocumentFragment;
111use crate::dom::domrect::DOMRect;
112use crate::dom::domrectlist::DOMRectList;
113use crate::dom::domtokenlist::DOMTokenList;
114use crate::dom::elementinternals::ElementInternals;
115use crate::dom::eventtarget::EventTarget;
116use crate::dom::globalscope::GlobalScope;
117use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
118use crate::dom::html::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
119use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
120use crate::dom::html::htmlcollection::HTMLCollection;
121use crate::dom::html::htmlelement::HTMLElement;
122use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
123use crate::dom::html::htmlfontelement::{HTMLFontElement, HTMLFontElementLayoutHelpers};
124use crate::dom::html::htmlformelement::FormControlElementHelpers;
125use crate::dom::html::htmlhrelement::{HTMLHRElement, HTMLHRLayoutHelpers, SizePresentationalHint};
126use crate::dom::html::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods};
127use crate::dom::html::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
128use crate::dom::html::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
129use crate::dom::html::htmllabelelement::HTMLLabelElement;
130use crate::dom::html::htmllegendelement::HTMLLegendElement;
131use crate::dom::html::htmllinkelement::HTMLLinkElement;
132use crate::dom::html::htmlobjectelement::HTMLObjectElement;
133use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
134use crate::dom::html::htmloutputelement::HTMLOutputElement;
135use crate::dom::html::htmlscriptelement::HTMLScriptElement;
136use crate::dom::html::htmlselectelement::HTMLSelectElement;
137use crate::dom::html::htmlslotelement::{HTMLSlotElement, Slottable};
138use crate::dom::html::htmlstyleelement::HTMLStyleElement;
139use crate::dom::html::htmltablecellelement::{
140    HTMLTableCellElement, HTMLTableCellElementLayoutHelpers,
141};
142use crate::dom::html::htmltablecolelement::{
143    HTMLTableColElement, HTMLTableColElementLayoutHelpers,
144};
145use crate::dom::html::htmltableelement::{HTMLTableElement, HTMLTableElementLayoutHelpers};
146use crate::dom::html::htmltablerowelement::{
147    HTMLTableRowElement, HTMLTableRowElementLayoutHelpers,
148};
149use crate::dom::html::htmltablesectionelement::{
150    HTMLTableSectionElement, HTMLTableSectionElementLayoutHelpers,
151};
152use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
153use crate::dom::html::htmltextareaelement::{
154    HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers,
155};
156use crate::dom::html::htmlvideoelement::{HTMLVideoElement, LayoutHTMLVideoElementHelpers};
157use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserverRegistration};
158use crate::dom::mutationobserver::{Mutation, MutationObserver};
159use crate::dom::namednodemap::NamedNodeMap;
160use crate::dom::node::{
161    BindContext, ChildrenMutation, CloneChildrenFlag, IsShadowTree, LayoutNodeHelpers, Node,
162    NodeDamage, NodeFlags, NodeTraits, ShadowIncluding, UnbindContext,
163};
164use crate::dom::nodelist::NodeList;
165use crate::dom::promise::Promise;
166use crate::dom::raredata::ElementRareData;
167use crate::dom::scrolling_box::ScrollingBox;
168use crate::dom::servoparser::ServoParser;
169use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
170use crate::dom::text::Text;
171use crate::dom::trustedhtml::TrustedHTML;
172use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
173use crate::dom::validation::Validatable;
174use crate::dom::validitystate::ValidationFlags;
175use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
176use crate::script_runtime::CanGc;
177use crate::script_thread::ScriptThread;
178use crate::stylesheet_loader::StylesheetOwner;
179use crate::task::TaskOnce;
180
181// TODO: Update focus state when the top-level browsing context gains or loses system focus,
182// and when the element enters or leaves a browsing context container.
183// https://html.spec.whatwg.org/multipage/#selector-focus
184
185/// <https://dom.spec.whatwg.org/#element>
186#[dom_struct]
187pub struct Element {
188    node: Node,
189    #[no_trace]
190    local_name: LocalName,
191    tag_name: TagName,
192    #[no_trace]
193    namespace: Namespace,
194    #[no_trace]
195    prefix: DomRefCell<Option<Prefix>>,
196    attrs: DomRefCell<Vec<Dom<Attr>>>,
197    #[no_trace]
198    id_attribute: DomRefCell<Option<Atom>>,
199    /// <https://dom.spec.whatwg.org/#concept-element-is-value>
200    #[no_trace]
201    is: DomRefCell<Option<LocalName>>,
202    #[conditional_malloc_size_of]
203    #[no_trace]
204    style_attribute: DomRefCell<Option<Arc<Locked<PropertyDeclarationBlock>>>>,
205    attr_list: MutNullableDom<NamedNodeMap>,
206    class_list: MutNullableDom<DOMTokenList>,
207    #[no_trace]
208    state: Cell<ElementState>,
209    /// These flags are set by the style system to indicate the that certain
210    /// operations may require restyling this element or its descendants. The
211    /// flags are not atomic, so the style system takes care of only set them
212    /// when it has exclusive access to the element.
213    #[ignore_malloc_size_of = "bitflags defined in rust-selectors"]
214    #[no_trace]
215    selector_flags: Cell<ElementSelectorFlags>,
216    rare_data: DomRefCell<Option<Box<ElementRareData>>>,
217}
218
219impl fmt::Debug for Element {
220    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221        write!(f, "<{}", self.local_name)?;
222        if let Some(ref id) = *self.id_attribute.borrow() {
223            write!(f, " id={}", id)?;
224        }
225        write!(f, ">")
226    }
227}
228
229#[derive(MallocSizeOf, PartialEq)]
230pub(crate) enum ElementCreator {
231    ParserCreated(u64),
232    ScriptCreated,
233}
234
235pub(crate) enum CustomElementCreationMode {
236    Synchronous,
237    Asynchronous,
238}
239
240impl ElementCreator {
241    pub(crate) fn is_parser_created(&self) -> bool {
242        match *self {
243            ElementCreator::ParserCreated(_) => true,
244            ElementCreator::ScriptCreated => false,
245        }
246    }
247    pub(crate) fn return_line_number(&self) -> u64 {
248        match *self {
249            ElementCreator::ParserCreated(l) => l,
250            ElementCreator::ScriptCreated => 1,
251        }
252    }
253}
254
255pub(crate) enum AdjacentPosition {
256    BeforeBegin,
257    AfterEnd,
258    AfterBegin,
259    BeforeEnd,
260}
261
262impl FromStr for AdjacentPosition {
263    type Err = Error;
264
265    fn from_str(position: &str) -> Result<Self, Self::Err> {
266        match_ignore_ascii_case! { position,
267            "beforebegin" => Ok(AdjacentPosition::BeforeBegin),
268            "afterbegin"  => Ok(AdjacentPosition::AfterBegin),
269            "beforeend"   => Ok(AdjacentPosition::BeforeEnd),
270            "afterend"    => Ok(AdjacentPosition::AfterEnd),
271            _             => Err(Error::Syntax(None))
272        }
273    }
274}
275
276//
277// Element methods
278//
279impl Element {
280    pub(crate) fn create(
281        name: QualName,
282        is: Option<LocalName>,
283        document: &Document,
284        creator: ElementCreator,
285        mode: CustomElementCreationMode,
286        proto: Option<HandleObject>,
287        can_gc: CanGc,
288    ) -> DomRoot<Element> {
289        create_element(name, is, document, creator, mode, proto, can_gc)
290    }
291
292    pub(crate) fn new_inherited(
293        local_name: LocalName,
294        namespace: Namespace,
295        prefix: Option<Prefix>,
296        document: &Document,
297    ) -> Element {
298        Element::new_inherited_with_state(
299            ElementState::empty(),
300            local_name,
301            namespace,
302            prefix,
303            document,
304        )
305    }
306
307    pub(crate) fn new_inherited_with_state(
308        state: ElementState,
309        local_name: LocalName,
310        namespace: Namespace,
311        prefix: Option<Prefix>,
312        document: &Document,
313    ) -> Element {
314        Element {
315            node: Node::new_inherited(document),
316            local_name,
317            tag_name: TagName::new(),
318            namespace,
319            prefix: DomRefCell::new(prefix),
320            attrs: DomRefCell::new(vec![]),
321            id_attribute: DomRefCell::new(None),
322            is: DomRefCell::new(None),
323            style_attribute: DomRefCell::new(None),
324            attr_list: Default::default(),
325            class_list: Default::default(),
326            state: Cell::new(state),
327            selector_flags: Cell::new(ElementSelectorFlags::empty()),
328            rare_data: Default::default(),
329        }
330    }
331
332    pub(crate) fn new(
333        local_name: LocalName,
334        namespace: Namespace,
335        prefix: Option<Prefix>,
336        document: &Document,
337        proto: Option<HandleObject>,
338        can_gc: CanGc,
339    ) -> DomRoot<Element> {
340        Node::reflect_node_with_proto(
341            Box::new(Element::new_inherited(
342                local_name, namespace, prefix, document,
343            )),
344            document,
345            proto,
346            can_gc,
347        )
348    }
349
350    fn rare_data(&self) -> Ref<'_, Option<Box<ElementRareData>>> {
351        self.rare_data.borrow()
352    }
353
354    fn rare_data_mut(&self) -> RefMut<'_, Option<Box<ElementRareData>>> {
355        self.rare_data.borrow_mut()
356    }
357
358    fn ensure_rare_data(&self) -> RefMut<'_, Box<ElementRareData>> {
359        let mut rare_data = self.rare_data.borrow_mut();
360        if rare_data.is_none() {
361            *rare_data = Some(Default::default());
362        }
363        RefMut::map(rare_data, |rare_data| rare_data.as_mut().unwrap())
364    }
365
366    pub(crate) fn restyle(&self, damage: NodeDamage) {
367        let doc = self.node.owner_doc();
368        let mut restyle = doc.ensure_pending_restyle(self);
369
370        // FIXME(bholley): I think we should probably only do this for
371        // NodeStyleDamaged, but I'm preserving existing behavior.
372        restyle.hint.insert(RestyleHint::RESTYLE_SELF);
373
374        match damage {
375            NodeDamage::Style => {},
376            NodeDamage::ContentOrHeritage => {
377                doc.note_node_with_dirty_descendants(self.upcast());
378                restyle
379                    .damage
380                    .insert(LayoutDamage::recollect_box_tree_children());
381            },
382            NodeDamage::Other => {
383                doc.note_node_with_dirty_descendants(self.upcast());
384                restyle.damage.insert(RestyleDamage::reconstruct());
385            },
386        }
387    }
388
389    pub(crate) fn set_is(&self, is: LocalName) {
390        *self.is.borrow_mut() = Some(is);
391    }
392
393    /// <https://dom.spec.whatwg.org/#concept-element-is-value>
394    pub(crate) fn get_is(&self) -> Option<LocalName> {
395        self.is.borrow().clone()
396    }
397
398    /// This is a performance optimization. `Element::create` can simply call
399    /// `element.set_custom_element_state(CustomElementState::Uncustomized)` to initialize
400    /// uncustomized, built-in elements with the right state, which currently just means that the
401    /// `DEFINED` state should be `true` for styling. However `set_custom_element_state` has a high
402    /// performance cost and it is unnecessary if the element is being created as an uncustomized
403    /// built-in element.
404    ///
405    /// See <https://github.com/servo/servo/issues/37745> for more details.
406    pub(crate) fn set_initial_custom_element_state_to_uncustomized(&self) {
407        let mut state = self.state.get();
408        state.insert(ElementState::DEFINED);
409        self.state.set(state);
410    }
411
412    /// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
413    pub(crate) fn set_custom_element_state(&self, state: CustomElementState) {
414        // no need to inflate rare data for uncustomized
415        if state != CustomElementState::Uncustomized {
416            self.ensure_rare_data().custom_element_state = state;
417        }
418
419        let in_defined_state = matches!(
420            state,
421            CustomElementState::Uncustomized | CustomElementState::Custom
422        );
423        self.set_state(ElementState::DEFINED, in_defined_state)
424    }
425
426    pub(crate) fn get_custom_element_state(&self) -> CustomElementState {
427        if let Some(rare_data) = self.rare_data().as_ref() {
428            return rare_data.custom_element_state;
429        }
430        CustomElementState::Uncustomized
431    }
432
433    /// <https://dom.spec.whatwg.org/#concept-element-custom>
434    pub(crate) fn is_custom(&self) -> bool {
435        self.get_custom_element_state() == CustomElementState::Custom
436    }
437
438    pub(crate) fn set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>) {
439        self.ensure_rare_data().custom_element_definition = Some(definition);
440    }
441
442    pub(crate) fn get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>> {
443        self.rare_data().as_ref()?.custom_element_definition.clone()
444    }
445
446    pub(crate) fn clear_custom_element_definition(&self) {
447        self.ensure_rare_data().custom_element_definition = None;
448    }
449
450    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
451    pub(crate) fn push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>) {
452        self.ensure_rare_data()
453            .custom_element_reaction_queue
454            .push(CustomElementReaction::Callback(function, args));
455    }
456
457    pub(crate) fn push_upgrade_reaction(&self, definition: Rc<CustomElementDefinition>) {
458        self.ensure_rare_data()
459            .custom_element_reaction_queue
460            .push(CustomElementReaction::Upgrade(definition));
461    }
462
463    pub(crate) fn clear_reaction_queue(&self) {
464        if let Some(ref mut rare_data) = *self.rare_data_mut() {
465            rare_data.custom_element_reaction_queue.clear();
466        }
467    }
468
469    pub(crate) fn invoke_reactions(&self, can_gc: CanGc) {
470        loop {
471            rooted_vec!(let mut reactions);
472            match *self.rare_data_mut() {
473                Some(ref mut data) => {
474                    mem::swap(&mut *reactions, &mut data.custom_element_reaction_queue)
475                },
476                None => break,
477            };
478
479            if reactions.is_empty() {
480                break;
481            }
482
483            for reaction in reactions.iter() {
484                reaction.invoke(self, can_gc);
485            }
486
487            reactions.clear();
488        }
489    }
490
491    /// style will be `None` for elements in a `display: none` subtree. otherwise, the element has a
492    /// layout box iff it doesn't have `display: none`.
493    pub(crate) fn style(&self) -> Option<Arc<ComputedValues>> {
494        self.upcast::<Node>().style()
495    }
496
497    // https://drafts.csswg.org/cssom-view/#css-layout-box
498    pub(crate) fn has_css_layout_box(&self) -> bool {
499        self.style()
500            .is_some_and(|s| !s.get_box().clone_display().is_none())
501    }
502
503    /// <https://drafts.csswg.org/cssom-view/#potentially-scrollable>
504    pub(crate) fn is_potentially_scrollable_body(&self) -> bool {
505        self.is_potentially_scrollable_body_shared_logic(false)
506    }
507
508    /// <https://drafts.csswg.org/cssom-view/#potentially-scrollable>
509    pub(crate) fn is_potentially_scrollable_body_for_scrolling_element(&self) -> bool {
510        self.is_potentially_scrollable_body_shared_logic(true)
511    }
512
513    /// <https://drafts.csswg.org/cssom-view/#potentially-scrollable>
514    fn is_potentially_scrollable_body_shared_logic(
515        &self,
516        treat_overflow_clip_on_parent_as_hidden: bool,
517    ) -> bool {
518        let node = self.upcast::<Node>();
519        debug_assert!(
520            node.owner_doc().GetBody().as_deref() == self.downcast::<HTMLElement>(),
521            "Called is_potentially_scrollable_body on element that is not the <body>"
522        );
523
524        // "An element body (which will be the body element) is potentially
525        // scrollable if all of the following conditions are true:
526        //  - body has an associated box."
527        if !self.has_css_layout_box() {
528            return false;
529        }
530
531        // " - body’s parent element’s computed value of the overflow-x or
532        //     overflow-y properties is neither visible nor clip."
533        if let Some(parent) = node.GetParentElement() {
534            if let Some(style) = parent.style() {
535                let mut overflow_x = style.get_box().clone_overflow_x();
536                let mut overflow_y = style.get_box().clone_overflow_y();
537
538                // This fulfills the 'treat parent element overflow:clip as overflow:hidden' stipulation
539                // from the document.scrollingElement specification.
540                if treat_overflow_clip_on_parent_as_hidden {
541                    if overflow_x == Overflow::Clip {
542                        overflow_x = Overflow::Hidden;
543                    }
544                    if overflow_y == Overflow::Clip {
545                        overflow_y = Overflow::Hidden;
546                    }
547                }
548
549                if !overflow_x.is_scrollable() && !overflow_y.is_scrollable() {
550                    return false;
551                }
552            };
553        }
554
555        // " - body’s computed value of the overflow-x or overflow-y properties
556        //     is neither visible nor clip."
557        if let Some(style) = self.style() {
558            if !style.get_box().clone_overflow_x().is_scrollable() &&
559                !style.get_box().clone_overflow_y().is_scrollable()
560            {
561                return false;
562            }
563        };
564
565        true
566    }
567
568    /// <https://drafts.csswg.org/cssom-view/#scrolling-box>
569    fn has_scrolling_box(&self) -> bool {
570        // TODO: scrolling mechanism, such as scrollbar (We don't have scrollbar yet)
571        //       self.has_scrolling_mechanism()
572        self.style().is_some_and(|style| {
573            style.get_box().clone_overflow_x().is_scrollable() ||
574                style.get_box().clone_overflow_y().is_scrollable()
575        })
576    }
577
578    fn has_overflow(&self) -> bool {
579        self.ScrollHeight() > self.ClientHeight() || self.ScrollWidth() > self.ClientWidth()
580    }
581
582    pub(crate) fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
583        self.rare_data()
584            .as_ref()?
585            .shadow_root
586            .as_ref()
587            .map(|sr| DomRoot::from_ref(&**sr))
588    }
589
590    pub(crate) fn is_shadow_host(&self) -> bool {
591        self.shadow_root().is_some()
592    }
593
594    /// <https://dom.spec.whatwg.org/#dom-element-attachshadow>
595    #[allow(clippy::too_many_arguments)]
596    pub(crate) fn attach_shadow(
597        &self,
598        is_ua_widget: IsUserAgentWidget,
599        mode: ShadowRootMode,
600        clonable: bool,
601        serializable: bool,
602        delegates_focus: bool,
603        slot_assignment_mode: SlotAssignmentMode,
604        can_gc: CanGc,
605    ) -> Fallible<DomRoot<ShadowRoot>> {
606        // Step 1. If element’s namespace is not the HTML namespace,
607        // then throw a "NotSupportedError" DOMException.
608        if self.namespace != ns!(html) {
609            return Err(Error::NotSupported);
610        }
611
612        // Step 2. If element’s local name is not a valid shadow host name,
613        // then throw a "NotSupportedError" DOMException.
614        if !is_valid_shadow_host_name(self.local_name()) {
615            // UA shadow roots may be attached to anything
616            if is_ua_widget != IsUserAgentWidget::Yes {
617                return Err(Error::NotSupported);
618            }
619        }
620
621        // Step 3. If element’s local name is a valid custom element name,
622        // or element’s is value is non-null
623        if is_valid_custom_element_name(self.local_name()) || self.get_is().is_some() {
624            // Step 3.1. Let definition be the result of looking up a custom element definition
625            // given element’s node document, its namespace, its local name, and its is value.
626
627            let definition = self.get_custom_element_definition();
628            // Step 3.2. If definition is not null and definition’s disable shadow
629            //  is true, then throw a "NotSupportedError" DOMException.
630            if definition.is_some_and(|definition| definition.disable_shadow) {
631                return Err(Error::NotSupported);
632            }
633        }
634
635        // Step 4. If element is a shadow host:
636        // Step 4.1. Let currentShadowRoot be element’s shadow root.
637        if let Some(current_shadow_root) = self.shadow_root() {
638            // Step 4.2. If currentShadowRoot’s declarative is false
639            // or currentShadowRoot’s mode is not mode
640            // then throw a "NotSupportedError" DOMException.
641            if !current_shadow_root.is_declarative() ||
642                current_shadow_root.shadow_root_mode() != mode
643            {
644                return Err(Error::NotSupported);
645            }
646
647            // Step 4.3.1. Remove all of currentShadowRoot’s children, in tree order.
648            for child in current_shadow_root.upcast::<Node>().children() {
649                child.remove_self(can_gc);
650            }
651
652            // Step 4.3.2. Set currentShadowRoot’s declarative to false.
653            current_shadow_root.set_declarative(false);
654
655            // Step 4.3.3. Return
656            return Ok(current_shadow_root);
657        }
658
659        // Step 5. Let shadow be a new shadow root whose node document
660        // is element’s node document, host is element, and mode is mode
661        //
662        // Step 8. Set shadow’s slot assignment to slotAssignment
663        //
664        // Step 10. Set shadow’s clonable to clonable
665        let shadow_root = ShadowRoot::new(
666            self,
667            &self.node.owner_doc(),
668            mode,
669            slot_assignment_mode,
670            clonable,
671            is_ua_widget,
672            can_gc,
673        );
674
675        // Step 6. Set shadow's delegates focus to delegatesFocus
676        shadow_root.set_delegates_focus(delegates_focus);
677
678        // Step 7. If element’s custom element state is "precustomized" or "custom",
679        // then set shadow’s available to element internals to true.
680        if matches!(
681            self.get_custom_element_state(),
682            CustomElementState::Precustomized | CustomElementState::Custom
683        ) {
684            shadow_root.set_available_to_element_internals(true);
685        }
686
687        // Step 9. Set shadow's declarative to false
688        shadow_root.set_declarative(false);
689
690        // Step 11. Set shadow's serializable to serializable
691        shadow_root.set_serializable(serializable);
692
693        // Step 12. Set element’s shadow root to shadow
694        self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
695        shadow_root
696            .upcast::<Node>()
697            .set_containing_shadow_root(Some(&shadow_root));
698
699        let bind_context = BindContext::new(self.upcast(), IsShadowTree::Yes);
700        shadow_root.bind_to_tree(&bind_context, can_gc);
701
702        let node = self.upcast::<Node>();
703        node.dirty(NodeDamage::Other);
704
705        Ok(shadow_root)
706    }
707
708    /// Attach a UA widget shadow root with its default parameters.
709    /// Additionally mark ShadowRoot to use styling configuration for a UA widget.
710    ///
711    /// The general trait of these elements is that it would hide the implementation.
712    /// Thus, we would make it inaccessible (i.e., closed mode, not cloneable, and
713    /// not serializable).
714    ///
715    /// With UA shadow root element being assumed as one element, any focus should
716    /// be delegated to its host.
717    ///
718    // TODO: Ideally, all of the UA shadow root should use UA widget styling, but
719    //       some of the UA widget implemented prior to the implementation of Gecko's
720    //       UA widget matching might need some tweaking.
721    // FIXME: We are yet to implement more complex focusing with that is necessary
722    //        for delegate focus, and we are using workarounds for that right now.
723    pub(crate) fn attach_ua_shadow_root(
724        &self,
725        use_ua_widget_styling: bool,
726        can_gc: CanGc,
727    ) -> DomRoot<ShadowRoot> {
728        let root = self
729            .attach_shadow(
730                IsUserAgentWidget::Yes,
731                ShadowRootMode::Closed,
732                false,
733                false,
734                false,
735                SlotAssignmentMode::Manual,
736                can_gc,
737            )
738            .expect("Attaching UA shadow root failed");
739
740        root.upcast::<Node>()
741            .set_in_ua_widget(use_ua_widget_styling);
742        root
743    }
744
745    // https://html.spec.whatwg.org/multipage/#translation-mode
746    pub(crate) fn is_translate_enabled(&self) -> bool {
747        let name = &local_name!("translate");
748        if self.has_attribute(name) {
749            let attribute = self.get_string_attribute(name);
750            match_ignore_ascii_case! { &*attribute.str(),
751                "yes" | "" => return true,
752                "no" => return false,
753                _ => {},
754            }
755        }
756        if let Some(parent) = self.upcast::<Node>().GetParentNode() {
757            if let Some(elem) = parent.downcast::<Element>() {
758                return elem.is_translate_enabled();
759            }
760        }
761        true
762    }
763
764    // https://html.spec.whatwg.org/multipage/#the-directionality
765    pub(crate) fn directionality(&self) -> String {
766        self.downcast::<HTMLElement>()
767            .and_then(|html_element| html_element.directionality())
768            .unwrap_or_else(|| {
769                let node = self.upcast::<Node>();
770                node.parent_directionality()
771            })
772    }
773
774    pub(crate) fn is_root(&self) -> bool {
775        match self.node.GetParentNode() {
776            None => false,
777            Some(node) => node.is::<Document>(),
778        }
779    }
780
781    /// Return all IntersectionObserverRegistration for this element.
782    /// Lazily initialize the raredata if it does not exist.
783    pub(crate) fn registered_intersection_observers_mut(
784        &self,
785    ) -> RefMut<'_, Vec<IntersectionObserverRegistration>> {
786        RefMut::map(self.ensure_rare_data(), |rare_data| {
787            &mut rare_data.registered_intersection_observers
788        })
789    }
790
791    pub(crate) fn registered_intersection_observers(
792        &self,
793    ) -> Option<Ref<'_, Vec<IntersectionObserverRegistration>>> {
794        let rare_data: Ref<'_, _> = self.rare_data.borrow();
795
796        if rare_data.is_none() {
797            return None;
798        }
799        Some(Ref::map(rare_data, |rare_data| {
800            &rare_data
801                .as_ref()
802                .unwrap()
803                .registered_intersection_observers
804        }))
805    }
806
807    pub(crate) fn get_intersection_observer_registration(
808        &self,
809        observer: &IntersectionObserver,
810    ) -> Option<Ref<'_, IntersectionObserverRegistration>> {
811        if let Some(registrations) = self.registered_intersection_observers() {
812            registrations
813                .iter()
814                .position(|reg_obs| reg_obs.observer == observer)
815                .map(|index| Ref::map(registrations, |registrations| &registrations[index]))
816        } else {
817            None
818        }
819    }
820
821    /// Add a new IntersectionObserverRegistration with initial value to the element.
822    pub(crate) fn add_initial_intersection_observer_registration(
823        &self,
824        observer: &IntersectionObserver,
825    ) {
826        self.ensure_rare_data()
827            .registered_intersection_observers
828            .push(IntersectionObserverRegistration::new_initial(observer));
829    }
830
831    /// Removes a certain IntersectionObserver.
832    pub(crate) fn remove_intersection_observer(&self, observer: &IntersectionObserver) {
833        self.ensure_rare_data()
834            .registered_intersection_observers
835            .retain(|reg_obs| *reg_obs.observer != *observer)
836    }
837
838    /// Get the [`ScrollingBox`] that contains this element, if one does. `position:
839    /// fixed` elements do not have a containing [`ScrollingBox`].
840    pub(crate) fn scrolling_box(&self, flags: ScrollContainerQueryFlags) -> Option<ScrollingBox> {
841        self.owner_window()
842            .scrolling_box_query(Some(self.upcast()), flags)
843    }
844
845    /// <https://drafts.csswg.org/cssom-view/#scroll-a-target-into-view>
846    fn scroll_into_view_with_options(
847        &self,
848        behavior: ScrollBehavior,
849        block: ScrollLogicalPosition,
850        inline: ScrollLogicalPosition,
851        container: Option<&Element>,
852        inner_target_rect: Option<Rect<Au>>,
853    ) {
854        let get_target_rect = || match inner_target_rect {
855            None => self.upcast::<Node>().border_box().unwrap_or_default(),
856            Some(inner_target_rect) => inner_target_rect.translate(
857                self.upcast::<Node>()
858                    .content_box()
859                    .unwrap_or_default()
860                    .origin
861                    .to_vector(),
862            ),
863        };
864
865        // Step 1: For each ancestor element or viewport that establishes a scrolling box `scrolling
866        // box`, in order of innermost to outermost scrolling box, run these substeps:
867        let mut parent_scrolling_box = self.scrolling_box(ScrollContainerQueryFlags::empty());
868        while let Some(scrolling_box) = parent_scrolling_box {
869            parent_scrolling_box = scrolling_box.parent();
870
871            // Step 1.1: If the Document associated with `target` is not same origin with the
872            // Document associated with the element or viewport associated with `scrolling box`,
873            // terminate these steps.
874            //
875            // TODO: Handle this. We currently do not chain up to parent Documents.
876
877            // Step 1.2 Let `position` be the scroll position resulting from running the steps to
878            // determine the scroll-into-view position of `target` with `behavior` as the scroll
879            // behavior, `block` as the block flow position, `inline` as the inline base direction
880            // position and `scrolling box` as the scrolling box.
881            let position =
882                scrolling_box.determine_scroll_into_view_position(block, inline, get_target_rect());
883
884            // Step 1.3: If `position` is not the same as `scrolling box`’s current scroll position, or
885            // `scrolling box` has an ongoing smooth scroll,
886            //
887            // TODO: Handle smooth scrolling.
888            if position != scrolling_box.scroll_position() {
889                //  ↪ If `scrolling box` is associated with an element
890                //    Perform a scroll of the element’s scrolling box to `position`,
891                //    with the `element` as the associated element and `behavior` as the
892                //    scroll behavior.
893                //  ↪ If `scrolling box` is associated with a viewport
894                //    Step 1: Let `document` be the viewport’s associated Document.
895                //    Step 2: Let `root element` be document’s root element, if there is one, or
896                //    null otherwise.
897                //    Step 3: Perform a scroll of the viewport to `position`, with `root element`
898                //    as the associated element and `behavior` as the scroll behavior.
899                scrolling_box.scroll_to(position, behavior);
900            }
901
902            // Step 1.4: If `container` is not null and either `scrolling box` is a shadow-including
903            // inclusive ancestor of `container` or is a viewport whose document is a shadow-including
904            // inclusive ancestor of `container`, abort the rest of these steps.
905            if container.is_some_and(|container| {
906                let container_node = container.upcast::<Node>();
907                scrolling_box
908                    .node()
909                    .is_shadow_including_inclusive_ancestor_of(container_node)
910            }) {
911                return;
912            }
913        }
914
915        let window_proxy = self.owner_window().window_proxy();
916        let Some(frame_element) = window_proxy.frame_element() else {
917            return;
918        };
919
920        let inner_target_rect = Some(get_target_rect());
921        let parent_window = frame_element.owner_window();
922        let cx = GlobalScope::get_cx();
923        let _ac = JSAutoRealm::new(*cx, *parent_window.reflector().get_jsobject());
924        frame_element.scroll_into_view_with_options(
925            behavior,
926            block,
927            inline,
928            None,
929            inner_target_rect,
930        )
931    }
932}
933
934/// <https://dom.spec.whatwg.org/#valid-shadow-host-name>
935#[inline]
936pub(crate) fn is_valid_shadow_host_name(name: &LocalName) -> bool {
937    // > A valid shadow host name is:
938    // > - a valid custom element name
939    if is_valid_custom_element_name(name) {
940        return true;
941    }
942
943    // > - "article", "aside", "blockquote", "body", "div", "footer", "h1", "h2", "h3",
944    // >   "h4", "h5", "h6", "header", "main", "nav", "p", "section", or "span"
945    matches!(
946        name,
947        &local_name!("article") |
948            &local_name!("aside") |
949            &local_name!("blockquote") |
950            &local_name!("body") |
951            &local_name!("div") |
952            &local_name!("footer") |
953            &local_name!("h1") |
954            &local_name!("h2") |
955            &local_name!("h3") |
956            &local_name!("h4") |
957            &local_name!("h5") |
958            &local_name!("h6") |
959            &local_name!("header") |
960            &local_name!("main") |
961            &local_name!("nav") |
962            &local_name!("p") |
963            &local_name!("section") |
964            &local_name!("span")
965    )
966}
967
968#[inline]
969pub(crate) fn get_attr_for_layout<'dom>(
970    elem: LayoutDom<'dom, Element>,
971    namespace: &Namespace,
972    name: &LocalName,
973) -> Option<LayoutDom<'dom, Attr>> {
974    elem.attrs()
975        .iter()
976        .find(|attr| name == attr.local_name() && namespace == attr.namespace())
977        .cloned()
978}
979
980pub(crate) trait LayoutElementHelpers<'dom> {
981    fn attrs(self) -> &'dom [LayoutDom<'dom, Attr>];
982    fn has_class_or_part_for_layout(
983        self,
984        name: &AtomIdent,
985        attr_name: &LocalName,
986        case_sensitivity: CaseSensitivity,
987    ) -> bool;
988    fn get_classes_for_layout(self) -> Option<&'dom [Atom]>;
989    fn get_parts_for_layout(self) -> Option<&'dom [Atom]>;
990
991    fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
992    where
993        V: Push<ApplicableDeclarationBlock>;
994    fn get_span(self) -> Option<u32>;
995    fn get_colspan(self) -> Option<u32>;
996    fn get_rowspan(self) -> Option<u32>;
997    fn is_html_element(&self) -> bool;
998    fn id_attribute(self) -> *const Option<Atom>;
999    fn style_attribute(self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>>;
1000    fn local_name(self) -> &'dom LocalName;
1001    fn namespace(self) -> &'dom Namespace;
1002    fn get_lang_attr_val_for_layout(self) -> Option<&'dom str>;
1003    fn get_lang_for_layout(self) -> String;
1004    fn get_state_for_layout(self) -> ElementState;
1005    fn insert_selector_flags(self, flags: ElementSelectorFlags);
1006    fn get_selector_flags(self) -> ElementSelectorFlags;
1007    /// The shadow root this element is a host of.
1008    fn get_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>>;
1009    fn get_attr_for_layout(
1010        self,
1011        namespace: &Namespace,
1012        name: &LocalName,
1013    ) -> Option<&'dom AttrValue>;
1014    fn get_attr_val_for_layout(self, namespace: &Namespace, name: &LocalName) -> Option<&'dom str>;
1015    fn get_attr_vals_for_layout(self, name: &LocalName) -> Vec<&'dom AttrValue>;
1016    fn each_custom_state<F>(self, callback: F)
1017    where
1018        F: FnMut(&AtomIdent);
1019}
1020
1021impl LayoutDom<'_, Element> {
1022    pub(super) fn focus_state(self) -> bool {
1023        self.unsafe_get().state.get().contains(ElementState::FOCUS)
1024    }
1025}
1026
1027impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> {
1028    #[expect(unsafe_code)]
1029    #[inline]
1030    fn attrs(self) -> &'dom [LayoutDom<'dom, Attr>] {
1031        unsafe { LayoutDom::to_layout_slice(self.unsafe_get().attrs.borrow_for_layout()) }
1032    }
1033
1034    #[inline]
1035    fn has_class_or_part_for_layout(
1036        self,
1037        name: &AtomIdent,
1038        attr_name: &LocalName,
1039        case_sensitivity: CaseSensitivity,
1040    ) -> bool {
1041        get_attr_for_layout(self, &ns!(), attr_name).is_some_and(|attr| {
1042            attr.to_tokens()
1043                .unwrap()
1044                .iter()
1045                .any(|atom| case_sensitivity.eq_atom(atom, name))
1046        })
1047    }
1048
1049    #[inline]
1050    fn get_classes_for_layout(self) -> Option<&'dom [Atom]> {
1051        get_attr_for_layout(self, &ns!(), &local_name!("class"))
1052            .map(|attr| attr.to_tokens().unwrap())
1053    }
1054
1055    fn get_parts_for_layout(self) -> Option<&'dom [Atom]> {
1056        get_attr_for_layout(self, &ns!(), &local_name!("part"))
1057            .map(|attr| attr.to_tokens().unwrap())
1058    }
1059
1060    fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
1061    where
1062        V: Push<ApplicableDeclarationBlock>,
1063    {
1064        let mut property_declaration_block = None;
1065        let mut push = |declaration| {
1066            property_declaration_block
1067                .get_or_insert_with(PropertyDeclarationBlock::default)
1068                .push(declaration, Importance::Normal);
1069        };
1070
1071        // TODO(xiaochengh): This is probably not enough. When the root element doesn't have a `lang`,
1072        // we should check the browser settings and system locale.
1073        if let Some(lang) = self.get_lang_attr_val_for_layout() {
1074            push(PropertyDeclaration::XLang(specified::XLang(Atom::from(
1075                lang.to_owned(),
1076            ))));
1077        }
1078
1079        let bgcolor = if let Some(this) = self.downcast::<HTMLBodyElement>() {
1080            this.get_background_color()
1081        } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1082            this.get_background_color()
1083        } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1084            this.get_background_color()
1085        } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1086            this.get_background_color()
1087        } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1088            this.get_background_color()
1089        } else {
1090            None
1091        };
1092
1093        if let Some(color) = bgcolor {
1094            push(PropertyDeclaration::BackgroundColor(
1095                specified::Color::from_absolute_color(color),
1096            ));
1097        }
1098
1099        let background = self
1100            .downcast::<HTMLBodyElement>()
1101            .and_then(HTMLBodyElementLayoutHelpers::get_background);
1102        if let Some(url) = background {
1103            push(PropertyDeclaration::BackgroundImage(
1104                background_image::SpecifiedValue(
1105                    vec![specified::Image::for_cascade(url.get_arc())].into(),
1106                ),
1107            ));
1108        }
1109
1110        let color = if let Some(this) = self.downcast::<HTMLFontElement>() {
1111            this.get_color()
1112        } else if let Some(this) = self.downcast::<HTMLBodyElement>() {
1113            // https://html.spec.whatwg.org/multipage/#the-page:the-body-element-20
1114            this.get_color()
1115        } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1116            // https://html.spec.whatwg.org/multipage/#the-hr-element-2:presentational-hints-5
1117            this.get_color()
1118        } else {
1119            None
1120        };
1121
1122        if let Some(color) = color {
1123            push(PropertyDeclaration::Color(
1124                longhands::color::SpecifiedValue(specified::Color::from_absolute_color(color)),
1125            ));
1126        }
1127
1128        let font_face = self
1129            .downcast::<HTMLFontElement>()
1130            .and_then(HTMLFontElementLayoutHelpers::get_face);
1131        if let Some(font_face) = font_face {
1132            push(PropertyDeclaration::FontFamily(
1133                font_family::SpecifiedValue::Values(computed::font::FontFamilyList {
1134                    list: ArcSlice::from_iter(
1135                        HTMLFontElement::parse_face_attribute(font_face).into_iter(),
1136                    ),
1137                }),
1138            ));
1139        }
1140
1141        let font_size = self
1142            .downcast::<HTMLFontElement>()
1143            .and_then(HTMLFontElementLayoutHelpers::get_size);
1144        if let Some(font_size) = font_size {
1145            push(PropertyDeclaration::FontSize(
1146                font_size::SpecifiedValue::from_html_size(font_size as u8),
1147            ));
1148        }
1149
1150        let cellspacing = self
1151            .downcast::<HTMLTableElement>()
1152            .and_then(HTMLTableElementLayoutHelpers::get_cellspacing);
1153        if let Some(cellspacing) = cellspacing {
1154            let width_value = specified::Length::from_px(cellspacing as f32);
1155            push(PropertyDeclaration::BorderSpacing(Box::new(
1156                border_spacing::SpecifiedValue::new(width_value.clone().into(), width_value.into()),
1157            )));
1158        }
1159
1160        // Textual input, specifically text entry and domain specific input has
1161        // a default preferred size.
1162        //
1163        // <https://html.spec.whatwg.org/multipage/#the-input-element-as-a-text-entry-widget>
1164        // <https://html.spec.whatwg.org/multipage/#the-input-element-as-domain-specific-widgets>
1165        let size = self
1166            .downcast::<HTMLInputElement>()
1167            .and_then(|input_element| {
1168                // FIXME(pcwalton): More use of atoms, please!
1169                match self.get_attr_val_for_layout(&ns!(), &local_name!("type")) {
1170                    Some("hidden") | Some("range") | Some("color") | Some("checkbox") |
1171                    Some("radio") | Some("file") | Some("submit") | Some("image") |
1172                    Some("reset") | Some("button") => None,
1173                    // Others
1174                    _ => match input_element.size_for_layout() {
1175                        0 => None,
1176                        s => Some(s as i32),
1177                    },
1178                }
1179            });
1180
1181        if let Some(size) = size {
1182            let value =
1183                specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(size));
1184            push(PropertyDeclaration::Width(
1185                specified::Size::LengthPercentage(NonNegative(
1186                    specified::LengthPercentage::Length(value),
1187                )),
1188            ));
1189        }
1190
1191        let width = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1192            this.get_width()
1193        } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1194            this.get_width()
1195        } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1196            this.get_width()
1197        } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1198            this.get_width()
1199        } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1200            this.get_width()
1201        } else if let Some(this) = self.downcast::<HTMLTableColElement>() {
1202            this.get_width()
1203        } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1204            // https://html.spec.whatwg.org/multipage/#the-hr-element-2:attr-hr-width
1205            this.get_width()
1206        } else {
1207            LengthOrPercentageOrAuto::Auto
1208        };
1209
1210        // FIXME(emilio): Use from_computed value here and below.
1211        match width {
1212            LengthOrPercentageOrAuto::Auto => {},
1213            LengthOrPercentageOrAuto::Percentage(percentage) => {
1214                let width_value = specified::Size::LengthPercentage(NonNegative(
1215                    specified::LengthPercentage::Percentage(computed::Percentage(percentage)),
1216                ));
1217                push(PropertyDeclaration::Width(width_value));
1218            },
1219            LengthOrPercentageOrAuto::Length(length) => {
1220                let width_value = specified::Size::LengthPercentage(NonNegative(
1221                    specified::LengthPercentage::Length(specified::NoCalcLength::Absolute(
1222                        specified::AbsoluteLength::Px(length.to_f32_px()),
1223                    )),
1224                ));
1225                push(PropertyDeclaration::Width(width_value));
1226            },
1227        }
1228
1229        let height = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1230            this.get_height()
1231        } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1232            this.get_height()
1233        } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1234            this.get_height()
1235        } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1236            this.get_height()
1237        } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1238            this.get_height()
1239        } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1240            this.get_height()
1241        } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1242            this.get_height()
1243        } else {
1244            LengthOrPercentageOrAuto::Auto
1245        };
1246
1247        match height {
1248            LengthOrPercentageOrAuto::Auto => {},
1249            LengthOrPercentageOrAuto::Percentage(percentage) => {
1250                let height_value = specified::Size::LengthPercentage(NonNegative(
1251                    specified::LengthPercentage::Percentage(computed::Percentage(percentage)),
1252                ));
1253                push(PropertyDeclaration::Height(height_value));
1254            },
1255            LengthOrPercentageOrAuto::Length(length) => {
1256                let height_value = specified::Size::LengthPercentage(NonNegative(
1257                    specified::LengthPercentage::Length(specified::NoCalcLength::Absolute(
1258                        specified::AbsoluteLength::Px(length.to_f32_px()),
1259                    )),
1260                ));
1261                push(PropertyDeclaration::Height(height_value));
1262            },
1263        }
1264
1265        // Aspect ratio when providing both width and height.
1266        // https://html.spec.whatwg.org/multipage/#attributes-for-embedded-content-and-images
1267        if self.downcast::<HTMLImageElement>().is_some() ||
1268            self.downcast::<HTMLVideoElement>().is_some()
1269        {
1270            if let LengthOrPercentageOrAuto::Length(width) = width {
1271                if let LengthOrPercentageOrAuto::Length(height) = height {
1272                    let width_value = NonNegative(specified::Number::new(width.to_f32_px()));
1273                    let height_value = NonNegative(specified::Number::new(height.to_f32_px()));
1274                    let aspect_ratio = specified::position::AspectRatio {
1275                        auto: true,
1276                        ratio: PreferredRatio::Ratio(Ratio(width_value, height_value)),
1277                    };
1278                    push(PropertyDeclaration::AspectRatio(aspect_ratio));
1279                }
1280            }
1281        }
1282
1283        let cols = self
1284            .downcast::<HTMLTextAreaElement>()
1285            .map(LayoutHTMLTextAreaElementHelpers::get_cols);
1286        if let Some(cols) = cols {
1287            let cols = cols as i32;
1288            if cols > 0 {
1289                // TODO(mttr) ServoCharacterWidth uses the size math for <input type="text">, but
1290                // the math for <textarea> is a little different since we need to take
1291                // scrollbar size into consideration (but we don't have a scrollbar yet!)
1292                //
1293                // https://html.spec.whatwg.org/multipage/#textarea-effective-width
1294                let value =
1295                    specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(cols));
1296                push(PropertyDeclaration::Width(
1297                    specified::Size::LengthPercentage(NonNegative(
1298                        specified::LengthPercentage::Length(value),
1299                    )),
1300                ));
1301            }
1302        }
1303
1304        let rows = self
1305            .downcast::<HTMLTextAreaElement>()
1306            .map(LayoutHTMLTextAreaElementHelpers::get_rows);
1307        if let Some(rows) = rows {
1308            let rows = rows as i32;
1309            if rows > 0 {
1310                // TODO(mttr) This should take scrollbar size into consideration.
1311                //
1312                // https://html.spec.whatwg.org/multipage/#textarea-effective-height
1313                let value = specified::NoCalcLength::FontRelative(
1314                    specified::FontRelativeLength::Em(rows as CSSFloat),
1315                );
1316                push(PropertyDeclaration::Height(
1317                    specified::Size::LengthPercentage(NonNegative(
1318                        specified::LengthPercentage::Length(value),
1319                    )),
1320                ));
1321            }
1322        }
1323
1324        let border = self
1325            .downcast::<HTMLTableElement>()
1326            .and_then(|table| table.get_border());
1327        if let Some(border) = border {
1328            let width_value = specified::BorderSideWidth::from_px(border as f32);
1329            push(PropertyDeclaration::BorderTopWidth(width_value.clone()));
1330            push(PropertyDeclaration::BorderLeftWidth(width_value.clone()));
1331            push(PropertyDeclaration::BorderBottomWidth(width_value.clone()));
1332            push(PropertyDeclaration::BorderRightWidth(width_value));
1333        }
1334
1335        if let Some(cellpadding) = self
1336            .downcast::<HTMLTableCellElement>()
1337            .and_then(|this| this.get_table())
1338            .and_then(|table| table.get_cellpadding())
1339        {
1340            let cellpadding = NonNegative(specified::LengthPercentage::Length(
1341                specified::NoCalcLength::from_px(cellpadding as f32),
1342            ));
1343            push(PropertyDeclaration::PaddingTop(cellpadding.clone()));
1344            push(PropertyDeclaration::PaddingLeft(cellpadding.clone()));
1345            push(PropertyDeclaration::PaddingBottom(cellpadding.clone()));
1346            push(PropertyDeclaration::PaddingRight(cellpadding));
1347        }
1348
1349        // https://html.spec.whatwg.org/multipage/#the-hr-element-2
1350        if let Some(size_info) = self
1351            .downcast::<HTMLHRElement>()
1352            .and_then(|hr_element| hr_element.get_size_info())
1353        {
1354            match size_info {
1355                SizePresentationalHint::SetHeightTo(height) => {
1356                    push(PropertyDeclaration::Height(height));
1357                },
1358                SizePresentationalHint::SetAllBorderWidthValuesTo(border_width) => {
1359                    push(PropertyDeclaration::BorderLeftWidth(border_width.clone()));
1360                    push(PropertyDeclaration::BorderRightWidth(border_width.clone()));
1361                    push(PropertyDeclaration::BorderTopWidth(border_width.clone()));
1362                    push(PropertyDeclaration::BorderBottomWidth(border_width));
1363                },
1364                SizePresentationalHint::SetBottomBorderWidthToZero => {
1365                    push(PropertyDeclaration::BorderBottomWidth(
1366                        specified::border::BorderSideWidth::from_px(0.),
1367                    ));
1368                },
1369            }
1370        }
1371
1372        let Some(property_declaration_block) = property_declaration_block else {
1373            return;
1374        };
1375
1376        let document = self.upcast::<Node>().owner_doc_for_layout();
1377        let shared_lock = document.style_shared_lock();
1378        hints.push(ApplicableDeclarationBlock::from_declarations(
1379            Arc::new(shared_lock.wrap(property_declaration_block)),
1380            CascadeLevel::PresHints,
1381            LayerOrder::root(),
1382        ));
1383    }
1384
1385    fn get_span(self) -> Option<u32> {
1386        // Don't panic since `display` can cause this to be called on arbitrary elements.
1387        self.downcast::<HTMLTableColElement>()
1388            .and_then(|element| element.get_span())
1389    }
1390
1391    fn get_colspan(self) -> Option<u32> {
1392        // Don't panic since `display` can cause this to be called on arbitrary elements.
1393        self.downcast::<HTMLTableCellElement>()
1394            .and_then(|element| element.get_colspan())
1395    }
1396
1397    fn get_rowspan(self) -> Option<u32> {
1398        // Don't panic since `display` can cause this to be called on arbitrary elements.
1399        self.downcast::<HTMLTableCellElement>()
1400            .and_then(|element| element.get_rowspan())
1401    }
1402
1403    #[inline]
1404    fn is_html_element(&self) -> bool {
1405        *self.namespace() == ns!(html)
1406    }
1407
1408    #[expect(unsafe_code)]
1409    fn id_attribute(self) -> *const Option<Atom> {
1410        unsafe { (self.unsafe_get()).id_attribute.borrow_for_layout() }
1411    }
1412
1413    #[expect(unsafe_code)]
1414    fn style_attribute(self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>> {
1415        unsafe { (self.unsafe_get()).style_attribute.borrow_for_layout() }
1416    }
1417
1418    fn local_name(self) -> &'dom LocalName {
1419        &(self.unsafe_get()).local_name
1420    }
1421
1422    fn namespace(self) -> &'dom Namespace {
1423        &(self.unsafe_get()).namespace
1424    }
1425
1426    fn get_lang_attr_val_for_layout(self) -> Option<&'dom str> {
1427        if let Some(attr) = self.get_attr_val_for_layout(&ns!(xml), &local_name!("lang")) {
1428            return Some(attr);
1429        }
1430        if let Some(attr) = self.get_attr_val_for_layout(&ns!(), &local_name!("lang")) {
1431            return Some(attr);
1432        }
1433        None
1434    }
1435
1436    fn get_lang_for_layout(self) -> String {
1437        let mut current_node = Some(self.upcast::<Node>());
1438        while let Some(node) = current_node {
1439            current_node = node.composed_parent_node_ref();
1440            match node.downcast::<Element>() {
1441                Some(elem) => {
1442                    if let Some(attr) = elem.get_lang_attr_val_for_layout() {
1443                        return attr.to_owned();
1444                    }
1445                },
1446                None => continue,
1447            }
1448        }
1449        // TODO: Check meta tags for a pragma-set default language
1450        // TODO: Check HTTP Content-Language header
1451        String::new()
1452    }
1453
1454    #[inline]
1455    fn get_state_for_layout(self) -> ElementState {
1456        (self.unsafe_get()).state.get()
1457    }
1458
1459    #[inline]
1460    fn insert_selector_flags(self, flags: ElementSelectorFlags) {
1461        debug_assert!(thread_state::get().is_layout());
1462        let f = &(self.unsafe_get()).selector_flags;
1463        f.set(f.get() | flags);
1464    }
1465
1466    #[inline]
1467    fn get_selector_flags(self) -> ElementSelectorFlags {
1468        self.unsafe_get().selector_flags.get()
1469    }
1470
1471    #[inline]
1472    #[expect(unsafe_code)]
1473    fn get_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
1474        unsafe {
1475            self.unsafe_get()
1476                .rare_data
1477                .borrow_for_layout()
1478                .as_ref()?
1479                .shadow_root
1480                .as_ref()
1481                .map(|sr| sr.to_layout())
1482        }
1483    }
1484
1485    #[inline]
1486    fn get_attr_for_layout(
1487        self,
1488        namespace: &Namespace,
1489        name: &LocalName,
1490    ) -> Option<&'dom AttrValue> {
1491        get_attr_for_layout(self, namespace, name).map(|attr| attr.value())
1492    }
1493
1494    #[inline]
1495    fn get_attr_val_for_layout(self, namespace: &Namespace, name: &LocalName) -> Option<&'dom str> {
1496        get_attr_for_layout(self, namespace, name).map(|attr| attr.as_str())
1497    }
1498
1499    #[inline]
1500    fn get_attr_vals_for_layout(self, name: &LocalName) -> Vec<&'dom AttrValue> {
1501        self.attrs()
1502            .iter()
1503            .filter_map(|attr| {
1504                if name == attr.local_name() {
1505                    Some(attr.value())
1506                } else {
1507                    None
1508                }
1509            })
1510            .collect()
1511    }
1512
1513    fn each_custom_state<F>(self, callback: F)
1514    where
1515        F: FnMut(&AtomIdent),
1516    {
1517        self.unsafe_get().each_custom_state(callback)
1518    }
1519}
1520
1521impl Element {
1522    pub(crate) fn is_html_element(&self) -> bool {
1523        self.namespace == ns!(html)
1524    }
1525
1526    pub(crate) fn html_element_in_html_document(&self) -> bool {
1527        self.is_html_element() && self.upcast::<Node>().is_in_html_doc()
1528    }
1529
1530    pub(crate) fn local_name(&self) -> &LocalName {
1531        &self.local_name
1532    }
1533
1534    pub(crate) fn parsed_name(&self, mut name: DOMString) -> LocalName {
1535        if self.html_element_in_html_document() {
1536            name.make_ascii_lowercase();
1537        }
1538        LocalName::from(name)
1539    }
1540
1541    pub(crate) fn namespace(&self) -> &Namespace {
1542        &self.namespace
1543    }
1544
1545    pub(crate) fn prefix(&self) -> Ref<'_, Option<Prefix>> {
1546        self.prefix.borrow()
1547    }
1548
1549    pub(crate) fn set_prefix(&self, prefix: Option<Prefix>) {
1550        *self.prefix.borrow_mut() = prefix;
1551    }
1552
1553    pub(crate) fn attrs(&self) -> Ref<'_, [Dom<Attr>]> {
1554        Ref::map(self.attrs.borrow(), |attrs| &**attrs)
1555    }
1556
1557    /// Element branch of <https://dom.spec.whatwg.org/#locate-a-namespace>
1558    pub(crate) fn locate_namespace(&self, prefix: Option<DOMString>) -> Namespace {
1559        let namespace_prefix = prefix.clone().map(|s| Prefix::from(&*s.str()));
1560
1561        // Step 1. If prefix is "xml", then return the XML namespace.
1562        if namespace_prefix == Some(namespace_prefix!("xml")) {
1563            return ns!(xml);
1564        }
1565
1566        // Step 2. If prefix is "xmlns", then return the XMLNS namespace.
1567        if namespace_prefix == Some(namespace_prefix!("xmlns")) {
1568            return ns!(xmlns);
1569        }
1570
1571        let prefix = prefix.map(LocalName::from);
1572
1573        let inclusive_ancestor_elements = self
1574            .upcast::<Node>()
1575            .inclusive_ancestors(ShadowIncluding::No)
1576            .filter_map(DomRoot::downcast::<Self>);
1577
1578        // Step 5. If its parent element is null, then return null.
1579        // Step 6. Return the result of running locate a namespace on its parent element using prefix.
1580        for element in inclusive_ancestor_elements {
1581            // Step 3. If its namespace is non-null and its namespace prefix is prefix, then return namespace.
1582            if element.namespace() != &ns!() &&
1583                element.prefix().as_ref().map(|p| &**p) == prefix.as_deref()
1584            {
1585                return element.namespace().clone();
1586            }
1587
1588            // Step 4. If it has an attribute whose namespace is the XMLNS namespace, namespace prefix
1589            // is "xmlns", and local name is prefix, or if prefix is null and it has an attribute
1590            // whose namespace is the XMLNS namespace, namespace prefix is null, and local name is
1591            // "xmlns", then return its value if it is not the empty string, and null otherwise.
1592            let attr = Ref::filter_map(self.attrs(), |attrs| {
1593                attrs.iter().find(|attr| {
1594                    if attr.namespace() != &ns!(xmlns) {
1595                        return false;
1596                    }
1597                    match (attr.prefix(), prefix.as_ref()) {
1598                        (Some(&namespace_prefix!("xmlns")), Some(prefix)) => {
1599                            attr.local_name() == prefix
1600                        },
1601                        (None, None) => attr.local_name() == &local_name!("xmlns"),
1602                        _ => false,
1603                    }
1604                })
1605            })
1606            .ok();
1607
1608            if let Some(attr) = attr {
1609                return (**attr.value()).into();
1610            }
1611        }
1612
1613        ns!()
1614    }
1615
1616    pub(crate) fn name_attribute(&self) -> Option<Atom> {
1617        self.rare_data().as_ref()?.name_attribute.clone()
1618    }
1619
1620    pub(crate) fn style_attribute(
1621        &self,
1622    ) -> &DomRefCell<Option<Arc<Locked<PropertyDeclarationBlock>>>> {
1623        &self.style_attribute
1624    }
1625
1626    pub(crate) fn summarize(&self) -> Vec<AttrInfo> {
1627        self.attrs
1628            .borrow()
1629            .iter()
1630            .map(|attr| attr.summarize())
1631            .collect()
1632    }
1633
1634    pub(crate) fn is_void(&self) -> bool {
1635        if self.namespace != ns!(html) {
1636            return false;
1637        }
1638        match self.local_name {
1639            /* List of void elements from
1640            https://html.spec.whatwg.org/multipage/#html-fragment-serialisation-algorithm */
1641            local_name!("area") |
1642            local_name!("base") |
1643            local_name!("basefont") |
1644            local_name!("bgsound") |
1645            local_name!("br") |
1646            local_name!("col") |
1647            local_name!("embed") |
1648            local_name!("frame") |
1649            local_name!("hr") |
1650            local_name!("img") |
1651            local_name!("input") |
1652            local_name!("keygen") |
1653            local_name!("link") |
1654            local_name!("meta") |
1655            local_name!("param") |
1656            local_name!("source") |
1657            local_name!("track") |
1658            local_name!("wbr") => true,
1659            _ => false,
1660        }
1661    }
1662
1663    pub(crate) fn root_element(&self) -> DomRoot<Element> {
1664        if self.node.is_in_a_document_tree() {
1665            self.upcast::<Node>()
1666                .owner_doc()
1667                .GetDocumentElement()
1668                .unwrap()
1669        } else {
1670            self.upcast::<Node>()
1671                .inclusive_ancestors(ShadowIncluding::No)
1672                .filter_map(DomRoot::downcast)
1673                .last()
1674                .expect("We know inclusive_ancestors will return `self` which is an element")
1675        }
1676    }
1677
1678    // https://dom.spec.whatwg.org/#locate-a-namespace-prefix
1679    pub(crate) fn lookup_prefix(&self, namespace: Namespace) -> Option<DOMString> {
1680        for node in self
1681            .upcast::<Node>()
1682            .inclusive_ancestors(ShadowIncluding::No)
1683        {
1684            let element = node.downcast::<Element>()?;
1685            // Step 1.
1686            if *element.namespace() == namespace {
1687                if let Some(prefix) = element.GetPrefix() {
1688                    return Some(prefix);
1689                }
1690            }
1691
1692            // Step 2.
1693            for attr in element.attrs.borrow().iter() {
1694                if attr.prefix() == Some(&namespace_prefix!("xmlns")) &&
1695                    **attr.value() == *namespace
1696                {
1697                    return Some(attr.LocalName());
1698                }
1699            }
1700        }
1701        None
1702    }
1703
1704    /// <https://dom.spec.whatwg.org/#document-element>
1705    pub(crate) fn is_document_element(&self) -> bool {
1706        if let Some(document_element) = self.owner_document().GetDocumentElement() {
1707            *document_element == *self
1708        } else {
1709            false
1710        }
1711    }
1712
1713    /// <https://html.spec.whatwg.org/multipage/#dom-document-activeelement>
1714    pub(crate) fn is_active_element(&self) -> bool {
1715        if let Some(active_element) = self.owner_document().GetActiveElement() {
1716            *active_element == *self
1717        } else {
1718            false
1719        }
1720    }
1721
1722    pub(crate) fn is_focusable_area(&self) -> bool {
1723        if self.is_actually_disabled() {
1724            return false;
1725        }
1726        let node = self.upcast::<Node>();
1727        if node.get_flag(NodeFlags::SEQUENTIALLY_FOCUSABLE) {
1728            return true;
1729        }
1730
1731        // <a>, <input>, <select>, and <textrea> are inherently focusable.
1732        matches!(
1733            node.type_id(),
1734            NodeTypeId::Element(ElementTypeId::HTMLElement(
1735                HTMLElementTypeId::HTMLAnchorElement,
1736            )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
1737                HTMLElementTypeId::HTMLInputElement,
1738            )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
1739                HTMLElementTypeId::HTMLSelectElement,
1740            )) | NodeTypeId::Element(ElementTypeId::HTMLElement(
1741                HTMLElementTypeId::HTMLTextAreaElement,
1742            ))
1743        )
1744    }
1745
1746    /// Returns the focusable shadow host if this is a text control inner editor.
1747    /// This is a workaround for the focus delegation of shadow DOM and should be
1748    /// used only to delegate focusable inner editor of [HTMLInputElement] and
1749    /// [HTMLTextAreaElement].
1750    pub(crate) fn find_focusable_shadow_host_if_necessary(&self) -> Option<DomRoot<Element>> {
1751        if self.is_focusable_area() {
1752            Some(DomRoot::from_ref(self))
1753        } else if self.upcast::<Node>().implemented_pseudo_element() ==
1754            Some(PseudoElement::ServoTextControlInnerEditor)
1755        {
1756            let containing_shadow_host = self.containing_shadow_root().map(|root| root.Host());
1757            debug_assert!(
1758                containing_shadow_host
1759                    .as_ref()
1760                    .is_some_and(|e| e.is_focusable_area()),
1761                "Containing shadow host is not focusable"
1762            );
1763            containing_shadow_host
1764        } else {
1765            None
1766        }
1767    }
1768
1769    pub(crate) fn is_actually_disabled(&self) -> bool {
1770        let node = self.upcast::<Node>();
1771        match node.type_id() {
1772            NodeTypeId::Element(ElementTypeId::HTMLElement(
1773                HTMLElementTypeId::HTMLButtonElement,
1774            )) |
1775            NodeTypeId::Element(ElementTypeId::HTMLElement(
1776                HTMLElementTypeId::HTMLInputElement,
1777            )) |
1778            NodeTypeId::Element(ElementTypeId::HTMLElement(
1779                HTMLElementTypeId::HTMLSelectElement,
1780            )) |
1781            NodeTypeId::Element(ElementTypeId::HTMLElement(
1782                HTMLElementTypeId::HTMLTextAreaElement,
1783            )) |
1784            NodeTypeId::Element(ElementTypeId::HTMLElement(
1785                HTMLElementTypeId::HTMLOptionElement,
1786            )) => self.disabled_state(),
1787            NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
1788                self.downcast::<HTMLElement>()
1789                    .unwrap()
1790                    .is_form_associated_custom_element() &&
1791                    self.disabled_state()
1792            },
1793            // TODO:
1794            // an optgroup element that has a disabled attribute
1795            // a menuitem element that has a disabled attribute
1796            // a fieldset element that is a disabled fieldset
1797            _ => false,
1798        }
1799    }
1800
1801    #[allow(clippy::too_many_arguments)]
1802    pub(crate) fn push_new_attribute(
1803        &self,
1804        local_name: LocalName,
1805        value: AttrValue,
1806        name: LocalName,
1807        namespace: Namespace,
1808        prefix: Option<Prefix>,
1809        reason: AttributeMutationReason,
1810        can_gc: CanGc,
1811    ) {
1812        let attr = Attr::new(
1813            &self.node.owner_doc(),
1814            local_name,
1815            value,
1816            name,
1817            namespace,
1818            prefix,
1819            Some(self),
1820            can_gc,
1821        );
1822        self.push_attribute(&attr, reason, can_gc);
1823    }
1824
1825    /// <https://dom.spec.whatwg.org/#handle-attribute-changes>
1826    fn handle_attribute_changes(
1827        &self,
1828        attr: &Attr,
1829        old_value: Option<&AttrValue>,
1830        new_value: Option<DOMString>,
1831        reason: AttributeMutationReason,
1832        can_gc: CanGc,
1833    ) {
1834        let old_value_string = old_value.map(|old_value| DOMString::from(&**old_value));
1835        // Step 1. Queue a mutation record of "attributes" for element with attribute’s local name,
1836        // attribute’s namespace, oldValue, « », « », null, and null.
1837        let name = attr.local_name().clone();
1838        let namespace = attr.namespace().clone();
1839        let mutation = LazyCell::new(|| Mutation::Attribute {
1840            name: name.clone(),
1841            namespace: namespace.clone(),
1842            old_value: old_value_string.clone(),
1843        });
1844        MutationObserver::queue_a_mutation_record(&self.node, mutation);
1845
1846        // Avoid double borrow
1847        let has_new_value = new_value.is_some();
1848
1849        // Step 2. If element is custom, then enqueue a custom element callback reaction with element,
1850        // callback name "attributeChangedCallback", and « attribute’s local name, oldValue, newValue, attribute’s namespace ».
1851        if self.is_custom() {
1852            let reaction = CallbackReaction::AttributeChanged(
1853                attr.local_name().clone(),
1854                old_value_string,
1855                new_value,
1856                attr.namespace().clone(),
1857            );
1858            ScriptThread::enqueue_callback_reaction(self, reaction, None);
1859        }
1860
1861        // Step 3. Run the attribute change steps with element, attribute’s local name, oldValue, newValue, and attribute’s namespace.
1862        if is_relevant_attribute(attr.namespace(), attr.local_name()) {
1863            let attribute_mutation = if has_new_value {
1864                AttributeMutation::Set(old_value, reason)
1865            } else {
1866                AttributeMutation::Removed
1867            };
1868            vtable_for(self.upcast()).attribute_mutated(attr, attribute_mutation, can_gc);
1869        }
1870    }
1871
1872    /// <https://dom.spec.whatwg.org/#concept-element-attributes-change>
1873    pub(crate) fn change_attribute(&self, attr: &Attr, mut value: AttrValue, can_gc: CanGc) {
1874        // Step 1. Let oldValue be attribute’s value.
1875        //
1876        // Clone to avoid double borrow
1877        let old_value = &attr.value().clone();
1878        // Step 2. Set attribute’s value to value.
1879        self.will_mutate_attr(attr);
1880        attr.swap_value(&mut value);
1881        // Step 3. Handle attribute changes for attribute with attribute’s element, oldValue, and value.
1882        //
1883        // Put on a separate line to avoid double borrow
1884        let new_value = DOMString::from(&**attr.value());
1885        self.handle_attribute_changes(
1886            attr,
1887            Some(old_value),
1888            Some(new_value),
1889            AttributeMutationReason::Directly,
1890            can_gc,
1891        );
1892    }
1893
1894    /// <https://dom.spec.whatwg.org/#concept-element-attributes-append>
1895    pub(crate) fn push_attribute(
1896        &self,
1897        attr: &Attr,
1898        reason: AttributeMutationReason,
1899        can_gc: CanGc,
1900    ) {
1901        // Step 2. Set attribute’s element to element.
1902        //
1903        // Handled by callers of this function and asserted here.
1904        assert!(attr.GetOwnerElement().as_deref() == Some(self));
1905        // Step 3. Set attribute’s node document to element’s node document.
1906        //
1907        // Handled by callers of this function and asserted here.
1908        assert!(attr.upcast::<Node>().owner_doc() == self.node.owner_doc());
1909        // Step 1. Append attribute to element’s attribute list.
1910        self.will_mutate_attr(attr);
1911        self.attrs.borrow_mut().push(Dom::from_ref(attr));
1912        // Step 4. Handle attribute changes for attribute with element, null, and attribute’s value.
1913        //
1914        // Put on a separate line to avoid double borrow
1915        let new_value = DOMString::from(&**attr.value());
1916        self.handle_attribute_changes(attr, None, Some(new_value), reason, can_gc);
1917    }
1918
1919    pub(crate) fn get_attribute(
1920        &self,
1921        namespace: &Namespace,
1922        local_name: &LocalName,
1923    ) -> Option<DomRoot<Attr>> {
1924        self.attrs
1925            .borrow()
1926            .iter()
1927            .find(|attr| attr.local_name() == local_name && attr.namespace() == namespace)
1928            .map(|js| DomRoot::from_ref(&**js))
1929    }
1930
1931    /// <https://dom.spec.whatwg.org/#concept-element-attributes-get-by-name>
1932    pub(crate) fn get_attribute_by_name(&self, name: DOMString) -> Option<DomRoot<Attr>> {
1933        let name = &self.parsed_name(name);
1934        let maybe_attribute = self
1935            .attrs
1936            .borrow()
1937            .iter()
1938            .find(|a| a.name() == name)
1939            .map(|js| DomRoot::from_ref(&**js));
1940        fn id_and_name_must_be_atoms(name: &LocalName, maybe_attr: &Option<DomRoot<Attr>>) -> bool {
1941            if *name == local_name!("id") || *name == local_name!("name") {
1942                match maybe_attr {
1943                    None => true,
1944                    Some(attr) => matches!(*attr.value(), AttrValue::Atom(_)),
1945                }
1946            } else {
1947                true
1948            }
1949        }
1950        debug_assert!(id_and_name_must_be_atoms(name, &maybe_attribute));
1951        maybe_attribute
1952    }
1953
1954    pub(crate) fn set_attribute_from_parser(
1955        &self,
1956        qname: QualName,
1957        value: DOMString,
1958        prefix: Option<Prefix>,
1959        can_gc: CanGc,
1960    ) {
1961        // Don't set if the attribute already exists, so we can handle add_attrs_if_missing
1962        if self
1963            .attrs
1964            .borrow()
1965            .iter()
1966            .any(|a| *a.local_name() == qname.local && *a.namespace() == qname.ns)
1967        {
1968            return;
1969        }
1970
1971        let name = match prefix {
1972            None => qname.local.clone(),
1973            Some(ref prefix) => {
1974                let name = format!("{}:{}", &**prefix, &*qname.local);
1975                LocalName::from(name)
1976            },
1977        };
1978        let value = self.parse_attribute(&qname.ns, &qname.local, value);
1979        self.push_new_attribute(
1980            qname.local,
1981            value,
1982            name,
1983            qname.ns,
1984            prefix,
1985            AttributeMutationReason::ByParser,
1986            can_gc,
1987        );
1988    }
1989
1990    pub(crate) fn set_attribute(&self, name: &LocalName, value: AttrValue, can_gc: CanGc) {
1991        assert!(name == &name.to_ascii_lowercase());
1992        assert!(!name.contains(':'));
1993
1994        self.set_first_matching_attribute(
1995            name.clone(),
1996            value,
1997            name.clone(),
1998            ns!(),
1999            None,
2000            |attr| attr.local_name() == name,
2001            can_gc,
2002        );
2003    }
2004
2005    // https://html.spec.whatwg.org/multipage/#attr-data-*
2006    pub(crate) fn set_custom_attribute(
2007        &self,
2008        name: DOMString,
2009        value: DOMString,
2010        can_gc: CanGc,
2011    ) -> ErrorResult {
2012        // Step 1.
2013        if !matches_name_production(&name.str()) {
2014            return Err(Error::InvalidCharacter(None));
2015        }
2016
2017        // Steps 2-5.
2018        let name = LocalName::from(name);
2019        let value = self.parse_attribute(&ns!(), &name, value);
2020        self.set_first_matching_attribute(
2021            name.clone(),
2022            value,
2023            name.clone(),
2024            ns!(),
2025            None,
2026            |attr| *attr.name() == name && *attr.namespace() == ns!(),
2027            can_gc,
2028        );
2029        Ok(())
2030    }
2031
2032    /// <https://dom.spec.whatwg.org/#concept-element-attributes-set-value>
2033    #[allow(clippy::too_many_arguments)]
2034    fn set_first_matching_attribute<F>(
2035        &self,
2036        local_name: LocalName,
2037        value: AttrValue,
2038        name: LocalName,
2039        namespace: Namespace,
2040        prefix: Option<Prefix>,
2041        find: F,
2042        can_gc: CanGc,
2043    ) where
2044        F: Fn(&Attr) -> bool,
2045    {
2046        // Step 1. Let attribute be the result of getting an attribute given namespace, localName, and element.
2047        let attr = self
2048            .attrs
2049            .borrow()
2050            .iter()
2051            .find(|attr| find(attr))
2052            .map(|js| DomRoot::from_ref(&**js));
2053        if let Some(attr) = attr {
2054            // Step 3. Change attribute to value.
2055            self.will_mutate_attr(&attr);
2056            self.change_attribute(&attr, value, can_gc);
2057        } else {
2058            // Step 2. If attribute is null, create an attribute whose namespace is namespace,
2059            // namespace prefix is prefix, local name is localName, value is value,
2060            // and node document is element’s node document,
2061            // then append this attribute to element, and then return.
2062            self.push_new_attribute(
2063                local_name,
2064                value,
2065                name,
2066                namespace,
2067                prefix,
2068                AttributeMutationReason::Directly,
2069                can_gc,
2070            );
2071        };
2072    }
2073
2074    pub(crate) fn parse_attribute(
2075        &self,
2076        namespace: &Namespace,
2077        local_name: &LocalName,
2078        value: DOMString,
2079    ) -> AttrValue {
2080        if is_relevant_attribute(namespace, local_name) {
2081            vtable_for(self.upcast()).parse_plain_attribute(local_name, value)
2082        } else {
2083            AttrValue::String(value.into())
2084        }
2085    }
2086
2087    pub(crate) fn remove_attribute(
2088        &self,
2089        namespace: &Namespace,
2090        local_name: &LocalName,
2091        can_gc: CanGc,
2092    ) -> Option<DomRoot<Attr>> {
2093        self.remove_first_matching_attribute(
2094            |attr| attr.namespace() == namespace && attr.local_name() == local_name,
2095            can_gc,
2096        )
2097    }
2098
2099    pub(crate) fn remove_attribute_by_name(
2100        &self,
2101        name: &LocalName,
2102        can_gc: CanGc,
2103    ) -> Option<DomRoot<Attr>> {
2104        self.remove_first_matching_attribute(|attr| attr.name() == name, can_gc)
2105    }
2106
2107    /// <https://dom.spec.whatwg.org/#concept-element-attributes-remove>
2108    fn remove_first_matching_attribute<F>(&self, find: F, can_gc: CanGc) -> Option<DomRoot<Attr>>
2109    where
2110        F: Fn(&Attr) -> bool,
2111    {
2112        let idx = self.attrs.borrow().iter().position(|attr| find(attr));
2113        idx.map(|idx| {
2114            let attr = DomRoot::from_ref(&*(*self.attrs.borrow())[idx]);
2115
2116            // Step 2. Remove attribute from element’s attribute list.
2117            self.will_mutate_attr(&attr);
2118            self.attrs.borrow_mut().remove(idx);
2119            // Step 3. Set attribute’s element to null.
2120            attr.set_owner(None);
2121            // Step 4. Handle attribute changes for attribute with element, attribute’s value, and null.
2122            self.handle_attribute_changes(
2123                &attr,
2124                Some(&attr.value()),
2125                None,
2126                AttributeMutationReason::Directly,
2127                can_gc,
2128            );
2129
2130            attr
2131        })
2132    }
2133
2134    pub(crate) fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2135        self.get_attribute(&ns!(), &local_name!("class"))
2136            .is_some_and(|attr| {
2137                attr.value()
2138                    .as_tokens()
2139                    .iter()
2140                    .any(|atom| case_sensitivity.eq_atom(name, atom))
2141            })
2142    }
2143
2144    pub(crate) fn is_part(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2145        self.get_attribute(&ns!(), &LocalName::from("part"))
2146            .is_some_and(|attr| {
2147                attr.value()
2148                    .as_tokens()
2149                    .iter()
2150                    .any(|atom| case_sensitivity.eq_atom(name, atom))
2151            })
2152    }
2153
2154    pub(crate) fn set_atomic_attribute(
2155        &self,
2156        local_name: &LocalName,
2157        value: DOMString,
2158        can_gc: CanGc,
2159    ) {
2160        assert!(*local_name == local_name.to_ascii_lowercase());
2161        let value = AttrValue::from_atomic(value.into());
2162        self.set_attribute(local_name, value, can_gc);
2163    }
2164
2165    pub(crate) fn has_attribute(&self, local_name: &LocalName) -> bool {
2166        assert!(local_name.bytes().all(|b| b.to_ascii_lowercase() == b));
2167        self.attrs
2168            .borrow()
2169            .iter()
2170            .any(|attr| attr.local_name() == local_name && attr.namespace() == &ns!())
2171    }
2172
2173    pub(crate) fn set_bool_attribute(&self, local_name: &LocalName, value: bool, can_gc: CanGc) {
2174        if self.has_attribute(local_name) == value {
2175            return;
2176        }
2177        if value {
2178            self.set_string_attribute(local_name, DOMString::new(), can_gc);
2179        } else {
2180            self.remove_attribute(&ns!(), local_name, can_gc);
2181        }
2182    }
2183
2184    pub(crate) fn get_url_attribute(&self, local_name: &LocalName) -> USVString {
2185        assert!(*local_name == local_name.to_ascii_lowercase());
2186        let attr = match self.get_attribute(&ns!(), local_name) {
2187            Some(attr) => attr,
2188            None => return USVString::default(),
2189        };
2190        let value = &**attr.value();
2191        // XXXManishearth this doesn't handle `javascript:` urls properly
2192        self.owner_document()
2193            .base_url()
2194            .join(value)
2195            .map(|parsed| USVString(parsed.into_string()))
2196            .unwrap_or_else(|_| USVString(value.to_owned()))
2197    }
2198
2199    pub(crate) fn set_url_attribute(
2200        &self,
2201        local_name: &LocalName,
2202        value: USVString,
2203        can_gc: CanGc,
2204    ) {
2205        assert!(*local_name == local_name.to_ascii_lowercase());
2206        self.set_attribute(local_name, AttrValue::String(value.to_string()), can_gc);
2207    }
2208
2209    pub(crate) fn get_trusted_type_url_attribute(
2210        &self,
2211        local_name: &LocalName,
2212    ) -> TrustedScriptURLOrUSVString {
2213        assert_eq!(*local_name, local_name.to_ascii_lowercase());
2214        let attr = match self.get_attribute(&ns!(), local_name) {
2215            Some(attr) => attr,
2216            None => return TrustedScriptURLOrUSVString::USVString(USVString::default()),
2217        };
2218        let value = &**attr.value();
2219        // XXXManishearth this doesn't handle `javascript:` urls properly
2220        self.owner_document()
2221            .base_url()
2222            .join(value)
2223            .map(|parsed| TrustedScriptURLOrUSVString::USVString(USVString(parsed.into_string())))
2224            .unwrap_or_else(|_| TrustedScriptURLOrUSVString::USVString(USVString(value.to_owned())))
2225    }
2226
2227    pub(crate) fn get_trusted_html_attribute(&self, local_name: &LocalName) -> TrustedHTMLOrString {
2228        assert_eq!(*local_name, local_name.to_ascii_lowercase());
2229        let value = match self.get_attribute(&ns!(), local_name) {
2230            Some(attr) => (&**attr.value()).into(),
2231            None => "".into(),
2232        };
2233        TrustedHTMLOrString::String(value)
2234    }
2235
2236    pub(crate) fn get_string_attribute(&self, local_name: &LocalName) -> DOMString {
2237        match self.get_attribute(&ns!(), local_name) {
2238            Some(x) => x.Value(),
2239            None => DOMString::new(),
2240        }
2241    }
2242
2243    pub(crate) fn set_string_attribute(
2244        &self,
2245        local_name: &LocalName,
2246        value: DOMString,
2247        can_gc: CanGc,
2248    ) {
2249        assert!(*local_name == local_name.to_ascii_lowercase());
2250        self.set_attribute(local_name, AttrValue::String(value.into()), can_gc);
2251    }
2252
2253    /// Used for string attribute reflections where absence of the attribute returns `null`,
2254    /// e.g. `element.ariaLabel` returning `null` when the `aria-label` attribute is absent.
2255    fn get_nullable_string_attribute(&self, local_name: &LocalName) -> Option<DOMString> {
2256        if self.has_attribute(local_name) {
2257            Some(self.get_string_attribute(local_name))
2258        } else {
2259            None
2260        }
2261    }
2262
2263    /// Used for string attribute reflections where setting `null`/`undefined` removes the
2264    /// attribute, e.g. `element.ariaLabel = null` removing the `aria-label` attribute.
2265    fn set_nullable_string_attribute(
2266        &self,
2267        local_name: &LocalName,
2268        value: Option<DOMString>,
2269        can_gc: CanGc,
2270    ) {
2271        match value {
2272            Some(val) => {
2273                self.set_string_attribute(local_name, val, can_gc);
2274            },
2275            None => {
2276                self.remove_attribute(&ns!(), local_name, can_gc);
2277            },
2278        }
2279    }
2280
2281    pub(crate) fn get_tokenlist_attribute(&self, local_name: &LocalName) -> Vec<Atom> {
2282        self.get_attribute(&ns!(), local_name)
2283            .map(|attr| attr.value().as_tokens().to_vec())
2284            .unwrap_or_default()
2285    }
2286
2287    pub(crate) fn set_tokenlist_attribute(
2288        &self,
2289        local_name: &LocalName,
2290        value: DOMString,
2291        can_gc: CanGc,
2292    ) {
2293        assert!(*local_name == local_name.to_ascii_lowercase());
2294        self.set_attribute(
2295            local_name,
2296            AttrValue::from_serialized_tokenlist(value.into()),
2297            can_gc,
2298        );
2299    }
2300
2301    pub(crate) fn set_atomic_tokenlist_attribute(
2302        &self,
2303        local_name: &LocalName,
2304        tokens: Vec<Atom>,
2305        can_gc: CanGc,
2306    ) {
2307        assert!(*local_name == local_name.to_ascii_lowercase());
2308        self.set_attribute(local_name, AttrValue::from_atomic_tokens(tokens), can_gc);
2309    }
2310
2311    pub(crate) fn get_int_attribute(&self, local_name: &LocalName, default: i32) -> i32 {
2312        // TODO: Is this assert necessary?
2313        assert!(
2314            local_name
2315                .chars()
2316                .all(|ch| !ch.is_ascii() || ch.to_ascii_lowercase() == ch)
2317        );
2318        let attribute = self.get_attribute(&ns!(), local_name);
2319
2320        match attribute {
2321            Some(ref attribute) => match *attribute.value() {
2322                AttrValue::Int(_, value) => value,
2323                _ => panic!(
2324                    "Expected an AttrValue::Int: \
2325                     implement parse_plain_attribute"
2326                ),
2327            },
2328            None => default,
2329        }
2330    }
2331
2332    pub(crate) fn set_int_attribute(&self, local_name: &LocalName, value: i32, can_gc: CanGc) {
2333        assert!(*local_name == local_name.to_ascii_lowercase());
2334        self.set_attribute(local_name, AttrValue::Int(value.to_string(), value), can_gc);
2335    }
2336
2337    pub(crate) fn get_uint_attribute(&self, local_name: &LocalName, default: u32) -> u32 {
2338        assert!(
2339            local_name
2340                .chars()
2341                .all(|ch| !ch.is_ascii() || ch.to_ascii_lowercase() == ch)
2342        );
2343        let attribute = self.get_attribute(&ns!(), local_name);
2344        match attribute {
2345            Some(ref attribute) => match *attribute.value() {
2346                AttrValue::UInt(_, value) => value,
2347                _ => panic!("Expected an AttrValue::UInt: implement parse_plain_attribute"),
2348            },
2349            None => default,
2350        }
2351    }
2352    pub(crate) fn set_uint_attribute(&self, local_name: &LocalName, value: u32, can_gc: CanGc) {
2353        assert!(*local_name == local_name.to_ascii_lowercase());
2354        self.set_attribute(
2355            local_name,
2356            AttrValue::UInt(value.to_string(), value),
2357            can_gc,
2358        );
2359    }
2360
2361    pub(crate) fn will_mutate_attr(&self, attr: &Attr) {
2362        let node = self.upcast::<Node>();
2363        node.owner_doc().element_attr_will_change(self, attr);
2364    }
2365
2366    /// <https://html.spec.whatwg.org/multipage/#the-style-attribute>
2367    fn update_style_attribute(&self, attr: &Attr, mutation: AttributeMutation) {
2368        let doc = self.upcast::<Node>().owner_doc();
2369        // Modifying the `style` attribute might change style.
2370        *self.style_attribute.borrow_mut() = match mutation {
2371            AttributeMutation::Set(..) => {
2372                // This is the fast path we use from
2373                // CSSStyleDeclaration.
2374                //
2375                // Juggle a bit to keep the borrow checker happy
2376                // while avoiding the extra clone.
2377                let is_declaration = matches!(*attr.value(), AttrValue::Declaration(..));
2378
2379                let block = if is_declaration {
2380                    let mut value = AttrValue::String(String::new());
2381                    attr.swap_value(&mut value);
2382                    let (serialization, block) = match value {
2383                        AttrValue::Declaration(s, b) => (s, b),
2384                        _ => unreachable!(),
2385                    };
2386                    let mut value = AttrValue::String(serialization);
2387                    attr.swap_value(&mut value);
2388                    block
2389                } else {
2390                    let win = self.owner_window();
2391                    let source = &**attr.value();
2392                    let global = &self.owner_global();
2393                    // However, if the Should element's inline behavior be blocked by
2394                    // Content Security Policy? algorithm returns "Blocked" when executed
2395                    // upon the attribute's element, "style attribute", and the attribute's value,
2396                    // then the style rules defined in the attribute's value must not be applied to the element. [CSP]
2397                    if global
2398                        .get_csp_list()
2399                        .should_elements_inline_type_behavior_be_blocked(
2400                            global,
2401                            self,
2402                            InlineCheckType::StyleAttribute,
2403                            source,
2404                            doc.get_current_parser_line(),
2405                        )
2406                    {
2407                        return;
2408                    }
2409                    Arc::new(doc.style_shared_lock().wrap(parse_style_attribute(
2410                        source,
2411                        &UrlExtraData(doc.base_url().get_arc()),
2412                        win.css_error_reporter(),
2413                        doc.quirks_mode(),
2414                        CssRuleType::Style,
2415                    )))
2416                };
2417
2418                Some(block)
2419            },
2420            AttributeMutation::Removed => None,
2421        };
2422    }
2423
2424    /// <https://dom.spec.whatwg.org/#concept-element-attributes-set>
2425    /// including steps of
2426    /// <https://dom.spec.whatwg.org/#concept-element-attributes-replace>
2427    fn set_attribute_node(&self, attr: &Attr, can_gc: CanGc) -> Fallible<Option<DomRoot<Attr>>> {
2428        // Step 1. Let verifiedValue be the result of calling
2429        // get Trusted Types-compliant attribute value with attr’s local name,
2430        // attr’s namespace, element, and attr’s value. [TRUSTED-TYPES]
2431        let verified_value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
2432            self.namespace(),
2433            self.local_name(),
2434            attr.local_name(),
2435            Some(attr.namespace()),
2436            TrustedTypeOrString::String(attr.Value()),
2437            &self.owner_global(),
2438            can_gc,
2439        )?;
2440
2441        // Step 2. If attr’s element is neither null nor element,
2442        // throw an "InUseAttributeError" DOMException.
2443        if let Some(owner) = attr.GetOwnerElement() {
2444            if &*owner != self {
2445                return Err(Error::InUseAttribute);
2446            }
2447        }
2448
2449        let vtable = vtable_for(self.upcast());
2450
2451        // Step 5. Set attr’s value to verifiedValue.
2452        //
2453        // This ensures that the attribute is of the expected kind for this
2454        // specific element. This is inefficient and should probably be done
2455        // differently.
2456        attr.swap_value(
2457            &mut vtable.parse_plain_attribute(attr.local_name(), verified_value.clone()),
2458        );
2459
2460        // Step 3. Let oldAttr be the result of getting an attribute given attr’s namespace, attr’s local name, and element.
2461        let position = self.attrs.borrow().iter().position(|old_attr| {
2462            attr.namespace() == old_attr.namespace() && attr.local_name() == old_attr.local_name()
2463        });
2464
2465        let old_attr = if let Some(position) = position {
2466            let old_attr = DomRoot::from_ref(&*self.attrs.borrow()[position]);
2467
2468            // Step 4. If oldAttr is attr, return attr.
2469            if &*old_attr == attr {
2470                return Ok(Some(DomRoot::from_ref(attr)));
2471            }
2472
2473            // Step 6. If oldAttr is non-null, then replace oldAttr with attr.
2474            //
2475            // Start of steps for https://dom.spec.whatwg.org/#concept-element-attributes-replace
2476
2477            // Step 1. Let element be oldAttribute’s element.
2478            //
2479            // Skipped, as that points to self.
2480
2481            // Step 2. Replace oldAttribute by newAttribute in element’s attribute list.
2482            self.will_mutate_attr(attr);
2483            self.attrs.borrow_mut()[position] = Dom::from_ref(attr);
2484            // Step 3. Set newAttribute’s element to element.
2485            attr.set_owner(Some(self));
2486            // Step 4. Set newAttribute’s node document to element’s node document.
2487            attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2488            // Step 5. Set oldAttribute’s element to null.
2489            old_attr.set_owner(None);
2490            // Step 6. Handle attribute changes for oldAttribute with element, oldAttribute’s value, and newAttribute’s value.
2491            self.handle_attribute_changes(
2492                attr,
2493                Some(&old_attr.value()),
2494                Some(verified_value),
2495                AttributeMutationReason::Directly,
2496                can_gc,
2497            );
2498
2499            Some(old_attr)
2500        } else {
2501            // Step 7. Otherwise, append attr to element.
2502            attr.set_owner(Some(self));
2503            attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2504            self.push_attribute(attr, AttributeMutationReason::Directly, can_gc);
2505
2506            None
2507        };
2508
2509        // Step 8. Return oldAttr.
2510        Ok(old_attr)
2511    }
2512
2513    /// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
2514    pub(crate) fn update_nonce_internal_slot(&self, nonce: String) {
2515        self.ensure_rare_data().cryptographic_nonce = nonce;
2516    }
2517
2518    /// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
2519    pub(crate) fn nonce_value(&self) -> String {
2520        match self.rare_data().as_ref() {
2521            None => String::new(),
2522            Some(rare_data) => rare_data.cryptographic_nonce.clone(),
2523        }
2524    }
2525
2526    /// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
2527    pub(crate) fn update_nonce_post_connection(&self) {
2528        // Whenever an element including HTMLOrSVGElement becomes browsing-context connected,
2529        // the user agent must execute the following steps on the element:
2530        if !self.upcast::<Node>().is_connected_with_browsing_context() {
2531            return;
2532        }
2533        let global = self.owner_global();
2534        // Step 1: Let CSP list be element's shadow-including root's policy container's CSP list.
2535        let csp_list = match global.get_csp_list() {
2536            None => return,
2537            Some(csp_list) => csp_list,
2538        };
2539        // Step 2: If CSP list contains a header-delivered Content Security Policy,
2540        // and element has a nonce content attribute whose value is not the empty string, then:
2541        if !csp_list.contains_a_header_delivered_content_security_policy() ||
2542            self.get_string_attribute(&local_name!("nonce")).is_empty()
2543        {
2544            return;
2545        }
2546        // Step 2.1: Let nonce be element's [[CryptographicNonce]].
2547        let nonce = self.nonce_value();
2548        // Step 2.2: Set an attribute value for element using "nonce" and the empty string.
2549        self.set_string_attribute(&local_name!("nonce"), "".into(), CanGc::note());
2550        // Step 2.3: Set element's [[CryptographicNonce]] to nonce.
2551        self.update_nonce_internal_slot(nonce);
2552    }
2553
2554    /// <https://www.w3.org/TR/CSP/#is-element-nonceable>
2555    pub(crate) fn nonce_value_if_nonceable(&self) -> Option<String> {
2556        // Step 1: If element does not have an attribute named "nonce", return "Not Nonceable".
2557        if !self.has_attribute(&local_name!("nonce")) {
2558            return None;
2559        }
2560        // Step 2: If element is a script element, then for each attribute of element’s attribute list:
2561        if self.downcast::<HTMLScriptElement>().is_some() {
2562            for attr in self.attrs().iter() {
2563                // Step 2.1: If attribute’s name contains an ASCII case-insensitive match
2564                // for "<script" or "<style", return "Not Nonceable".
2565                let attr_name = attr.name().to_ascii_lowercase();
2566                if attr_name.contains("<script") || attr_name.contains("<style") {
2567                    return None;
2568                }
2569                // Step 2.2: If attribute’s value contains an ASCII case-insensitive match
2570                // for "<script" or "<style", return "Not Nonceable".
2571                let attr_value = attr.value().to_ascii_lowercase();
2572                if attr_value.contains("<script") || attr_value.contains("<style") {
2573                    return None;
2574                }
2575            }
2576        }
2577        // Step 3: If element had a duplicate-attribute parse error during tokenization, return "Not Nonceable".
2578        // TODO(https://github.com/servo/servo/issues/4577 and https://github.com/whatwg/html/issues/3257):
2579        // Figure out how to retrieve this information from the parser
2580        // Step 4: Return "Nonceable".
2581        Some(self.nonce_value().trim().to_owned())
2582    }
2583
2584    // https://dom.spec.whatwg.org/#insert-adjacent
2585    pub(crate) fn insert_adjacent(
2586        &self,
2587        where_: AdjacentPosition,
2588        node: &Node,
2589        can_gc: CanGc,
2590    ) -> Fallible<Option<DomRoot<Node>>> {
2591        let self_node = self.upcast::<Node>();
2592        match where_ {
2593            AdjacentPosition::BeforeBegin => {
2594                if let Some(parent) = self_node.GetParentNode() {
2595                    Node::pre_insert(node, &parent, Some(self_node), can_gc).map(Some)
2596                } else {
2597                    Ok(None)
2598                }
2599            },
2600            AdjacentPosition::AfterBegin => Node::pre_insert(
2601                node,
2602                self_node,
2603                self_node.GetFirstChild().as_deref(),
2604                can_gc,
2605            )
2606            .map(Some),
2607            AdjacentPosition::BeforeEnd => {
2608                Node::pre_insert(node, self_node, None, can_gc).map(Some)
2609            },
2610            AdjacentPosition::AfterEnd => {
2611                if let Some(parent) = self_node.GetParentNode() {
2612                    Node::pre_insert(node, &parent, self_node.GetNextSibling().as_deref(), can_gc)
2613                        .map(Some)
2614                } else {
2615                    Ok(None)
2616                }
2617            },
2618        }
2619    }
2620
2621    /// <https://drafts.csswg.org/cssom-view/#dom-element-scroll>
2622    ///
2623    /// TODO(stevennovaryo): Need to update the scroll API to follow the spec since it is
2624    /// quite outdated.
2625    pub(crate) fn scroll(&self, x: f64, y: f64, behavior: ScrollBehavior) {
2626        // Step 1.2 or 2.3
2627        let x = if x.is_finite() { x } else { 0.0 } as f32;
2628        let y = if y.is_finite() { y } else { 0.0 } as f32;
2629
2630        let node = self.upcast::<Node>();
2631
2632        // Step 3
2633        let doc = node.owner_doc();
2634
2635        // Step 4
2636        if !doc.is_fully_active() {
2637            return;
2638        }
2639
2640        // Step 5
2641        let win = match doc.GetDefaultView() {
2642            None => return,
2643            Some(win) => win,
2644        };
2645
2646        // Step 7
2647        if *self.root_element() == *self {
2648            if doc.quirks_mode() != QuirksMode::Quirks {
2649                win.scroll(x, y, behavior);
2650            }
2651
2652            return;
2653        }
2654
2655        // Step 9
2656        if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
2657            doc.quirks_mode() == QuirksMode::Quirks &&
2658            !self.is_potentially_scrollable_body()
2659        {
2660            win.scroll(x, y, behavior);
2661            return;
2662        }
2663
2664        // Step 10
2665        if !self.has_css_layout_box() || !self.has_scrolling_box() || !self.has_overflow() {
2666            return;
2667        }
2668
2669        // Step 11
2670        win.scroll_an_element(self, x, y, behavior);
2671    }
2672
2673    /// <https://html.spec.whatwg.org/multipage/#fragment-parsing-algorithm-steps>
2674    pub(crate) fn parse_fragment(
2675        &self,
2676        markup: DOMString,
2677        can_gc: CanGc,
2678    ) -> Fallible<DomRoot<DocumentFragment>> {
2679        // Steps 1-2.
2680        // TODO(#11995): XML case.
2681        let new_children = ServoParser::parse_html_fragment(self, markup, false, can_gc);
2682        // Step 3.
2683        // See https://github.com/w3c/DOM-Parsing/issues/61.
2684        let context_document = {
2685            if let Some(template) = self.downcast::<HTMLTemplateElement>() {
2686                template.Content(can_gc).upcast::<Node>().owner_doc()
2687            } else {
2688                self.owner_document()
2689            }
2690        };
2691        let fragment = DocumentFragment::new(&context_document, can_gc);
2692        // Step 4.
2693        for child in new_children {
2694            fragment
2695                .upcast::<Node>()
2696                .AppendChild(&child, can_gc)
2697                .unwrap();
2698        }
2699        // Step 5.
2700        Ok(fragment)
2701    }
2702
2703    /// Step 4 of <https://html.spec.whatwg.org/multipage/#dom-element-insertadjacenthtml>
2704    /// and step 6. of <https://html.spec.whatwg.org/multipage/#dom-range-createcontextualfragment>
2705    pub(crate) fn fragment_parsing_context(
2706        owner_doc: &Document,
2707        element: Option<&Self>,
2708        can_gc: CanGc,
2709    ) -> DomRoot<Self> {
2710        // If context is not an Element or all of the following are true:
2711        match element {
2712            Some(elem)
2713                // context's node document is an HTML document;
2714                // context's local name is "html"; and
2715                // context's namespace is the HTML namespace,
2716                if elem.local_name() != &local_name!("html") ||
2717                    !elem.html_element_in_html_document() =>
2718            {
2719                DomRoot::from_ref(elem)
2720            },
2721            // set context to the result of creating an element
2722            // given this's node document, "body", and the HTML namespace.
2723            _ => Element::create(
2724                QualName::new(None, ns!(html), local_name!("body")),
2725                None,
2726                owner_doc,
2727                ElementCreator::ScriptCreated,
2728                CustomElementCreationMode::Asynchronous,
2729                None,
2730                can_gc,
2731            ),
2732        }
2733    }
2734
2735    // https://fullscreen.spec.whatwg.org/#fullscreen-element-ready-check
2736    pub(crate) fn fullscreen_element_ready_check(&self) -> bool {
2737        if !self.is_connected() {
2738            return false;
2739        }
2740        self.owner_document().get_allow_fullscreen()
2741    }
2742
2743    // https://html.spec.whatwg.org/multipage/#home-subtree
2744    pub(crate) fn is_in_same_home_subtree<T>(&self, other: &T) -> bool
2745    where
2746        T: DerivedFrom<Element> + DomObject,
2747    {
2748        let other = other.upcast::<Element>();
2749        self.root_element() == other.root_element()
2750    }
2751
2752    pub(crate) fn get_id(&self) -> Option<Atom> {
2753        self.id_attribute.borrow().clone()
2754    }
2755
2756    pub(crate) fn get_name(&self) -> Option<Atom> {
2757        self.rare_data().as_ref()?.name_attribute.clone()
2758    }
2759
2760    fn is_sequentially_focusable(&self) -> bool {
2761        let element = self.upcast::<Element>();
2762        let node = self.upcast::<Node>();
2763        if !node.is_connected() {
2764            return false;
2765        }
2766
2767        if element.has_attribute(&local_name!("hidden")) {
2768            return false;
2769        }
2770
2771        if self.disabled_state() {
2772            return false;
2773        }
2774
2775        if element.has_attribute(&local_name!("tabindex")) {
2776            return true;
2777        }
2778
2779        match node.type_id() {
2780            // <button>, <select>, <iframe>, and <textarea> are implicitly focusable.
2781            NodeTypeId::Element(ElementTypeId::HTMLElement(
2782                HTMLElementTypeId::HTMLButtonElement,
2783            )) |
2784            NodeTypeId::Element(ElementTypeId::HTMLElement(
2785                HTMLElementTypeId::HTMLSelectElement,
2786            )) |
2787            NodeTypeId::Element(ElementTypeId::HTMLElement(
2788                HTMLElementTypeId::HTMLIFrameElement,
2789            )) |
2790            NodeTypeId::Element(ElementTypeId::HTMLElement(
2791                HTMLElementTypeId::HTMLTextAreaElement,
2792            )) => true,
2793
2794            // Links that generate actual links are focusable.
2795            NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) |
2796            NodeTypeId::Element(ElementTypeId::HTMLElement(
2797                HTMLElementTypeId::HTMLAnchorElement,
2798            )) => element.has_attribute(&local_name!("href")),
2799
2800            // TODO focusable if editing host
2801            // TODO focusable if "sorting interface th elements"
2802            _ => {
2803                // Draggable elements are focusable.
2804                element.get_string_attribute(&local_name!("draggable")) == "true"
2805            },
2806        }
2807    }
2808
2809    pub(crate) fn update_sequentially_focusable_status(&self, can_gc: CanGc) {
2810        let node = self.upcast::<Node>();
2811        let is_sequentially_focusable = self.is_sequentially_focusable();
2812        node.set_flag(NodeFlags::SEQUENTIALLY_FOCUSABLE, is_sequentially_focusable);
2813
2814        // https://html.spec.whatwg.org/multipage/#focus-fixup-rule
2815        if !is_sequentially_focusable {
2816            self.owner_document().perform_focus_fixup_rule(self, can_gc);
2817        }
2818    }
2819
2820    pub(crate) fn get_element_internals(&self) -> Option<DomRoot<ElementInternals>> {
2821        self.rare_data()
2822            .as_ref()?
2823            .element_internals
2824            .as_ref()
2825            .map(|sr| DomRoot::from_ref(&**sr))
2826    }
2827
2828    pub(crate) fn ensure_element_internals(&self, can_gc: CanGc) -> DomRoot<ElementInternals> {
2829        let mut rare_data = self.ensure_rare_data();
2830        DomRoot::from_ref(rare_data.element_internals.get_or_insert_with(|| {
2831            let elem = self
2832                .downcast::<HTMLElement>()
2833                .expect("ensure_element_internals should only be called for an HTMLElement");
2834            Dom::from_ref(&*ElementInternals::new(elem, can_gc))
2835        }))
2836    }
2837
2838    pub(crate) fn outer_html(&self, can_gc: CanGc) -> Fallible<DOMString> {
2839        match self.GetOuterHTML(can_gc)? {
2840            TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => Ok(str),
2841            TrustedHTMLOrNullIsEmptyString::TrustedHTML(_) => unreachable!(),
2842        }
2843    }
2844
2845    pub(crate) fn compute_source_position(&self, line_number: u32) -> SourcePosition {
2846        SourcePosition {
2847            source_file: self.owner_global().get_url().to_string(),
2848            line_number: line_number + 2,
2849            column_number: 0,
2850        }
2851    }
2852}
2853
2854#[allow(non_snake_case)]
2855impl ElementMethods<crate::DomTypeHolder> for Element {
2856    /// <https://dom.spec.whatwg.org/#dom-element-namespaceuri>
2857    fn GetNamespaceURI(&self) -> Option<DOMString> {
2858        Node::namespace_to_string(self.namespace.clone())
2859    }
2860
2861    /// <https://dom.spec.whatwg.org/#dom-element-localname>
2862    fn LocalName(&self) -> DOMString {
2863        // FIXME(ajeffrey): Convert directly from LocalName to DOMString
2864        DOMString::from(&*self.local_name)
2865    }
2866
2867    /// <https://dom.spec.whatwg.org/#dom-element-prefix>
2868    fn GetPrefix(&self) -> Option<DOMString> {
2869        self.prefix.borrow().as_ref().map(|p| DOMString::from(&**p))
2870    }
2871
2872    /// <https://dom.spec.whatwg.org/#dom-element-tagname>
2873    fn TagName(&self) -> DOMString {
2874        let name = self.tag_name.or_init(|| {
2875            let qualified_name = match *self.prefix.borrow() {
2876                Some(ref prefix) => Cow::Owned(format!("{}:{}", &**prefix, &*self.local_name)),
2877                None => Cow::Borrowed(&*self.local_name),
2878            };
2879            if self.html_element_in_html_document() {
2880                LocalName::from(qualified_name.to_ascii_uppercase())
2881            } else {
2882                LocalName::from(qualified_name)
2883            }
2884        });
2885        DOMString::from(&*name)
2886    }
2887
2888    // https://dom.spec.whatwg.org/#dom-element-id
2889    // This always returns a string; if you'd rather see None
2890    // on a null id, call get_id
2891    fn Id(&self) -> DOMString {
2892        self.get_string_attribute(&local_name!("id"))
2893    }
2894
2895    /// <https://dom.spec.whatwg.org/#dom-element-id>
2896    fn SetId(&self, id: DOMString, can_gc: CanGc) {
2897        self.set_atomic_attribute(&local_name!("id"), id, can_gc);
2898    }
2899
2900    /// <https://dom.spec.whatwg.org/#dom-element-classname>
2901    fn ClassName(&self) -> DOMString {
2902        self.get_string_attribute(&local_name!("class"))
2903    }
2904
2905    /// <https://dom.spec.whatwg.org/#dom-element-classname>
2906    fn SetClassName(&self, class: DOMString, can_gc: CanGc) {
2907        self.set_tokenlist_attribute(&local_name!("class"), class, can_gc);
2908    }
2909
2910    /// <https://dom.spec.whatwg.org/#dom-element-classlist>
2911    fn ClassList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
2912        self.class_list
2913            .or_init(|| DOMTokenList::new(self, &local_name!("class"), None, can_gc))
2914    }
2915
2916    // https://dom.spec.whatwg.org/#dom-element-slot
2917    make_getter!(Slot, "slot");
2918
2919    // https://dom.spec.whatwg.org/#dom-element-slot
2920    make_setter!(SetSlot, "slot");
2921
2922    /// <https://dom.spec.whatwg.org/#dom-element-attributes>
2923    fn Attributes(&self, can_gc: CanGc) -> DomRoot<NamedNodeMap> {
2924        self.attr_list
2925            .or_init(|| NamedNodeMap::new(&self.owner_window(), self, can_gc))
2926    }
2927
2928    /// <https://dom.spec.whatwg.org/#dom-element-hasattributes>
2929    fn HasAttributes(&self) -> bool {
2930        !self.attrs.borrow().is_empty()
2931    }
2932
2933    /// <https://dom.spec.whatwg.org/#dom-element-getattributenames>
2934    fn GetAttributeNames(&self) -> Vec<DOMString> {
2935        self.attrs.borrow().iter().map(|attr| attr.Name()).collect()
2936    }
2937
2938    /// <https://dom.spec.whatwg.org/#dom-element-getattribute>
2939    fn GetAttribute(&self, name: DOMString) -> Option<DOMString> {
2940        self.GetAttributeNode(name).map(|s| s.Value())
2941    }
2942
2943    /// <https://dom.spec.whatwg.org/#dom-element-getattributens>
2944    fn GetAttributeNS(
2945        &self,
2946        namespace: Option<DOMString>,
2947        local_name: DOMString,
2948    ) -> Option<DOMString> {
2949        self.GetAttributeNodeNS(namespace, local_name)
2950            .map(|attr| attr.Value())
2951    }
2952
2953    /// <https://dom.spec.whatwg.org/#dom-element-getattributenode>
2954    fn GetAttributeNode(&self, name: DOMString) -> Option<DomRoot<Attr>> {
2955        self.get_attribute_by_name(name)
2956    }
2957
2958    /// <https://dom.spec.whatwg.org/#dom-element-getattributenodens>
2959    fn GetAttributeNodeNS(
2960        &self,
2961        namespace: Option<DOMString>,
2962        local_name: DOMString,
2963    ) -> Option<DomRoot<Attr>> {
2964        let namespace = &namespace_from_domstring(namespace);
2965        self.get_attribute(namespace, &LocalName::from(local_name))
2966    }
2967
2968    /// <https://dom.spec.whatwg.org/#dom-element-toggleattribute>
2969    fn ToggleAttribute(
2970        &self,
2971        name: DOMString,
2972        force: Option<bool>,
2973        can_gc: CanGc,
2974    ) -> Fallible<bool> {
2975        // Step 1. If qualifiedName is not a valid attribute local name,
2976        //      then throw an "InvalidCharacterError" DOMException.
2977        if !is_valid_attribute_local_name(&name.str()) {
2978            return Err(Error::InvalidCharacter(None));
2979        }
2980
2981        // Step 3.
2982        let attribute = self.GetAttribute(name.clone());
2983
2984        // Step 2.
2985        let name = self.parsed_name(name);
2986        match attribute {
2987            // Step 4
2988            None => match force {
2989                // Step 4.1.
2990                None | Some(true) => {
2991                    self.set_first_matching_attribute(
2992                        name.clone(),
2993                        AttrValue::String(String::new()),
2994                        name.clone(),
2995                        ns!(),
2996                        None,
2997                        |attr| *attr.name() == name,
2998                        can_gc,
2999                    );
3000                    Ok(true)
3001                },
3002                // Step 4.2.
3003                Some(false) => Ok(false),
3004            },
3005            Some(_index) => match force {
3006                // Step 5.
3007                None | Some(false) => {
3008                    self.remove_attribute_by_name(&name, can_gc);
3009                    Ok(false)
3010                },
3011                // Step 6.
3012                Some(true) => Ok(true),
3013            },
3014        }
3015    }
3016
3017    /// <https://dom.spec.whatwg.org/#dom-element-setattribute>
3018    fn SetAttribute(
3019        &self,
3020        name: DOMString,
3021        value: TrustedTypeOrString,
3022        can_gc: CanGc,
3023    ) -> ErrorResult {
3024        // Step 1. If qualifiedName does not match the Name production in XML,
3025        // then throw an "InvalidCharacterError" DOMException.
3026        if !is_valid_attribute_local_name(&name.str()) {
3027            return Err(Error::InvalidCharacter(None));
3028        }
3029
3030        // Step 2. If this is in the HTML namespace and its node document is an HTML document,
3031        // then set qualifiedName to qualifiedName in ASCII lowercase.
3032        let name = self.parsed_name(name);
3033
3034        // Step 3. Let verifiedValue be the result of calling get
3035        // Trusted Types-compliant attribute value with qualifiedName, null,
3036        // this, and value. [TRUSTED-TYPES]
3037        let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3038            self.namespace(),
3039            self.local_name(),
3040            &name,
3041            None,
3042            value,
3043            &self.owner_global(),
3044            can_gc,
3045        )?;
3046
3047        // Step 4. Let attribute be the first attribute in this’s attribute list whose qualified name is qualifiedName, and null otherwise.
3048        // Step 5. If attribute is null, create an attribute whose local name is qualifiedName, value is verifiedValue, and node document
3049        // is this’s node document, then append this attribute to this, and then return.
3050        // Step 6. Change attribute to verifiedValue.
3051        let value = self.parse_attribute(&ns!(), &name, value);
3052        self.set_first_matching_attribute(
3053            name.clone(),
3054            value,
3055            name.clone(),
3056            ns!(),
3057            None,
3058            |attr| *attr.name() == name,
3059            can_gc,
3060        );
3061        Ok(())
3062    }
3063
3064    /// <https://dom.spec.whatwg.org/#dom-element-setattributens>
3065    fn SetAttributeNS(
3066        &self,
3067        namespace: Option<DOMString>,
3068        qualified_name: DOMString,
3069        value: TrustedTypeOrString,
3070        can_gc: CanGc,
3071    ) -> ErrorResult {
3072        // Step 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
3073        let (namespace, prefix, local_name) =
3074            domname::validate_and_extract(namespace, &qualified_name, domname::Context::Element)?;
3075        // Step 2. Let verifiedValue be the result of calling get
3076        // Trusted Types-compliant attribute value with localName, namespace, element, and value. [TRUSTED-TYPES]
3077        let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3078            self.namespace(),
3079            self.local_name(),
3080            &local_name,
3081            Some(&namespace),
3082            value,
3083            &self.owner_global(),
3084            can_gc,
3085        )?;
3086        // Step 3. Set an attribute value for this using localName, verifiedValue, and also prefix and namespace.
3087        let value = self.parse_attribute(&namespace, &local_name, value);
3088        self.set_first_matching_attribute(
3089            local_name.clone(),
3090            value,
3091            LocalName::from(qualified_name),
3092            namespace.clone(),
3093            prefix,
3094            |attr| *attr.local_name() == local_name && *attr.namespace() == namespace,
3095            can_gc,
3096        );
3097        Ok(())
3098    }
3099
3100    /// <https://dom.spec.whatwg.org/#dom-element-setattributenode>
3101    fn SetAttributeNode(&self, attr: &Attr, can_gc: CanGc) -> Fallible<Option<DomRoot<Attr>>> {
3102        self.set_attribute_node(attr, can_gc)
3103    }
3104
3105    /// <https://dom.spec.whatwg.org/#dom-element-setattributenodens>
3106    fn SetAttributeNodeNS(&self, attr: &Attr, can_gc: CanGc) -> Fallible<Option<DomRoot<Attr>>> {
3107        self.set_attribute_node(attr, can_gc)
3108    }
3109
3110    /// <https://dom.spec.whatwg.org/#dom-element-removeattribute>
3111    fn RemoveAttribute(&self, name: DOMString, can_gc: CanGc) {
3112        let name = self.parsed_name(name);
3113        self.remove_attribute_by_name(&name, can_gc);
3114    }
3115
3116    /// <https://dom.spec.whatwg.org/#dom-element-removeattributens>
3117    fn RemoveAttributeNS(
3118        &self,
3119        namespace: Option<DOMString>,
3120        local_name: DOMString,
3121        can_gc: CanGc,
3122    ) {
3123        let namespace = namespace_from_domstring(namespace);
3124        let local_name = LocalName::from(local_name);
3125        self.remove_attribute(&namespace, &local_name, can_gc);
3126    }
3127
3128    /// <https://dom.spec.whatwg.org/#dom-element-removeattributenode>
3129    fn RemoveAttributeNode(&self, attr: &Attr, can_gc: CanGc) -> Fallible<DomRoot<Attr>> {
3130        self.remove_first_matching_attribute(|a| a == attr, can_gc)
3131            .ok_or(Error::NotFound(None))
3132    }
3133
3134    /// <https://dom.spec.whatwg.org/#dom-element-hasattribute>
3135    fn HasAttribute(&self, name: DOMString) -> bool {
3136        self.GetAttribute(name).is_some()
3137    }
3138
3139    /// <https://dom.spec.whatwg.org/#dom-element-hasattributens>
3140    fn HasAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> bool {
3141        self.GetAttributeNS(namespace, local_name).is_some()
3142    }
3143
3144    /// <https://dom.spec.whatwg.org/#dom-element-getelementsbytagname>
3145    fn GetElementsByTagName(&self, localname: DOMString, can_gc: CanGc) -> DomRoot<HTMLCollection> {
3146        let window = self.owner_window();
3147        HTMLCollection::by_qualified_name(
3148            &window,
3149            self.upcast(),
3150            LocalName::from(localname),
3151            can_gc,
3152        )
3153    }
3154
3155    /// <https://dom.spec.whatwg.org/#dom-element-getelementsbytagnamens>
3156    fn GetElementsByTagNameNS(
3157        &self,
3158        maybe_ns: Option<DOMString>,
3159        localname: DOMString,
3160        can_gc: CanGc,
3161    ) -> DomRoot<HTMLCollection> {
3162        let window = self.owner_window();
3163        HTMLCollection::by_tag_name_ns(&window, self.upcast(), localname, maybe_ns, can_gc)
3164    }
3165
3166    /// <https://dom.spec.whatwg.org/#dom-element-getelementsbyclassname>
3167    fn GetElementsByClassName(&self, classes: DOMString, can_gc: CanGc) -> DomRoot<HTMLCollection> {
3168        let window = self.owner_window();
3169        HTMLCollection::by_class_name(&window, self.upcast(), classes, can_gc)
3170    }
3171
3172    /// <https://drafts.csswg.org/cssom-view/#dom-element-getclientrects>
3173    fn GetClientRects(&self, can_gc: CanGc) -> DomRoot<DOMRectList> {
3174        let win = self.owner_window();
3175        let raw_rects = self.upcast::<Node>().border_boxes();
3176        let rects: Vec<DomRoot<DOMRect>> = raw_rects
3177            .iter()
3178            .map(|rect| {
3179                DOMRect::new(
3180                    win.upcast(),
3181                    rect.origin.x.to_f64_px(),
3182                    rect.origin.y.to_f64_px(),
3183                    rect.size.width.to_f64_px(),
3184                    rect.size.height.to_f64_px(),
3185                    can_gc,
3186                )
3187            })
3188            .collect();
3189        DOMRectList::new(&win, rects, can_gc)
3190    }
3191
3192    /// <https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect>
3193    fn GetBoundingClientRect(&self, can_gc: CanGc) -> DomRoot<DOMRect> {
3194        let win = self.owner_window();
3195        let rect = self.upcast::<Node>().border_box().unwrap_or_default();
3196        DOMRect::new(
3197            win.upcast(),
3198            rect.origin.x.to_f64_px(),
3199            rect.origin.y.to_f64_px(),
3200            rect.size.width.to_f64_px(),
3201            rect.size.height.to_f64_px(),
3202            can_gc,
3203        )
3204    }
3205
3206    /// <https://drafts.csswg.org/cssom-view/#dom-element-scroll>
3207    fn Scroll(&self, options: &ScrollToOptions) {
3208        // Step 1
3209        let left = options.left.unwrap_or(self.ScrollLeft());
3210        let top = options.top.unwrap_or(self.ScrollTop());
3211        self.scroll(left, top, options.parent.behavior);
3212    }
3213
3214    /// <https://drafts.csswg.org/cssom-view/#dom-element-scroll>
3215    fn Scroll_(&self, x: f64, y: f64) {
3216        self.scroll(x, y, ScrollBehavior::Auto);
3217    }
3218
3219    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollto>
3220    fn ScrollTo(&self, options: &ScrollToOptions) {
3221        self.Scroll(options);
3222    }
3223
3224    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollto>
3225    fn ScrollTo_(&self, x: f64, y: f64) {
3226        self.Scroll_(x, y);
3227    }
3228
3229    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollby>
3230    fn ScrollBy(&self, options: &ScrollToOptions) {
3231        // Step 2
3232        let delta_left = options.left.unwrap_or(0.0f64);
3233        let delta_top = options.top.unwrap_or(0.0f64);
3234        let left = self.ScrollLeft();
3235        let top = self.ScrollTop();
3236        self.scroll(left + delta_left, top + delta_top, options.parent.behavior);
3237    }
3238
3239    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollby>
3240    fn ScrollBy_(&self, x: f64, y: f64) {
3241        let left = self.ScrollLeft();
3242        let top = self.ScrollTop();
3243        self.scroll(left + x, top + y, ScrollBehavior::Auto);
3244    }
3245
3246    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrolltop>
3247    fn ScrollTop(&self) -> f64 {
3248        let node = self.upcast::<Node>();
3249
3250        // Step 1
3251        let doc = node.owner_doc();
3252
3253        // Step 2
3254        if !doc.is_fully_active() {
3255            return 0.0;
3256        }
3257
3258        // Step 3
3259        let win = match doc.GetDefaultView() {
3260            None => return 0.0,
3261            Some(win) => win,
3262        };
3263
3264        // Step 5
3265        if *self.root_element() == *self {
3266            if doc.quirks_mode() == QuirksMode::Quirks {
3267                return 0.0;
3268            }
3269
3270            // Step 6
3271            return win.ScrollY() as f64;
3272        }
3273
3274        // Step 7
3275        if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3276            doc.quirks_mode() == QuirksMode::Quirks &&
3277            !self.is_potentially_scrollable_body()
3278        {
3279            return win.ScrollY() as f64;
3280        }
3281
3282        // Step 8
3283        if !self.has_css_layout_box() {
3284            return 0.0;
3285        }
3286
3287        // Step 9
3288        let point = win.scroll_offset_query(node);
3289        point.y.abs() as f64
3290    }
3291
3292    // https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
3293    // TODO(stevennovaryo): Need to update the scroll API to follow the spec since it is quite outdated.
3294    fn SetScrollTop(&self, y_: f64) {
3295        let behavior = ScrollBehavior::Auto;
3296
3297        // Step 1, 2
3298        let y = if y_.is_finite() { y_ } else { 0.0 } as f32;
3299
3300        let node = self.upcast::<Node>();
3301
3302        // Step 3
3303        let doc = node.owner_doc();
3304
3305        // Step 4
3306        if !doc.is_fully_active() {
3307            return;
3308        }
3309
3310        // Step 5
3311        let win = match doc.GetDefaultView() {
3312            None => return,
3313            Some(win) => win,
3314        };
3315
3316        // Step 7
3317        if *self.root_element() == *self {
3318            if doc.quirks_mode() != QuirksMode::Quirks {
3319                win.scroll(win.ScrollX() as f32, y, behavior);
3320            }
3321
3322            return;
3323        }
3324
3325        // Step 9
3326        if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3327            doc.quirks_mode() == QuirksMode::Quirks &&
3328            !self.is_potentially_scrollable_body()
3329        {
3330            win.scroll(win.ScrollX() as f32, y, behavior);
3331            return;
3332        }
3333
3334        // Step 10
3335        if !self.has_css_layout_box() || !self.has_scrolling_box() || !self.has_overflow() {
3336            return;
3337        }
3338
3339        // Step 11
3340        win.scroll_an_element(self, self.ScrollLeft() as f32, y, behavior);
3341    }
3342
3343    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollleft>
3344    fn ScrollLeft(&self) -> f64 {
3345        let node = self.upcast::<Node>();
3346
3347        // Step 1
3348        let doc = node.owner_doc();
3349
3350        // Step 2
3351        if !doc.is_fully_active() {
3352            return 0.0;
3353        }
3354
3355        // Step 3
3356        let win = match doc.GetDefaultView() {
3357            None => return 0.0,
3358            Some(win) => win,
3359        };
3360
3361        // Step 5
3362        if *self.root_element() == *self {
3363            if doc.quirks_mode() != QuirksMode::Quirks {
3364                // Step 6
3365                return win.ScrollX() as f64;
3366            }
3367
3368            return 0.0;
3369        }
3370
3371        // Step 7
3372        if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3373            doc.quirks_mode() == QuirksMode::Quirks &&
3374            !self.is_potentially_scrollable_body()
3375        {
3376            return win.ScrollX() as f64;
3377        }
3378
3379        // Step 8
3380        if !self.has_css_layout_box() {
3381            return 0.0;
3382        }
3383
3384        // Step 9
3385        let point = win.scroll_offset_query(node);
3386        point.x.abs() as f64
3387    }
3388
3389    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollleft>
3390    fn SetScrollLeft(&self, x: f64) {
3391        let behavior = ScrollBehavior::Auto;
3392
3393        // Step 1, 2
3394        let x = if x.is_finite() { x } else { 0.0 } as f32;
3395
3396        let node = self.upcast::<Node>();
3397
3398        // Step 3
3399        let doc = node.owner_doc();
3400
3401        // Step 4
3402        if !doc.is_fully_active() {
3403            return;
3404        }
3405
3406        // Step 5
3407        let win = match doc.GetDefaultView() {
3408            None => return,
3409            Some(win) => win,
3410        };
3411
3412        // Step 7
3413        if *self.root_element() == *self {
3414            if doc.quirks_mode() == QuirksMode::Quirks {
3415                return;
3416            }
3417
3418            win.scroll(x, win.ScrollY() as f32, behavior);
3419            return;
3420        }
3421
3422        // Step 9
3423        if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3424            doc.quirks_mode() == QuirksMode::Quirks &&
3425            !self.is_potentially_scrollable_body()
3426        {
3427            win.scroll(x, win.ScrollY() as f32, behavior);
3428            return;
3429        }
3430
3431        // Step 10
3432        if !self.has_css_layout_box() || !self.has_scrolling_box() || !self.has_overflow() {
3433            return;
3434        }
3435
3436        // Step 11
3437        win.scroll_an_element(self, x, self.ScrollTop() as f32, behavior);
3438    }
3439
3440    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview>
3441    fn ScrollIntoView(&self, arg: BooleanOrScrollIntoViewOptions) {
3442        let (behavior, block, inline, container) = match arg {
3443            // If arg is true:
3444            BooleanOrScrollIntoViewOptions::Boolean(true) => (
3445                ScrollBehavior::Auto,           // Step 1: Let behavior be "auto".
3446                ScrollLogicalPosition::Start,   // Step 2: Let block be "start".
3447                ScrollLogicalPosition::Nearest, // Step 3: Let inline be "nearest".
3448                None,                           // Step 4: Let container be null.
3449            ),
3450            // Step 5: If arg is a ScrollIntoViewOptions dictionary, set its properties
3451            // to the corresponding values in the dictionary.
3452            BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(options) => (
3453                options.parent.behavior,
3454                options.block,
3455                options.inline,
3456                // Step 5.4: If the container dictionary member of options is "nearest",
3457                // set container to the element.
3458                if options.container == ScrollIntoViewContainer::Nearest {
3459                    Some(self)
3460                } else {
3461                    None
3462                },
3463            ),
3464            // Step 6: Otherwise, if arg is false, then set block to "end".
3465            BooleanOrScrollIntoViewOptions::Boolean(false) => (
3466                ScrollBehavior::Auto,
3467                ScrollLogicalPosition::End,
3468                ScrollLogicalPosition::Nearest,
3469                None,
3470            ),
3471        };
3472
3473        // Step 7: If the element does not have any associated box, or is not
3474        // available to user-agent features, then return.
3475        if !self.has_css_layout_box() {
3476            return;
3477        }
3478
3479        // Step 8: Scroll the element into view with behavior, block, inline, and container.
3480        self.scroll_into_view_with_options(behavior, block, inline, container, None);
3481
3482        // Step 9: Optionally perform some other action that brings the
3483        // element to the user’s attention.
3484    }
3485
3486    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth>
3487    fn ScrollWidth(&self) -> i32 {
3488        self.upcast::<Node>().scroll_area().size.width
3489    }
3490
3491    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollheight>
3492    fn ScrollHeight(&self) -> i32 {
3493        self.upcast::<Node>().scroll_area().size.height
3494    }
3495
3496    /// <https://drafts.csswg.org/cssom-view/#dom-element-clienttop>
3497    fn ClientTop(&self) -> i32 {
3498        self.client_rect().origin.y
3499    }
3500
3501    /// <https://drafts.csswg.org/cssom-view/#dom-element-clientleft>
3502    fn ClientLeft(&self) -> i32 {
3503        self.client_rect().origin.x
3504    }
3505
3506    /// <https://drafts.csswg.org/cssom-view/#dom-element-clientwidth>
3507    fn ClientWidth(&self) -> i32 {
3508        self.client_rect().size.width
3509    }
3510
3511    /// <https://drafts.csswg.org/cssom-view/#dom-element-clientheight>
3512    fn ClientHeight(&self) -> i32 {
3513        self.client_rect().size.height
3514    }
3515
3516    // https://drafts.csswg.org/cssom-view/#dom-element-currentcsszoom
3517    fn CurrentCSSZoom(&self) -> Finite<f64> {
3518        let window = self.owner_window();
3519        Finite::wrap(window.current_css_zoom_query(self.upcast::<Node>()) as f64)
3520    }
3521
3522    /// <https://html.spec.whatwg.org/multipage/#dom-element-sethtmlunsafe>
3523    fn SetHTMLUnsafe(&self, html: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult {
3524        // Step 1. Let compliantHTML be the result of invoking the
3525        // Get Trusted Type compliant string algorithm with TrustedHTML,
3526        // this's relevant global object, html, "Element setHTMLUnsafe", and "script".
3527        let html = TrustedHTML::get_trusted_script_compliant_string(
3528            &self.owner_global(),
3529            html,
3530            "Element setHTMLUnsafe",
3531            can_gc,
3532        )?;
3533        // Step 2. Let target be this's template contents if this is a template element; otherwise this.
3534        let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3535            DomRoot::upcast(template.Content(can_gc))
3536        } else {
3537            DomRoot::from_ref(self.upcast())
3538        };
3539
3540        // Step 3. Unsafely set HTML given target, this, and compliantHTML
3541        Node::unsafely_set_html(&target, self, html, can_gc);
3542        Ok(())
3543    }
3544
3545    /// <https://html.spec.whatwg.org/multipage/#dom-element-gethtml>
3546    fn GetHTML(&self, options: &GetHTMLOptions, can_gc: CanGc) -> DOMString {
3547        // > Element's getHTML(options) method steps are to return the result of HTML fragment serialization
3548        // > algorithm with this, options["serializableShadowRoots"], and options["shadowRoots"].
3549        self.upcast::<Node>().html_serialize(
3550            TraversalScope::ChildrenOnly(None),
3551            options.serializableShadowRoots,
3552            options.shadowRoots.clone(),
3553            can_gc,
3554        )
3555    }
3556
3557    /// <https://html.spec.whatwg.org/multipage/#dom-element-innerhtml>
3558    fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3559        let qname = QualName::new(
3560            self.prefix().clone(),
3561            self.namespace().clone(),
3562            self.local_name().clone(),
3563        );
3564
3565        // FIXME: This should use the fragment serialization algorithm, which takes
3566        // care of distinguishing between html/xml documents
3567        let result = if self.owner_document().is_html_document() {
3568            self.upcast::<Node>()
3569                .html_serialize(ChildrenOnly(Some(qname)), false, vec![], can_gc)
3570        } else {
3571            self.upcast::<Node>()
3572                .xml_serialize(XmlChildrenOnly(Some(qname)))?
3573        };
3574
3575        Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3576    }
3577
3578    /// <https://html.spec.whatwg.org/multipage/#dom-element-innerhtml>
3579    fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult {
3580        // Step 1: Let compliantString be the result of invoking the
3581        // Get Trusted Type compliant string algorithm with TrustedHTML,
3582        // this's relevant global object, the given value, "Element innerHTML", and "script".
3583        let value = TrustedHTML::get_trusted_script_compliant_string(
3584            &self.owner_global(),
3585            value.convert(),
3586            "Element innerHTML",
3587            can_gc,
3588        )?;
3589        // https://github.com/w3c/DOM-Parsing/issues/1
3590        let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3591            // Step 4: If context is a template element, then set context to
3592            // the template element's template contents (a DocumentFragment).
3593            DomRoot::upcast(template.Content(can_gc))
3594        } else {
3595            // Step 2: Let context be this.
3596            DomRoot::from_ref(self.upcast())
3597        };
3598
3599        // Fast path for when the value is small, doesn't contain any markup and doesn't require
3600        // extra work to set innerHTML.
3601        if !self.node.has_weird_parser_insertion_mode() &&
3602            value.len() < 100 &&
3603            !value
3604                .as_bytes()
3605                .iter()
3606                .any(|c| matches!(*c, b'&' | b'\0' | b'<' | b'\r'))
3607        {
3608            return Node::SetTextContent(&target, Some(value), can_gc);
3609        }
3610
3611        // Step 3: Let fragment be the result of invoking the fragment parsing algorithm steps
3612        // with context and compliantString.
3613        let frag = self.parse_fragment(value, can_gc)?;
3614
3615        // Step 5: Replace all with fragment within context.
3616        Node::replace_all(Some(frag.upcast()), &target, can_gc);
3617        Ok(())
3618    }
3619
3620    /// <https://html.spec.whatwg.org/multipage/#dom-element-outerhtml>
3621    fn GetOuterHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3622        // FIXME: This should use the fragment serialization algorithm, which takes
3623        // care of distinguishing between html/xml documents
3624        let result = if self.owner_document().is_html_document() {
3625            self.upcast::<Node>()
3626                .html_serialize(IncludeNode, false, vec![], can_gc)
3627        } else {
3628            self.upcast::<Node>().xml_serialize(XmlIncludeNode)?
3629        };
3630
3631        Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3632    }
3633
3634    /// <https://html.spec.whatwg.org/multipage/#dom-element-outerhtml>
3635    fn SetOuterHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult {
3636        // Step 1: Let compliantString be the result of invoking the
3637        // Get Trusted Type compliant string algorithm with TrustedHTML,
3638        // this's relevant global object, the given value, "Element outerHTML", and "script".
3639        let value = TrustedHTML::get_trusted_script_compliant_string(
3640            &self.owner_global(),
3641            value.convert(),
3642            "Element outerHTML",
3643            can_gc,
3644        )?;
3645        let context_document = self.owner_document();
3646        let context_node = self.upcast::<Node>();
3647        // Step 2: Let parent be this's parent.
3648        let context_parent = match context_node.GetParentNode() {
3649            None => {
3650                // Step 3: If parent is null, return. There would be no way to
3651                // obtain a reference to the nodes created even if the remaining steps were run.
3652                return Ok(());
3653            },
3654            Some(parent) => parent,
3655        };
3656
3657        let parent = match context_parent.type_id() {
3658            // Step 4: If parent is a Document, throw a "NoModificationAllowedError" DOMException.
3659            NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed),
3660
3661            // Step 5: If parent is a DocumentFragment, set parent to the result of
3662            // creating an element given this's node document, "body", and the HTML namespace.
3663            NodeTypeId::DocumentFragment(_) => {
3664                let body_elem = Element::create(
3665                    QualName::new(None, ns!(html), local_name!("body")),
3666                    None,
3667                    &context_document,
3668                    ElementCreator::ScriptCreated,
3669                    CustomElementCreationMode::Synchronous,
3670                    None,
3671                    can_gc,
3672                );
3673                DomRoot::upcast(body_elem)
3674            },
3675            _ => context_node.GetParentElement().unwrap(),
3676        };
3677
3678        // Step 6: Let fragment be the result of invoking the
3679        // fragment parsing algorithm steps given parent and compliantString.
3680        let frag = parent.parse_fragment(value, can_gc)?;
3681        // Step 7: Replace this with fragment within this's parent.
3682        context_parent.ReplaceChild(frag.upcast(), context_node, can_gc)?;
3683        Ok(())
3684    }
3685
3686    /// <https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling>
3687    fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
3688        self.upcast::<Node>()
3689            .preceding_siblings()
3690            .find_map(DomRoot::downcast)
3691    }
3692
3693    /// <https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-nextelementsibling>
3694    fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
3695        self.upcast::<Node>()
3696            .following_siblings()
3697            .find_map(DomRoot::downcast)
3698    }
3699
3700    /// <https://dom.spec.whatwg.org/#dom-parentnode-children>
3701    fn Children(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
3702        let window = self.owner_window();
3703        HTMLCollection::children(&window, self.upcast(), can_gc)
3704    }
3705
3706    /// <https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild>
3707    fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
3708        self.upcast::<Node>().child_elements().next()
3709    }
3710
3711    /// <https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild>
3712    fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
3713        self.upcast::<Node>()
3714            .rev_children()
3715            .find_map(DomRoot::downcast::<Element>)
3716    }
3717
3718    /// <https://dom.spec.whatwg.org/#dom-parentnode-childelementcount>
3719    fn ChildElementCount(&self) -> u32 {
3720        self.upcast::<Node>().child_elements().count() as u32
3721    }
3722
3723    /// <https://dom.spec.whatwg.org/#dom-parentnode-prepend>
3724    fn Prepend(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3725        self.upcast::<Node>().prepend(nodes, can_gc)
3726    }
3727
3728    /// <https://dom.spec.whatwg.org/#dom-parentnode-append>
3729    fn Append(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3730        self.upcast::<Node>().append(nodes, can_gc)
3731    }
3732
3733    /// <https://dom.spec.whatwg.org/#dom-parentnode-replacechildren>
3734    fn ReplaceChildren(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3735        self.upcast::<Node>().replace_children(nodes, can_gc)
3736    }
3737
3738    /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
3739    fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
3740        let root = self.upcast::<Node>();
3741        root.query_selector(selectors)
3742    }
3743
3744    /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall>
3745    fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>> {
3746        let root = self.upcast::<Node>();
3747        root.query_selector_all(selectors)
3748    }
3749
3750    /// <https://dom.spec.whatwg.org/#dom-childnode-before>
3751    fn Before(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3752        self.upcast::<Node>().before(nodes, can_gc)
3753    }
3754
3755    /// <https://dom.spec.whatwg.org/#dom-childnode-after>
3756    fn After(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3757        self.upcast::<Node>().after(nodes, can_gc)
3758    }
3759
3760    /// <https://dom.spec.whatwg.org/#dom-childnode-replacewith>
3761    fn ReplaceWith(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
3762        self.upcast::<Node>().replace_with(nodes, can_gc)
3763    }
3764
3765    /// <https://dom.spec.whatwg.org/#dom-childnode-remove>
3766    fn Remove(&self, can_gc: CanGc) {
3767        self.upcast::<Node>().remove_self(can_gc);
3768    }
3769
3770    /// <https://dom.spec.whatwg.org/#dom-element-matches>
3771    fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
3772        let doc = self.owner_document();
3773        let url = doc.url();
3774        let selectors = match SelectorParser::parse_author_origin_no_namespace(
3775            &selectors.str(),
3776            &UrlExtraData(url.get_arc()),
3777        ) {
3778            Err(_) => return Err(Error::Syntax(None)),
3779            Ok(selectors) => selectors,
3780        };
3781
3782        let quirks_mode = doc.quirks_mode();
3783        let element = DomRoot::from_ref(self);
3784
3785        Ok(dom_apis::element_matches(
3786            &SelectorWrapper::Borrowed(&element),
3787            &selectors,
3788            quirks_mode,
3789        ))
3790    }
3791
3792    /// <https://dom.spec.whatwg.org/#dom-element-webkitmatchesselector>
3793    fn WebkitMatchesSelector(&self, selectors: DOMString) -> Fallible<bool> {
3794        self.Matches(selectors)
3795    }
3796
3797    /// <https://dom.spec.whatwg.org/#dom-element-closest>
3798    fn Closest(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
3799        let doc = self.owner_document();
3800        let url = doc.url();
3801        let selectors = match SelectorParser::parse_author_origin_no_namespace(
3802            &selectors.str(),
3803            &UrlExtraData(url.get_arc()),
3804        ) {
3805            Err(_) => return Err(Error::Syntax(None)),
3806            Ok(selectors) => selectors,
3807        };
3808
3809        let quirks_mode = doc.quirks_mode();
3810        Ok(dom_apis::element_closest(
3811            SelectorWrapper::Owned(DomRoot::from_ref(self)),
3812            &selectors,
3813            quirks_mode,
3814        )
3815        .map(SelectorWrapper::into_owned))
3816    }
3817
3818    /// <https://dom.spec.whatwg.org/#dom-element-insertadjacentelement>
3819    fn InsertAdjacentElement(
3820        &self,
3821        where_: DOMString,
3822        element: &Element,
3823        can_gc: CanGc,
3824    ) -> Fallible<Option<DomRoot<Element>>> {
3825        let where_ = where_.parse::<AdjacentPosition>()?;
3826        let inserted_node = self.insert_adjacent(where_, element.upcast(), can_gc)?;
3827        Ok(inserted_node.map(|node| DomRoot::downcast(node).unwrap()))
3828    }
3829
3830    /// <https://dom.spec.whatwg.org/#dom-element-insertadjacenttext>
3831    fn InsertAdjacentText(&self, where_: DOMString, data: DOMString, can_gc: CanGc) -> ErrorResult {
3832        // Step 1.
3833        let text = Text::new(data, &self.owner_document(), can_gc);
3834
3835        // Step 2.
3836        let where_ = where_.parse::<AdjacentPosition>()?;
3837        self.insert_adjacent(where_, text.upcast(), can_gc)
3838            .map(|_| ())
3839    }
3840
3841    /// <https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml>
3842    fn InsertAdjacentHTML(
3843        &self,
3844        position: DOMString,
3845        text: TrustedHTMLOrString,
3846        can_gc: CanGc,
3847    ) -> ErrorResult {
3848        // Step 1: Let compliantString be the result of invoking the
3849        // Get Trusted Type compliant string algorithm with TrustedHTML,
3850        // this's relevant global object, string, "Element insertAdjacentHTML", and "script".
3851        let text = TrustedHTML::get_trusted_script_compliant_string(
3852            &self.owner_global(),
3853            text,
3854            "Element insertAdjacentHTML",
3855            can_gc,
3856        )?;
3857        let position = position.parse::<AdjacentPosition>()?;
3858
3859        // Step 2: Let context be null.
3860        // Step 3: Use the first matching item from this list:
3861        let context = match position {
3862            // If position is an ASCII case-insensitive match for the string "beforebegin"
3863            // If position is an ASCII case-insensitive match for the string "afterend"
3864            AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => {
3865                match self.upcast::<Node>().GetParentNode() {
3866                    // Step 3.2: If context is null or a Document, throw a "NoModificationAllowedError" DOMException.
3867                    Some(ref node) if node.is::<Document>() => {
3868                        return Err(Error::NoModificationAllowed);
3869                    },
3870                    None => return Err(Error::NoModificationAllowed),
3871                    // Step 3.1: Set context to this's parent.
3872                    Some(node) => node,
3873                }
3874            },
3875            // If position is an ASCII case-insensitive match for the string "afterbegin"
3876            // If position is an ASCII case-insensitive match for the string "beforeend"
3877            AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => {
3878                // Set context to this.
3879                DomRoot::from_ref(self.upcast::<Node>())
3880            },
3881        };
3882
3883        // Step 4.
3884        let context = Element::fragment_parsing_context(
3885            &context.owner_doc(),
3886            context.downcast::<Element>(),
3887            can_gc,
3888        );
3889
3890        // Step 5: Let fragment be the result of invoking the
3891        // fragment parsing algorithm steps with context and compliantString.
3892        let fragment = context.parse_fragment(text, can_gc)?;
3893
3894        // Step 6.
3895        self.insert_adjacent(position, fragment.upcast(), can_gc)
3896            .map(|_| ())
3897    }
3898
3899    // check-tidy: no specs after this line
3900    fn EnterFormalActivationState(&self) -> ErrorResult {
3901        match self.as_maybe_activatable() {
3902            Some(a) => {
3903                a.enter_formal_activation_state();
3904                Ok(())
3905            },
3906            None => Err(Error::NotSupported),
3907        }
3908    }
3909
3910    fn ExitFormalActivationState(&self) -> ErrorResult {
3911        match self.as_maybe_activatable() {
3912            Some(a) => {
3913                a.exit_formal_activation_state();
3914                Ok(())
3915            },
3916            None => Err(Error::NotSupported),
3917        }
3918    }
3919
3920    /// <https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen>
3921    fn RequestFullscreen(&self, can_gc: CanGc) -> Rc<Promise> {
3922        let doc = self.owner_document();
3923        doc.enter_fullscreen(self, can_gc)
3924    }
3925
3926    /// <https://dom.spec.whatwg.org/#dom-element-attachshadow>
3927    fn AttachShadow(&self, init: &ShadowRootInit, can_gc: CanGc) -> Fallible<DomRoot<ShadowRoot>> {
3928        // Step 1. Run attach a shadow root with this, init["mode"], init["clonable"], init["serializable"],
3929        // init["delegatesFocus"], and init["slotAssignment"].
3930        let shadow_root = self.attach_shadow(
3931            IsUserAgentWidget::No,
3932            init.mode,
3933            init.clonable,
3934            init.serializable,
3935            init.delegatesFocus,
3936            init.slotAssignment,
3937            can_gc,
3938        )?;
3939
3940        // Step 2. Return this’s shadow root.
3941        Ok(shadow_root)
3942    }
3943
3944    /// <https://dom.spec.whatwg.org/#dom-element-shadowroot>
3945    fn GetShadowRoot(&self) -> Option<DomRoot<ShadowRoot>> {
3946        // Step 1. Let shadow be this’s shadow root.
3947        let shadow_or_none = self.shadow_root();
3948
3949        // Step 2. If shadow is null or its mode is "closed", then return null.
3950        let shadow = shadow_or_none?;
3951        if shadow.Mode() == ShadowRootMode::Closed {
3952            return None;
3953        }
3954
3955        // Step 3. Return shadow.
3956        Some(shadow)
3957    }
3958
3959    fn GetRole(&self) -> Option<DOMString> {
3960        self.get_nullable_string_attribute(&local_name!("role"))
3961    }
3962
3963    fn SetRole(&self, value: Option<DOMString>, can_gc: CanGc) {
3964        self.set_nullable_string_attribute(&local_name!("role"), value, can_gc);
3965    }
3966
3967    fn GetAriaAtomic(&self) -> Option<DOMString> {
3968        self.get_nullable_string_attribute(&local_name!("aria-atomic"))
3969    }
3970
3971    fn SetAriaAtomic(&self, value: Option<DOMString>, can_gc: CanGc) {
3972        self.set_nullable_string_attribute(&local_name!("aria-atomic"), value, can_gc);
3973    }
3974
3975    fn GetAriaAutoComplete(&self) -> Option<DOMString> {
3976        self.get_nullable_string_attribute(&local_name!("aria-autocomplete"))
3977    }
3978
3979    fn SetAriaAutoComplete(&self, value: Option<DOMString>, can_gc: CanGc) {
3980        self.set_nullable_string_attribute(&local_name!("aria-autocomplete"), value, can_gc);
3981    }
3982
3983    fn GetAriaBrailleLabel(&self) -> Option<DOMString> {
3984        self.get_nullable_string_attribute(&local_name!("aria-braillelabel"))
3985    }
3986
3987    fn SetAriaBrailleLabel(&self, value: Option<DOMString>, can_gc: CanGc) {
3988        self.set_nullable_string_attribute(&local_name!("aria-braillelabel"), value, can_gc);
3989    }
3990
3991    fn GetAriaBrailleRoleDescription(&self) -> Option<DOMString> {
3992        self.get_nullable_string_attribute(&local_name!("aria-brailleroledescription"))
3993    }
3994
3995    fn SetAriaBrailleRoleDescription(&self, value: Option<DOMString>, can_gc: CanGc) {
3996        self.set_nullable_string_attribute(
3997            &local_name!("aria-brailleroledescription"),
3998            value,
3999            can_gc,
4000        );
4001    }
4002
4003    fn GetAriaBusy(&self) -> Option<DOMString> {
4004        self.get_nullable_string_attribute(&local_name!("aria-busy"))
4005    }
4006
4007    fn SetAriaBusy(&self, value: Option<DOMString>, can_gc: CanGc) {
4008        self.set_nullable_string_attribute(&local_name!("aria-busy"), value, can_gc);
4009    }
4010
4011    fn GetAriaChecked(&self) -> Option<DOMString> {
4012        self.get_nullable_string_attribute(&local_name!("aria-checked"))
4013    }
4014
4015    fn SetAriaChecked(&self, value: Option<DOMString>, can_gc: CanGc) {
4016        self.set_nullable_string_attribute(&local_name!("aria-checked"), value, can_gc);
4017    }
4018
4019    fn GetAriaColCount(&self) -> Option<DOMString> {
4020        self.get_nullable_string_attribute(&local_name!("aria-colcount"))
4021    }
4022
4023    fn SetAriaColCount(&self, value: Option<DOMString>, can_gc: CanGc) {
4024        self.set_nullable_string_attribute(&local_name!("aria-colcount"), value, can_gc);
4025    }
4026
4027    fn GetAriaColIndex(&self) -> Option<DOMString> {
4028        self.get_nullable_string_attribute(&local_name!("aria-colindex"))
4029    }
4030
4031    fn SetAriaColIndex(&self, value: Option<DOMString>, can_gc: CanGc) {
4032        self.set_nullable_string_attribute(&local_name!("aria-colindex"), value, can_gc);
4033    }
4034
4035    fn GetAriaColIndexText(&self) -> Option<DOMString> {
4036        self.get_nullable_string_attribute(&local_name!("aria-colindextext"))
4037    }
4038
4039    fn SetAriaColIndexText(&self, value: Option<DOMString>, can_gc: CanGc) {
4040        self.set_nullable_string_attribute(&local_name!("aria-colindextext"), value, can_gc);
4041    }
4042
4043    fn GetAriaColSpan(&self) -> Option<DOMString> {
4044        self.get_nullable_string_attribute(&local_name!("aria-colspan"))
4045    }
4046
4047    fn SetAriaColSpan(&self, value: Option<DOMString>, can_gc: CanGc) {
4048        self.set_nullable_string_attribute(&local_name!("aria-colspan"), value, can_gc);
4049    }
4050
4051    fn GetAriaCurrent(&self) -> Option<DOMString> {
4052        self.get_nullable_string_attribute(&local_name!("aria-current"))
4053    }
4054
4055    fn SetAriaCurrent(&self, value: Option<DOMString>, can_gc: CanGc) {
4056        self.set_nullable_string_attribute(&local_name!("aria-current"), value, can_gc);
4057    }
4058
4059    fn GetAriaDescription(&self) -> Option<DOMString> {
4060        self.get_nullable_string_attribute(&local_name!("aria-description"))
4061    }
4062
4063    fn SetAriaDescription(&self, value: Option<DOMString>, can_gc: CanGc) {
4064        self.set_nullable_string_attribute(&local_name!("aria-description"), value, can_gc);
4065    }
4066
4067    fn GetAriaDisabled(&self) -> Option<DOMString> {
4068        self.get_nullable_string_attribute(&local_name!("aria-disabled"))
4069    }
4070
4071    fn SetAriaDisabled(&self, value: Option<DOMString>, can_gc: CanGc) {
4072        self.set_nullable_string_attribute(&local_name!("aria-disabled"), value, can_gc);
4073    }
4074
4075    fn GetAriaExpanded(&self) -> Option<DOMString> {
4076        self.get_nullable_string_attribute(&local_name!("aria-expanded"))
4077    }
4078
4079    fn SetAriaExpanded(&self, value: Option<DOMString>, can_gc: CanGc) {
4080        self.set_nullable_string_attribute(&local_name!("aria-expanded"), value, can_gc);
4081    }
4082
4083    fn GetAriaHasPopup(&self) -> Option<DOMString> {
4084        self.get_nullable_string_attribute(&local_name!("aria-haspopup"))
4085    }
4086
4087    fn SetAriaHasPopup(&self, value: Option<DOMString>, can_gc: CanGc) {
4088        self.set_nullable_string_attribute(&local_name!("aria-haspopup"), value, can_gc);
4089    }
4090
4091    fn GetAriaHidden(&self) -> Option<DOMString> {
4092        self.get_nullable_string_attribute(&local_name!("aria-hidden"))
4093    }
4094
4095    fn SetAriaHidden(&self, value: Option<DOMString>, can_gc: CanGc) {
4096        self.set_nullable_string_attribute(&local_name!("aria-hidden"), value, can_gc);
4097    }
4098
4099    fn GetAriaInvalid(&self) -> Option<DOMString> {
4100        self.get_nullable_string_attribute(&local_name!("aria-invalid"))
4101    }
4102
4103    fn SetAriaInvalid(&self, value: Option<DOMString>, can_gc: CanGc) {
4104        self.set_nullable_string_attribute(&local_name!("aria-invalid"), value, can_gc);
4105    }
4106
4107    fn GetAriaKeyShortcuts(&self) -> Option<DOMString> {
4108        self.get_nullable_string_attribute(&local_name!("aria-keyshortcuts"))
4109    }
4110
4111    fn SetAriaKeyShortcuts(&self, value: Option<DOMString>, can_gc: CanGc) {
4112        self.set_nullable_string_attribute(&local_name!("aria-keyshortcuts"), value, can_gc);
4113    }
4114
4115    fn GetAriaLabel(&self) -> Option<DOMString> {
4116        self.get_nullable_string_attribute(&local_name!("aria-label"))
4117    }
4118
4119    fn SetAriaLabel(&self, value: Option<DOMString>, can_gc: CanGc) {
4120        self.set_nullable_string_attribute(&local_name!("aria-label"), value, can_gc);
4121    }
4122
4123    fn GetAriaLevel(&self) -> Option<DOMString> {
4124        self.get_nullable_string_attribute(&local_name!("aria-level"))
4125    }
4126
4127    fn SetAriaLevel(&self, value: Option<DOMString>, can_gc: CanGc) {
4128        self.set_nullable_string_attribute(&local_name!("aria-level"), value, can_gc);
4129    }
4130
4131    fn GetAriaLive(&self) -> Option<DOMString> {
4132        self.get_nullable_string_attribute(&local_name!("aria-live"))
4133    }
4134
4135    fn SetAriaLive(&self, value: Option<DOMString>, can_gc: CanGc) {
4136        self.set_nullable_string_attribute(&local_name!("aria-live"), value, can_gc);
4137    }
4138
4139    fn GetAriaModal(&self) -> Option<DOMString> {
4140        self.get_nullable_string_attribute(&local_name!("aria-modal"))
4141    }
4142
4143    fn SetAriaModal(&self, value: Option<DOMString>, can_gc: CanGc) {
4144        self.set_nullable_string_attribute(&local_name!("aria-modal"), value, can_gc);
4145    }
4146
4147    fn GetAriaMultiLine(&self) -> Option<DOMString> {
4148        self.get_nullable_string_attribute(&local_name!("aria-multiline"))
4149    }
4150
4151    fn SetAriaMultiLine(&self, value: Option<DOMString>, can_gc: CanGc) {
4152        self.set_nullable_string_attribute(&local_name!("aria-multiline"), value, can_gc);
4153    }
4154
4155    fn GetAriaMultiSelectable(&self) -> Option<DOMString> {
4156        self.get_nullable_string_attribute(&local_name!("aria-multiselectable"))
4157    }
4158
4159    fn SetAriaMultiSelectable(&self, value: Option<DOMString>, can_gc: CanGc) {
4160        self.set_nullable_string_attribute(&local_name!("aria-multiselectable"), value, can_gc);
4161    }
4162
4163    fn GetAriaOrientation(&self) -> Option<DOMString> {
4164        self.get_nullable_string_attribute(&local_name!("aria-orientation"))
4165    }
4166
4167    fn SetAriaOrientation(&self, value: Option<DOMString>, can_gc: CanGc) {
4168        self.set_nullable_string_attribute(&local_name!("aria-orientation"), value, can_gc);
4169    }
4170
4171    fn GetAriaPlaceholder(&self) -> Option<DOMString> {
4172        self.get_nullable_string_attribute(&local_name!("aria-placeholder"))
4173    }
4174
4175    fn SetAriaPlaceholder(&self, value: Option<DOMString>, can_gc: CanGc) {
4176        self.set_nullable_string_attribute(&local_name!("aria-placeholder"), value, can_gc);
4177    }
4178
4179    fn GetAriaPosInSet(&self) -> Option<DOMString> {
4180        self.get_nullable_string_attribute(&local_name!("aria-posinset"))
4181    }
4182
4183    fn SetAriaPosInSet(&self, value: Option<DOMString>, can_gc: CanGc) {
4184        self.set_nullable_string_attribute(&local_name!("aria-posinset"), value, can_gc);
4185    }
4186
4187    fn GetAriaPressed(&self) -> Option<DOMString> {
4188        self.get_nullable_string_attribute(&local_name!("aria-pressed"))
4189    }
4190
4191    fn SetAriaPressed(&self, value: Option<DOMString>, can_gc: CanGc) {
4192        self.set_nullable_string_attribute(&local_name!("aria-pressed"), value, can_gc);
4193    }
4194
4195    fn GetAriaReadOnly(&self) -> Option<DOMString> {
4196        self.get_nullable_string_attribute(&local_name!("aria-readonly"))
4197    }
4198
4199    fn SetAriaReadOnly(&self, value: Option<DOMString>, can_gc: CanGc) {
4200        self.set_nullable_string_attribute(&local_name!("aria-readonly"), value, can_gc);
4201    }
4202
4203    fn GetAriaRelevant(&self) -> Option<DOMString> {
4204        self.get_nullable_string_attribute(&local_name!("aria-relevant"))
4205    }
4206
4207    fn SetAriaRelevant(&self, value: Option<DOMString>, can_gc: CanGc) {
4208        self.set_nullable_string_attribute(&local_name!("aria-relevant"), value, can_gc);
4209    }
4210
4211    fn GetAriaRequired(&self) -> Option<DOMString> {
4212        self.get_nullable_string_attribute(&local_name!("aria-required"))
4213    }
4214
4215    fn SetAriaRequired(&self, value: Option<DOMString>, can_gc: CanGc) {
4216        self.set_nullable_string_attribute(&local_name!("aria-required"), value, can_gc);
4217    }
4218
4219    fn GetAriaRoleDescription(&self) -> Option<DOMString> {
4220        self.get_nullable_string_attribute(&local_name!("aria-roledescription"))
4221    }
4222
4223    fn SetAriaRoleDescription(&self, value: Option<DOMString>, can_gc: CanGc) {
4224        self.set_nullable_string_attribute(&local_name!("aria-roledescription"), value, can_gc);
4225    }
4226
4227    fn GetAriaRowCount(&self) -> Option<DOMString> {
4228        self.get_nullable_string_attribute(&local_name!("aria-rowcount"))
4229    }
4230
4231    fn SetAriaRowCount(&self, value: Option<DOMString>, can_gc: CanGc) {
4232        self.set_nullable_string_attribute(&local_name!("aria-rowcount"), value, can_gc);
4233    }
4234
4235    fn GetAriaRowIndex(&self) -> Option<DOMString> {
4236        self.get_nullable_string_attribute(&local_name!("aria-rowindex"))
4237    }
4238
4239    fn SetAriaRowIndex(&self, value: Option<DOMString>, can_gc: CanGc) {
4240        self.set_nullable_string_attribute(&local_name!("aria-rowindex"), value, can_gc);
4241    }
4242
4243    fn GetAriaRowIndexText(&self) -> Option<DOMString> {
4244        self.get_nullable_string_attribute(&local_name!("aria-rowindextext"))
4245    }
4246
4247    fn SetAriaRowIndexText(&self, value: Option<DOMString>, can_gc: CanGc) {
4248        self.set_nullable_string_attribute(&local_name!("aria-rowindextext"), value, can_gc);
4249    }
4250
4251    fn GetAriaRowSpan(&self) -> Option<DOMString> {
4252        self.get_nullable_string_attribute(&local_name!("aria-rowspan"))
4253    }
4254
4255    fn SetAriaRowSpan(&self, value: Option<DOMString>, can_gc: CanGc) {
4256        self.set_nullable_string_attribute(&local_name!("aria-rowspan"), value, can_gc);
4257    }
4258
4259    fn GetAriaSelected(&self) -> Option<DOMString> {
4260        self.get_nullable_string_attribute(&local_name!("aria-selected"))
4261    }
4262
4263    fn SetAriaSelected(&self, value: Option<DOMString>, can_gc: CanGc) {
4264        self.set_nullable_string_attribute(&local_name!("aria-selected"), value, can_gc);
4265    }
4266
4267    fn GetAriaSetSize(&self) -> Option<DOMString> {
4268        self.get_nullable_string_attribute(&local_name!("aria-setsize"))
4269    }
4270
4271    fn SetAriaSetSize(&self, value: Option<DOMString>, can_gc: CanGc) {
4272        self.set_nullable_string_attribute(&local_name!("aria-setsize"), value, can_gc);
4273    }
4274
4275    fn GetAriaSort(&self) -> Option<DOMString> {
4276        self.get_nullable_string_attribute(&local_name!("aria-sort"))
4277    }
4278
4279    fn SetAriaSort(&self, value: Option<DOMString>, can_gc: CanGc) {
4280        self.set_nullable_string_attribute(&local_name!("aria-sort"), value, can_gc);
4281    }
4282
4283    fn GetAriaValueMax(&self) -> Option<DOMString> {
4284        self.get_nullable_string_attribute(&local_name!("aria-valuemax"))
4285    }
4286
4287    fn SetAriaValueMax(&self, value: Option<DOMString>, can_gc: CanGc) {
4288        self.set_nullable_string_attribute(&local_name!("aria-valuemax"), value, can_gc);
4289    }
4290
4291    fn GetAriaValueMin(&self) -> Option<DOMString> {
4292        self.get_nullable_string_attribute(&local_name!("aria-valuemin"))
4293    }
4294
4295    fn SetAriaValueMin(&self, value: Option<DOMString>, can_gc: CanGc) {
4296        self.set_nullable_string_attribute(&local_name!("aria-valuemin"), value, can_gc);
4297    }
4298
4299    fn GetAriaValueNow(&self) -> Option<DOMString> {
4300        self.get_nullable_string_attribute(&local_name!("aria-valuenow"))
4301    }
4302
4303    fn SetAriaValueNow(&self, value: Option<DOMString>, can_gc: CanGc) {
4304        self.set_nullable_string_attribute(&local_name!("aria-valuenow"), value, can_gc);
4305    }
4306
4307    fn GetAriaValueText(&self) -> Option<DOMString> {
4308        self.get_nullable_string_attribute(&local_name!("aria-valuetext"))
4309    }
4310
4311    fn SetAriaValueText(&self, value: Option<DOMString>, can_gc: CanGc) {
4312        self.set_nullable_string_attribute(&local_name!("aria-valuetext"), value, can_gc);
4313    }
4314
4315    /// <https://dom.spec.whatwg.org/#dom-slotable-assignedslot>
4316    fn GetAssignedSlot(&self) -> Option<DomRoot<HTMLSlotElement>> {
4317        let cx = GlobalScope::get_cx();
4318
4319        // > The assignedSlot getter steps are to return the result of
4320        // > find a slot given this and with the open flag set.
4321        rooted!(in(*cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4322        slottable.find_a_slot(true)
4323    }
4324
4325    /// <https://drafts.csswg.org/css-shadow-parts/#dom-element-part>
4326    fn Part(&self) -> DomRoot<DOMTokenList> {
4327        self.ensure_rare_data()
4328            .part
4329            .or_init(|| DOMTokenList::new(self, &local_name!("part"), None, CanGc::note()))
4330    }
4331}
4332
4333impl VirtualMethods for Element {
4334    fn super_type(&self) -> Option<&dyn VirtualMethods> {
4335        Some(self.upcast::<Node>() as &dyn VirtualMethods)
4336    }
4337
4338    fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
4339        // FIXME: This should be more fine-grained, not all elements care about these.
4340        if attr.local_name() == &local_name!("lang") {
4341            return true;
4342        }
4343
4344        self.super_type()
4345            .unwrap()
4346            .attribute_affects_presentational_hints(attr)
4347    }
4348
4349    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
4350        self.super_type()
4351            .unwrap()
4352            .attribute_mutated(attr, mutation, can_gc);
4353        let node = self.upcast::<Node>();
4354        let doc = node.owner_doc();
4355        match attr.local_name() {
4356            &local_name!("tabindex") | &local_name!("draggable") | &local_name!("hidden") => {
4357                self.update_sequentially_focusable_status(can_gc)
4358            },
4359            &local_name!("style") => self.update_style_attribute(attr, mutation),
4360            &local_name!("id") => {
4361                // https://dom.spec.whatwg.org/#ref-for-concept-element-attributes-change-ext%E2%91%A2
4362                *self.id_attribute.borrow_mut() = mutation.new_value(attr).and_then(|value| {
4363                    let value = value.as_atom();
4364                    if value != &atom!("") {
4365                        // Step 2. Otherwise, if localName is id, namespace is null, then set element’s ID to value.
4366                        Some(value.clone())
4367                    } else {
4368                        // Step 1. If localName is id, namespace is null, and value is null or the empty string, then unset element’s ID.
4369                        None
4370                    }
4371                });
4372
4373                let containing_shadow_root = self.containing_shadow_root();
4374                if node.is_in_a_document_tree() || node.is_in_a_shadow_tree() {
4375                    let value = attr.value().as_atom().clone();
4376                    match mutation {
4377                        AttributeMutation::Set(old_value, _) => {
4378                            if let Some(old_value) = old_value {
4379                                let old_value = old_value.as_atom().clone();
4380                                if let Some(ref shadow_root) = containing_shadow_root {
4381                                    shadow_root.unregister_element_id(self, old_value, can_gc);
4382                                } else {
4383                                    doc.unregister_element_id(self, old_value, can_gc);
4384                                }
4385                            }
4386                            if value != atom!("") {
4387                                if let Some(ref shadow_root) = containing_shadow_root {
4388                                    shadow_root.register_element_id(self, value, can_gc);
4389                                } else {
4390                                    doc.register_element_id(self, value, can_gc);
4391                                }
4392                            }
4393                        },
4394                        AttributeMutation::Removed => {
4395                            if value != atom!("") {
4396                                if let Some(ref shadow_root) = containing_shadow_root {
4397                                    shadow_root.unregister_element_id(self, value, can_gc);
4398                                } else {
4399                                    doc.unregister_element_id(self, value, can_gc);
4400                                }
4401                            }
4402                        },
4403                    }
4404                }
4405            },
4406            &local_name!("name") => {
4407                // Keep the name in rare data for fast access
4408                self.ensure_rare_data().name_attribute =
4409                    mutation.new_value(attr).and_then(|value| {
4410                        let value = value.as_atom();
4411                        if value != &atom!("") {
4412                            Some(value.clone())
4413                        } else {
4414                            None
4415                        }
4416                    });
4417                // Keep the document name_map up to date
4418                // (if we're not in shadow DOM)
4419                if node.is_connected() && node.containing_shadow_root().is_none() {
4420                    let value = attr.value().as_atom().clone();
4421                    match mutation {
4422                        AttributeMutation::Set(old_value, _) => {
4423                            if let Some(old_value) = old_value {
4424                                let old_value = old_value.as_atom().clone();
4425                                doc.unregister_element_name(self, old_value);
4426                            }
4427                            if value != atom!("") {
4428                                doc.register_element_name(self, value);
4429                            }
4430                        },
4431                        AttributeMutation::Removed => {
4432                            if value != atom!("") {
4433                                doc.unregister_element_name(self, value);
4434                            }
4435                        },
4436                    }
4437                }
4438            },
4439            &local_name!("slot") => {
4440                // Update slottable data
4441                let cx = GlobalScope::get_cx();
4442
4443                rooted!(in(*cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4444
4445                // Slottable name change steps from https://dom.spec.whatwg.org/#light-tree-slotables
4446                if let Some(assigned_slot) = slottable.assigned_slot() {
4447                    assigned_slot.assign_slottables();
4448                }
4449                slottable.assign_a_slot();
4450            },
4451            _ => {
4452                // FIXME(emilio): This is pretty dubious, and should be done in
4453                // the relevant super-classes.
4454                if attr.namespace() == &ns!() && attr.local_name() == &local_name!("src") {
4455                    node.dirty(NodeDamage::Other);
4456                }
4457            },
4458        };
4459
4460        // Make sure we rev the version even if we didn't dirty the node. If we
4461        // don't do this, various attribute-dependent htmlcollections (like those
4462        // generated by getElementsByClassName) might become stale.
4463        node.rev_version();
4464    }
4465
4466    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
4467        match *name {
4468            local_name!("id") => AttrValue::Atom(value.into()),
4469            local_name!("name") => AttrValue::Atom(value.into()),
4470            local_name!("class") | local_name!("part") => {
4471                AttrValue::from_serialized_tokenlist(value.into())
4472            },
4473            local_name!("exportparts") => AttrValue::from_shadow_parts(value.into()),
4474            _ => self
4475                .super_type()
4476                .unwrap()
4477                .parse_plain_attribute(name, value),
4478        }
4479    }
4480
4481    fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
4482        if let Some(s) = self.super_type() {
4483            s.bind_to_tree(context, can_gc);
4484        }
4485
4486        if let Some(f) = self.as_maybe_form_control() {
4487            f.bind_form_control_to_tree(can_gc);
4488        }
4489
4490        let doc = self.owner_document();
4491
4492        if let Some(ref shadow_root) = self.shadow_root() {
4493            shadow_root.bind_to_tree(context, can_gc);
4494        }
4495
4496        if !context.is_in_tree() {
4497            return;
4498        }
4499
4500        self.update_sequentially_focusable_status(can_gc);
4501
4502        if let Some(ref id) = *self.id_attribute.borrow() {
4503            if let Some(shadow_root) = self.containing_shadow_root() {
4504                shadow_root.register_element_id(self, id.clone(), can_gc);
4505            } else {
4506                doc.register_element_id(self, id.clone(), can_gc);
4507            }
4508        }
4509        if let Some(ref name) = self.name_attribute() {
4510            if self.containing_shadow_root().is_none() {
4511                doc.register_element_name(self, name.clone());
4512            }
4513        }
4514
4515        // This is used for layout optimization.
4516        doc.increment_dom_count();
4517    }
4518
4519    fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
4520        self.super_type().unwrap().unbind_from_tree(context, can_gc);
4521
4522        if let Some(f) = self.as_maybe_form_control() {
4523            // TODO: The valid state of ancestors might be wrong if the form control element
4524            // has a fieldset ancestor, for instance: `<form><fieldset><input>`,
4525            // if `<input>` is unbound, `<form><fieldset>` should trigger a call to `update_validity()`.
4526            f.unbind_form_control_from_tree(can_gc);
4527        }
4528
4529        if !context.tree_is_in_a_document_tree && !context.tree_is_in_a_shadow_tree {
4530            return;
4531        }
4532
4533        self.update_sequentially_focusable_status(can_gc);
4534
4535        let doc = self.owner_document();
4536
4537        let fullscreen = doc.GetFullscreenElement();
4538        if fullscreen.as_deref() == Some(self) {
4539            doc.exit_fullscreen(can_gc);
4540        }
4541        if let Some(ref value) = *self.id_attribute.borrow() {
4542            if let Some(ref shadow_root) = self.containing_shadow_root() {
4543                // Only unregister the element id if the node was disconnected from it's shadow root
4544                // (as opposed to the whole shadow tree being disconnected as a whole)
4545                if !self.upcast::<Node>().is_in_a_shadow_tree() {
4546                    shadow_root.unregister_element_id(self, value.clone(), can_gc);
4547                }
4548            } else {
4549                doc.unregister_element_id(self, value.clone(), can_gc);
4550            }
4551        }
4552        if let Some(ref value) = self.name_attribute() {
4553            if self.containing_shadow_root().is_none() {
4554                doc.unregister_element_name(self, value.clone());
4555            }
4556        }
4557        // This is used for layout optimization.
4558        doc.decrement_dom_count();
4559    }
4560
4561    fn children_changed(&self, mutation: &ChildrenMutation, can_gc: CanGc) {
4562        if let Some(s) = self.super_type() {
4563            s.children_changed(mutation, can_gc);
4564        }
4565
4566        let flags = self.selector_flags.get();
4567        if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
4568            // All children of this node need to be restyled when any child changes.
4569            self.upcast::<Node>().dirty(NodeDamage::Other);
4570        } else {
4571            if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
4572                if let Some(next_child) = mutation.next_child() {
4573                    for child in next_child.inclusively_following_siblings() {
4574                        if child.is::<Element>() {
4575                            child.dirty(NodeDamage::Other);
4576                        }
4577                    }
4578                }
4579            }
4580            if flags.intersects(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {
4581                if let Some(child) = mutation.modified_edge_element() {
4582                    child.dirty(NodeDamage::Other);
4583                }
4584            }
4585        }
4586    }
4587
4588    fn adopting_steps(&self, old_doc: &Document, can_gc: CanGc) {
4589        self.super_type().unwrap().adopting_steps(old_doc, can_gc);
4590
4591        if self.owner_document().is_html_document() != old_doc.is_html_document() {
4592            self.tag_name.clear();
4593        }
4594    }
4595
4596    fn post_connection_steps(&self, can_gc: CanGc) {
4597        if let Some(s) = self.super_type() {
4598            s.post_connection_steps(can_gc);
4599        }
4600
4601        self.update_nonce_post_connection();
4602    }
4603
4604    /// <https://html.spec.whatwg.org/multipage/#nonce-attributes%3Aconcept-node-clone-ext>
4605    fn cloning_steps(
4606        &self,
4607        copy: &Node,
4608        maybe_doc: Option<&Document>,
4609        clone_children: CloneChildrenFlag,
4610        can_gc: CanGc,
4611    ) {
4612        if let Some(s) = self.super_type() {
4613            s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
4614        }
4615        let elem = copy.downcast::<Element>().unwrap();
4616        if let Some(rare_data) = self.rare_data().as_ref() {
4617            elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone());
4618        }
4619    }
4620}
4621
4622#[derive(Clone, PartialEq)]
4623/// A type that wraps a DomRoot value so we can implement the SelectorsElement
4624/// trait without violating the orphan rule. Since the trait assumes that the
4625/// return type and self type of various methods is the same type that it is
4626/// implemented against, we need to be able to represent multiple ownership styles.
4627pub enum SelectorWrapper<'a> {
4628    Borrowed(&'a DomRoot<Element>),
4629    Owned(DomRoot<Element>),
4630}
4631
4632impl fmt::Debug for SelectorWrapper<'_> {
4633    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4634        self.deref().fmt(f)
4635    }
4636}
4637
4638impl Deref for SelectorWrapper<'_> {
4639    type Target = DomRoot<Element>;
4640
4641    fn deref(&self) -> &Self::Target {
4642        match self {
4643            SelectorWrapper::Owned(r) => r,
4644            SelectorWrapper::Borrowed(r) => r,
4645        }
4646    }
4647}
4648
4649impl SelectorWrapper<'_> {
4650    fn into_owned(self) -> DomRoot<Element> {
4651        match self {
4652            SelectorWrapper::Owned(r) => r,
4653            SelectorWrapper::Borrowed(r) => r.clone(),
4654        }
4655    }
4656}
4657
4658impl SelectorsElement for SelectorWrapper<'_> {
4659    type Impl = SelectorImpl;
4660
4661    #[expect(unsafe_code)]
4662    fn opaque(&self) -> ::selectors::OpaqueElement {
4663        ::selectors::OpaqueElement::new(unsafe { &*self.reflector().get_jsobject().get() })
4664    }
4665
4666    fn parent_element(&self) -> Option<Self> {
4667        self.upcast::<Node>()
4668            .GetParentElement()
4669            .map(SelectorWrapper::Owned)
4670    }
4671
4672    fn parent_node_is_shadow_root(&self) -> bool {
4673        match self.upcast::<Node>().GetParentNode() {
4674            None => false,
4675            Some(node) => node.is::<ShadowRoot>(),
4676        }
4677    }
4678
4679    fn containing_shadow_host(&self) -> Option<Self> {
4680        self.containing_shadow_root()
4681            .map(|shadow_root| shadow_root.Host())
4682            .map(SelectorWrapper::Owned)
4683    }
4684
4685    fn is_pseudo_element(&self) -> bool {
4686        false
4687    }
4688
4689    fn match_pseudo_element(
4690        &self,
4691        _pseudo: &PseudoElement,
4692        _context: &mut MatchingContext<Self::Impl>,
4693    ) -> bool {
4694        false
4695    }
4696
4697    fn prev_sibling_element(&self) -> Option<Self> {
4698        self.node
4699            .preceding_siblings()
4700            .find_map(DomRoot::downcast)
4701            .map(SelectorWrapper::Owned)
4702    }
4703
4704    fn next_sibling_element(&self) -> Option<Self> {
4705        self.node
4706            .following_siblings()
4707            .find_map(DomRoot::downcast)
4708            .map(SelectorWrapper::Owned)
4709    }
4710
4711    fn first_element_child(&self) -> Option<Self> {
4712        self.GetFirstElementChild().map(SelectorWrapper::Owned)
4713    }
4714
4715    fn attr_matches(
4716        &self,
4717        ns: &NamespaceConstraint<&style::Namespace>,
4718        local_name: &style::LocalName,
4719        operation: &AttrSelectorOperation<&AtomString>,
4720    ) -> bool {
4721        match *ns {
4722            NamespaceConstraint::Specific(ns) => self
4723                .get_attribute(ns, local_name)
4724                .is_some_and(|attr| attr.value().eval_selector(operation)),
4725            NamespaceConstraint::Any => self.attrs.borrow().iter().any(|attr| {
4726                *attr.local_name() == **local_name && attr.value().eval_selector(operation)
4727            }),
4728        }
4729    }
4730
4731    fn is_root(&self) -> bool {
4732        Element::is_root(self)
4733    }
4734
4735    fn is_empty(&self) -> bool {
4736        self.node.children().all(|node| {
4737            !node.is::<Element>() &&
4738                match node.downcast::<Text>() {
4739                    None => true,
4740                    Some(text) => text.upcast::<CharacterData>().data().is_empty(),
4741                }
4742        })
4743    }
4744
4745    fn has_local_name(&self, local_name: &LocalName) -> bool {
4746        Element::local_name(self) == local_name
4747    }
4748
4749    fn has_namespace(&self, ns: &Namespace) -> bool {
4750        Element::namespace(self) == ns
4751    }
4752
4753    fn is_same_type(&self, other: &Self) -> bool {
4754        Element::local_name(self) == Element::local_name(other) &&
4755            Element::namespace(self) == Element::namespace(other)
4756    }
4757
4758    fn match_non_ts_pseudo_class(
4759        &self,
4760        pseudo_class: &NonTSPseudoClass,
4761        _: &mut MatchingContext<Self::Impl>,
4762    ) -> bool {
4763        match *pseudo_class {
4764            // https://github.com/servo/servo/issues/8718
4765            NonTSPseudoClass::Link | NonTSPseudoClass::AnyLink => self.is_link(),
4766            NonTSPseudoClass::Visited => false,
4767
4768            NonTSPseudoClass::ServoNonZeroBorder => match self.downcast::<HTMLTableElement>() {
4769                None => false,
4770                Some(this) => match this.get_border() {
4771                    None | Some(0) => false,
4772                    Some(_) => true,
4773                },
4774            },
4775
4776            NonTSPseudoClass::CustomState(ref state) => self.has_custom_state(&state.0),
4777
4778            // FIXME(heycam): This is wrong, since extended_filtering accepts
4779            // a string containing commas (separating each language tag in
4780            // a list) but the pseudo-class instead should be parsing and
4781            // storing separate <ident> or <string>s for each language tag.
4782            NonTSPseudoClass::Lang(ref lang) => {
4783                extended_filtering(&self.upcast::<Node>().get_lang().unwrap_or_default(), lang)
4784            },
4785
4786            NonTSPseudoClass::ReadOnly => {
4787                !Element::state(self).contains(NonTSPseudoClass::ReadWrite.state_flag())
4788            },
4789
4790            NonTSPseudoClass::Active |
4791            NonTSPseudoClass::Autofill |
4792            NonTSPseudoClass::Checked |
4793            NonTSPseudoClass::Default |
4794            NonTSPseudoClass::Defined |
4795            NonTSPseudoClass::Disabled |
4796            NonTSPseudoClass::Enabled |
4797            NonTSPseudoClass::Focus |
4798            NonTSPseudoClass::FocusVisible |
4799            NonTSPseudoClass::FocusWithin |
4800            NonTSPseudoClass::Fullscreen |
4801            NonTSPseudoClass::Hover |
4802            NonTSPseudoClass::InRange |
4803            NonTSPseudoClass::Indeterminate |
4804            NonTSPseudoClass::Invalid |
4805            NonTSPseudoClass::Modal |
4806            NonTSPseudoClass::MozMeterOptimum |
4807            NonTSPseudoClass::MozMeterSubOptimum |
4808            NonTSPseudoClass::MozMeterSubSubOptimum |
4809            NonTSPseudoClass::Optional |
4810            NonTSPseudoClass::OutOfRange |
4811            NonTSPseudoClass::PlaceholderShown |
4812            NonTSPseudoClass::PopoverOpen |
4813            NonTSPseudoClass::ReadWrite |
4814            NonTSPseudoClass::Required |
4815            NonTSPseudoClass::Target |
4816            NonTSPseudoClass::UserInvalid |
4817            NonTSPseudoClass::UserValid |
4818            NonTSPseudoClass::Valid => Element::state(self).contains(pseudo_class.state_flag()),
4819        }
4820    }
4821
4822    fn is_link(&self) -> bool {
4823        // FIXME: This is HTML only.
4824        let node = self.upcast::<Node>();
4825        match node.type_id() {
4826            // https://html.spec.whatwg.org/multipage/#selector-link
4827            NodeTypeId::Element(ElementTypeId::HTMLElement(
4828                HTMLElementTypeId::HTMLAnchorElement,
4829            )) |
4830            NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
4831            NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
4832                self.has_attribute(&local_name!("href"))
4833            },
4834            _ => false,
4835        }
4836    }
4837
4838    fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
4839        self.id_attribute
4840            .borrow()
4841            .as_ref()
4842            .is_some_and(|atom| case_sensitivity.eq_atom(id, atom))
4843    }
4844
4845    fn is_part(&self, name: &AtomIdent) -> bool {
4846        Element::is_part(self, name, CaseSensitivity::CaseSensitive)
4847    }
4848
4849    fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
4850        None
4851    }
4852
4853    fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
4854        Element::has_class(self, name, case_sensitivity)
4855    }
4856
4857    fn is_html_element_in_html_document(&self) -> bool {
4858        self.html_element_in_html_document()
4859    }
4860
4861    fn is_html_slot_element(&self) -> bool {
4862        self.is_html_element() && self.local_name() == &local_name!("slot")
4863    }
4864
4865    fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
4866        // Handle flags that apply to the element.
4867        let self_flags = flags.for_self();
4868        if !self_flags.is_empty() {
4869            #[expect(unsafe_code)]
4870            unsafe {
4871                Dom::from_ref(&***self)
4872                    .to_layout()
4873                    .insert_selector_flags(self_flags);
4874            }
4875        }
4876
4877        // Handle flags that apply to the parent.
4878        let parent_flags = flags.for_parent();
4879        if !parent_flags.is_empty() {
4880            if let Some(p) = self.parent_element() {
4881                #[expect(unsafe_code)]
4882                unsafe {
4883                    Dom::from_ref(&**p)
4884                        .to_layout()
4885                        .insert_selector_flags(parent_flags);
4886                }
4887            }
4888        }
4889    }
4890
4891    fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool {
4892        let mut f = |hash| filter.insert_hash(hash & BLOOM_HASH_MASK);
4893
4894        // We can't use style::bloom::each_relevant_element_hash(*self, f)
4895        // since DomRoot<Element> doesn't have the TElement trait.
4896        f(Element::local_name(self).get_hash());
4897        f(Element::namespace(self).get_hash());
4898
4899        if let Some(ref id) = *self.id_attribute.borrow() {
4900            f(id.get_hash());
4901        }
4902
4903        if let Some(attr) = self.get_attribute(&ns!(), &local_name!("class")) {
4904            for class in attr.value().as_tokens() {
4905                f(AtomIdent::cast(class).get_hash());
4906            }
4907        }
4908
4909        for attr in self.attrs.borrow().iter() {
4910            let name = style::values::GenericAtomIdent::cast(attr.local_name());
4911            if !style::bloom::is_attr_name_excluded_from_filter(name) {
4912                f(name.get_hash());
4913            }
4914        }
4915
4916        true
4917    }
4918
4919    fn has_custom_state(&self, name: &AtomIdent) -> bool {
4920        let mut has_state = false;
4921        self.each_custom_state(|state| has_state |= state == name);
4922
4923        has_state
4924    }
4925}
4926
4927impl Element {
4928    fn each_custom_state<F>(&self, callback: F)
4929    where
4930        F: FnMut(&AtomIdent),
4931    {
4932        self.get_element_internals()
4933            .and_then(|internals| internals.custom_states())
4934            .inspect(|states| states.for_each_state(callback));
4935    }
4936
4937    pub(crate) fn client_rect(&self) -> Rect<i32> {
4938        let doc = self.node.owner_doc();
4939
4940        if let Some(rect) = self
4941            .rare_data()
4942            .as_ref()
4943            .and_then(|data| data.client_rect.as_ref())
4944            .and_then(|rect| rect.get().ok())
4945        {
4946            if doc.restyle_reason().is_empty() {
4947                return rect;
4948            }
4949        }
4950
4951        let mut rect = self.upcast::<Node>().client_rect();
4952        let in_quirks_mode = doc.quirks_mode() == QuirksMode::Quirks;
4953
4954        if (in_quirks_mode && doc.GetBody().as_deref() == self.downcast::<HTMLElement>()) ||
4955            (!in_quirks_mode && *self.root_element() == *self)
4956        {
4957            let viewport_dimensions = doc.window().viewport_details().size.round().to_i32();
4958            rect.size = Size2D::<i32>::new(viewport_dimensions.width, viewport_dimensions.height);
4959        }
4960
4961        self.ensure_rare_data().client_rect = Some(self.owner_window().cache_layout_value(rect));
4962        rect
4963    }
4964
4965    pub(crate) fn as_maybe_activatable(&self) -> Option<&dyn Activatable> {
4966        let element = match self.upcast::<Node>().type_id() {
4967            NodeTypeId::Element(ElementTypeId::HTMLElement(
4968                HTMLElementTypeId::HTMLInputElement,
4969            )) => {
4970                let element = self.downcast::<HTMLInputElement>().unwrap();
4971                Some(element as &dyn Activatable)
4972            },
4973            NodeTypeId::Element(ElementTypeId::HTMLElement(
4974                HTMLElementTypeId::HTMLButtonElement,
4975            )) => {
4976                let element = self.downcast::<HTMLButtonElement>().unwrap();
4977                Some(element as &dyn Activatable)
4978            },
4979            NodeTypeId::Element(ElementTypeId::HTMLElement(
4980                HTMLElementTypeId::HTMLAnchorElement,
4981            )) => {
4982                let element = self.downcast::<HTMLAnchorElement>().unwrap();
4983                Some(element as &dyn Activatable)
4984            },
4985            NodeTypeId::Element(ElementTypeId::HTMLElement(
4986                HTMLElementTypeId::HTMLLabelElement,
4987            )) => {
4988                let element = self.downcast::<HTMLLabelElement>().unwrap();
4989                Some(element as &dyn Activatable)
4990            },
4991            NodeTypeId::Element(ElementTypeId::HTMLElement(
4992                HTMLElementTypeId::HTMLSelectElement,
4993            )) => {
4994                let element = self.downcast::<HTMLSelectElement>().unwrap();
4995                Some(element as &dyn Activatable)
4996            },
4997            NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
4998                let element = self.downcast::<HTMLElement>().unwrap();
4999                Some(element as &dyn Activatable)
5000            },
5001            _ => None,
5002        };
5003        element.and_then(|elem| {
5004            if elem.is_instance_activatable() {
5005                Some(elem)
5006            } else {
5007                None
5008            }
5009        })
5010    }
5011
5012    pub(crate) fn as_stylesheet_owner(&self) -> Option<&dyn StylesheetOwner> {
5013        if let Some(s) = self.downcast::<HTMLStyleElement>() {
5014            return Some(s as &dyn StylesheetOwner);
5015        }
5016
5017        if let Some(l) = self.downcast::<HTMLLinkElement>() {
5018            return Some(l as &dyn StylesheetOwner);
5019        }
5020
5021        None
5022    }
5023
5024    // https://html.spec.whatwg.org/multipage/#category-submit
5025    pub(crate) fn as_maybe_validatable(&self) -> Option<&dyn Validatable> {
5026        match self.upcast::<Node>().type_id() {
5027            NodeTypeId::Element(ElementTypeId::HTMLElement(
5028                HTMLElementTypeId::HTMLInputElement,
5029            )) => {
5030                let element = self.downcast::<HTMLInputElement>().unwrap();
5031                Some(element as &dyn Validatable)
5032            },
5033            NodeTypeId::Element(ElementTypeId::HTMLElement(
5034                HTMLElementTypeId::HTMLButtonElement,
5035            )) => {
5036                let element = self.downcast::<HTMLButtonElement>().unwrap();
5037                Some(element as &dyn Validatable)
5038            },
5039            NodeTypeId::Element(ElementTypeId::HTMLElement(
5040                HTMLElementTypeId::HTMLObjectElement,
5041            )) => {
5042                let element = self.downcast::<HTMLObjectElement>().unwrap();
5043                Some(element as &dyn Validatable)
5044            },
5045            NodeTypeId::Element(ElementTypeId::HTMLElement(
5046                HTMLElementTypeId::HTMLSelectElement,
5047            )) => {
5048                let element = self.downcast::<HTMLSelectElement>().unwrap();
5049                Some(element as &dyn Validatable)
5050            },
5051            NodeTypeId::Element(ElementTypeId::HTMLElement(
5052                HTMLElementTypeId::HTMLTextAreaElement,
5053            )) => {
5054                let element = self.downcast::<HTMLTextAreaElement>().unwrap();
5055                Some(element as &dyn Validatable)
5056            },
5057            NodeTypeId::Element(ElementTypeId::HTMLElement(
5058                HTMLElementTypeId::HTMLFieldSetElement,
5059            )) => {
5060                let element = self.downcast::<HTMLFieldSetElement>().unwrap();
5061                Some(element as &dyn Validatable)
5062            },
5063            NodeTypeId::Element(ElementTypeId::HTMLElement(
5064                HTMLElementTypeId::HTMLOutputElement,
5065            )) => {
5066                let element = self.downcast::<HTMLOutputElement>().unwrap();
5067                Some(element as &dyn Validatable)
5068            },
5069            _ => None,
5070        }
5071    }
5072
5073    pub(crate) fn is_invalid(&self, needs_update: bool, can_gc: CanGc) -> bool {
5074        if let Some(validatable) = self.as_maybe_validatable() {
5075            if needs_update {
5076                validatable
5077                    .validity_state(can_gc)
5078                    .perform_validation_and_update(ValidationFlags::all(), can_gc);
5079            }
5080            return validatable.is_instance_validatable() &&
5081                !validatable.satisfies_constraints(can_gc);
5082        }
5083
5084        if let Some(internals) = self.get_element_internals() {
5085            return internals.is_invalid(can_gc);
5086        }
5087        false
5088    }
5089
5090    pub(crate) fn is_instance_validatable(&self) -> bool {
5091        if let Some(validatable) = self.as_maybe_validatable() {
5092            return validatable.is_instance_validatable();
5093        }
5094        if let Some(internals) = self.get_element_internals() {
5095            return internals.is_instance_validatable();
5096        }
5097        false
5098    }
5099
5100    pub(crate) fn init_state_for_internals(&self) {
5101        self.set_enabled_state(true);
5102        self.set_state(ElementState::VALID, true);
5103        self.set_state(ElementState::INVALID, false);
5104    }
5105
5106    pub(crate) fn click_in_progress(&self) -> bool {
5107        self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
5108    }
5109
5110    pub(crate) fn set_click_in_progress(&self, click: bool) {
5111        self.upcast::<Node>()
5112            .set_flag(NodeFlags::CLICK_IN_PROGRESS, click)
5113    }
5114
5115    // https://html.spec.whatwg.org/multipage/#nearest-activatable-element
5116    pub(crate) fn nearest_activable_element(&self) -> Option<DomRoot<Element>> {
5117        match self.as_maybe_activatable() {
5118            Some(el) => Some(DomRoot::from_ref(el.as_element())),
5119            None => {
5120                let node = self.upcast::<Node>();
5121                for node in node.ancestors() {
5122                    if let Some(node) = node.downcast::<Element>() {
5123                        if node.as_maybe_activatable().is_some() {
5124                            return Some(DomRoot::from_ref(node));
5125                        }
5126                    }
5127                }
5128                None
5129            },
5130        }
5131    }
5132
5133    pub fn state(&self) -> ElementState {
5134        self.state.get()
5135    }
5136
5137    pub(crate) fn set_state(&self, which: ElementState, value: bool) {
5138        let mut state = self.state.get();
5139        let previous_state = state;
5140        if value {
5141            state.insert(which);
5142        } else {
5143            state.remove(which);
5144        }
5145
5146        if previous_state == state {
5147            // Nothing to do
5148            return;
5149        }
5150
5151        // Add a pending restyle for this node which captures a snapshot of the state
5152        // before the change.
5153        {
5154            let document = self.owner_document();
5155            let mut entry = document.ensure_pending_restyle(self);
5156            if entry.snapshot.is_none() {
5157                entry.snapshot = Some(Snapshot::new());
5158            }
5159            let snapshot = entry.snapshot.as_mut().unwrap();
5160            if snapshot.state.is_none() {
5161                snapshot.state = Some(self.state());
5162            }
5163        }
5164
5165        // Dirty the node so that it is laid out again if necessary.
5166        self.upcast::<Node>().dirty(NodeDamage::ContentOrHeritage);
5167
5168        self.state.set(state);
5169    }
5170
5171    /// <https://html.spec.whatwg.org/multipage/#concept-selector-active>
5172    pub(crate) fn set_active_state(&self, value: bool) {
5173        self.set_state(ElementState::ACTIVE, value);
5174
5175        if let Some(parent) = self.upcast::<Node>().GetParentElement() {
5176            parent.set_active_state(value);
5177        }
5178    }
5179
5180    pub(crate) fn focus_state(&self) -> bool {
5181        self.state.get().contains(ElementState::FOCUS)
5182    }
5183
5184    pub(crate) fn set_focus_state(&self, value: bool) {
5185        self.set_state(ElementState::FOCUS, value);
5186    }
5187
5188    pub(crate) fn hover_state(&self) -> bool {
5189        self.state.get().contains(ElementState::HOVER)
5190    }
5191
5192    pub(crate) fn set_hover_state(&self, value: bool) {
5193        self.set_state(ElementState::HOVER, value);
5194    }
5195
5196    pub(crate) fn enabled_state(&self) -> bool {
5197        self.state.get().contains(ElementState::ENABLED)
5198    }
5199
5200    pub(crate) fn set_enabled_state(&self, value: bool) {
5201        self.set_state(ElementState::ENABLED, value)
5202    }
5203
5204    pub(crate) fn disabled_state(&self) -> bool {
5205        self.state.get().contains(ElementState::DISABLED)
5206    }
5207
5208    pub(crate) fn set_disabled_state(&self, value: bool) {
5209        self.set_state(ElementState::DISABLED, value)
5210    }
5211
5212    pub(crate) fn read_write_state(&self) -> bool {
5213        self.state.get().contains(ElementState::READWRITE)
5214    }
5215
5216    pub(crate) fn set_read_write_state(&self, value: bool) {
5217        self.set_state(ElementState::READWRITE, value)
5218    }
5219
5220    pub(crate) fn placeholder_shown_state(&self) -> bool {
5221        self.state.get().contains(ElementState::PLACEHOLDER_SHOWN)
5222    }
5223
5224    pub(crate) fn set_placeholder_shown_state(&self, value: bool) {
5225        self.set_state(ElementState::PLACEHOLDER_SHOWN, value);
5226    }
5227
5228    pub(crate) fn set_target_state(&self, value: bool) {
5229        self.set_state(ElementState::URLTARGET, value)
5230    }
5231
5232    pub(crate) fn set_fullscreen_state(&self, value: bool) {
5233        self.set_state(ElementState::FULLSCREEN, value)
5234    }
5235
5236    /// <https://dom.spec.whatwg.org/#connected>
5237    pub(crate) fn is_connected(&self) -> bool {
5238        self.upcast::<Node>().is_connected()
5239    }
5240
5241    // https://html.spec.whatwg.org/multipage/#cannot-navigate
5242    pub(crate) fn cannot_navigate(&self) -> bool {
5243        let document = self.owner_document();
5244
5245        // Step 1.
5246        !document.is_fully_active() ||
5247            (
5248                // Step 2.
5249                !self.is::<HTMLAnchorElement>() && !self.is_connected()
5250            )
5251    }
5252}
5253
5254impl Element {
5255    pub(crate) fn check_ancestors_disabled_state_for_form_control(&self) {
5256        let node = self.upcast::<Node>();
5257        if self.disabled_state() {
5258            return;
5259        }
5260        for ancestor in node.ancestors() {
5261            if !ancestor.is::<HTMLFieldSetElement>() {
5262                continue;
5263            }
5264            if !ancestor.downcast::<Element>().unwrap().disabled_state() {
5265                continue;
5266            }
5267            if ancestor.is_parent_of(node) {
5268                self.set_disabled_state(true);
5269                self.set_enabled_state(false);
5270                return;
5271            }
5272            if let Some(ref legend) = ancestor.children().find(|n| n.is::<HTMLLegendElement>()) {
5273                // XXXabinader: should we save previous ancestor to avoid this iteration?
5274                if node.ancestors().any(|ancestor| ancestor == *legend) {
5275                    continue;
5276                }
5277            }
5278            self.set_disabled_state(true);
5279            self.set_enabled_state(false);
5280            return;
5281        }
5282    }
5283
5284    pub(crate) fn check_parent_disabled_state_for_option(&self) {
5285        if self.disabled_state() {
5286            return;
5287        }
5288        let node = self.upcast::<Node>();
5289        if let Some(ref parent) = node.GetParentNode() {
5290            if parent.is::<HTMLOptGroupElement>() &&
5291                parent.downcast::<Element>().unwrap().disabled_state()
5292            {
5293                self.set_disabled_state(true);
5294                self.set_enabled_state(false);
5295            }
5296        }
5297    }
5298
5299    pub(crate) fn check_disabled_attribute(&self) {
5300        let has_disabled_attrib = self.has_attribute(&local_name!("disabled"));
5301        self.set_disabled_state(has_disabled_attrib);
5302        self.set_enabled_state(!has_disabled_attrib);
5303    }
5304
5305    pub(crate) fn update_read_write_state_from_readonly_attribute(&self) {
5306        let has_readonly_attribute = self.has_attribute(&local_name!("readonly"));
5307        self.set_read_write_state(has_readonly_attribute);
5308    }
5309}
5310
5311#[derive(Clone, Copy, PartialEq)]
5312pub(crate) enum AttributeMutationReason {
5313    ByCloning,
5314    ByParser,
5315    Directly,
5316}
5317
5318#[derive(Clone, Copy)]
5319pub(crate) enum AttributeMutation<'a> {
5320    /// The attribute is set, keep track of old value.
5321    /// <https://dom.spec.whatwg.org/#attribute-is-set>
5322    Set(Option<&'a AttrValue>, AttributeMutationReason),
5323
5324    /// The attribute is removed.
5325    /// <https://dom.spec.whatwg.org/#attribute-is-removed>
5326    Removed,
5327}
5328
5329impl AttributeMutation<'_> {
5330    pub(crate) fn is_removal(&self) -> bool {
5331        match *self {
5332            AttributeMutation::Removed => true,
5333            AttributeMutation::Set(..) => false,
5334        }
5335    }
5336
5337    pub(crate) fn new_value<'b>(&self, attr: &'b Attr) -> Option<Ref<'b, AttrValue>> {
5338        match *self {
5339            AttributeMutation::Set(..) => Some(attr.value()),
5340            AttributeMutation::Removed => None,
5341        }
5342    }
5343}
5344
5345/// A holder for an element's "tag name", which will be lazily
5346/// resolved and cached. Should be reset when the document
5347/// owner changes.
5348#[derive(JSTraceable, MallocSizeOf)]
5349struct TagName {
5350    #[no_trace]
5351    ptr: DomRefCell<Option<LocalName>>,
5352}
5353
5354impl TagName {
5355    fn new() -> TagName {
5356        TagName {
5357            ptr: DomRefCell::new(None),
5358        }
5359    }
5360
5361    /// Retrieve a copy of the current inner value. If it is `None`, it is
5362    /// initialized with the result of `cb` first.
5363    fn or_init<F>(&self, cb: F) -> LocalName
5364    where
5365        F: FnOnce() -> LocalName,
5366    {
5367        match &mut *self.ptr.borrow_mut() {
5368            &mut Some(ref name) => name.clone(),
5369            ptr => {
5370                let name = cb();
5371                *ptr = Some(name.clone());
5372                name
5373            },
5374        }
5375    }
5376
5377    /// Clear the cached tag name, so that it will be re-calculated the
5378    /// next time that `or_init()` is called.
5379    fn clear(&self) {
5380        *self.ptr.borrow_mut() = None;
5381    }
5382}
5383
5384pub(crate) struct ElementPerformFullscreenEnter {
5385    element: Trusted<Element>,
5386    promise: TrustedPromise,
5387    error: bool,
5388}
5389
5390impl ElementPerformFullscreenEnter {
5391    pub(crate) fn new(
5392        element: Trusted<Element>,
5393        promise: TrustedPromise,
5394        error: bool,
5395    ) -> Box<ElementPerformFullscreenEnter> {
5396        Box::new(ElementPerformFullscreenEnter {
5397            element,
5398            promise,
5399            error,
5400        })
5401    }
5402}
5403
5404impl TaskOnce for ElementPerformFullscreenEnter {
5405    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
5406    fn run_once(self) {
5407        let element = self.element.root();
5408        let promise = self.promise.root();
5409        let document = element.owner_document();
5410
5411        // Step 7.1
5412        if self.error || !element.fullscreen_element_ready_check() {
5413            document
5414                .upcast::<EventTarget>()
5415                .fire_event(atom!("fullscreenerror"), CanGc::note());
5416            promise.reject_error(
5417                Error::Type(String::from("fullscreen is not connected")),
5418                CanGc::note(),
5419            );
5420            return;
5421        }
5422
5423        // TODO Step 7.2-4
5424        // Step 7.5
5425        element.set_fullscreen_state(true);
5426        document.set_fullscreen_element(Some(&element));
5427
5428        // Step 7.6
5429        document
5430            .upcast::<EventTarget>()
5431            .fire_event(atom!("fullscreenchange"), CanGc::note());
5432
5433        // Step 7.7
5434        promise.resolve_native(&(), CanGc::note());
5435    }
5436}
5437
5438pub(crate) struct ElementPerformFullscreenExit {
5439    element: Trusted<Element>,
5440    promise: TrustedPromise,
5441}
5442
5443impl ElementPerformFullscreenExit {
5444    pub(crate) fn new(
5445        element: Trusted<Element>,
5446        promise: TrustedPromise,
5447    ) -> Box<ElementPerformFullscreenExit> {
5448        Box::new(ElementPerformFullscreenExit { element, promise })
5449    }
5450}
5451
5452impl TaskOnce for ElementPerformFullscreenExit {
5453    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
5454    fn run_once(self) {
5455        let element = self.element.root();
5456        let document = element.owner_document();
5457        // TODO Step 9.1-5
5458        // Step 9.6
5459        element.set_fullscreen_state(false);
5460        document.set_fullscreen_element(None);
5461
5462        // Step 9.8
5463        document
5464            .upcast::<EventTarget>()
5465            .fire_event(atom!("fullscreenchange"), CanGc::note());
5466
5467        // Step 9.10
5468        self.promise.root().resolve_native(&(), CanGc::note());
5469    }
5470}
5471
5472/// <https://html.spec.whatwg.org/multipage/#cors-settings-attribute>
5473pub(crate) fn reflect_cross_origin_attribute(element: &Element) -> Option<DOMString> {
5474    element
5475        .get_attribute(&ns!(), &local_name!("crossorigin"))
5476        .map(|attribute| {
5477            let value = attribute.value().to_ascii_lowercase();
5478            if value == "anonymous" || value == "use-credentials" {
5479                DOMString::from(value)
5480            } else {
5481                DOMString::from("anonymous")
5482            }
5483        })
5484}
5485
5486pub(crate) fn set_cross_origin_attribute(
5487    element: &Element,
5488    value: Option<DOMString>,
5489    can_gc: CanGc,
5490) {
5491    match value {
5492        Some(val) => element.set_string_attribute(&local_name!("crossorigin"), val, can_gc),
5493        None => {
5494            element.remove_attribute(&ns!(), &local_name!("crossorigin"), can_gc);
5495        },
5496    }
5497}
5498
5499/// <https://html.spec.whatwg.org/multipage/#referrer-policy-attribute>
5500pub(crate) fn reflect_referrer_policy_attribute(element: &Element) -> DOMString {
5501    element
5502        .get_attribute(&ns!(), &local_name!("referrerpolicy"))
5503        .map(|attribute| {
5504            let value = attribute.value().to_ascii_lowercase();
5505            if value == "no-referrer" ||
5506                value == "no-referrer-when-downgrade" ||
5507                value == "same-origin" ||
5508                value == "origin" ||
5509                value == "strict-origin" ||
5510                value == "origin-when-cross-origin" ||
5511                value == "strict-origin-when-cross-origin" ||
5512                value == "unsafe-url"
5513            {
5514                DOMString::from(value)
5515            } else {
5516                DOMString::new()
5517            }
5518        })
5519        .unwrap_or_default()
5520}
5521
5522pub(crate) fn referrer_policy_for_element(element: &Element) -> ReferrerPolicy {
5523    element
5524        .get_attribute(&ns!(), &local_name!("referrerpolicy"))
5525        .map(|attribute| ReferrerPolicy::from(&**attribute.value()))
5526        .unwrap_or(element.owner_document().get_referrer_policy())
5527}
5528
5529pub(crate) fn cors_setting_for_element(element: &Element) -> Option<CorsSettings> {
5530    element
5531        .get_attribute(&ns!(), &local_name!("crossorigin"))
5532        .map(|attribute| CorsSettings::from_enumerated_attribute(&attribute.value()))
5533}