script/dom/
element.rs

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