Skip to main content

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