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