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