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