script/dom/
element.rs

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