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