1use std::borrow::Cow;
8use std::cell::{Cell, LazyCell};
9use std::default::Default;
10use std::rc::Rc;
11use std::str::FromStr;
12use std::sync::atomic::{AtomicUsize, Ordering};
13use std::{fmt, mem};
14
15use app_units::Au;
16use cssparser::match_ignore_ascii_case;
17use devtools_traits::{AttrInfo, DomMutation, ScriptToDevtoolsControlMsg};
18use dom_struct::dom_struct;
19use euclid::Rect;
20use html5ever::serialize::TraversalScope;
21use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
22use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns};
23use js::context::JSContext;
24use js::jsapi::Heap;
25use js::jsval::JSVal;
26use js::rust::HandleObject;
27use layout_api::{LayoutDamage, QueryMsg, ScrollContainerQueryFlags, StyleData, with_layout_state};
28use net_traits::ReferrerPolicy;
29use net_traits::request::{CorsSettings, CredentialsMode};
30use script_bindings::cell::{DomRefCell, Ref, RefMut};
31use script_bindings::reflector::DomObject;
32use selectors::attr::CaseSensitivity;
33use selectors::matching::ElementSelectorFlags;
34use selectors::sink::Push;
35use servo_arc::Arc as ServoArc;
36use style::applicable_declarations::ApplicableDeclarationBlock;
37use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto};
38use style::context::QuirksMode;
39use style::invalidation::element::restyle_hints::RestyleHint;
40use style::properties::longhands::{
41 self, background_image, border_spacing, color, font_family, font_size,
42};
43use style::properties::{
44 ComputedValues, Importance, PropertyDeclaration, PropertyDeclarationBlock,
45 parse_style_attribute,
46};
47use style::rule_tree::{CascadeLevel, CascadeOrigin};
48use style::selector_parser::{RestyleDamage, SelectorParser, Snapshot};
49use style::shared_lock::Locked;
50use style::stylesheets::layer_rule::LayerOrder;
51use style::stylesheets::{CssRuleType, UrlExtraData};
52use style::values::computed::Overflow;
53use style::values::generics::NonNegative;
54use style::values::generics::position::PreferredRatio;
55use style::values::generics::ratio::Ratio;
56use style::values::{AtomIdent, CSSFloat, GenericAtomIdent, computed, specified};
57use style::{ArcSlice, CaseSensitivityExt, dom_apis, thread_state};
58use style_traits::CSSPixel;
59use stylo_atoms::Atom;
60use stylo_dom::ElementState;
61use xml5ever::serialize::TraversalScope::{
62 ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode,
63};
64
65use crate::conversions::Convert;
66use crate::dom::activation::Activatable;
67use crate::dom::attr::{Attr, is_relevant_attribute};
68use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
69use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
70use crate::dom::bindings::codegen::Bindings::ElementBinding::{
71 ElementMethods, GetHTMLOptions, ScrollIntoViewContainer, ScrollLogicalPosition, ShadowRootInit,
72};
73use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
74use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
75use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
76use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
77use crate::dom::bindings::codegen::Bindings::SanitizerBinding::SetHTMLOptions;
78use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
79 ShadowRootMethods, ShadowRootMode, SlotAssignmentMode,
80};
81use crate::dom::bindings::codegen::Bindings::WindowBinding::{
82 ScrollBehavior, ScrollToOptions, WindowMethods,
83};
84use crate::dom::bindings::codegen::UnionTypes::{
85 BooleanOrScrollIntoViewOptions, NodeOrString, TrustedHTMLOrNullIsEmptyString,
86 TrustedHTMLOrString,
87 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString,
88};
89use crate::dom::bindings::conversions::DerivedFrom;
90use crate::dom::bindings::domname::{
91 self, is_valid_attribute_local_name, namespace_from_domstring,
92};
93use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
94use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
95use crate::dom::bindings::num::Finite;
96use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout};
97use crate::dom::bindings::str::DOMString;
98use crate::dom::create::create_element;
99use crate::dom::csp::{CspReporting, InlineCheckType, SourcePosition};
100use crate::dom::customelementregistry::{
101 CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementRegistry,
102 CustomElementState, is_valid_custom_element_name,
103};
104use crate::dom::document::Document;
105use crate::dom::documentfragment::DocumentFragment;
106use crate::dom::domrect::DOMRect;
107use crate::dom::domrectlist::DOMRectList;
108use crate::dom::domtokenlist::DOMTokenList;
109use crate::dom::element::attributes::storage::{
110 AttrRef, AttrValueRef, AttributeEntry, AttributeStorage, ContentAttributeData,
111};
112use crate::dom::elementinternals::ElementInternals;
113use crate::dom::globalscope::GlobalScope;
114use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
115use crate::dom::html::htmlareaelement::HTMLAreaElement;
116use crate::dom::html::htmlbodyelement::HTMLBodyElement;
117use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
118use crate::dom::html::htmlcollection::HTMLCollection;
119use crate::dom::html::htmlelement::HTMLElement;
120use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
121use crate::dom::html::htmlfontelement::HTMLFontElement;
122use crate::dom::html::htmlformelement::FormControlElementHelpers;
123use crate::dom::html::htmlhrelement::{HTMLHRElement, SizePresentationalHint};
124use crate::dom::html::htmliframeelement::HTMLIFrameElement;
125use crate::dom::html::htmlimageelement::HTMLImageElement;
126use crate::dom::html::htmllabelelement::HTMLLabelElement;
127use crate::dom::html::htmllegendelement::HTMLLegendElement;
128use crate::dom::html::htmllinkelement::HTMLLinkElement;
129use crate::dom::html::htmlobjectelement::HTMLObjectElement;
130use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
131use crate::dom::html::htmloutputelement::HTMLOutputElement;
132use crate::dom::html::htmlscriptelement::HTMLScriptElement;
133use crate::dom::html::htmlselectelement::HTMLSelectElement;
134use crate::dom::html::htmlslotelement::{HTMLSlotElement, Slottable};
135use crate::dom::html::htmlstyleelement::HTMLStyleElement;
136use crate::dom::html::htmltablecellelement::HTMLTableCellElement;
137use crate::dom::html::htmltablecolelement::HTMLTableColElement;
138use crate::dom::html::htmltableelement::HTMLTableElement;
139use crate::dom::html::htmltablerowelement::HTMLTableRowElement;
140use crate::dom::html::htmltablesectionelement::HTMLTableSectionElement;
141use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
142use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
143use crate::dom::html::htmlvideoelement::HTMLVideoElement;
144use crate::dom::input_element::HTMLInputElement;
145use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserverRegistration};
146use crate::dom::mutationobserver::{Mutation, MutationObserver};
147use crate::dom::namednodemap::NamedNodeMap;
148use crate::dom::node::{
149 BindContext, ChildrenMutation, CloneChildrenFlag, IsShadowTree, Node, NodeDamage, NodeFlags,
150 NodeTraits, ShadowIncluding, UnbindContext,
151};
152use crate::dom::nodelist::NodeList;
153use crate::dom::promise::Promise;
154use crate::dom::range::Range;
155use crate::dom::raredata::ElementRareData;
156use crate::dom::sanitizer::Sanitizer;
157use crate::dom::scrolling_box::{ScrollAxisState, ScrollingBox};
158use crate::dom::servoparser::ServoParser;
159use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
160use crate::dom::svg::svgsvgelement::SVGSVGElement;
161use crate::dom::text::Text;
162use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
163use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
164use crate::dom::validation::Validatable;
165use crate::dom::validitystate::ValidationFlags;
166use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
167use crate::layout_dom::ServoDangerousStyleElement;
168use crate::realms::enter_auto_realm;
169use crate::script_runtime::CanGc;
170use crate::script_thread::ScriptThread;
171use crate::stylesheet_loader::StylesheetOwner;
172
173#[dom_struct]
179pub struct Element {
180 node: Node,
181 #[no_trace]
182 local_name: LocalName,
183 tag_name: TagName,
184 #[no_trace]
185 namespace: Namespace,
186 #[no_trace]
187 prefix: DomRefCell<Option<Prefix>>,
188 attrs: AttributeStorage,
189 #[no_trace]
190 id_attribute: DomRefCell<Option<Atom>>,
191 #[no_trace]
193 is: DomRefCell<Option<LocalName>>,
194 #[conditional_malloc_size_of]
195 #[no_trace]
196 style_attribute: DomRefCell<Option<ServoArc<Locked<PropertyDeclarationBlock>>>>,
197 attr_list: MutNullableDom<NamedNodeMap>,
198 class_list: MutNullableDom<DOMTokenList>,
199 #[no_trace]
200 state: Cell<ElementState>,
201 selector_flags: AtomicUsize,
204 rare_data: DomRefCell<Option<Box<ElementRareData>>>,
205
206 #[no_trace]
209 style_data: DomRefCell<Option<Box<StyleData>>>,
210}
211
212impl fmt::Debug for Element {
213 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214 write!(f, "<{}", self.local_name)?;
215 if let Some(ref id) = *self.id_attribute.borrow() {
216 write!(f, " id={}", id)?;
217 }
218 write!(f, ">")
219 }
220}
221
222#[derive(MallocSizeOf, PartialEq)]
223pub(crate) enum ElementCreator {
224 ParserCreated(u64),
225 ScriptCreated,
226}
227
228pub(crate) enum CustomElementCreationMode {
229 Synchronous,
230 Asynchronous,
231}
232
233impl ElementCreator {
234 pub(crate) fn is_parser_created(&self) -> bool {
235 match *self {
236 ElementCreator::ParserCreated(_) => true,
237 ElementCreator::ScriptCreated => false,
238 }
239 }
240 pub(crate) fn return_line_number(&self) -> u64 {
241 match *self {
242 ElementCreator::ParserCreated(l) => l,
243 ElementCreator::ScriptCreated => 1,
244 }
245 }
246}
247
248pub(crate) enum AdjacentPosition {
249 BeforeBegin,
250 AfterEnd,
251 AfterBegin,
252 BeforeEnd,
253}
254
255impl FromStr for AdjacentPosition {
256 type Err = Error;
257
258 fn from_str(position: &str) -> Result<Self, Self::Err> {
259 match_ignore_ascii_case! { position,
260 "beforebegin" => Ok(AdjacentPosition::BeforeBegin),
261 "afterbegin" => Ok(AdjacentPosition::AfterBegin),
262 "beforeend" => Ok(AdjacentPosition::BeforeEnd),
263 "afterend" => Ok(AdjacentPosition::AfterEnd),
264 _ => Err(Error::Syntax(None))
265 }
266 }
267}
268
269impl Element {
273 pub(crate) fn create(
274 cx: &mut JSContext,
275 name: QualName,
276 is: Option<LocalName>,
277 document: &Document,
278 creator: ElementCreator,
279 mode: CustomElementCreationMode,
280 proto: Option<HandleObject>,
281 ) -> DomRoot<Element> {
282 create_element(cx, name, is, document, creator, mode, proto)
283 }
284
285 pub(crate) fn new_inherited(
286 local_name: LocalName,
287 namespace: Namespace,
288 prefix: Option<Prefix>,
289 document: &Document,
290 ) -> Element {
291 Element::new_inherited_with_state(
292 ElementState::empty(),
293 local_name,
294 namespace,
295 prefix,
296 document,
297 )
298 }
299
300 pub(crate) fn new_inherited_with_state(
301 state: ElementState,
302 local_name: LocalName,
303 namespace: Namespace,
304 prefix: Option<Prefix>,
305 document: &Document,
306 ) -> Element {
307 Element {
308 node: Node::new_inherited(document),
309 local_name,
310 tag_name: TagName::new(),
311 namespace,
312 prefix: DomRefCell::new(prefix),
313 attrs: Default::default(),
314 id_attribute: DomRefCell::new(None),
315 is: DomRefCell::new(None),
316 style_attribute: DomRefCell::new(None),
317 attr_list: Default::default(),
318 class_list: Default::default(),
319 state: Cell::new(state),
320 selector_flags: Default::default(),
321 rare_data: Default::default(),
322 style_data: Default::default(),
323 }
324 }
325
326 pub(crate) fn set_had_duplicate_attributes(&self) {
327 self.ensure_rare_data().had_duplicate_attributes = true;
328 }
329
330 pub(crate) fn new(
331 cx: &mut JSContext,
332 local_name: LocalName,
333 namespace: Namespace,
334 prefix: Option<Prefix>,
335 document: &Document,
336 proto: Option<HandleObject>,
337 ) -> DomRoot<Element> {
338 Node::reflect_node_with_proto(
339 cx,
340 Box::new(Element::new_inherited(
341 local_name, namespace, prefix, document,
342 )),
343 document,
344 proto,
345 )
346 }
347
348 fn rare_data(&self) -> Ref<'_, Option<Box<ElementRareData>>> {
349 self.rare_data.borrow()
350 }
351
352 fn rare_data_mut(&self) -> RefMut<'_, Option<Box<ElementRareData>>> {
353 self.rare_data.borrow_mut()
354 }
355
356 fn ensure_rare_data(&self) -> RefMut<'_, Box<ElementRareData>> {
357 let mut rare_data = self.rare_data.borrow_mut();
358 if rare_data.is_none() {
359 *rare_data = Some(Default::default());
360 }
361 RefMut::map(rare_data, |rare_data| rare_data.as_mut().unwrap())
362 }
363
364 pub(crate) fn clean_up_style_data(&self) {
365 self.style_data.borrow_mut().take();
366 }
367
368 pub(crate) fn restyle(&self, damage: NodeDamage) {
369 let doc = self.node.owner_doc();
370 let mut restyle = doc.ensure_pending_restyle(self);
371
372 restyle.hint.insert(RestyleHint::RESTYLE_SELF);
375
376 match damage {
377 NodeDamage::Style => {},
378 NodeDamage::ContentOrHeritage => {
379 doc.note_node_with_dirty_descendants(self.upcast());
380 restyle
381 .damage
382 .insert(LayoutDamage::descendant_has_box_damage());
383 },
384 NodeDamage::Other => {
385 doc.note_node_with_dirty_descendants(self.upcast());
386 restyle.damage.insert(RestyleDamage::reconstruct());
387 },
388 }
389 }
390
391 pub(crate) fn set_is(&self, is: LocalName) {
392 *self.is.borrow_mut() = Some(is);
393 }
394
395 pub(crate) fn get_is(&self) -> Option<LocalName> {
397 self.is.borrow().clone()
398 }
399
400 pub(crate) fn set_initial_custom_element_state_to_uncustomized(&self) {
409 let mut state = self.state.get();
410 state.insert(ElementState::DEFINED);
411 self.state.set(state);
412 }
413
414 pub(crate) fn set_custom_element_state(&self, state: CustomElementState) {
416 if state != CustomElementState::Uncustomized {
418 self.ensure_rare_data().custom_element_state = state;
419 }
420
421 let in_defined_state = matches!(
422 state,
423 CustomElementState::Uncustomized | CustomElementState::Custom
424 );
425 self.set_state(ElementState::DEFINED, in_defined_state)
426 }
427
428 pub(crate) fn get_custom_element_state(&self) -> CustomElementState {
429 if let Some(rare_data) = self.rare_data().as_ref() {
430 return rare_data.custom_element_state;
431 }
432 CustomElementState::Uncustomized
433 }
434
435 pub(crate) fn is_custom(&self) -> bool {
437 self.get_custom_element_state() == CustomElementState::Custom
438 }
439
440 pub(crate) fn set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>) {
441 self.ensure_rare_data().custom_element_definition = Some(definition);
442 }
443
444 pub(crate) fn get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>> {
445 self.rare_data().as_ref()?.custom_element_definition.clone()
446 }
447
448 pub(crate) fn clear_custom_element_definition(&self) {
449 self.ensure_rare_data().custom_element_definition = None;
450 }
451
452 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
453 pub(crate) fn push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>) {
454 self.ensure_rare_data()
455 .custom_element_reaction_queue
456 .push(CustomElementReaction::Callback(function, args));
457 }
458
459 pub(crate) fn push_upgrade_reaction(&self, definition: Rc<CustomElementDefinition>) {
460 self.ensure_rare_data()
461 .custom_element_reaction_queue
462 .push(CustomElementReaction::Upgrade(definition));
463 }
464
465 pub(crate) fn clear_reaction_queue(&self) {
466 if let Some(ref mut rare_data) = *self.rare_data_mut() {
467 rare_data.custom_element_reaction_queue.clear();
468 }
469 }
470
471 pub(crate) fn invoke_reactions(&self, cx: &mut JSContext) {
472 loop {
473 rooted_vec!(let mut reactions);
474 match *self.rare_data_mut() {
475 Some(ref mut data) => {
476 mem::swap(&mut *reactions, &mut data.custom_element_reaction_queue)
477 },
478 None => break,
479 };
480
481 if reactions.is_empty() {
482 break;
483 }
484
485 for reaction in reactions.iter() {
486 reaction.invoke(cx, self);
487 }
488
489 reactions.clear();
490 }
491 }
492
493 pub(crate) fn has_css_layout_box(&self) -> bool {
495 self.style()
496 .is_some_and(|s| !s.get_box().clone_display().is_none())
497 }
498
499 pub(crate) fn is_potentially_scrollable_body(&self) -> bool {
501 self.is_potentially_scrollable_body_shared_logic(false)
502 }
503
504 pub(crate) fn is_potentially_scrollable_body_for_scrolling_element(&self) -> bool {
506 self.is_potentially_scrollable_body_shared_logic(true)
507 }
508
509 fn is_potentially_scrollable_body_shared_logic(
511 &self,
512 treat_overflow_clip_on_parent_as_hidden: bool,
513 ) -> bool {
514 let node = self.upcast::<Node>();
515 debug_assert!(
516 node.owner_doc().GetBody().as_deref() == self.downcast::<HTMLElement>(),
517 "Called is_potentially_scrollable_body on element that is not the <body>"
518 );
519
520 if !self.has_css_layout_box() {
524 return false;
525 }
526
527 if let Some(parent) = node.GetParentElement() &&
530 let Some(style) = parent.style()
531 {
532 let mut overflow_x = style.get_box().clone_overflow_x();
533 let mut overflow_y = style.get_box().clone_overflow_y();
534
535 if treat_overflow_clip_on_parent_as_hidden {
538 if overflow_x == Overflow::Clip {
539 overflow_x = Overflow::Hidden;
540 }
541 if overflow_y == Overflow::Clip {
542 overflow_y = Overflow::Hidden;
543 }
544 }
545
546 if !overflow_x.is_scrollable() && !overflow_y.is_scrollable() {
547 return false;
548 }
549 };
550
551 if let Some(style) = self.style() &&
554 !style.get_box().clone_overflow_x().is_scrollable() &&
555 !style.get_box().clone_overflow_y().is_scrollable()
556 {
557 return false;
558 };
559
560 true
561 }
562
563 pub(crate) fn establishes_scroll_container(&self) -> bool {
566 self.upcast::<Node>()
568 .effective_overflow()
569 .is_some_and(|overflow| overflow.establishes_scroll_container())
570 }
571
572 pub(crate) fn has_overflow(&self) -> bool {
573 self.ScrollHeight() > self.ClientHeight() || self.ScrollWidth() > self.ClientWidth()
574 }
575
576 fn has_scrolling_box(&self) -> bool {
584 self.has_css_layout_box() && self.establishes_scroll_container() && self.has_overflow()
585 }
586
587 pub(crate) fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
588 self.rare_data()
589 .as_ref()?
590 .shadow_root
591 .as_ref()
592 .map(|sr| DomRoot::from_ref(&**sr))
593 }
594
595 pub(crate) fn is_shadow_host(&self) -> bool {
596 self.shadow_root().is_some()
597 }
598
599 #[allow(clippy::too_many_arguments)]
601 pub(crate) fn attach_shadow(
602 &self,
603 cx: &mut JSContext,
604 is_ua_widget: IsUserAgentWidget,
605 mode: ShadowRootMode,
606 clonable: bool,
607 serializable: bool,
608 delegates_focus: bool,
609 slot_assignment_mode: SlotAssignmentMode,
610 ) -> Fallible<DomRoot<ShadowRoot>> {
611 if self.namespace != ns!(html) {
614 return Err(Error::NotSupported(Some(
615 "Cannot attach shadow roots to elements with non-HTML namespaces".to_owned(),
616 )));
617 }
618
619 if !is_valid_shadow_host_name(self.local_name()) {
622 if is_ua_widget != IsUserAgentWidget::Yes {
624 let error_message = format!(
625 "Cannot attach shadow roots to <{}> elements",
626 *self.local_name()
627 );
628 return Err(Error::NotSupported(Some(error_message)));
629 }
630 }
631
632 if is_valid_custom_element_name(self.local_name()) || self.get_is().is_some() {
635 let definition = self.get_custom_element_definition();
639 if definition.is_some_and(|definition| definition.disable_shadow) {
642 let error_message = format!(
643 "The custom element constructor of <{}> disabled attachment of shadow roots",
644 self.local_name()
645 );
646 return Err(Error::NotSupported(Some(error_message)));
647 }
648 }
649
650 if let Some(current_shadow_root) = self.shadow_root() {
653 if !current_shadow_root.is_declarative() ||
657 current_shadow_root.shadow_root_mode() != mode
658 {
659 return Err(Error::NotSupported(Some(
660 "Cannot attach a second shadow root to the same element".into(),
661 )));
662 }
663
664 for child in current_shadow_root.upcast::<Node>().children() {
666 child.remove_self(cx);
667 }
668
669 current_shadow_root.set_declarative(false);
671
672 return Ok(current_shadow_root);
674 }
675
676 let shadow_root = ShadowRoot::new(
683 self,
684 &self.node.owner_doc(),
685 mode,
686 slot_assignment_mode,
687 clonable,
688 is_ua_widget,
689 CanGc::from_cx(cx),
690 );
691
692 let node = self.upcast::<Node>();
696 if node.is_connected() {
697 node.remove_style_and_layout_data_from_subtree(cx.no_gc());
698 }
699 shadow_root.set_delegates_focus(delegates_focus);
701
702 if matches!(
705 self.get_custom_element_state(),
706 CustomElementState::Precustomized | CustomElementState::Custom
707 ) {
708 shadow_root.set_available_to_element_internals(true);
709 }
710
711 shadow_root.set_declarative(false);
713
714 shadow_root.set_serializable(serializable);
716
717 self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
719 shadow_root
720 .upcast::<Node>()
721 .set_containing_shadow_root(Some(&shadow_root));
722
723 let bind_context = BindContext::new(self.upcast(), IsShadowTree::Yes);
724 shadow_root.bind_to_tree(cx, &bind_context);
725
726 node.dirty(NodeDamage::Other);
727
728 Ok(shadow_root)
729 }
730
731 pub(crate) fn attach_ua_shadow_root(
747 &self,
748 cx: &mut JSContext,
749 use_ua_widget_styling: bool,
750 ) -> DomRoot<ShadowRoot> {
751 let root = self
752 .attach_shadow(
753 cx,
754 IsUserAgentWidget::Yes,
755 ShadowRootMode::Closed,
756 false,
757 false,
758 false,
759 SlotAssignmentMode::Manual,
760 )
761 .expect("Attaching UA shadow root failed");
762
763 root.upcast::<Node>()
764 .set_in_ua_widget(use_ua_widget_styling);
765 root
766 }
767
768 pub(crate) fn is_translate_enabled(&self) -> bool {
770 let name = &local_name!("translate");
771 if self.has_attribute(name) {
772 let attribute = self.get_string_attribute(name);
773 match_ignore_ascii_case! { &*attribute.str(),
774 "yes" | "" => return true,
775 "no" => return false,
776 _ => {},
777 }
778 }
779 if let Some(parent) = self.upcast::<Node>().GetParentNode() &&
780 let Some(elem) = parent.downcast::<Element>()
781 {
782 return elem.is_translate_enabled();
783 }
784 true
785 }
786
787 pub(crate) fn directionality(&self) -> String {
789 self.downcast::<HTMLElement>()
790 .and_then(|html_element| html_element.directionality())
791 .unwrap_or_else(|| {
792 let node = self.upcast::<Node>();
793 node.parent_directionality()
794 })
795 }
796
797 pub(crate) fn is_root(&self) -> bool {
798 match self.node.GetParentNode() {
799 None => false,
800 Some(node) => node.is::<Document>(),
801 }
802 }
803
804 pub(crate) fn registered_intersection_observers_mut(
807 &self,
808 ) -> RefMut<'_, Vec<IntersectionObserverRegistration>> {
809 RefMut::map(self.ensure_rare_data(), |rare_data| {
810 &mut rare_data.registered_intersection_observers
811 })
812 }
813
814 pub(crate) fn registered_intersection_observers(
815 &self,
816 ) -> Option<Ref<'_, Vec<IntersectionObserverRegistration>>> {
817 let rare_data: Ref<'_, _> = self.rare_data.borrow();
818
819 if rare_data.is_none() {
820 return None;
821 }
822 Some(Ref::map(rare_data, |rare_data| {
823 &rare_data
824 .as_ref()
825 .unwrap()
826 .registered_intersection_observers
827 }))
828 }
829
830 pub(crate) fn get_intersection_observer_registration(
831 &self,
832 observer: &IntersectionObserver,
833 ) -> Option<Ref<'_, IntersectionObserverRegistration>> {
834 if let Some(registrations) = self.registered_intersection_observers() {
835 registrations
836 .iter()
837 .position(|reg_obs| reg_obs.observer == observer)
838 .map(|index| Ref::map(registrations, |registrations| ®istrations[index]))
839 } else {
840 None
841 }
842 }
843
844 pub(crate) fn add_initial_intersection_observer_registration(
846 &self,
847 observer: &IntersectionObserver,
848 ) {
849 self.ensure_rare_data()
850 .registered_intersection_observers
851 .push(IntersectionObserverRegistration::new_initial(observer));
852 }
853
854 pub(crate) fn remove_intersection_observer(&self, observer: &IntersectionObserver) {
856 self.ensure_rare_data()
857 .registered_intersection_observers
858 .retain(|reg_obs| *reg_obs.observer != *observer)
859 }
860
861 pub(crate) fn scrolling_box(&self, flags: ScrollContainerQueryFlags) -> Option<ScrollingBox> {
864 self.owner_window()
865 .scrolling_box_query(Some(self.upcast()), flags)
866 }
867
868 pub(crate) fn scroll_into_view_with_options(
870 &self,
871 cx: &mut JSContext,
872 behavior: ScrollBehavior,
873 block: ScrollAxisState,
874 inline: ScrollAxisState,
875 container: Option<&Element>,
876 inner_target_rect: Option<Rect<Au, CSSPixel>>,
877 ) {
878 let get_target_rect = || match inner_target_rect {
879 None => self.upcast::<Node>().border_box().unwrap_or_default(),
880 Some(inner_target_rect) => inner_target_rect.translate(
881 self.upcast::<Node>()
882 .content_box()
883 .unwrap_or_default()
884 .origin
885 .to_vector(),
886 ),
887 };
888
889 let mut parent_scrolling_box = self.scrolling_box(ScrollContainerQueryFlags::empty());
892 while let Some(scrolling_box) = parent_scrolling_box {
893 parent_scrolling_box = scrolling_box.parent();
894
895 let position =
906 scrolling_box.determine_scroll_into_view_position(block, inline, get_target_rect());
907
908 if position != scrolling_box.scroll_position() {
913 scrolling_box.scroll_to(cx, position, behavior);
924 }
925
926 if container.is_some_and(|container| {
930 let container_node = container.upcast::<Node>();
931 scrolling_box
932 .node()
933 .is_shadow_including_inclusive_ancestor_of(container_node)
934 }) {
935 return;
936 }
937 }
938
939 let window_proxy = self.owner_window().window_proxy();
940 let Some(frame_element) = window_proxy.frame_element() else {
941 return;
942 };
943
944 let inner_target_rect = Some(get_target_rect());
945
946 let mut realm = enter_auto_realm(cx, frame_element);
947 let cx = &mut realm;
948
949 frame_element.scroll_into_view_with_options(
950 cx,
951 behavior,
952 block,
953 inline,
954 None,
955 inner_target_rect,
956 )
957 }
958
959 pub(crate) fn ensure_contenteditable_selection_range(
960 &self,
961 document: &Document,
962 can_gc: CanGc,
963 ) -> DomRoot<Range> {
964 self.ensure_rare_data()
965 .contenteditable_selection_range
966 .or_init(|| Range::new_with_doc(document, None, can_gc))
967 }
968
969 pub(crate) fn handle_scroll_event(&self) {
974 let document = self.owner_document();
976
977 document.finish_handle_scroll_event(self.upcast());
986 }
987
988 pub(crate) fn style(&self) -> Option<ServoArc<ComputedValues>> {
989 self.owner_window().layout_reflow(QueryMsg::StyleQuery);
990 self.style_data
991 .borrow()
992 .as_ref()
993 .map(|data| data.element_data.borrow().styles.primary().clone())
994 }
995
996 pub(crate) fn is_styled(&self) -> bool {
997 self.style_data.borrow().is_some()
998 }
999
1000 pub(crate) fn is_display_none(&self) -> bool {
1001 self.style_data.borrow().as_ref().is_none_or(|data| {
1002 data.element_data
1003 .borrow()
1004 .styles
1005 .primary()
1006 .get_box()
1007 .display
1008 .is_none()
1009 })
1010 }
1011}
1012
1013#[inline]
1015pub(crate) fn is_valid_shadow_host_name(name: &LocalName) -> bool {
1016 if is_valid_custom_element_name(name) {
1019 return true;
1020 }
1021
1022 matches!(
1025 name,
1026 &local_name!("article") |
1027 &local_name!("aside") |
1028 &local_name!("blockquote") |
1029 &local_name!("body") |
1030 &local_name!("div") |
1031 &local_name!("footer") |
1032 &local_name!("h1") |
1033 &local_name!("h2") |
1034 &local_name!("h3") |
1035 &local_name!("h4") |
1036 &local_name!("h5") |
1037 &local_name!("h6") |
1038 &local_name!("header") |
1039 &local_name!("main") |
1040 &local_name!("nav") |
1041 &local_name!("p") |
1042 &local_name!("section") |
1043 &local_name!("span")
1044 )
1045}
1046
1047#[inline]
1048#[expect(unsafe_code)]
1049pub(crate) fn get_attr_for_layout<'dom>(
1050 elem: LayoutDom<'dom, Element>,
1051 namespace: &Namespace,
1052 name: &LocalName,
1053) -> Option<&'dom AttrValue> {
1054 let storage = unsafe { elem.unsafe_get().attrs.borrow_for_layout() };
1055 storage
1056 .iter()
1057 .find(|e: &&AttributeEntry| {
1058 name == e.local_name_for_layout() && namespace == e.namespace_for_layout()
1059 })
1060 .map(|e: &AttributeEntry| e.value_for_layout())
1061}
1062
1063impl<'dom> LayoutDom<'dom, Element> {
1064 #[inline]
1065 pub(crate) fn is_root(&self) -> bool {
1066 self.upcast::<Node>()
1067 .parent_node_ref()
1068 .is_some_and(|parent| matches!(parent.type_id_for_layout(), NodeTypeId::Document(_)))
1069 }
1070
1071 pub(crate) fn is_body_element_of_html_element_root(&self) -> bool {
1073 if self.local_name() != &local_name!("body") {
1074 return false;
1075 }
1076 let Some(parent_node) = self.upcast::<Node>().parent_node_ref() else {
1077 return false;
1078 };
1079 let Some(parent_element) = parent_node.downcast::<Element>() else {
1080 return false;
1081 };
1082 parent_element.local_name() == &local_name!("html")
1083 }
1084
1085 #[expect(unsafe_code)]
1087 #[inline]
1088 pub(crate) fn each_attr_name_for_layout<F>(self, mut callback: F)
1089 where
1090 F: FnMut(&LocalName),
1091 {
1092 let storage = unsafe { self.unsafe_get().attrs.borrow_for_layout() };
1093 for entry in storage.iter() {
1094 callback(entry.local_name_for_layout());
1095 }
1096 }
1097
1098 #[inline]
1099 pub(crate) fn has_class_or_part_for_layout(
1100 self,
1101 name: &AtomIdent,
1102 attr_name: &LocalName,
1103 case_sensitivity: CaseSensitivity,
1104 ) -> bool {
1105 get_attr_for_layout(self, &ns!(), attr_name).is_some_and(|attr| {
1106 attr.as_tokens()
1107 .iter()
1108 .any(|atom| case_sensitivity.eq_atom(atom, name))
1109 })
1110 }
1111
1112 #[inline]
1113 pub(crate) fn get_classes_for_layout(self) -> Option<&'dom [Atom]> {
1114 get_attr_for_layout(self, &ns!(), &local_name!("class")).map(|attr| attr.as_tokens())
1115 }
1116
1117 pub(crate) fn get_parts_for_layout(self) -> Option<&'dom [Atom]> {
1118 get_attr_for_layout(self, &ns!(), &local_name!("part")).map(|attr| attr.as_tokens())
1119 }
1120
1121 #[inline]
1122 #[expect(unsafe_code)]
1123 pub(crate) fn style_data(self) -> Option<&'dom StyleData> {
1124 unsafe { self.unsafe_get().style_data.borrow_for_layout().as_deref() }
1125 }
1126
1127 #[inline]
1128 #[expect(unsafe_code)]
1129 pub(crate) unsafe fn initialize_style_data(self) {
1130 let data = unsafe { self.unsafe_get().style_data.borrow_mut_for_layout() };
1131 debug_assert!(data.is_none());
1132 *data = Some(Box::default());
1133 }
1134
1135 #[inline]
1136 #[expect(unsafe_code)]
1137 pub(crate) unsafe fn clear_style_data(self) {
1138 unsafe {
1139 self.unsafe_get().style_data.borrow_mut_for_layout().take();
1140 }
1141 }
1142
1143 pub(crate) fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
1144 where
1145 V: Push<ApplicableDeclarationBlock>,
1146 {
1147 let document = self.upcast::<Node>().owner_doc_for_layout();
1148 let mut property_declaration_block = None;
1149 let mut push = |declaration| {
1150 property_declaration_block
1151 .get_or_insert_with(PropertyDeclarationBlock::default)
1152 .push(declaration, Importance::Normal);
1153 };
1154
1155 if let Some(lang) = self.get_lang_attr_val_for_layout() {
1158 push(PropertyDeclaration::XLang(specified::XLang(Atom::from(
1159 lang.to_owned(),
1160 ))));
1161 }
1162
1163 let bgcolor = if let Some(this) = self.downcast::<HTMLBodyElement>() {
1164 this.get_background_color()
1165 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1166 this.get_background_color()
1167 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1168 this.get_background_color()
1169 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1170 this.get_background_color()
1171 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1172 this.get_background_color()
1173 } else {
1174 None
1175 };
1176
1177 if let Some(color) = bgcolor {
1178 push(PropertyDeclaration::BackgroundColor(
1179 specified::Color::from_absolute_color(color),
1180 ));
1181 }
1182
1183 if is_element_affected_by_legacy_background_presentational_hint(
1184 self.namespace(),
1185 self.local_name(),
1186 ) && let Some(url) = self
1187 .get_attr_for_layout(&ns!(), &local_name!("background"))
1188 .and_then(AttrValue::as_resolved_url)
1189 .cloned()
1190 {
1191 push(PropertyDeclaration::BackgroundImage(
1192 background_image::SpecifiedValue(vec![specified::Image::for_cascade(url)].into()),
1193 ));
1194 }
1195
1196 let color = if let Some(this) = self.downcast::<HTMLFontElement>() {
1197 this.get_color()
1198 } else if let Some(this) = self.downcast::<HTMLBodyElement>() {
1199 this.get_color()
1201 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1202 this.get_color()
1204 } else {
1205 None
1206 };
1207
1208 if let Some(color) = color {
1209 push(PropertyDeclaration::Color(
1210 longhands::color::SpecifiedValue(specified::Color::from_absolute_color(color)),
1211 ));
1212 }
1213
1214 let font_face = self
1215 .downcast::<HTMLFontElement>()
1216 .and_then(LayoutDom::get_face);
1217 if let Some(font_face) = font_face {
1218 push(PropertyDeclaration::FontFamily(
1219 font_family::SpecifiedValue::Values(computed::font::FontFamilyList {
1220 list: ArcSlice::from_iter(
1221 HTMLFontElement::parse_face_attribute(font_face).into_iter(),
1222 ),
1223 }),
1224 ));
1225 }
1226
1227 let font_size = self
1228 .downcast::<HTMLFontElement>()
1229 .and_then(LayoutDom::get_size);
1230 if let Some(font_size) = font_size {
1231 push(PropertyDeclaration::FontSize(
1232 font_size::SpecifiedValue::from_html_size(font_size as u8),
1233 ));
1234 }
1235
1236 let size = self
1242 .downcast::<HTMLInputElement>()
1243 .and_then(|input_element| {
1244 match self.get_attr_val_for_layout(&ns!(), &local_name!("type")) {
1246 Some("hidden") | Some("range") | Some("color") | Some("checkbox") |
1247 Some("radio") | Some("file") | Some("submit") | Some("image") |
1248 Some("reset") | Some("button") => None,
1249 _ => match input_element.size_for_layout() {
1251 0 => None,
1252 s => Some(s as i32),
1253 },
1254 }
1255 });
1256
1257 if let Some(size) = size {
1258 let value =
1259 specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(size));
1260 push(PropertyDeclaration::Width(
1261 specified::Size::LengthPercentage(NonNegative(
1262 specified::LengthPercentage::Length(value),
1263 )),
1264 ));
1265 }
1266
1267 let width = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1268 this.get_width()
1269 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1270 this.get_width()
1271 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1272 this.get_width()
1273 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1274 this.get_width()
1275 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1276 this.get_width()
1277 } else if let Some(this) = self.downcast::<HTMLTableColElement>() {
1278 this.get_width()
1279 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1280 this.get_width()
1282 } else {
1283 LengthOrPercentageOrAuto::Auto
1284 };
1285
1286 match width {
1288 LengthOrPercentageOrAuto::Auto => {},
1289 LengthOrPercentageOrAuto::Percentage(percentage) => {
1290 let width_value = specified::Size::LengthPercentage(NonNegative(
1291 specified::LengthPercentage::Percentage(computed::Percentage(percentage)),
1292 ));
1293 push(PropertyDeclaration::Width(width_value));
1294 },
1295 LengthOrPercentageOrAuto::Length(length) => {
1296 let width_value = specified::Size::LengthPercentage(NonNegative(
1297 specified::LengthPercentage::Length(specified::NoCalcLength::Absolute(
1298 specified::AbsoluteLength::Px(length.to_f32_px()),
1299 )),
1300 ));
1301 push(PropertyDeclaration::Width(width_value));
1302 },
1303 }
1304
1305 let height = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1306 this.get_height()
1307 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1308 this.get_height()
1309 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1310 this.get_height()
1311 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1312 this.get_height()
1313 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1314 this.get_height()
1315 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1316 this.get_height()
1317 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1318 this.get_height()
1319 } else {
1320 LengthOrPercentageOrAuto::Auto
1321 };
1322
1323 match height {
1324 LengthOrPercentageOrAuto::Auto => {},
1325 LengthOrPercentageOrAuto::Percentage(percentage) => {
1326 let height_value = specified::Size::LengthPercentage(NonNegative(
1327 specified::LengthPercentage::Percentage(computed::Percentage(percentage)),
1328 ));
1329 push(PropertyDeclaration::Height(height_value));
1330 },
1331 LengthOrPercentageOrAuto::Length(length) => {
1332 let height_value = specified::Size::LengthPercentage(NonNegative(
1333 specified::LengthPercentage::Length(specified::NoCalcLength::Absolute(
1334 specified::AbsoluteLength::Px(length.to_f32_px()),
1335 )),
1336 ));
1337 push(PropertyDeclaration::Height(height_value));
1338 },
1339 }
1340
1341 if let Some(this) = self.downcast::<SVGSVGElement>() {
1342 let data = this.data();
1343 if let Some(width) = data.width.and_then(AttrValue::as_length_percentage) {
1344 push(PropertyDeclaration::Width(
1345 specified::Size::LengthPercentage(NonNegative(width.clone())),
1346 ));
1347 }
1348 if let Some(height) = data.height.and_then(AttrValue::as_length_percentage) {
1349 push(PropertyDeclaration::Height(
1350 specified::Size::LengthPercentage(NonNegative(height.clone())),
1351 ));
1352 }
1353 }
1354
1355 if (self.downcast::<HTMLImageElement>().is_some() ||
1358 self.downcast::<HTMLVideoElement>().is_some()) &&
1359 let LengthOrPercentageOrAuto::Length(width) = width &&
1360 let LengthOrPercentageOrAuto::Length(height) = height
1361 {
1362 let width_value = NonNegative(specified::Number::new(width.to_f32_px()));
1363 let height_value = NonNegative(specified::Number::new(height.to_f32_px()));
1364 let aspect_ratio = specified::position::AspectRatio {
1365 auto: true,
1366 ratio: PreferredRatio::Ratio(Ratio(width_value, height_value)),
1367 };
1368 push(PropertyDeclaration::AspectRatio(aspect_ratio));
1369 }
1370
1371 let cols = self
1372 .downcast::<HTMLTextAreaElement>()
1373 .map(LayoutDom::get_cols);
1374 if let Some(cols) = cols {
1375 let cols = cols as i32;
1376 if cols > 0 {
1377 let value =
1383 specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(cols));
1384 push(PropertyDeclaration::Width(
1385 specified::Size::LengthPercentage(NonNegative(
1386 specified::LengthPercentage::Length(value),
1387 )),
1388 ));
1389 }
1390 }
1391
1392 let rows = self
1393 .downcast::<HTMLTextAreaElement>()
1394 .map(LayoutDom::get_rows);
1395 if let Some(rows) = rows {
1396 let rows = rows as i32;
1397 if rows > 0 {
1398 let value = specified::NoCalcLength::FontRelative(
1402 specified::FontRelativeLength::Em(rows as CSSFloat),
1403 );
1404 push(PropertyDeclaration::Height(
1405 specified::Size::LengthPercentage(NonNegative(
1406 specified::LengthPercentage::Length(value),
1407 )),
1408 ));
1409 }
1410 }
1411
1412 if let Some(table) = self.downcast::<HTMLTableElement>() {
1413 if let Some(cellspacing) = table.get_cellspacing() {
1414 let width_value = specified::Length::from_px(cellspacing as f32);
1415 push(PropertyDeclaration::BorderSpacing(Box::new(
1416 border_spacing::SpecifiedValue::new(
1417 width_value.clone().into(),
1418 width_value.into(),
1419 ),
1420 )));
1421 }
1422 if let Some(border) = table.get_border() {
1423 let width_value = specified::BorderSideWidth::from_px(border as f32);
1424 push(PropertyDeclaration::BorderTopWidth(width_value.clone()));
1425 push(PropertyDeclaration::BorderLeftWidth(width_value.clone()));
1426 push(PropertyDeclaration::BorderBottomWidth(width_value.clone()));
1427 push(PropertyDeclaration::BorderRightWidth(width_value));
1428 }
1429 if document.quirks_mode() == QuirksMode::Quirks {
1430 push(PropertyDeclaration::Color(color::SpecifiedValue(
1432 specified::Color::InheritFromBodyQuirk,
1433 )));
1434 }
1435 }
1436
1437 if let Some(cellpadding) = self
1438 .downcast::<HTMLTableCellElement>()
1439 .and_then(|this| this.get_table())
1440 .and_then(|table| table.get_cellpadding())
1441 {
1442 let cellpadding = NonNegative(specified::LengthPercentage::Length(
1443 specified::NoCalcLength::from_px(cellpadding as f32),
1444 ));
1445 push(PropertyDeclaration::PaddingTop(cellpadding.clone()));
1446 push(PropertyDeclaration::PaddingLeft(cellpadding.clone()));
1447 push(PropertyDeclaration::PaddingBottom(cellpadding.clone()));
1448 push(PropertyDeclaration::PaddingRight(cellpadding));
1449 }
1450
1451 if let Some(size_info) = self
1453 .downcast::<HTMLHRElement>()
1454 .and_then(|hr_element| hr_element.get_size_info())
1455 {
1456 match size_info {
1457 SizePresentationalHint::SetHeightTo(height) => {
1458 push(PropertyDeclaration::Height(height));
1459 },
1460 SizePresentationalHint::SetAllBorderWidthValuesTo(border_width) => {
1461 push(PropertyDeclaration::BorderLeftWidth(border_width.clone()));
1462 push(PropertyDeclaration::BorderRightWidth(border_width.clone()));
1463 push(PropertyDeclaration::BorderTopWidth(border_width.clone()));
1464 push(PropertyDeclaration::BorderBottomWidth(border_width));
1465 },
1466 SizePresentationalHint::SetBottomBorderWidthToZero => {
1467 push(PropertyDeclaration::BorderBottomWidth(
1468 specified::border::BorderSideWidth::from_px(0.),
1469 ));
1470 },
1471 }
1472 }
1473
1474 let Some(property_declaration_block) = property_declaration_block else {
1475 return;
1476 };
1477
1478 let shared_lock = &document.shared_style_locks().author;
1479 hints.push(ApplicableDeclarationBlock::from_declarations(
1480 ServoArc::new(shared_lock.wrap(property_declaration_block)),
1481 CascadeLevel::new(CascadeOrigin::PresHints),
1482 LayerOrder::root(),
1483 ));
1484 }
1485
1486 pub(crate) fn get_span(self) -> Option<u32> {
1487 self.downcast::<HTMLTableColElement>()
1489 .and_then(|element| element.get_span())
1490 }
1491
1492 pub(crate) fn get_colspan(self) -> Option<u32> {
1493 self.downcast::<HTMLTableCellElement>()
1495 .and_then(|element| element.get_colspan())
1496 }
1497
1498 pub(crate) fn get_rowspan(self) -> Option<u32> {
1499 self.downcast::<HTMLTableCellElement>()
1501 .and_then(|element| element.get_rowspan())
1502 }
1503
1504 #[inline]
1505 pub(crate) fn is_html_element(&self) -> bool {
1506 *self.namespace() == ns!(html)
1507 }
1508
1509 #[expect(unsafe_code)]
1510 pub(crate) fn id_attribute(self) -> *const Option<Atom> {
1511 unsafe { (self.unsafe_get()).id_attribute.borrow_for_layout() }
1512 }
1513
1514 #[expect(unsafe_code)]
1515 pub(crate) fn style_attribute(
1516 self,
1517 ) -> *const Option<ServoArc<Locked<PropertyDeclarationBlock>>> {
1518 unsafe { (self.unsafe_get()).style_attribute.borrow_for_layout() }
1519 }
1520
1521 pub(crate) fn local_name(self) -> &'dom LocalName {
1522 &(self.unsafe_get()).local_name
1523 }
1524
1525 pub(crate) fn namespace(self) -> &'dom Namespace {
1526 &(self.unsafe_get()).namespace
1527 }
1528
1529 pub(crate) fn get_lang_attr_val_for_layout(self) -> Option<&'dom str> {
1530 if let Some(attr) = self.get_attr_val_for_layout(&ns!(xml), &local_name!("lang")) {
1531 return Some(attr);
1532 }
1533 if let Some(attr) = self.get_attr_val_for_layout(&ns!(), &local_name!("lang")) {
1534 return Some(attr);
1535 }
1536 None
1537 }
1538
1539 pub(crate) fn get_lang_for_layout(self) -> String {
1540 let mut current_node = Some(self.upcast::<Node>());
1541 while let Some(node) = current_node {
1542 current_node = node.composed_parent_node_ref();
1543 match node.downcast::<Element>() {
1544 Some(elem) => {
1545 if let Some(attr) = elem.get_lang_attr_val_for_layout() {
1546 return attr.to_owned();
1547 }
1548 },
1549 None => continue,
1550 }
1551 }
1552 String::new()
1555 }
1556
1557 #[inline]
1558 pub(crate) fn get_state_for_layout(self) -> ElementState {
1559 (self.unsafe_get()).state.get()
1560 }
1561
1562 #[inline]
1563 pub(crate) fn insert_selector_flags(self, flags: ElementSelectorFlags) {
1564 debug_assert!(thread_state::get().is_layout());
1565 self.unsafe_get().insert_selector_flags(flags);
1566 }
1567
1568 #[inline]
1569 pub(crate) fn get_selector_flags(self) -> ElementSelectorFlags {
1570 self.unsafe_get().get_selector_flags()
1571 }
1572
1573 #[inline]
1574 #[expect(unsafe_code)]
1575 pub(crate) fn get_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
1576 unsafe {
1577 self.unsafe_get()
1578 .rare_data
1579 .borrow_for_layout()
1580 .as_ref()?
1581 .shadow_root
1582 .as_ref()
1583 .map(|sr| sr.to_layout())
1584 }
1585 }
1586
1587 #[inline]
1588 pub(crate) fn get_attr_for_layout(
1589 self,
1590 namespace: &Namespace,
1591 name: &LocalName,
1592 ) -> Option<&'dom AttrValue> {
1593 get_attr_for_layout(self, namespace, name)
1594 }
1595
1596 #[inline]
1597 pub(crate) fn get_attr_val_for_layout(
1598 self,
1599 namespace: &Namespace,
1600 name: &LocalName,
1601 ) -> Option<&'dom str> {
1602 get_attr_for_layout(self, namespace, name).map(|attr| &**attr)
1603 }
1604
1605 #[inline]
1606 #[expect(unsafe_code)]
1607 pub(crate) fn get_attr_vals_for_layout(
1608 self,
1609 name: &LocalName,
1610 ) -> impl Iterator<Item = &'dom AttrValue> {
1611 let storage = unsafe { self.unsafe_get().attrs.borrow_for_layout() };
1612 storage
1613 .iter()
1614 .filter(move |e: &&AttributeEntry| name == e.local_name_for_layout())
1615 .map(|e: &AttributeEntry| e.value_for_layout())
1616 }
1617
1618 #[expect(unsafe_code)]
1619 pub(crate) fn each_custom_state_for_layout(self, mut callback: impl FnMut(&AtomIdent)) {
1620 let rare_data = unsafe { self.unsafe_get().rare_data.borrow_for_layout() };
1621 let Some(rare_data) = rare_data.as_ref() else {
1622 return;
1623 };
1624 let Some(element_internals) = rare_data.element_internals.as_ref() else {
1625 return;
1626 };
1627
1628 let element_internals = unsafe { element_internals.to_layout() };
1629 if let Some(states) = element_internals.unsafe_get().custom_states_for_layout() {
1630 for state in unsafe { states.unsafe_get().set_for_layout().iter() } {
1631 callback(&AtomIdent::from(&*state.str()));
1633 }
1634 }
1635 }
1636}
1637
1638impl Element {
1639 pub(crate) fn is_html_element(&self) -> bool {
1640 self.namespace == ns!(html)
1641 }
1642
1643 pub(crate) fn html_element_in_html_document(&self) -> bool {
1644 self.is_html_element() && self.upcast::<Node>().is_in_html_doc()
1645 }
1646
1647 pub(crate) fn local_name(&self) -> &LocalName {
1648 &self.local_name
1649 }
1650
1651 pub(crate) fn parsed_name(&self, mut name: DOMString) -> LocalName {
1652 if self.html_element_in_html_document() {
1653 name.make_ascii_lowercase();
1654 }
1655 LocalName::from(name)
1656 }
1657
1658 pub(crate) fn namespace(&self) -> &Namespace {
1659 &self.namespace
1660 }
1661
1662 pub(crate) fn prefix(&self) -> Ref<'_, Option<Prefix>> {
1663 self.prefix.borrow()
1664 }
1665
1666 pub(crate) fn set_prefix(&self, prefix: Option<Prefix>) {
1667 *self.prefix.borrow_mut() = prefix;
1668 }
1669
1670 pub(crate) fn set_custom_element_registry(
1671 &self,
1672 registry: Option<DomRoot<CustomElementRegistry>>,
1673 ) {
1674 self.ensure_rare_data().custom_element_registry = registry.as_deref().map(Dom::from_ref);
1675 }
1676
1677 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
1678 self.rare_data()
1679 .as_ref()?
1680 .custom_element_registry
1681 .as_deref()
1682 .map(DomRoot::from_ref)
1683 }
1684
1685 pub(crate) fn attrs(&self) -> &AttributeStorage {
1686 &self.attrs
1687 }
1688
1689 pub(crate) fn dom_attrs(&self, cx: &mut JSContext) -> &AttributeStorage {
1690 let len = self.attrs.borrow().len();
1692 for i in 0..len {
1693 self.attrs.ensure_dom(cx, i, self);
1694 }
1695 &self.attrs
1696 }
1697
1698 pub(crate) fn locate_namespace(&self, prefix: Option<DOMString>) -> Namespace {
1700 let namespace_prefix = prefix.clone().map(|s| Prefix::from(&*s.str()));
1701
1702 if namespace_prefix == Some(namespace_prefix!("xml")) {
1704 return ns!(xml);
1705 }
1706
1707 if namespace_prefix == Some(namespace_prefix!("xmlns")) {
1709 return ns!(xmlns);
1710 }
1711
1712 let prefix = prefix.map(LocalName::from);
1713
1714 let inclusive_ancestor_elements = self
1715 .upcast::<Node>()
1716 .inclusive_ancestors(ShadowIncluding::No)
1717 .filter_map(DomRoot::downcast::<Self>);
1718
1719 for element in inclusive_ancestor_elements {
1722 if element.namespace() != &ns!() &&
1724 element.prefix().as_ref().map(|p| &**p) == prefix.as_deref()
1725 {
1726 return element.namespace().clone();
1727 }
1728
1729 let found_ns = element.attrs.borrow().iter().find_map(|attr| {
1734 if attr.namespace() != &ns!(xmlns) {
1735 return None;
1736 }
1737 match (attr.prefix(), prefix.as_ref()) {
1738 (Some(&namespace_prefix!("xmlns")), Some(prefix)) => {
1739 if attr.local_name() == prefix {
1740 Some(Namespace::from(&**attr.value()))
1741 } else {
1742 None
1743 }
1744 },
1745 (None, None) => {
1746 if attr.local_name() == &local_name!("xmlns") {
1747 Some(Namespace::from(&**attr.value()))
1748 } else {
1749 None
1750 }
1751 },
1752 _ => None,
1753 }
1754 });
1755
1756 if let Some(ns) = found_ns {
1757 return ns;
1758 }
1759 }
1760
1761 ns!()
1762 }
1763
1764 pub(crate) fn name_attribute(&self) -> Option<Atom> {
1765 self.rare_data().as_ref()?.name_attribute.clone()
1766 }
1767
1768 pub(crate) fn style_attribute(
1769 &self,
1770 ) -> &DomRefCell<Option<ServoArc<Locked<PropertyDeclarationBlock>>>> {
1771 &self.style_attribute
1772 }
1773
1774 pub(crate) fn summarize(&self) -> Vec<AttrInfo> {
1775 self.attrs
1776 .borrow()
1777 .iter()
1778 .map(|attr| attr.summarize())
1779 .collect()
1780 }
1781
1782 pub(crate) fn is_void(&self) -> bool {
1783 if self.namespace != ns!(html) {
1784 return false;
1785 }
1786 match self.local_name {
1787 local_name!("area") |
1790 local_name!("base") |
1791 local_name!("basefont") |
1792 local_name!("bgsound") |
1793 local_name!("br") |
1794 local_name!("col") |
1795 local_name!("embed") |
1796 local_name!("frame") |
1797 local_name!("hr") |
1798 local_name!("img") |
1799 local_name!("input") |
1800 local_name!("keygen") |
1801 local_name!("link") |
1802 local_name!("meta") |
1803 local_name!("param") |
1804 local_name!("source") |
1805 local_name!("track") |
1806 local_name!("wbr") => true,
1807 _ => false,
1808 }
1809 }
1810
1811 pub(crate) fn root_element(&self) -> DomRoot<Element> {
1812 if self.node.is_in_a_document_tree() {
1813 self.upcast::<Node>()
1814 .owner_doc()
1815 .GetDocumentElement()
1816 .unwrap()
1817 } else {
1818 self.upcast::<Node>()
1819 .inclusive_ancestors(ShadowIncluding::No)
1820 .filter_map(DomRoot::downcast)
1821 .last()
1822 .expect("We know inclusive_ancestors will return `self` which is an element")
1823 }
1824 }
1825
1826 pub(crate) fn lookup_prefix(&self, namespace: Namespace) -> Option<DOMString> {
1828 for node in self
1829 .upcast::<Node>()
1830 .inclusive_ancestors(ShadowIncluding::No)
1831 {
1832 let element = node.downcast::<Element>()?;
1833 if *element.namespace() == namespace &&
1835 let Some(prefix) = element.GetPrefix()
1836 {
1837 return Some(prefix);
1838 }
1839
1840 for attr in element.attrs.borrow().iter() {
1842 if attr.prefix() == Some(&namespace_prefix!("xmlns")) &&
1843 **attr.value() == *namespace
1844 {
1845 return Some(DOMString::from(&**attr.local_name()));
1846 }
1847 }
1848 }
1849 None
1850 }
1851
1852 pub(crate) fn is_document_element(&self) -> bool {
1854 if let Some(document_element) = self.owner_document().GetDocumentElement() {
1855 *document_element == *self
1856 } else {
1857 false
1858 }
1859 }
1860
1861 pub(crate) fn is_active_element(&self) -> bool {
1863 if let Some(active_element) = self.owner_document().GetActiveElement() {
1864 *active_element == *self
1865 } else {
1866 false
1867 }
1868 }
1869
1870 pub(crate) fn is_editing_host(&self) -> bool {
1871 self.downcast::<HTMLElement>()
1872 .is_some_and(|element| element.IsContentEditable())
1873 }
1874
1875 pub(crate) fn is_actually_disabled(&self) -> bool {
1876 let node = self.upcast::<Node>();
1877 match node.type_id() {
1878 NodeTypeId::Element(ElementTypeId::HTMLElement(
1879 HTMLElementTypeId::HTMLButtonElement,
1880 )) |
1881 NodeTypeId::Element(ElementTypeId::HTMLElement(
1882 HTMLElementTypeId::HTMLInputElement,
1883 )) |
1884 NodeTypeId::Element(ElementTypeId::HTMLElement(
1885 HTMLElementTypeId::HTMLSelectElement,
1886 )) |
1887 NodeTypeId::Element(ElementTypeId::HTMLElement(
1888 HTMLElementTypeId::HTMLTextAreaElement,
1889 )) |
1890 NodeTypeId::Element(ElementTypeId::HTMLElement(
1891 HTMLElementTypeId::HTMLOptionElement,
1892 )) => self.disabled_state(),
1893 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
1894 self.downcast::<HTMLElement>()
1895 .unwrap()
1896 .is_form_associated_custom_element() &&
1897 self.disabled_state()
1898 },
1899 _ => false,
1904 }
1905 }
1906
1907 #[allow(clippy::too_many_arguments)]
1908 pub(crate) fn push_new_attribute(
1909 &self,
1910 cx: &mut JSContext,
1911 local_name: LocalName,
1912 value: AttrValue,
1913 name: LocalName,
1914 namespace: Namespace,
1915 prefix: Option<Prefix>,
1916 reason: AttributeMutationReason,
1917 ) {
1918 let data = ContentAttributeData {
1923 identifier: AttrIdentifier {
1924 local_name: GenericAtomIdent(local_name),
1925 name: GenericAtomIdent(name),
1926 namespace: GenericAtomIdent(namespace),
1927 prefix: prefix.map(GenericAtomIdent),
1928 },
1929 value,
1930 };
1931 let attr_ref = AttrRef::Raw(&data);
1932 self.will_mutate_attr(attr_ref);
1933 self.attrs.push_raw(ContentAttributeData {
1935 identifier: data.identifier.clone(),
1936 value: data.value.clone(),
1937 });
1938 self.handle_attribute_changes(cx, attr_ref, None, Some(&*attr_ref.value()), reason);
1940 }
1941
1942 fn handle_attribute_changes(
1944 &self,
1945 cx: &mut JSContext,
1946 attr: AttrRef<'_>,
1947 old_value: Option<&AttrValue>,
1948 new_value: Option<&AttrValue>,
1949 reason: AttributeMutationReason,
1950 ) {
1951 let name = attr.local_name().clone();
1954 let namespace = attr.namespace().clone();
1955 let mutation = LazyCell::new(|| Mutation::Attribute {
1956 name: name.clone(),
1957 namespace: namespace.clone(),
1958 old_value: old_value.map(|old_value| DOMString::from(&**old_value)),
1959 });
1960 MutationObserver::queue_a_mutation_record(&self.node, mutation);
1961
1962 let has_new_value = new_value.is_some();
1964
1965 if self.is_custom() {
1968 let reaction = CallbackReaction::AttributeChanged(
1969 attr.local_name().clone(),
1970 old_value,
1971 new_value,
1972 attr.namespace().clone(),
1973 );
1974 ScriptThread::enqueue_callback_reaction(self, reaction, None);
1975 }
1976
1977 if is_relevant_attribute(attr.namespace(), attr.local_name()) {
1979 let attribute_mutation = if has_new_value {
1980 AttributeMutation::Set(old_value, reason)
1981 } else {
1982 AttributeMutation::Removed
1983 };
1984 vtable_for(self.upcast()).attribute_mutated(cx, attr, attribute_mutation);
1985 }
1986 }
1987
1988 pub(crate) fn change_attribute(&self, cx: &mut JSContext, attr: &Attr, mut value: AttrValue) {
1990 let old_value = &attr.value().clone();
1994 self.will_mutate_attr(AttrRef::Dom(attr));
1996 attr.swap_value(&mut value);
1997 self.handle_attribute_changes(
2001 cx,
2002 AttrRef::Dom(attr),
2003 Some(old_value),
2004 Some(&*attr.value()),
2005 AttributeMutationReason::Directly,
2006 );
2007 }
2008
2009 pub(crate) fn push_attribute(
2011 &self,
2012 cx: &mut JSContext,
2013 attr: &Attr,
2014 reason: AttributeMutationReason,
2015 ) {
2016 assert!(attr.GetOwnerElement().as_deref() == Some(self));
2020 assert!(attr.upcast::<Node>().owner_doc() == self.node.owner_doc());
2024 self.will_mutate_attr(AttrRef::Dom(attr));
2026 self.attrs.push_dom(attr);
2027 self.handle_attribute_changes(cx, AttrRef::Dom(attr), None, Some(&*attr.value()), reason);
2031 }
2032
2033 pub(crate) fn get_attribute_with_namespace(
2039 &self,
2040 cx: &mut JSContext,
2041 namespace: &Namespace,
2042 local_name: &LocalName,
2043 ) -> Option<DomRoot<Attr>> {
2044 let idx = self
2045 .attrs
2046 .borrow()
2047 .iter()
2048 .position(|a| a.local_name() == local_name && a.namespace() == namespace)?;
2049 Some(self.attrs.ensure_dom(cx, idx, self))
2050 }
2051
2052 #[expect(unsafe_code)]
2057 pub(crate) fn get_attribute(&self, local_name: &LocalName) -> Option<DomRoot<Attr>> {
2058 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
2060 let cx = &mut cx;
2061
2062 debug_assert_eq!(
2063 *local_name,
2064 local_name.to_ascii_lowercase(),
2065 "All namespace-less attribute accesses should use a lowercase ASCII name"
2066 );
2067 self.get_attribute_with_namespace(cx, &ns!(), local_name)
2068 }
2069
2070 pub(crate) fn get_attribute_by_name(
2072 &self,
2073 cx: &mut JSContext,
2074 name: DOMString,
2075 ) -> Option<DomRoot<Attr>> {
2076 let name = &self.parsed_name(name);
2077 let idx = self.attrs.borrow().iter().position(|a| a.name() == name)?;
2078 let attr_dom = Some(self.attrs.ensure_dom(cx, idx, self));
2079 fn id_and_name_must_be_atoms(name: &LocalName, maybe_attr: &Option<DomRoot<Attr>>) -> bool {
2080 if *name == local_name!("id") || *name == local_name!("name") {
2081 match maybe_attr {
2082 None => true,
2083 Some(attr) => matches!(*attr.value(), AttrValue::Atom(_)),
2084 }
2085 } else {
2086 true
2087 }
2088 }
2089 debug_assert!(id_and_name_must_be_atoms(name, &attr_dom));
2090 attr_dom
2091 }
2092
2093 pub(crate) fn set_attribute_from_parser(
2094 &self,
2095 cx: &mut JSContext,
2096 qname: QualName,
2097 value: DOMString,
2098 prefix: Option<Prefix>,
2099 ) {
2100 if self
2102 .attrs
2103 .borrow()
2104 .iter()
2105 .any(|a| *a.local_name() == qname.local && *a.namespace() == qname.ns)
2106 {
2107 return;
2108 }
2109
2110 let name = match prefix {
2111 None => qname.local.clone(),
2112 Some(ref prefix) => {
2113 let name = format!("{}:{}", &**prefix, &*qname.local);
2114 LocalName::from(name)
2115 },
2116 };
2117 let value = self.parse_attribute(&qname.ns, &qname.local, value);
2118 self.push_new_attribute(
2119 cx,
2120 qname.local,
2121 value,
2122 name,
2123 qname.ns,
2124 prefix,
2125 AttributeMutationReason::ByParser,
2126 );
2127 }
2128
2129 pub(crate) fn set_attribute(&self, cx: &mut JSContext, name: &LocalName, value: AttrValue) {
2130 debug_assert_eq!(
2131 *name,
2132 name.to_ascii_lowercase(),
2133 "All attribute accesses should use a lowercase ASCII name"
2134 );
2135 debug_assert!(!name.contains(':'));
2136
2137 self.set_first_matching_attribute(
2138 cx,
2139 name.clone(),
2140 value,
2141 name.clone(),
2142 ns!(),
2143 None,
2144 |attr| attr.local_name() == name,
2145 );
2146 }
2147
2148 pub(crate) fn set_attribute_with_namespace(
2149 &self,
2150 cx: &mut JSContext,
2151 local_name: LocalName,
2152 value: AttrValue,
2153 name: LocalName,
2154 namespace: Namespace,
2155 prefix: Option<Prefix>,
2156 ) {
2157 self.set_first_matching_attribute(
2158 cx,
2159 local_name.clone(),
2160 value,
2161 name,
2162 namespace.clone(),
2163 prefix,
2164 |attr| *attr.local_name() == local_name && *attr.namespace() == namespace,
2165 );
2166 }
2167
2168 #[allow(clippy::too_many_arguments)]
2170 fn set_first_matching_attribute<F>(
2171 &self,
2172 cx: &mut JSContext,
2173 local_name: LocalName,
2174 value: AttrValue,
2175 name: LocalName,
2176 namespace: Namespace,
2177 prefix: Option<Prefix>,
2178 find: F,
2179 ) where
2180 F: Fn(AttrRef<'_>) -> bool,
2181 {
2182 let found_idx = self.attrs.borrow().iter().position(find);
2185 if let Some(idx) = found_idx {
2186 let attr = self.attrs.ensure_dom(cx, idx, self);
2187 self.will_mutate_attr(AttrRef::Dom(&attr));
2189 self.change_attribute(cx, &attr, value);
2190 } else {
2191 self.push_new_attribute(
2196 cx,
2197 local_name,
2198 value,
2199 name,
2200 namespace,
2201 prefix,
2202 AttributeMutationReason::Directly,
2203 );
2204 };
2205 }
2206
2207 pub(crate) fn parse_attribute(
2208 &self,
2209 namespace: &Namespace,
2210 local_name: &LocalName,
2211 value: DOMString,
2212 ) -> AttrValue {
2213 if is_relevant_attribute(namespace, local_name) {
2214 vtable_for(self.upcast()).parse_plain_attribute(local_name, value)
2215 } else {
2216 AttrValue::String(value.into())
2217 }
2218 }
2219
2220 pub(crate) fn remove_attribute(
2221 &self,
2222 cx: &mut JSContext,
2223 namespace: &Namespace,
2224 local_name: &LocalName,
2225 ) -> Option<DomRoot<Attr>> {
2226 self.remove_first_matching_attribute(cx, |attr| {
2227 attr.namespace() == namespace && attr.local_name() == local_name
2228 })
2229 }
2230
2231 pub(crate) fn remove_attribute_by_name(
2232 &self,
2233 cx: &mut JSContext,
2234 name: &LocalName,
2235 ) -> Option<DomRoot<Attr>> {
2236 self.remove_first_matching_attribute(cx, |attr| attr.name() == name)
2237 }
2238
2239 fn remove_first_matching_attribute<F>(
2241 &self,
2242 cx: &mut JSContext,
2243 find: F,
2244 ) -> Option<DomRoot<Attr>>
2245 where
2246 F: Fn(AttrRef<'_>) -> bool,
2247 {
2248 let idx = self.attrs.borrow().iter().position(find);
2249 idx.map(|idx| {
2250 let attr = self.attrs.ensure_dom(cx, idx, self);
2251
2252 self.will_mutate_attr(AttrRef::Dom(&attr));
2254 self.attrs.remove(idx);
2255 attr.set_owner(cx, None);
2257 self.handle_attribute_changes(
2259 cx,
2260 AttrRef::Dom(&attr),
2261 Some(&attr.value()),
2262 None,
2263 AttributeMutationReason::Directly,
2264 );
2265
2266 attr
2267 })
2268 }
2269
2270 pub(crate) fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2271 self.get_attribute(&local_name!("class"))
2272 .is_some_and(|attr| {
2273 attr.value()
2274 .as_tokens()
2275 .iter()
2276 .any(|atom| case_sensitivity.eq_atom(name, atom))
2277 })
2278 }
2279
2280 pub(crate) fn has_attribute(&self, local_name: &LocalName) -> bool {
2281 debug_assert_eq!(
2282 *local_name,
2283 local_name.to_ascii_lowercase(),
2284 "All attribute accesses should use a lowercase ASCII name"
2285 );
2286 debug_assert!(!local_name.contains(':'));
2287 self.attrs
2288 .borrow()
2289 .iter()
2290 .any(|attr| attr.local_name() == local_name && attr.namespace() == &ns!())
2291 }
2292
2293 pub(crate) fn will_mutate_attr(&self, attr: AttrRef<'_>) {
2294 let node = self.upcast::<Node>();
2295 node.owner_doc().element_attr_will_change(self, attr);
2296 }
2297
2298 fn update_style_attribute(&self, attr: AttrRef<'_>, mutation: AttributeMutation) {
2300 let doc = self.upcast::<Node>().owner_doc();
2301 *self.style_attribute.borrow_mut() = match mutation {
2303 AttributeMutation::Set(..) => {
2304 let value = attr.as_attr().map_or_else(
2305 || attr.value(),
2306 |attribute| AttrValueRef::Borrowed(attribute.value()),
2307 );
2308
2309 Some(match &*value {
2310 AttrValue::Declaration { block, .. } => block.clone(),
2311 _ => {
2312 let win = self.owner_window();
2313 let source = &**attr.value();
2314 let global = &self.owner_global();
2315 if global
2320 .get_csp_list()
2321 .should_elements_inline_type_behavior_be_blocked(
2322 global,
2323 self,
2324 InlineCheckType::StyleAttribute,
2325 source,
2326 doc.get_current_parser_line(),
2327 )
2328 {
2329 return;
2330 }
2331 ServoArc::new(doc.style_shared_author_lock().wrap(parse_style_attribute(
2332 source,
2333 &UrlExtraData(doc.base_url().get_arc()),
2334 Some(win.css_error_reporter()),
2335 doc.quirks_mode(),
2336 CssRuleType::Style,
2337 )))
2338 },
2339 })
2340 },
2341 AttributeMutation::Removed => None,
2342 };
2343 }
2344
2345 fn set_attribute_node(
2349 &self,
2350 cx: &mut JSContext,
2351 attr: &Attr,
2352 ) -> Fallible<Option<DomRoot<Attr>>> {
2353 let verified_value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
2357 cx,
2358 self.namespace(),
2359 self.local_name(),
2360 attr.local_name(),
2361 Some(attr.namespace()),
2362 TrustedTypeOrString::String(attr.Value()),
2363 &self.owner_global(),
2364 )?;
2365
2366 if let Some(owner) = attr.GetOwnerElement() &&
2369 &*owner != self
2370 {
2371 return Err(Error::InUseAttribute(None));
2372 }
2373
2374 let vtable = vtable_for(self.upcast());
2375
2376 attr.swap_value(
2382 &mut vtable.parse_plain_attribute(attr.local_name(), verified_value.clone()),
2383 );
2384
2385 let position = self.attrs.borrow().iter().position(|old_attr| {
2387 attr.namespace() == old_attr.namespace() && attr.local_name() == old_attr.local_name()
2388 });
2389
2390 let old_attr = if let Some(position) = position {
2391 let old_attr = self.attrs.ensure_dom(cx, position, self);
2392
2393 if &*old_attr == attr {
2395 return Ok(Some(DomRoot::from_ref(attr)));
2396 }
2397
2398 self.will_mutate_attr(AttrRef::Dom(attr));
2408 self.attrs
2409 .set(position, AttributeEntry::Dom(Dom::from_ref(attr)));
2410 attr.set_owner(cx, Some(self));
2412 attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2414 old_attr.set_owner(cx, None);
2416 self.handle_attribute_changes(
2418 cx,
2419 AttrRef::Dom(attr),
2420 Some(&old_attr.value()),
2421 Some(&AttrValue::String(verified_value.into())),
2422 AttributeMutationReason::Directly,
2423 );
2424
2425 Some(old_attr)
2426 } else {
2427 attr.set_owner(cx, Some(self));
2429 attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2430 self.push_attribute(cx, attr, AttributeMutationReason::Directly);
2431
2432 None
2433 };
2434
2435 Ok(old_attr)
2437 }
2438
2439 pub(crate) fn update_nonce_internal_slot(&self, nonce: String) {
2441 self.ensure_rare_data().cryptographic_nonce = nonce;
2442 }
2443
2444 pub(crate) fn nonce_value(&self) -> String {
2446 match self.rare_data().as_ref() {
2447 None => String::new(),
2448 Some(rare_data) => rare_data.cryptographic_nonce.clone(),
2449 }
2450 }
2451
2452 pub(crate) fn update_nonce_post_connection(&self, cx: &mut JSContext) {
2454 if !self.upcast::<Node>().is_connected_with_browsing_context() {
2457 return;
2458 }
2459 let global = self.owner_global();
2460 let csp_list = match global.get_csp_list() {
2462 None => return,
2463 Some(csp_list) => csp_list,
2464 };
2465 if !csp_list.contains_a_header_delivered_content_security_policy() ||
2468 self.get_string_attribute(&local_name!("nonce")).is_empty()
2469 {
2470 return;
2471 }
2472 let nonce = self.nonce_value();
2474 self.set_string_attribute(cx, &local_name!("nonce"), "".into());
2476 self.update_nonce_internal_slot(nonce);
2478 }
2479
2480 pub(crate) fn is_nonceable(&self) -> bool {
2482 if !self.has_attribute(&local_name!("nonce")) {
2484 return false;
2485 }
2486 if self.downcast::<HTMLScriptElement>().is_some() {
2488 for attr in self.attrs().borrow().iter() {
2489 let attr_name = attr.name().to_ascii_lowercase();
2492 if attr_name.contains("<script") || attr_name.contains("<style") {
2493 return false;
2494 }
2495 let attr_value = attr.value().to_ascii_lowercase();
2498 if attr_value.contains("<script") || attr_value.contains("<style") {
2499 return false;
2500 }
2501 }
2502 }
2503 if self
2505 .rare_data()
2506 .as_ref()
2507 .is_some_and(|d| d.had_duplicate_attributes)
2508 {
2509 return false;
2510 }
2511 true
2513 }
2514
2515 pub(crate) fn insert_adjacent(
2517 &self,
2518 cx: &mut JSContext,
2519 where_: AdjacentPosition,
2520 node: &Node,
2521 ) -> Fallible<Option<DomRoot<Node>>> {
2522 let self_node = self.upcast::<Node>();
2523 match where_ {
2524 AdjacentPosition::BeforeBegin => {
2525 if let Some(parent) = self_node.GetParentNode() {
2526 Node::pre_insert(cx, node, &parent, Some(self_node)).map(Some)
2527 } else {
2528 Ok(None)
2529 }
2530 },
2531 AdjacentPosition::AfterBegin => {
2532 Node::pre_insert(cx, node, self_node, self_node.GetFirstChild().as_deref())
2533 .map(Some)
2534 },
2535 AdjacentPosition::BeforeEnd => Node::pre_insert(cx, node, self_node, None).map(Some),
2536 AdjacentPosition::AfterEnd => {
2537 if let Some(parent) = self_node.GetParentNode() {
2538 Node::pre_insert(cx, node, &parent, self_node.GetNextSibling().as_deref())
2539 .map(Some)
2540 } else {
2541 Ok(None)
2542 }
2543 },
2544 }
2545 }
2546
2547 pub(crate) fn scroll(&self, cx: &mut JSContext, x: f64, y: f64, behavior: ScrollBehavior) {
2552 let x = if x.is_finite() { x } else { 0.0 } as f32;
2554 let y = if y.is_finite() { y } else { 0.0 } as f32;
2555
2556 let node = self.upcast::<Node>();
2557
2558 let doc = node.owner_doc();
2560
2561 if !doc.is_fully_active() {
2563 return;
2564 }
2565
2566 let win = match doc.GetDefaultView() {
2568 None => return,
2569 Some(win) => win,
2570 };
2571
2572 if *self.root_element() == *self {
2574 if doc.quirks_mode() != QuirksMode::Quirks {
2575 win.scroll(cx, x, y, behavior);
2576 }
2577
2578 return;
2579 }
2580
2581 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
2583 doc.quirks_mode() == QuirksMode::Quirks &&
2584 !self.is_potentially_scrollable_body()
2585 {
2586 win.scroll(cx, x, y, behavior);
2587 return;
2588 }
2589
2590 if !self.has_scrolling_box() {
2592 return;
2593 }
2594
2595 win.scroll_an_element(cx, self, x, y, behavior);
2597 }
2598
2599 pub(crate) fn parse_fragment(
2601 &self,
2602 markup: DOMString,
2603 cx: &mut JSContext,
2604 ) -> Fallible<DomRoot<DocumentFragment>> {
2605 let new_children = ServoParser::parse_html_fragment(self, markup, false, cx);
2608 let context_document = {
2611 if let Some(template) = self.downcast::<HTMLTemplateElement>() {
2612 template.Content(cx).upcast::<Node>().owner_doc()
2613 } else {
2614 self.owner_document()
2615 }
2616 };
2617 let fragment = DocumentFragment::new(cx, &context_document);
2618 for child in new_children {
2620 fragment.upcast::<Node>().AppendChild(cx, &child).unwrap();
2621 }
2622 Ok(fragment)
2624 }
2625
2626 pub(crate) fn fragment_parsing_context(
2629 cx: &mut JSContext,
2630 owner_doc: &Document,
2631 element: Option<&Self>,
2632 ) -> DomRoot<Self> {
2633 match element {
2635 Some(elem)
2636 if elem.local_name() != &local_name!("html") ||
2640 !elem.html_element_in_html_document() =>
2641 {
2642 DomRoot::from_ref(elem)
2643 },
2644 _ => Element::create(
2647 cx,
2648 QualName::new(None, ns!(html), local_name!("body")),
2649 None,
2650 owner_doc,
2651 ElementCreator::ScriptCreated,
2652 CustomElementCreationMode::Asynchronous,
2653 None
2654 ),
2655 }
2656 }
2657
2658 pub(crate) fn is_in_same_home_subtree<T>(&self, other: &T) -> bool
2660 where
2661 T: DerivedFrom<Element> + DomObject,
2662 {
2663 let other = other.upcast::<Element>();
2664 self.root_element() == other.root_element()
2665 }
2666
2667 pub(crate) fn get_id(&self) -> Option<Atom> {
2668 self.id_attribute.borrow().clone()
2669 }
2670
2671 pub(crate) fn get_name(&self) -> Option<Atom> {
2672 self.rare_data().as_ref()?.name_attribute.clone()
2673 }
2674
2675 pub(crate) fn get_element_internals(&self) -> Option<DomRoot<ElementInternals>> {
2676 self.rare_data()
2677 .as_ref()?
2678 .element_internals
2679 .as_ref()
2680 .map(|sr| DomRoot::from_ref(&**sr))
2681 }
2682
2683 pub(crate) fn ensure_element_internals(&self, can_gc: CanGc) -> DomRoot<ElementInternals> {
2684 let mut rare_data = self.ensure_rare_data();
2685 DomRoot::from_ref(rare_data.element_internals.get_or_insert_with(|| {
2686 let elem = self
2687 .downcast::<HTMLElement>()
2688 .expect("ensure_element_internals should only be called for an HTMLElement");
2689 Dom::from_ref(&*ElementInternals::new(elem, can_gc))
2690 }))
2691 }
2692
2693 pub(crate) fn outer_html(&self, cx: &mut JSContext) -> Fallible<DOMString> {
2694 match self.GetOuterHTML(cx)? {
2695 TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => Ok(str),
2696 TrustedHTMLOrNullIsEmptyString::TrustedHTML(_) => unreachable!(),
2697 }
2698 }
2699
2700 pub(crate) fn compute_source_position(&self, line_number: u32) -> SourcePosition {
2701 SourcePosition {
2702 source_file: self.owner_global().get_url().to_string(),
2703 line_number: line_number + 2,
2704 column_number: 0,
2705 }
2706 }
2707
2708 pub(crate) fn explicitly_set_tab_index(&self) -> Option<i32> {
2709 if self.has_attribute(&local_name!("tabindex")) {
2710 Some(self.get_int_attribute(&local_name!("tabindex"), 0))
2711 } else {
2712 None
2713 }
2714 }
2715
2716 pub(crate) fn tab_index(&self) -> i32 {
2718 if let Some(tab_index) = self.explicitly_set_tab_index() {
2724 return tab_index;
2725 }
2726
2727 if matches!(
2733 self.upcast::<Node>().type_id(),
2734 NodeTypeId::Element(ElementTypeId::HTMLElement(
2735 HTMLElementTypeId::HTMLAnchorElement |
2736 HTMLElementTypeId::HTMLAreaElement |
2737 HTMLElementTypeId::HTMLButtonElement |
2738 HTMLElementTypeId::HTMLFrameElement |
2739 HTMLElementTypeId::HTMLIFrameElement |
2740 HTMLElementTypeId::HTMLInputElement |
2741 HTMLElementTypeId::HTMLObjectElement |
2742 HTMLElementTypeId::HTMLSelectElement |
2743 HTMLElementTypeId::HTMLTextAreaElement
2744 ))
2745 ) {
2746 return 0;
2747 }
2748 if self
2749 .downcast::<HTMLElement>()
2750 .is_some_and(|html_element| html_element.is_a_summary_for_its_parent_details())
2751 {
2752 return 0;
2753 }
2754
2755 -1
2756 }
2757
2758 #[inline]
2759 fn insert_selector_flags(&self, flags: ElementSelectorFlags) {
2760 self.selector_flags
2761 .fetch_or(flags.bits(), Ordering::Relaxed);
2762 }
2763
2764 #[inline]
2765 fn get_selector_flags(&self) -> ElementSelectorFlags {
2766 ElementSelectorFlags::from_bits_retain(self.selector_flags.load(Ordering::Relaxed))
2767 }
2768
2769 pub(crate) fn needs_preserved_style_attribute_after_change(&self) -> bool {
2774 self.owner_window().get_exists_mut_observer() ||
2781 self.get_custom_element_definition()
2782 .is_some_and(|custom_element_definition| {
2783 custom_element_definition.has_attribute_changed_callback()
2784 })
2785 }
2786}
2787
2788impl ElementMethods<crate::DomTypeHolder> for Element {
2789 fn GetNamespaceURI(&self) -> Option<DOMString> {
2791 Node::namespace_to_string(self.namespace.clone())
2792 }
2793
2794 fn LocalName(&self) -> DOMString {
2796 DOMString::from(&*self.local_name)
2798 }
2799
2800 fn GetPrefix(&self) -> Option<DOMString> {
2802 self.prefix.borrow().as_ref().map(|p| DOMString::from(&**p))
2803 }
2804
2805 fn TagName(&self) -> DOMString {
2807 let name = self.tag_name.or_init(|| {
2808 let qualified_name = match *self.prefix.borrow() {
2809 Some(ref prefix) => Cow::Owned(format!("{}:{}", &**prefix, &*self.local_name)),
2810 None => Cow::Borrowed(&*self.local_name),
2811 };
2812 if self.html_element_in_html_document() {
2813 LocalName::from(qualified_name.to_ascii_uppercase())
2814 } else {
2815 LocalName::from(qualified_name)
2816 }
2817 });
2818 DOMString::from(&*name)
2819 }
2820
2821 fn Id(&self) -> DOMString {
2825 self.get_string_attribute(&local_name!("id"))
2826 }
2827
2828 fn SetId(&self, cx: &mut JSContext, id: DOMString) {
2830 self.set_atomic_attribute(cx, &local_name!("id"), id);
2831 }
2832
2833 fn ClassName(&self) -> DOMString {
2835 self.get_string_attribute(&local_name!("class"))
2836 }
2837
2838 fn SetClassName(&self, cx: &mut JSContext, class: DOMString) {
2840 self.set_tokenlist_attribute(cx, &local_name!("class"), class);
2841 }
2842
2843 fn ClassList(&self, cx: &mut js::context::JSContext) -> DomRoot<DOMTokenList> {
2845 self.class_list
2846 .or_init(|| DOMTokenList::new(cx, self, &local_name!("class"), None))
2847 }
2848
2849 make_getter!(Slot, "slot");
2851
2852 make_setter!(SetSlot, "slot");
2854
2855 fn Attributes(&self, can_gc: CanGc) -> DomRoot<NamedNodeMap> {
2857 self.attr_list
2858 .or_init(|| NamedNodeMap::new(&self.owner_window(), self, can_gc))
2859 }
2860
2861 fn HasAttributes(&self) -> bool {
2863 !self.attrs.borrow().is_empty()
2864 }
2865
2866 fn GetAttributeNames(&self) -> Vec<DOMString> {
2868 self.attrs
2869 .borrow()
2870 .iter()
2871 .map(|attr| DOMString::from(&**attr.name()))
2872 .collect()
2873 }
2874
2875 fn GetAttribute(&self, cx: &mut JSContext, name: DOMString) -> Option<DOMString> {
2877 self.GetAttributeNode(cx, name).map(|s| s.Value())
2878 }
2879
2880 fn GetAttributeNS(
2882 &self,
2883 cx: &mut JSContext,
2884 namespace: Option<DOMString>,
2885 local_name: DOMString,
2886 ) -> Option<DOMString> {
2887 self.GetAttributeNodeNS(cx, namespace, local_name)
2888 .map(|attr| attr.Value())
2889 }
2890
2891 fn GetAttributeNode(&self, cx: &mut JSContext, name: DOMString) -> Option<DomRoot<Attr>> {
2893 self.get_attribute_by_name(cx, name)
2894 }
2895
2896 fn GetAttributeNodeNS(
2898 &self,
2899 cx: &mut JSContext,
2900 namespace: Option<DOMString>,
2901 local_name: DOMString,
2902 ) -> Option<DomRoot<Attr>> {
2903 let namespace = &namespace_from_domstring(namespace);
2904 self.get_attribute_with_namespace(cx, namespace, &LocalName::from(local_name))
2905 }
2906
2907 fn ToggleAttribute(
2909 &self,
2910 cx: &mut JSContext,
2911 name: DOMString,
2912 force: Option<bool>,
2913 ) -> Fallible<bool> {
2914 if !is_valid_attribute_local_name(&name.str()) {
2917 return Err(Error::InvalidCharacter(None));
2918 }
2919
2920 let attribute = self.GetAttribute(cx, name.clone());
2922
2923 let name = self.parsed_name(name);
2925 match attribute {
2926 None => match force {
2928 None | Some(true) => {
2930 self.set_first_matching_attribute(
2931 cx,
2932 name.clone(),
2933 AttrValue::String(String::new()),
2934 name.clone(),
2935 ns!(),
2936 None,
2937 |attr| *attr.name() == name,
2938 );
2939 Ok(true)
2940 },
2941 Some(false) => Ok(false),
2943 },
2944 Some(_index) => match force {
2945 None | Some(false) => {
2947 self.remove_attribute_by_name(cx, &name);
2948 Ok(false)
2949 },
2950 Some(true) => Ok(true),
2952 },
2953 }
2954 }
2955
2956 fn SetAttribute(
2958 &self,
2959 cx: &mut JSContext,
2960 name: DOMString,
2961 value: TrustedTypeOrString,
2962 ) -> ErrorResult {
2963 if !is_valid_attribute_local_name(&name.str()) {
2966 return Err(Error::InvalidCharacter(None));
2967 }
2968
2969 let name = self.parsed_name(name);
2972
2973 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
2977 cx,
2978 self.namespace(),
2979 self.local_name(),
2980 &name,
2981 None,
2982 value,
2983 &self.owner_global(),
2984 )?;
2985
2986 let value = self.parse_attribute(&ns!(), &name, value);
2991 self.set_first_matching_attribute(
2992 cx,
2993 name.clone(),
2994 value,
2995 name.clone(),
2996 ns!(),
2997 None,
2998 |attr| *attr.name() == name,
2999 );
3000 Ok(())
3001 }
3002
3003 fn SetAttributeNS(
3005 &self,
3006 cx: &mut JSContext,
3007 namespace: Option<DOMString>,
3008 qualified_name: DOMString,
3009 value: TrustedTypeOrString,
3010 ) -> ErrorResult {
3011 let (namespace, prefix, local_name) =
3013 domname::validate_and_extract(namespace, &qualified_name, domname::Context::Element)?;
3014 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3017 cx,
3018 self.namespace(),
3019 self.local_name(),
3020 &local_name,
3021 Some(&namespace),
3022 value,
3023 &self.owner_global(),
3024 )?;
3025 let value = self.parse_attribute(&namespace, &local_name, value);
3027 self.set_attribute_with_namespace(
3028 cx,
3029 local_name,
3030 value,
3031 LocalName::from(qualified_name),
3032 namespace,
3033 prefix,
3034 );
3035 Ok(())
3036 }
3037
3038 fn SetAttributeNode(&self, cx: &mut JSContext, attr: &Attr) -> Fallible<Option<DomRoot<Attr>>> {
3040 self.set_attribute_node(cx, attr)
3041 }
3042
3043 fn SetAttributeNodeNS(
3045 &self,
3046 cx: &mut JSContext,
3047 attr: &Attr,
3048 ) -> Fallible<Option<DomRoot<Attr>>> {
3049 self.set_attribute_node(cx, attr)
3050 }
3051
3052 fn RemoveAttribute(&self, cx: &mut JSContext, name: DOMString) {
3054 let name = self.parsed_name(name);
3055 self.remove_attribute_by_name(cx, &name);
3056 }
3057
3058 fn RemoveAttributeNS(
3060 &self,
3061 cx: &mut JSContext,
3062 namespace: Option<DOMString>,
3063 local_name: DOMString,
3064 ) {
3065 let namespace = namespace_from_domstring(namespace);
3066 let local_name = LocalName::from(local_name);
3067 self.remove_attribute(cx, &namespace, &local_name);
3068 }
3069
3070 fn RemoveAttributeNode(&self, cx: &mut JSContext, attr: &Attr) -> Fallible<DomRoot<Attr>> {
3072 self.remove_first_matching_attribute(cx, |a| {
3075 a.as_attr().is_some_and(|a| std::ptr::eq(a, attr))
3076 })
3077 .ok_or(Error::NotFound(None))
3078 }
3079
3080 fn HasAttribute(&self, cx: &mut JSContext, name: DOMString) -> bool {
3082 self.GetAttribute(cx, name).is_some()
3083 }
3084
3085 fn HasAttributeNS(
3087 &self,
3088 cx: &mut JSContext,
3089 namespace: Option<DOMString>,
3090 local_name: DOMString,
3091 ) -> bool {
3092 self.GetAttributeNS(cx, namespace, local_name).is_some()
3093 }
3094
3095 fn GetElementsByTagName(
3097 &self,
3098 cx: &mut JSContext,
3099 localname: DOMString,
3100 ) -> DomRoot<HTMLCollection> {
3101 let window = self.owner_window();
3102 HTMLCollection::by_qualified_name(cx, &window, self.upcast(), LocalName::from(localname))
3103 }
3104
3105 fn GetElementsByTagNameNS(
3107 &self,
3108 cx: &mut JSContext,
3109 maybe_ns: Option<DOMString>,
3110 localname: DOMString,
3111 ) -> DomRoot<HTMLCollection> {
3112 let window = self.owner_window();
3113 HTMLCollection::by_tag_name_ns(cx, &window, self.upcast(), localname, maybe_ns)
3114 }
3115
3116 fn GetElementsByClassName(
3118 &self,
3119 cx: &mut JSContext,
3120 classes: DOMString,
3121 ) -> DomRoot<HTMLCollection> {
3122 let window = self.owner_window();
3123 HTMLCollection::by_class_name(cx, &window, self.upcast(), classes)
3124 }
3125
3126 fn GetClientRects(&self, cx: &mut JSContext) -> DomRoot<DOMRectList> {
3128 let win = self.owner_window();
3129 let raw_rects = self.upcast::<Node>().border_boxes();
3130 let rects: Vec<DomRoot<DOMRect>> = raw_rects
3131 .into_iter()
3132 .map(|rect| {
3133 DOMRect::new(
3134 win.upcast(),
3135 rect.origin.x.to_f64_px(),
3136 rect.origin.y.to_f64_px(),
3137 rect.size.width.to_f64_px(),
3138 rect.size.height.to_f64_px(),
3139 CanGc::from_cx(cx),
3140 )
3141 })
3142 .collect();
3143 DOMRectList::new(&win, rects, CanGc::from_cx(cx))
3144 }
3145
3146 fn GetBoundingClientRect(&self, cx: &mut JSContext) -> DomRoot<DOMRect> {
3148 let win = self.owner_window();
3149 let rect = self.upcast::<Node>().border_box().unwrap_or_default();
3150 debug_assert!(rect.size.width.to_f64_px() >= 0.0 && rect.size.height.to_f64_px() >= 0.0);
3151 DOMRect::new(
3152 win.upcast(),
3153 rect.origin.x.to_f64_px(),
3154 rect.origin.y.to_f64_px(),
3155 rect.size.width.to_f64_px(),
3156 rect.size.height.to_f64_px(),
3157 CanGc::from_cx(cx),
3158 )
3159 }
3160
3161 fn Scroll(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3163 let left = options.left.unwrap_or(self.ScrollLeft());
3165 let top = options.top.unwrap_or(self.ScrollTop());
3166 self.scroll(cx, left, top, options.parent.behavior);
3167 }
3168
3169 fn Scroll_(&self, cx: &mut JSContext, x: f64, y: f64) {
3171 self.scroll(cx, x, y, ScrollBehavior::Auto);
3172 }
3173
3174 fn ScrollTo(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3176 self.Scroll(cx, options);
3177 }
3178
3179 fn ScrollTo_(&self, cx: &mut JSContext, x: f64, y: f64) {
3181 self.Scroll_(cx, x, y);
3182 }
3183
3184 fn ScrollBy(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3186 let delta_left = options.left.unwrap_or(0.0f64);
3188 let delta_top = options.top.unwrap_or(0.0f64);
3189 let left = self.ScrollLeft();
3190 let top = self.ScrollTop();
3191 self.scroll(
3192 cx,
3193 left + delta_left,
3194 top + delta_top,
3195 options.parent.behavior,
3196 );
3197 }
3198
3199 fn ScrollBy_(&self, cx: &mut JSContext, x: f64, y: f64) {
3201 let left = self.ScrollLeft();
3202 let top = self.ScrollTop();
3203 self.scroll(cx, left + x, top + y, ScrollBehavior::Auto);
3204 }
3205
3206 fn ScrollTop(&self) -> f64 {
3208 let node = self.upcast::<Node>();
3209
3210 let doc = node.owner_doc();
3212
3213 if !doc.is_fully_active() {
3215 return 0.0;
3216 }
3217
3218 let win = match doc.GetDefaultView() {
3220 None => return 0.0,
3221 Some(win) => win,
3222 };
3223
3224 if self.is_document_element() {
3226 if doc.quirks_mode() == QuirksMode::Quirks {
3227 return 0.0;
3228 }
3229
3230 return win.ScrollY() as f64;
3232 }
3233
3234 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3236 doc.quirks_mode() == QuirksMode::Quirks &&
3237 !self.is_potentially_scrollable_body()
3238 {
3239 return win.ScrollY() as f64;
3240 }
3241
3242 if !self.has_css_layout_box() {
3244 return 0.0;
3245 }
3246
3247 let point = win.scroll_offset_query(node);
3249 point.y.abs() as f64
3250 }
3251
3252 fn SetScrollTop(&self, cx: &mut JSContext, y_: f64) {
3255 let behavior = ScrollBehavior::Auto;
3256
3257 let y = if y_.is_finite() { y_ } else { 0.0 } as f32;
3259
3260 let node = self.upcast::<Node>();
3261
3262 let doc = node.owner_doc();
3264
3265 if !doc.is_fully_active() {
3267 return;
3268 }
3269
3270 let win = match doc.GetDefaultView() {
3272 None => return,
3273 Some(win) => win,
3274 };
3275
3276 if self.is_document_element() {
3278 if doc.quirks_mode() != QuirksMode::Quirks {
3279 win.scroll(cx, win.ScrollX() as f32, y, behavior);
3280 }
3281
3282 return;
3283 }
3284
3285 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3287 doc.quirks_mode() == QuirksMode::Quirks &&
3288 !self.is_potentially_scrollable_body()
3289 {
3290 win.scroll(cx, win.ScrollX() as f32, y, behavior);
3291 return;
3292 }
3293
3294 if !self.has_scrolling_box() {
3296 return;
3297 }
3298
3299 win.scroll_an_element(cx, self, self.ScrollLeft() as f32, y, behavior);
3301 }
3302
3303 fn ScrollLeft(&self) -> f64 {
3305 let node = self.upcast::<Node>();
3306
3307 let doc = node.owner_doc();
3309
3310 if !doc.is_fully_active() {
3312 return 0.0;
3313 }
3314
3315 let win = match doc.GetDefaultView() {
3317 None => return 0.0,
3318 Some(win) => win,
3319 };
3320
3321 if self.is_document_element() {
3323 if doc.quirks_mode() != QuirksMode::Quirks {
3324 return win.ScrollX() as f64;
3326 }
3327
3328 return 0.0;
3329 }
3330
3331 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3333 doc.quirks_mode() == QuirksMode::Quirks &&
3334 !self.is_potentially_scrollable_body()
3335 {
3336 return win.ScrollX() as f64;
3337 }
3338
3339 if !self.has_css_layout_box() {
3341 return 0.0;
3342 }
3343
3344 let point = win.scroll_offset_query(node);
3346 point.x.abs() as f64
3347 }
3348
3349 fn SetScrollLeft(&self, cx: &mut JSContext, x: f64) {
3351 let behavior = ScrollBehavior::Auto;
3352
3353 let x = if x.is_finite() { x } else { 0.0 } as f32;
3355
3356 let node = self.upcast::<Node>();
3357
3358 let doc = node.owner_doc();
3360
3361 if !doc.is_fully_active() {
3363 return;
3364 }
3365
3366 let win = match doc.GetDefaultView() {
3368 None => return,
3369 Some(win) => win,
3370 };
3371
3372 if self.is_document_element() {
3374 if doc.quirks_mode() == QuirksMode::Quirks {
3375 return;
3376 }
3377
3378 win.scroll(cx, x, win.ScrollY() as f32, behavior);
3379 return;
3380 }
3381
3382 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3384 doc.quirks_mode() == QuirksMode::Quirks &&
3385 !self.is_potentially_scrollable_body()
3386 {
3387 win.scroll(cx, x, win.ScrollY() as f32, behavior);
3388 return;
3389 }
3390
3391 if !self.has_scrolling_box() {
3393 return;
3394 }
3395
3396 win.scroll_an_element(cx, self, x, self.ScrollTop() as f32, behavior);
3398 }
3399
3400 fn ScrollIntoView(&self, cx: &mut JSContext, arg: BooleanOrScrollIntoViewOptions) {
3402 let (behavior, block, inline, container) = match arg {
3403 BooleanOrScrollIntoViewOptions::Boolean(true) => (
3405 ScrollBehavior::Auto, ScrollLogicalPosition::Start, ScrollLogicalPosition::Nearest, None, ),
3410 BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(options) => (
3413 options.parent.behavior,
3414 options.block,
3415 options.inline,
3416 if options.container == ScrollIntoViewContainer::Nearest {
3419 Some(self)
3420 } else {
3421 None
3422 },
3423 ),
3424 BooleanOrScrollIntoViewOptions::Boolean(false) => (
3426 ScrollBehavior::Auto,
3427 ScrollLogicalPosition::End,
3428 ScrollLogicalPosition::Nearest,
3429 None,
3430 ),
3431 };
3432
3433 if !self.has_css_layout_box() {
3436 return;
3437 }
3438
3439 self.scroll_into_view_with_options(
3441 cx,
3442 behavior,
3443 ScrollAxisState::new_always_scroll_position(block),
3444 ScrollAxisState::new_always_scroll_position(inline),
3445 container,
3446 None,
3447 );
3448
3449 }
3452
3453 fn ScrollWidth(&self) -> i32 {
3455 self.upcast::<Node>().scroll_area().size.width
3456 }
3457
3458 fn ScrollHeight(&self) -> i32 {
3460 self.upcast::<Node>().scroll_area().size.height
3461 }
3462
3463 fn ClientTop(&self) -> i32 {
3465 self.client_rect().origin.y
3466 }
3467
3468 fn ClientLeft(&self) -> i32 {
3470 self.client_rect().origin.x
3471 }
3472
3473 fn ClientWidth(&self) -> i32 {
3475 self.client_rect().size.width
3476 }
3477
3478 fn ClientHeight(&self) -> i32 {
3480 self.client_rect().size.height
3481 }
3482
3483 fn CurrentCSSZoom(&self) -> Finite<f64> {
3485 let window = self.owner_window();
3486 Finite::wrap(window.current_css_zoom_query(self.upcast::<Node>()) as f64)
3487 }
3488
3489 fn SetHTMLUnsafe(&self, cx: &mut JSContext, html: TrustedHTMLOrString) -> ErrorResult {
3491 let html = TrustedHTML::get_trusted_type_compliant_string(
3495 cx,
3496 &self.owner_global(),
3497 html,
3498 "Element setHTMLUnsafe",
3499 )?;
3500 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3502 DomRoot::upcast(template.Content(cx))
3503 } else {
3504 DomRoot::from_ref(self.upcast())
3505 };
3506
3507 Node::unsafely_set_html(&target, self, html, cx);
3509 Ok(())
3510 }
3511
3512 fn SetHTML(
3514 &self,
3515 cx: &mut JSContext,
3516 html: DOMString,
3517 options: &SetHTMLOptions,
3518 ) -> ErrorResult {
3519 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3521 DomRoot::upcast(template.Content(cx))
3522 } else {
3523 DomRoot::from_ref(self.upcast())
3524 };
3525
3526 Sanitizer::set_and_filter_html(cx, &target, self, html, options, true)
3528 }
3529
3530 fn GetHTML(&self, cx: &mut JSContext, options: &GetHTMLOptions) -> DOMString {
3532 self.upcast::<Node>().html_serialize(
3535 cx,
3536 TraversalScope::ChildrenOnly(None),
3537 options.serializableShadowRoots,
3538 options.shadowRoots.clone(),
3539 )
3540 }
3541
3542 fn GetInnerHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3544 let qname = QualName::new(
3545 self.prefix().clone(),
3546 self.namespace().clone(),
3547 self.local_name().clone(),
3548 );
3549
3550 let result = if self.owner_document().is_html_document() {
3553 self.upcast::<Node>()
3554 .html_serialize(cx, ChildrenOnly(Some(qname)), false, vec![])
3555 } else {
3556 self.upcast::<Node>()
3557 .xml_serialize(XmlChildrenOnly(Some(qname)))?
3558 };
3559
3560 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3561 }
3562
3563 fn SetInnerHTML(
3565 &self,
3566 cx: &mut JSContext,
3567 value: TrustedHTMLOrNullIsEmptyString,
3568 ) -> ErrorResult {
3569 let value = TrustedHTML::get_trusted_type_compliant_string(
3573 cx,
3574 &self.owner_global(),
3575 value.convert(),
3576 "Element innerHTML",
3577 )?;
3578 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3580 DomRoot::upcast(template.Content(cx))
3583 } else {
3584 DomRoot::from_ref(self.upcast())
3586 };
3587
3588 if !self.node.has_weird_parser_insertion_mode() &&
3591 value.len() < 100 &&
3592 !value
3593 .as_bytes()
3594 .iter()
3595 .any(|c| matches!(*c, b'&' | b'\0' | b'<' | b'\r'))
3596 {
3597 return Node::SetTextContent(&target, cx, Some(value));
3598 }
3599
3600 let frag = self.parse_fragment(value, cx)?;
3603
3604 Node::replace_all(cx, Some(frag.upcast()), &target);
3606 Ok(())
3607 }
3608
3609 fn GetOuterHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3611 let result = if self.owner_document().is_html_document() {
3614 self.upcast::<Node>()
3615 .html_serialize(cx, IncludeNode, false, vec![])
3616 } else {
3617 self.upcast::<Node>().xml_serialize(XmlIncludeNode)?
3618 };
3619
3620 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3621 }
3622
3623 fn SetOuterHTML(
3625 &self,
3626 cx: &mut JSContext,
3627 value: TrustedHTMLOrNullIsEmptyString,
3628 ) -> ErrorResult {
3629 let value = TrustedHTML::get_trusted_type_compliant_string(
3633 cx,
3634 &self.owner_global(),
3635 value.convert(),
3636 "Element outerHTML",
3637 )?;
3638 let context_document = self.owner_document();
3639 let context_node = self.upcast::<Node>();
3640 let context_parent = match context_node.GetParentNode() {
3642 None => {
3643 return Ok(());
3646 },
3647 Some(parent) => parent,
3648 };
3649
3650 let parent = match context_parent.type_id() {
3651 NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed(None)),
3653
3654 NodeTypeId::DocumentFragment(_) => {
3657 let body_elem = Element::create(
3658 cx,
3659 QualName::new(None, ns!(html), local_name!("body")),
3660 None,
3661 &context_document,
3662 ElementCreator::ScriptCreated,
3663 CustomElementCreationMode::Synchronous,
3664 None,
3665 );
3666 DomRoot::upcast(body_elem)
3667 },
3668 _ => context_node.GetParentElement().unwrap(),
3669 };
3670
3671 let frag = parent.parse_fragment(value, cx)?;
3674 context_parent.ReplaceChild(cx, frag.upcast(), context_node)?;
3676 Ok(())
3677 }
3678
3679 fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
3681 self.upcast::<Node>()
3682 .preceding_siblings()
3683 .find_map(DomRoot::downcast)
3684 }
3685
3686 fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
3688 self.upcast::<Node>()
3689 .following_siblings()
3690 .find_map(DomRoot::downcast)
3691 }
3692
3693 fn Children(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
3695 let window = self.owner_window();
3696 HTMLCollection::children(cx, &window, self.upcast())
3697 }
3698
3699 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
3701 self.upcast::<Node>().child_elements().next()
3702 }
3703
3704 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
3706 self.upcast::<Node>()
3707 .rev_children()
3708 .find_map(DomRoot::downcast::<Element>)
3709 }
3710
3711 fn ChildElementCount(&self) -> u32 {
3713 self.upcast::<Node>().child_elements().count() as u32
3714 }
3715
3716 fn Prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3718 self.upcast::<Node>().prepend(cx, nodes)
3719 }
3720
3721 fn Append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3723 self.upcast::<Node>().append(cx, nodes)
3724 }
3725
3726 fn ReplaceChildren(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3728 self.upcast::<Node>().replace_children(cx, nodes)
3729 }
3730
3731 fn MoveBefore(&self, cx: &mut JSContext, node: &Node, child: Option<&Node>) -> ErrorResult {
3733 self.upcast::<Node>().move_before(cx, node, child)
3734 }
3735
3736 fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
3738 let root = self.upcast::<Node>();
3739 root.query_selector(selectors)
3740 }
3741
3742 fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>> {
3744 let root = self.upcast::<Node>();
3745 root.query_selector_all(selectors)
3746 }
3747
3748 fn Before(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3750 self.upcast::<Node>().before(cx, nodes)
3751 }
3752
3753 fn After(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3755 self.upcast::<Node>().after(cx, nodes)
3756 }
3757
3758 fn ReplaceWith(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3760 self.upcast::<Node>().replace_with(cx, nodes)
3761 }
3762
3763 fn Remove(&self, cx: &mut JSContext) {
3765 self.upcast::<Node>().remove_self(cx);
3766 }
3767
3768 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
3770 fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
3771 let document = self.owner_document();
3772 let url = document.url();
3773 let selectors = match SelectorParser::parse_author_origin_no_namespace(
3774 &selectors.str(),
3775 &UrlExtraData(url.get_arc()),
3776 ) {
3777 Err(_) => return Err(Error::Syntax(None)),
3778 Ok(selectors) => selectors,
3779 };
3780
3781 let traced_self = Dom::from_ref(self);
3783 let quirks_mode = document.quirks_mode();
3784 Ok(with_layout_state(|| {
3785 #[expect(unsafe_code)]
3786 let layout_element = unsafe { traced_self.to_layout() };
3787 dom_apis::element_matches(
3788 &ServoDangerousStyleElement::from(layout_element.upcast()),
3789 &selectors,
3790 quirks_mode,
3791 )
3792 }))
3793 }
3794
3795 fn WebkitMatchesSelector(&self, selectors: DOMString) -> Fallible<bool> {
3797 self.Matches(selectors)
3798 }
3799
3800 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
3802 fn Closest(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
3803 let document = self.owner_document();
3804 let url = document.url();
3805 let selectors = match SelectorParser::parse_author_origin_no_namespace(
3806 &selectors.str(),
3807 &UrlExtraData(url.get_arc()),
3808 ) {
3809 Err(_) => return Err(Error::Syntax(None)),
3810 Ok(selectors) => selectors,
3811 };
3812
3813 let traced_self = Dom::from_ref(self);
3815 let quirks_mode = document.quirks_mode();
3816 let closest_element = with_layout_state(|| {
3817 #[expect(unsafe_code)]
3818 let layout_element = unsafe { traced_self.to_layout() };
3819 dom_apis::element_closest(
3820 ServoDangerousStyleElement::from(layout_element.upcast()),
3821 &selectors,
3822 quirks_mode,
3823 )
3824 });
3825 Ok(closest_element.map(ServoDangerousStyleElement::rooted))
3826 }
3827
3828 fn InsertAdjacentElement(
3830 &self,
3831 cx: &mut JSContext,
3832 where_: DOMString,
3833 element: &Element,
3834 ) -> Fallible<Option<DomRoot<Element>>> {
3835 let where_ = where_.parse::<AdjacentPosition>()?;
3836 let inserted_node = self.insert_adjacent(cx, where_, element.upcast())?;
3837 Ok(inserted_node.map(|node| DomRoot::downcast(node).unwrap()))
3838 }
3839
3840 fn InsertAdjacentText(
3842 &self,
3843 cx: &mut JSContext,
3844 where_: DOMString,
3845 data: DOMString,
3846 ) -> ErrorResult {
3847 let text = Text::new(cx, data, &self.owner_document());
3849
3850 let where_ = where_.parse::<AdjacentPosition>()?;
3852 self.insert_adjacent(cx, where_, text.upcast()).map(|_| ())
3853 }
3854
3855 fn InsertAdjacentHTML(
3857 &self,
3858 cx: &mut JSContext,
3859 position: DOMString,
3860 text: TrustedHTMLOrString,
3861 ) -> ErrorResult {
3862 let text = TrustedHTML::get_trusted_type_compliant_string(
3866 cx,
3867 &self.owner_global(),
3868 text,
3869 "Element insertAdjacentHTML",
3870 )?;
3871 let position = position.parse::<AdjacentPosition>()?;
3872
3873 let context = match position {
3876 AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => {
3879 match self.upcast::<Node>().GetParentNode() {
3880 Some(ref node) if node.is::<Document>() => {
3882 return Err(Error::NoModificationAllowed(None));
3883 },
3884 None => return Err(Error::NoModificationAllowed(None)),
3885 Some(node) => node,
3887 }
3888 },
3889 AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => {
3892 DomRoot::from_ref(self.upcast::<Node>())
3894 },
3895 };
3896
3897 let context = Element::fragment_parsing_context(
3899 cx,
3900 &context.owner_doc(),
3901 context.downcast::<Element>(),
3902 );
3903
3904 let fragment = context.parse_fragment(text, cx)?;
3907
3908 self.insert_adjacent(cx, position, fragment.upcast())
3910 .map(|_| ())
3911 }
3912
3913 fn EnterFormalActivationState(&self) -> ErrorResult {
3915 match self.as_maybe_activatable() {
3916 Some(a) => {
3917 a.enter_formal_activation_state();
3918 Ok(())
3919 },
3920 None => Err(Error::NotSupported(None)),
3921 }
3922 }
3923
3924 fn ExitFormalActivationState(&self) -> ErrorResult {
3925 match self.as_maybe_activatable() {
3926 Some(a) => {
3927 a.exit_formal_activation_state();
3928 Ok(())
3929 },
3930 None => Err(Error::NotSupported(None)),
3931 }
3932 }
3933
3934 fn RequestFullscreen(&self, can_gc: CanGc) -> Rc<Promise> {
3936 let doc = self.owner_document();
3937 doc.enter_fullscreen(self, can_gc)
3938 }
3939
3940 fn AttachShadow(
3942 &self,
3943 cx: &mut JSContext,
3944 init: &ShadowRootInit,
3945 ) -> Fallible<DomRoot<ShadowRoot>> {
3946 let shadow_root = self.attach_shadow(
3949 cx,
3950 IsUserAgentWidget::No,
3951 init.mode,
3952 init.clonable,
3953 init.serializable,
3954 init.delegatesFocus,
3955 init.slotAssignment,
3956 )?;
3957
3958 Ok(shadow_root)
3960 }
3961
3962 fn GetShadowRoot(&self) -> Option<DomRoot<ShadowRoot>> {
3964 let shadow_or_none = self.shadow_root();
3966
3967 let shadow = shadow_or_none?;
3969 if shadow.Mode() == ShadowRootMode::Closed {
3970 return None;
3971 }
3972
3973 Some(shadow)
3975 }
3976
3977 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
3979 self.custom_element_registry()
3981 }
3982
3983 fn GetRole(&self) -> Option<DOMString> {
3985 self.get_nullable_string_attribute(&local_name!("role"))
3986 }
3987
3988 fn SetRole(&self, cx: &mut JSContext, value: Option<DOMString>) {
3990 self.set_nullable_string_attribute(cx, &local_name!("role"), value);
3991 }
3992
3993 fn GetAriaAtomic(&self) -> Option<DOMString> {
3994 self.get_nullable_string_attribute(&local_name!("aria-atomic"))
3995 }
3996
3997 fn SetAriaAtomic(&self, cx: &mut JSContext, value: Option<DOMString>) {
3998 self.set_nullable_string_attribute(cx, &local_name!("aria-atomic"), value);
3999 }
4000
4001 fn GetAriaAutoComplete(&self) -> Option<DOMString> {
4002 self.get_nullable_string_attribute(&local_name!("aria-autocomplete"))
4003 }
4004
4005 fn SetAriaAutoComplete(&self, cx: &mut JSContext, value: Option<DOMString>) {
4006 self.set_nullable_string_attribute(cx, &local_name!("aria-autocomplete"), value);
4007 }
4008
4009 fn GetAriaBrailleLabel(&self) -> Option<DOMString> {
4010 self.get_nullable_string_attribute(&local_name!("aria-braillelabel"))
4011 }
4012
4013 fn SetAriaBrailleLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4014 self.set_nullable_string_attribute(cx, &local_name!("aria-braillelabel"), value);
4015 }
4016
4017 fn GetAriaBrailleRoleDescription(&self) -> Option<DOMString> {
4018 self.get_nullable_string_attribute(&local_name!("aria-brailleroledescription"))
4019 }
4020
4021 fn SetAriaBrailleRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4022 self.set_nullable_string_attribute(cx, &local_name!("aria-brailleroledescription"), value);
4023 }
4024
4025 fn GetAriaBusy(&self) -> Option<DOMString> {
4026 self.get_nullable_string_attribute(&local_name!("aria-busy"))
4027 }
4028
4029 fn SetAriaBusy(&self, cx: &mut JSContext, value: Option<DOMString>) {
4030 self.set_nullable_string_attribute(cx, &local_name!("aria-busy"), value);
4031 }
4032
4033 fn GetAriaChecked(&self) -> Option<DOMString> {
4034 self.get_nullable_string_attribute(&local_name!("aria-checked"))
4035 }
4036
4037 fn SetAriaChecked(&self, cx: &mut JSContext, value: Option<DOMString>) {
4038 self.set_nullable_string_attribute(cx, &local_name!("aria-checked"), value);
4039 }
4040
4041 fn GetAriaColCount(&self) -> Option<DOMString> {
4042 self.get_nullable_string_attribute(&local_name!("aria-colcount"))
4043 }
4044
4045 fn SetAriaColCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4046 self.set_nullable_string_attribute(cx, &local_name!("aria-colcount"), value);
4047 }
4048
4049 fn GetAriaColIndex(&self) -> Option<DOMString> {
4050 self.get_nullable_string_attribute(&local_name!("aria-colindex"))
4051 }
4052
4053 fn SetAriaColIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4054 self.set_nullable_string_attribute(cx, &local_name!("aria-colindex"), value);
4055 }
4056
4057 fn GetAriaColIndexText(&self) -> Option<DOMString> {
4058 self.get_nullable_string_attribute(&local_name!("aria-colindextext"))
4059 }
4060
4061 fn SetAriaColIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4062 self.set_nullable_string_attribute(cx, &local_name!("aria-colindextext"), value);
4063 }
4064
4065 fn GetAriaColSpan(&self) -> Option<DOMString> {
4066 self.get_nullable_string_attribute(&local_name!("aria-colspan"))
4067 }
4068
4069 fn SetAriaColSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4070 self.set_nullable_string_attribute(cx, &local_name!("aria-colspan"), value);
4071 }
4072
4073 fn GetAriaCurrent(&self) -> Option<DOMString> {
4074 self.get_nullable_string_attribute(&local_name!("aria-current"))
4075 }
4076
4077 fn SetAriaCurrent(&self, cx: &mut JSContext, value: Option<DOMString>) {
4078 self.set_nullable_string_attribute(cx, &local_name!("aria-current"), value);
4079 }
4080
4081 fn GetAriaDescription(&self) -> Option<DOMString> {
4082 self.get_nullable_string_attribute(&local_name!("aria-description"))
4083 }
4084
4085 fn SetAriaDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4086 self.set_nullable_string_attribute(cx, &local_name!("aria-description"), value);
4087 }
4088
4089 fn GetAriaDisabled(&self) -> Option<DOMString> {
4090 self.get_nullable_string_attribute(&local_name!("aria-disabled"))
4091 }
4092
4093 fn SetAriaDisabled(&self, cx: &mut JSContext, value: Option<DOMString>) {
4094 self.set_nullable_string_attribute(cx, &local_name!("aria-disabled"), value);
4095 }
4096
4097 fn GetAriaExpanded(&self) -> Option<DOMString> {
4098 self.get_nullable_string_attribute(&local_name!("aria-expanded"))
4099 }
4100
4101 fn SetAriaExpanded(&self, cx: &mut JSContext, value: Option<DOMString>) {
4102 self.set_nullable_string_attribute(cx, &local_name!("aria-expanded"), value);
4103 }
4104
4105 fn GetAriaHasPopup(&self) -> Option<DOMString> {
4106 self.get_nullable_string_attribute(&local_name!("aria-haspopup"))
4107 }
4108
4109 fn SetAriaHasPopup(&self, cx: &mut JSContext, value: Option<DOMString>) {
4110 self.set_nullable_string_attribute(cx, &local_name!("aria-haspopup"), value);
4111 }
4112
4113 fn GetAriaHidden(&self) -> Option<DOMString> {
4114 self.get_nullable_string_attribute(&local_name!("aria-hidden"))
4115 }
4116
4117 fn SetAriaHidden(&self, cx: &mut JSContext, value: Option<DOMString>) {
4118 self.set_nullable_string_attribute(cx, &local_name!("aria-hidden"), value);
4119 }
4120
4121 fn GetAriaInvalid(&self) -> Option<DOMString> {
4122 self.get_nullable_string_attribute(&local_name!("aria-invalid"))
4123 }
4124
4125 fn SetAriaInvalid(&self, cx: &mut JSContext, value: Option<DOMString>) {
4126 self.set_nullable_string_attribute(cx, &local_name!("aria-invalid"), value);
4127 }
4128
4129 fn GetAriaKeyShortcuts(&self) -> Option<DOMString> {
4130 self.get_nullable_string_attribute(&local_name!("aria-keyshortcuts"))
4131 }
4132
4133 fn SetAriaKeyShortcuts(&self, cx: &mut JSContext, value: Option<DOMString>) {
4134 self.set_nullable_string_attribute(cx, &local_name!("aria-keyshortcuts"), value);
4135 }
4136
4137 fn GetAriaLabel(&self) -> Option<DOMString> {
4138 self.get_nullable_string_attribute(&local_name!("aria-label"))
4139 }
4140
4141 fn SetAriaLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4142 self.set_nullable_string_attribute(cx, &local_name!("aria-label"), value);
4143 }
4144
4145 fn GetAriaLevel(&self) -> Option<DOMString> {
4146 self.get_nullable_string_attribute(&local_name!("aria-level"))
4147 }
4148
4149 fn SetAriaLevel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4150 self.set_nullable_string_attribute(cx, &local_name!("aria-level"), value);
4151 }
4152
4153 fn GetAriaLive(&self) -> Option<DOMString> {
4154 self.get_nullable_string_attribute(&local_name!("aria-live"))
4155 }
4156
4157 fn SetAriaLive(&self, cx: &mut JSContext, value: Option<DOMString>) {
4158 self.set_nullable_string_attribute(cx, &local_name!("aria-live"), value);
4159 }
4160
4161 fn GetAriaModal(&self) -> Option<DOMString> {
4162 self.get_nullable_string_attribute(&local_name!("aria-modal"))
4163 }
4164
4165 fn SetAriaModal(&self, cx: &mut JSContext, value: Option<DOMString>) {
4166 self.set_nullable_string_attribute(cx, &local_name!("aria-modal"), value);
4167 }
4168
4169 fn GetAriaMultiLine(&self) -> Option<DOMString> {
4170 self.get_nullable_string_attribute(&local_name!("aria-multiline"))
4171 }
4172
4173 fn SetAriaMultiLine(&self, cx: &mut JSContext, value: Option<DOMString>) {
4174 self.set_nullable_string_attribute(cx, &local_name!("aria-multiline"), value);
4175 }
4176
4177 fn GetAriaMultiSelectable(&self) -> Option<DOMString> {
4178 self.get_nullable_string_attribute(&local_name!("aria-multiselectable"))
4179 }
4180
4181 fn SetAriaMultiSelectable(&self, cx: &mut JSContext, value: Option<DOMString>) {
4182 self.set_nullable_string_attribute(cx, &local_name!("aria-multiselectable"), value);
4183 }
4184
4185 fn GetAriaOrientation(&self) -> Option<DOMString> {
4186 self.get_nullable_string_attribute(&local_name!("aria-orientation"))
4187 }
4188
4189 fn SetAriaOrientation(&self, cx: &mut JSContext, value: Option<DOMString>) {
4190 self.set_nullable_string_attribute(cx, &local_name!("aria-orientation"), value);
4191 }
4192
4193 fn GetAriaPlaceholder(&self) -> Option<DOMString> {
4194 self.get_nullable_string_attribute(&local_name!("aria-placeholder"))
4195 }
4196
4197 fn SetAriaPlaceholder(&self, cx: &mut JSContext, value: Option<DOMString>) {
4198 self.set_nullable_string_attribute(cx, &local_name!("aria-placeholder"), value);
4199 }
4200
4201 fn GetAriaPosInSet(&self) -> Option<DOMString> {
4202 self.get_nullable_string_attribute(&local_name!("aria-posinset"))
4203 }
4204
4205 fn SetAriaPosInSet(&self, cx: &mut JSContext, value: Option<DOMString>) {
4206 self.set_nullable_string_attribute(cx, &local_name!("aria-posinset"), value);
4207 }
4208
4209 fn GetAriaPressed(&self) -> Option<DOMString> {
4210 self.get_nullable_string_attribute(&local_name!("aria-pressed"))
4211 }
4212
4213 fn SetAriaPressed(&self, cx: &mut JSContext, value: Option<DOMString>) {
4214 self.set_nullable_string_attribute(cx, &local_name!("aria-pressed"), value);
4215 }
4216
4217 fn GetAriaReadOnly(&self) -> Option<DOMString> {
4218 self.get_nullable_string_attribute(&local_name!("aria-readonly"))
4219 }
4220
4221 fn SetAriaReadOnly(&self, cx: &mut JSContext, value: Option<DOMString>) {
4222 self.set_nullable_string_attribute(cx, &local_name!("aria-readonly"), value);
4223 }
4224
4225 fn GetAriaRelevant(&self) -> Option<DOMString> {
4226 self.get_nullable_string_attribute(&local_name!("aria-relevant"))
4227 }
4228
4229 fn SetAriaRelevant(&self, cx: &mut JSContext, value: Option<DOMString>) {
4230 self.set_nullable_string_attribute(cx, &local_name!("aria-relevant"), value);
4231 }
4232
4233 fn GetAriaRequired(&self) -> Option<DOMString> {
4234 self.get_nullable_string_attribute(&local_name!("aria-required"))
4235 }
4236
4237 fn SetAriaRequired(&self, cx: &mut JSContext, value: Option<DOMString>) {
4238 self.set_nullable_string_attribute(cx, &local_name!("aria-required"), value);
4239 }
4240
4241 fn GetAriaRoleDescription(&self) -> Option<DOMString> {
4242 self.get_nullable_string_attribute(&local_name!("aria-roledescription"))
4243 }
4244
4245 fn SetAriaRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4246 self.set_nullable_string_attribute(cx, &local_name!("aria-roledescription"), value);
4247 }
4248
4249 fn GetAriaRowCount(&self) -> Option<DOMString> {
4250 self.get_nullable_string_attribute(&local_name!("aria-rowcount"))
4251 }
4252
4253 fn SetAriaRowCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4254 self.set_nullable_string_attribute(cx, &local_name!("aria-rowcount"), value);
4255 }
4256
4257 fn GetAriaRowIndex(&self) -> Option<DOMString> {
4258 self.get_nullable_string_attribute(&local_name!("aria-rowindex"))
4259 }
4260
4261 fn SetAriaRowIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4262 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindex"), value);
4263 }
4264
4265 fn GetAriaRowIndexText(&self) -> Option<DOMString> {
4266 self.get_nullable_string_attribute(&local_name!("aria-rowindextext"))
4267 }
4268
4269 fn SetAriaRowIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4270 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindextext"), value);
4271 }
4272
4273 fn GetAriaRowSpan(&self) -> Option<DOMString> {
4274 self.get_nullable_string_attribute(&local_name!("aria-rowspan"))
4275 }
4276
4277 fn SetAriaRowSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4278 self.set_nullable_string_attribute(cx, &local_name!("aria-rowspan"), value);
4279 }
4280
4281 fn GetAriaSelected(&self) -> Option<DOMString> {
4282 self.get_nullable_string_attribute(&local_name!("aria-selected"))
4283 }
4284
4285 fn SetAriaSelected(&self, cx: &mut JSContext, value: Option<DOMString>) {
4286 self.set_nullable_string_attribute(cx, &local_name!("aria-selected"), value);
4287 }
4288
4289 fn GetAriaSetSize(&self) -> Option<DOMString> {
4290 self.get_nullable_string_attribute(&local_name!("aria-setsize"))
4291 }
4292
4293 fn SetAriaSetSize(&self, cx: &mut JSContext, value: Option<DOMString>) {
4294 self.set_nullable_string_attribute(cx, &local_name!("aria-setsize"), value);
4295 }
4296
4297 fn GetAriaSort(&self) -> Option<DOMString> {
4298 self.get_nullable_string_attribute(&local_name!("aria-sort"))
4299 }
4300
4301 fn SetAriaSort(&self, cx: &mut JSContext, value: Option<DOMString>) {
4302 self.set_nullable_string_attribute(cx, &local_name!("aria-sort"), value);
4303 }
4304
4305 fn GetAriaValueMax(&self) -> Option<DOMString> {
4306 self.get_nullable_string_attribute(&local_name!("aria-valuemax"))
4307 }
4308
4309 fn SetAriaValueMax(&self, cx: &mut JSContext, value: Option<DOMString>) {
4310 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemax"), value);
4311 }
4312
4313 fn GetAriaValueMin(&self) -> Option<DOMString> {
4314 self.get_nullable_string_attribute(&local_name!("aria-valuemin"))
4315 }
4316
4317 fn SetAriaValueMin(&self, cx: &mut JSContext, value: Option<DOMString>) {
4318 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemin"), value);
4319 }
4320
4321 fn GetAriaValueNow(&self) -> Option<DOMString> {
4322 self.get_nullable_string_attribute(&local_name!("aria-valuenow"))
4323 }
4324
4325 fn SetAriaValueNow(&self, cx: &mut JSContext, value: Option<DOMString>) {
4326 self.set_nullable_string_attribute(cx, &local_name!("aria-valuenow"), value);
4327 }
4328
4329 fn GetAriaValueText(&self) -> Option<DOMString> {
4330 self.get_nullable_string_attribute(&local_name!("aria-valuetext"))
4331 }
4332
4333 fn SetAriaValueText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4334 self.set_nullable_string_attribute(cx, &local_name!("aria-valuetext"), value);
4335 }
4336
4337 fn GetAssignedSlot(&self) -> Option<DomRoot<HTMLSlotElement>> {
4339 let cx = GlobalScope::get_cx();
4340
4341 rooted!(in(*cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4344 slottable.find_a_slot(true)
4345 }
4346
4347 fn Part(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
4349 self.ensure_rare_data()
4350 .part
4351 .or_init(|| DOMTokenList::new(cx, self, &local_name!("part"), None))
4352 }
4353}
4354
4355impl VirtualMethods for Element {
4356 fn super_type(&self) -> Option<&dyn VirtualMethods> {
4357 Some(self.upcast::<Node>() as &dyn VirtualMethods)
4358 }
4359
4360 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
4361 if attr.local_name() == &local_name!("lang") {
4363 return true;
4364 }
4365
4366 self.super_type()
4367 .unwrap()
4368 .attribute_affects_presentational_hints(attr)
4369 }
4370
4371 fn attribute_mutated(
4372 &self,
4373 cx: &mut JSContext,
4374 attr: AttrRef<'_>,
4375 mutation: AttributeMutation,
4376 ) {
4377 self.super_type()
4378 .unwrap()
4379 .attribute_mutated(cx, attr, mutation);
4380 let node = self.upcast::<Node>();
4381 let doc = node.owner_doc();
4382 match *attr.local_name() {
4383 local_name!("style") => self.update_style_attribute(attr, mutation),
4384 local_name!("id") => {
4385 *self.id_attribute.borrow_mut() = mutation.new_value(attr).and_then(|value| {
4387 let value = value.as_atom();
4388 if value != &atom!("") {
4389 Some(value.clone())
4391 } else {
4392 None
4394 }
4395 });
4396
4397 let containing_shadow_root = self.containing_shadow_root();
4398 if node.is_in_a_document_tree() || node.is_in_a_shadow_tree() {
4399 let value = attr.value().as_atom().clone();
4400 match mutation {
4401 AttributeMutation::Set(old_value, _) => {
4402 if let Some(old_value) = old_value {
4403 let old_value = old_value.as_atom().clone();
4404 if let Some(ref shadow_root) = containing_shadow_root {
4405 shadow_root.unregister_element_id(
4406 self,
4407 old_value,
4408 CanGc::from_cx(cx),
4409 );
4410 } else {
4411 doc.unregister_element_id(self, old_value, CanGc::from_cx(cx));
4412 }
4413 }
4414 if value != atom!("") {
4415 if let Some(ref shadow_root) = containing_shadow_root {
4416 shadow_root.register_element_id(
4417 self,
4418 value,
4419 CanGc::from_cx(cx),
4420 );
4421 } else {
4422 doc.register_element_id(self, value, CanGc::from_cx(cx));
4423 }
4424 }
4425 },
4426 AttributeMutation::Removed => {
4427 if value != atom!("") {
4428 if let Some(ref shadow_root) = containing_shadow_root {
4429 shadow_root.unregister_element_id(
4430 self,
4431 value,
4432 CanGc::from_cx(cx),
4433 );
4434 } else {
4435 doc.unregister_element_id(self, value, CanGc::from_cx(cx));
4436 }
4437 }
4438 },
4439 }
4440 }
4441 },
4442 local_name!("name") => {
4443 self.ensure_rare_data().name_attribute =
4445 mutation.new_value(attr).and_then(|value| {
4446 let value = value.as_atom();
4447 if value != &atom!("") {
4448 Some(value.clone())
4449 } else {
4450 None
4451 }
4452 });
4453 if node.is_connected() && node.containing_shadow_root().is_none() {
4456 let value = attr.value().as_atom().clone();
4457 match mutation {
4458 AttributeMutation::Set(old_value, _) => {
4459 if let Some(old_value) = old_value {
4460 let old_value = old_value.as_atom().clone();
4461 doc.unregister_element_name(self, old_value);
4462 }
4463 if value != atom!("") {
4464 doc.register_element_name(self, value);
4465 }
4466 },
4467 AttributeMutation::Removed => {
4468 if value != atom!("") {
4469 doc.unregister_element_name(self, value);
4470 }
4471 },
4472 }
4473 }
4474 },
4475 local_name!("slot") => {
4476 rooted!(&in(cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4478
4479 if let Some(assigned_slot) = slottable.assigned_slot() {
4481 assigned_slot.assign_slottables(cx.no_gc());
4482 }
4483 slottable.assign_a_slot(cx.no_gc());
4484 },
4485 _ => {
4486 if attr.namespace() == &ns!() && attr.local_name() == &local_name!("src") {
4489 node.dirty(NodeDamage::Other);
4490 }
4491 },
4492 };
4493
4494 if self
4497 .upcast::<Node>()
4498 .get_flag(NodeFlags::USES_ATTR_IN_CONTENT_ATTRIBUTE)
4499 {
4500 node.dirty(NodeDamage::ContentOrHeritage);
4501 }
4502
4503 node.rev_version();
4507
4508 let window = self.owner_window();
4510 if window.live_devtools_updates() {
4511 let global = window.upcast::<GlobalScope>();
4512 if let Some(sender) = global.devtools_chan() {
4513 let pipeline_id = global.pipeline_id();
4514 if ScriptThread::devtools_want_updates_for_node(pipeline_id, self.upcast()) {
4515 let devtools_message = ScriptToDevtoolsControlMsg::DomMutation(
4516 pipeline_id,
4517 DomMutation::AttributeModified {
4518 node: self.upcast::<Node>().unique_id(pipeline_id),
4519 attribute_name: attr.local_name().to_string(),
4520 new_value: mutation.new_value(attr).map(|value| value.to_string()),
4521 },
4522 );
4523 sender.send(devtools_message).unwrap();
4524 }
4525 }
4526 }
4527 }
4528
4529 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
4530 match *name {
4531 local_name!("id") => AttrValue::Atom(value.into()),
4532 local_name!("name") => AttrValue::Atom(value.into()),
4533 local_name!("class") | local_name!("part") => {
4534 AttrValue::from_serialized_tokenlist(value.into())
4535 },
4536 local_name!("exportparts") => AttrValue::from_shadow_parts(value.into()),
4537 local_name!("tabindex") => AttrValue::from_i32(value.into(), -1),
4538 _ => self
4539 .super_type()
4540 .unwrap()
4541 .parse_plain_attribute(name, value),
4542 }
4543 }
4544
4545 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
4546 if let Some(s) = self.super_type() {
4547 s.bind_to_tree(cx, context);
4548 }
4549
4550 if let Some(f) = self.as_maybe_form_control() {
4551 f.bind_form_control_to_tree(CanGc::from_cx(cx));
4552 }
4553
4554 let doc = self.owner_document();
4555
4556 if let Some(ref shadow_root) = self.shadow_root() {
4557 shadow_root.bind_to_tree(cx, context);
4558 }
4559
4560 if !context.is_in_tree() {
4561 return;
4562 }
4563
4564 if let Some(ref id) = *self.id_attribute.borrow() {
4565 if let Some(shadow_root) = self.containing_shadow_root() {
4566 shadow_root.register_element_id(self, id.clone(), CanGc::from_cx(cx));
4567 } else {
4568 doc.register_element_id(self, id.clone(), CanGc::from_cx(cx));
4569 }
4570 }
4571 if let Some(ref name) = self.name_attribute() &&
4572 self.containing_shadow_root().is_none()
4573 {
4574 doc.register_element_name(self, name.clone());
4575 }
4576 }
4577
4578 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
4579 self.super_type().unwrap().unbind_from_tree(cx, context);
4580
4581 if let Some(f) = self.as_maybe_form_control() {
4582 f.unbind_form_control_from_tree(CanGc::from_cx(cx));
4586 }
4587
4588 if !context.tree_is_in_a_document_tree && !context.tree_is_in_a_shadow_tree {
4589 return;
4590 }
4591
4592 let doc = self.owner_document();
4593
4594 let fullscreen = doc.fullscreen_element();
4595 if fullscreen.as_deref() == Some(self) {
4596 doc.exit_fullscreen(CanGc::from_cx(cx));
4597 }
4598 if let Some(ref value) = *self.id_attribute.borrow() {
4599 if let Some(ref shadow_root) = self.containing_shadow_root() {
4600 if !self.upcast::<Node>().is_in_a_shadow_tree() {
4603 shadow_root.unregister_element_id(self, value.clone(), CanGc::from_cx(cx));
4604 }
4605 } else {
4606 doc.unregister_element_id(self, value.clone(), CanGc::from_cx(cx));
4607 }
4608 }
4609 if let Some(ref value) = self.name_attribute() &&
4610 self.containing_shadow_root().is_none()
4611 {
4612 doc.unregister_element_name(self, value.clone());
4613 }
4614 }
4615
4616 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
4617 if let Some(s) = self.super_type() {
4618 s.children_changed(cx, mutation);
4619 }
4620
4621 let flags = self.get_selector_flags();
4622 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
4623 self.upcast::<Node>().dirty(NodeDamage::Other);
4625 } else {
4626 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) &&
4627 let Some(next_child) = mutation.next_child()
4628 {
4629 for child in next_child.inclusively_following_siblings_unrooted(cx.no_gc()) {
4630 if child.is::<Element>() {
4631 child.dirty(NodeDamage::Other);
4632 }
4633 }
4634 }
4635 if flags.intersects(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) &&
4636 let Some(child) = mutation.modified_edge_element(cx.no_gc())
4637 {
4638 child.dirty(NodeDamage::Other);
4639 }
4640 }
4641 }
4642
4643 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
4644 self.super_type().unwrap().adopting_steps(cx, old_doc);
4645
4646 if self.owner_document().is_html_document() != old_doc.is_html_document() {
4647 self.tag_name.clear();
4648 }
4649 }
4650
4651 fn post_connection_steps(&self, cx: &mut JSContext) {
4652 if let Some(s) = self.super_type() {
4653 s.post_connection_steps(cx);
4654 }
4655
4656 self.update_nonce_post_connection(cx);
4657 }
4658
4659 fn cloning_steps(
4661 &self,
4662 cx: &mut JSContext,
4663 copy: &Node,
4664 maybe_doc: Option<&Document>,
4665 clone_children: CloneChildrenFlag,
4666 ) {
4667 if let Some(s) = self.super_type() {
4668 s.cloning_steps(cx, copy, maybe_doc, clone_children);
4669 }
4670 let elem = copy.downcast::<Element>().unwrap();
4671 if let Some(rare_data) = self.rare_data().as_ref() {
4672 elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone());
4673 }
4674 }
4675}
4676impl Element {
4677 pub(crate) fn client_rect(&self) -> Rect<i32, CSSPixel> {
4678 let doc = self.node.owner_doc();
4679
4680 if let Some(rect) = self
4681 .rare_data()
4682 .as_ref()
4683 .and_then(|data| data.client_rect.as_ref())
4684 .and_then(|rect| rect.get().ok()) &&
4685 doc.restyle_reason().is_empty()
4686 {
4687 return rect;
4688 }
4689
4690 let mut rect = self.upcast::<Node>().client_rect();
4691 let in_quirks_mode = doc.quirks_mode() == QuirksMode::Quirks;
4692
4693 if (in_quirks_mode && doc.GetBody().as_deref() == self.downcast::<HTMLElement>()) ||
4694 (!in_quirks_mode && self.is_document_element())
4695 {
4696 rect.size = doc.window().viewport_details().size.round().to_i32();
4697 }
4698
4699 self.ensure_rare_data().client_rect = Some(self.owner_window().cache_layout_value(rect));
4700 rect
4701 }
4702
4703 pub(crate) fn as_maybe_activatable(&self) -> Option<&dyn Activatable> {
4704 let element = match self.upcast::<Node>().type_id() {
4705 NodeTypeId::Element(ElementTypeId::HTMLElement(
4706 HTMLElementTypeId::HTMLInputElement,
4707 )) => {
4708 let element = self.downcast::<HTMLInputElement>().unwrap();
4709 Some(element as &dyn Activatable)
4710 },
4711 NodeTypeId::Element(ElementTypeId::HTMLElement(
4712 HTMLElementTypeId::HTMLButtonElement,
4713 )) => {
4714 let element = self.downcast::<HTMLButtonElement>().unwrap();
4715 Some(element as &dyn Activatable)
4716 },
4717 NodeTypeId::Element(ElementTypeId::HTMLElement(
4718 HTMLElementTypeId::HTMLAnchorElement,
4719 )) => {
4720 let element = self.downcast::<HTMLAnchorElement>().unwrap();
4721 Some(element as &dyn Activatable)
4722 },
4723 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) => {
4724 let element = self.downcast::<HTMLAreaElement>().unwrap();
4725 Some(element as &dyn Activatable)
4726 },
4727 NodeTypeId::Element(ElementTypeId::HTMLElement(
4728 HTMLElementTypeId::HTMLLabelElement,
4729 )) => {
4730 let element = self.downcast::<HTMLLabelElement>().unwrap();
4731 Some(element as &dyn Activatable)
4732 },
4733 NodeTypeId::Element(ElementTypeId::HTMLElement(
4734 HTMLElementTypeId::HTMLSelectElement,
4735 )) => {
4736 let element = self.downcast::<HTMLSelectElement>().unwrap();
4737 Some(element as &dyn Activatable)
4738 },
4739 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
4740 let element = self.downcast::<HTMLElement>().unwrap();
4741 Some(element as &dyn Activatable)
4742 },
4743 _ => None,
4744 };
4745 element.and_then(|elem| {
4746 if elem.is_instance_activatable() {
4747 Some(elem)
4748 } else {
4749 None
4750 }
4751 })
4752 }
4753
4754 pub(crate) fn as_stylesheet_owner(&self) -> Option<&dyn StylesheetOwner> {
4755 if let Some(s) = self.downcast::<HTMLStyleElement>() {
4756 return Some(s as &dyn StylesheetOwner);
4757 }
4758
4759 if let Some(l) = self.downcast::<HTMLLinkElement>() {
4760 return Some(l as &dyn StylesheetOwner);
4761 }
4762
4763 None
4764 }
4765
4766 pub(crate) fn as_maybe_validatable(&self) -> Option<&dyn Validatable> {
4768 match self.upcast::<Node>().type_id() {
4769 NodeTypeId::Element(ElementTypeId::HTMLElement(
4770 HTMLElementTypeId::HTMLInputElement,
4771 )) => {
4772 let element = self.downcast::<HTMLInputElement>().unwrap();
4773 Some(element as &dyn Validatable)
4774 },
4775 NodeTypeId::Element(ElementTypeId::HTMLElement(
4776 HTMLElementTypeId::HTMLButtonElement,
4777 )) => {
4778 let element = self.downcast::<HTMLButtonElement>().unwrap();
4779 Some(element as &dyn Validatable)
4780 },
4781 NodeTypeId::Element(ElementTypeId::HTMLElement(
4782 HTMLElementTypeId::HTMLObjectElement,
4783 )) => {
4784 let element = self.downcast::<HTMLObjectElement>().unwrap();
4785 Some(element as &dyn Validatable)
4786 },
4787 NodeTypeId::Element(ElementTypeId::HTMLElement(
4788 HTMLElementTypeId::HTMLSelectElement,
4789 )) => {
4790 let element = self.downcast::<HTMLSelectElement>().unwrap();
4791 Some(element as &dyn Validatable)
4792 },
4793 NodeTypeId::Element(ElementTypeId::HTMLElement(
4794 HTMLElementTypeId::HTMLTextAreaElement,
4795 )) => {
4796 let element = self.downcast::<HTMLTextAreaElement>().unwrap();
4797 Some(element as &dyn Validatable)
4798 },
4799 NodeTypeId::Element(ElementTypeId::HTMLElement(
4800 HTMLElementTypeId::HTMLFieldSetElement,
4801 )) => {
4802 let element = self.downcast::<HTMLFieldSetElement>().unwrap();
4803 Some(element as &dyn Validatable)
4804 },
4805 NodeTypeId::Element(ElementTypeId::HTMLElement(
4806 HTMLElementTypeId::HTMLOutputElement,
4807 )) => {
4808 let element = self.downcast::<HTMLOutputElement>().unwrap();
4809 Some(element as &dyn Validatable)
4810 },
4811 _ => None,
4812 }
4813 }
4814
4815 pub(crate) fn is_invalid(&self, needs_update: bool, can_gc: CanGc) -> bool {
4816 if let Some(validatable) = self.as_maybe_validatable() {
4817 if needs_update {
4818 validatable
4819 .validity_state(can_gc)
4820 .perform_validation_and_update(ValidationFlags::all(), can_gc);
4821 }
4822 return validatable.is_instance_validatable() &&
4823 !validatable.satisfies_constraints(can_gc);
4824 }
4825
4826 if let Some(internals) = self.get_element_internals() {
4827 return internals.is_invalid(can_gc);
4828 }
4829 false
4830 }
4831
4832 pub(crate) fn is_instance_validatable(&self) -> bool {
4833 if let Some(validatable) = self.as_maybe_validatable() {
4834 return validatable.is_instance_validatable();
4835 }
4836 if let Some(internals) = self.get_element_internals() {
4837 return internals.is_instance_validatable();
4838 }
4839 false
4840 }
4841
4842 pub(crate) fn init_state_for_internals(&self) {
4843 self.set_enabled_state(true);
4844 self.set_state(ElementState::VALID, true);
4845 self.set_state(ElementState::INVALID, false);
4846 }
4847
4848 pub(crate) fn click_in_progress(&self) -> bool {
4849 self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
4850 }
4851
4852 pub(crate) fn set_click_in_progress(&self, click: bool) {
4853 self.upcast::<Node>()
4854 .set_flag(NodeFlags::CLICK_IN_PROGRESS, click)
4855 }
4856
4857 pub fn state(&self) -> ElementState {
4858 self.state.get()
4859 }
4860
4861 pub(crate) fn set_state(&self, which: ElementState, value: bool) {
4862 let mut state = self.state.get();
4863 let previous_state = state;
4864 if value {
4865 state.insert(which);
4866 } else {
4867 state.remove(which);
4868 }
4869
4870 if previous_state == state {
4871 return;
4873 }
4874
4875 {
4878 let document = self.owner_document();
4879 let mut entry = document.ensure_pending_restyle(self);
4880 if entry.snapshot.is_none() {
4881 entry.snapshot = Some(Snapshot::new());
4882 }
4883 let snapshot = entry.snapshot.as_mut().unwrap();
4884 if snapshot.state.is_none() {
4885 snapshot.state = Some(self.state());
4886 }
4887 }
4888
4889 self.state.set(state);
4890 }
4891
4892 pub(crate) fn set_active_state(&self, value: bool) {
4894 self.set_state(ElementState::ACTIVE, value);
4895
4896 if let Some(parent) = self.upcast::<Node>().GetParentElement() {
4897 parent.set_active_state(value);
4898 }
4899 }
4900
4901 pub(crate) fn focus_state(&self) -> bool {
4902 self.state.get().contains(ElementState::FOCUS)
4903 }
4904
4905 pub(crate) fn set_focus_state(&self, value: bool) {
4906 self.set_state(ElementState::FOCUS, value);
4907 }
4908
4909 pub(crate) fn set_hover_state(&self, value: bool) {
4910 self.set_state(ElementState::HOVER, value);
4911 }
4912
4913 pub(crate) fn enabled_state(&self) -> bool {
4914 self.state.get().contains(ElementState::ENABLED)
4915 }
4916
4917 pub(crate) fn set_enabled_state(&self, value: bool) {
4918 self.set_state(ElementState::ENABLED, value)
4919 }
4920
4921 pub(crate) fn disabled_state(&self) -> bool {
4922 self.state.get().contains(ElementState::DISABLED)
4923 }
4924
4925 pub(crate) fn set_disabled_state(&self, value: bool) {
4926 self.set_state(ElementState::DISABLED, value)
4927 }
4928
4929 pub(crate) fn read_write_state(&self) -> bool {
4930 self.state.get().contains(ElementState::READWRITE)
4931 }
4932
4933 pub(crate) fn set_read_write_state(&self, value: bool) {
4934 self.set_state(ElementState::READWRITE, value)
4935 }
4936
4937 pub(crate) fn set_open_state(&self, value: bool) {
4938 self.set_state(ElementState::OPEN, value);
4939 }
4940
4941 pub(crate) fn set_placeholder_shown_state(&self, value: bool) {
4942 self.set_state(ElementState::PLACEHOLDER_SHOWN, value);
4943 }
4944
4945 pub(crate) fn set_modal_state(&self, value: bool) {
4946 self.set_state(ElementState::MODAL, value);
4947 }
4948
4949 pub(crate) fn set_target_state(&self, value: bool) {
4950 self.set_state(ElementState::URLTARGET, value)
4951 }
4952
4953 pub(crate) fn set_fullscreen_state(&self, value: bool) {
4954 self.set_state(ElementState::FULLSCREEN, value)
4955 }
4956
4957 pub(crate) fn is_connected(&self) -> bool {
4959 self.upcast::<Node>().is_connected()
4960 }
4961
4962 pub(crate) fn cannot_navigate(&self) -> bool {
4964 let document = self.owner_document();
4965
4966 !document.is_fully_active() ||
4968 (
4969 !self.is::<HTMLAnchorElement>() && !self.is_connected()
4971 )
4972 }
4973}
4974
4975impl Element {
4976 pub(crate) fn check_ancestors_disabled_state_for_form_control(&self) {
4977 let node = self.upcast::<Node>();
4978 if self.disabled_state() {
4979 return;
4980 }
4981 for ancestor in node.ancestors() {
4982 if !ancestor.is::<HTMLFieldSetElement>() {
4983 continue;
4984 }
4985 if !ancestor.downcast::<Element>().unwrap().disabled_state() {
4986 continue;
4987 }
4988 if ancestor.is_parent_of(node) {
4989 self.set_disabled_state(true);
4990 self.set_enabled_state(false);
4991 return;
4992 }
4993 if let Some(ref legend) = ancestor.children().find(|n| n.is::<HTMLLegendElement>()) {
4994 if node.ancestors().any(|ancestor| ancestor == *legend) {
4996 continue;
4997 }
4998 }
4999 self.set_disabled_state(true);
5000 self.set_enabled_state(false);
5001 return;
5002 }
5003 }
5004
5005 pub(crate) fn check_parent_disabled_state_for_option(&self) {
5006 if self.disabled_state() {
5007 return;
5008 }
5009 let node = self.upcast::<Node>();
5010 if let Some(ref parent) = node.GetParentNode() &&
5011 parent.is::<HTMLOptGroupElement>() &&
5012 parent.downcast::<Element>().unwrap().disabled_state()
5013 {
5014 self.set_disabled_state(true);
5015 self.set_enabled_state(false);
5016 }
5017 }
5018
5019 pub(crate) fn check_disabled_attribute(&self) {
5020 let has_disabled_attrib = self.has_attribute(&local_name!("disabled"));
5021 self.set_disabled_state(has_disabled_attrib);
5022 self.set_enabled_state(!has_disabled_attrib);
5023 }
5024
5025 pub(crate) fn update_read_write_state_from_readonly_attribute(&self) {
5026 let has_readonly_attribute = self.has_attribute(&local_name!("readonly"));
5027 self.set_read_write_state(has_readonly_attribute);
5028 }
5029}
5030
5031#[derive(Clone, Copy, PartialEq)]
5032pub(crate) enum AttributeMutationReason {
5033 ByCloning,
5034 ByParser,
5035 Directly,
5036}
5037
5038#[derive(Clone, Copy)]
5039pub(crate) enum AttributeMutation<'a> {
5040 Set(Option<&'a AttrValue>, AttributeMutationReason),
5043
5044 Removed,
5047}
5048
5049impl AttributeMutation<'_> {
5050 pub(crate) fn is_removal(&self) -> bool {
5051 match *self {
5052 AttributeMutation::Removed => true,
5053 AttributeMutation::Set(..) => false,
5054 }
5055 }
5056
5057 pub(crate) fn new_value<'b>(&self, attr: AttrRef<'b>) -> Option<AttrValueRef<'b>> {
5058 match *self {
5059 AttributeMutation::Set(..) => Some(attr.value()),
5060 AttributeMutation::Removed => None,
5061 }
5062 }
5063}
5064
5065#[derive(JSTraceable, MallocSizeOf)]
5069struct TagName {
5070 #[no_trace]
5071 ptr: DomRefCell<Option<LocalName>>,
5072}
5073
5074impl TagName {
5075 fn new() -> TagName {
5076 TagName {
5077 ptr: DomRefCell::new(None),
5078 }
5079 }
5080
5081 fn or_init<F>(&self, cb: F) -> LocalName
5084 where
5085 F: FnOnce() -> LocalName,
5086 {
5087 match &mut *self.ptr.borrow_mut() {
5088 &mut Some(ref name) => name.clone(),
5089 ptr => {
5090 let name = cb();
5091 *ptr = Some(name.clone());
5092 name
5093 },
5094 }
5095 }
5096
5097 fn clear(&self) {
5100 *self.ptr.borrow_mut() = None;
5101 }
5102}
5103
5104pub(crate) fn reflect_cross_origin_attribute(element: &Element) -> Option<DOMString> {
5106 element
5107 .get_attribute(&local_name!("crossorigin"))
5108 .map(|attribute| {
5109 let value = attribute.value().to_ascii_lowercase();
5110 if value == "anonymous" || value == "use-credentials" {
5111 DOMString::from(value)
5112 } else {
5113 DOMString::from("anonymous")
5114 }
5115 })
5116}
5117
5118pub(crate) fn set_cross_origin_attribute(
5119 cx: &mut JSContext,
5120 element: &Element,
5121 value: Option<DOMString>,
5122) {
5123 match value {
5124 Some(val) => element.set_string_attribute(cx, &local_name!("crossorigin"), val),
5125 None => {
5126 element.remove_attribute(cx, &ns!(), &local_name!("crossorigin"));
5127 },
5128 }
5129}
5130
5131pub(crate) fn reflect_referrer_policy_attribute(element: &Element) -> DOMString {
5133 element
5134 .get_attribute(&local_name!("referrerpolicy"))
5135 .map(|attribute| {
5136 let value = attribute.value().to_ascii_lowercase();
5137 if value == "no-referrer" ||
5138 value == "no-referrer-when-downgrade" ||
5139 value == "same-origin" ||
5140 value == "origin" ||
5141 value == "strict-origin" ||
5142 value == "origin-when-cross-origin" ||
5143 value == "strict-origin-when-cross-origin" ||
5144 value == "unsafe-url"
5145 {
5146 DOMString::from(value)
5147 } else {
5148 DOMString::new()
5149 }
5150 })
5151 .unwrap_or_default()
5152}
5153
5154pub(crate) fn referrer_policy_for_element(element: &Element) -> ReferrerPolicy {
5155 element
5156 .get_attribute(&local_name!("referrerpolicy"))
5157 .map(|attribute| ReferrerPolicy::from(&**attribute.value()))
5158 .unwrap_or(element.owner_document().get_referrer_policy())
5159}
5160
5161pub(crate) fn cors_setting_for_element(element: &Element) -> Option<CorsSettings> {
5162 element
5163 .get_attribute(&local_name!("crossorigin"))
5164 .map(|attribute| CorsSettings::from_enumerated_attribute(&attribute.value()))
5165}
5166
5167pub(crate) fn cors_settings_attribute_credential_mode(element: &Element) -> CredentialsMode {
5169 element
5170 .get_attribute(&local_name!("crossorigin"))
5171 .map(|attr| {
5172 if attr.value().eq_ignore_ascii_case("use-credentials") {
5173 CredentialsMode::Include
5174 } else {
5175 CredentialsMode::CredentialsSameOrigin
5177 }
5178 })
5179 .unwrap_or(CredentialsMode::CredentialsSameOrigin)
5181}
5182
5183pub(crate) fn is_element_affected_by_legacy_background_presentational_hint(
5184 namespace: &Namespace,
5185 local_name: &LocalName,
5186) -> bool {
5187 *namespace == ns!(html) &&
5188 matches!(
5189 *local_name,
5190 local_name!("body") |
5191 local_name!("table") |
5192 local_name!("thead") |
5193 local_name!("tbody") |
5194 local_name!("tfoot") |
5195 local_name!("tr") |
5196 local_name!("td") |
5197 local_name!("th")
5198 )
5199}