1use std::borrow::Cow;
8use std::cell::{Cell, LazyCell};
9use std::default::Default;
10use std::ops::Deref;
11use std::rc::Rc;
12use std::str::FromStr;
13use std::sync::atomic::{AtomicUsize, Ordering};
14use std::{fmt, mem};
15
16use app_units::Au;
17use cssparser::match_ignore_ascii_case;
18use devtools_traits::{AttrInfo, DomMutation, ScriptToDevtoolsControlMsg};
19use dom_struct::dom_struct;
20use euclid::Rect;
21use html5ever::serialize::TraversalScope;
22use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
23use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns};
24use js::context::JSContext;
25use js::jsapi::{Heap, JSAutoRealm};
26use js::jsval::JSVal;
27use js::rust::HandleObject;
28use layout_api::{LayoutDamage, ScrollContainerQueryFlags};
29use net_traits::ReferrerPolicy;
30use net_traits::request::{CorsSettings, CredentialsMode};
31use selectors::Element as SelectorsElement;
32use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
33use selectors::bloom::{BLOOM_HASH_MASK, BloomFilter};
34use selectors::matching::{ElementSelectorFlags, MatchingContext};
35use selectors::sink::Push;
36use servo_arc::Arc;
37use style::applicable_declarations::ApplicableDeclarationBlock;
38use style::attr::{AttrValue, LengthOrPercentageOrAuto};
39use style::context::QuirksMode;
40use style::invalidation::element::restyle_hints::RestyleHint;
41use style::properties::longhands::{
42 self, background_image, border_spacing, color, font_family, font_size,
43};
44use style::properties::{
45 ComputedValues, Importance, PropertyDeclaration, PropertyDeclarationBlock,
46 parse_style_attribute,
47};
48use style::rule_tree::{CascadeLevel, CascadeOrigin};
49use style::selector_parser::{
50 NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser, Snapshot,
51 extended_filtering,
52};
53use style::shared_lock::Locked;
54use style::stylesheets::layer_rule::LayerOrder;
55use style::stylesheets::{CssRuleType, UrlExtraData};
56use style::values::computed::Overflow;
57use style::values::generics::NonNegative;
58use style::values::generics::position::PreferredRatio;
59use style::values::generics::ratio::Ratio;
60use style::values::{AtomIdent, AtomString, CSSFloat, computed, specified};
61use style::{ArcSlice, CaseSensitivityExt, dom_apis, thread_state};
62use style_traits::CSSPixel;
63use stylo_atoms::Atom;
64use stylo_dom::ElementState;
65use xml5ever::serialize::TraversalScope::{
66 ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode,
67};
68
69use crate::conversions::Convert;
70use crate::dom::activation::Activatable;
71use crate::dom::attr::{Attr, AttrHelpersForLayout, is_relevant_attribute};
72use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut};
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::FunctionBinding::Function;
79use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
80use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
81use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
82use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
83 ShadowRootMethods, ShadowRootMode, SlotAssignmentMode,
84};
85use crate::dom::bindings::codegen::Bindings::WindowBinding::{
86 ScrollBehavior, ScrollToOptions, WindowMethods,
87};
88use crate::dom::bindings::codegen::UnionTypes::{
89 BooleanOrScrollIntoViewOptions, NodeOrString, TrustedHTMLOrNullIsEmptyString,
90 TrustedHTMLOrString,
91 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString,
92};
93use crate::dom::bindings::conversions::DerivedFrom;
94use crate::dom::bindings::domname::{
95 self, is_valid_attribute_local_name, namespace_from_domstring,
96};
97use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
98use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
99use crate::dom::bindings::num::Finite;
100use crate::dom::bindings::reflector::DomObject;
101use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout};
102use crate::dom::bindings::str::DOMString;
103use crate::dom::characterdata::CharacterData;
104use crate::dom::create::create_element;
105use crate::dom::csp::{CspReporting, InlineCheckType, SourcePosition};
106use crate::dom::customelementregistry::{
107 CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementRegistry,
108 CustomElementState, is_valid_custom_element_name,
109};
110use crate::dom::document::{Document, LayoutDocumentHelpers};
111use crate::dom::documentfragment::DocumentFragment;
112use crate::dom::domrect::DOMRect;
113use crate::dom::domrectlist::DOMRectList;
114use crate::dom::domtokenlist::DOMTokenList;
115use crate::dom::elementinternals::ElementInternals;
116use crate::dom::globalscope::GlobalScope;
117use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
118use crate::dom::html::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
119use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
120use crate::dom::html::htmlcollection::HTMLCollection;
121use crate::dom::html::htmlelement::HTMLElement;
122use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
123use crate::dom::html::htmlfontelement::{HTMLFontElement, HTMLFontElementLayoutHelpers};
124use crate::dom::html::htmlformelement::FormControlElementHelpers;
125use crate::dom::html::htmlhrelement::{HTMLHRElement, HTMLHRLayoutHelpers, SizePresentationalHint};
126use crate::dom::html::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods};
127use crate::dom::html::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
128use crate::dom::html::htmllabelelement::HTMLLabelElement;
129use crate::dom::html::htmllegendelement::HTMLLegendElement;
130use crate::dom::html::htmllinkelement::HTMLLinkElement;
131use crate::dom::html::htmlobjectelement::HTMLObjectElement;
132use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
133use crate::dom::html::htmloutputelement::HTMLOutputElement;
134use crate::dom::html::htmlscriptelement::HTMLScriptElement;
135use crate::dom::html::htmlselectelement::HTMLSelectElement;
136use crate::dom::html::htmlslotelement::{HTMLSlotElement, Slottable};
137use crate::dom::html::htmlstyleelement::HTMLStyleElement;
138use crate::dom::html::htmltablecellelement::{
139 HTMLTableCellElement, HTMLTableCellElementLayoutHelpers,
140};
141use crate::dom::html::htmltablecolelement::{
142 HTMLTableColElement, HTMLTableColElementLayoutHelpers,
143};
144use crate::dom::html::htmltableelement::{HTMLTableElement, HTMLTableElementLayoutHelpers};
145use crate::dom::html::htmltablerowelement::{
146 HTMLTableRowElement, HTMLTableRowElementLayoutHelpers,
147};
148use crate::dom::html::htmltablesectionelement::{
149 HTMLTableSectionElement, HTMLTableSectionElementLayoutHelpers,
150};
151use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
152use crate::dom::html::htmltextareaelement::{
153 HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers,
154};
155use crate::dom::html::htmlvideoelement::{HTMLVideoElement, LayoutHTMLVideoElementHelpers};
156use crate::dom::input_element::{HTMLInputElement, LayoutHTMLInputElementHelpers};
157use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserverRegistration};
158use crate::dom::mutationobserver::{Mutation, MutationObserver};
159use crate::dom::namednodemap::NamedNodeMap;
160use crate::dom::node::{
161 BindContext, ChildrenMutation, CloneChildrenFlag, IsShadowTree, LayoutNodeHelpers, Node,
162 NodeDamage, NodeFlags, NodeTraits, ShadowIncluding, 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::scrolling_box::{ScrollAxisState, ScrollingBox};
169use crate::dom::servoparser::ServoParser;
170use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
171use crate::dom::svg::svgsvgelement::{LayoutSVGSVGElementHelpers, SVGSVGElement};
172use crate::dom::text::Text;
173use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
174use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
175use crate::dom::validation::Validatable;
176use crate::dom::validitystate::ValidationFlags;
177use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
178use crate::script_runtime::CanGc;
179use crate::script_thread::ScriptThread;
180use crate::stylesheet_loader::StylesheetOwner;
181
182#[dom_struct]
188pub struct Element {
189 node: Node,
190 #[no_trace]
191 local_name: LocalName,
192 tag_name: TagName,
193 #[no_trace]
194 namespace: Namespace,
195 #[no_trace]
196 prefix: DomRefCell<Option<Prefix>>,
197 attrs: DomRefCell<Vec<Dom<Attr>>>,
198 #[no_trace]
199 id_attribute: DomRefCell<Option<Atom>>,
200 #[no_trace]
202 is: DomRefCell<Option<LocalName>>,
203 #[conditional_malloc_size_of]
204 #[no_trace]
205 style_attribute: DomRefCell<Option<Arc<Locked<PropertyDeclarationBlock>>>>,
206 attr_list: MutNullableDom<NamedNodeMap>,
207 class_list: MutNullableDom<DOMTokenList>,
208 #[no_trace]
209 state: Cell<ElementState>,
210 selector_flags: AtomicUsize,
213 rare_data: DomRefCell<Option<Box<ElementRareData>>>,
214}
215
216impl fmt::Debug for Element {
217 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218 write!(f, "<{}", self.local_name)?;
219 if let Some(ref id) = *self.id_attribute.borrow() {
220 write!(f, " id={}", id)?;
221 }
222 write!(f, ">")
223 }
224}
225
226#[derive(MallocSizeOf, PartialEq)]
227pub(crate) enum ElementCreator {
228 ParserCreated(u64),
229 ScriptCreated,
230}
231
232pub(crate) enum CustomElementCreationMode {
233 Synchronous,
234 Asynchronous,
235}
236
237impl ElementCreator {
238 pub(crate) fn is_parser_created(&self) -> bool {
239 match *self {
240 ElementCreator::ParserCreated(_) => true,
241 ElementCreator::ScriptCreated => false,
242 }
243 }
244 pub(crate) fn return_line_number(&self) -> u64 {
245 match *self {
246 ElementCreator::ParserCreated(l) => l,
247 ElementCreator::ScriptCreated => 1,
248 }
249 }
250}
251
252pub(crate) enum AdjacentPosition {
253 BeforeBegin,
254 AfterEnd,
255 AfterBegin,
256 BeforeEnd,
257}
258
259impl FromStr for AdjacentPosition {
260 type Err = Error;
261
262 fn from_str(position: &str) -> Result<Self, Self::Err> {
263 match_ignore_ascii_case! { position,
264 "beforebegin" => Ok(AdjacentPosition::BeforeBegin),
265 "afterbegin" => Ok(AdjacentPosition::AfterBegin),
266 "beforeend" => Ok(AdjacentPosition::BeforeEnd),
267 "afterend" => Ok(AdjacentPosition::AfterEnd),
268 _ => Err(Error::Syntax(None))
269 }
270 }
271}
272
273impl Element {
277 pub(crate) fn create(
278 cx: &mut JSContext,
279 name: QualName,
280 is: Option<LocalName>,
281 document: &Document,
282 creator: ElementCreator,
283 mode: CustomElementCreationMode,
284 proto: Option<HandleObject>,
285 ) -> DomRoot<Element> {
286 create_element(cx, name, is, document, creator, mode, proto)
287 }
288
289 pub(crate) fn new_inherited(
290 local_name: LocalName,
291 namespace: Namespace,
292 prefix: Option<Prefix>,
293 document: &Document,
294 ) -> Element {
295 Element::new_inherited_with_state(
296 ElementState::empty(),
297 local_name,
298 namespace,
299 prefix,
300 document,
301 )
302 }
303
304 pub(crate) fn new_inherited_with_state(
305 state: ElementState,
306 local_name: LocalName,
307 namespace: Namespace,
308 prefix: Option<Prefix>,
309 document: &Document,
310 ) -> Element {
311 Element {
312 node: Node::new_inherited(document),
313 local_name,
314 tag_name: TagName::new(),
315 namespace,
316 prefix: DomRefCell::new(prefix),
317 attrs: DomRefCell::new(vec![]),
318 id_attribute: DomRefCell::new(None),
319 is: DomRefCell::new(None),
320 style_attribute: DomRefCell::new(None),
321 attr_list: Default::default(),
322 class_list: Default::default(),
323 state: Cell::new(state),
324 selector_flags: Default::default(),
325 rare_data: Default::default(),
326 }
327 }
328
329 pub(crate) fn set_had_duplicate_attributes(&self) {
330 self.ensure_rare_data().had_duplicate_attributes = true;
331 }
332
333 pub(crate) fn new(
334 cx: &mut js::context::JSContext,
335 local_name: LocalName,
336 namespace: Namespace,
337 prefix: Option<Prefix>,
338 document: &Document,
339 proto: Option<HandleObject>,
340 ) -> DomRoot<Element> {
341 Node::reflect_node_with_proto(
342 cx,
343 Box::new(Element::new_inherited(
344 local_name, namespace, prefix, document,
345 )),
346 document,
347 proto,
348 )
349 }
350
351 fn rare_data(&self) -> Ref<'_, Option<Box<ElementRareData>>> {
352 self.rare_data.borrow()
353 }
354
355 fn rare_data_mut(&self) -> RefMut<'_, Option<Box<ElementRareData>>> {
356 self.rare_data.borrow_mut()
357 }
358
359 fn ensure_rare_data(&self) -> RefMut<'_, Box<ElementRareData>> {
360 let mut rare_data = self.rare_data.borrow_mut();
361 if rare_data.is_none() {
362 *rare_data = Some(Default::default());
363 }
364 RefMut::map(rare_data, |rare_data| rare_data.as_mut().unwrap())
365 }
366
367 pub(crate) fn restyle(&self, damage: NodeDamage) {
368 let doc = self.node.owner_doc();
369 let mut restyle = doc.ensure_pending_restyle(self);
370
371 restyle.hint.insert(RestyleHint::RESTYLE_SELF);
374
375 match damage {
376 NodeDamage::Style => {},
377 NodeDamage::ContentOrHeritage => {
378 doc.note_node_with_dirty_descendants(self.upcast());
379 restyle
380 .damage
381 .insert(LayoutDamage::descendant_has_box_damage());
382 },
383 NodeDamage::Other => {
384 doc.note_node_with_dirty_descendants(self.upcast());
385 restyle.damage.insert(RestyleDamage::reconstruct());
386 },
387 }
388 }
389
390 pub(crate) fn set_is(&self, is: LocalName) {
391 *self.is.borrow_mut() = Some(is);
392 }
393
394 pub(crate) fn get_is(&self) -> Option<LocalName> {
396 self.is.borrow().clone()
397 }
398
399 pub(crate) fn set_initial_custom_element_state_to_uncustomized(&self) {
408 let mut state = self.state.get();
409 state.insert(ElementState::DEFINED);
410 self.state.set(state);
411 }
412
413 pub(crate) fn set_custom_element_state(&self, state: CustomElementState) {
415 if state != CustomElementState::Uncustomized {
417 self.ensure_rare_data().custom_element_state = state;
418 }
419
420 let in_defined_state = matches!(
421 state,
422 CustomElementState::Uncustomized | CustomElementState::Custom
423 );
424 self.set_state(ElementState::DEFINED, in_defined_state)
425 }
426
427 pub(crate) fn get_custom_element_state(&self) -> CustomElementState {
428 if let Some(rare_data) = self.rare_data().as_ref() {
429 return rare_data.custom_element_state;
430 }
431 CustomElementState::Uncustomized
432 }
433
434 pub(crate) fn is_custom(&self) -> bool {
436 self.get_custom_element_state() == CustomElementState::Custom
437 }
438
439 pub(crate) fn set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>) {
440 self.ensure_rare_data().custom_element_definition = Some(definition);
441 }
442
443 pub(crate) fn get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>> {
444 self.rare_data().as_ref()?.custom_element_definition.clone()
445 }
446
447 pub(crate) fn clear_custom_element_definition(&self) {
448 self.ensure_rare_data().custom_element_definition = None;
449 }
450
451 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
452 pub(crate) fn push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>) {
453 self.ensure_rare_data()
454 .custom_element_reaction_queue
455 .push(CustomElementReaction::Callback(function, args));
456 }
457
458 pub(crate) fn push_upgrade_reaction(&self, definition: Rc<CustomElementDefinition>) {
459 self.ensure_rare_data()
460 .custom_element_reaction_queue
461 .push(CustomElementReaction::Upgrade(definition));
462 }
463
464 pub(crate) fn clear_reaction_queue(&self) {
465 if let Some(ref mut rare_data) = *self.rare_data_mut() {
466 rare_data.custom_element_reaction_queue.clear();
467 }
468 }
469
470 pub(crate) fn invoke_reactions(&self, cx: &mut JSContext) {
471 loop {
472 rooted_vec!(let mut reactions);
473 match *self.rare_data_mut() {
474 Some(ref mut data) => {
475 mem::swap(&mut *reactions, &mut data.custom_element_reaction_queue)
476 },
477 None => break,
478 };
479
480 if reactions.is_empty() {
481 break;
482 }
483
484 for reaction in reactions.iter() {
485 reaction.invoke(cx, self);
486 }
487
488 reactions.clear();
489 }
490 }
491
492 pub(crate) fn style(&self) -> Option<Arc<ComputedValues>> {
495 self.upcast::<Node>().style()
496 }
497
498 pub(crate) fn has_css_layout_box(&self) -> bool {
500 self.style()
501 .is_some_and(|s| !s.get_box().clone_display().is_none())
502 }
503
504 pub(crate) fn is_potentially_scrollable_body(&self) -> bool {
506 self.is_potentially_scrollable_body_shared_logic(false)
507 }
508
509 pub(crate) fn is_potentially_scrollable_body_for_scrolling_element(&self) -> bool {
511 self.is_potentially_scrollable_body_shared_logic(true)
512 }
513
514 fn is_potentially_scrollable_body_shared_logic(
516 &self,
517 treat_overflow_clip_on_parent_as_hidden: bool,
518 ) -> bool {
519 let node = self.upcast::<Node>();
520 debug_assert!(
521 node.owner_doc().GetBody().as_deref() == self.downcast::<HTMLElement>(),
522 "Called is_potentially_scrollable_body on element that is not the <body>"
523 );
524
525 if !self.has_css_layout_box() {
529 return false;
530 }
531
532 if let Some(parent) = node.GetParentElement() {
535 if let Some(style) = parent.style() {
536 let mut overflow_x = style.get_box().clone_overflow_x();
537 let mut overflow_y = style.get_box().clone_overflow_y();
538
539 if treat_overflow_clip_on_parent_as_hidden {
542 if overflow_x == Overflow::Clip {
543 overflow_x = Overflow::Hidden;
544 }
545 if overflow_y == Overflow::Clip {
546 overflow_y = Overflow::Hidden;
547 }
548 }
549
550 if !overflow_x.is_scrollable() && !overflow_y.is_scrollable() {
551 return false;
552 }
553 };
554 }
555
556 if let Some(style) = self.style() {
559 if !style.get_box().clone_overflow_x().is_scrollable() &&
560 !style.get_box().clone_overflow_y().is_scrollable()
561 {
562 return false;
563 }
564 };
565
566 true
567 }
568
569 pub(crate) fn establishes_scroll_container(&self) -> bool {
572 self.upcast::<Node>()
574 .effective_overflow()
575 .is_some_and(|overflow| overflow.establishes_scroll_container())
576 }
577
578 pub(crate) fn has_overflow(&self) -> bool {
579 self.ScrollHeight() > self.ClientHeight() || self.ScrollWidth() > self.ClientWidth()
580 }
581
582 fn has_scrolling_box(&self) -> bool {
590 self.has_css_layout_box() && self.establishes_scroll_container() && self.has_overflow()
591 }
592
593 pub(crate) fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
594 self.rare_data()
595 .as_ref()?
596 .shadow_root
597 .as_ref()
598 .map(|sr| DomRoot::from_ref(&**sr))
599 }
600
601 pub(crate) fn is_shadow_host(&self) -> bool {
602 self.shadow_root().is_some()
603 }
604
605 #[allow(clippy::too_many_arguments)]
607 pub(crate) fn attach_shadow(
608 &self,
609 cx: &mut JSContext,
610 is_ua_widget: IsUserAgentWidget,
611 mode: ShadowRootMode,
612 clonable: bool,
613 serializable: bool,
614 delegates_focus: bool,
615 slot_assignment_mode: SlotAssignmentMode,
616 ) -> Fallible<DomRoot<ShadowRoot>> {
617 if self.namespace != ns!(html) {
620 return Err(Error::NotSupported(Some(
621 "Cannot attach shadow roots to elements with non-HTML namespaces".to_owned(),
622 )));
623 }
624
625 if !is_valid_shadow_host_name(self.local_name()) {
628 if is_ua_widget != IsUserAgentWidget::Yes {
630 let error_message = format!(
631 "Cannot attach shadow roots to <{}> elements",
632 *self.local_name()
633 );
634 return Err(Error::NotSupported(Some(error_message)));
635 }
636 }
637
638 if is_valid_custom_element_name(self.local_name()) || self.get_is().is_some() {
641 let definition = self.get_custom_element_definition();
645 if definition.is_some_and(|definition| definition.disable_shadow) {
648 let error_message = format!(
649 "The custom element constructor of <{}> disabled attachment of shadow roots",
650 self.local_name()
651 );
652 return Err(Error::NotSupported(Some(error_message)));
653 }
654 }
655
656 if let Some(current_shadow_root) = self.shadow_root() {
659 if !current_shadow_root.is_declarative() ||
663 current_shadow_root.shadow_root_mode() != mode
664 {
665 return Err(Error::NotSupported(Some(
666 "Cannot attach a second shadow root to the same element".into(),
667 )));
668 }
669
670 for child in current_shadow_root.upcast::<Node>().children() {
672 child.remove_self(cx);
673 }
674
675 current_shadow_root.set_declarative(false);
677
678 return Ok(current_shadow_root);
680 }
681
682 let shadow_root = ShadowRoot::new(
689 self,
690 &self.node.owner_doc(),
691 mode,
692 slot_assignment_mode,
693 clonable,
694 is_ua_widget,
695 CanGc::from_cx(cx),
696 );
697
698 let node = self.upcast::<Node>();
702 node.remove_layout_boxes_from_subtree();
703
704 shadow_root.set_delegates_focus(delegates_focus);
706
707 if matches!(
710 self.get_custom_element_state(),
711 CustomElementState::Precustomized | CustomElementState::Custom
712 ) {
713 shadow_root.set_available_to_element_internals(true);
714 }
715
716 shadow_root.set_declarative(false);
718
719 shadow_root.set_serializable(serializable);
721
722 self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
724 shadow_root
725 .upcast::<Node>()
726 .set_containing_shadow_root(Some(&shadow_root));
727
728 let bind_context = BindContext::new(self.upcast(), IsShadowTree::Yes);
729 shadow_root.bind_to_tree(cx, &bind_context);
730
731 node.dirty(NodeDamage::Other);
732
733 Ok(shadow_root)
734 }
735
736 pub(crate) fn attach_ua_shadow_root(
752 &self,
753 cx: &mut JSContext,
754 use_ua_widget_styling: bool,
755 ) -> DomRoot<ShadowRoot> {
756 let root = self
757 .attach_shadow(
758 cx,
759 IsUserAgentWidget::Yes,
760 ShadowRootMode::Closed,
761 false,
762 false,
763 false,
764 SlotAssignmentMode::Manual,
765 )
766 .expect("Attaching UA shadow root failed");
767
768 root.upcast::<Node>()
769 .set_in_ua_widget(use_ua_widget_styling);
770 root
771 }
772
773 pub(crate) fn is_translate_enabled(&self) -> bool {
775 let name = &local_name!("translate");
776 if self.has_attribute(name) {
777 let attribute = self.get_string_attribute(name);
778 match_ignore_ascii_case! { &*attribute.str(),
779 "yes" | "" => return true,
780 "no" => return false,
781 _ => {},
782 }
783 }
784 if let Some(parent) = self.upcast::<Node>().GetParentNode() {
785 if let Some(elem) = parent.downcast::<Element>() {
786 return elem.is_translate_enabled();
787 }
788 }
789 true
790 }
791
792 pub(crate) fn directionality(&self) -> String {
794 self.downcast::<HTMLElement>()
795 .and_then(|html_element| html_element.directionality())
796 .unwrap_or_else(|| {
797 let node = self.upcast::<Node>();
798 node.parent_directionality()
799 })
800 }
801
802 pub(crate) fn is_root(&self) -> bool {
803 match self.node.GetParentNode() {
804 None => false,
805 Some(node) => node.is::<Document>(),
806 }
807 }
808
809 pub(crate) fn registered_intersection_observers_mut(
812 &self,
813 ) -> RefMut<'_, Vec<IntersectionObserverRegistration>> {
814 RefMut::map(self.ensure_rare_data(), |rare_data| {
815 &mut rare_data.registered_intersection_observers
816 })
817 }
818
819 pub(crate) fn registered_intersection_observers(
820 &self,
821 ) -> Option<Ref<'_, Vec<IntersectionObserverRegistration>>> {
822 let rare_data: Ref<'_, _> = self.rare_data.borrow();
823
824 if rare_data.is_none() {
825 return None;
826 }
827 Some(Ref::map(rare_data, |rare_data| {
828 &rare_data
829 .as_ref()
830 .unwrap()
831 .registered_intersection_observers
832 }))
833 }
834
835 pub(crate) fn get_intersection_observer_registration(
836 &self,
837 observer: &IntersectionObserver,
838 ) -> Option<Ref<'_, IntersectionObserverRegistration>> {
839 if let Some(registrations) = self.registered_intersection_observers() {
840 registrations
841 .iter()
842 .position(|reg_obs| reg_obs.observer == observer)
843 .map(|index| Ref::map(registrations, |registrations| ®istrations[index]))
844 } else {
845 None
846 }
847 }
848
849 pub(crate) fn add_initial_intersection_observer_registration(
851 &self,
852 observer: &IntersectionObserver,
853 ) {
854 self.ensure_rare_data()
855 .registered_intersection_observers
856 .push(IntersectionObserverRegistration::new_initial(observer));
857 }
858
859 pub(crate) fn remove_intersection_observer(&self, observer: &IntersectionObserver) {
861 self.ensure_rare_data()
862 .registered_intersection_observers
863 .retain(|reg_obs| *reg_obs.observer != *observer)
864 }
865
866 pub(crate) fn scrolling_box(&self, flags: ScrollContainerQueryFlags) -> Option<ScrollingBox> {
869 self.owner_window()
870 .scrolling_box_query(Some(self.upcast()), flags)
871 }
872
873 pub(crate) fn scroll_into_view_with_options(
875 &self,
876 behavior: ScrollBehavior,
877 block: ScrollAxisState,
878 inline: ScrollAxisState,
879 container: Option<&Element>,
880 inner_target_rect: Option<Rect<Au, CSSPixel>>,
881 ) {
882 let get_target_rect = || match inner_target_rect {
883 None => self.upcast::<Node>().border_box().unwrap_or_default(),
884 Some(inner_target_rect) => inner_target_rect.translate(
885 self.upcast::<Node>()
886 .content_box()
887 .unwrap_or_default()
888 .origin
889 .to_vector(),
890 ),
891 };
892
893 let mut parent_scrolling_box = self.scrolling_box(ScrollContainerQueryFlags::empty());
896 while let Some(scrolling_box) = parent_scrolling_box {
897 parent_scrolling_box = scrolling_box.parent();
898
899 let position =
910 scrolling_box.determine_scroll_into_view_position(block, inline, get_target_rect());
911
912 if position != scrolling_box.scroll_position() {
917 scrolling_box.scroll_to(position, behavior);
928 }
929
930 if container.is_some_and(|container| {
934 let container_node = container.upcast::<Node>();
935 scrolling_box
936 .node()
937 .is_shadow_including_inclusive_ancestor_of(container_node)
938 }) {
939 return;
940 }
941 }
942
943 let window_proxy = self.owner_window().window_proxy();
944 let Some(frame_element) = window_proxy.frame_element() else {
945 return;
946 };
947
948 let inner_target_rect = Some(get_target_rect());
949 let parent_window = frame_element.owner_window();
950 let cx = GlobalScope::get_cx();
951 let _ac = JSAutoRealm::new(*cx, *parent_window.reflector().get_jsobject());
952 frame_element.scroll_into_view_with_options(
953 behavior,
954 block,
955 inline,
956 None,
957 inner_target_rect,
958 )
959 }
960
961 pub(crate) fn ensure_contenteditable_selection_range(
962 &self,
963 document: &Document,
964 can_gc: CanGc,
965 ) -> DomRoot<Range> {
966 self.ensure_rare_data()
967 .contenteditable_selection_range
968 .or_init(|| Range::new_with_doc(document, None, can_gc))
969 }
970
971 pub(crate) fn handle_scroll_event(&self) {
976 let document = self.owner_document();
978
979 document.finish_handle_scroll_event(self.upcast());
988 }
989}
990
991#[inline]
993pub(crate) fn is_valid_shadow_host_name(name: &LocalName) -> bool {
994 if is_valid_custom_element_name(name) {
997 return true;
998 }
999
1000 matches!(
1003 name,
1004 &local_name!("article") |
1005 &local_name!("aside") |
1006 &local_name!("blockquote") |
1007 &local_name!("body") |
1008 &local_name!("div") |
1009 &local_name!("footer") |
1010 &local_name!("h1") |
1011 &local_name!("h2") |
1012 &local_name!("h3") |
1013 &local_name!("h4") |
1014 &local_name!("h5") |
1015 &local_name!("h6") |
1016 &local_name!("header") |
1017 &local_name!("main") |
1018 &local_name!("nav") |
1019 &local_name!("p") |
1020 &local_name!("section") |
1021 &local_name!("span")
1022 )
1023}
1024
1025#[inline]
1026pub(crate) fn get_attr_for_layout<'dom>(
1027 elem: LayoutDom<'dom, Element>,
1028 namespace: &Namespace,
1029 name: &LocalName,
1030) -> Option<&'dom AttrValue> {
1031 elem.attrs()
1032 .iter()
1033 .find(|attr| name == attr.local_name() && namespace == attr.namespace())
1034 .map(|attr| attr.value())
1035}
1036
1037pub(crate) trait LayoutElementHelpers<'dom> {
1038 fn attrs(self) -> &'dom [LayoutDom<'dom, Attr>];
1039 fn has_class_or_part_for_layout(
1040 self,
1041 name: &AtomIdent,
1042 attr_name: &LocalName,
1043 case_sensitivity: CaseSensitivity,
1044 ) -> bool;
1045 fn get_classes_for_layout(self) -> Option<&'dom [Atom]>;
1046 fn get_parts_for_layout(self) -> Option<&'dom [Atom]>;
1047
1048 fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
1049 where
1050 V: Push<ApplicableDeclarationBlock>;
1051 fn get_span(self) -> Option<u32>;
1052 fn get_colspan(self) -> Option<u32>;
1053 fn get_rowspan(self) -> Option<u32>;
1054 fn is_html_element(&self) -> bool;
1055 fn id_attribute(self) -> *const Option<Atom>;
1056 fn style_attribute(self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>>;
1057 fn local_name(self) -> &'dom LocalName;
1058 fn namespace(self) -> &'dom Namespace;
1059 fn get_lang_attr_val_for_layout(self) -> Option<&'dom str>;
1060 fn get_lang_for_layout(self) -> String;
1061 fn get_state_for_layout(self) -> ElementState;
1062 fn insert_selector_flags(self, flags: ElementSelectorFlags);
1063 fn get_selector_flags(self) -> ElementSelectorFlags;
1064 fn get_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>>;
1066 fn get_attr_for_layout(
1067 self,
1068 namespace: &Namespace,
1069 name: &LocalName,
1070 ) -> Option<&'dom AttrValue>;
1071 fn get_attr_val_for_layout(self, namespace: &Namespace, name: &LocalName) -> Option<&'dom str>;
1072 fn get_attr_vals_for_layout(self, name: &LocalName) -> impl Iterator<Item = &'dom AttrValue>;
1073 fn each_custom_state_for_layout(self, allback: impl FnMut(&AtomIdent));
1074}
1075
1076impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> {
1077 #[expect(unsafe_code)]
1078 #[inline]
1079 fn attrs(self) -> &'dom [LayoutDom<'dom, Attr>] {
1080 unsafe { LayoutDom::to_layout_slice(self.unsafe_get().attrs.borrow_for_layout()) }
1081 }
1082
1083 #[inline]
1084 fn has_class_or_part_for_layout(
1085 self,
1086 name: &AtomIdent,
1087 attr_name: &LocalName,
1088 case_sensitivity: CaseSensitivity,
1089 ) -> bool {
1090 get_attr_for_layout(self, &ns!(), attr_name).is_some_and(|attr| {
1091 attr.as_tokens()
1092 .iter()
1093 .any(|atom| case_sensitivity.eq_atom(atom, name))
1094 })
1095 }
1096
1097 #[inline]
1098 fn get_classes_for_layout(self) -> Option<&'dom [Atom]> {
1099 get_attr_for_layout(self, &ns!(), &local_name!("class")).map(|attr| attr.as_tokens())
1100 }
1101
1102 fn get_parts_for_layout(self) -> Option<&'dom [Atom]> {
1103 get_attr_for_layout(self, &ns!(), &local_name!("part")).map(|attr| attr.as_tokens())
1104 }
1105
1106 fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
1107 where
1108 V: Push<ApplicableDeclarationBlock>,
1109 {
1110 let document = self.upcast::<Node>().owner_doc_for_layout();
1111 let mut property_declaration_block = None;
1112 let mut push = |declaration| {
1113 property_declaration_block
1114 .get_or_insert_with(PropertyDeclarationBlock::default)
1115 .push(declaration, Importance::Normal);
1116 };
1117
1118 if let Some(lang) = self.get_lang_attr_val_for_layout() {
1121 push(PropertyDeclaration::XLang(specified::XLang(Atom::from(
1122 lang.to_owned(),
1123 ))));
1124 }
1125
1126 let bgcolor = if let Some(this) = self.downcast::<HTMLBodyElement>() {
1127 this.get_background_color()
1128 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1129 this.get_background_color()
1130 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1131 this.get_background_color()
1132 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1133 this.get_background_color()
1134 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1135 this.get_background_color()
1136 } else {
1137 None
1138 };
1139
1140 if let Some(color) = bgcolor {
1141 push(PropertyDeclaration::BackgroundColor(
1142 specified::Color::from_absolute_color(color),
1143 ));
1144 }
1145
1146 if is_element_affected_by_legacy_background_presentational_hint(
1147 self.namespace(),
1148 self.local_name(),
1149 ) {
1150 if let Some(url) = self
1151 .get_attr_for_layout(&ns!(), &local_name!("background"))
1152 .and_then(AttrValue::as_resolved_url)
1153 .cloned()
1154 {
1155 push(PropertyDeclaration::BackgroundImage(
1156 background_image::SpecifiedValue(
1157 vec![specified::Image::for_cascade(url)].into(),
1158 ),
1159 ));
1160 }
1161 }
1162
1163 let color = if let Some(this) = self.downcast::<HTMLFontElement>() {
1164 this.get_color()
1165 } else if let Some(this) = self.downcast::<HTMLBodyElement>() {
1166 this.get_color()
1168 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1169 this.get_color()
1171 } else {
1172 None
1173 };
1174
1175 if let Some(color) = color {
1176 push(PropertyDeclaration::Color(
1177 longhands::color::SpecifiedValue(specified::Color::from_absolute_color(color)),
1178 ));
1179 }
1180
1181 let font_face = self
1182 .downcast::<HTMLFontElement>()
1183 .and_then(HTMLFontElementLayoutHelpers::get_face);
1184 if let Some(font_face) = font_face {
1185 push(PropertyDeclaration::FontFamily(
1186 font_family::SpecifiedValue::Values(computed::font::FontFamilyList {
1187 list: ArcSlice::from_iter(
1188 HTMLFontElement::parse_face_attribute(font_face).into_iter(),
1189 ),
1190 }),
1191 ));
1192 }
1193
1194 let font_size = self
1195 .downcast::<HTMLFontElement>()
1196 .and_then(HTMLFontElementLayoutHelpers::get_size);
1197 if let Some(font_size) = font_size {
1198 push(PropertyDeclaration::FontSize(
1199 font_size::SpecifiedValue::from_html_size(font_size as u8),
1200 ));
1201 }
1202
1203 let size = self
1209 .downcast::<HTMLInputElement>()
1210 .and_then(|input_element| {
1211 match self.get_attr_val_for_layout(&ns!(), &local_name!("type")) {
1213 Some("hidden") | Some("range") | Some("color") | Some("checkbox") |
1214 Some("radio") | Some("file") | Some("submit") | Some("image") |
1215 Some("reset") | Some("button") => None,
1216 _ => match input_element.size_for_layout() {
1218 0 => None,
1219 s => Some(s as i32),
1220 },
1221 }
1222 });
1223
1224 if let Some(size) = size {
1225 let value =
1226 specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(size));
1227 push(PropertyDeclaration::Width(
1228 specified::Size::LengthPercentage(NonNegative(
1229 specified::LengthPercentage::Length(value),
1230 )),
1231 ));
1232 }
1233
1234 let width = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1235 this.get_width()
1236 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1237 this.get_width()
1238 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1239 this.get_width()
1240 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1241 this.get_width()
1242 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1243 this.get_width()
1244 } else if let Some(this) = self.downcast::<HTMLTableColElement>() {
1245 this.get_width()
1246 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1247 this.get_width()
1249 } else {
1250 LengthOrPercentageOrAuto::Auto
1251 };
1252
1253 match width {
1255 LengthOrPercentageOrAuto::Auto => {},
1256 LengthOrPercentageOrAuto::Percentage(percentage) => {
1257 let width_value = specified::Size::LengthPercentage(NonNegative(
1258 specified::LengthPercentage::Percentage(computed::Percentage(percentage)),
1259 ));
1260 push(PropertyDeclaration::Width(width_value));
1261 },
1262 LengthOrPercentageOrAuto::Length(length) => {
1263 let width_value = specified::Size::LengthPercentage(NonNegative(
1264 specified::LengthPercentage::Length(specified::NoCalcLength::Absolute(
1265 specified::AbsoluteLength::Px(length.to_f32_px()),
1266 )),
1267 ));
1268 push(PropertyDeclaration::Width(width_value));
1269 },
1270 }
1271
1272 let height = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1273 this.get_height()
1274 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1275 this.get_height()
1276 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1277 this.get_height()
1278 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1279 this.get_height()
1280 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1281 this.get_height()
1282 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1283 this.get_height()
1284 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1285 this.get_height()
1286 } else {
1287 LengthOrPercentageOrAuto::Auto
1288 };
1289
1290 match height {
1291 LengthOrPercentageOrAuto::Auto => {},
1292 LengthOrPercentageOrAuto::Percentage(percentage) => {
1293 let height_value = specified::Size::LengthPercentage(NonNegative(
1294 specified::LengthPercentage::Percentage(computed::Percentage(percentage)),
1295 ));
1296 push(PropertyDeclaration::Height(height_value));
1297 },
1298 LengthOrPercentageOrAuto::Length(length) => {
1299 let height_value = specified::Size::LengthPercentage(NonNegative(
1300 specified::LengthPercentage::Length(specified::NoCalcLength::Absolute(
1301 specified::AbsoluteLength::Px(length.to_f32_px()),
1302 )),
1303 ));
1304 push(PropertyDeclaration::Height(height_value));
1305 },
1306 }
1307
1308 if let Some(this) = self.downcast::<SVGSVGElement>() {
1309 let data = this.data();
1310 if let Some(width) = data.width.and_then(AttrValue::as_length_percentage) {
1311 push(PropertyDeclaration::Width(
1312 specified::Size::LengthPercentage(NonNegative(width.clone())),
1313 ));
1314 }
1315 if let Some(height) = data.height.and_then(AttrValue::as_length_percentage) {
1316 push(PropertyDeclaration::Height(
1317 specified::Size::LengthPercentage(NonNegative(height.clone())),
1318 ));
1319 }
1320 }
1321
1322 if self.downcast::<HTMLImageElement>().is_some() ||
1325 self.downcast::<HTMLVideoElement>().is_some()
1326 {
1327 if let LengthOrPercentageOrAuto::Length(width) = width {
1328 if let LengthOrPercentageOrAuto::Length(height) = height {
1329 let width_value = NonNegative(specified::Number::new(width.to_f32_px()));
1330 let height_value = NonNegative(specified::Number::new(height.to_f32_px()));
1331 let aspect_ratio = specified::position::AspectRatio {
1332 auto: true,
1333 ratio: PreferredRatio::Ratio(Ratio(width_value, height_value)),
1334 };
1335 push(PropertyDeclaration::AspectRatio(aspect_ratio));
1336 }
1337 }
1338 }
1339
1340 let cols = self
1341 .downcast::<HTMLTextAreaElement>()
1342 .map(LayoutHTMLTextAreaElementHelpers::get_cols);
1343 if let Some(cols) = cols {
1344 let cols = cols as i32;
1345 if cols > 0 {
1346 let value =
1352 specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(cols));
1353 push(PropertyDeclaration::Width(
1354 specified::Size::LengthPercentage(NonNegative(
1355 specified::LengthPercentage::Length(value),
1356 )),
1357 ));
1358 }
1359 }
1360
1361 let rows = self
1362 .downcast::<HTMLTextAreaElement>()
1363 .map(LayoutHTMLTextAreaElementHelpers::get_rows);
1364 if let Some(rows) = rows {
1365 let rows = rows as i32;
1366 if rows > 0 {
1367 let value = specified::NoCalcLength::FontRelative(
1371 specified::FontRelativeLength::Em(rows as CSSFloat),
1372 );
1373 push(PropertyDeclaration::Height(
1374 specified::Size::LengthPercentage(NonNegative(
1375 specified::LengthPercentage::Length(value),
1376 )),
1377 ));
1378 }
1379 }
1380
1381 if let Some(table) = self.downcast::<HTMLTableElement>() {
1382 if let Some(cellspacing) = table.get_cellspacing() {
1383 let width_value = specified::Length::from_px(cellspacing as f32);
1384 push(PropertyDeclaration::BorderSpacing(Box::new(
1385 border_spacing::SpecifiedValue::new(
1386 width_value.clone().into(),
1387 width_value.into(),
1388 ),
1389 )));
1390 }
1391 if let Some(border) = table.get_border() {
1392 let width_value = specified::BorderSideWidth::from_px(border as f32);
1393 push(PropertyDeclaration::BorderTopWidth(width_value.clone()));
1394 push(PropertyDeclaration::BorderLeftWidth(width_value.clone()));
1395 push(PropertyDeclaration::BorderBottomWidth(width_value.clone()));
1396 push(PropertyDeclaration::BorderRightWidth(width_value));
1397 }
1398 if document.quirks_mode() == QuirksMode::Quirks {
1399 push(PropertyDeclaration::Color(color::SpecifiedValue(
1401 specified::Color::InheritFromBodyQuirk,
1402 )));
1403 }
1404 }
1405
1406 if let Some(cellpadding) = self
1407 .downcast::<HTMLTableCellElement>()
1408 .and_then(|this| this.get_table())
1409 .and_then(|table| table.get_cellpadding())
1410 {
1411 let cellpadding = NonNegative(specified::LengthPercentage::Length(
1412 specified::NoCalcLength::from_px(cellpadding as f32),
1413 ));
1414 push(PropertyDeclaration::PaddingTop(cellpadding.clone()));
1415 push(PropertyDeclaration::PaddingLeft(cellpadding.clone()));
1416 push(PropertyDeclaration::PaddingBottom(cellpadding.clone()));
1417 push(PropertyDeclaration::PaddingRight(cellpadding));
1418 }
1419
1420 if let Some(size_info) = self
1422 .downcast::<HTMLHRElement>()
1423 .and_then(|hr_element| hr_element.get_size_info())
1424 {
1425 match size_info {
1426 SizePresentationalHint::SetHeightTo(height) => {
1427 push(PropertyDeclaration::Height(height));
1428 },
1429 SizePresentationalHint::SetAllBorderWidthValuesTo(border_width) => {
1430 push(PropertyDeclaration::BorderLeftWidth(border_width.clone()));
1431 push(PropertyDeclaration::BorderRightWidth(border_width.clone()));
1432 push(PropertyDeclaration::BorderTopWidth(border_width.clone()));
1433 push(PropertyDeclaration::BorderBottomWidth(border_width));
1434 },
1435 SizePresentationalHint::SetBottomBorderWidthToZero => {
1436 push(PropertyDeclaration::BorderBottomWidth(
1437 specified::border::BorderSideWidth::from_px(0.),
1438 ));
1439 },
1440 }
1441 }
1442
1443 let Some(property_declaration_block) = property_declaration_block else {
1444 return;
1445 };
1446
1447 let shared_lock = document.style_shared_lock();
1448 hints.push(ApplicableDeclarationBlock::from_declarations(
1449 Arc::new(shared_lock.wrap(property_declaration_block)),
1450 CascadeLevel::new(CascadeOrigin::PresHints),
1451 LayerOrder::root(),
1452 ));
1453 }
1454
1455 fn get_span(self) -> Option<u32> {
1456 self.downcast::<HTMLTableColElement>()
1458 .and_then(|element| element.get_span())
1459 }
1460
1461 fn get_colspan(self) -> Option<u32> {
1462 self.downcast::<HTMLTableCellElement>()
1464 .and_then(|element| element.get_colspan())
1465 }
1466
1467 fn get_rowspan(self) -> Option<u32> {
1468 self.downcast::<HTMLTableCellElement>()
1470 .and_then(|element| element.get_rowspan())
1471 }
1472
1473 #[inline]
1474 fn is_html_element(&self) -> bool {
1475 *self.namespace() == ns!(html)
1476 }
1477
1478 #[expect(unsafe_code)]
1479 fn id_attribute(self) -> *const Option<Atom> {
1480 unsafe { (self.unsafe_get()).id_attribute.borrow_for_layout() }
1481 }
1482
1483 #[expect(unsafe_code)]
1484 fn style_attribute(self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>> {
1485 unsafe { (self.unsafe_get()).style_attribute.borrow_for_layout() }
1486 }
1487
1488 fn local_name(self) -> &'dom LocalName {
1489 &(self.unsafe_get()).local_name
1490 }
1491
1492 fn namespace(self) -> &'dom Namespace {
1493 &(self.unsafe_get()).namespace
1494 }
1495
1496 fn get_lang_attr_val_for_layout(self) -> Option<&'dom str> {
1497 if let Some(attr) = self.get_attr_val_for_layout(&ns!(xml), &local_name!("lang")) {
1498 return Some(attr);
1499 }
1500 if let Some(attr) = self.get_attr_val_for_layout(&ns!(), &local_name!("lang")) {
1501 return Some(attr);
1502 }
1503 None
1504 }
1505
1506 fn get_lang_for_layout(self) -> String {
1507 let mut current_node = Some(self.upcast::<Node>());
1508 while let Some(node) = current_node {
1509 current_node = node.composed_parent_node_ref();
1510 match node.downcast::<Element>() {
1511 Some(elem) => {
1512 if let Some(attr) = elem.get_lang_attr_val_for_layout() {
1513 return attr.to_owned();
1514 }
1515 },
1516 None => continue,
1517 }
1518 }
1519 String::new()
1522 }
1523
1524 #[inline]
1525 fn get_state_for_layout(self) -> ElementState {
1526 (self.unsafe_get()).state.get()
1527 }
1528
1529 #[inline]
1530 fn insert_selector_flags(self, flags: ElementSelectorFlags) {
1531 debug_assert!(thread_state::get().is_layout());
1532 self.unsafe_get().insert_selector_flags(flags);
1533 }
1534
1535 #[inline]
1536 fn get_selector_flags(self) -> ElementSelectorFlags {
1537 self.unsafe_get().get_selector_flags()
1538 }
1539
1540 #[inline]
1541 #[expect(unsafe_code)]
1542 fn get_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
1543 unsafe {
1544 self.unsafe_get()
1545 .rare_data
1546 .borrow_for_layout()
1547 .as_ref()?
1548 .shadow_root
1549 .as_ref()
1550 .map(|sr| sr.to_layout())
1551 }
1552 }
1553
1554 #[inline]
1555 fn get_attr_for_layout(
1556 self,
1557 namespace: &Namespace,
1558 name: &LocalName,
1559 ) -> Option<&'dom AttrValue> {
1560 get_attr_for_layout(self, namespace, name)
1561 }
1562
1563 #[inline]
1564 fn get_attr_val_for_layout(self, namespace: &Namespace, name: &LocalName) -> Option<&'dom str> {
1565 get_attr_for_layout(self, namespace, name).map(|attr| &**attr)
1566 }
1567
1568 #[inline]
1569 fn get_attr_vals_for_layout(self, name: &LocalName) -> impl Iterator<Item = &'dom AttrValue> {
1570 self.attrs().iter().filter_map(move |attr| {
1571 if name == attr.local_name() {
1572 Some(attr.value())
1573 } else {
1574 None
1575 }
1576 })
1577 }
1578
1579 #[expect(unsafe_code)]
1580 fn each_custom_state_for_layout(self, mut callback: impl FnMut(&AtomIdent)) {
1581 let rare_data = unsafe { self.unsafe_get().rare_data.borrow_for_layout() };
1582 let Some(rare_data) = rare_data.as_ref() else {
1583 return;
1584 };
1585 let Some(element_internals) = rare_data.element_internals.as_ref() else {
1586 return;
1587 };
1588
1589 let element_internals = unsafe { element_internals.to_layout() };
1590 if let Some(states) = element_internals.unsafe_get().custom_states_for_layout() {
1591 for state in unsafe { states.unsafe_get().set_for_layout().iter() } {
1592 callback(&AtomIdent::from(&*state.str()));
1594 }
1595 }
1596 }
1597}
1598
1599impl Element {
1600 pub(crate) fn is_html_element(&self) -> bool {
1601 self.namespace == ns!(html)
1602 }
1603
1604 pub(crate) fn html_element_in_html_document(&self) -> bool {
1605 self.is_html_element() && self.upcast::<Node>().is_in_html_doc()
1606 }
1607
1608 pub(crate) fn local_name(&self) -> &LocalName {
1609 &self.local_name
1610 }
1611
1612 pub(crate) fn parsed_name(&self, mut name: DOMString) -> LocalName {
1613 if self.html_element_in_html_document() {
1614 name.make_ascii_lowercase();
1615 }
1616 LocalName::from(name)
1617 }
1618
1619 pub(crate) fn namespace(&self) -> &Namespace {
1620 &self.namespace
1621 }
1622
1623 pub(crate) fn prefix(&self) -> Ref<'_, Option<Prefix>> {
1624 self.prefix.borrow()
1625 }
1626
1627 pub(crate) fn set_prefix(&self, prefix: Option<Prefix>) {
1628 *self.prefix.borrow_mut() = prefix;
1629 }
1630
1631 pub(crate) fn set_custom_element_registry(
1632 &self,
1633 registry: Option<DomRoot<CustomElementRegistry>>,
1634 ) {
1635 self.ensure_rare_data().custom_element_registry = registry.as_deref().map(Dom::from_ref);
1636 }
1637
1638 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
1639 self.rare_data()
1640 .as_ref()?
1641 .custom_element_registry
1642 .as_deref()
1643 .map(DomRoot::from_ref)
1644 }
1645
1646 pub(crate) fn attrs(&self) -> Ref<'_, [Dom<Attr>]> {
1647 Ref::map(self.attrs.borrow(), |attrs| &**attrs)
1648 }
1649
1650 pub(crate) fn locate_namespace(&self, prefix: Option<DOMString>) -> Namespace {
1652 let namespace_prefix = prefix.clone().map(|s| Prefix::from(&*s.str()));
1653
1654 if namespace_prefix == Some(namespace_prefix!("xml")) {
1656 return ns!(xml);
1657 }
1658
1659 if namespace_prefix == Some(namespace_prefix!("xmlns")) {
1661 return ns!(xmlns);
1662 }
1663
1664 let prefix = prefix.map(LocalName::from);
1665
1666 let inclusive_ancestor_elements = self
1667 .upcast::<Node>()
1668 .inclusive_ancestors(ShadowIncluding::No)
1669 .filter_map(DomRoot::downcast::<Self>);
1670
1671 for element in inclusive_ancestor_elements {
1674 if element.namespace() != &ns!() &&
1676 element.prefix().as_ref().map(|p| &**p) == prefix.as_deref()
1677 {
1678 return element.namespace().clone();
1679 }
1680
1681 let attr = Ref::filter_map(self.attrs(), |attrs| {
1686 attrs.iter().find(|attr| {
1687 if attr.namespace() != &ns!(xmlns) {
1688 return false;
1689 }
1690 match (attr.prefix(), prefix.as_ref()) {
1691 (Some(&namespace_prefix!("xmlns")), Some(prefix)) => {
1692 attr.local_name() == prefix
1693 },
1694 (None, None) => attr.local_name() == &local_name!("xmlns"),
1695 _ => false,
1696 }
1697 })
1698 })
1699 .ok();
1700
1701 if let Some(attr) = attr {
1702 return (**attr.value()).into();
1703 }
1704 }
1705
1706 ns!()
1707 }
1708
1709 pub(crate) fn name_attribute(&self) -> Option<Atom> {
1710 self.rare_data().as_ref()?.name_attribute.clone()
1711 }
1712
1713 pub(crate) fn style_attribute(
1714 &self,
1715 ) -> &DomRefCell<Option<Arc<Locked<PropertyDeclarationBlock>>>> {
1716 &self.style_attribute
1717 }
1718
1719 pub(crate) fn summarize(&self) -> Vec<AttrInfo> {
1720 self.attrs
1721 .borrow()
1722 .iter()
1723 .map(|attr| attr.summarize())
1724 .collect()
1725 }
1726
1727 pub(crate) fn is_void(&self) -> bool {
1728 if self.namespace != ns!(html) {
1729 return false;
1730 }
1731 match self.local_name {
1732 local_name!("area") |
1735 local_name!("base") |
1736 local_name!("basefont") |
1737 local_name!("bgsound") |
1738 local_name!("br") |
1739 local_name!("col") |
1740 local_name!("embed") |
1741 local_name!("frame") |
1742 local_name!("hr") |
1743 local_name!("img") |
1744 local_name!("input") |
1745 local_name!("keygen") |
1746 local_name!("link") |
1747 local_name!("meta") |
1748 local_name!("param") |
1749 local_name!("source") |
1750 local_name!("track") |
1751 local_name!("wbr") => true,
1752 _ => false,
1753 }
1754 }
1755
1756 pub(crate) fn root_element(&self) -> DomRoot<Element> {
1757 if self.node.is_in_a_document_tree() {
1758 self.upcast::<Node>()
1759 .owner_doc()
1760 .GetDocumentElement()
1761 .unwrap()
1762 } else {
1763 self.upcast::<Node>()
1764 .inclusive_ancestors(ShadowIncluding::No)
1765 .filter_map(DomRoot::downcast)
1766 .last()
1767 .expect("We know inclusive_ancestors will return `self` which is an element")
1768 }
1769 }
1770
1771 pub(crate) fn lookup_prefix(&self, namespace: Namespace) -> Option<DOMString> {
1773 for node in self
1774 .upcast::<Node>()
1775 .inclusive_ancestors(ShadowIncluding::No)
1776 {
1777 let element = node.downcast::<Element>()?;
1778 if *element.namespace() == namespace {
1780 if let Some(prefix) = element.GetPrefix() {
1781 return Some(prefix);
1782 }
1783 }
1784
1785 for attr in element.attrs.borrow().iter() {
1787 if attr.prefix() == Some(&namespace_prefix!("xmlns")) &&
1788 **attr.value() == *namespace
1789 {
1790 return Some(attr.LocalName());
1791 }
1792 }
1793 }
1794 None
1795 }
1796
1797 pub(crate) fn is_document_element(&self) -> bool {
1799 if let Some(document_element) = self.owner_document().GetDocumentElement() {
1800 *document_element == *self
1801 } else {
1802 false
1803 }
1804 }
1805
1806 pub(crate) fn is_active_element(&self) -> bool {
1808 if let Some(active_element) = self.owner_document().GetActiveElement() {
1809 *active_element == *self
1810 } else {
1811 false
1812 }
1813 }
1814
1815 pub(crate) fn is_editing_host(&self) -> bool {
1816 self.downcast::<HTMLElement>()
1817 .is_some_and(|element| element.IsContentEditable())
1818 }
1819
1820 pub(crate) fn is_actually_disabled(&self) -> bool {
1821 let node = self.upcast::<Node>();
1822 match node.type_id() {
1823 NodeTypeId::Element(ElementTypeId::HTMLElement(
1824 HTMLElementTypeId::HTMLButtonElement,
1825 )) |
1826 NodeTypeId::Element(ElementTypeId::HTMLElement(
1827 HTMLElementTypeId::HTMLInputElement,
1828 )) |
1829 NodeTypeId::Element(ElementTypeId::HTMLElement(
1830 HTMLElementTypeId::HTMLSelectElement,
1831 )) |
1832 NodeTypeId::Element(ElementTypeId::HTMLElement(
1833 HTMLElementTypeId::HTMLTextAreaElement,
1834 )) |
1835 NodeTypeId::Element(ElementTypeId::HTMLElement(
1836 HTMLElementTypeId::HTMLOptionElement,
1837 )) => self.disabled_state(),
1838 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
1839 self.downcast::<HTMLElement>()
1840 .unwrap()
1841 .is_form_associated_custom_element() &&
1842 self.disabled_state()
1843 },
1844 _ => false,
1849 }
1850 }
1851
1852 #[expect(unsafe_code)]
1853 #[allow(clippy::too_many_arguments)]
1854 pub(crate) fn push_new_attribute(
1855 &self,
1856 local_name: LocalName,
1857 value: AttrValue,
1858 name: LocalName,
1859 namespace: Namespace,
1860 prefix: Option<Prefix>,
1861 reason: AttributeMutationReason,
1862 can_gc: CanGc,
1863 ) {
1864 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
1866 let cx = &mut cx;
1867 let attr = Attr::new(
1868 cx,
1869 &self.node.owner_doc(),
1870 local_name,
1871 value,
1872 name,
1873 namespace,
1874 prefix,
1875 Some(self),
1876 );
1877 self.push_attribute(&attr, reason, can_gc);
1878 }
1879
1880 #[expect(unsafe_code)]
1882 fn handle_attribute_changes(
1883 &self,
1884 attr: &Attr,
1885 old_value: Option<&AttrValue>,
1886 new_value: Option<DOMString>,
1887 reason: AttributeMutationReason,
1888 _can_gc: CanGc,
1889 ) {
1890 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
1892 let cx = &mut cx;
1893
1894 let old_value_string = old_value.map(|old_value| DOMString::from(&**old_value));
1895 let name = attr.local_name().clone();
1898 let namespace = attr.namespace().clone();
1899 let mutation = LazyCell::new(|| Mutation::Attribute {
1900 name: name.clone(),
1901 namespace: namespace.clone(),
1902 old_value: old_value_string.clone(),
1903 });
1904 MutationObserver::queue_a_mutation_record(&self.node, mutation);
1905
1906 let has_new_value = new_value.is_some();
1908
1909 if self.is_custom() {
1912 let reaction = CallbackReaction::AttributeChanged(
1913 attr.local_name().clone(),
1914 old_value_string,
1915 new_value,
1916 attr.namespace().clone(),
1917 );
1918 ScriptThread::enqueue_callback_reaction(self, reaction, None);
1919 }
1920
1921 if is_relevant_attribute(attr.namespace(), attr.local_name()) {
1923 let attribute_mutation = if has_new_value {
1924 AttributeMutation::Set(old_value, reason)
1925 } else {
1926 AttributeMutation::Removed
1927 };
1928 vtable_for(self.upcast()).attribute_mutated(cx, attr, attribute_mutation);
1929 }
1930 }
1931
1932 pub(crate) fn change_attribute(&self, attr: &Attr, mut value: AttrValue, can_gc: CanGc) {
1934 let old_value = &attr.value().clone();
1938 self.will_mutate_attr(attr);
1940 attr.swap_value(&mut value);
1941 let new_value = DOMString::from(&**attr.value());
1945 self.handle_attribute_changes(
1946 attr,
1947 Some(old_value),
1948 Some(new_value),
1949 AttributeMutationReason::Directly,
1950 can_gc,
1951 );
1952 }
1953
1954 pub(crate) fn push_attribute(
1956 &self,
1957 attr: &Attr,
1958 reason: AttributeMutationReason,
1959 can_gc: CanGc,
1960 ) {
1961 assert!(attr.GetOwnerElement().as_deref() == Some(self));
1965 assert!(attr.upcast::<Node>().owner_doc() == self.node.owner_doc());
1969 self.will_mutate_attr(attr);
1971 self.attrs.borrow_mut().push(Dom::from_ref(attr));
1972 let new_value = DOMString::from(&**attr.value());
1976 self.handle_attribute_changes(attr, None, Some(new_value), reason, can_gc);
1977 }
1978
1979 pub(crate) fn get_attribute_with_namespace(
1985 &self,
1986 namespace: &Namespace,
1987 local_name: &LocalName,
1988 ) -> Option<DomRoot<Attr>> {
1989 self.attrs
1990 .borrow()
1991 .iter()
1992 .find(|attr| attr.local_name() == local_name && attr.namespace() == namespace)
1993 .map(|js| DomRoot::from_ref(&**js))
1994 }
1995
1996 pub(crate) fn get_attribute(&self, local_name: &LocalName) -> Option<DomRoot<Attr>> {
2001 debug_assert_eq!(
2002 *local_name,
2003 local_name.to_ascii_lowercase(),
2004 "All namespace-less attribute accesses should use a lowercase ASCII name"
2005 );
2006 self.get_attribute_with_namespace(&ns!(), local_name)
2007 }
2008
2009 pub(crate) fn get_attribute_by_name(&self, name: DOMString) -> Option<DomRoot<Attr>> {
2011 let name = &self.parsed_name(name);
2012 let maybe_attribute = self
2013 .attrs
2014 .borrow()
2015 .iter()
2016 .find(|a| a.name() == name)
2017 .map(|js| DomRoot::from_ref(&**js));
2018 fn id_and_name_must_be_atoms(name: &LocalName, maybe_attr: &Option<DomRoot<Attr>>) -> bool {
2019 if *name == local_name!("id") || *name == local_name!("name") {
2020 match maybe_attr {
2021 None => true,
2022 Some(attr) => matches!(*attr.value(), AttrValue::Atom(_)),
2023 }
2024 } else {
2025 true
2026 }
2027 }
2028 debug_assert!(id_and_name_must_be_atoms(name, &maybe_attribute));
2029 maybe_attribute
2030 }
2031
2032 pub(crate) fn set_attribute_from_parser(
2033 &self,
2034 qname: QualName,
2035 value: DOMString,
2036 prefix: Option<Prefix>,
2037 can_gc: CanGc,
2038 ) {
2039 if self
2041 .attrs
2042 .borrow()
2043 .iter()
2044 .any(|a| *a.local_name() == qname.local && *a.namespace() == qname.ns)
2045 {
2046 return;
2047 }
2048
2049 let name = match prefix {
2050 None => qname.local.clone(),
2051 Some(ref prefix) => {
2052 let name = format!("{}:{}", &**prefix, &*qname.local);
2053 LocalName::from(name)
2054 },
2055 };
2056 let value = self.parse_attribute(&qname.ns, &qname.local, value);
2057 self.push_new_attribute(
2058 qname.local,
2059 value,
2060 name,
2061 qname.ns,
2062 prefix,
2063 AttributeMutationReason::ByParser,
2064 can_gc,
2065 );
2066 }
2067
2068 pub(crate) fn set_attribute(&self, name: &LocalName, value: AttrValue, can_gc: CanGc) {
2069 debug_assert_eq!(
2070 *name,
2071 name.to_ascii_lowercase(),
2072 "All attribute accesses should use a lowercase ASCII name"
2073 );
2074 debug_assert!(!name.contains(':'));
2075
2076 self.set_first_matching_attribute(
2077 name.clone(),
2078 value,
2079 name.clone(),
2080 ns!(),
2081 None,
2082 |attr| attr.local_name() == name,
2083 can_gc,
2084 );
2085 }
2086
2087 pub(crate) fn set_attribute_with_namespace(
2088 &self,
2089 cx: &mut js::context::JSContext,
2090 local_name: LocalName,
2091 value: AttrValue,
2092 name: LocalName,
2093 namespace: Namespace,
2094 prefix: Option<Prefix>,
2095 ) {
2096 self.set_first_matching_attribute(
2097 local_name.clone(),
2098 value,
2099 name,
2100 namespace.clone(),
2101 prefix,
2102 |attr| *attr.local_name() == local_name && *attr.namespace() == namespace,
2103 CanGc::from_cx(cx),
2104 );
2105 }
2106
2107 #[allow(clippy::too_many_arguments)]
2109 fn set_first_matching_attribute<F>(
2110 &self,
2111 local_name: LocalName,
2112 value: AttrValue,
2113 name: LocalName,
2114 namespace: Namespace,
2115 prefix: Option<Prefix>,
2116 find: F,
2117 can_gc: CanGc,
2118 ) where
2119 F: Fn(&Attr) -> bool,
2120 {
2121 let attr = self
2123 .attrs
2124 .borrow()
2125 .iter()
2126 .find(|attr| find(attr))
2127 .map(|js| DomRoot::from_ref(&**js));
2128 if let Some(attr) = attr {
2129 self.will_mutate_attr(&attr);
2131 self.change_attribute(&attr, value, can_gc);
2132 } else {
2133 self.push_new_attribute(
2138 local_name,
2139 value,
2140 name,
2141 namespace,
2142 prefix,
2143 AttributeMutationReason::Directly,
2144 can_gc,
2145 );
2146 };
2147 }
2148
2149 pub(crate) fn parse_attribute(
2150 &self,
2151 namespace: &Namespace,
2152 local_name: &LocalName,
2153 value: DOMString,
2154 ) -> AttrValue {
2155 if is_relevant_attribute(namespace, local_name) {
2156 vtable_for(self.upcast()).parse_plain_attribute(local_name, value)
2157 } else {
2158 AttrValue::String(value.into())
2159 }
2160 }
2161
2162 pub(crate) fn remove_attribute(
2163 &self,
2164 namespace: &Namespace,
2165 local_name: &LocalName,
2166 can_gc: CanGc,
2167 ) -> Option<DomRoot<Attr>> {
2168 self.remove_first_matching_attribute(
2169 |attr| attr.namespace() == namespace && attr.local_name() == local_name,
2170 can_gc,
2171 )
2172 }
2173
2174 pub(crate) fn remove_attribute_by_name(
2175 &self,
2176 name: &LocalName,
2177 can_gc: CanGc,
2178 ) -> Option<DomRoot<Attr>> {
2179 self.remove_first_matching_attribute(|attr| attr.name() == name, can_gc)
2180 }
2181
2182 fn remove_first_matching_attribute<F>(&self, find: F, can_gc: CanGc) -> Option<DomRoot<Attr>>
2184 where
2185 F: Fn(&Attr) -> bool,
2186 {
2187 let idx = self.attrs.borrow().iter().position(|attr| find(attr));
2188 idx.map(|idx| {
2189 let attr = DomRoot::from_ref(&*(*self.attrs.borrow())[idx]);
2190
2191 self.will_mutate_attr(&attr);
2193 self.attrs.borrow_mut().remove(idx);
2194 attr.set_owner(None);
2196 self.handle_attribute_changes(
2198 &attr,
2199 Some(&attr.value()),
2200 None,
2201 AttributeMutationReason::Directly,
2202 can_gc,
2203 );
2204
2205 attr
2206 })
2207 }
2208
2209 pub(crate) fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2210 self.get_attribute(&local_name!("class"))
2211 .is_some_and(|attr| {
2212 attr.value()
2213 .as_tokens()
2214 .iter()
2215 .any(|atom| case_sensitivity.eq_atom(name, atom))
2216 })
2217 }
2218
2219 pub(crate) fn is_part(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2220 self.get_attribute(&LocalName::from("part"))
2221 .is_some_and(|attr| {
2222 attr.value()
2223 .as_tokens()
2224 .iter()
2225 .any(|atom| case_sensitivity.eq_atom(name, atom))
2226 })
2227 }
2228
2229 pub(crate) fn has_attribute(&self, local_name: &LocalName) -> bool {
2230 debug_assert_eq!(
2231 *local_name,
2232 local_name.to_ascii_lowercase(),
2233 "All attribute accesses should use a lowercase ASCII name"
2234 );
2235 debug_assert!(!local_name.contains(':'));
2236 self.attrs
2237 .borrow()
2238 .iter()
2239 .any(|attr| attr.local_name() == local_name && attr.namespace() == &ns!())
2240 }
2241
2242 pub(crate) fn will_mutate_attr(&self, attr: &Attr) {
2243 let node = self.upcast::<Node>();
2244 node.owner_doc().element_attr_will_change(self, attr);
2245 }
2246
2247 fn update_style_attribute(&self, attr: &Attr, mutation: AttributeMutation) {
2249 let doc = self.upcast::<Node>().owner_doc();
2250 *self.style_attribute.borrow_mut() = match mutation {
2252 AttributeMutation::Set(..) => {
2253 let is_declaration = matches!(*attr.value(), AttrValue::Declaration(..));
2259
2260 let block = if is_declaration {
2261 let mut value = AttrValue::String(String::new());
2262 attr.swap_value(&mut value);
2263 let (serialization, block) = match value {
2264 AttrValue::Declaration(s, b) => (s, b),
2265 _ => unreachable!(),
2266 };
2267 let mut value = AttrValue::String(serialization);
2268 attr.swap_value(&mut value);
2269 block
2270 } else {
2271 let win = self.owner_window();
2272 let source = &**attr.value();
2273 let global = &self.owner_global();
2274 if global
2279 .get_csp_list()
2280 .should_elements_inline_type_behavior_be_blocked(
2281 global,
2282 self,
2283 InlineCheckType::StyleAttribute,
2284 source,
2285 doc.get_current_parser_line(),
2286 )
2287 {
2288 return;
2289 }
2290 Arc::new(doc.style_shared_lock().wrap(parse_style_attribute(
2291 source,
2292 &UrlExtraData(doc.base_url().get_arc()),
2293 Some(win.css_error_reporter()),
2294 doc.quirks_mode(),
2295 CssRuleType::Style,
2296 )))
2297 };
2298
2299 Some(block)
2300 },
2301 AttributeMutation::Removed => None,
2302 };
2303 }
2304
2305 fn set_attribute_node(
2309 &self,
2310 cx: &mut js::context::JSContext,
2311 attr: &Attr,
2312 ) -> Fallible<Option<DomRoot<Attr>>> {
2313 let verified_value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
2317 cx,
2318 self.namespace(),
2319 self.local_name(),
2320 attr.local_name(),
2321 Some(attr.namespace()),
2322 TrustedTypeOrString::String(attr.Value()),
2323 &self.owner_global(),
2324 )?;
2325
2326 if let Some(owner) = attr.GetOwnerElement() {
2329 if &*owner != self {
2330 return Err(Error::InUseAttribute(None));
2331 }
2332 }
2333
2334 let vtable = vtable_for(self.upcast());
2335
2336 attr.swap_value(
2342 &mut vtable.parse_plain_attribute(attr.local_name(), verified_value.clone()),
2343 );
2344
2345 let position = self.attrs.borrow().iter().position(|old_attr| {
2347 attr.namespace() == old_attr.namespace() && attr.local_name() == old_attr.local_name()
2348 });
2349
2350 let old_attr = if let Some(position) = position {
2351 let old_attr = DomRoot::from_ref(&*self.attrs.borrow()[position]);
2352
2353 if &*old_attr == attr {
2355 return Ok(Some(DomRoot::from_ref(attr)));
2356 }
2357
2358 self.will_mutate_attr(attr);
2368 self.attrs.borrow_mut()[position] = Dom::from_ref(attr);
2369 attr.set_owner(Some(self));
2371 attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2373 old_attr.set_owner(None);
2375 self.handle_attribute_changes(
2377 attr,
2378 Some(&old_attr.value()),
2379 Some(verified_value),
2380 AttributeMutationReason::Directly,
2381 CanGc::from_cx(cx),
2382 );
2383
2384 Some(old_attr)
2385 } else {
2386 attr.set_owner(Some(self));
2388 attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2389 self.push_attribute(attr, AttributeMutationReason::Directly, CanGc::from_cx(cx));
2390
2391 None
2392 };
2393
2394 Ok(old_attr)
2396 }
2397
2398 pub(crate) fn update_nonce_internal_slot(&self, nonce: String) {
2400 self.ensure_rare_data().cryptographic_nonce = nonce;
2401 }
2402
2403 pub(crate) fn nonce_value(&self) -> String {
2405 match self.rare_data().as_ref() {
2406 None => String::new(),
2407 Some(rare_data) => rare_data.cryptographic_nonce.clone(),
2408 }
2409 }
2410
2411 pub(crate) fn update_nonce_post_connection(&self) {
2413 if !self.upcast::<Node>().is_connected_with_browsing_context() {
2416 return;
2417 }
2418 let global = self.owner_global();
2419 let csp_list = match global.get_csp_list() {
2421 None => return,
2422 Some(csp_list) => csp_list,
2423 };
2424 if !csp_list.contains_a_header_delivered_content_security_policy() ||
2427 self.get_string_attribute(&local_name!("nonce")).is_empty()
2428 {
2429 return;
2430 }
2431 let nonce = self.nonce_value();
2433 self.set_string_attribute(&local_name!("nonce"), "".into(), CanGc::note());
2435 self.update_nonce_internal_slot(nonce);
2437 }
2438
2439 pub(crate) fn is_nonceable(&self) -> bool {
2441 if !self.has_attribute(&local_name!("nonce")) {
2443 return false;
2444 }
2445 if self.downcast::<HTMLScriptElement>().is_some() {
2447 for attr in self.attrs().iter() {
2448 let attr_name = attr.name().to_ascii_lowercase();
2451 if attr_name.contains("<script") || attr_name.contains("<style") {
2452 return false;
2453 }
2454 let attr_value = attr.value().to_ascii_lowercase();
2457 if attr_value.contains("<script") || attr_value.contains("<style") {
2458 return false;
2459 }
2460 }
2461 }
2462 if self
2464 .rare_data()
2465 .as_ref()
2466 .is_some_and(|d| d.had_duplicate_attributes)
2467 {
2468 return false;
2469 }
2470 true
2472 }
2473
2474 pub(crate) fn insert_adjacent(
2476 &self,
2477 cx: &mut JSContext,
2478 where_: AdjacentPosition,
2479 node: &Node,
2480 ) -> Fallible<Option<DomRoot<Node>>> {
2481 let self_node = self.upcast::<Node>();
2482 match where_ {
2483 AdjacentPosition::BeforeBegin => {
2484 if let Some(parent) = self_node.GetParentNode() {
2485 Node::pre_insert(cx, node, &parent, Some(self_node)).map(Some)
2486 } else {
2487 Ok(None)
2488 }
2489 },
2490 AdjacentPosition::AfterBegin => {
2491 Node::pre_insert(cx, node, self_node, self_node.GetFirstChild().as_deref())
2492 .map(Some)
2493 },
2494 AdjacentPosition::BeforeEnd => Node::pre_insert(cx, node, self_node, None).map(Some),
2495 AdjacentPosition::AfterEnd => {
2496 if let Some(parent) = self_node.GetParentNode() {
2497 Node::pre_insert(cx, node, &parent, self_node.GetNextSibling().as_deref())
2498 .map(Some)
2499 } else {
2500 Ok(None)
2501 }
2502 },
2503 }
2504 }
2505
2506 pub(crate) fn scroll(&self, x: f64, y: f64, behavior: ScrollBehavior) {
2511 let x = if x.is_finite() { x } else { 0.0 } as f32;
2513 let y = if y.is_finite() { y } else { 0.0 } as f32;
2514
2515 let node = self.upcast::<Node>();
2516
2517 let doc = node.owner_doc();
2519
2520 if !doc.is_fully_active() {
2522 return;
2523 }
2524
2525 let win = match doc.GetDefaultView() {
2527 None => return,
2528 Some(win) => win,
2529 };
2530
2531 if *self.root_element() == *self {
2533 if doc.quirks_mode() != QuirksMode::Quirks {
2534 win.scroll(x, y, behavior);
2535 }
2536
2537 return;
2538 }
2539
2540 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
2542 doc.quirks_mode() == QuirksMode::Quirks &&
2543 !self.is_potentially_scrollable_body()
2544 {
2545 win.scroll(x, y, behavior);
2546 return;
2547 }
2548
2549 if !self.has_scrolling_box() {
2551 return;
2552 }
2553
2554 win.scroll_an_element(self, x, y, behavior);
2556 }
2557
2558 pub(crate) fn parse_fragment(
2560 &self,
2561 markup: DOMString,
2562 cx: &mut js::context::JSContext,
2563 ) -> Fallible<DomRoot<DocumentFragment>> {
2564 let new_children = ServoParser::parse_html_fragment(self, markup, false, cx);
2567 let context_document = {
2570 if let Some(template) = self.downcast::<HTMLTemplateElement>() {
2571 template.Content(cx).upcast::<Node>().owner_doc()
2572 } else {
2573 self.owner_document()
2574 }
2575 };
2576 let fragment = DocumentFragment::new(cx, &context_document);
2577 for child in new_children {
2579 fragment.upcast::<Node>().AppendChild(cx, &child).unwrap();
2580 }
2581 Ok(fragment)
2583 }
2584
2585 pub(crate) fn fragment_parsing_context(
2588 cx: &mut JSContext,
2589 owner_doc: &Document,
2590 element: Option<&Self>,
2591 ) -> DomRoot<Self> {
2592 match element {
2594 Some(elem)
2595 if elem.local_name() != &local_name!("html") ||
2599 !elem.html_element_in_html_document() =>
2600 {
2601 DomRoot::from_ref(elem)
2602 },
2603 _ => Element::create(
2606 cx,
2607 QualName::new(None, ns!(html), local_name!("body")),
2608 None,
2609 owner_doc,
2610 ElementCreator::ScriptCreated,
2611 CustomElementCreationMode::Asynchronous,
2612 None
2613 ),
2614 }
2615 }
2616
2617 pub(crate) fn is_in_same_home_subtree<T>(&self, other: &T) -> bool
2619 where
2620 T: DerivedFrom<Element> + DomObject,
2621 {
2622 let other = other.upcast::<Element>();
2623 self.root_element() == other.root_element()
2624 }
2625
2626 pub(crate) fn get_id(&self) -> Option<Atom> {
2627 self.id_attribute.borrow().clone()
2628 }
2629
2630 pub(crate) fn get_name(&self) -> Option<Atom> {
2631 self.rare_data().as_ref()?.name_attribute.clone()
2632 }
2633
2634 pub(crate) fn get_element_internals(&self) -> Option<DomRoot<ElementInternals>> {
2635 self.rare_data()
2636 .as_ref()?
2637 .element_internals
2638 .as_ref()
2639 .map(|sr| DomRoot::from_ref(&**sr))
2640 }
2641
2642 pub(crate) fn ensure_element_internals(&self, can_gc: CanGc) -> DomRoot<ElementInternals> {
2643 let mut rare_data = self.ensure_rare_data();
2644 DomRoot::from_ref(rare_data.element_internals.get_or_insert_with(|| {
2645 let elem = self
2646 .downcast::<HTMLElement>()
2647 .expect("ensure_element_internals should only be called for an HTMLElement");
2648 Dom::from_ref(&*ElementInternals::new(elem, can_gc))
2649 }))
2650 }
2651
2652 pub(crate) fn outer_html(&self, cx: &mut js::context::JSContext) -> Fallible<DOMString> {
2653 match self.GetOuterHTML(cx)? {
2654 TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => Ok(str),
2655 TrustedHTMLOrNullIsEmptyString::TrustedHTML(_) => unreachable!(),
2656 }
2657 }
2658
2659 pub(crate) fn compute_source_position(&self, line_number: u32) -> SourcePosition {
2660 SourcePosition {
2661 source_file: self.owner_global().get_url().to_string(),
2662 line_number: line_number + 2,
2663 column_number: 0,
2664 }
2665 }
2666
2667 pub(crate) fn explicitly_set_tab_index(&self) -> Option<i32> {
2668 if self.has_attribute(&local_name!("tabindex")) {
2669 Some(self.get_int_attribute(&local_name!("tabindex"), 0))
2670 } else {
2671 None
2672 }
2673 }
2674
2675 pub(crate) fn tab_index(&self) -> i32 {
2677 if let Some(tab_index) = self.explicitly_set_tab_index() {
2683 return tab_index;
2684 }
2685
2686 if matches!(
2692 self.upcast::<Node>().type_id(),
2693 NodeTypeId::Element(ElementTypeId::HTMLElement(
2694 HTMLElementTypeId::HTMLAnchorElement |
2695 HTMLElementTypeId::HTMLAreaElement |
2696 HTMLElementTypeId::HTMLButtonElement |
2697 HTMLElementTypeId::HTMLFrameElement |
2698 HTMLElementTypeId::HTMLIFrameElement |
2699 HTMLElementTypeId::HTMLInputElement |
2700 HTMLElementTypeId::HTMLObjectElement |
2701 HTMLElementTypeId::HTMLSelectElement |
2702 HTMLElementTypeId::HTMLTextAreaElement
2703 ))
2704 ) {
2705 return 0;
2706 }
2707 if self
2708 .downcast::<HTMLElement>()
2709 .is_some_and(|html_element| html_element.is_a_summary_for_its_parent_details())
2710 {
2711 return 0;
2712 }
2713
2714 -1
2715 }
2716
2717 #[inline]
2718 fn insert_selector_flags(&self, flags: ElementSelectorFlags) {
2719 self.selector_flags
2720 .fetch_or(flags.bits(), Ordering::Relaxed);
2721 }
2722
2723 #[inline]
2724 fn get_selector_flags(&self) -> ElementSelectorFlags {
2725 ElementSelectorFlags::from_bits_retain(self.selector_flags.load(Ordering::Relaxed))
2726 }
2727}
2728
2729impl ElementMethods<crate::DomTypeHolder> for Element {
2730 fn GetNamespaceURI(&self) -> Option<DOMString> {
2732 Node::namespace_to_string(self.namespace.clone())
2733 }
2734
2735 fn LocalName(&self) -> DOMString {
2737 DOMString::from(&*self.local_name)
2739 }
2740
2741 fn GetPrefix(&self) -> Option<DOMString> {
2743 self.prefix.borrow().as_ref().map(|p| DOMString::from(&**p))
2744 }
2745
2746 fn TagName(&self) -> DOMString {
2748 let name = self.tag_name.or_init(|| {
2749 let qualified_name = match *self.prefix.borrow() {
2750 Some(ref prefix) => Cow::Owned(format!("{}:{}", &**prefix, &*self.local_name)),
2751 None => Cow::Borrowed(&*self.local_name),
2752 };
2753 if self.html_element_in_html_document() {
2754 LocalName::from(qualified_name.to_ascii_uppercase())
2755 } else {
2756 LocalName::from(qualified_name)
2757 }
2758 });
2759 DOMString::from(&*name)
2760 }
2761
2762 fn Id(&self) -> DOMString {
2766 self.get_string_attribute(&local_name!("id"))
2767 }
2768
2769 fn SetId(&self, id: DOMString, can_gc: CanGc) {
2771 self.set_atomic_attribute(&local_name!("id"), id, can_gc);
2772 }
2773
2774 fn ClassName(&self) -> DOMString {
2776 self.get_string_attribute(&local_name!("class"))
2777 }
2778
2779 fn SetClassName(&self, class: DOMString, can_gc: CanGc) {
2781 self.set_tokenlist_attribute(&local_name!("class"), class, can_gc);
2782 }
2783
2784 fn ClassList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
2786 self.class_list
2787 .or_init(|| DOMTokenList::new(self, &local_name!("class"), None, can_gc))
2788 }
2789
2790 make_getter!(Slot, "slot");
2792
2793 make_setter!(SetSlot, "slot");
2795
2796 fn Attributes(&self, can_gc: CanGc) -> DomRoot<NamedNodeMap> {
2798 self.attr_list
2799 .or_init(|| NamedNodeMap::new(&self.owner_window(), self, can_gc))
2800 }
2801
2802 fn HasAttributes(&self) -> bool {
2804 !self.attrs.borrow().is_empty()
2805 }
2806
2807 fn GetAttributeNames(&self) -> Vec<DOMString> {
2809 self.attrs.borrow().iter().map(|attr| attr.Name()).collect()
2810 }
2811
2812 fn GetAttribute(&self, name: DOMString) -> Option<DOMString> {
2814 self.GetAttributeNode(name).map(|s| s.Value())
2815 }
2816
2817 fn GetAttributeNS(
2819 &self,
2820 namespace: Option<DOMString>,
2821 local_name: DOMString,
2822 ) -> Option<DOMString> {
2823 self.GetAttributeNodeNS(namespace, local_name)
2824 .map(|attr| attr.Value())
2825 }
2826
2827 fn GetAttributeNode(&self, name: DOMString) -> Option<DomRoot<Attr>> {
2829 self.get_attribute_by_name(name)
2830 }
2831
2832 fn GetAttributeNodeNS(
2834 &self,
2835 namespace: Option<DOMString>,
2836 local_name: DOMString,
2837 ) -> Option<DomRoot<Attr>> {
2838 let namespace = &namespace_from_domstring(namespace);
2839 self.get_attribute_with_namespace(namespace, &LocalName::from(local_name))
2840 }
2841
2842 fn ToggleAttribute(
2844 &self,
2845 cx: &mut js::context::JSContext,
2846 name: DOMString,
2847 force: Option<bool>,
2848 ) -> Fallible<bool> {
2849 if !is_valid_attribute_local_name(&name.str()) {
2852 return Err(Error::InvalidCharacter(None));
2853 }
2854
2855 let attribute = self.GetAttribute(name.clone());
2857
2858 let name = self.parsed_name(name);
2860 match attribute {
2861 None => match force {
2863 None | Some(true) => {
2865 self.set_first_matching_attribute(
2866 name.clone(),
2867 AttrValue::String(String::new()),
2868 name.clone(),
2869 ns!(),
2870 None,
2871 |attr| *attr.name() == name,
2872 CanGc::from_cx(cx),
2873 );
2874 Ok(true)
2875 },
2876 Some(false) => Ok(false),
2878 },
2879 Some(_index) => match force {
2880 None | Some(false) => {
2882 self.remove_attribute_by_name(&name, CanGc::from_cx(cx));
2883 Ok(false)
2884 },
2885 Some(true) => Ok(true),
2887 },
2888 }
2889 }
2890
2891 fn SetAttribute(
2893 &self,
2894 cx: &mut js::context::JSContext,
2895 name: DOMString,
2896 value: TrustedTypeOrString,
2897 ) -> ErrorResult {
2898 if !is_valid_attribute_local_name(&name.str()) {
2901 return Err(Error::InvalidCharacter(None));
2902 }
2903
2904 let name = self.parsed_name(name);
2907
2908 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
2912 cx,
2913 self.namespace(),
2914 self.local_name(),
2915 &name,
2916 None,
2917 value,
2918 &self.owner_global(),
2919 )?;
2920
2921 let value = self.parse_attribute(&ns!(), &name, value);
2926 self.set_first_matching_attribute(
2927 name.clone(),
2928 value,
2929 name.clone(),
2930 ns!(),
2931 None,
2932 |attr| *attr.name() == name,
2933 CanGc::from_cx(cx),
2934 );
2935 Ok(())
2936 }
2937
2938 fn SetAttributeNS(
2940 &self,
2941 cx: &mut js::context::JSContext,
2942 namespace: Option<DOMString>,
2943 qualified_name: DOMString,
2944 value: TrustedTypeOrString,
2945 ) -> ErrorResult {
2946 let (namespace, prefix, local_name) =
2948 domname::validate_and_extract(namespace, &qualified_name, domname::Context::Element)?;
2949 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
2952 cx,
2953 self.namespace(),
2954 self.local_name(),
2955 &local_name,
2956 Some(&namespace),
2957 value,
2958 &self.owner_global(),
2959 )?;
2960 let value = self.parse_attribute(&namespace, &local_name, value);
2962 self.set_attribute_with_namespace(
2963 cx,
2964 local_name,
2965 value,
2966 LocalName::from(qualified_name),
2967 namespace,
2968 prefix,
2969 );
2970 Ok(())
2971 }
2972
2973 fn SetAttributeNode(
2975 &self,
2976 cx: &mut js::context::JSContext,
2977 attr: &Attr,
2978 ) -> Fallible<Option<DomRoot<Attr>>> {
2979 self.set_attribute_node(cx, attr)
2980 }
2981
2982 fn SetAttributeNodeNS(
2984 &self,
2985 cx: &mut js::context::JSContext,
2986 attr: &Attr,
2987 ) -> Fallible<Option<DomRoot<Attr>>> {
2988 self.set_attribute_node(cx, attr)
2989 }
2990
2991 fn RemoveAttribute(&self, name: DOMString, can_gc: CanGc) {
2993 let name = self.parsed_name(name);
2994 self.remove_attribute_by_name(&name, can_gc);
2995 }
2996
2997 fn RemoveAttributeNS(
2999 &self,
3000 cx: &mut js::context::JSContext,
3001 namespace: Option<DOMString>,
3002 local_name: DOMString,
3003 ) {
3004 let namespace = namespace_from_domstring(namespace);
3005 let local_name = LocalName::from(local_name);
3006 self.remove_attribute(&namespace, &local_name, CanGc::from_cx(cx));
3007 }
3008
3009 fn RemoveAttributeNode(
3011 &self,
3012 cx: &mut js::context::JSContext,
3013 attr: &Attr,
3014 ) -> Fallible<DomRoot<Attr>> {
3015 self.remove_first_matching_attribute(|a| a == attr, CanGc::from_cx(cx))
3016 .ok_or(Error::NotFound(None))
3017 }
3018
3019 fn HasAttribute(&self, name: DOMString) -> bool {
3021 self.GetAttribute(name).is_some()
3022 }
3023
3024 fn HasAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> bool {
3026 self.GetAttributeNS(namespace, local_name).is_some()
3027 }
3028
3029 fn GetElementsByTagName(&self, localname: DOMString, can_gc: CanGc) -> DomRoot<HTMLCollection> {
3031 let window = self.owner_window();
3032 HTMLCollection::by_qualified_name(
3033 &window,
3034 self.upcast(),
3035 LocalName::from(localname),
3036 can_gc,
3037 )
3038 }
3039
3040 fn GetElementsByTagNameNS(
3042 &self,
3043 maybe_ns: Option<DOMString>,
3044 localname: DOMString,
3045 can_gc: CanGc,
3046 ) -> DomRoot<HTMLCollection> {
3047 let window = self.owner_window();
3048 HTMLCollection::by_tag_name_ns(&window, self.upcast(), localname, maybe_ns, can_gc)
3049 }
3050
3051 fn GetElementsByClassName(&self, classes: DOMString, can_gc: CanGc) -> DomRoot<HTMLCollection> {
3053 let window = self.owner_window();
3054 HTMLCollection::by_class_name(&window, self.upcast(), classes, can_gc)
3055 }
3056
3057 fn GetClientRects(&self, can_gc: CanGc) -> DomRoot<DOMRectList> {
3059 let win = self.owner_window();
3060 let raw_rects = self.upcast::<Node>().border_boxes();
3061 let rects: Vec<DomRoot<DOMRect>> = raw_rects
3062 .map(|rect| {
3063 DOMRect::new(
3064 win.upcast(),
3065 rect.origin.x.to_f64_px(),
3066 rect.origin.y.to_f64_px(),
3067 rect.size.width.to_f64_px(),
3068 rect.size.height.to_f64_px(),
3069 can_gc,
3070 )
3071 })
3072 .collect();
3073 DOMRectList::new(&win, rects, can_gc)
3074 }
3075
3076 fn GetBoundingClientRect(&self, can_gc: CanGc) -> DomRoot<DOMRect> {
3078 let win = self.owner_window();
3079 let rect = self.upcast::<Node>().border_box().unwrap_or_default();
3080 debug_assert!(rect.size.width.to_f64_px() >= 0.0 && rect.size.height.to_f64_px() >= 0.0);
3081 DOMRect::new(
3082 win.upcast(),
3083 rect.origin.x.to_f64_px(),
3084 rect.origin.y.to_f64_px(),
3085 rect.size.width.to_f64_px(),
3086 rect.size.height.to_f64_px(),
3087 can_gc,
3088 )
3089 }
3090
3091 fn Scroll(&self, options: &ScrollToOptions) {
3093 let left = options.left.unwrap_or(self.ScrollLeft());
3095 let top = options.top.unwrap_or(self.ScrollTop());
3096 self.scroll(left, top, options.parent.behavior);
3097 }
3098
3099 fn Scroll_(&self, x: f64, y: f64) {
3101 self.scroll(x, y, ScrollBehavior::Auto);
3102 }
3103
3104 fn ScrollTo(&self, options: &ScrollToOptions) {
3106 self.Scroll(options);
3107 }
3108
3109 fn ScrollTo_(&self, x: f64, y: f64) {
3111 self.Scroll_(x, y);
3112 }
3113
3114 fn ScrollBy(&self, options: &ScrollToOptions) {
3116 let delta_left = options.left.unwrap_or(0.0f64);
3118 let delta_top = options.top.unwrap_or(0.0f64);
3119 let left = self.ScrollLeft();
3120 let top = self.ScrollTop();
3121 self.scroll(left + delta_left, top + delta_top, options.parent.behavior);
3122 }
3123
3124 fn ScrollBy_(&self, x: f64, y: f64) {
3126 let left = self.ScrollLeft();
3127 let top = self.ScrollTop();
3128 self.scroll(left + x, top + y, ScrollBehavior::Auto);
3129 }
3130
3131 fn ScrollTop(&self) -> f64 {
3133 let node = self.upcast::<Node>();
3134
3135 let doc = node.owner_doc();
3137
3138 if !doc.is_fully_active() {
3140 return 0.0;
3141 }
3142
3143 let win = match doc.GetDefaultView() {
3145 None => return 0.0,
3146 Some(win) => win,
3147 };
3148
3149 if self.is_document_element() {
3151 if doc.quirks_mode() == QuirksMode::Quirks {
3152 return 0.0;
3153 }
3154
3155 return win.ScrollY() as f64;
3157 }
3158
3159 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3161 doc.quirks_mode() == QuirksMode::Quirks &&
3162 !self.is_potentially_scrollable_body()
3163 {
3164 return win.ScrollY() as f64;
3165 }
3166
3167 if !self.has_css_layout_box() {
3169 return 0.0;
3170 }
3171
3172 let point = win.scroll_offset_query(node);
3174 point.y.abs() as f64
3175 }
3176
3177 fn SetScrollTop(&self, y_: f64) {
3180 let behavior = ScrollBehavior::Auto;
3181
3182 let y = if y_.is_finite() { y_ } else { 0.0 } as f32;
3184
3185 let node = self.upcast::<Node>();
3186
3187 let doc = node.owner_doc();
3189
3190 if !doc.is_fully_active() {
3192 return;
3193 }
3194
3195 let win = match doc.GetDefaultView() {
3197 None => return,
3198 Some(win) => win,
3199 };
3200
3201 if self.is_document_element() {
3203 if doc.quirks_mode() != QuirksMode::Quirks {
3204 win.scroll(win.ScrollX() as f32, y, behavior);
3205 }
3206
3207 return;
3208 }
3209
3210 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3212 doc.quirks_mode() == QuirksMode::Quirks &&
3213 !self.is_potentially_scrollable_body()
3214 {
3215 win.scroll(win.ScrollX() as f32, y, behavior);
3216 return;
3217 }
3218
3219 if !self.has_scrolling_box() {
3221 return;
3222 }
3223
3224 win.scroll_an_element(self, self.ScrollLeft() as f32, y, behavior);
3226 }
3227
3228 fn ScrollLeft(&self) -> f64 {
3230 let node = self.upcast::<Node>();
3231
3232 let doc = node.owner_doc();
3234
3235 if !doc.is_fully_active() {
3237 return 0.0;
3238 }
3239
3240 let win = match doc.GetDefaultView() {
3242 None => return 0.0,
3243 Some(win) => win,
3244 };
3245
3246 if self.is_document_element() {
3248 if doc.quirks_mode() != QuirksMode::Quirks {
3249 return win.ScrollX() as f64;
3251 }
3252
3253 return 0.0;
3254 }
3255
3256 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3258 doc.quirks_mode() == QuirksMode::Quirks &&
3259 !self.is_potentially_scrollable_body()
3260 {
3261 return win.ScrollX() as f64;
3262 }
3263
3264 if !self.has_css_layout_box() {
3266 return 0.0;
3267 }
3268
3269 let point = win.scroll_offset_query(node);
3271 point.x.abs() as f64
3272 }
3273
3274 fn SetScrollLeft(&self, x: f64) {
3276 let behavior = ScrollBehavior::Auto;
3277
3278 let x = if x.is_finite() { x } else { 0.0 } as f32;
3280
3281 let node = self.upcast::<Node>();
3282
3283 let doc = node.owner_doc();
3285
3286 if !doc.is_fully_active() {
3288 return;
3289 }
3290
3291 let win = match doc.GetDefaultView() {
3293 None => return,
3294 Some(win) => win,
3295 };
3296
3297 if self.is_document_element() {
3299 if doc.quirks_mode() == QuirksMode::Quirks {
3300 return;
3301 }
3302
3303 win.scroll(x, win.ScrollY() as f32, behavior);
3304 return;
3305 }
3306
3307 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3309 doc.quirks_mode() == QuirksMode::Quirks &&
3310 !self.is_potentially_scrollable_body()
3311 {
3312 win.scroll(x, win.ScrollY() as f32, behavior);
3313 return;
3314 }
3315
3316 if !self.has_scrolling_box() {
3318 return;
3319 }
3320
3321 win.scroll_an_element(self, x, self.ScrollTop() as f32, behavior);
3323 }
3324
3325 fn ScrollIntoView(&self, arg: BooleanOrScrollIntoViewOptions) {
3327 let (behavior, block, inline, container) = match arg {
3328 BooleanOrScrollIntoViewOptions::Boolean(true) => (
3330 ScrollBehavior::Auto, ScrollLogicalPosition::Start, ScrollLogicalPosition::Nearest, None, ),
3335 BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(options) => (
3338 options.parent.behavior,
3339 options.block,
3340 options.inline,
3341 if options.container == ScrollIntoViewContainer::Nearest {
3344 Some(self)
3345 } else {
3346 None
3347 },
3348 ),
3349 BooleanOrScrollIntoViewOptions::Boolean(false) => (
3351 ScrollBehavior::Auto,
3352 ScrollLogicalPosition::End,
3353 ScrollLogicalPosition::Nearest,
3354 None,
3355 ),
3356 };
3357
3358 if !self.has_css_layout_box() {
3361 return;
3362 }
3363
3364 self.scroll_into_view_with_options(
3366 behavior,
3367 ScrollAxisState::new_always_scroll_position(block),
3368 ScrollAxisState::new_always_scroll_position(inline),
3369 container,
3370 None,
3371 );
3372
3373 }
3376
3377 fn ScrollWidth(&self) -> i32 {
3379 self.upcast::<Node>().scroll_area().size.width
3380 }
3381
3382 fn ScrollHeight(&self) -> i32 {
3384 self.upcast::<Node>().scroll_area().size.height
3385 }
3386
3387 fn ClientTop(&self) -> i32 {
3389 self.client_rect().origin.y
3390 }
3391
3392 fn ClientLeft(&self) -> i32 {
3394 self.client_rect().origin.x
3395 }
3396
3397 fn ClientWidth(&self) -> i32 {
3399 self.client_rect().size.width
3400 }
3401
3402 fn ClientHeight(&self) -> i32 {
3404 self.client_rect().size.height
3405 }
3406
3407 fn CurrentCSSZoom(&self) -> Finite<f64> {
3409 let window = self.owner_window();
3410 Finite::wrap(window.current_css_zoom_query(self.upcast::<Node>()) as f64)
3411 }
3412
3413 fn SetHTMLUnsafe(
3415 &self,
3416 cx: &mut js::context::JSContext,
3417 html: TrustedHTMLOrString,
3418 ) -> ErrorResult {
3419 let html = TrustedHTML::get_trusted_type_compliant_string(
3423 cx,
3424 &self.owner_global(),
3425 html,
3426 "Element setHTMLUnsafe",
3427 )?;
3428 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3430 DomRoot::upcast(template.Content(cx))
3431 } else {
3432 DomRoot::from_ref(self.upcast())
3433 };
3434
3435 Node::unsafely_set_html(&target, self, html, cx);
3437 Ok(())
3438 }
3439
3440 fn GetHTML(&self, cx: &mut js::context::JSContext, options: &GetHTMLOptions) -> DOMString {
3442 self.upcast::<Node>().html_serialize(
3445 cx,
3446 TraversalScope::ChildrenOnly(None),
3447 options.serializableShadowRoots,
3448 options.shadowRoots.clone(),
3449 )
3450 }
3451
3452 fn GetInnerHTML(
3454 &self,
3455 cx: &mut js::context::JSContext,
3456 ) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3457 let qname = QualName::new(
3458 self.prefix().clone(),
3459 self.namespace().clone(),
3460 self.local_name().clone(),
3461 );
3462
3463 let result = if self.owner_document().is_html_document() {
3466 self.upcast::<Node>()
3467 .html_serialize(cx, ChildrenOnly(Some(qname)), false, vec![])
3468 } else {
3469 self.upcast::<Node>()
3470 .xml_serialize(XmlChildrenOnly(Some(qname)))?
3471 };
3472
3473 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3474 }
3475
3476 fn SetInnerHTML(
3478 &self,
3479 cx: &mut js::context::JSContext,
3480 value: TrustedHTMLOrNullIsEmptyString,
3481 ) -> ErrorResult {
3482 let value = TrustedHTML::get_trusted_type_compliant_string(
3486 cx,
3487 &self.owner_global(),
3488 value.convert(),
3489 "Element innerHTML",
3490 )?;
3491 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3493 DomRoot::upcast(template.Content(cx))
3496 } else {
3497 DomRoot::from_ref(self.upcast())
3499 };
3500
3501 if !self.node.has_weird_parser_insertion_mode() &&
3504 value.len() < 100 &&
3505 !value
3506 .as_bytes()
3507 .iter()
3508 .any(|c| matches!(*c, b'&' | b'\0' | b'<' | b'\r'))
3509 {
3510 return Node::SetTextContent(&target, cx, Some(value));
3511 }
3512
3513 let frag = self.parse_fragment(value, cx)?;
3516
3517 Node::replace_all(cx, Some(frag.upcast()), &target);
3519 Ok(())
3520 }
3521
3522 fn GetOuterHTML(
3524 &self,
3525 cx: &mut js::context::JSContext,
3526 ) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3527 let result = if self.owner_document().is_html_document() {
3530 self.upcast::<Node>()
3531 .html_serialize(cx, IncludeNode, false, vec![])
3532 } else {
3533 self.upcast::<Node>().xml_serialize(XmlIncludeNode)?
3534 };
3535
3536 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3537 }
3538
3539 fn SetOuterHTML(
3541 &self,
3542 cx: &mut js::context::JSContext,
3543 value: TrustedHTMLOrNullIsEmptyString,
3544 ) -> ErrorResult {
3545 let value = TrustedHTML::get_trusted_type_compliant_string(
3549 cx,
3550 &self.owner_global(),
3551 value.convert(),
3552 "Element outerHTML",
3553 )?;
3554 let context_document = self.owner_document();
3555 let context_node = self.upcast::<Node>();
3556 let context_parent = match context_node.GetParentNode() {
3558 None => {
3559 return Ok(());
3562 },
3563 Some(parent) => parent,
3564 };
3565
3566 let parent = match context_parent.type_id() {
3567 NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed(None)),
3569
3570 NodeTypeId::DocumentFragment(_) => {
3573 let body_elem = Element::create(
3574 cx,
3575 QualName::new(None, ns!(html), local_name!("body")),
3576 None,
3577 &context_document,
3578 ElementCreator::ScriptCreated,
3579 CustomElementCreationMode::Synchronous,
3580 None,
3581 );
3582 DomRoot::upcast(body_elem)
3583 },
3584 _ => context_node.GetParentElement().unwrap(),
3585 };
3586
3587 let frag = parent.parse_fragment(value, cx)?;
3590 context_parent.ReplaceChild(cx, frag.upcast(), context_node)?;
3592 Ok(())
3593 }
3594
3595 fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
3597 self.upcast::<Node>()
3598 .preceding_siblings()
3599 .find_map(DomRoot::downcast)
3600 }
3601
3602 fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
3604 self.upcast::<Node>()
3605 .following_siblings()
3606 .find_map(DomRoot::downcast)
3607 }
3608
3609 fn Children(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
3611 let window = self.owner_window();
3612 HTMLCollection::children(&window, self.upcast(), can_gc)
3613 }
3614
3615 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
3617 self.upcast::<Node>().child_elements().next()
3618 }
3619
3620 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
3622 self.upcast::<Node>()
3623 .rev_children()
3624 .find_map(DomRoot::downcast::<Element>)
3625 }
3626
3627 fn ChildElementCount(&self) -> u32 {
3629 self.upcast::<Node>().child_elements().count() as u32
3630 }
3631
3632 fn Prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3634 self.upcast::<Node>().prepend(cx, nodes)
3635 }
3636
3637 fn Append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3639 self.upcast::<Node>().append(cx, nodes)
3640 }
3641
3642 fn ReplaceChildren(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3644 self.upcast::<Node>().replace_children(cx, nodes)
3645 }
3646
3647 fn MoveBefore(&self, cx: &mut JSContext, node: &Node, child: Option<&Node>) -> ErrorResult {
3649 self.upcast::<Node>().move_before(cx, node, child)
3650 }
3651
3652 fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
3654 let root = self.upcast::<Node>();
3655 root.query_selector(selectors)
3656 }
3657
3658 fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>> {
3660 let root = self.upcast::<Node>();
3661 root.query_selector_all(selectors)
3662 }
3663
3664 fn Before(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3666 self.upcast::<Node>().before(cx, nodes)
3667 }
3668
3669 fn After(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3671 self.upcast::<Node>().after(cx, nodes)
3672 }
3673
3674 fn ReplaceWith(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3676 self.upcast::<Node>().replace_with(cx, nodes)
3677 }
3678
3679 fn Remove(&self, cx: &mut JSContext) {
3681 self.upcast::<Node>().remove_self(cx);
3682 }
3683
3684 fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
3686 let doc = self.owner_document();
3687 let url = doc.url();
3688 let selectors = match SelectorParser::parse_author_origin_no_namespace(
3689 &selectors.str(),
3690 &UrlExtraData(url.get_arc()),
3691 ) {
3692 Err(_) => return Err(Error::Syntax(None)),
3693 Ok(selectors) => selectors,
3694 };
3695
3696 let quirks_mode = doc.quirks_mode();
3697 let element = DomRoot::from_ref(self);
3698
3699 Ok(dom_apis::element_matches(
3700 &SelectorWrapper::Borrowed(&element),
3701 &selectors,
3702 quirks_mode,
3703 ))
3704 }
3705
3706 fn WebkitMatchesSelector(&self, selectors: DOMString) -> Fallible<bool> {
3708 self.Matches(selectors)
3709 }
3710
3711 fn Closest(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
3713 let doc = self.owner_document();
3714 let url = doc.url();
3715 let selectors = match SelectorParser::parse_author_origin_no_namespace(
3716 &selectors.str(),
3717 &UrlExtraData(url.get_arc()),
3718 ) {
3719 Err(_) => return Err(Error::Syntax(None)),
3720 Ok(selectors) => selectors,
3721 };
3722
3723 let quirks_mode = doc.quirks_mode();
3724 Ok(dom_apis::element_closest(
3725 SelectorWrapper::Owned(DomRoot::from_ref(self)),
3726 &selectors,
3727 quirks_mode,
3728 )
3729 .map(SelectorWrapper::into_owned))
3730 }
3731
3732 fn InsertAdjacentElement(
3734 &self,
3735 cx: &mut JSContext,
3736 where_: DOMString,
3737 element: &Element,
3738 ) -> Fallible<Option<DomRoot<Element>>> {
3739 let where_ = where_.parse::<AdjacentPosition>()?;
3740 let inserted_node = self.insert_adjacent(cx, where_, element.upcast())?;
3741 Ok(inserted_node.map(|node| DomRoot::downcast(node).unwrap()))
3742 }
3743
3744 fn InsertAdjacentText(
3746 &self,
3747 cx: &mut JSContext,
3748 where_: DOMString,
3749 data: DOMString,
3750 ) -> ErrorResult {
3751 let text = Text::new(cx, data, &self.owner_document());
3753
3754 let where_ = where_.parse::<AdjacentPosition>()?;
3756 self.insert_adjacent(cx, where_, text.upcast()).map(|_| ())
3757 }
3758
3759 fn InsertAdjacentHTML(
3761 &self,
3762 cx: &mut JSContext,
3763 position: DOMString,
3764 text: TrustedHTMLOrString,
3765 ) -> ErrorResult {
3766 let text = TrustedHTML::get_trusted_type_compliant_string(
3770 cx,
3771 &self.owner_global(),
3772 text,
3773 "Element insertAdjacentHTML",
3774 )?;
3775 let position = position.parse::<AdjacentPosition>()?;
3776
3777 let context = match position {
3780 AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => {
3783 match self.upcast::<Node>().GetParentNode() {
3784 Some(ref node) if node.is::<Document>() => {
3786 return Err(Error::NoModificationAllowed(None));
3787 },
3788 None => return Err(Error::NoModificationAllowed(None)),
3789 Some(node) => node,
3791 }
3792 },
3793 AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => {
3796 DomRoot::from_ref(self.upcast::<Node>())
3798 },
3799 };
3800
3801 let context = Element::fragment_parsing_context(
3803 cx,
3804 &context.owner_doc(),
3805 context.downcast::<Element>(),
3806 );
3807
3808 let fragment = context.parse_fragment(text, cx)?;
3811
3812 self.insert_adjacent(cx, position, fragment.upcast())
3814 .map(|_| ())
3815 }
3816
3817 fn EnterFormalActivationState(&self) -> ErrorResult {
3819 match self.as_maybe_activatable() {
3820 Some(a) => {
3821 a.enter_formal_activation_state();
3822 Ok(())
3823 },
3824 None => Err(Error::NotSupported(None)),
3825 }
3826 }
3827
3828 fn ExitFormalActivationState(&self) -> ErrorResult {
3829 match self.as_maybe_activatable() {
3830 Some(a) => {
3831 a.exit_formal_activation_state();
3832 Ok(())
3833 },
3834 None => Err(Error::NotSupported(None)),
3835 }
3836 }
3837
3838 fn RequestFullscreen(&self, can_gc: CanGc) -> Rc<Promise> {
3840 let doc = self.owner_document();
3841 doc.enter_fullscreen(self, can_gc)
3842 }
3843
3844 fn AttachShadow(
3846 &self,
3847 cx: &mut JSContext,
3848 init: &ShadowRootInit,
3849 ) -> Fallible<DomRoot<ShadowRoot>> {
3850 let shadow_root = self.attach_shadow(
3853 cx,
3854 IsUserAgentWidget::No,
3855 init.mode,
3856 init.clonable,
3857 init.serializable,
3858 init.delegatesFocus,
3859 init.slotAssignment,
3860 )?;
3861
3862 Ok(shadow_root)
3864 }
3865
3866 fn GetShadowRoot(&self) -> Option<DomRoot<ShadowRoot>> {
3868 let shadow_or_none = self.shadow_root();
3870
3871 let shadow = shadow_or_none?;
3873 if shadow.Mode() == ShadowRootMode::Closed {
3874 return None;
3875 }
3876
3877 Some(shadow)
3879 }
3880
3881 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
3883 self.custom_element_registry()
3885 }
3886
3887 fn GetRole(&self) -> Option<DOMString> {
3889 self.get_nullable_string_attribute(&local_name!("role"))
3890 }
3891
3892 fn SetRole(&self, cx: &mut JSContext, value: Option<DOMString>) {
3894 self.set_nullable_string_attribute(cx, &local_name!("role"), value);
3895 }
3896
3897 fn GetAriaAtomic(&self) -> Option<DOMString> {
3898 self.get_nullable_string_attribute(&local_name!("aria-atomic"))
3899 }
3900
3901 fn SetAriaAtomic(&self, cx: &mut JSContext, value: Option<DOMString>) {
3902 self.set_nullable_string_attribute(cx, &local_name!("aria-atomic"), value);
3903 }
3904
3905 fn GetAriaAutoComplete(&self) -> Option<DOMString> {
3906 self.get_nullable_string_attribute(&local_name!("aria-autocomplete"))
3907 }
3908
3909 fn SetAriaAutoComplete(&self, cx: &mut JSContext, value: Option<DOMString>) {
3910 self.set_nullable_string_attribute(cx, &local_name!("aria-autocomplete"), value);
3911 }
3912
3913 fn GetAriaBrailleLabel(&self) -> Option<DOMString> {
3914 self.get_nullable_string_attribute(&local_name!("aria-braillelabel"))
3915 }
3916
3917 fn SetAriaBrailleLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
3918 self.set_nullable_string_attribute(cx, &local_name!("aria-braillelabel"), value);
3919 }
3920
3921 fn GetAriaBrailleRoleDescription(&self) -> Option<DOMString> {
3922 self.get_nullable_string_attribute(&local_name!("aria-brailleroledescription"))
3923 }
3924
3925 fn SetAriaBrailleRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
3926 self.set_nullable_string_attribute(cx, &local_name!("aria-brailleroledescription"), value);
3927 }
3928
3929 fn GetAriaBusy(&self) -> Option<DOMString> {
3930 self.get_nullable_string_attribute(&local_name!("aria-busy"))
3931 }
3932
3933 fn SetAriaBusy(&self, cx: &mut JSContext, value: Option<DOMString>) {
3934 self.set_nullable_string_attribute(cx, &local_name!("aria-busy"), value);
3935 }
3936
3937 fn GetAriaChecked(&self) -> Option<DOMString> {
3938 self.get_nullable_string_attribute(&local_name!("aria-checked"))
3939 }
3940
3941 fn SetAriaChecked(&self, cx: &mut JSContext, value: Option<DOMString>) {
3942 self.set_nullable_string_attribute(cx, &local_name!("aria-checked"), value);
3943 }
3944
3945 fn GetAriaColCount(&self) -> Option<DOMString> {
3946 self.get_nullable_string_attribute(&local_name!("aria-colcount"))
3947 }
3948
3949 fn SetAriaColCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
3950 self.set_nullable_string_attribute(cx, &local_name!("aria-colcount"), value);
3951 }
3952
3953 fn GetAriaColIndex(&self) -> Option<DOMString> {
3954 self.get_nullable_string_attribute(&local_name!("aria-colindex"))
3955 }
3956
3957 fn SetAriaColIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
3958 self.set_nullable_string_attribute(cx, &local_name!("aria-colindex"), value);
3959 }
3960
3961 fn GetAriaColIndexText(&self) -> Option<DOMString> {
3962 self.get_nullable_string_attribute(&local_name!("aria-colindextext"))
3963 }
3964
3965 fn SetAriaColIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
3966 self.set_nullable_string_attribute(cx, &local_name!("aria-colindextext"), value);
3967 }
3968
3969 fn GetAriaColSpan(&self) -> Option<DOMString> {
3970 self.get_nullable_string_attribute(&local_name!("aria-colspan"))
3971 }
3972
3973 fn SetAriaColSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
3974 self.set_nullable_string_attribute(cx, &local_name!("aria-colspan"), value);
3975 }
3976
3977 fn GetAriaCurrent(&self) -> Option<DOMString> {
3978 self.get_nullable_string_attribute(&local_name!("aria-current"))
3979 }
3980
3981 fn SetAriaCurrent(&self, cx: &mut JSContext, value: Option<DOMString>) {
3982 self.set_nullable_string_attribute(cx, &local_name!("aria-current"), value);
3983 }
3984
3985 fn GetAriaDescription(&self) -> Option<DOMString> {
3986 self.get_nullable_string_attribute(&local_name!("aria-description"))
3987 }
3988
3989 fn SetAriaDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
3990 self.set_nullable_string_attribute(cx, &local_name!("aria-description"), value);
3991 }
3992
3993 fn GetAriaDisabled(&self) -> Option<DOMString> {
3994 self.get_nullable_string_attribute(&local_name!("aria-disabled"))
3995 }
3996
3997 fn SetAriaDisabled(&self, cx: &mut JSContext, value: Option<DOMString>) {
3998 self.set_nullable_string_attribute(cx, &local_name!("aria-disabled"), value);
3999 }
4000
4001 fn GetAriaExpanded(&self) -> Option<DOMString> {
4002 self.get_nullable_string_attribute(&local_name!("aria-expanded"))
4003 }
4004
4005 fn SetAriaExpanded(&self, cx: &mut JSContext, value: Option<DOMString>) {
4006 self.set_nullable_string_attribute(cx, &local_name!("aria-expanded"), value);
4007 }
4008
4009 fn GetAriaHasPopup(&self) -> Option<DOMString> {
4010 self.get_nullable_string_attribute(&local_name!("aria-haspopup"))
4011 }
4012
4013 fn SetAriaHasPopup(&self, cx: &mut JSContext, value: Option<DOMString>) {
4014 self.set_nullable_string_attribute(cx, &local_name!("aria-haspopup"), value);
4015 }
4016
4017 fn GetAriaHidden(&self) -> Option<DOMString> {
4018 self.get_nullable_string_attribute(&local_name!("aria-hidden"))
4019 }
4020
4021 fn SetAriaHidden(&self, cx: &mut JSContext, value: Option<DOMString>) {
4022 self.set_nullable_string_attribute(cx, &local_name!("aria-hidden"), value);
4023 }
4024
4025 fn GetAriaInvalid(&self) -> Option<DOMString> {
4026 self.get_nullable_string_attribute(&local_name!("aria-invalid"))
4027 }
4028
4029 fn SetAriaInvalid(&self, cx: &mut JSContext, value: Option<DOMString>) {
4030 self.set_nullable_string_attribute(cx, &local_name!("aria-invalid"), value);
4031 }
4032
4033 fn GetAriaKeyShortcuts(&self) -> Option<DOMString> {
4034 self.get_nullable_string_attribute(&local_name!("aria-keyshortcuts"))
4035 }
4036
4037 fn SetAriaKeyShortcuts(&self, cx: &mut JSContext, value: Option<DOMString>) {
4038 self.set_nullable_string_attribute(cx, &local_name!("aria-keyshortcuts"), value);
4039 }
4040
4041 fn GetAriaLabel(&self) -> Option<DOMString> {
4042 self.get_nullable_string_attribute(&local_name!("aria-label"))
4043 }
4044
4045 fn SetAriaLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4046 self.set_nullable_string_attribute(cx, &local_name!("aria-label"), value);
4047 }
4048
4049 fn GetAriaLevel(&self) -> Option<DOMString> {
4050 self.get_nullable_string_attribute(&local_name!("aria-level"))
4051 }
4052
4053 fn SetAriaLevel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4054 self.set_nullable_string_attribute(cx, &local_name!("aria-level"), value);
4055 }
4056
4057 fn GetAriaLive(&self) -> Option<DOMString> {
4058 self.get_nullable_string_attribute(&local_name!("aria-live"))
4059 }
4060
4061 fn SetAriaLive(&self, cx: &mut JSContext, value: Option<DOMString>) {
4062 self.set_nullable_string_attribute(cx, &local_name!("aria-live"), value);
4063 }
4064
4065 fn GetAriaModal(&self) -> Option<DOMString> {
4066 self.get_nullable_string_attribute(&local_name!("aria-modal"))
4067 }
4068
4069 fn SetAriaModal(&self, cx: &mut JSContext, value: Option<DOMString>) {
4070 self.set_nullable_string_attribute(cx, &local_name!("aria-modal"), value);
4071 }
4072
4073 fn GetAriaMultiLine(&self) -> Option<DOMString> {
4074 self.get_nullable_string_attribute(&local_name!("aria-multiline"))
4075 }
4076
4077 fn SetAriaMultiLine(&self, cx: &mut JSContext, value: Option<DOMString>) {
4078 self.set_nullable_string_attribute(cx, &local_name!("aria-multiline"), value);
4079 }
4080
4081 fn GetAriaMultiSelectable(&self) -> Option<DOMString> {
4082 self.get_nullable_string_attribute(&local_name!("aria-multiselectable"))
4083 }
4084
4085 fn SetAriaMultiSelectable(&self, cx: &mut JSContext, value: Option<DOMString>) {
4086 self.set_nullable_string_attribute(cx, &local_name!("aria-multiselectable"), value);
4087 }
4088
4089 fn GetAriaOrientation(&self) -> Option<DOMString> {
4090 self.get_nullable_string_attribute(&local_name!("aria-orientation"))
4091 }
4092
4093 fn SetAriaOrientation(&self, cx: &mut JSContext, value: Option<DOMString>) {
4094 self.set_nullable_string_attribute(cx, &local_name!("aria-orientation"), value);
4095 }
4096
4097 fn GetAriaPlaceholder(&self) -> Option<DOMString> {
4098 self.get_nullable_string_attribute(&local_name!("aria-placeholder"))
4099 }
4100
4101 fn SetAriaPlaceholder(&self, cx: &mut JSContext, value: Option<DOMString>) {
4102 self.set_nullable_string_attribute(cx, &local_name!("aria-placeholder"), value);
4103 }
4104
4105 fn GetAriaPosInSet(&self) -> Option<DOMString> {
4106 self.get_nullable_string_attribute(&local_name!("aria-posinset"))
4107 }
4108
4109 fn SetAriaPosInSet(&self, cx: &mut JSContext, value: Option<DOMString>) {
4110 self.set_nullable_string_attribute(cx, &local_name!("aria-posinset"), value);
4111 }
4112
4113 fn GetAriaPressed(&self) -> Option<DOMString> {
4114 self.get_nullable_string_attribute(&local_name!("aria-pressed"))
4115 }
4116
4117 fn SetAriaPressed(&self, cx: &mut JSContext, value: Option<DOMString>) {
4118 self.set_nullable_string_attribute(cx, &local_name!("aria-pressed"), value);
4119 }
4120
4121 fn GetAriaReadOnly(&self) -> Option<DOMString> {
4122 self.get_nullable_string_attribute(&local_name!("aria-readonly"))
4123 }
4124
4125 fn SetAriaReadOnly(&self, cx: &mut JSContext, value: Option<DOMString>) {
4126 self.set_nullable_string_attribute(cx, &local_name!("aria-readonly"), value);
4127 }
4128
4129 fn GetAriaRelevant(&self) -> Option<DOMString> {
4130 self.get_nullable_string_attribute(&local_name!("aria-relevant"))
4131 }
4132
4133 fn SetAriaRelevant(&self, cx: &mut JSContext, value: Option<DOMString>) {
4134 self.set_nullable_string_attribute(cx, &local_name!("aria-relevant"), value);
4135 }
4136
4137 fn GetAriaRequired(&self) -> Option<DOMString> {
4138 self.get_nullable_string_attribute(&local_name!("aria-required"))
4139 }
4140
4141 fn SetAriaRequired(&self, cx: &mut JSContext, value: Option<DOMString>) {
4142 self.set_nullable_string_attribute(cx, &local_name!("aria-required"), value);
4143 }
4144
4145 fn GetAriaRoleDescription(&self) -> Option<DOMString> {
4146 self.get_nullable_string_attribute(&local_name!("aria-roledescription"))
4147 }
4148
4149 fn SetAriaRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4150 self.set_nullable_string_attribute(cx, &local_name!("aria-roledescription"), value);
4151 }
4152
4153 fn GetAriaRowCount(&self) -> Option<DOMString> {
4154 self.get_nullable_string_attribute(&local_name!("aria-rowcount"))
4155 }
4156
4157 fn SetAriaRowCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4158 self.set_nullable_string_attribute(cx, &local_name!("aria-rowcount"), value);
4159 }
4160
4161 fn GetAriaRowIndex(&self) -> Option<DOMString> {
4162 self.get_nullable_string_attribute(&local_name!("aria-rowindex"))
4163 }
4164
4165 fn SetAriaRowIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4166 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindex"), value);
4167 }
4168
4169 fn GetAriaRowIndexText(&self) -> Option<DOMString> {
4170 self.get_nullable_string_attribute(&local_name!("aria-rowindextext"))
4171 }
4172
4173 fn SetAriaRowIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4174 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindextext"), value);
4175 }
4176
4177 fn GetAriaRowSpan(&self) -> Option<DOMString> {
4178 self.get_nullable_string_attribute(&local_name!("aria-rowspan"))
4179 }
4180
4181 fn SetAriaRowSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4182 self.set_nullable_string_attribute(cx, &local_name!("aria-rowspan"), value);
4183 }
4184
4185 fn GetAriaSelected(&self) -> Option<DOMString> {
4186 self.get_nullable_string_attribute(&local_name!("aria-selected"))
4187 }
4188
4189 fn SetAriaSelected(&self, cx: &mut JSContext, value: Option<DOMString>) {
4190 self.set_nullable_string_attribute(cx, &local_name!("aria-selected"), value);
4191 }
4192
4193 fn GetAriaSetSize(&self) -> Option<DOMString> {
4194 self.get_nullable_string_attribute(&local_name!("aria-setsize"))
4195 }
4196
4197 fn SetAriaSetSize(&self, cx: &mut JSContext, value: Option<DOMString>) {
4198 self.set_nullable_string_attribute(cx, &local_name!("aria-setsize"), value);
4199 }
4200
4201 fn GetAriaSort(&self) -> Option<DOMString> {
4202 self.get_nullable_string_attribute(&local_name!("aria-sort"))
4203 }
4204
4205 fn SetAriaSort(&self, cx: &mut JSContext, value: Option<DOMString>) {
4206 self.set_nullable_string_attribute(cx, &local_name!("aria-sort"), value);
4207 }
4208
4209 fn GetAriaValueMax(&self) -> Option<DOMString> {
4210 self.get_nullable_string_attribute(&local_name!("aria-valuemax"))
4211 }
4212
4213 fn SetAriaValueMax(&self, cx: &mut JSContext, value: Option<DOMString>) {
4214 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemax"), value);
4215 }
4216
4217 fn GetAriaValueMin(&self) -> Option<DOMString> {
4218 self.get_nullable_string_attribute(&local_name!("aria-valuemin"))
4219 }
4220
4221 fn SetAriaValueMin(&self, cx: &mut JSContext, value: Option<DOMString>) {
4222 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemin"), value);
4223 }
4224
4225 fn GetAriaValueNow(&self) -> Option<DOMString> {
4226 self.get_nullable_string_attribute(&local_name!("aria-valuenow"))
4227 }
4228
4229 fn SetAriaValueNow(&self, cx: &mut JSContext, value: Option<DOMString>) {
4230 self.set_nullable_string_attribute(cx, &local_name!("aria-valuenow"), value);
4231 }
4232
4233 fn GetAriaValueText(&self) -> Option<DOMString> {
4234 self.get_nullable_string_attribute(&local_name!("aria-valuetext"))
4235 }
4236
4237 fn SetAriaValueText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4238 self.set_nullable_string_attribute(cx, &local_name!("aria-valuetext"), value);
4239 }
4240
4241 fn GetAssignedSlot(&self) -> Option<DomRoot<HTMLSlotElement>> {
4243 let cx = GlobalScope::get_cx();
4244
4245 rooted!(in(*cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4248 slottable.find_a_slot(true)
4249 }
4250
4251 fn Part(&self) -> DomRoot<DOMTokenList> {
4253 self.ensure_rare_data()
4254 .part
4255 .or_init(|| DOMTokenList::new(self, &local_name!("part"), None, CanGc::note()))
4256 }
4257}
4258
4259impl VirtualMethods for Element {
4260 fn super_type(&self) -> Option<&dyn VirtualMethods> {
4261 Some(self.upcast::<Node>() as &dyn VirtualMethods)
4262 }
4263
4264 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
4265 if attr.local_name() == &local_name!("lang") {
4267 return true;
4268 }
4269
4270 self.super_type()
4271 .unwrap()
4272 .attribute_affects_presentational_hints(attr)
4273 }
4274
4275 fn attribute_mutated(
4276 &self,
4277 cx: &mut js::context::JSContext,
4278 attr: &Attr,
4279 mutation: AttributeMutation,
4280 ) {
4281 self.super_type()
4282 .unwrap()
4283 .attribute_mutated(cx, attr, mutation);
4284 let node = self.upcast::<Node>();
4285 let doc = node.owner_doc();
4286 match *attr.local_name() {
4287 local_name!("style") => self.update_style_attribute(attr, mutation),
4288 local_name!("id") => {
4289 *self.id_attribute.borrow_mut() = mutation.new_value(attr).and_then(|value| {
4291 let value = value.as_atom();
4292 if value != &atom!("") {
4293 Some(value.clone())
4295 } else {
4296 None
4298 }
4299 });
4300
4301 let containing_shadow_root = self.containing_shadow_root();
4302 if node.is_in_a_document_tree() || node.is_in_a_shadow_tree() {
4303 let value = attr.value().as_atom().clone();
4304 match mutation {
4305 AttributeMutation::Set(old_value, _) => {
4306 if let Some(old_value) = old_value {
4307 let old_value = old_value.as_atom().clone();
4308 if let Some(ref shadow_root) = containing_shadow_root {
4309 shadow_root.unregister_element_id(
4310 self,
4311 old_value,
4312 CanGc::from_cx(cx),
4313 );
4314 } else {
4315 doc.unregister_element_id(self, old_value, CanGc::from_cx(cx));
4316 }
4317 }
4318 if value != atom!("") {
4319 if let Some(ref shadow_root) = containing_shadow_root {
4320 shadow_root.register_element_id(
4321 self,
4322 value,
4323 CanGc::from_cx(cx),
4324 );
4325 } else {
4326 doc.register_element_id(self, value, CanGc::from_cx(cx));
4327 }
4328 }
4329 },
4330 AttributeMutation::Removed => {
4331 if value != atom!("") {
4332 if let Some(ref shadow_root) = containing_shadow_root {
4333 shadow_root.unregister_element_id(
4334 self,
4335 value,
4336 CanGc::from_cx(cx),
4337 );
4338 } else {
4339 doc.unregister_element_id(self, value, CanGc::from_cx(cx));
4340 }
4341 }
4342 },
4343 }
4344 }
4345 },
4346 local_name!("name") => {
4347 self.ensure_rare_data().name_attribute =
4349 mutation.new_value(attr).and_then(|value| {
4350 let value = value.as_atom();
4351 if value != &atom!("") {
4352 Some(value.clone())
4353 } else {
4354 None
4355 }
4356 });
4357 if node.is_connected() && node.containing_shadow_root().is_none() {
4360 let value = attr.value().as_atom().clone();
4361 match mutation {
4362 AttributeMutation::Set(old_value, _) => {
4363 if let Some(old_value) = old_value {
4364 let old_value = old_value.as_atom().clone();
4365 doc.unregister_element_name(self, old_value);
4366 }
4367 if value != atom!("") {
4368 doc.register_element_name(self, value);
4369 }
4370 },
4371 AttributeMutation::Removed => {
4372 if value != atom!("") {
4373 doc.unregister_element_name(self, value);
4374 }
4375 },
4376 }
4377 }
4378 },
4379 local_name!("slot") => {
4380 let cx = GlobalScope::get_cx();
4382
4383 rooted!(in(*cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4384
4385 if let Some(assigned_slot) = slottable.assigned_slot() {
4387 assigned_slot.assign_slottables();
4388 }
4389 slottable.assign_a_slot();
4390 },
4391 _ => {
4392 if attr.namespace() == &ns!() && attr.local_name() == &local_name!("src") {
4395 node.dirty(NodeDamage::Other);
4396 }
4397 },
4398 };
4399
4400 if self
4403 .upcast::<Node>()
4404 .get_flag(NodeFlags::USES_ATTR_IN_CONTENT_ATTRIBUTE)
4405 {
4406 node.dirty(NodeDamage::ContentOrHeritage);
4407 }
4408
4409 node.rev_version();
4413
4414 let global = self.owner_global();
4416 if global.live_devtools_updates() {
4417 if let Some(sender) = global.devtools_chan() {
4418 let pipeline_id = global.pipeline_id();
4419 if ScriptThread::devtools_want_updates_for_node(pipeline_id, self.upcast()) {
4420 let devtools_message = ScriptToDevtoolsControlMsg::DomMutation(
4421 pipeline_id,
4422 DomMutation::AttributeModified {
4423 node: self.upcast::<Node>().unique_id(pipeline_id),
4424 attribute_name: attr.local_name().to_string(),
4425 new_value: mutation.new_value(attr).map(|value| value.to_string()),
4426 },
4427 );
4428 sender.send(devtools_message).unwrap();
4429 }
4430 }
4431 }
4432 }
4433
4434 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
4435 match *name {
4436 local_name!("id") => AttrValue::Atom(value.into()),
4437 local_name!("name") => AttrValue::Atom(value.into()),
4438 local_name!("class") | local_name!("part") => {
4439 AttrValue::from_serialized_tokenlist(value.into())
4440 },
4441 local_name!("exportparts") => AttrValue::from_shadow_parts(value.into()),
4442 local_name!("tabindex") => AttrValue::from_i32(value.into(), -1),
4443 _ => self
4444 .super_type()
4445 .unwrap()
4446 .parse_plain_attribute(name, value),
4447 }
4448 }
4449
4450 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
4451 if let Some(s) = self.super_type() {
4452 s.bind_to_tree(cx, context);
4453 }
4454
4455 if let Some(f) = self.as_maybe_form_control() {
4456 f.bind_form_control_to_tree(CanGc::from_cx(cx));
4457 }
4458
4459 let doc = self.owner_document();
4460
4461 if let Some(ref shadow_root) = self.shadow_root() {
4462 shadow_root.bind_to_tree(cx, context);
4463 }
4464
4465 if !context.is_in_tree() {
4466 return;
4467 }
4468
4469 if let Some(ref id) = *self.id_attribute.borrow() {
4470 if let Some(shadow_root) = self.containing_shadow_root() {
4471 shadow_root.register_element_id(self, id.clone(), CanGc::from_cx(cx));
4472 } else {
4473 doc.register_element_id(self, id.clone(), CanGc::from_cx(cx));
4474 }
4475 }
4476 if let Some(ref name) = self.name_attribute() {
4477 if self.containing_shadow_root().is_none() {
4478 doc.register_element_name(self, name.clone());
4479 }
4480 }
4481
4482 doc.increment_dom_count();
4484 }
4485
4486 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
4487 self.super_type().unwrap().unbind_from_tree(context, can_gc);
4488
4489 if let Some(f) = self.as_maybe_form_control() {
4490 f.unbind_form_control_from_tree(can_gc);
4494 }
4495
4496 if !context.tree_is_in_a_document_tree && !context.tree_is_in_a_shadow_tree {
4497 return;
4498 }
4499
4500 let doc = self.owner_document();
4501
4502 let fullscreen = doc.fullscreen_element();
4503 if fullscreen.as_deref() == Some(self) {
4504 doc.exit_fullscreen(can_gc);
4505 }
4506 if let Some(ref value) = *self.id_attribute.borrow() {
4507 if let Some(ref shadow_root) = self.containing_shadow_root() {
4508 if !self.upcast::<Node>().is_in_a_shadow_tree() {
4511 shadow_root.unregister_element_id(self, value.clone(), can_gc);
4512 }
4513 } else {
4514 doc.unregister_element_id(self, value.clone(), can_gc);
4515 }
4516 }
4517 if let Some(ref value) = self.name_attribute() {
4518 if self.containing_shadow_root().is_none() {
4519 doc.unregister_element_name(self, value.clone());
4520 }
4521 }
4522 doc.decrement_dom_count();
4524 }
4525
4526 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
4527 if let Some(s) = self.super_type() {
4528 s.children_changed(cx, mutation);
4529 }
4530
4531 let flags = self.get_selector_flags();
4532 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
4533 self.upcast::<Node>().dirty(NodeDamage::Other);
4535 } else {
4536 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
4537 if let Some(next_child) = mutation.next_child() {
4538 for child in next_child.inclusively_following_siblings_unrooted(cx.no_gc()) {
4539 if child.is::<Element>() {
4540 child.dirty(NodeDamage::Other);
4541 }
4542 }
4543 }
4544 }
4545 if flags.intersects(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {
4546 if let Some(child) = mutation.modified_edge_element() {
4547 child.dirty(NodeDamage::Other);
4548 }
4549 }
4550 }
4551 }
4552
4553 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
4554 self.super_type().unwrap().adopting_steps(cx, old_doc);
4555
4556 if self.owner_document().is_html_document() != old_doc.is_html_document() {
4557 self.tag_name.clear();
4558 }
4559 }
4560
4561 fn post_connection_steps(&self, cx: &mut js::context::JSContext) {
4562 if let Some(s) = self.super_type() {
4563 s.post_connection_steps(cx);
4564 }
4565
4566 self.update_nonce_post_connection();
4567 }
4568
4569 fn cloning_steps(
4571 &self,
4572 cx: &mut JSContext,
4573 copy: &Node,
4574 maybe_doc: Option<&Document>,
4575 clone_children: CloneChildrenFlag,
4576 ) {
4577 if let Some(s) = self.super_type() {
4578 s.cloning_steps(cx, copy, maybe_doc, clone_children);
4579 }
4580 let elem = copy.downcast::<Element>().unwrap();
4581 if let Some(rare_data) = self.rare_data().as_ref() {
4582 elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone());
4583 }
4584 }
4585}
4586
4587#[derive(Clone, PartialEq)]
4588pub enum SelectorWrapper<'a> {
4593 Borrowed(&'a DomRoot<Element>),
4594 Owned(DomRoot<Element>),
4595}
4596
4597impl fmt::Debug for SelectorWrapper<'_> {
4598 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4599 self.deref().fmt(f)
4600 }
4601}
4602
4603impl Deref for SelectorWrapper<'_> {
4604 type Target = DomRoot<Element>;
4605
4606 fn deref(&self) -> &Self::Target {
4607 match self {
4608 SelectorWrapper::Owned(r) => r,
4609 SelectorWrapper::Borrowed(r) => r,
4610 }
4611 }
4612}
4613
4614impl SelectorWrapper<'_> {
4615 fn into_owned(self) -> DomRoot<Element> {
4616 match self {
4617 SelectorWrapper::Owned(r) => r,
4618 SelectorWrapper::Borrowed(r) => r.clone(),
4619 }
4620 }
4621}
4622
4623impl SelectorsElement for SelectorWrapper<'_> {
4624 type Impl = SelectorImpl;
4625
4626 #[expect(unsafe_code)]
4627 fn opaque(&self) -> ::selectors::OpaqueElement {
4628 ::selectors::OpaqueElement::new(unsafe { &*self.reflector().get_jsobject().get() })
4629 }
4630
4631 fn parent_element(&self) -> Option<Self> {
4632 self.upcast::<Node>()
4633 .GetParentElement()
4634 .map(SelectorWrapper::Owned)
4635 }
4636
4637 fn parent_node_is_shadow_root(&self) -> bool {
4638 match self.upcast::<Node>().GetParentNode() {
4639 None => false,
4640 Some(node) => node.is::<ShadowRoot>(),
4641 }
4642 }
4643
4644 fn containing_shadow_host(&self) -> Option<Self> {
4645 self.containing_shadow_root()
4646 .map(|shadow_root| shadow_root.Host())
4647 .map(SelectorWrapper::Owned)
4648 }
4649
4650 fn is_pseudo_element(&self) -> bool {
4651 false
4652 }
4653
4654 fn match_pseudo_element(
4655 &self,
4656 _pseudo: &PseudoElement,
4657 _context: &mut MatchingContext<Self::Impl>,
4658 ) -> bool {
4659 false
4660 }
4661
4662 fn prev_sibling_element(&self) -> Option<Self> {
4663 self.node
4664 .preceding_siblings()
4665 .find_map(DomRoot::downcast)
4666 .map(SelectorWrapper::Owned)
4667 }
4668
4669 fn next_sibling_element(&self) -> Option<Self> {
4670 self.node
4671 .following_siblings()
4672 .find_map(DomRoot::downcast)
4673 .map(SelectorWrapper::Owned)
4674 }
4675
4676 fn first_element_child(&self) -> Option<Self> {
4677 self.GetFirstElementChild().map(SelectorWrapper::Owned)
4678 }
4679
4680 fn attr_matches(
4681 &self,
4682 ns: &NamespaceConstraint<&style::Namespace>,
4683 local_name: &style::LocalName,
4684 operation: &AttrSelectorOperation<&AtomString>,
4685 ) -> bool {
4686 match *ns {
4687 NamespaceConstraint::Specific(ns) => self
4688 .get_attribute_with_namespace(ns, local_name)
4689 .is_some_and(|attr| attr.value().eval_selector(operation)),
4690 NamespaceConstraint::Any => self.attrs.borrow().iter().any(|attr| {
4691 *attr.local_name() == **local_name && attr.value().eval_selector(operation)
4692 }),
4693 }
4694 }
4695
4696 fn is_root(&self) -> bool {
4697 Element::is_root(self)
4698 }
4699
4700 fn is_empty(&self) -> bool {
4701 self.node.children().all(|node| {
4702 !node.is::<Element>() &&
4703 match node.downcast::<Text>() {
4704 None => true,
4705 Some(text) => text.upcast::<CharacterData>().data().is_empty(),
4706 }
4707 })
4708 }
4709
4710 fn has_local_name(&self, local_name: &LocalName) -> bool {
4711 Element::local_name(self) == local_name
4712 }
4713
4714 fn has_namespace(&self, ns: &Namespace) -> bool {
4715 Element::namespace(self) == ns
4716 }
4717
4718 fn is_same_type(&self, other: &Self) -> bool {
4719 Element::local_name(self) == Element::local_name(other) &&
4720 Element::namespace(self) == Element::namespace(other)
4721 }
4722
4723 fn match_non_ts_pseudo_class(
4724 &self,
4725 pseudo_class: &NonTSPseudoClass,
4726 _: &mut MatchingContext<Self::Impl>,
4727 ) -> bool {
4728 match *pseudo_class {
4729 NonTSPseudoClass::Link | NonTSPseudoClass::AnyLink => self.is_link(),
4731 NonTSPseudoClass::Visited => false,
4732
4733 NonTSPseudoClass::ServoNonZeroBorder => match self.downcast::<HTMLTableElement>() {
4734 None => false,
4735 Some(this) => match this.get_border() {
4736 None | Some(0) => false,
4737 Some(_) => true,
4738 },
4739 },
4740
4741 NonTSPseudoClass::CustomState(ref state) => self.has_custom_state(&state.0),
4742
4743 NonTSPseudoClass::Lang(ref lang) => {
4748 extended_filtering(&self.upcast::<Node>().get_lang().unwrap_or_default(), lang)
4749 },
4750
4751 NonTSPseudoClass::ReadOnly => {
4752 !Element::state(self).contains(NonTSPseudoClass::ReadWrite.state_flag())
4753 },
4754
4755 NonTSPseudoClass::Active |
4756 NonTSPseudoClass::Autofill |
4757 NonTSPseudoClass::Checked |
4758 NonTSPseudoClass::Default |
4759 NonTSPseudoClass::Defined |
4760 NonTSPseudoClass::Disabled |
4761 NonTSPseudoClass::Enabled |
4762 NonTSPseudoClass::Focus |
4763 NonTSPseudoClass::FocusVisible |
4764 NonTSPseudoClass::FocusWithin |
4765 NonTSPseudoClass::Fullscreen |
4766 NonTSPseudoClass::Hover |
4767 NonTSPseudoClass::InRange |
4768 NonTSPseudoClass::Indeterminate |
4769 NonTSPseudoClass::Invalid |
4770 NonTSPseudoClass::Modal |
4771 NonTSPseudoClass::MozMeterOptimum |
4772 NonTSPseudoClass::MozMeterSubOptimum |
4773 NonTSPseudoClass::MozMeterSubSubOptimum |
4774 NonTSPseudoClass::Open |
4775 NonTSPseudoClass::Optional |
4776 NonTSPseudoClass::OutOfRange |
4777 NonTSPseudoClass::PlaceholderShown |
4778 NonTSPseudoClass::PopoverOpen |
4779 NonTSPseudoClass::ReadWrite |
4780 NonTSPseudoClass::Required |
4781 NonTSPseudoClass::Target |
4782 NonTSPseudoClass::UserInvalid |
4783 NonTSPseudoClass::UserValid |
4784 NonTSPseudoClass::Valid => Element::state(self).contains(pseudo_class.state_flag()),
4785 }
4786 }
4787
4788 fn is_link(&self) -> bool {
4789 let node = self.upcast::<Node>();
4791 match node.type_id() {
4792 NodeTypeId::Element(ElementTypeId::HTMLElement(
4794 HTMLElementTypeId::HTMLAnchorElement,
4795 )) |
4796 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
4797 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
4798 self.has_attribute(&local_name!("href"))
4799 },
4800 _ => false,
4801 }
4802 }
4803
4804 fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
4805 self.id_attribute
4806 .borrow()
4807 .as_ref()
4808 .is_some_and(|atom| case_sensitivity.eq_atom(id, atom))
4809 }
4810
4811 fn is_part(&self, name: &AtomIdent) -> bool {
4812 Element::is_part(self, name, CaseSensitivity::CaseSensitive)
4813 }
4814
4815 fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
4816 None
4817 }
4818
4819 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
4820 Element::has_class(self, name, case_sensitivity)
4821 }
4822
4823 fn is_html_element_in_html_document(&self) -> bool {
4824 self.html_element_in_html_document()
4825 }
4826
4827 fn is_html_slot_element(&self) -> bool {
4828 self.is_html_element() && self.local_name() == &local_name!("slot")
4829 }
4830
4831 fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
4832 let self_flags = flags.for_self();
4834 if !self_flags.is_empty() {
4835 #[expect(unsafe_code)]
4836 unsafe {
4837 Dom::from_ref(&***self)
4838 .to_layout()
4839 .insert_selector_flags(self_flags);
4840 }
4841 }
4842
4843 let parent_flags = flags.for_parent();
4845 if !parent_flags.is_empty() {
4846 if let Some(p) = self.parent_element() {
4847 #[expect(unsafe_code)]
4848 unsafe {
4849 Dom::from_ref(&**p)
4850 .to_layout()
4851 .insert_selector_flags(parent_flags);
4852 }
4853 }
4854 }
4855 }
4856
4857 fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool {
4858 let mut f = |hash| filter.insert_hash(hash & BLOOM_HASH_MASK);
4859
4860 f(Element::local_name(self).get_hash());
4863 f(Element::namespace(self).get_hash());
4864
4865 if let Some(ref id) = *self.id_attribute.borrow() {
4866 f(id.get_hash());
4867 }
4868
4869 if let Some(attr) = self.get_attribute(&local_name!("class")) {
4870 for class in attr.value().as_tokens() {
4871 f(AtomIdent::cast(class).get_hash());
4872 }
4873 }
4874
4875 for attr in self.attrs.borrow().iter() {
4876 let name = style::values::GenericAtomIdent::cast(attr.local_name());
4877 if !style::bloom::is_attr_name_excluded_from_filter(name) {
4878 f(name.get_hash());
4879 }
4880 }
4881
4882 true
4883 }
4884
4885 fn has_custom_state(&self, name: &AtomIdent) -> bool {
4886 let mut has_state = false;
4887 self.each_custom_state(|state| has_state |= state == name);
4888
4889 has_state
4890 }
4891}
4892
4893impl Element {
4894 fn each_custom_state<F>(&self, callback: F)
4895 where
4896 F: FnMut(&AtomIdent),
4897 {
4898 self.get_element_internals()
4899 .and_then(|internals| internals.custom_states())
4900 .inspect(|states| states.for_each_state(callback));
4901 }
4902
4903 pub(crate) fn client_rect(&self) -> Rect<i32, CSSPixel> {
4904 let doc = self.node.owner_doc();
4905
4906 if let Some(rect) = self
4907 .rare_data()
4908 .as_ref()
4909 .and_then(|data| data.client_rect.as_ref())
4910 .and_then(|rect| rect.get().ok())
4911 {
4912 if doc.restyle_reason().is_empty() {
4913 return rect;
4914 }
4915 }
4916
4917 let mut rect = self.upcast::<Node>().client_rect();
4918 let in_quirks_mode = doc.quirks_mode() == QuirksMode::Quirks;
4919
4920 if (in_quirks_mode && doc.GetBody().as_deref() == self.downcast::<HTMLElement>()) ||
4921 (!in_quirks_mode && self.is_document_element())
4922 {
4923 rect.size = doc.window().viewport_details().size.round().to_i32();
4924 }
4925
4926 self.ensure_rare_data().client_rect = Some(self.owner_window().cache_layout_value(rect));
4927 rect
4928 }
4929
4930 pub(crate) fn as_maybe_activatable(&self) -> Option<&dyn Activatable> {
4931 let element = match self.upcast::<Node>().type_id() {
4932 NodeTypeId::Element(ElementTypeId::HTMLElement(
4933 HTMLElementTypeId::HTMLInputElement,
4934 )) => {
4935 let element = self.downcast::<HTMLInputElement>().unwrap();
4936 Some(element as &dyn Activatable)
4937 },
4938 NodeTypeId::Element(ElementTypeId::HTMLElement(
4939 HTMLElementTypeId::HTMLButtonElement,
4940 )) => {
4941 let element = self.downcast::<HTMLButtonElement>().unwrap();
4942 Some(element as &dyn Activatable)
4943 },
4944 NodeTypeId::Element(ElementTypeId::HTMLElement(
4945 HTMLElementTypeId::HTMLAnchorElement,
4946 )) => {
4947 let element = self.downcast::<HTMLAnchorElement>().unwrap();
4948 Some(element as &dyn Activatable)
4949 },
4950 NodeTypeId::Element(ElementTypeId::HTMLElement(
4951 HTMLElementTypeId::HTMLLabelElement,
4952 )) => {
4953 let element = self.downcast::<HTMLLabelElement>().unwrap();
4954 Some(element as &dyn Activatable)
4955 },
4956 NodeTypeId::Element(ElementTypeId::HTMLElement(
4957 HTMLElementTypeId::HTMLSelectElement,
4958 )) => {
4959 let element = self.downcast::<HTMLSelectElement>().unwrap();
4960 Some(element as &dyn Activatable)
4961 },
4962 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
4963 let element = self.downcast::<HTMLElement>().unwrap();
4964 Some(element as &dyn Activatable)
4965 },
4966 _ => None,
4967 };
4968 element.and_then(|elem| {
4969 if elem.is_instance_activatable() {
4970 Some(elem)
4971 } else {
4972 None
4973 }
4974 })
4975 }
4976
4977 pub(crate) fn as_stylesheet_owner(&self) -> Option<&dyn StylesheetOwner> {
4978 if let Some(s) = self.downcast::<HTMLStyleElement>() {
4979 return Some(s as &dyn StylesheetOwner);
4980 }
4981
4982 if let Some(l) = self.downcast::<HTMLLinkElement>() {
4983 return Some(l as &dyn StylesheetOwner);
4984 }
4985
4986 None
4987 }
4988
4989 pub(crate) fn as_maybe_validatable(&self) -> Option<&dyn Validatable> {
4991 match self.upcast::<Node>().type_id() {
4992 NodeTypeId::Element(ElementTypeId::HTMLElement(
4993 HTMLElementTypeId::HTMLInputElement,
4994 )) => {
4995 let element = self.downcast::<HTMLInputElement>().unwrap();
4996 Some(element as &dyn Validatable)
4997 },
4998 NodeTypeId::Element(ElementTypeId::HTMLElement(
4999 HTMLElementTypeId::HTMLButtonElement,
5000 )) => {
5001 let element = self.downcast::<HTMLButtonElement>().unwrap();
5002 Some(element as &dyn Validatable)
5003 },
5004 NodeTypeId::Element(ElementTypeId::HTMLElement(
5005 HTMLElementTypeId::HTMLObjectElement,
5006 )) => {
5007 let element = self.downcast::<HTMLObjectElement>().unwrap();
5008 Some(element as &dyn Validatable)
5009 },
5010 NodeTypeId::Element(ElementTypeId::HTMLElement(
5011 HTMLElementTypeId::HTMLSelectElement,
5012 )) => {
5013 let element = self.downcast::<HTMLSelectElement>().unwrap();
5014 Some(element as &dyn Validatable)
5015 },
5016 NodeTypeId::Element(ElementTypeId::HTMLElement(
5017 HTMLElementTypeId::HTMLTextAreaElement,
5018 )) => {
5019 let element = self.downcast::<HTMLTextAreaElement>().unwrap();
5020 Some(element as &dyn Validatable)
5021 },
5022 NodeTypeId::Element(ElementTypeId::HTMLElement(
5023 HTMLElementTypeId::HTMLFieldSetElement,
5024 )) => {
5025 let element = self.downcast::<HTMLFieldSetElement>().unwrap();
5026 Some(element as &dyn Validatable)
5027 },
5028 NodeTypeId::Element(ElementTypeId::HTMLElement(
5029 HTMLElementTypeId::HTMLOutputElement,
5030 )) => {
5031 let element = self.downcast::<HTMLOutputElement>().unwrap();
5032 Some(element as &dyn Validatable)
5033 },
5034 _ => None,
5035 }
5036 }
5037
5038 pub(crate) fn is_invalid(&self, needs_update: bool, can_gc: CanGc) -> bool {
5039 if let Some(validatable) = self.as_maybe_validatable() {
5040 if needs_update {
5041 validatable
5042 .validity_state(can_gc)
5043 .perform_validation_and_update(ValidationFlags::all(), can_gc);
5044 }
5045 return validatable.is_instance_validatable() &&
5046 !validatable.satisfies_constraints(can_gc);
5047 }
5048
5049 if let Some(internals) = self.get_element_internals() {
5050 return internals.is_invalid(can_gc);
5051 }
5052 false
5053 }
5054
5055 pub(crate) fn is_instance_validatable(&self) -> bool {
5056 if let Some(validatable) = self.as_maybe_validatable() {
5057 return validatable.is_instance_validatable();
5058 }
5059 if let Some(internals) = self.get_element_internals() {
5060 return internals.is_instance_validatable();
5061 }
5062 false
5063 }
5064
5065 pub(crate) fn init_state_for_internals(&self) {
5066 self.set_enabled_state(true);
5067 self.set_state(ElementState::VALID, true);
5068 self.set_state(ElementState::INVALID, false);
5069 }
5070
5071 pub(crate) fn click_in_progress(&self) -> bool {
5072 self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
5073 }
5074
5075 pub(crate) fn set_click_in_progress(&self, click: bool) {
5076 self.upcast::<Node>()
5077 .set_flag(NodeFlags::CLICK_IN_PROGRESS, click)
5078 }
5079
5080 pub fn state(&self) -> ElementState {
5081 self.state.get()
5082 }
5083
5084 pub(crate) fn set_state(&self, which: ElementState, value: bool) {
5085 let mut state = self.state.get();
5086 let previous_state = state;
5087 if value {
5088 state.insert(which);
5089 } else {
5090 state.remove(which);
5091 }
5092
5093 if previous_state == state {
5094 return;
5096 }
5097
5098 {
5101 let document = self.owner_document();
5102 let mut entry = document.ensure_pending_restyle(self);
5103 if entry.snapshot.is_none() {
5104 entry.snapshot = Some(Snapshot::new());
5105 }
5106 let snapshot = entry.snapshot.as_mut().unwrap();
5107 if snapshot.state.is_none() {
5108 snapshot.state = Some(self.state());
5109 }
5110 }
5111
5112 self.state.set(state);
5113 }
5114
5115 pub(crate) fn set_active_state(&self, value: bool) {
5117 self.set_state(ElementState::ACTIVE, value);
5118
5119 if let Some(parent) = self.upcast::<Node>().GetParentElement() {
5120 parent.set_active_state(value);
5121 }
5122 }
5123
5124 pub(crate) fn focus_state(&self) -> bool {
5125 self.state.get().contains(ElementState::FOCUS)
5126 }
5127
5128 pub(crate) fn set_focus_state(&self, value: bool) {
5129 self.set_state(ElementState::FOCUS, value);
5130 }
5131
5132 pub(crate) fn set_hover_state(&self, value: bool) {
5133 self.set_state(ElementState::HOVER, value);
5134 }
5135
5136 pub(crate) fn enabled_state(&self) -> bool {
5137 self.state.get().contains(ElementState::ENABLED)
5138 }
5139
5140 pub(crate) fn set_enabled_state(&self, value: bool) {
5141 self.set_state(ElementState::ENABLED, value)
5142 }
5143
5144 pub(crate) fn disabled_state(&self) -> bool {
5145 self.state.get().contains(ElementState::DISABLED)
5146 }
5147
5148 pub(crate) fn set_disabled_state(&self, value: bool) {
5149 self.set_state(ElementState::DISABLED, value)
5150 }
5151
5152 pub(crate) fn read_write_state(&self) -> bool {
5153 self.state.get().contains(ElementState::READWRITE)
5154 }
5155
5156 pub(crate) fn set_read_write_state(&self, value: bool) {
5157 self.set_state(ElementState::READWRITE, value)
5158 }
5159
5160 pub(crate) fn set_open_state(&self, value: bool) {
5161 self.set_state(ElementState::OPEN, value);
5162 }
5163
5164 pub(crate) fn set_placeholder_shown_state(&self, value: bool) {
5165 self.set_state(ElementState::PLACEHOLDER_SHOWN, value);
5166 }
5167
5168 pub(crate) fn set_modal_state(&self, value: bool) {
5169 self.set_state(ElementState::MODAL, value);
5170 }
5171
5172 pub(crate) fn set_target_state(&self, value: bool) {
5173 self.set_state(ElementState::URLTARGET, value)
5174 }
5175
5176 pub(crate) fn set_fullscreen_state(&self, value: bool) {
5177 self.set_state(ElementState::FULLSCREEN, value)
5178 }
5179
5180 pub(crate) fn is_connected(&self) -> bool {
5182 self.upcast::<Node>().is_connected()
5183 }
5184
5185 pub(crate) fn cannot_navigate(&self) -> bool {
5187 let document = self.owner_document();
5188
5189 !document.is_fully_active() ||
5191 (
5192 !self.is::<HTMLAnchorElement>() && !self.is_connected()
5194 )
5195 }
5196}
5197
5198impl Element {
5199 pub(crate) fn check_ancestors_disabled_state_for_form_control(&self) {
5200 let node = self.upcast::<Node>();
5201 if self.disabled_state() {
5202 return;
5203 }
5204 for ancestor in node.ancestors() {
5205 if !ancestor.is::<HTMLFieldSetElement>() {
5206 continue;
5207 }
5208 if !ancestor.downcast::<Element>().unwrap().disabled_state() {
5209 continue;
5210 }
5211 if ancestor.is_parent_of(node) {
5212 self.set_disabled_state(true);
5213 self.set_enabled_state(false);
5214 return;
5215 }
5216 if let Some(ref legend) = ancestor.children().find(|n| n.is::<HTMLLegendElement>()) {
5217 if node.ancestors().any(|ancestor| ancestor == *legend) {
5219 continue;
5220 }
5221 }
5222 self.set_disabled_state(true);
5223 self.set_enabled_state(false);
5224 return;
5225 }
5226 }
5227
5228 pub(crate) fn check_parent_disabled_state_for_option(&self) {
5229 if self.disabled_state() {
5230 return;
5231 }
5232 let node = self.upcast::<Node>();
5233 if let Some(ref parent) = node.GetParentNode() {
5234 if parent.is::<HTMLOptGroupElement>() &&
5235 parent.downcast::<Element>().unwrap().disabled_state()
5236 {
5237 self.set_disabled_state(true);
5238 self.set_enabled_state(false);
5239 }
5240 }
5241 }
5242
5243 pub(crate) fn check_disabled_attribute(&self) {
5244 let has_disabled_attrib = self.has_attribute(&local_name!("disabled"));
5245 self.set_disabled_state(has_disabled_attrib);
5246 self.set_enabled_state(!has_disabled_attrib);
5247 }
5248
5249 pub(crate) fn update_read_write_state_from_readonly_attribute(&self) {
5250 let has_readonly_attribute = self.has_attribute(&local_name!("readonly"));
5251 self.set_read_write_state(has_readonly_attribute);
5252 }
5253}
5254
5255#[derive(Clone, Copy, PartialEq)]
5256pub(crate) enum AttributeMutationReason {
5257 ByCloning,
5258 ByParser,
5259 Directly,
5260}
5261
5262#[derive(Clone, Copy)]
5263pub(crate) enum AttributeMutation<'a> {
5264 Set(Option<&'a AttrValue>, AttributeMutationReason),
5267
5268 Removed,
5271}
5272
5273impl AttributeMutation<'_> {
5274 pub(crate) fn is_removal(&self) -> bool {
5275 match *self {
5276 AttributeMutation::Removed => true,
5277 AttributeMutation::Set(..) => false,
5278 }
5279 }
5280
5281 pub(crate) fn new_value<'b>(&self, attr: &'b Attr) -> Option<Ref<'b, AttrValue>> {
5282 match *self {
5283 AttributeMutation::Set(..) => Some(attr.value()),
5284 AttributeMutation::Removed => None,
5285 }
5286 }
5287}
5288
5289#[derive(JSTraceable, MallocSizeOf)]
5293struct TagName {
5294 #[no_trace]
5295 ptr: DomRefCell<Option<LocalName>>,
5296}
5297
5298impl TagName {
5299 fn new() -> TagName {
5300 TagName {
5301 ptr: DomRefCell::new(None),
5302 }
5303 }
5304
5305 fn or_init<F>(&self, cb: F) -> LocalName
5308 where
5309 F: FnOnce() -> LocalName,
5310 {
5311 match &mut *self.ptr.borrow_mut() {
5312 &mut Some(ref name) => name.clone(),
5313 ptr => {
5314 let name = cb();
5315 *ptr = Some(name.clone());
5316 name
5317 },
5318 }
5319 }
5320
5321 fn clear(&self) {
5324 *self.ptr.borrow_mut() = None;
5325 }
5326}
5327
5328pub(crate) fn reflect_cross_origin_attribute(element: &Element) -> Option<DOMString> {
5330 element
5331 .get_attribute(&local_name!("crossorigin"))
5332 .map(|attribute| {
5333 let value = attribute.value().to_ascii_lowercase();
5334 if value == "anonymous" || value == "use-credentials" {
5335 DOMString::from(value)
5336 } else {
5337 DOMString::from("anonymous")
5338 }
5339 })
5340}
5341
5342pub(crate) fn set_cross_origin_attribute(
5343 cx: &mut JSContext,
5344 element: &Element,
5345 value: Option<DOMString>,
5346) {
5347 match value {
5348 Some(val) => {
5349 element.set_string_attribute(&local_name!("crossorigin"), val, CanGc::from_cx(cx))
5350 },
5351 None => {
5352 element.remove_attribute(&ns!(), &local_name!("crossorigin"), CanGc::from_cx(cx));
5353 },
5354 }
5355}
5356
5357pub(crate) fn reflect_referrer_policy_attribute(element: &Element) -> DOMString {
5359 element
5360 .get_attribute(&local_name!("referrerpolicy"))
5361 .map(|attribute| {
5362 let value = attribute.value().to_ascii_lowercase();
5363 if value == "no-referrer" ||
5364 value == "no-referrer-when-downgrade" ||
5365 value == "same-origin" ||
5366 value == "origin" ||
5367 value == "strict-origin" ||
5368 value == "origin-when-cross-origin" ||
5369 value == "strict-origin-when-cross-origin" ||
5370 value == "unsafe-url"
5371 {
5372 DOMString::from(value)
5373 } else {
5374 DOMString::new()
5375 }
5376 })
5377 .unwrap_or_default()
5378}
5379
5380pub(crate) fn referrer_policy_for_element(element: &Element) -> ReferrerPolicy {
5381 element
5382 .get_attribute(&local_name!("referrerpolicy"))
5383 .map(|attribute| ReferrerPolicy::from(&**attribute.value()))
5384 .unwrap_or(element.owner_document().get_referrer_policy())
5385}
5386
5387pub(crate) fn cors_setting_for_element(element: &Element) -> Option<CorsSettings> {
5388 element
5389 .get_attribute(&local_name!("crossorigin"))
5390 .map(|attribute| CorsSettings::from_enumerated_attribute(&attribute.value()))
5391}
5392
5393pub(crate) fn cors_settings_attribute_credential_mode(element: &Element) -> CredentialsMode {
5395 element
5396 .get_attribute(&local_name!("crossorigin"))
5397 .map(|attr| {
5398 if attr.value().eq_ignore_ascii_case("use-credentials") {
5399 CredentialsMode::Include
5400 } else {
5401 CredentialsMode::CredentialsSameOrigin
5403 }
5404 })
5405 .unwrap_or(CredentialsMode::CredentialsSameOrigin)
5407}
5408
5409pub(crate) fn is_element_affected_by_legacy_background_presentational_hint(
5410 namespace: &Namespace,
5411 local_name: &LocalName,
5412) -> bool {
5413 *namespace == ns!(html) &&
5414 matches!(
5415 *local_name,
5416 local_name!("body") |
5417 local_name!("table") |
5418 local_name!("thead") |
5419 local_name!("tbody") |
5420 local_name!("tfoot") |
5421 local_name!("tr") |
5422 local_name!("td") |
5423 local_name!("th")
5424 )
5425}