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::EventHandlerBinding::EventHandlerNonNull;
74use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
75use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
76use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
77use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
78use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
79 SetHTMLOptions, SetHTMLUnsafeOptions,
80};
81use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
82 ShadowRootMethods, ShadowRootMode, SlotAssignmentMode,
83};
84use crate::dom::bindings::codegen::Bindings::WindowBinding::{
85 ScrollBehavior, ScrollToOptions, WindowMethods,
86};
87use crate::dom::bindings::codegen::UnionTypes::{
88 BooleanOrScrollIntoViewOptions, NodeOrString, TrustedHTMLOrNullIsEmptyString,
89 TrustedHTMLOrString,
90 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString,
91};
92use crate::dom::bindings::conversions::DerivedFrom;
93use crate::dom::bindings::domname::{
94 self, is_valid_attribute_local_name, namespace_from_domstring,
95};
96use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
97use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
98use crate::dom::bindings::num::Finite;
99use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout};
100use crate::dom::bindings::str::DOMString;
101use crate::dom::create::create_element;
102use crate::dom::csp::{CspReporting, InlineCheckType, SourcePosition};
103use crate::dom::customelementregistry::{
104 CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementRegistry,
105 CustomElementState, is_valid_custom_element_name,
106};
107use crate::dom::document::Document;
108use crate::dom::documentfragment::DocumentFragment;
109use crate::dom::domrect::DOMRect;
110use crate::dom::domrectlist::DOMRectList;
111use crate::dom::domtokenlist::DOMTokenList;
112use crate::dom::element::attributes::storage::{
113 AttrRef, AttrValueRef, AttributeEntry, AttributeStorage, ContentAttributeData,
114};
115use crate::dom::elementinternals::ElementInternals;
116use crate::dom::eventtarget::EventTarget;
117use crate::dom::globalscope::GlobalScope;
118use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
119use crate::dom::html::htmlareaelement::HTMLAreaElement;
120use crate::dom::html::htmlbodyelement::HTMLBodyElement;
121use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
122use crate::dom::html::htmlcollection::HTMLCollection;
123use crate::dom::html::htmlelement::HTMLElement;
124use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
125use crate::dom::html::htmlfontelement::HTMLFontElement;
126use crate::dom::html::htmlformelement::FormControlElementHelpers;
127use crate::dom::html::htmlhrelement::{HTMLHRElement, SizePresentationalHint};
128use crate::dom::html::htmliframeelement::HTMLIFrameElement;
129use crate::dom::html::htmlimageelement::HTMLImageElement;
130use crate::dom::html::htmllabelelement::HTMLLabelElement;
131use crate::dom::html::htmllegendelement::HTMLLegendElement;
132use crate::dom::html::htmllinkelement::HTMLLinkElement;
133use crate::dom::html::htmlobjectelement::HTMLObjectElement;
134use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
135use crate::dom::html::htmloutputelement::HTMLOutputElement;
136use crate::dom::html::htmlscriptelement::HTMLScriptElement;
137use crate::dom::html::htmlselectelement::HTMLSelectElement;
138use crate::dom::html::htmlslotelement::{HTMLSlotElement, Slottable};
139use crate::dom::html::htmlstyleelement::HTMLStyleElement;
140use crate::dom::html::htmltablecellelement::HTMLTableCellElement;
141use crate::dom::html::htmltablecolelement::HTMLTableColElement;
142use crate::dom::html::htmltableelement::HTMLTableElement;
143use crate::dom::html::htmltablerowelement::HTMLTableRowElement;
144use crate::dom::html::htmltablesectionelement::HTMLTableSectionElement;
145use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
146use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
147use crate::dom::html::htmlvideoelement::HTMLVideoElement;
148use crate::dom::input_element::HTMLInputElement;
149use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserverRegistration};
150use crate::dom::iterators::ShadowIncluding;
151use crate::dom::mutationobserver::{Mutation, MutationObserver};
152use crate::dom::namednodemap::NamedNodeMap;
153use crate::dom::node::{
154 BindContext, ChildrenMutation, CloneChildrenFlag, IsShadowTree, Node, NodeDamage, NodeFlags,
155 NodeTraits, UnbindContext,
156};
157use crate::dom::nodelist::NodeList;
158use crate::dom::promise::Promise;
159use crate::dom::range::Range;
160use crate::dom::raredata::ElementRareData;
161use crate::dom::sanitizer::Sanitizer;
162use crate::dom::scrolling_box::{ScrollAxisState, ScrollingBox};
163use crate::dom::servoparser::ServoParser;
164use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
165use crate::dom::svg::svgsvgelement::SVGSVGElement;
166use crate::dom::text::Text;
167use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
168use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
169use crate::dom::validation::Validatable;
170use crate::dom::validitystate::ValidationFlags;
171use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
172use crate::layout_dom::ServoDangerousStyleElement;
173use crate::realms::enter_auto_realm;
174use crate::script_runtime::CanGc;
175use crate::script_thread::ScriptThread;
176use crate::stylesheet_loader::StylesheetOwner;
177
178#[dom_struct]
184pub struct Element {
185 node: Node,
186 #[no_trace]
187 local_name: LocalName,
188 tag_name: TagName,
189 #[no_trace]
190 namespace: Namespace,
191 #[no_trace]
192 prefix: DomRefCell<Option<Prefix>>,
193 attrs: AttributeStorage,
194 #[no_trace]
195 id_attribute: DomRefCell<Option<Atom>>,
196 #[no_trace]
198 is: DomRefCell<Option<LocalName>>,
199 #[conditional_malloc_size_of]
200 #[no_trace]
201 style_attribute: DomRefCell<Option<ServoArc<Locked<PropertyDeclarationBlock>>>>,
202 attr_list: MutNullableDom<NamedNodeMap>,
203 class_list: MutNullableDom<DOMTokenList>,
204 #[no_trace]
205 state: Cell<ElementState>,
206 selector_flags: AtomicUsize,
209 rare_data: DomRefCell<Option<Box<ElementRareData>>>,
210
211 #[no_trace]
214 style_data: DomRefCell<Option<Box<StyleData>>>,
215}
216
217impl fmt::Debug for Element {
218 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
219 write!(f, "<{}", self.local_name)?;
220 if let Some(ref id) = *self.id_attribute.borrow() {
221 write!(f, " id={}", id)?;
222 }
223 write!(f, ">")
224 }
225}
226
227#[derive(MallocSizeOf, PartialEq)]
228pub(crate) enum ElementCreator {
229 ParserCreated(u64),
230 ScriptCreated,
231}
232
233pub(crate) enum CustomElementCreationMode {
234 Synchronous,
235 Asynchronous,
236}
237
238impl ElementCreator {
239 pub(crate) fn is_parser_created(&self) -> bool {
240 match *self {
241 ElementCreator::ParserCreated(_) => true,
242 ElementCreator::ScriptCreated => false,
243 }
244 }
245 pub(crate) fn return_line_number(&self) -> u64 {
246 match *self {
247 ElementCreator::ParserCreated(l) => l,
248 ElementCreator::ScriptCreated => 1,
249 }
250 }
251}
252
253pub(crate) enum AdjacentPosition {
254 BeforeBegin,
255 AfterEnd,
256 AfterBegin,
257 BeforeEnd,
258}
259
260impl FromStr for AdjacentPosition {
261 type Err = Error;
262
263 fn from_str(position: &str) -> Result<Self, Self::Err> {
264 match_ignore_ascii_case! { position,
265 "beforebegin" => Ok(AdjacentPosition::BeforeBegin),
266 "afterbegin" => Ok(AdjacentPosition::AfterBegin),
267 "beforeend" => Ok(AdjacentPosition::BeforeEnd),
268 "afterend" => Ok(AdjacentPosition::AfterEnd),
269 _ => Err(Error::Syntax(None))
270 }
271 }
272}
273
274impl Element {
278 pub(crate) fn create(
279 cx: &mut JSContext,
280 name: QualName,
281 is: Option<LocalName>,
282 document: &Document,
283 creator: ElementCreator,
284 mode: CustomElementCreationMode,
285 proto: Option<HandleObject>,
286 ) -> DomRoot<Element> {
287 create_element(cx, name, is, document, creator, mode, proto)
288 }
289
290 pub(crate) fn new_inherited(
291 local_name: LocalName,
292 namespace: Namespace,
293 prefix: Option<Prefix>,
294 document: &Document,
295 ) -> Element {
296 Element::new_inherited_with_state(
297 ElementState::empty(),
298 local_name,
299 namespace,
300 prefix,
301 document,
302 )
303 }
304
305 pub(crate) fn new_inherited_with_state(
306 state: ElementState,
307 local_name: LocalName,
308 namespace: Namespace,
309 prefix: Option<Prefix>,
310 document: &Document,
311 ) -> Element {
312 Element {
313 node: Node::new_inherited(document),
314 local_name,
315 tag_name: TagName::new(),
316 namespace,
317 prefix: DomRefCell::new(prefix),
318 attrs: Default::default(),
319 id_attribute: DomRefCell::new(None),
320 is: DomRefCell::new(None),
321 style_attribute: DomRefCell::new(None),
322 attr_list: Default::default(),
323 class_list: Default::default(),
324 state: Cell::new(state),
325 selector_flags: Default::default(),
326 rare_data: Default::default(),
327 style_data: Default::default(),
328 }
329 }
330
331 pub(crate) fn set_had_duplicate_attributes(&self) {
332 self.ensure_rare_data().had_duplicate_attributes = true;
333 }
334
335 pub(crate) fn new(
336 cx: &mut JSContext,
337 local_name: LocalName,
338 namespace: Namespace,
339 prefix: Option<Prefix>,
340 document: &Document,
341 proto: Option<HandleObject>,
342 ) -> DomRoot<Element> {
343 Node::reflect_node_with_proto(
344 cx,
345 Box::new(Element::new_inherited(
346 local_name, namespace, prefix, document,
347 )),
348 document,
349 proto,
350 )
351 }
352
353 fn rare_data(&self) -> Ref<'_, Option<Box<ElementRareData>>> {
354 self.rare_data.borrow()
355 }
356
357 fn rare_data_mut(&self) -> RefMut<'_, Option<Box<ElementRareData>>> {
358 self.rare_data.borrow_mut()
359 }
360
361 fn ensure_rare_data(&self) -> RefMut<'_, Box<ElementRareData>> {
362 let mut rare_data = self.rare_data.borrow_mut();
363 if rare_data.is_none() {
364 *rare_data = Some(Default::default());
365 }
366 RefMut::map(rare_data, |rare_data| rare_data.as_mut().unwrap())
367 }
368
369 pub(crate) fn clean_up_style_data(&self) {
370 self.style_data.borrow_mut().take();
371 }
372
373 pub(crate) fn restyle(&self, damage: NodeDamage) {
374 let doc = self.node.owner_doc();
375 let mut restyle = doc.ensure_pending_restyle(self);
376
377 restyle.hint.insert(RestyleHint::RESTYLE_SELF);
380
381 match damage {
382 NodeDamage::Style => {},
383 NodeDamage::ContentOrHeritage => {
384 doc.note_node_with_dirty_descendants(self.upcast());
385 restyle
386 .damage
387 .insert(RestyleDamage::from(LayoutDamage::DescendantHasBoxDamage));
388 },
389 NodeDamage::Other => {
390 doc.note_node_with_dirty_descendants(self.upcast());
391 restyle.damage.insert(RestyleDamage::reconstruct());
392 },
393 }
394 }
395
396 pub(crate) fn set_is(&self, is: LocalName) {
397 *self.is.borrow_mut() = Some(is);
398 }
399
400 pub(crate) fn get_is(&self) -> Option<LocalName> {
402 self.is.borrow().clone()
403 }
404
405 pub(crate) fn set_initial_custom_element_state_to_uncustomized(&self) {
414 let mut state = self.state.get();
415 state.insert(ElementState::DEFINED);
416 self.state.set(state);
417 }
418
419 pub(crate) fn set_custom_element_state(&self, state: CustomElementState) {
421 if state != CustomElementState::Uncustomized {
423 self.ensure_rare_data().custom_element_state = state;
424 }
425
426 let in_defined_state = matches!(
427 state,
428 CustomElementState::Uncustomized | CustomElementState::Custom
429 );
430 self.set_state(ElementState::DEFINED, in_defined_state)
431 }
432
433 pub(crate) fn get_custom_element_state(&self) -> CustomElementState {
434 if let Some(rare_data) = self.rare_data().as_ref() {
435 return rare_data.custom_element_state;
436 }
437 CustomElementState::Uncustomized
438 }
439
440 pub(crate) fn is_custom(&self) -> bool {
442 self.get_custom_element_state() == CustomElementState::Custom
443 }
444
445 pub(crate) fn set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>) {
446 self.ensure_rare_data().custom_element_definition = Some(definition);
447 }
448
449 pub(crate) fn get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>> {
450 self.rare_data().as_ref()?.custom_element_definition.clone()
451 }
452
453 pub(crate) fn clear_custom_element_definition(&self) {
454 self.ensure_rare_data().custom_element_definition = None;
455 }
456
457 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
458 pub(crate) fn push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>) {
459 self.ensure_rare_data()
460 .custom_element_reaction_queue
461 .push(CustomElementReaction::Callback(function, args));
462 }
463
464 pub(crate) fn push_upgrade_reaction(&self, definition: Rc<CustomElementDefinition>) {
465 self.ensure_rare_data()
466 .custom_element_reaction_queue
467 .push(CustomElementReaction::Upgrade(definition));
468 }
469
470 pub(crate) fn clear_reaction_queue(&self) {
471 if let Some(ref mut rare_data) = *self.rare_data_mut() {
472 rare_data.custom_element_reaction_queue.clear();
473 }
474 }
475
476 pub(crate) fn invoke_reactions(&self, cx: &mut JSContext) {
477 loop {
478 rooted_vec!(let mut reactions);
479 match *self.rare_data_mut() {
480 Some(ref mut data) => {
481 mem::swap(&mut *reactions, &mut data.custom_element_reaction_queue)
482 },
483 None => break,
484 };
485
486 if reactions.is_empty() {
487 break;
488 }
489
490 for reaction in reactions.iter() {
491 reaction.invoke(cx, self);
492 }
493
494 reactions.clear();
495 }
496 }
497
498 pub(crate) fn has_css_layout_box(&self) -> bool {
500 self.style()
501 .is_some_and(|s| !s.get_box().clone_display().is_none())
502 }
503
504 pub(crate) fn is_potentially_scrollable_body(&self) -> bool {
506 self.is_potentially_scrollable_body_shared_logic(false)
507 }
508
509 pub(crate) fn is_potentially_scrollable_body_for_scrolling_element(&self) -> bool {
511 self.is_potentially_scrollable_body_shared_logic(true)
512 }
513
514 fn is_potentially_scrollable_body_shared_logic(
516 &self,
517 treat_overflow_clip_on_parent_as_hidden: bool,
518 ) -> bool {
519 let node = self.upcast::<Node>();
520 debug_assert!(
521 node.owner_doc().GetBody().as_deref() == self.downcast::<HTMLElement>(),
522 "Called is_potentially_scrollable_body on element that is not the <body>"
523 );
524
525 if !self.has_css_layout_box() {
529 return false;
530 }
531
532 if let Some(parent) = node.GetParentElement() &&
535 let Some(style) = parent.style()
536 {
537 let mut overflow_x = style.get_box().clone_overflow_x();
538 let mut overflow_y = style.get_box().clone_overflow_y();
539
540 if treat_overflow_clip_on_parent_as_hidden {
543 if overflow_x == Overflow::Clip {
544 overflow_x = Overflow::Hidden;
545 }
546 if overflow_y == Overflow::Clip {
547 overflow_y = Overflow::Hidden;
548 }
549 }
550
551 if !overflow_x.is_scrollable() && !overflow_y.is_scrollable() {
552 return false;
553 }
554 };
555
556 if let Some(style) = self.style() &&
559 !style.get_box().clone_overflow_x().is_scrollable() &&
560 !style.get_box().clone_overflow_y().is_scrollable()
561 {
562 return false;
563 };
564
565 true
566 }
567
568 pub(crate) fn establishes_scroll_container(&self) -> bool {
571 self.upcast::<Node>()
573 .effective_overflow()
574 .is_some_and(|overflow| overflow.establishes_scroll_container())
575 }
576
577 pub(crate) fn has_overflow(&self) -> bool {
578 self.ScrollHeight() > self.ClientHeight() || self.ScrollWidth() > self.ClientWidth()
579 }
580
581 fn has_scrolling_box(&self) -> bool {
589 self.has_css_layout_box() && self.establishes_scroll_container() && self.has_overflow()
590 }
591
592 pub(crate) fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
593 self.rare_data()
594 .as_ref()?
595 .shadow_root
596 .as_ref()
597 .map(|sr| DomRoot::from_ref(&**sr))
598 }
599
600 pub(crate) fn is_shadow_host(&self) -> bool {
601 self.shadow_root().is_some()
602 }
603
604 #[allow(clippy::too_many_arguments)]
606 pub(crate) fn attach_shadow(
607 &self,
608 cx: &mut JSContext,
609 is_ua_widget: IsUserAgentWidget,
610 mode: ShadowRootMode,
611 clonable: bool,
612 serializable: bool,
613 delegates_focus: bool,
614 slot_assignment_mode: SlotAssignmentMode,
615 ) -> Fallible<DomRoot<ShadowRoot>> {
616 if self.namespace != ns!(html) {
619 return Err(Error::NotSupported(Some(
620 "Cannot attach shadow roots to elements with non-HTML namespaces".to_owned(),
621 )));
622 }
623
624 if !is_valid_shadow_host_name(self.local_name()) {
627 if is_ua_widget != IsUserAgentWidget::Yes {
629 let error_message = format!(
630 "Cannot attach shadow roots to <{}> elements",
631 *self.local_name()
632 );
633 return Err(Error::NotSupported(Some(error_message)));
634 }
635 }
636
637 if is_valid_custom_element_name(self.local_name()) || self.get_is().is_some() {
640 let definition = self.get_custom_element_definition();
644 if definition.is_some_and(|definition| definition.disable_shadow) {
647 let error_message = format!(
648 "The custom element constructor of <{}> disabled attachment of shadow roots",
649 self.local_name()
650 );
651 return Err(Error::NotSupported(Some(error_message)));
652 }
653 }
654
655 if let Some(current_shadow_root) = self.shadow_root() {
658 if !current_shadow_root.is_declarative() ||
662 current_shadow_root.shadow_root_mode() != mode
663 {
664 return Err(Error::NotSupported(Some(
665 "Cannot attach a second shadow root to the same element".into(),
666 )));
667 }
668
669 for child in current_shadow_root.upcast::<Node>().children() {
671 child.remove_self(cx);
672 }
673
674 current_shadow_root.set_declarative(false);
676
677 return Ok(current_shadow_root);
679 }
680
681 let shadow_root = ShadowRoot::new(
688 self,
689 &self.node.owner_doc(),
690 mode,
691 slot_assignment_mode,
692 clonable,
693 is_ua_widget,
694 CanGc::from_cx(cx),
695 );
696
697 let node = self.upcast::<Node>();
701 if node.is_connected() {
702 node.remove_style_and_layout_data_from_subtree(cx.no_gc());
703 }
704 shadow_root.set_delegates_focus(delegates_focus);
706
707 if matches!(
710 self.get_custom_element_state(),
711 CustomElementState::Precustomized | CustomElementState::Custom
712 ) {
713 shadow_root.set_available_to_element_internals(true);
714 }
715
716 shadow_root.set_declarative(false);
718
719 shadow_root.set_serializable(serializable);
721
722 self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
724 shadow_root
725 .upcast::<Node>()
726 .set_containing_shadow_root(Some(&shadow_root));
727
728 let bind_context = BindContext::new(self.upcast(), IsShadowTree::Yes);
729 shadow_root.bind_to_tree(cx, &bind_context);
730
731 node.dirty(NodeDamage::Other);
732
733 Ok(shadow_root)
734 }
735
736 pub(crate) fn attach_ua_shadow_root(
752 &self,
753 cx: &mut JSContext,
754 use_ua_widget_styling: bool,
755 ) -> DomRoot<ShadowRoot> {
756 let root = self
757 .attach_shadow(
758 cx,
759 IsUserAgentWidget::Yes,
760 ShadowRootMode::Closed,
761 false,
762 false,
763 false,
764 SlotAssignmentMode::Manual,
765 )
766 .expect("Attaching UA shadow root failed");
767
768 root.upcast::<Node>()
769 .set_in_ua_widget(use_ua_widget_styling);
770 root
771 }
772
773 pub(crate) fn is_translate_enabled(&self) -> bool {
775 let name = &local_name!("translate");
776 if self.has_attribute(name) {
777 let attribute = self.get_string_attribute(name);
778 match_ignore_ascii_case! { &*attribute.str(),
779 "yes" | "" => return true,
780 "no" => return false,
781 _ => {},
782 }
783 }
784 if let Some(parent) = self.upcast::<Node>().GetParentNode() &&
785 let Some(elem) = parent.downcast::<Element>()
786 {
787 return elem.is_translate_enabled();
788 }
789 true
790 }
791
792 pub(crate) fn directionality(&self) -> String {
794 self.downcast::<HTMLElement>()
795 .and_then(|html_element| html_element.directionality())
796 .unwrap_or_else(|| {
797 let node = self.upcast::<Node>();
798 node.parent_directionality()
799 })
800 }
801
802 pub(crate) fn is_root(&self) -> bool {
803 match self.node.GetParentNode() {
804 None => false,
805 Some(node) => node.is::<Document>(),
806 }
807 }
808
809 pub(crate) fn registered_intersection_observers_mut(
812 &self,
813 ) -> RefMut<'_, Vec<IntersectionObserverRegistration>> {
814 RefMut::map(self.ensure_rare_data(), |rare_data| {
815 &mut rare_data.registered_intersection_observers
816 })
817 }
818
819 pub(crate) fn registered_intersection_observers(
820 &self,
821 ) -> Option<Ref<'_, Vec<IntersectionObserverRegistration>>> {
822 let rare_data: Ref<'_, _> = self.rare_data.borrow();
823
824 if rare_data.is_none() {
825 return None;
826 }
827 Some(Ref::map(rare_data, |rare_data| {
828 &rare_data
829 .as_ref()
830 .unwrap()
831 .registered_intersection_observers
832 }))
833 }
834
835 pub(crate) fn get_intersection_observer_registration(
836 &self,
837 observer: &IntersectionObserver,
838 ) -> Option<Ref<'_, IntersectionObserverRegistration>> {
839 if let Some(registrations) = self.registered_intersection_observers() {
840 registrations
841 .iter()
842 .position(|reg_obs| reg_obs.observer == observer)
843 .map(|index| Ref::map(registrations, |registrations| ®istrations[index]))
844 } else {
845 None
846 }
847 }
848
849 pub(crate) fn add_initial_intersection_observer_registration(
851 &self,
852 observer: &IntersectionObserver,
853 ) {
854 self.ensure_rare_data()
855 .registered_intersection_observers
856 .push(IntersectionObserverRegistration::new_initial(observer));
857 }
858
859 pub(crate) fn remove_intersection_observer(&self, observer: &IntersectionObserver) {
861 self.ensure_rare_data()
862 .registered_intersection_observers
863 .retain(|reg_obs| *reg_obs.observer != *observer)
864 }
865
866 pub(crate) fn scrolling_box(&self, flags: ScrollContainerQueryFlags) -> Option<ScrollingBox> {
869 self.owner_window()
870 .scrolling_box_query(Some(self.upcast()), flags)
871 }
872
873 pub(crate) fn scroll_into_view_with_options(
875 &self,
876 cx: &mut JSContext,
877 behavior: ScrollBehavior,
878 block: ScrollAxisState,
879 inline: ScrollAxisState,
880 container: Option<&Element>,
881 inner_target_rect: Option<Rect<Au, CSSPixel>>,
882 ) {
883 let get_target_rect = || match inner_target_rect {
884 None => self.upcast::<Node>().border_box().unwrap_or_default(),
885 Some(inner_target_rect) => inner_target_rect.translate(
886 self.upcast::<Node>()
887 .content_box()
888 .unwrap_or_default()
889 .origin
890 .to_vector(),
891 ),
892 };
893
894 let mut parent_scrolling_box = self.scrolling_box(ScrollContainerQueryFlags::empty());
897 while let Some(scrolling_box) = parent_scrolling_box {
898 parent_scrolling_box = scrolling_box.parent();
899
900 let position =
911 scrolling_box.determine_scroll_into_view_position(block, inline, get_target_rect());
912
913 if position != scrolling_box.scroll_position() {
918 scrolling_box.scroll_to(cx, position, behavior);
929 }
930
931 if container.is_some_and(|container| {
935 let container_node = container.upcast::<Node>();
936 scrolling_box
937 .node()
938 .is_shadow_including_inclusive_ancestor_of(container_node)
939 }) {
940 return;
941 }
942 }
943
944 let window_proxy = self.owner_window().window_proxy();
945 let Some(frame_element) = window_proxy.frame_element() else {
946 return;
947 };
948
949 let inner_target_rect = Some(get_target_rect());
950
951 let mut realm = enter_auto_realm(cx, frame_element);
952 let cx = &mut realm;
953
954 frame_element.scroll_into_view_with_options(
955 cx,
956 behavior,
957 block,
958 inline,
959 None,
960 inner_target_rect,
961 )
962 }
963
964 pub(crate) fn ensure_contenteditable_selection_range(
965 &self,
966 cx: &mut JSContext,
967 document: &Document,
968 ) -> DomRoot<Range> {
969 self.ensure_rare_data()
970 .contenteditable_selection_range
971 .or_init(|| Range::new_with_doc(document, None, CanGc::from_cx(cx)))
972 }
973
974 pub(crate) fn handle_scroll_event(&self) {
979 let document = self.owner_document();
981
982 document.finish_handle_scroll_event(self.upcast());
991 }
992
993 pub(crate) fn style(&self) -> Option<ServoArc<ComputedValues>> {
994 self.owner_window().layout_reflow(QueryMsg::StyleQuery);
995 self.style_data
996 .borrow()
997 .as_ref()
998 .map(|data| data.element_data.borrow().styles.primary().clone())
999 }
1000
1001 pub(crate) fn is_styled(&self) -> bool {
1002 self.style_data.borrow().is_some()
1003 }
1004
1005 pub(crate) fn is_display_none(&self) -> bool {
1006 self.style_data.borrow().as_ref().is_none_or(|data| {
1007 data.element_data
1008 .borrow()
1009 .styles
1010 .primary()
1011 .get_box()
1012 .display
1013 .is_none()
1014 })
1015 }
1016}
1017
1018#[inline]
1020pub(crate) fn is_valid_shadow_host_name(name: &LocalName) -> bool {
1021 if is_valid_custom_element_name(name) {
1024 return true;
1025 }
1026
1027 matches!(
1030 name,
1031 &local_name!("article") |
1032 &local_name!("aside") |
1033 &local_name!("blockquote") |
1034 &local_name!("body") |
1035 &local_name!("div") |
1036 &local_name!("footer") |
1037 &local_name!("h1") |
1038 &local_name!("h2") |
1039 &local_name!("h3") |
1040 &local_name!("h4") |
1041 &local_name!("h5") |
1042 &local_name!("h6") |
1043 &local_name!("header") |
1044 &local_name!("main") |
1045 &local_name!("nav") |
1046 &local_name!("p") |
1047 &local_name!("section") |
1048 &local_name!("span")
1049 )
1050}
1051
1052#[inline]
1053#[expect(unsafe_code)]
1054pub(crate) fn get_attr_for_layout<'dom>(
1055 elem: LayoutDom<'dom, Element>,
1056 namespace: &Namespace,
1057 name: &LocalName,
1058) -> Option<&'dom AttrValue> {
1059 let storage = unsafe { elem.unsafe_get().attrs.borrow_for_layout() };
1060 storage
1061 .iter()
1062 .find(|e: &&AttributeEntry| {
1063 name == e.local_name_for_layout() && namespace == e.namespace_for_layout()
1064 })
1065 .map(|e: &AttributeEntry| e.value_for_layout())
1066}
1067
1068impl<'dom> LayoutDom<'dom, Element> {
1069 #[inline]
1070 pub(crate) fn is_root(&self) -> bool {
1071 self.upcast::<Node>()
1072 .parent_node_ref()
1073 .is_some_and(|parent| matches!(parent.type_id_for_layout(), NodeTypeId::Document(_)))
1074 }
1075
1076 pub(crate) fn is_body_element_of_html_element_root(&self) -> bool {
1078 if self.local_name() != &local_name!("body") {
1079 return false;
1080 }
1081 let Some(parent_node) = self.upcast::<Node>().parent_node_ref() else {
1082 return false;
1083 };
1084 let Some(parent_element) = parent_node.downcast::<Element>() else {
1085 return false;
1086 };
1087 parent_element.local_name() == &local_name!("html")
1088 }
1089
1090 #[expect(unsafe_code)]
1092 #[inline]
1093 pub(crate) fn each_attr_name_for_layout<F>(self, mut callback: F)
1094 where
1095 F: FnMut(&LocalName),
1096 {
1097 let storage = unsafe { self.unsafe_get().attrs.borrow_for_layout() };
1098 for entry in storage.iter() {
1099 callback(entry.local_name_for_layout());
1100 }
1101 }
1102
1103 #[inline]
1104 pub(crate) fn has_class_or_part_for_layout(
1105 self,
1106 name: &AtomIdent,
1107 attr_name: &LocalName,
1108 case_sensitivity: CaseSensitivity,
1109 ) -> bool {
1110 get_attr_for_layout(self, &ns!(), attr_name).is_some_and(|attr| {
1111 attr.as_tokens()
1112 .iter()
1113 .any(|atom| case_sensitivity.eq_atom(atom, name))
1114 })
1115 }
1116
1117 #[inline]
1118 pub(crate) fn get_classes_for_layout(self) -> Option<&'dom [Atom]> {
1119 get_attr_for_layout(self, &ns!(), &local_name!("class")).map(|attr| attr.as_tokens())
1120 }
1121
1122 pub(crate) fn get_parts_for_layout(self) -> Option<&'dom [Atom]> {
1123 get_attr_for_layout(self, &ns!(), &local_name!("part")).map(|attr| attr.as_tokens())
1124 }
1125
1126 #[inline]
1127 #[expect(unsafe_code)]
1128 pub(crate) fn style_data(self) -> Option<&'dom StyleData> {
1129 unsafe { self.unsafe_get().style_data.borrow_for_layout().as_deref() }
1130 }
1131
1132 #[inline]
1133 #[expect(unsafe_code)]
1134 pub(crate) unsafe fn initialize_style_data(self) {
1135 let data = unsafe { self.unsafe_get().style_data.borrow_mut_for_layout() };
1136 debug_assert!(data.is_none());
1137 *data = Some(Box::default());
1138 }
1139
1140 #[inline]
1141 #[expect(unsafe_code)]
1142 pub(crate) unsafe fn clear_style_data(self) {
1143 unsafe {
1144 self.unsafe_get().style_data.borrow_mut_for_layout().take();
1145 }
1146 }
1147
1148 pub(crate) fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
1149 where
1150 V: Push<ApplicableDeclarationBlock>,
1151 {
1152 let document = self.upcast::<Node>().owner_doc_for_layout();
1153 let mut property_declaration_block = None;
1154 let mut push = |declaration| {
1155 property_declaration_block
1156 .get_or_insert_with(PropertyDeclarationBlock::default)
1157 .push(declaration, Importance::Normal);
1158 };
1159
1160 if let Some(lang) = self.get_lang_attr_val_for_layout() {
1163 push(PropertyDeclaration::XLang(specified::XLang(Atom::from(
1164 lang.to_owned(),
1165 ))));
1166 }
1167
1168 let bgcolor = if let Some(this) = self.downcast::<HTMLBodyElement>() {
1169 this.get_background_color()
1170 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1171 this.get_background_color()
1172 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1173 this.get_background_color()
1174 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1175 this.get_background_color()
1176 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1177 this.get_background_color()
1178 } else {
1179 None
1180 };
1181
1182 if let Some(color) = bgcolor {
1183 push(PropertyDeclaration::BackgroundColor(
1184 specified::Color::from_absolute_color(color),
1185 ));
1186 }
1187
1188 if is_element_affected_by_legacy_background_presentational_hint(
1189 self.namespace(),
1190 self.local_name(),
1191 ) && let Some(url) = self
1192 .get_attr_for_layout(&ns!(), &local_name!("background"))
1193 .and_then(AttrValue::as_resolved_url)
1194 .cloned()
1195 {
1196 push(PropertyDeclaration::BackgroundImage(
1197 background_image::SpecifiedValue(vec![specified::Image::for_cascade(url)].into()),
1198 ));
1199 }
1200
1201 let color = if let Some(this) = self.downcast::<HTMLFontElement>() {
1202 this.get_color()
1203 } else if let Some(this) = self.downcast::<HTMLBodyElement>() {
1204 this.get_color()
1206 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1207 this.get_color()
1209 } else {
1210 None
1211 };
1212
1213 if let Some(color) = color {
1214 push(PropertyDeclaration::Color(
1215 longhands::color::SpecifiedValue(specified::Color::from_absolute_color(color)),
1216 ));
1217 }
1218
1219 let font_face = self
1220 .downcast::<HTMLFontElement>()
1221 .and_then(LayoutDom::get_face);
1222 if let Some(font_face) = font_face {
1223 push(PropertyDeclaration::FontFamily(
1224 font_family::SpecifiedValue::Values(computed::font::FontFamilyList {
1225 list: ArcSlice::from_iter(
1226 HTMLFontElement::parse_face_attribute(font_face).into_iter(),
1227 ),
1228 }),
1229 ));
1230 }
1231
1232 let font_size = self
1233 .downcast::<HTMLFontElement>()
1234 .and_then(LayoutDom::get_size);
1235 if let Some(font_size) = font_size {
1236 push(PropertyDeclaration::FontSize(
1237 font_size::SpecifiedValue::from_html_size(font_size as u8),
1238 ));
1239 }
1240
1241 let size = self
1247 .downcast::<HTMLInputElement>()
1248 .and_then(|input_element| {
1249 match self.get_attr_val_for_layout(&ns!(), &local_name!("type")) {
1251 Some("hidden") | Some("range") | Some("color") | Some("checkbox") |
1252 Some("radio") | Some("file") | Some("submit") | Some("image") |
1253 Some("reset") | Some("button") => None,
1254 _ => match input_element.size_for_layout() {
1256 0 => None,
1257 s => Some(s as i32),
1258 },
1259 }
1260 });
1261
1262 if let Some(size) = size {
1263 let value =
1264 specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(size));
1265 push(PropertyDeclaration::Width(
1266 specified::Size::LengthPercentage(NonNegative(
1267 specified::LengthPercentage::Length(value),
1268 )),
1269 ));
1270 }
1271
1272 let width = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1273 this.get_width()
1274 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1275 this.get_width()
1276 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1277 this.get_width()
1278 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1279 this.get_width()
1280 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1281 this.get_width()
1282 } else if let Some(this) = self.downcast::<HTMLTableColElement>() {
1283 this.get_width()
1284 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1285 this.get_width()
1287 } else {
1288 LengthOrPercentageOrAuto::Auto
1289 };
1290
1291 match width {
1293 LengthOrPercentageOrAuto::Auto => {},
1294 LengthOrPercentageOrAuto::Percentage(percentage) => {
1295 let width_value = specified::Size::LengthPercentage(NonNegative(
1296 specified::LengthPercentage::Percentage(computed::Percentage(percentage)),
1297 ));
1298 push(PropertyDeclaration::Width(width_value));
1299 },
1300 LengthOrPercentageOrAuto::Length(length) => {
1301 let width_value = specified::Size::LengthPercentage(NonNegative(
1302 specified::LengthPercentage::Length(specified::NoCalcLength::Absolute(
1303 specified::AbsoluteLength::Px(length.to_f32_px()),
1304 )),
1305 ));
1306 push(PropertyDeclaration::Width(width_value));
1307 },
1308 }
1309
1310 let height = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1311 this.get_height()
1312 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1313 this.get_height()
1314 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1315 this.get_height()
1316 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1317 this.get_height()
1318 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1319 this.get_height()
1320 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1321 this.get_height()
1322 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1323 this.get_height()
1324 } else {
1325 LengthOrPercentageOrAuto::Auto
1326 };
1327
1328 match height {
1329 LengthOrPercentageOrAuto::Auto => {},
1330 LengthOrPercentageOrAuto::Percentage(percentage) => {
1331 let height_value = specified::Size::LengthPercentage(NonNegative(
1332 specified::LengthPercentage::Percentage(computed::Percentage(percentage)),
1333 ));
1334 push(PropertyDeclaration::Height(height_value));
1335 },
1336 LengthOrPercentageOrAuto::Length(length) => {
1337 let height_value = specified::Size::LengthPercentage(NonNegative(
1338 specified::LengthPercentage::Length(specified::NoCalcLength::Absolute(
1339 specified::AbsoluteLength::Px(length.to_f32_px()),
1340 )),
1341 ));
1342 push(PropertyDeclaration::Height(height_value));
1343 },
1344 }
1345
1346 if let Some(this) = self.downcast::<SVGSVGElement>() {
1347 let data = this.data();
1348 if let Some(width) = data.width.and_then(AttrValue::as_length_percentage) {
1349 push(PropertyDeclaration::Width(
1350 specified::Size::LengthPercentage(NonNegative(width.clone())),
1351 ));
1352 }
1353 if let Some(height) = data.height.and_then(AttrValue::as_length_percentage) {
1354 push(PropertyDeclaration::Height(
1355 specified::Size::LengthPercentage(NonNegative(height.clone())),
1356 ));
1357 }
1358 }
1359
1360 if (self.is::<HTMLImageElement>() || self.is::<HTMLVideoElement>()) &&
1363 let LengthOrPercentageOrAuto::Length(width) = width &&
1364 let LengthOrPercentageOrAuto::Length(height) = height
1365 {
1366 let width_value = NonNegative(specified::Number::new(width.to_f32_px()));
1367 let height_value = NonNegative(specified::Number::new(height.to_f32_px()));
1368 let aspect_ratio = specified::position::AspectRatio {
1369 auto: true,
1370 ratio: PreferredRatio::Ratio(Ratio(width_value, height_value)),
1371 };
1372 push(PropertyDeclaration::AspectRatio(aspect_ratio));
1373 }
1374
1375 let cols = self
1376 .downcast::<HTMLTextAreaElement>()
1377 .map(LayoutDom::get_cols);
1378 if let Some(cols) = cols {
1379 let cols = cols as i32;
1380 if cols > 0 {
1381 let value =
1387 specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(cols));
1388 push(PropertyDeclaration::Width(
1389 specified::Size::LengthPercentage(NonNegative(
1390 specified::LengthPercentage::Length(value),
1391 )),
1392 ));
1393 }
1394 }
1395
1396 let rows = self
1397 .downcast::<HTMLTextAreaElement>()
1398 .map(LayoutDom::get_rows);
1399 if let Some(rows) = rows {
1400 let rows = rows as i32;
1401 if rows > 0 {
1402 let value = specified::NoCalcLength::FontRelative(
1406 specified::FontRelativeLength::Em(rows as CSSFloat),
1407 );
1408 push(PropertyDeclaration::Height(
1409 specified::Size::LengthPercentage(NonNegative(
1410 specified::LengthPercentage::Length(value),
1411 )),
1412 ));
1413 }
1414 }
1415
1416 if let Some(table) = self.downcast::<HTMLTableElement>() {
1417 if let Some(cellspacing) = table.get_cellspacing() {
1418 let width_value = specified::Length::from_px(cellspacing as f32);
1419 push(PropertyDeclaration::BorderSpacing(Box::new(
1420 border_spacing::SpecifiedValue::new(
1421 width_value.clone().into(),
1422 width_value.into(),
1423 ),
1424 )));
1425 }
1426 if let Some(border) = table.get_border() {
1427 let width_value = specified::BorderSideWidth::from_px(border as f32);
1428 push(PropertyDeclaration::BorderTopWidth(width_value.clone()));
1429 push(PropertyDeclaration::BorderLeftWidth(width_value.clone()));
1430 push(PropertyDeclaration::BorderBottomWidth(width_value.clone()));
1431 push(PropertyDeclaration::BorderRightWidth(width_value));
1432 }
1433 if document.quirks_mode() == QuirksMode::Quirks {
1434 push(PropertyDeclaration::Color(color::SpecifiedValue(
1436 specified::Color::InheritFromBodyQuirk,
1437 )));
1438 }
1439 }
1440
1441 if let Some(cellpadding) = self
1442 .downcast::<HTMLTableCellElement>()
1443 .and_then(|this| this.get_table())
1444 .and_then(|table| table.get_cellpadding())
1445 {
1446 let cellpadding = NonNegative(specified::LengthPercentage::Length(
1447 specified::NoCalcLength::from_px(cellpadding as f32),
1448 ));
1449 push(PropertyDeclaration::PaddingTop(cellpadding.clone()));
1450 push(PropertyDeclaration::PaddingLeft(cellpadding.clone()));
1451 push(PropertyDeclaration::PaddingBottom(cellpadding.clone()));
1452 push(PropertyDeclaration::PaddingRight(cellpadding));
1453 }
1454
1455 if let Some(size_info) = self
1457 .downcast::<HTMLHRElement>()
1458 .and_then(|hr_element| hr_element.get_size_info())
1459 {
1460 match size_info {
1461 SizePresentationalHint::SetHeightTo(height) => {
1462 push(PropertyDeclaration::Height(height));
1463 },
1464 SizePresentationalHint::SetAllBorderWidthValuesTo(border_width) => {
1465 push(PropertyDeclaration::BorderLeftWidth(border_width.clone()));
1466 push(PropertyDeclaration::BorderRightWidth(border_width.clone()));
1467 push(PropertyDeclaration::BorderTopWidth(border_width.clone()));
1468 push(PropertyDeclaration::BorderBottomWidth(border_width));
1469 },
1470 SizePresentationalHint::SetBottomBorderWidthToZero => {
1471 push(PropertyDeclaration::BorderBottomWidth(
1472 specified::border::BorderSideWidth::from_px(0.),
1473 ));
1474 },
1475 }
1476 }
1477
1478 let Some(property_declaration_block) = property_declaration_block else {
1479 return;
1480 };
1481
1482 let shared_lock = &document.shared_style_locks().author;
1483 hints.push(ApplicableDeclarationBlock::from_declarations(
1484 ServoArc::new(shared_lock.wrap(property_declaration_block)),
1485 CascadeLevel::new(CascadeOrigin::PresHints),
1486 LayerOrder::root(),
1487 ));
1488 }
1489
1490 pub(crate) fn get_span(self) -> Option<u32> {
1491 self.downcast::<HTMLTableColElement>()
1493 .and_then(|element| element.get_span())
1494 }
1495
1496 pub(crate) fn get_colspan(self) -> Option<u32> {
1497 self.downcast::<HTMLTableCellElement>()
1499 .and_then(|element| element.get_colspan())
1500 }
1501
1502 pub(crate) fn get_rowspan(self) -> Option<u32> {
1503 self.downcast::<HTMLTableCellElement>()
1505 .and_then(|element| element.get_rowspan())
1506 }
1507
1508 #[inline]
1509 pub(crate) fn is_html_element(&self) -> bool {
1510 *self.namespace() == ns!(html)
1511 }
1512
1513 #[expect(unsafe_code)]
1514 pub(crate) fn id_attribute(self) -> *const Option<Atom> {
1515 unsafe { (self.unsafe_get()).id_attribute.borrow_for_layout() }
1516 }
1517
1518 #[expect(unsafe_code)]
1519 pub(crate) fn style_attribute(
1520 self,
1521 ) -> *const Option<ServoArc<Locked<PropertyDeclarationBlock>>> {
1522 unsafe { (self.unsafe_get()).style_attribute.borrow_for_layout() }
1523 }
1524
1525 pub(crate) fn local_name(self) -> &'dom LocalName {
1526 &(self.unsafe_get()).local_name
1527 }
1528
1529 pub(crate) fn namespace(self) -> &'dom Namespace {
1530 &(self.unsafe_get()).namespace
1531 }
1532
1533 pub(crate) fn get_lang_attr_val_for_layout(self) -> Option<&'dom str> {
1534 if let Some(attr) = self.get_attr_val_for_layout(&ns!(xml), &local_name!("lang")) {
1535 return Some(attr);
1536 }
1537 if let Some(attr) = self.get_attr_val_for_layout(&ns!(), &local_name!("lang")) {
1538 return Some(attr);
1539 }
1540 None
1541 }
1542
1543 pub(crate) fn get_lang_for_layout(self) -> String {
1544 let mut current_node = Some(self.upcast::<Node>());
1545 while let Some(node) = current_node {
1546 current_node = node.composed_parent_node_ref();
1547 match node.downcast::<Element>() {
1548 Some(elem) => {
1549 if let Some(attr) = elem.get_lang_attr_val_for_layout() {
1550 return attr.to_owned();
1551 }
1552 },
1553 None => continue,
1554 }
1555 }
1556 String::new()
1559 }
1560
1561 #[inline]
1562 pub(crate) fn get_state_for_layout(self) -> ElementState {
1563 (self.unsafe_get()).state.get()
1564 }
1565
1566 #[inline]
1567 pub(crate) fn insert_selector_flags(self, flags: ElementSelectorFlags) {
1568 debug_assert!(thread_state::get().is_layout());
1569 self.unsafe_get().insert_selector_flags(flags);
1570 }
1571
1572 #[inline]
1573 pub(crate) fn get_selector_flags(self) -> ElementSelectorFlags {
1574 self.unsafe_get().get_selector_flags()
1575 }
1576
1577 #[inline]
1578 #[expect(unsafe_code)]
1579 pub(crate) fn get_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
1580 unsafe {
1581 self.unsafe_get()
1582 .rare_data
1583 .borrow_for_layout()
1584 .as_ref()?
1585 .shadow_root
1586 .as_ref()
1587 .map(|sr| sr.to_layout())
1588 }
1589 }
1590
1591 #[inline]
1592 pub(crate) fn get_attr_for_layout(
1593 self,
1594 namespace: &Namespace,
1595 name: &LocalName,
1596 ) -> Option<&'dom AttrValue> {
1597 get_attr_for_layout(self, namespace, name)
1598 }
1599
1600 #[inline]
1601 pub(crate) fn get_attr_val_for_layout(
1602 self,
1603 namespace: &Namespace,
1604 name: &LocalName,
1605 ) -> Option<&'dom str> {
1606 get_attr_for_layout(self, namespace, name).map(|attr| &**attr)
1607 }
1608
1609 #[inline]
1610 #[expect(unsafe_code)]
1611 pub(crate) fn get_attr_vals_for_layout(
1612 self,
1613 name: &LocalName,
1614 ) -> impl Iterator<Item = &'dom AttrValue> {
1615 let storage = unsafe { self.unsafe_get().attrs.borrow_for_layout() };
1616 storage
1617 .iter()
1618 .filter(move |e: &&AttributeEntry| name == e.local_name_for_layout())
1619 .map(|e: &AttributeEntry| e.value_for_layout())
1620 }
1621
1622 #[expect(unsafe_code)]
1623 pub(crate) fn each_custom_state_for_layout(self, mut callback: impl FnMut(&AtomIdent)) {
1624 let rare_data = unsafe { self.unsafe_get().rare_data.borrow_for_layout() };
1625 let Some(rare_data) = rare_data.as_ref() else {
1626 return;
1627 };
1628 let Some(element_internals) = rare_data.element_internals.as_ref() else {
1629 return;
1630 };
1631
1632 let element_internals: LayoutDom<'_, _> = unsafe { element_internals.to_layout() };
1633 if let Some(states) = element_internals.unsafe_get().custom_states_for_layout() {
1634 for state in unsafe { states.unsafe_get().set_for_layout().iter() } {
1635 callback(&AtomIdent::from(&*state.str()));
1637 }
1638 }
1639 }
1640}
1641
1642impl Element {
1643 pub(crate) fn is_html_element(&self) -> bool {
1644 self.namespace == ns!(html)
1645 }
1646
1647 pub(crate) fn html_element_in_html_document(&self) -> bool {
1648 self.is_html_element() && self.upcast::<Node>().is_in_html_doc()
1649 }
1650
1651 pub(crate) fn local_name(&self) -> &LocalName {
1652 &self.local_name
1653 }
1654
1655 pub(crate) fn parsed_name(&self, mut name: DOMString) -> LocalName {
1656 if self.html_element_in_html_document() {
1657 name.make_ascii_lowercase();
1658 }
1659 LocalName::from(name)
1660 }
1661
1662 pub(crate) fn namespace(&self) -> &Namespace {
1663 &self.namespace
1664 }
1665
1666 pub(crate) fn prefix(&self) -> Ref<'_, Option<Prefix>> {
1667 self.prefix.borrow()
1668 }
1669
1670 pub(crate) fn set_prefix(&self, prefix: Option<Prefix>) {
1671 *self.prefix.borrow_mut() = prefix;
1672 }
1673
1674 pub(crate) fn set_custom_element_registry(
1675 &self,
1676 registry: Option<DomRoot<CustomElementRegistry>>,
1677 ) {
1678 self.ensure_rare_data().custom_element_registry = registry.as_deref().map(Dom::from_ref);
1679 }
1680
1681 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
1682 self.rare_data()
1683 .as_ref()?
1684 .custom_element_registry
1685 .as_deref()
1686 .map(DomRoot::from_ref)
1687 }
1688
1689 pub(crate) fn attrs(&self) -> &AttributeStorage {
1690 &self.attrs
1691 }
1692
1693 pub(crate) fn dom_attrs(&self, cx: &mut JSContext) -> &AttributeStorage {
1694 let len = self.attrs.borrow().len();
1696 for i in 0..len {
1697 self.attrs.ensure_dom(cx, i, self);
1698 }
1699 &self.attrs
1700 }
1701
1702 pub(crate) fn locate_namespace(&self, prefix: Option<DOMString>) -> Namespace {
1704 let namespace_prefix = prefix.clone().map(|s| Prefix::from(&*s.str()));
1705
1706 if namespace_prefix == Some(namespace_prefix!("xml")) {
1708 return ns!(xml);
1709 }
1710
1711 if namespace_prefix == Some(namespace_prefix!("xmlns")) {
1713 return ns!(xmlns);
1714 }
1715
1716 let prefix = prefix.map(LocalName::from);
1717
1718 let inclusive_ancestor_elements = self
1719 .upcast::<Node>()
1720 .inclusive_ancestors(ShadowIncluding::No)
1721 .filter_map(DomRoot::downcast::<Self>);
1722
1723 for element in inclusive_ancestor_elements {
1726 if element.namespace() != &ns!() &&
1728 element.prefix().as_ref().map(|p| &**p) == prefix.as_deref()
1729 {
1730 return element.namespace().clone();
1731 }
1732
1733 let found_ns = element.attrs.borrow().iter().find_map(|attr| {
1738 if attr.namespace() != &ns!(xmlns) {
1739 return None;
1740 }
1741 match (attr.prefix(), prefix.as_ref()) {
1742 (Some(&namespace_prefix!("xmlns")), Some(prefix)) => {
1743 if attr.local_name() == prefix {
1744 Some(Namespace::from(&**attr.value()))
1745 } else {
1746 None
1747 }
1748 },
1749 (None, None) => {
1750 if attr.local_name() == &local_name!("xmlns") {
1751 Some(Namespace::from(&**attr.value()))
1752 } else {
1753 None
1754 }
1755 },
1756 _ => None,
1757 }
1758 });
1759
1760 if let Some(ns) = found_ns {
1761 return ns;
1762 }
1763 }
1764
1765 ns!()
1766 }
1767
1768 pub(crate) fn name_attribute(&self) -> Option<Atom> {
1769 self.rare_data().as_ref()?.name_attribute.clone()
1770 }
1771
1772 pub(crate) fn style_attribute(
1773 &self,
1774 ) -> &DomRefCell<Option<ServoArc<Locked<PropertyDeclarationBlock>>>> {
1775 &self.style_attribute
1776 }
1777
1778 pub(crate) fn summarize(&self) -> Vec<AttrInfo> {
1779 self.attrs
1780 .borrow()
1781 .iter()
1782 .map(|attr| attr.summarize())
1783 .collect()
1784 }
1785
1786 pub(crate) fn is_void(&self) -> bool {
1787 if self.namespace != ns!(html) {
1788 return false;
1789 }
1790 match self.local_name {
1791 local_name!("area") |
1794 local_name!("base") |
1795 local_name!("basefont") |
1796 local_name!("bgsound") |
1797 local_name!("br") |
1798 local_name!("col") |
1799 local_name!("embed") |
1800 local_name!("frame") |
1801 local_name!("hr") |
1802 local_name!("img") |
1803 local_name!("input") |
1804 local_name!("keygen") |
1805 local_name!("link") |
1806 local_name!("meta") |
1807 local_name!("param") |
1808 local_name!("source") |
1809 local_name!("track") |
1810 local_name!("wbr") => true,
1811 _ => false,
1812 }
1813 }
1814
1815 pub(crate) fn root_element(&self) -> DomRoot<Element> {
1816 if self.node.is_in_a_document_tree() {
1817 self.upcast::<Node>()
1818 .owner_doc()
1819 .GetDocumentElement()
1820 .unwrap()
1821 } else {
1822 self.upcast::<Node>()
1823 .inclusive_ancestors(ShadowIncluding::No)
1824 .filter_map(DomRoot::downcast)
1825 .last()
1826 .expect("We know inclusive_ancestors will return `self` which is an element")
1827 }
1828 }
1829
1830 pub(crate) fn lookup_prefix(&self, namespace: Namespace) -> Option<DOMString> {
1832 for node in self
1833 .upcast::<Node>()
1834 .inclusive_ancestors(ShadowIncluding::No)
1835 {
1836 let element = node.downcast::<Element>()?;
1837 if *element.namespace() == namespace &&
1839 let Some(prefix) = element.GetPrefix()
1840 {
1841 return Some(prefix);
1842 }
1843
1844 for attr in element.attrs.borrow().iter() {
1846 if attr.prefix() == Some(&namespace_prefix!("xmlns")) &&
1847 **attr.value() == *namespace
1848 {
1849 return Some(DOMString::from(&**attr.local_name()));
1850 }
1851 }
1852 }
1853 None
1854 }
1855
1856 pub(crate) fn is_document_element(&self) -> bool {
1858 if let Some(document_element) = self.owner_document().GetDocumentElement() {
1859 *document_element == *self
1860 } else {
1861 false
1862 }
1863 }
1864
1865 pub(crate) fn is_active_element(&self) -> bool {
1867 if let Some(active_element) = self.owner_document().GetActiveElement() {
1868 *active_element == *self
1869 } else {
1870 false
1871 }
1872 }
1873
1874 pub(crate) fn is_editing_host(&self) -> bool {
1875 self.downcast::<HTMLElement>()
1876 .is_some_and(|element| element.IsContentEditable())
1877 }
1878
1879 pub(crate) fn is_actually_disabled(&self) -> bool {
1880 let node = self.upcast::<Node>();
1881 match node.type_id() {
1882 NodeTypeId::Element(ElementTypeId::HTMLElement(
1883 HTMLElementTypeId::HTMLButtonElement,
1884 )) |
1885 NodeTypeId::Element(ElementTypeId::HTMLElement(
1886 HTMLElementTypeId::HTMLInputElement,
1887 )) |
1888 NodeTypeId::Element(ElementTypeId::HTMLElement(
1889 HTMLElementTypeId::HTMLSelectElement,
1890 )) |
1891 NodeTypeId::Element(ElementTypeId::HTMLElement(
1892 HTMLElementTypeId::HTMLTextAreaElement,
1893 )) |
1894 NodeTypeId::Element(ElementTypeId::HTMLElement(
1895 HTMLElementTypeId::HTMLOptionElement,
1896 )) => self.disabled_state(),
1897 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
1898 self.downcast::<HTMLElement>()
1899 .unwrap()
1900 .is_form_associated_custom_element() &&
1901 self.disabled_state()
1902 },
1903 _ => false,
1908 }
1909 }
1910
1911 #[allow(clippy::too_many_arguments)]
1912 pub(crate) fn push_new_attribute(
1913 &self,
1914 cx: &mut JSContext,
1915 local_name: LocalName,
1916 value: AttrValue,
1917 name: LocalName,
1918 namespace: Namespace,
1919 prefix: Option<Prefix>,
1920 reason: AttributeMutationReason,
1921 ) {
1922 let data = ContentAttributeData {
1927 identifier: AttrIdentifier {
1928 local_name: GenericAtomIdent(local_name),
1929 name: GenericAtomIdent(name),
1930 namespace: GenericAtomIdent(namespace),
1931 prefix: prefix.map(GenericAtomIdent),
1932 },
1933 value,
1934 };
1935 let attr_ref = AttrRef::Raw(&data);
1936 self.will_mutate_attr(attr_ref);
1937 self.attrs.push_raw(ContentAttributeData {
1939 identifier: data.identifier.clone(),
1940 value: data.value.clone(),
1941 });
1942 self.handle_attribute_changes(cx, attr_ref, None, Some(&*attr_ref.value()), reason);
1944 }
1945
1946 fn handle_attribute_changes(
1948 &self,
1949 cx: &mut JSContext,
1950 attr: AttrRef<'_>,
1951 old_value: Option<&AttrValue>,
1952 new_value: Option<&AttrValue>,
1953 reason: AttributeMutationReason,
1954 ) {
1955 let name = attr.local_name().clone();
1958 let namespace = attr.namespace().clone();
1959 let mutation = LazyCell::new(|| Mutation::Attribute {
1960 name: name.clone(),
1961 namespace: namespace.clone(),
1962 old_value: old_value.map(|old_value| DOMString::from(&**old_value)),
1963 });
1964 MutationObserver::queue_a_mutation_record(&self.node, mutation);
1965
1966 let has_new_value = new_value.is_some();
1968
1969 if self.is_custom() {
1972 let reaction = CallbackReaction::AttributeChanged(
1973 attr.local_name().clone(),
1974 old_value,
1975 new_value,
1976 attr.namespace().clone(),
1977 );
1978 ScriptThread::enqueue_callback_reaction(self, reaction, None);
1979 }
1980
1981 if is_relevant_attribute(attr.namespace(), attr.local_name()) {
1983 let attribute_mutation = if has_new_value {
1984 AttributeMutation::Set(old_value, reason)
1985 } else {
1986 AttributeMutation::Removed
1987 };
1988 vtable_for(self.upcast()).attribute_mutated(cx, attr, attribute_mutation);
1989 }
1990 }
1991
1992 pub(crate) fn change_attribute(&self, cx: &mut JSContext, attr: &Attr, mut value: AttrValue) {
1994 let old_value = &attr.value().clone();
1998 self.will_mutate_attr(AttrRef::Dom(attr));
2000 attr.swap_value(&mut value);
2001 self.handle_attribute_changes(
2005 cx,
2006 AttrRef::Dom(attr),
2007 Some(old_value),
2008 Some(&*attr.value()),
2009 AttributeMutationReason::Directly,
2010 );
2011 }
2012
2013 pub(crate) fn push_attribute(
2015 &self,
2016 cx: &mut JSContext,
2017 attr: &Attr,
2018 reason: AttributeMutationReason,
2019 ) {
2020 assert!(attr.GetOwnerElement().as_deref() == Some(self));
2024 assert!(attr.upcast::<Node>().owner_doc() == self.node.owner_doc());
2028 self.will_mutate_attr(AttrRef::Dom(attr));
2030 self.attrs.push_dom(attr);
2031 self.handle_attribute_changes(cx, AttrRef::Dom(attr), None, Some(&*attr.value()), reason);
2035 }
2036
2037 pub(crate) fn with_attribute<R, F>(
2038 &self,
2039 namespace: &Namespace,
2040 local_name: &LocalName,
2041 map_func: F,
2042 ) -> Option<R>
2043 where
2044 F: FnOnce(AttrRef<'_>) -> R,
2045 {
2046 self.attrs
2047 .borrow()
2048 .iter()
2049 .find(|attribute| {
2050 attribute.local_name() == local_name && attribute.namespace() == namespace
2051 })
2052 .map(map_func)
2053 }
2054
2055 pub(crate) fn get_attribute_with_namespace(
2061 &self,
2062 cx: &mut JSContext,
2063 namespace: &Namespace,
2064 local_name: &LocalName,
2065 ) -> Option<DomRoot<Attr>> {
2066 let idx = self
2067 .attrs
2068 .borrow()
2069 .iter()
2070 .position(|a| a.local_name() == local_name && a.namespace() == namespace)?;
2071 Some(self.attrs.ensure_dom(cx, idx, self))
2072 }
2073
2074 pub(crate) fn get_attribute_by_name(
2076 &self,
2077 cx: &mut JSContext,
2078 name: DOMString,
2079 ) -> Option<DomRoot<Attr>> {
2080 let name = &self.parsed_name(name);
2081 let idx = self.attrs.borrow().iter().position(|a| a.name() == name)?;
2082 let attr_dom = Some(self.attrs.ensure_dom(cx, idx, self));
2083 fn id_and_name_must_be_atoms(name: &LocalName, maybe_attr: &Option<DomRoot<Attr>>) -> bool {
2084 if *name == local_name!("id") || *name == local_name!("name") {
2085 match maybe_attr {
2086 None => true,
2087 Some(attr) => matches!(*attr.value(), AttrValue::Atom(_)),
2088 }
2089 } else {
2090 true
2091 }
2092 }
2093 debug_assert!(id_and_name_must_be_atoms(name, &attr_dom));
2094 attr_dom
2095 }
2096
2097 pub(crate) fn set_attribute_from_parser(
2098 &self,
2099 cx: &mut JSContext,
2100 qname: QualName,
2101 value: DOMString,
2102 prefix: Option<Prefix>,
2103 ) {
2104 if self
2106 .attrs
2107 .borrow()
2108 .iter()
2109 .any(|a| *a.local_name() == qname.local && *a.namespace() == qname.ns)
2110 {
2111 return;
2112 }
2113
2114 let name = match prefix {
2115 None => qname.local.clone(),
2116 Some(ref prefix) => {
2117 let name = format!("{}:{}", &**prefix, &*qname.local);
2118 LocalName::from(name)
2119 },
2120 };
2121 let value = self.parse_attribute(&qname.ns, &qname.local, value);
2122 self.push_new_attribute(
2123 cx,
2124 qname.local,
2125 value,
2126 name,
2127 qname.ns,
2128 prefix,
2129 AttributeMutationReason::ByParser,
2130 );
2131 }
2132
2133 pub(crate) fn set_attribute(&self, cx: &mut JSContext, name: &LocalName, value: AttrValue) {
2134 debug_assert_eq!(
2135 *name,
2136 name.to_ascii_lowercase(),
2137 "All attribute accesses should use a lowercase ASCII name"
2138 );
2139 debug_assert!(!name.contains(':'));
2140
2141 self.set_first_matching_attribute(
2142 cx,
2143 name.clone(),
2144 value,
2145 name.clone(),
2146 ns!(),
2147 None,
2148 |attr| attr.local_name() == name,
2149 );
2150 }
2151
2152 pub(crate) fn set_attribute_with_namespace(
2153 &self,
2154 cx: &mut JSContext,
2155 local_name: LocalName,
2156 value: AttrValue,
2157 name: LocalName,
2158 namespace: Namespace,
2159 prefix: Option<Prefix>,
2160 ) {
2161 self.set_first_matching_attribute(
2162 cx,
2163 local_name.clone(),
2164 value,
2165 name,
2166 namespace.clone(),
2167 prefix,
2168 |attr| *attr.local_name() == local_name && *attr.namespace() == namespace,
2169 );
2170 }
2171
2172 #[allow(clippy::too_many_arguments)]
2174 fn set_first_matching_attribute<F>(
2175 &self,
2176 cx: &mut JSContext,
2177 local_name: LocalName,
2178 value: AttrValue,
2179 name: LocalName,
2180 namespace: Namespace,
2181 prefix: Option<Prefix>,
2182 find: F,
2183 ) where
2184 F: Fn(AttrRef<'_>) -> bool,
2185 {
2186 let found_idx = self.attrs.borrow().iter().position(find);
2189 if let Some(idx) = found_idx {
2190 let attr = self.attrs.ensure_dom(cx, idx, self);
2191 self.will_mutate_attr(AttrRef::Dom(&attr));
2193 self.change_attribute(cx, &attr, value);
2194 } else {
2195 self.push_new_attribute(
2200 cx,
2201 local_name,
2202 value,
2203 name,
2204 namespace,
2205 prefix,
2206 AttributeMutationReason::Directly,
2207 );
2208 };
2209 }
2210
2211 pub(crate) fn parse_attribute(
2212 &self,
2213 namespace: &Namespace,
2214 local_name: &LocalName,
2215 value: DOMString,
2216 ) -> AttrValue {
2217 if is_relevant_attribute(namespace, local_name) {
2218 vtable_for(self.upcast()).parse_plain_attribute(local_name, value)
2219 } else {
2220 AttrValue::String(value.into())
2221 }
2222 }
2223
2224 pub(crate) fn remove_attribute(
2225 &self,
2226 cx: &mut JSContext,
2227 namespace: &Namespace,
2228 local_name: &LocalName,
2229 ) -> Option<DomRoot<Attr>> {
2230 self.remove_first_matching_attribute(cx, |attr| {
2231 attr.namespace() == namespace && attr.local_name() == local_name
2232 })
2233 }
2234
2235 pub(crate) fn remove_attribute_by_name(
2236 &self,
2237 cx: &mut JSContext,
2238 name: &LocalName,
2239 ) -> Option<DomRoot<Attr>> {
2240 self.remove_first_matching_attribute(cx, |attr| attr.name() == name)
2241 }
2242
2243 fn remove_first_matching_attribute<F>(
2245 &self,
2246 cx: &mut JSContext,
2247 find: F,
2248 ) -> Option<DomRoot<Attr>>
2249 where
2250 F: Fn(AttrRef<'_>) -> bool,
2251 {
2252 let idx = self.attrs.borrow().iter().position(find);
2253 idx.map(|idx| {
2254 let attr = self.attrs.ensure_dom(cx, idx, self);
2255
2256 self.will_mutate_attr(AttrRef::Dom(&attr));
2258 self.attrs.remove(idx);
2259 attr.set_owner(cx, None);
2261 self.handle_attribute_changes(
2263 cx,
2264 AttrRef::Dom(&attr),
2265 Some(&attr.value()),
2266 None,
2267 AttributeMutationReason::Directly,
2268 );
2269
2270 attr
2271 })
2272 }
2273
2274 pub(crate) fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2275 self.get_tokenlist_attribute(&local_name!("class"))
2276 .iter()
2277 .any(|atom| case_sensitivity.eq_atom(name, atom))
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.is::<HTMLScriptElement>() {
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(cx, self, markup, false);
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 pub(crate) fn register_current_id_and_name_attribute(&self, cx: &mut JSContext) {
2788 if let Some(shadow_root) = self.containing_shadow_root() {
2789 if let Some(ref id) = *self.id_attribute.borrow() {
2790 shadow_root.register_element_id(self, id, CanGc::from_cx(cx));
2791 }
2792 } else {
2793 let document = self.owner_document();
2794 if let Some(ref id) = *self.id_attribute.borrow() {
2795 document.register_element_id(cx, self, id);
2796 }
2797 if let Some(ref name) = self.name_attribute() {
2798 document.register_element_name(self, name);
2799 }
2800 }
2801 }
2802
2803 pub(crate) fn unregister_current_id_and_name_attribute(&self, cx: &mut JSContext) {
2804 if let Some(shadow_root) = self.containing_shadow_root() {
2805 if self.upcast::<Node>().is_in_a_shadow_tree() {
2809 return;
2810 }
2811 if let Some(ref id) = *self.id_attribute.borrow() {
2812 shadow_root.unregister_element_id(id);
2813 }
2814 } else {
2815 let document = self.owner_document();
2816 if let Some(ref id) = *self.id_attribute.borrow() {
2817 document.unregister_element_id(cx, id);
2818 }
2819 if let Some(ref name) = self.name_attribute() {
2820 document.unregister_element_name(name);
2821 }
2822 }
2823 }
2824}
2825
2826impl ElementMethods<crate::DomTypeHolder> for Element {
2827 fn GetNamespaceURI(&self) -> Option<DOMString> {
2829 Node::namespace_to_string(self.namespace.clone())
2830 }
2831
2832 fn LocalName(&self) -> DOMString {
2834 DOMString::from(&*self.local_name)
2836 }
2837
2838 fn GetPrefix(&self) -> Option<DOMString> {
2840 self.prefix.borrow().as_ref().map(|p| DOMString::from(&**p))
2841 }
2842
2843 fn TagName(&self) -> DOMString {
2845 let name = self.tag_name.or_init(|| {
2846 let qualified_name = match *self.prefix.borrow() {
2847 Some(ref prefix) => Cow::Owned(format!("{}:{}", &**prefix, &*self.local_name)),
2848 None => Cow::Borrowed(&*self.local_name),
2849 };
2850 if self.html_element_in_html_document() {
2851 LocalName::from(qualified_name.to_ascii_uppercase())
2852 } else {
2853 LocalName::from(qualified_name)
2854 }
2855 });
2856 DOMString::from(&*name)
2857 }
2858
2859 fn Id(&self) -> DOMString {
2863 self.get_string_attribute(&local_name!("id"))
2864 }
2865
2866 fn SetId(&self, cx: &mut JSContext, id: DOMString) {
2868 self.set_atomic_attribute(cx, &local_name!("id"), id);
2869 }
2870
2871 fn ClassName(&self) -> DOMString {
2873 self.get_string_attribute(&local_name!("class"))
2874 }
2875
2876 fn SetClassName(&self, cx: &mut JSContext, class: DOMString) {
2878 self.set_tokenlist_attribute(cx, &local_name!("class"), class);
2879 }
2880
2881 fn ClassList(&self, cx: &mut js::context::JSContext) -> DomRoot<DOMTokenList> {
2883 self.class_list
2884 .or_init(|| DOMTokenList::new(cx, self, &local_name!("class"), None))
2885 }
2886
2887 make_getter!(Slot, "slot");
2889
2890 make_setter!(SetSlot, "slot");
2892
2893 fn Attributes(&self, can_gc: CanGc) -> DomRoot<NamedNodeMap> {
2895 self.attr_list
2896 .or_init(|| NamedNodeMap::new(&self.owner_window(), self, can_gc))
2897 }
2898
2899 fn HasAttributes(&self) -> bool {
2901 !self.attrs.borrow().is_empty()
2902 }
2903
2904 fn GetAttributeNames(&self) -> Vec<DOMString> {
2906 self.attrs
2907 .borrow()
2908 .iter()
2909 .map(|attr| DOMString::from(&**attr.name()))
2910 .collect()
2911 }
2912
2913 fn GetAttribute(&self, cx: &mut JSContext, name: DOMString) -> Option<DOMString> {
2915 self.GetAttributeNode(cx, name).map(|s| s.Value())
2916 }
2917
2918 fn GetAttributeNS(
2920 &self,
2921 cx: &mut JSContext,
2922 namespace: Option<DOMString>,
2923 local_name: DOMString,
2924 ) -> Option<DOMString> {
2925 self.GetAttributeNodeNS(cx, namespace, local_name)
2926 .map(|attr| attr.Value())
2927 }
2928
2929 fn GetAttributeNode(&self, cx: &mut JSContext, name: DOMString) -> Option<DomRoot<Attr>> {
2931 self.get_attribute_by_name(cx, name)
2932 }
2933
2934 fn GetAttributeNodeNS(
2936 &self,
2937 cx: &mut JSContext,
2938 namespace: Option<DOMString>,
2939 local_name: DOMString,
2940 ) -> Option<DomRoot<Attr>> {
2941 let namespace = &namespace_from_domstring(namespace);
2942 self.get_attribute_with_namespace(cx, namespace, &LocalName::from(local_name))
2943 }
2944
2945 fn ToggleAttribute(
2947 &self,
2948 cx: &mut JSContext,
2949 name: DOMString,
2950 force: Option<bool>,
2951 ) -> Fallible<bool> {
2952 if !is_valid_attribute_local_name(&name.str()) {
2955 return Err(Error::InvalidCharacter(None));
2956 }
2957
2958 let attribute = self.GetAttribute(cx, name.clone());
2960
2961 let name = self.parsed_name(name);
2963 match attribute {
2964 None => match force {
2966 None | Some(true) => {
2968 self.set_first_matching_attribute(
2969 cx,
2970 name.clone(),
2971 AttrValue::String(String::new()),
2972 name.clone(),
2973 ns!(),
2974 None,
2975 |attr| *attr.name() == name,
2976 );
2977 Ok(true)
2978 },
2979 Some(false) => Ok(false),
2981 },
2982 Some(_index) => match force {
2983 None | Some(false) => {
2985 self.remove_attribute_by_name(cx, &name);
2986 Ok(false)
2987 },
2988 Some(true) => Ok(true),
2990 },
2991 }
2992 }
2993
2994 fn SetAttribute(
2996 &self,
2997 cx: &mut JSContext,
2998 name: DOMString,
2999 value: TrustedTypeOrString,
3000 ) -> ErrorResult {
3001 if !is_valid_attribute_local_name(&name.str()) {
3004 return Err(Error::InvalidCharacter(None));
3005 }
3006
3007 let name = self.parsed_name(name);
3010
3011 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3015 cx,
3016 self.namespace(),
3017 self.local_name(),
3018 &name,
3019 None,
3020 value,
3021 &self.owner_global(),
3022 )?;
3023
3024 let value = self.parse_attribute(&ns!(), &name, value);
3029 self.set_first_matching_attribute(
3030 cx,
3031 name.clone(),
3032 value,
3033 name.clone(),
3034 ns!(),
3035 None,
3036 |attr| *attr.name() == name,
3037 );
3038 Ok(())
3039 }
3040
3041 fn SetAttributeNS(
3043 &self,
3044 cx: &mut JSContext,
3045 namespace: Option<DOMString>,
3046 qualified_name: DOMString,
3047 value: TrustedTypeOrString,
3048 ) -> ErrorResult {
3049 let (namespace, prefix, local_name) =
3051 domname::validate_and_extract(namespace, &qualified_name, domname::Context::Element)?;
3052 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3055 cx,
3056 self.namespace(),
3057 self.local_name(),
3058 &local_name,
3059 Some(&namespace),
3060 value,
3061 &self.owner_global(),
3062 )?;
3063 let value = self.parse_attribute(&namespace, &local_name, value);
3065 self.set_attribute_with_namespace(
3066 cx,
3067 local_name,
3068 value,
3069 LocalName::from(qualified_name),
3070 namespace,
3071 prefix,
3072 );
3073 Ok(())
3074 }
3075
3076 fn SetAttributeNode(&self, cx: &mut JSContext, attr: &Attr) -> Fallible<Option<DomRoot<Attr>>> {
3078 self.set_attribute_node(cx, attr)
3079 }
3080
3081 fn SetAttributeNodeNS(
3083 &self,
3084 cx: &mut JSContext,
3085 attr: &Attr,
3086 ) -> Fallible<Option<DomRoot<Attr>>> {
3087 self.set_attribute_node(cx, attr)
3088 }
3089
3090 fn RemoveAttribute(&self, cx: &mut JSContext, name: DOMString) {
3092 let name = self.parsed_name(name);
3093 self.remove_attribute_by_name(cx, &name);
3094 }
3095
3096 fn RemoveAttributeNS(
3098 &self,
3099 cx: &mut JSContext,
3100 namespace: Option<DOMString>,
3101 local_name: DOMString,
3102 ) {
3103 let namespace = namespace_from_domstring(namespace);
3104 let local_name = LocalName::from(local_name);
3105 self.remove_attribute(cx, &namespace, &local_name);
3106 }
3107
3108 fn RemoveAttributeNode(&self, cx: &mut JSContext, attr: &Attr) -> Fallible<DomRoot<Attr>> {
3110 self.remove_first_matching_attribute(cx, |a| {
3113 a.as_attr().is_some_and(|a| std::ptr::eq(a, attr))
3114 })
3115 .ok_or(Error::NotFound(None))
3116 }
3117
3118 fn HasAttribute(&self, cx: &mut JSContext, name: DOMString) -> bool {
3120 self.GetAttribute(cx, name).is_some()
3121 }
3122
3123 fn HasAttributeNS(
3125 &self,
3126 cx: &mut JSContext,
3127 namespace: Option<DOMString>,
3128 local_name: DOMString,
3129 ) -> bool {
3130 self.GetAttributeNS(cx, namespace, local_name).is_some()
3131 }
3132
3133 fn GetElementsByTagName(
3135 &self,
3136 cx: &mut JSContext,
3137 localname: DOMString,
3138 ) -> DomRoot<HTMLCollection> {
3139 let window = self.owner_window();
3140 HTMLCollection::by_qualified_name(cx, &window, self.upcast(), LocalName::from(localname))
3141 }
3142
3143 fn GetElementsByTagNameNS(
3145 &self,
3146 cx: &mut JSContext,
3147 maybe_ns: Option<DOMString>,
3148 localname: DOMString,
3149 ) -> DomRoot<HTMLCollection> {
3150 let window = self.owner_window();
3151 HTMLCollection::by_tag_name_ns(cx, &window, self.upcast(), localname, maybe_ns)
3152 }
3153
3154 fn GetElementsByClassName(
3156 &self,
3157 cx: &mut JSContext,
3158 classes: DOMString,
3159 ) -> DomRoot<HTMLCollection> {
3160 let window = self.owner_window();
3161 HTMLCollection::by_class_name(cx, &window, self.upcast(), classes)
3162 }
3163
3164 fn GetClientRects(&self, cx: &mut JSContext) -> DomRoot<DOMRectList> {
3166 let win = self.owner_window();
3167 let raw_rects = self.upcast::<Node>().border_boxes();
3168 let rects: Vec<DomRoot<DOMRect>> = raw_rects
3169 .into_iter()
3170 .map(|rect| {
3171 DOMRect::new(
3172 win.upcast(),
3173 rect.origin.x.to_f64_px(),
3174 rect.origin.y.to_f64_px(),
3175 rect.size.width.to_f64_px(),
3176 rect.size.height.to_f64_px(),
3177 CanGc::from_cx(cx),
3178 )
3179 })
3180 .collect();
3181 DOMRectList::new(&win, rects, CanGc::from_cx(cx))
3182 }
3183
3184 fn GetBoundingClientRect(&self, cx: &mut JSContext) -> DomRoot<DOMRect> {
3186 let win = self.owner_window();
3187 let rect = self.upcast::<Node>().border_box().unwrap_or_default();
3188 debug_assert!(rect.size.width.to_f64_px() >= 0.0 && rect.size.height.to_f64_px() >= 0.0);
3189 DOMRect::new(
3190 win.upcast(),
3191 rect.origin.x.to_f64_px(),
3192 rect.origin.y.to_f64_px(),
3193 rect.size.width.to_f64_px(),
3194 rect.size.height.to_f64_px(),
3195 CanGc::from_cx(cx),
3196 )
3197 }
3198
3199 fn Scroll(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3201 let left = options.left.unwrap_or(self.ScrollLeft());
3203 let top = options.top.unwrap_or(self.ScrollTop());
3204 self.scroll(cx, left, top, options.parent.behavior);
3205 }
3206
3207 fn Scroll_(&self, cx: &mut JSContext, x: f64, y: f64) {
3209 self.scroll(cx, x, y, ScrollBehavior::Auto);
3210 }
3211
3212 fn ScrollTo(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3214 self.Scroll(cx, options);
3215 }
3216
3217 fn ScrollTo_(&self, cx: &mut JSContext, x: f64, y: f64) {
3219 self.Scroll_(cx, x, y);
3220 }
3221
3222 fn ScrollBy(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3224 let delta_left = options.left.unwrap_or(0.0f64);
3226 let delta_top = options.top.unwrap_or(0.0f64);
3227 let left = self.ScrollLeft();
3228 let top = self.ScrollTop();
3229 self.scroll(
3230 cx,
3231 left + delta_left,
3232 top + delta_top,
3233 options.parent.behavior,
3234 );
3235 }
3236
3237 fn ScrollBy_(&self, cx: &mut JSContext, x: f64, y: f64) {
3239 let left = self.ScrollLeft();
3240 let top = self.ScrollTop();
3241 self.scroll(cx, left + x, top + y, ScrollBehavior::Auto);
3242 }
3243
3244 fn ScrollTop(&self) -> f64 {
3246 let node = self.upcast::<Node>();
3247
3248 let doc = node.owner_doc();
3250
3251 if !doc.is_fully_active() {
3253 return 0.0;
3254 }
3255
3256 let win = match doc.GetDefaultView() {
3258 None => return 0.0,
3259 Some(win) => win,
3260 };
3261
3262 if self.is_document_element() {
3264 if doc.quirks_mode() == QuirksMode::Quirks {
3265 return 0.0;
3266 }
3267
3268 return win.ScrollY() as f64;
3270 }
3271
3272 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3274 doc.quirks_mode() == QuirksMode::Quirks &&
3275 !self.is_potentially_scrollable_body()
3276 {
3277 return win.ScrollY() as f64;
3278 }
3279
3280 if !self.has_css_layout_box() {
3282 return 0.0;
3283 }
3284
3285 let point = win.scroll_offset_query(node);
3287 point.y.abs() as f64
3288 }
3289
3290 fn SetScrollTop(&self, cx: &mut JSContext, y_: f64) {
3293 let behavior = ScrollBehavior::Auto;
3294
3295 let y = if y_.is_finite() { y_ } else { 0.0 } as f32;
3297
3298 let node = self.upcast::<Node>();
3299
3300 let doc = node.owner_doc();
3302
3303 if !doc.is_fully_active() {
3305 return;
3306 }
3307
3308 let win = match doc.GetDefaultView() {
3310 None => return,
3311 Some(win) => win,
3312 };
3313
3314 if self.is_document_element() {
3316 if doc.quirks_mode() != QuirksMode::Quirks {
3317 win.scroll(cx, win.ScrollX() as f32, y, behavior);
3318 }
3319
3320 return;
3321 }
3322
3323 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3325 doc.quirks_mode() == QuirksMode::Quirks &&
3326 !self.is_potentially_scrollable_body()
3327 {
3328 win.scroll(cx, win.ScrollX() as f32, y, behavior);
3329 return;
3330 }
3331
3332 if !self.has_scrolling_box() {
3334 return;
3335 }
3336
3337 win.scroll_an_element(cx, self, self.ScrollLeft() as f32, y, behavior);
3339 }
3340
3341 fn ScrollLeft(&self) -> f64 {
3343 let node = self.upcast::<Node>();
3344
3345 let doc = node.owner_doc();
3347
3348 if !doc.is_fully_active() {
3350 return 0.0;
3351 }
3352
3353 let win = match doc.GetDefaultView() {
3355 None => return 0.0,
3356 Some(win) => win,
3357 };
3358
3359 if self.is_document_element() {
3361 if doc.quirks_mode() != QuirksMode::Quirks {
3362 return win.ScrollX() as f64;
3364 }
3365
3366 return 0.0;
3367 }
3368
3369 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3371 doc.quirks_mode() == QuirksMode::Quirks &&
3372 !self.is_potentially_scrollable_body()
3373 {
3374 return win.ScrollX() as f64;
3375 }
3376
3377 if !self.has_css_layout_box() {
3379 return 0.0;
3380 }
3381
3382 let point = win.scroll_offset_query(node);
3384 point.x.abs() as f64
3385 }
3386
3387 fn SetScrollLeft(&self, cx: &mut JSContext, x: f64) {
3389 let behavior = ScrollBehavior::Auto;
3390
3391 let x = if x.is_finite() { x } else { 0.0 } as f32;
3393
3394 let node = self.upcast::<Node>();
3395
3396 let doc = node.owner_doc();
3398
3399 if !doc.is_fully_active() {
3401 return;
3402 }
3403
3404 let win = match doc.GetDefaultView() {
3406 None => return,
3407 Some(win) => win,
3408 };
3409
3410 if self.is_document_element() {
3412 if doc.quirks_mode() == QuirksMode::Quirks {
3413 return;
3414 }
3415
3416 win.scroll(cx, x, win.ScrollY() as f32, behavior);
3417 return;
3418 }
3419
3420 if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3422 doc.quirks_mode() == QuirksMode::Quirks &&
3423 !self.is_potentially_scrollable_body()
3424 {
3425 win.scroll(cx, x, win.ScrollY() as f32, behavior);
3426 return;
3427 }
3428
3429 if !self.has_scrolling_box() {
3431 return;
3432 }
3433
3434 win.scroll_an_element(cx, self, x, self.ScrollTop() as f32, behavior);
3436 }
3437
3438 fn ScrollIntoView(&self, cx: &mut JSContext, arg: BooleanOrScrollIntoViewOptions) {
3440 let (behavior, block, inline, container) = match arg {
3441 BooleanOrScrollIntoViewOptions::Boolean(true) => (
3443 ScrollBehavior::Auto, ScrollLogicalPosition::Start, ScrollLogicalPosition::Nearest, None, ),
3448 BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(options) => (
3451 options.parent.behavior,
3452 options.block,
3453 options.inline,
3454 if options.container == ScrollIntoViewContainer::Nearest {
3457 Some(self)
3458 } else {
3459 None
3460 },
3461 ),
3462 BooleanOrScrollIntoViewOptions::Boolean(false) => (
3464 ScrollBehavior::Auto,
3465 ScrollLogicalPosition::End,
3466 ScrollLogicalPosition::Nearest,
3467 None,
3468 ),
3469 };
3470
3471 if !self.has_css_layout_box() {
3474 return;
3475 }
3476
3477 self.scroll_into_view_with_options(
3479 cx,
3480 behavior,
3481 ScrollAxisState::new_always_scroll_position(block),
3482 ScrollAxisState::new_always_scroll_position(inline),
3483 container,
3484 None,
3485 );
3486
3487 }
3490
3491 fn ScrollWidth(&self) -> i32 {
3493 self.upcast::<Node>().scroll_area().size.width
3494 }
3495
3496 fn ScrollHeight(&self) -> i32 {
3498 self.upcast::<Node>().scroll_area().size.height
3499 }
3500
3501 fn ClientTop(&self) -> i32 {
3503 self.client_rect().origin.y
3504 }
3505
3506 fn ClientLeft(&self) -> i32 {
3508 self.client_rect().origin.x
3509 }
3510
3511 fn ClientWidth(&self) -> i32 {
3513 self.client_rect().size.width
3514 }
3515
3516 fn ClientHeight(&self) -> i32 {
3518 self.client_rect().size.height
3519 }
3520
3521 fn CurrentCSSZoom(&self) -> Finite<f64> {
3523 let window = self.owner_window();
3524 Finite::wrap(window.current_css_zoom_query(self.upcast::<Node>()) as f64)
3525 }
3526
3527 fn SetHTMLUnsafe(
3529 &self,
3530 cx: &mut JSContext,
3531 html: TrustedHTMLOrString,
3532 options: &SetHTMLUnsafeOptions,
3533 ) -> ErrorResult {
3534 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
3538 cx,
3539 &self.owner_global(),
3540 html,
3541 "Element setHTMLUnsafe",
3542 )?;
3543 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3545 DomRoot::upcast(template.Content(cx))
3546 } else {
3547 DomRoot::from_ref(self.upcast())
3548 };
3549
3550 Sanitizer::set_and_filter_html(cx, &target, self, compliant_html, options, false)?;
3552
3553 Ok(())
3554 }
3555
3556 fn SetHTML(
3558 &self,
3559 cx: &mut JSContext,
3560 html: DOMString,
3561 options: &SetHTMLOptions,
3562 ) -> ErrorResult {
3563 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3565 DomRoot::upcast(template.Content(cx))
3566 } else {
3567 DomRoot::from_ref(self.upcast())
3568 };
3569
3570 Sanitizer::set_and_filter_html(cx, &target, self, html, options, true)
3572 }
3573
3574 fn GetHTML(&self, cx: &mut JSContext, options: &GetHTMLOptions) -> DOMString {
3576 self.upcast::<Node>().html_serialize(
3579 cx,
3580 TraversalScope::ChildrenOnly(None),
3581 options.serializableShadowRoots,
3582 options.shadowRoots.clone(),
3583 )
3584 }
3585
3586 fn GetInnerHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3588 let qname = QualName::new(
3589 self.prefix().clone(),
3590 self.namespace().clone(),
3591 self.local_name().clone(),
3592 );
3593
3594 let result = if self.owner_document().is_html_document() {
3597 self.upcast::<Node>()
3598 .html_serialize(cx, ChildrenOnly(Some(qname)), false, vec![])
3599 } else {
3600 self.upcast::<Node>()
3601 .xml_serialize(XmlChildrenOnly(Some(qname)))?
3602 };
3603
3604 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3605 }
3606
3607 fn SetInnerHTML(
3609 &self,
3610 cx: &mut JSContext,
3611 value: TrustedHTMLOrNullIsEmptyString,
3612 ) -> ErrorResult {
3613 let value = TrustedHTML::get_trusted_type_compliant_string(
3617 cx,
3618 &self.owner_global(),
3619 value.convert(),
3620 "Element innerHTML",
3621 )?;
3622 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3624 DomRoot::upcast(template.Content(cx))
3627 } else {
3628 DomRoot::from_ref(self.upcast())
3630 };
3631
3632 if !self.node.has_weird_parser_insertion_mode() &&
3635 value.len() < 100 &&
3636 !value
3637 .as_bytes()
3638 .iter()
3639 .any(|c| matches!(*c, b'&' | b'\0' | b'<' | b'\r'))
3640 {
3641 return Node::SetTextContent(&target, cx, Some(value));
3642 }
3643
3644 let frag = self.parse_fragment(value, cx)?;
3647
3648 Node::replace_all(cx, Some(frag.upcast()), &target);
3650 Ok(())
3651 }
3652
3653 fn GetOuterHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3655 let result = if self.owner_document().is_html_document() {
3658 self.upcast::<Node>()
3659 .html_serialize(cx, IncludeNode, false, vec![])
3660 } else {
3661 self.upcast::<Node>().xml_serialize(XmlIncludeNode)?
3662 };
3663
3664 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3665 }
3666
3667 fn SetOuterHTML(
3669 &self,
3670 cx: &mut JSContext,
3671 value: TrustedHTMLOrNullIsEmptyString,
3672 ) -> ErrorResult {
3673 let value = TrustedHTML::get_trusted_type_compliant_string(
3677 cx,
3678 &self.owner_global(),
3679 value.convert(),
3680 "Element outerHTML",
3681 )?;
3682 let context_document = self.owner_document();
3683 let context_node = self.upcast::<Node>();
3684 let context_parent = match context_node.GetParentNode() {
3686 None => {
3687 return Ok(());
3690 },
3691 Some(parent) => parent,
3692 };
3693
3694 let parent = match context_parent.type_id() {
3695 NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed(None)),
3697
3698 NodeTypeId::DocumentFragment(_) => {
3701 let body_elem = Element::create(
3702 cx,
3703 QualName::new(None, ns!(html), local_name!("body")),
3704 None,
3705 &context_document,
3706 ElementCreator::ScriptCreated,
3707 CustomElementCreationMode::Synchronous,
3708 None,
3709 );
3710 DomRoot::upcast(body_elem)
3711 },
3712 _ => context_node.GetParentElement().unwrap(),
3713 };
3714
3715 let frag = parent.parse_fragment(value, cx)?;
3718 context_parent.ReplaceChild(cx, frag.upcast(), context_node)?;
3720 Ok(())
3721 }
3722
3723 fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
3725 self.upcast::<Node>()
3726 .preceding_siblings()
3727 .find_map(DomRoot::downcast)
3728 }
3729
3730 fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
3732 self.upcast::<Node>()
3733 .following_siblings()
3734 .find_map(DomRoot::downcast)
3735 }
3736
3737 fn Children(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
3739 let window = self.owner_window();
3740 HTMLCollection::children(cx, &window, self.upcast())
3741 }
3742
3743 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
3745 self.upcast::<Node>().child_elements().next()
3746 }
3747
3748 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
3750 self.upcast::<Node>()
3751 .rev_children()
3752 .find_map(DomRoot::downcast::<Element>)
3753 }
3754
3755 fn ChildElementCount(&self) -> u32 {
3757 self.upcast::<Node>().child_elements().count() as u32
3758 }
3759
3760 fn Prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3762 self.upcast::<Node>().prepend(cx, nodes)
3763 }
3764
3765 fn Append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3767 self.upcast::<Node>().append(cx, nodes)
3768 }
3769
3770 fn ReplaceChildren(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3772 self.upcast::<Node>().replace_children(cx, nodes)
3773 }
3774
3775 fn MoveBefore(&self, cx: &mut JSContext, node: &Node, child: Option<&Node>) -> ErrorResult {
3777 self.upcast::<Node>().move_before(cx, node, child)
3778 }
3779
3780 fn QuerySelector(
3782 &self,
3783 cx: &mut JSContext,
3784 selectors: DOMString,
3785 ) -> Fallible<Option<DomRoot<Element>>> {
3786 let root = self.upcast::<Node>();
3787 root.query_selector(cx.no_gc(), selectors)
3788 }
3789
3790 fn QuerySelectorAll(
3792 &self,
3793 cx: &mut JSContext,
3794 selectors: DOMString,
3795 ) -> Fallible<DomRoot<NodeList>> {
3796 let root = self.upcast::<Node>();
3797 root.query_selector_all(cx.no_gc(), selectors)
3798 }
3799
3800 fn Before(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3802 self.upcast::<Node>().before(cx, nodes)
3803 }
3804
3805 fn After(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3807 self.upcast::<Node>().after(cx, nodes)
3808 }
3809
3810 fn ReplaceWith(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3812 self.upcast::<Node>().replace_with(cx, nodes)
3813 }
3814
3815 fn Remove(&self, cx: &mut JSContext) {
3817 self.upcast::<Node>().remove_self(cx);
3818 }
3819
3820 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
3822 fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
3823 let document = self.owner_document();
3824 let url = document.url();
3825 let selectors = match SelectorParser::parse_author_origin_no_namespace(
3826 &selectors.str(),
3827 &UrlExtraData(url.get_arc()),
3828 ) {
3829 Err(_) => return Err(Error::Syntax(None)),
3830 Ok(selectors) => selectors,
3831 };
3832
3833 let traced_self = Dom::from_ref(self);
3835 let quirks_mode = document.quirks_mode();
3836 Ok(with_layout_state(|| {
3837 #[expect(unsafe_code)]
3838 let layout_element: LayoutDom<'_, _> = unsafe { traced_self.to_layout() };
3839 dom_apis::element_matches(
3840 &ServoDangerousStyleElement::from(layout_element.upcast()),
3841 &selectors,
3842 quirks_mode,
3843 )
3844 }))
3845 }
3846
3847 fn WebkitMatchesSelector(&self, selectors: DOMString) -> Fallible<bool> {
3849 self.Matches(selectors)
3850 }
3851
3852 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
3854 fn Closest(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
3855 let document = self.owner_document();
3856 let url = document.url();
3857 let selectors = match SelectorParser::parse_author_origin_no_namespace(
3858 &selectors.str(),
3859 &UrlExtraData(url.get_arc()),
3860 ) {
3861 Err(_) => return Err(Error::Syntax(None)),
3862 Ok(selectors) => selectors,
3863 };
3864
3865 let traced_self = Dom::from_ref(self);
3867 let quirks_mode = document.quirks_mode();
3868 let closest_element = with_layout_state(|| {
3869 #[expect(unsafe_code)]
3870 let layout_element: LayoutDom<'_, _> = unsafe { traced_self.to_layout() };
3871 dom_apis::element_closest(
3872 ServoDangerousStyleElement::from(layout_element.upcast()),
3873 &selectors,
3874 quirks_mode,
3875 )
3876 });
3877 Ok(closest_element.map(ServoDangerousStyleElement::rooted))
3878 }
3879
3880 fn InsertAdjacentElement(
3882 &self,
3883 cx: &mut JSContext,
3884 where_: DOMString,
3885 element: &Element,
3886 ) -> Fallible<Option<DomRoot<Element>>> {
3887 let where_ = where_.parse::<AdjacentPosition>()?;
3888 let inserted_node = self.insert_adjacent(cx, where_, element.upcast())?;
3889 Ok(inserted_node.map(|node| DomRoot::downcast(node).unwrap()))
3890 }
3891
3892 fn InsertAdjacentText(
3894 &self,
3895 cx: &mut JSContext,
3896 where_: DOMString,
3897 data: DOMString,
3898 ) -> ErrorResult {
3899 let text = Text::new(cx, data, &self.owner_document());
3901
3902 let where_ = where_.parse::<AdjacentPosition>()?;
3904 self.insert_adjacent(cx, where_, text.upcast()).map(|_| ())
3905 }
3906
3907 fn InsertAdjacentHTML(
3909 &self,
3910 cx: &mut JSContext,
3911 position: DOMString,
3912 text: TrustedHTMLOrString,
3913 ) -> ErrorResult {
3914 let text = TrustedHTML::get_trusted_type_compliant_string(
3918 cx,
3919 &self.owner_global(),
3920 text,
3921 "Element insertAdjacentHTML",
3922 )?;
3923 let position = position.parse::<AdjacentPosition>()?;
3924
3925 let context = match position {
3928 AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => {
3931 match self.upcast::<Node>().GetParentNode() {
3932 Some(ref node) if node.is::<Document>() => {
3934 return Err(Error::NoModificationAllowed(None));
3935 },
3936 None => return Err(Error::NoModificationAllowed(None)),
3937 Some(node) => node,
3939 }
3940 },
3941 AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => {
3944 DomRoot::from_ref(self.upcast::<Node>())
3946 },
3947 };
3948
3949 let context = Element::fragment_parsing_context(
3951 cx,
3952 &context.owner_doc(),
3953 context.downcast::<Element>(),
3954 );
3955
3956 let fragment = context.parse_fragment(text, cx)?;
3959
3960 self.insert_adjacent(cx, position, fragment.upcast())
3962 .map(|_| ())
3963 }
3964
3965 fn EnterFormalActivationState(&self) -> ErrorResult {
3967 match self.as_maybe_activatable() {
3968 Some(a) => {
3969 a.enter_formal_activation_state();
3970 Ok(())
3971 },
3972 None => Err(Error::NotSupported(None)),
3973 }
3974 }
3975
3976 fn ExitFormalActivationState(&self) -> ErrorResult {
3977 match self.as_maybe_activatable() {
3978 Some(a) => {
3979 a.exit_formal_activation_state();
3980 Ok(())
3981 },
3982 None => Err(Error::NotSupported(None)),
3983 }
3984 }
3985
3986 fn RequestFullscreen(&self, can_gc: CanGc) -> Rc<Promise> {
3988 let doc = self.owner_document();
3989 doc.enter_fullscreen(self, can_gc)
3990 }
3991
3992 fn SetPointerCapture(&self, pointer_id: i32) -> ErrorResult {
3994 let document = self.owner_document();
3995 let event_handler = document.event_handler();
3996
3997 if !self.upcast::<Node>().is_connected() {
4008 return Err(Error::InvalidState(Some(
4009 "Can't capture pointer on an unconnected element".into(),
4010 )));
4011 }
4012
4013 if !event_handler.is_active_pointer(pointer_id) {
4023 return Ok(());
4024 }
4025
4026 event_handler.set_pending_pointer_capture(pointer_id, self);
4029
4030 Ok(())
4031 }
4032
4033 fn ReleasePointerCapture(&self, pointer_id: i32) -> ErrorResult {
4035 let document = self.owner_document();
4036 let event_handler = document.event_handler();
4037
4038 if !event_handler.is_active_pointer(pointer_id) {
4042 return Err(Error::NotFound(Some(
4043 "Can't release a pointer that is not active".into(),
4044 )));
4045 }
4046
4047 if !event_handler.has_pointer_capture(pointer_id, self) {
4050 return Ok(());
4051 }
4052
4053 event_handler.clear_pending_pointer_capture(pointer_id);
4055
4056 Ok(())
4057 }
4058
4059 fn HasPointerCapture(&self, pointer_id: i32) -> bool {
4061 let document = self.owner_document();
4062 let event_handler = document.event_handler();
4063 event_handler.has_pointer_capture(pointer_id, self)
4064 }
4065
4066 fn AttachShadow(
4068 &self,
4069 cx: &mut JSContext,
4070 init: &ShadowRootInit,
4071 ) -> Fallible<DomRoot<ShadowRoot>> {
4072 let shadow_root = self.attach_shadow(
4075 cx,
4076 IsUserAgentWidget::No,
4077 init.mode,
4078 init.clonable,
4079 init.serializable,
4080 init.delegatesFocus,
4081 init.slotAssignment,
4082 )?;
4083
4084 Ok(shadow_root)
4086 }
4087
4088 fn GetShadowRoot(&self) -> Option<DomRoot<ShadowRoot>> {
4090 let shadow_or_none = self.shadow_root();
4092
4093 let shadow = shadow_or_none?;
4095 if shadow.Mode() == ShadowRootMode::Closed {
4096 return None;
4097 }
4098
4099 Some(shadow)
4101 }
4102
4103 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
4105 self.custom_element_registry()
4107 }
4108
4109 fn GetRole(&self) -> Option<DOMString> {
4111 self.get_nullable_string_attribute(&local_name!("role"))
4112 }
4113
4114 fn SetRole(&self, cx: &mut JSContext, value: Option<DOMString>) {
4116 self.set_nullable_string_attribute(cx, &local_name!("role"), value);
4117 }
4118
4119 fn GetAriaAtomic(&self) -> Option<DOMString> {
4120 self.get_nullable_string_attribute(&local_name!("aria-atomic"))
4121 }
4122
4123 fn SetAriaAtomic(&self, cx: &mut JSContext, value: Option<DOMString>) {
4124 self.set_nullable_string_attribute(cx, &local_name!("aria-atomic"), value);
4125 }
4126
4127 fn GetAriaAutoComplete(&self) -> Option<DOMString> {
4128 self.get_nullable_string_attribute(&local_name!("aria-autocomplete"))
4129 }
4130
4131 fn SetAriaAutoComplete(&self, cx: &mut JSContext, value: Option<DOMString>) {
4132 self.set_nullable_string_attribute(cx, &local_name!("aria-autocomplete"), value);
4133 }
4134
4135 fn GetAriaBrailleLabel(&self) -> Option<DOMString> {
4136 self.get_nullable_string_attribute(&local_name!("aria-braillelabel"))
4137 }
4138
4139 fn SetAriaBrailleLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4140 self.set_nullable_string_attribute(cx, &local_name!("aria-braillelabel"), value);
4141 }
4142
4143 fn GetAriaBrailleRoleDescription(&self) -> Option<DOMString> {
4144 self.get_nullable_string_attribute(&local_name!("aria-brailleroledescription"))
4145 }
4146
4147 fn SetAriaBrailleRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4148 self.set_nullable_string_attribute(cx, &local_name!("aria-brailleroledescription"), value);
4149 }
4150
4151 fn GetAriaBusy(&self) -> Option<DOMString> {
4152 self.get_nullable_string_attribute(&local_name!("aria-busy"))
4153 }
4154
4155 fn SetAriaBusy(&self, cx: &mut JSContext, value: Option<DOMString>) {
4156 self.set_nullable_string_attribute(cx, &local_name!("aria-busy"), value);
4157 }
4158
4159 fn GetAriaChecked(&self) -> Option<DOMString> {
4160 self.get_nullable_string_attribute(&local_name!("aria-checked"))
4161 }
4162
4163 fn SetAriaChecked(&self, cx: &mut JSContext, value: Option<DOMString>) {
4164 self.set_nullable_string_attribute(cx, &local_name!("aria-checked"), value);
4165 }
4166
4167 fn GetAriaColCount(&self) -> Option<DOMString> {
4168 self.get_nullable_string_attribute(&local_name!("aria-colcount"))
4169 }
4170
4171 fn SetAriaColCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4172 self.set_nullable_string_attribute(cx, &local_name!("aria-colcount"), value);
4173 }
4174
4175 fn GetAriaColIndex(&self) -> Option<DOMString> {
4176 self.get_nullable_string_attribute(&local_name!("aria-colindex"))
4177 }
4178
4179 fn SetAriaColIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4180 self.set_nullable_string_attribute(cx, &local_name!("aria-colindex"), value);
4181 }
4182
4183 fn GetAriaColIndexText(&self) -> Option<DOMString> {
4184 self.get_nullable_string_attribute(&local_name!("aria-colindextext"))
4185 }
4186
4187 fn SetAriaColIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4188 self.set_nullable_string_attribute(cx, &local_name!("aria-colindextext"), value);
4189 }
4190
4191 fn GetAriaColSpan(&self) -> Option<DOMString> {
4192 self.get_nullable_string_attribute(&local_name!("aria-colspan"))
4193 }
4194
4195 fn SetAriaColSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4196 self.set_nullable_string_attribute(cx, &local_name!("aria-colspan"), value);
4197 }
4198
4199 fn GetAriaCurrent(&self) -> Option<DOMString> {
4200 self.get_nullable_string_attribute(&local_name!("aria-current"))
4201 }
4202
4203 fn SetAriaCurrent(&self, cx: &mut JSContext, value: Option<DOMString>) {
4204 self.set_nullable_string_attribute(cx, &local_name!("aria-current"), value);
4205 }
4206
4207 fn GetAriaDescription(&self) -> Option<DOMString> {
4208 self.get_nullable_string_attribute(&local_name!("aria-description"))
4209 }
4210
4211 fn SetAriaDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4212 self.set_nullable_string_attribute(cx, &local_name!("aria-description"), value);
4213 }
4214
4215 fn GetAriaDisabled(&self) -> Option<DOMString> {
4216 self.get_nullable_string_attribute(&local_name!("aria-disabled"))
4217 }
4218
4219 fn SetAriaDisabled(&self, cx: &mut JSContext, value: Option<DOMString>) {
4220 self.set_nullable_string_attribute(cx, &local_name!("aria-disabled"), value);
4221 }
4222
4223 fn GetAriaExpanded(&self) -> Option<DOMString> {
4224 self.get_nullable_string_attribute(&local_name!("aria-expanded"))
4225 }
4226
4227 fn SetAriaExpanded(&self, cx: &mut JSContext, value: Option<DOMString>) {
4228 self.set_nullable_string_attribute(cx, &local_name!("aria-expanded"), value);
4229 }
4230
4231 fn GetAriaHasPopup(&self) -> Option<DOMString> {
4232 self.get_nullable_string_attribute(&local_name!("aria-haspopup"))
4233 }
4234
4235 fn SetAriaHasPopup(&self, cx: &mut JSContext, value: Option<DOMString>) {
4236 self.set_nullable_string_attribute(cx, &local_name!("aria-haspopup"), value);
4237 }
4238
4239 fn GetAriaHidden(&self) -> Option<DOMString> {
4240 self.get_nullable_string_attribute(&local_name!("aria-hidden"))
4241 }
4242
4243 fn SetAriaHidden(&self, cx: &mut JSContext, value: Option<DOMString>) {
4244 self.set_nullable_string_attribute(cx, &local_name!("aria-hidden"), value);
4245 }
4246
4247 fn GetAriaInvalid(&self) -> Option<DOMString> {
4248 self.get_nullable_string_attribute(&local_name!("aria-invalid"))
4249 }
4250
4251 fn SetAriaInvalid(&self, cx: &mut JSContext, value: Option<DOMString>) {
4252 self.set_nullable_string_attribute(cx, &local_name!("aria-invalid"), value);
4253 }
4254
4255 fn GetAriaKeyShortcuts(&self) -> Option<DOMString> {
4256 self.get_nullable_string_attribute(&local_name!("aria-keyshortcuts"))
4257 }
4258
4259 fn SetAriaKeyShortcuts(&self, cx: &mut JSContext, value: Option<DOMString>) {
4260 self.set_nullable_string_attribute(cx, &local_name!("aria-keyshortcuts"), value);
4261 }
4262
4263 fn GetAriaLabel(&self) -> Option<DOMString> {
4264 self.get_nullable_string_attribute(&local_name!("aria-label"))
4265 }
4266
4267 fn SetAriaLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4268 self.set_nullable_string_attribute(cx, &local_name!("aria-label"), value);
4269 }
4270
4271 fn GetAriaLevel(&self) -> Option<DOMString> {
4272 self.get_nullable_string_attribute(&local_name!("aria-level"))
4273 }
4274
4275 fn SetAriaLevel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4276 self.set_nullable_string_attribute(cx, &local_name!("aria-level"), value);
4277 }
4278
4279 fn GetAriaLive(&self) -> Option<DOMString> {
4280 self.get_nullable_string_attribute(&local_name!("aria-live"))
4281 }
4282
4283 fn SetAriaLive(&self, cx: &mut JSContext, value: Option<DOMString>) {
4284 self.set_nullable_string_attribute(cx, &local_name!("aria-live"), value);
4285 }
4286
4287 fn GetAriaModal(&self) -> Option<DOMString> {
4288 self.get_nullable_string_attribute(&local_name!("aria-modal"))
4289 }
4290
4291 fn SetAriaModal(&self, cx: &mut JSContext, value: Option<DOMString>) {
4292 self.set_nullable_string_attribute(cx, &local_name!("aria-modal"), value);
4293 }
4294
4295 fn GetAriaMultiLine(&self) -> Option<DOMString> {
4296 self.get_nullable_string_attribute(&local_name!("aria-multiline"))
4297 }
4298
4299 fn SetAriaMultiLine(&self, cx: &mut JSContext, value: Option<DOMString>) {
4300 self.set_nullable_string_attribute(cx, &local_name!("aria-multiline"), value);
4301 }
4302
4303 fn GetAriaMultiSelectable(&self) -> Option<DOMString> {
4304 self.get_nullable_string_attribute(&local_name!("aria-multiselectable"))
4305 }
4306
4307 fn SetAriaMultiSelectable(&self, cx: &mut JSContext, value: Option<DOMString>) {
4308 self.set_nullable_string_attribute(cx, &local_name!("aria-multiselectable"), value);
4309 }
4310
4311 fn GetAriaOrientation(&self) -> Option<DOMString> {
4312 self.get_nullable_string_attribute(&local_name!("aria-orientation"))
4313 }
4314
4315 fn SetAriaOrientation(&self, cx: &mut JSContext, value: Option<DOMString>) {
4316 self.set_nullable_string_attribute(cx, &local_name!("aria-orientation"), value);
4317 }
4318
4319 fn GetAriaPlaceholder(&self) -> Option<DOMString> {
4320 self.get_nullable_string_attribute(&local_name!("aria-placeholder"))
4321 }
4322
4323 fn SetAriaPlaceholder(&self, cx: &mut JSContext, value: Option<DOMString>) {
4324 self.set_nullable_string_attribute(cx, &local_name!("aria-placeholder"), value);
4325 }
4326
4327 fn GetAriaPosInSet(&self) -> Option<DOMString> {
4328 self.get_nullable_string_attribute(&local_name!("aria-posinset"))
4329 }
4330
4331 fn SetAriaPosInSet(&self, cx: &mut JSContext, value: Option<DOMString>) {
4332 self.set_nullable_string_attribute(cx, &local_name!("aria-posinset"), value);
4333 }
4334
4335 fn GetAriaPressed(&self) -> Option<DOMString> {
4336 self.get_nullable_string_attribute(&local_name!("aria-pressed"))
4337 }
4338
4339 fn SetAriaPressed(&self, cx: &mut JSContext, value: Option<DOMString>) {
4340 self.set_nullable_string_attribute(cx, &local_name!("aria-pressed"), value);
4341 }
4342
4343 fn GetAriaReadOnly(&self) -> Option<DOMString> {
4344 self.get_nullable_string_attribute(&local_name!("aria-readonly"))
4345 }
4346
4347 fn SetAriaReadOnly(&self, cx: &mut JSContext, value: Option<DOMString>) {
4348 self.set_nullable_string_attribute(cx, &local_name!("aria-readonly"), value);
4349 }
4350
4351 fn GetAriaRelevant(&self) -> Option<DOMString> {
4352 self.get_nullable_string_attribute(&local_name!("aria-relevant"))
4353 }
4354
4355 fn SetAriaRelevant(&self, cx: &mut JSContext, value: Option<DOMString>) {
4356 self.set_nullable_string_attribute(cx, &local_name!("aria-relevant"), value);
4357 }
4358
4359 fn GetAriaRequired(&self) -> Option<DOMString> {
4360 self.get_nullable_string_attribute(&local_name!("aria-required"))
4361 }
4362
4363 fn SetAriaRequired(&self, cx: &mut JSContext, value: Option<DOMString>) {
4364 self.set_nullable_string_attribute(cx, &local_name!("aria-required"), value);
4365 }
4366
4367 fn GetAriaRoleDescription(&self) -> Option<DOMString> {
4368 self.get_nullable_string_attribute(&local_name!("aria-roledescription"))
4369 }
4370
4371 fn SetAriaRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4372 self.set_nullable_string_attribute(cx, &local_name!("aria-roledescription"), value);
4373 }
4374
4375 fn GetAriaRowCount(&self) -> Option<DOMString> {
4376 self.get_nullable_string_attribute(&local_name!("aria-rowcount"))
4377 }
4378
4379 fn SetAriaRowCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4380 self.set_nullable_string_attribute(cx, &local_name!("aria-rowcount"), value);
4381 }
4382
4383 fn GetAriaRowIndex(&self) -> Option<DOMString> {
4384 self.get_nullable_string_attribute(&local_name!("aria-rowindex"))
4385 }
4386
4387 fn SetAriaRowIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4388 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindex"), value);
4389 }
4390
4391 fn GetAriaRowIndexText(&self) -> Option<DOMString> {
4392 self.get_nullable_string_attribute(&local_name!("aria-rowindextext"))
4393 }
4394
4395 fn SetAriaRowIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4396 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindextext"), value);
4397 }
4398
4399 fn GetAriaRowSpan(&self) -> Option<DOMString> {
4400 self.get_nullable_string_attribute(&local_name!("aria-rowspan"))
4401 }
4402
4403 fn SetAriaRowSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4404 self.set_nullable_string_attribute(cx, &local_name!("aria-rowspan"), value);
4405 }
4406
4407 fn GetAriaSelected(&self) -> Option<DOMString> {
4408 self.get_nullable_string_attribute(&local_name!("aria-selected"))
4409 }
4410
4411 fn SetAriaSelected(&self, cx: &mut JSContext, value: Option<DOMString>) {
4412 self.set_nullable_string_attribute(cx, &local_name!("aria-selected"), value);
4413 }
4414
4415 fn GetAriaSetSize(&self) -> Option<DOMString> {
4416 self.get_nullable_string_attribute(&local_name!("aria-setsize"))
4417 }
4418
4419 fn SetAriaSetSize(&self, cx: &mut JSContext, value: Option<DOMString>) {
4420 self.set_nullable_string_attribute(cx, &local_name!("aria-setsize"), value);
4421 }
4422
4423 fn GetAriaSort(&self) -> Option<DOMString> {
4424 self.get_nullable_string_attribute(&local_name!("aria-sort"))
4425 }
4426
4427 fn SetAriaSort(&self, cx: &mut JSContext, value: Option<DOMString>) {
4428 self.set_nullable_string_attribute(cx, &local_name!("aria-sort"), value);
4429 }
4430
4431 fn GetAriaValueMax(&self) -> Option<DOMString> {
4432 self.get_nullable_string_attribute(&local_name!("aria-valuemax"))
4433 }
4434
4435 fn SetAriaValueMax(&self, cx: &mut JSContext, value: Option<DOMString>) {
4436 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemax"), value);
4437 }
4438
4439 fn GetAriaValueMin(&self) -> Option<DOMString> {
4440 self.get_nullable_string_attribute(&local_name!("aria-valuemin"))
4441 }
4442
4443 fn SetAriaValueMin(&self, cx: &mut JSContext, value: Option<DOMString>) {
4444 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemin"), value);
4445 }
4446
4447 fn GetAriaValueNow(&self) -> Option<DOMString> {
4448 self.get_nullable_string_attribute(&local_name!("aria-valuenow"))
4449 }
4450
4451 fn SetAriaValueNow(&self, cx: &mut JSContext, value: Option<DOMString>) {
4452 self.set_nullable_string_attribute(cx, &local_name!("aria-valuenow"), value);
4453 }
4454
4455 fn GetAriaValueText(&self) -> Option<DOMString> {
4456 self.get_nullable_string_attribute(&local_name!("aria-valuetext"))
4457 }
4458
4459 fn SetAriaValueText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4460 self.set_nullable_string_attribute(cx, &local_name!("aria-valuetext"), value);
4461 }
4462
4463 fn GetAssignedSlot(&self) -> Option<DomRoot<HTMLSlotElement>> {
4465 let cx = GlobalScope::get_cx();
4466
4467 rooted!(in(*cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4470 slottable.find_a_slot(true)
4471 }
4472
4473 fn Part(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
4475 self.ensure_rare_data()
4476 .part
4477 .or_init(|| DOMTokenList::new(cx, self, &local_name!("part"), None))
4478 }
4479}
4480
4481impl VirtualMethods for Element {
4482 fn super_type(&self) -> Option<&dyn VirtualMethods> {
4483 Some(self.upcast::<Node>() as &dyn VirtualMethods)
4484 }
4485
4486 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
4487 if attr.local_name() == &local_name!("lang") {
4489 return true;
4490 }
4491
4492 self.super_type()
4493 .unwrap()
4494 .attribute_affects_presentational_hints(attr)
4495 }
4496
4497 fn attribute_mutated(
4498 &self,
4499 cx: &mut JSContext,
4500 attr: AttrRef<'_>,
4501 mutation: AttributeMutation,
4502 ) {
4503 self.super_type()
4504 .unwrap()
4505 .attribute_mutated(cx, attr, mutation);
4506 let node = self.upcast::<Node>();
4507 let doc = node.owner_doc();
4508 match *attr.local_name() {
4509 ref name if name.starts_with("on") && EventTarget::is_content_event_handler(name) => {
4513 let evtarget = self.upcast::<EventTarget>();
4514 let event_name = &name[2..];
4515 match mutation {
4516 AttributeMutation::Set(..) => {
4518 let source = &**attr.value();
4519 let source_line = 1; evtarget.set_event_handler_uncompiled(
4521 self.owner_window().get_url(),
4522 source_line,
4523 event_name,
4524 source,
4525 );
4526 },
4527 AttributeMutation::Removed => {
4529 evtarget
4530 .set_event_handler_common::<EventHandlerNonNull>(cx, event_name, None);
4531 },
4532 }
4533 },
4534 local_name!("style") => self.update_style_attribute(attr, mutation),
4535 local_name!("id") => {
4536 *self.id_attribute.borrow_mut() = mutation.new_value(attr).and_then(|value| {
4538 let value = value.as_atom();
4539 if value != &atom!("") {
4540 Some(value.clone())
4542 } else {
4543 None
4545 }
4546 });
4547
4548 let containing_shadow_root = self.containing_shadow_root();
4549 if node.is_in_a_document_tree() || node.is_in_a_shadow_tree() {
4550 let value = attr.value().as_atom().clone();
4551 match mutation {
4552 AttributeMutation::Set(old_value, _) => {
4553 if let Some(old_value) = old_value {
4554 let old_value = old_value.as_atom();
4555 if let Some(ref shadow_root) = containing_shadow_root {
4556 shadow_root.unregister_element_id(old_value);
4557 } else {
4558 doc.unregister_element_id(cx, old_value);
4559 }
4560 }
4561 if value != atom!("") {
4562 if let Some(ref shadow_root) = containing_shadow_root {
4563 shadow_root.register_element_id(
4564 self,
4565 &value,
4566 CanGc::from_cx(cx),
4567 );
4568 } else {
4569 doc.register_element_id(cx, self, &value);
4570 }
4571 }
4572 },
4573 AttributeMutation::Removed => {
4574 if value != atom!("") {
4575 if let Some(ref shadow_root) = containing_shadow_root {
4576 shadow_root.unregister_element_id(&value);
4577 } else {
4578 doc.unregister_element_id(cx, &value);
4579 }
4580 }
4581 },
4582 }
4583 }
4584 },
4585 local_name!("name") => {
4586 self.ensure_rare_data().name_attribute =
4588 mutation.new_value(attr).and_then(|value| {
4589 let value = value.as_atom();
4590 if value != &atom!("") {
4591 Some(value.clone())
4592 } else {
4593 None
4594 }
4595 });
4596 if node.is_connected() && node.containing_shadow_root().is_none() {
4599 let value = attr.value().as_atom().clone();
4600 match mutation {
4601 AttributeMutation::Set(old_value, _) => {
4602 if let Some(old_value) = old_value {
4603 doc.unregister_element_name(old_value.as_atom());
4604 }
4605 if value != atom!("") {
4606 doc.register_element_name(self, &value);
4607 }
4608 },
4609 AttributeMutation::Removed => {
4610 if value != atom!("") {
4611 doc.unregister_element_name(&value);
4612 }
4613 },
4614 }
4615 }
4616 },
4617 local_name!("slot") => {
4618 rooted!(&in(cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4620
4621 if let Some(assigned_slot) = slottable.assigned_slot() {
4623 assigned_slot.assign_slottables(cx.no_gc());
4624 }
4625 slottable.assign_a_slot(cx.no_gc());
4626 },
4627 _ => {
4628 if attr.namespace() == &ns!() && attr.local_name() == &local_name!("src") {
4631 node.dirty(NodeDamage::Other);
4632 }
4633 },
4634 };
4635
4636 if self
4639 .upcast::<Node>()
4640 .get_flag(NodeFlags::USES_ATTR_IN_CONTENT_ATTRIBUTE)
4641 {
4642 node.dirty(NodeDamage::ContentOrHeritage);
4643 }
4644
4645 node.rev_version();
4649
4650 let window = self.owner_window();
4652 if window.live_devtools_updates() {
4653 let global = window.upcast::<GlobalScope>();
4654 if let Some(sender) = global.devtools_chan() {
4655 let pipeline_id = global.pipeline_id();
4656 if ScriptThread::devtools_want_updates_for_node(pipeline_id, self.upcast()) {
4657 let devtools_message = ScriptToDevtoolsControlMsg::DomMutation(
4658 pipeline_id,
4659 DomMutation::AttributeModified {
4660 node: self.upcast::<Node>().unique_id(pipeline_id),
4661 attribute_name: attr.local_name().to_string(),
4662 new_value: mutation.new_value(attr).map(|value| value.to_string()),
4663 },
4664 );
4665 sender.send(devtools_message).unwrap();
4666 }
4667 }
4668 }
4669 }
4670
4671 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
4672 match *name {
4673 local_name!("id") => AttrValue::Atom(value.into()),
4674 local_name!("name") => AttrValue::Atom(value.into()),
4675 local_name!("class") | local_name!("part") => {
4676 AttrValue::from_serialized_tokenlist(value.into())
4677 },
4678 local_name!("exportparts") => AttrValue::from_shadow_parts(value.into()),
4679 local_name!("tabindex") => AttrValue::from_i32(value.into(), -1),
4680 _ => self
4681 .super_type()
4682 .unwrap()
4683 .parse_plain_attribute(name, value),
4684 }
4685 }
4686
4687 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
4688 if let Some(s) = self.super_type() {
4689 s.bind_to_tree(cx, context);
4690 }
4691
4692 if let Some(f) = self.as_maybe_form_control() {
4693 f.bind_form_control_to_tree(cx);
4694 }
4695
4696 if let Some(ref shadow_root) = self.shadow_root() {
4697 shadow_root.bind_to_tree(cx, context);
4698 }
4699
4700 if !context.is_in_tree() {
4701 return;
4702 }
4703
4704 self.register_current_id_and_name_attribute(cx);
4705 }
4706
4707 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
4708 self.super_type().unwrap().unbind_from_tree(cx, context);
4709
4710 if let Some(f) = self.as_maybe_form_control() {
4711 f.unbind_form_control_from_tree(cx);
4715 }
4716
4717 if !context.tree_is_in_a_document_tree && !context.tree_is_in_a_shadow_tree {
4718 return;
4719 }
4720
4721 let doc = self.owner_document();
4722
4723 let fullscreen = doc.fullscreen_element();
4724 if fullscreen.as_deref() == Some(self) {
4725 doc.exit_fullscreen(CanGc::from_cx(cx));
4726 }
4727
4728 self.unregister_current_id_and_name_attribute(cx);
4729 }
4730
4731 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
4732 if let Some(s) = self.super_type() {
4733 s.children_changed(cx, mutation);
4734 }
4735
4736 let flags = self.get_selector_flags();
4737 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
4738 self.upcast::<Node>().dirty(NodeDamage::Other);
4740 } else {
4741 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) &&
4742 let Some(next_child) = mutation.next_child()
4743 {
4744 for child in next_child.inclusively_following_siblings_unrooted(cx.no_gc()) {
4745 if child.is::<Element>() {
4746 child.dirty(NodeDamage::Other);
4747 }
4748 }
4749 }
4750 if flags.intersects(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) &&
4751 let Some(child) = mutation.modified_edge_element(cx.no_gc())
4752 {
4753 child.dirty(NodeDamage::Other);
4754 }
4755 }
4756 }
4757
4758 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
4759 self.super_type().unwrap().adopting_steps(cx, old_doc);
4760
4761 if self.owner_document().is_html_document() != old_doc.is_html_document() {
4762 self.tag_name.clear();
4763 }
4764 }
4765
4766 fn post_connection_steps(&self, cx: &mut JSContext) {
4767 if let Some(s) = self.super_type() {
4768 s.post_connection_steps(cx);
4769 }
4770
4771 self.update_nonce_post_connection(cx);
4772 }
4773
4774 fn cloning_steps(
4776 &self,
4777 cx: &mut JSContext,
4778 copy: &Node,
4779 maybe_doc: Option<&Document>,
4780 clone_children: CloneChildrenFlag,
4781 ) {
4782 if let Some(s) = self.super_type() {
4783 s.cloning_steps(cx, copy, maybe_doc, clone_children);
4784 }
4785 let elem = copy.downcast::<Element>().unwrap();
4786 if let Some(rare_data) = self.rare_data().as_ref() {
4787 elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone());
4788 }
4789 }
4790}
4791impl Element {
4792 pub(crate) fn client_rect(&self) -> Rect<i32, CSSPixel> {
4793 let doc = self.node.owner_doc();
4794
4795 if let Some(rect) = self
4796 .rare_data()
4797 .as_ref()
4798 .and_then(|data| data.client_rect.as_ref())
4799 .and_then(|rect| rect.get().ok()) &&
4800 doc.restyle_reason().is_empty()
4801 {
4802 return rect;
4803 }
4804
4805 let mut rect = self.upcast::<Node>().client_rect();
4806 let in_quirks_mode = doc.quirks_mode() == QuirksMode::Quirks;
4807
4808 if (in_quirks_mode && doc.GetBody().as_deref() == self.downcast::<HTMLElement>()) ||
4809 (!in_quirks_mode && self.is_document_element())
4810 {
4811 rect.size = doc.window().viewport_details().size.round().to_i32();
4812 }
4813
4814 self.ensure_rare_data().client_rect = Some(self.owner_window().cache_layout_value(rect));
4815 rect
4816 }
4817
4818 pub(crate) fn as_maybe_activatable(&self) -> Option<&dyn Activatable> {
4819 let element = match self.upcast::<Node>().type_id() {
4820 NodeTypeId::Element(ElementTypeId::HTMLElement(
4821 HTMLElementTypeId::HTMLInputElement,
4822 )) => {
4823 let element = self.downcast::<HTMLInputElement>().unwrap();
4824 Some(element as &dyn Activatable)
4825 },
4826 NodeTypeId::Element(ElementTypeId::HTMLElement(
4827 HTMLElementTypeId::HTMLButtonElement,
4828 )) => {
4829 let element = self.downcast::<HTMLButtonElement>().unwrap();
4830 Some(element as &dyn Activatable)
4831 },
4832 NodeTypeId::Element(ElementTypeId::HTMLElement(
4833 HTMLElementTypeId::HTMLAnchorElement,
4834 )) => {
4835 let element = self.downcast::<HTMLAnchorElement>().unwrap();
4836 Some(element as &dyn Activatable)
4837 },
4838 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) => {
4839 let element = self.downcast::<HTMLAreaElement>().unwrap();
4840 Some(element as &dyn Activatable)
4841 },
4842 NodeTypeId::Element(ElementTypeId::HTMLElement(
4843 HTMLElementTypeId::HTMLLabelElement,
4844 )) => {
4845 let element = self.downcast::<HTMLLabelElement>().unwrap();
4846 Some(element as &dyn Activatable)
4847 },
4848 NodeTypeId::Element(ElementTypeId::HTMLElement(
4849 HTMLElementTypeId::HTMLSelectElement,
4850 )) => {
4851 let element = self.downcast::<HTMLSelectElement>().unwrap();
4852 Some(element as &dyn Activatable)
4853 },
4854 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
4855 let element = self.downcast::<HTMLElement>().unwrap();
4856 Some(element as &dyn Activatable)
4857 },
4858 _ => None,
4859 };
4860 element.and_then(|elem| {
4861 if elem.is_instance_activatable() {
4862 Some(elem)
4863 } else {
4864 None
4865 }
4866 })
4867 }
4868
4869 pub(crate) fn as_stylesheet_owner(&self) -> Option<&dyn StylesheetOwner> {
4870 if let Some(s) = self.downcast::<HTMLStyleElement>() {
4871 return Some(s as &dyn StylesheetOwner);
4872 }
4873
4874 if let Some(l) = self.downcast::<HTMLLinkElement>() {
4875 return Some(l as &dyn StylesheetOwner);
4876 }
4877
4878 None
4879 }
4880
4881 pub(crate) fn as_maybe_validatable(&self) -> Option<&dyn Validatable> {
4883 match self.upcast::<Node>().type_id() {
4884 NodeTypeId::Element(ElementTypeId::HTMLElement(
4885 HTMLElementTypeId::HTMLInputElement,
4886 )) => {
4887 let element = self.downcast::<HTMLInputElement>().unwrap();
4888 Some(element as &dyn Validatable)
4889 },
4890 NodeTypeId::Element(ElementTypeId::HTMLElement(
4891 HTMLElementTypeId::HTMLButtonElement,
4892 )) => {
4893 let element = self.downcast::<HTMLButtonElement>().unwrap();
4894 Some(element as &dyn Validatable)
4895 },
4896 NodeTypeId::Element(ElementTypeId::HTMLElement(
4897 HTMLElementTypeId::HTMLObjectElement,
4898 )) => {
4899 let element = self.downcast::<HTMLObjectElement>().unwrap();
4900 Some(element as &dyn Validatable)
4901 },
4902 NodeTypeId::Element(ElementTypeId::HTMLElement(
4903 HTMLElementTypeId::HTMLSelectElement,
4904 )) => {
4905 let element = self.downcast::<HTMLSelectElement>().unwrap();
4906 Some(element as &dyn Validatable)
4907 },
4908 NodeTypeId::Element(ElementTypeId::HTMLElement(
4909 HTMLElementTypeId::HTMLTextAreaElement,
4910 )) => {
4911 let element = self.downcast::<HTMLTextAreaElement>().unwrap();
4912 Some(element as &dyn Validatable)
4913 },
4914 NodeTypeId::Element(ElementTypeId::HTMLElement(
4915 HTMLElementTypeId::HTMLFieldSetElement,
4916 )) => {
4917 let element = self.downcast::<HTMLFieldSetElement>().unwrap();
4918 Some(element as &dyn Validatable)
4919 },
4920 NodeTypeId::Element(ElementTypeId::HTMLElement(
4921 HTMLElementTypeId::HTMLOutputElement,
4922 )) => {
4923 let element = self.downcast::<HTMLOutputElement>().unwrap();
4924 Some(element as &dyn Validatable)
4925 },
4926 _ => None,
4927 }
4928 }
4929
4930 pub(crate) fn is_invalid(&self, cx: &mut JSContext, needs_update: bool) -> bool {
4931 if let Some(validatable) = self.as_maybe_validatable() {
4932 if needs_update {
4933 validatable
4934 .validity_state(cx)
4935 .perform_validation_and_update(cx, ValidationFlags::all());
4936 }
4937 return validatable.is_instance_validatable() && !validatable.satisfies_constraints(cx);
4938 }
4939
4940 if let Some(internals) = self.get_element_internals() {
4941 return internals.is_invalid(cx);
4942 }
4943 false
4944 }
4945
4946 pub(crate) fn is_instance_validatable(&self) -> bool {
4947 if let Some(validatable) = self.as_maybe_validatable() {
4948 return validatable.is_instance_validatable();
4949 }
4950 if let Some(internals) = self.get_element_internals() {
4951 return internals.is_instance_validatable();
4952 }
4953 false
4954 }
4955
4956 pub(crate) fn init_state_for_internals(&self) {
4957 self.set_enabled_state(true);
4958 self.set_state(ElementState::VALID, true);
4959 self.set_state(ElementState::INVALID, false);
4960 }
4961
4962 pub(crate) fn click_in_progress(&self) -> bool {
4963 self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
4964 }
4965
4966 pub(crate) fn set_click_in_progress(&self, click: bool) {
4967 self.upcast::<Node>()
4968 .set_flag(NodeFlags::CLICK_IN_PROGRESS, click)
4969 }
4970
4971 pub fn state(&self) -> ElementState {
4972 self.state.get()
4973 }
4974
4975 pub(crate) fn set_state(&self, which: ElementState, value: bool) {
4976 let mut state = self.state.get();
4977 let previous_state = state;
4978 if value {
4979 state.insert(which);
4980 } else {
4981 state.remove(which);
4982 }
4983
4984 if previous_state == state {
4985 return;
4987 }
4988
4989 {
4992 let document = self.owner_document();
4993 let mut entry = document.ensure_pending_restyle(self);
4994 if entry.snapshot.is_none() {
4995 entry.snapshot = Some(Snapshot::new());
4996 }
4997 let snapshot = entry.snapshot.as_mut().unwrap();
4998 if snapshot.state.is_none() {
4999 snapshot.state = Some(self.state());
5000 }
5001 }
5002
5003 self.state.set(state);
5004 }
5005
5006 pub(crate) fn set_active_state(&self, value: bool) {
5008 self.set_state(ElementState::ACTIVE, value);
5009
5010 if let Some(parent) = self.upcast::<Node>().GetParentElement() {
5011 parent.set_active_state(value);
5012 }
5013 }
5014
5015 pub(crate) fn focus_state(&self) -> bool {
5016 self.state.get().contains(ElementState::FOCUS)
5017 }
5018
5019 pub(crate) fn set_focus_state(&self, value: bool) {
5020 self.set_state(ElementState::FOCUS, value);
5021 }
5022
5023 pub(crate) fn set_hover_state(&self, value: bool) {
5024 self.set_state(ElementState::HOVER, value);
5025 }
5026
5027 pub(crate) fn enabled_state(&self) -> bool {
5028 self.state.get().contains(ElementState::ENABLED)
5029 }
5030
5031 pub(crate) fn set_enabled_state(&self, value: bool) {
5032 self.set_state(ElementState::ENABLED, value)
5033 }
5034
5035 pub(crate) fn disabled_state(&self) -> bool {
5036 self.state.get().contains(ElementState::DISABLED)
5037 }
5038
5039 pub(crate) fn set_disabled_state(&self, value: bool) {
5040 self.set_state(ElementState::DISABLED, value)
5041 }
5042
5043 pub(crate) fn read_write_state(&self) -> bool {
5044 self.state.get().contains(ElementState::READWRITE)
5045 }
5046
5047 pub(crate) fn set_read_write_state(&self, value: bool) {
5048 self.set_state(ElementState::READWRITE, value)
5049 }
5050
5051 pub(crate) fn set_open_state(&self, value: bool) {
5052 self.set_state(ElementState::OPEN, value);
5053 }
5054
5055 pub(crate) fn set_placeholder_shown_state(&self, value: bool) {
5056 self.set_state(ElementState::PLACEHOLDER_SHOWN, value);
5057 }
5058
5059 pub(crate) fn set_modal_state(&self, value: bool) {
5060 self.set_state(ElementState::MODAL, value);
5061 }
5062
5063 pub(crate) fn set_target_state(&self, value: bool) {
5064 self.set_state(ElementState::URLTARGET, value)
5065 }
5066
5067 pub(crate) fn set_fullscreen_state(&self, value: bool) {
5068 self.set_state(ElementState::FULLSCREEN, value)
5069 }
5070
5071 pub(crate) fn is_connected(&self) -> bool {
5073 self.upcast::<Node>().is_connected()
5074 }
5075
5076 pub(crate) fn cannot_navigate(&self) -> bool {
5078 let document = self.owner_document();
5079
5080 !document.is_fully_active() ||
5082 (
5083 !self.is::<HTMLAnchorElement>() && !self.is_connected()
5085 )
5086 }
5087}
5088
5089impl Element {
5090 pub(crate) fn check_ancestors_disabled_state_for_form_control(&self) {
5091 let node = self.upcast::<Node>();
5092 if self.disabled_state() {
5093 return;
5094 }
5095 for ancestor in node.ancestors() {
5096 if !ancestor.is::<HTMLFieldSetElement>() {
5097 continue;
5098 }
5099 if !ancestor.downcast::<Element>().unwrap().disabled_state() {
5100 continue;
5101 }
5102 if ancestor.is_parent_of(node) {
5103 self.set_disabled_state(true);
5104 self.set_enabled_state(false);
5105 return;
5106 }
5107 if let Some(ref legend) = ancestor.children().find(|n| n.is::<HTMLLegendElement>()) {
5108 if node.ancestors().any(|ancestor| ancestor == *legend) {
5110 continue;
5111 }
5112 }
5113 self.set_disabled_state(true);
5114 self.set_enabled_state(false);
5115 return;
5116 }
5117 }
5118
5119 pub(crate) fn check_parent_disabled_state_for_option(&self) {
5120 if self.disabled_state() {
5121 return;
5122 }
5123 let node = self.upcast::<Node>();
5124 if let Some(ref parent) = node.GetParentNode() &&
5125 parent.is::<HTMLOptGroupElement>() &&
5126 parent.downcast::<Element>().unwrap().disabled_state()
5127 {
5128 self.set_disabled_state(true);
5129 self.set_enabled_state(false);
5130 }
5131 }
5132
5133 pub(crate) fn check_disabled_attribute(&self) {
5134 let has_disabled_attrib = self.has_attribute(&local_name!("disabled"));
5135 self.set_disabled_state(has_disabled_attrib);
5136 self.set_enabled_state(!has_disabled_attrib);
5137 }
5138
5139 pub(crate) fn update_read_write_state_from_readonly_attribute(&self) {
5140 let has_readonly_attribute = self.has_attribute(&local_name!("readonly"));
5141 self.set_read_write_state(has_readonly_attribute);
5142 }
5143}
5144
5145#[derive(Clone, Copy, PartialEq)]
5146pub(crate) enum AttributeMutationReason {
5147 ByCloning,
5148 ByParser,
5149 Directly,
5150}
5151
5152#[derive(Clone, Copy)]
5153pub(crate) enum AttributeMutation<'a> {
5154 Set(Option<&'a AttrValue>, AttributeMutationReason),
5157
5158 Removed,
5161}
5162
5163impl AttributeMutation<'_> {
5164 pub(crate) fn is_removal(&self) -> bool {
5165 match *self {
5166 AttributeMutation::Removed => true,
5167 AttributeMutation::Set(..) => false,
5168 }
5169 }
5170
5171 pub(crate) fn new_value<'b>(&self, attr: AttrRef<'b>) -> Option<AttrValueRef<'b>> {
5172 match *self {
5173 AttributeMutation::Set(..) => Some(attr.value()),
5174 AttributeMutation::Removed => None,
5175 }
5176 }
5177}
5178
5179#[derive(JSTraceable, MallocSizeOf)]
5183struct TagName {
5184 #[no_trace]
5185 ptr: DomRefCell<Option<LocalName>>,
5186}
5187
5188impl TagName {
5189 fn new() -> TagName {
5190 TagName {
5191 ptr: DomRefCell::new(None),
5192 }
5193 }
5194
5195 fn or_init<F>(&self, cb: F) -> LocalName
5198 where
5199 F: FnOnce() -> LocalName,
5200 {
5201 match &mut *self.ptr.borrow_mut() {
5202 &mut Some(ref name) => name.clone(),
5203 ptr => {
5204 let name = cb();
5205 *ptr = Some(name.clone());
5206 name
5207 },
5208 }
5209 }
5210
5211 fn clear(&self) {
5214 *self.ptr.borrow_mut() = None;
5215 }
5216}
5217
5218pub(crate) fn reflect_cross_origin_attribute(element: &Element) -> Option<DOMString> {
5220 element
5221 .get_attribute_string_value(&local_name!("crossorigin"))
5222 .map(|value| {
5223 let value = value.to_ascii_lowercase();
5224 if value == "anonymous" || value == "use-credentials" {
5225 DOMString::from(value)
5226 } else {
5227 DOMString::from("anonymous")
5228 }
5229 })
5230}
5231
5232pub(crate) fn set_cross_origin_attribute(
5233 cx: &mut JSContext,
5234 element: &Element,
5235 value: Option<DOMString>,
5236) {
5237 match value {
5238 Some(val) => element.set_string_attribute(cx, &local_name!("crossorigin"), val),
5239 None => {
5240 element.remove_attribute(cx, &ns!(), &local_name!("crossorigin"));
5241 },
5242 }
5243}
5244
5245pub(crate) fn reflect_referrer_policy_attribute(element: &Element) -> DOMString {
5247 element
5248 .get_attribute_string_value(&local_name!("referrerpolicy"))
5249 .map(|value| {
5250 let value = value.to_ascii_lowercase();
5251 if value == "no-referrer" ||
5252 value == "no-referrer-when-downgrade" ||
5253 value == "same-origin" ||
5254 value == "origin" ||
5255 value == "strict-origin" ||
5256 value == "origin-when-cross-origin" ||
5257 value == "strict-origin-when-cross-origin" ||
5258 value == "unsafe-url"
5259 {
5260 DOMString::from(value)
5261 } else {
5262 DOMString::new()
5263 }
5264 })
5265 .unwrap_or_default()
5266}
5267
5268pub(crate) fn referrer_policy_for_element(element: &Element) -> ReferrerPolicy {
5269 element
5270 .get_attribute_string_value(&local_name!("referrerpolicy"))
5271 .map(|value| ReferrerPolicy::from(value.as_ref()))
5272 .unwrap_or(element.owner_document().get_referrer_policy())
5273}
5274
5275pub(crate) fn cors_setting_for_element(element: &Element) -> Option<CorsSettings> {
5276 element
5277 .get_attribute_string_value(&local_name!("crossorigin"))
5278 .map(|value| CorsSettings::from_enumerated_attribute(value.as_ref()))
5279}
5280
5281pub(crate) fn cors_settings_attribute_credential_mode(element: &Element) -> CredentialsMode {
5283 element
5284 .get_attribute_string_value(&local_name!("crossorigin"))
5285 .map(|value| {
5286 if value.eq_ignore_ascii_case("use-credentials") {
5287 CredentialsMode::Include
5288 } else {
5289 CredentialsMode::CredentialsSameOrigin
5291 }
5292 })
5293 .unwrap_or(CredentialsMode::CredentialsSameOrigin)
5295}
5296
5297pub(crate) fn is_element_affected_by_legacy_background_presentational_hint(
5298 namespace: &Namespace,
5299 local_name: &LocalName,
5300) -> bool {
5301 *namespace == ns!(html) &&
5302 matches!(
5303 *local_name,
5304 local_name!("body") |
5305 local_name!("table") |
5306 local_name!("thead") |
5307 local_name!("tbody") |
5308 local_name!("tfoot") |
5309 local_name!("tr") |
5310 local_name!("td") |
5311 local_name!("th")
5312 )
5313}