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