1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
8
9use std::borrow::Cow;
10use std::cell::{Cell, LazyCell};
11use std::default::Default;
12use std::rc::Rc;
13use std::str::FromStr;
14use std::sync::atomic::{AtomicUsize, Ordering};
15use std::{fmt, mem};
16
17use app_units::Au;
18use cssparser::match_ignore_ascii_case;
19use dom_struct::dom_struct;
20use euclid::Rect;
21use html5ever::serialize::TraversalScope;
22use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
23use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns};
24use js::context::{JSContext, NoGC};
25use js::jsapi::{Heap, JSObject};
26use js::jsval::JSVal;
27use js::realm::CurrentRealm;
28use js::rust::HandleObject;
29use layout_api::{
30 ACCESSKIT_ROLE_TO_ARIA_ROLE, AccessibilityDamage, LayoutDamage, QueryMsg,
31 ScrollContainerQueryFlags, StyleData, with_layout_state,
32};
33use net_traits::ReferrerPolicy;
34use net_traits::request::{CorsSettings, CredentialsMode};
35use script_bindings::callback::RootedCallback;
36use script_bindings::cell::{DomRefCell, Ref, RefMut};
37use script_bindings::codegen::GenericBindings::AnimationBinding::AnimationMethods;
38use script_bindings::codegen::GenericBindings::KeyframeEffectBinding::KeyframeEffectMethods;
39use script_bindings::dom::UnrootedDom;
40use script_bindings::reflector::DomObject;
41use selectors::attr::CaseSensitivity;
42use selectors::matching::ElementSelectorFlags;
43use selectors::sink::Push;
44use servo_arc::Arc as ServoArc;
45use style::applicable_declarations::ApplicableDeclarationBlock;
46use style::attr::{AttrValue, LengthOrPercentageOrAuto};
47use style::context::QuirksMode;
48use style::invalidation::element::restyle_hints::RestyleHint;
49use style::properties::longhands::{
50 self, background_image, border_spacing, color, font_family, font_size,
51};
52use style::properties::{
53 ComputedValues, Importance, PropertyDeclaration, PropertyDeclarationBlock,
54 parse_style_attribute,
55};
56use style::rule_tree::{CascadeLevel, CascadeOrigin};
57use style::selector_parser::{RestyleDamage, SelectorParser, Snapshot};
58use style::shared_lock::Locked;
59use style::str::string_as_ascii_lowercase;
60use style::stylesheets::layer_rule::LayerOrder;
61use style::stylesheets::{CssRuleType, UrlExtraData};
62use style::values::computed::{Overflow, UserSelect};
63use style::values::generics::NonNegative;
64use style::values::generics::position::PreferredRatio;
65use style::values::generics::ratio::Ratio;
66use style::values::{AtomIdent, AtomString, CSSFloat, computed, specified};
67use style::{ArcSlice, CaseSensitivityExt, dom_apis, thread_state};
68use style_traits::CSSPixel;
69use stylo_atoms::Atom;
70use stylo_dom::ElementState;
71use xml5ever::serialize::TraversalScope::{
72 ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode,
73};
74
75use crate::conversions::Convert;
76use crate::css::stylesheet_loader::StylesheetOwner;
77use crate::dom::RootedPromise;
78use crate::dom::accessors::AttrStrRef;
79use crate::dom::activation::Activatable;
80use crate::dom::animation::Animation;
81use crate::dom::animations::keyframeeffect::KeyframeEffect;
82use crate::dom::attr::{Attr, is_relevant_attribute};
83use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
84use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
85use crate::dom::bindings::codegen::Bindings::ElementBinding::{
86 ElementMethods, GetHTMLOptions, ScrollIntoViewContainer, ScrollLogicalPosition, ShadowRootInit,
87};
88use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
89use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
90use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
91use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
92use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
93 SetHTMLOptions, SetHTMLUnsafeOptions,
94};
95use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
96 ShadowRootMethods, ShadowRootMode, SlotAssignmentMode,
97};
98use crate::dom::bindings::codegen::Bindings::WindowBinding::{
99 ScrollBehavior, ScrollToOptions, WindowMethods,
100};
101use crate::dom::bindings::codegen::UnionTypes::{
102 BooleanOrScrollIntoViewOptions, NodeOrString, TrustedHTMLOrNullIsEmptyString,
103 TrustedHTMLOrString,
104 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString,
105 UnrestrictedDoubleOrKeyframeAnimationOptions, UnrestrictedDoubleOrKeyframeEffectOptions,
106};
107use crate::dom::bindings::conversions::DerivedFrom;
108use crate::dom::bindings::domname::{
109 self, is_valid_attribute_local_name, namespace_from_domstring,
110};
111use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
112use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
113use crate::dom::bindings::num::Finite;
114use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout};
115use crate::dom::bindings::str::DOMString;
116use crate::dom::csp::{CspReporting, InlineCheckType, SourcePosition};
117use crate::dom::customelementregistry::{
118 CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementRegistry,
119 CustomElementState, is_valid_custom_element_name,
120};
121use crate::dom::document::Document;
122use crate::dom::documentfragment::DocumentFragment;
123use crate::dom::domrect::DOMRect;
124use crate::dom::domrectlist::DOMRectList;
125use crate::dom::domtokenlist::DOMTokenList;
126use crate::dom::element::attributes::storage::{
127 AttrName, AttrRef, AttrValueRef, AttributeEntry, AttributeStorage, ContentAttributeData,
128};
129use crate::dom::element::create::create_element;
130use crate::dom::eventtarget::EventTarget;
131use crate::dom::globalscope::GlobalScope;
132use crate::dom::html::form_controls::htmlinputelement::HTMLInputElement;
133use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
134use crate::dom::html::htmlareaelement::HTMLAreaElement;
135use crate::dom::html::htmlbodyelement::HTMLBodyElement;
136use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
137use crate::dom::html::htmlcollection::HTMLCollection;
138use crate::dom::html::htmlelement::HTMLElement;
139use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
140use crate::dom::html::htmlfontelement::HTMLFontElement;
141use crate::dom::html::htmlformelement::FormControlElementHelpers;
142use crate::dom::html::htmlhrelement::{HTMLHRElement, SizePresentationalHint};
143use crate::dom::html::htmliframeelement::HTMLIFrameElement;
144use crate::dom::html::htmlimageelement::HTMLImageElement;
145use crate::dom::html::htmllabelelement::HTMLLabelElement;
146use crate::dom::html::htmllegendelement::HTMLLegendElement;
147use crate::dom::html::htmllinkelement::HTMLLinkElement;
148use crate::dom::html::htmlmarqueeelement::HTMLMarqueeElement;
149use crate::dom::html::htmlobjectelement::HTMLObjectElement;
150use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
151use crate::dom::html::htmloutputelement::HTMLOutputElement;
152use crate::dom::html::htmlscriptelement::HTMLScriptElement;
153use crate::dom::html::htmlselectelement::HTMLSelectElement;
154use crate::dom::html::htmlslotelement::{HTMLSlotElement, Slottable};
155use crate::dom::html::htmlstyleelement::HTMLStyleElement;
156use crate::dom::html::htmltablecellelement::HTMLTableCellElement;
157use crate::dom::html::htmltablecolelement::HTMLTableColElement;
158use crate::dom::html::htmltableelement::HTMLTableElement;
159use crate::dom::html::htmltablerowelement::HTMLTableRowElement;
160use crate::dom::html::htmltablesectionelement::HTMLTableSectionElement;
161use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
162use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
163use crate::dom::html::htmlvideoelement::HTMLVideoElement;
164use crate::dom::html::internals::elementinternals::ElementInternals;
165use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserverRegistration};
166use crate::dom::iterators::ShadowIncluding;
167use crate::dom::mutationobserver::{Mutation, MutationObserver};
168use crate::dom::namednodemap::NamedNodeMap;
169use crate::dom::node::virtualmethods::{VirtualMethods, vtable_for};
170use crate::dom::node::{
171 BindContext, ChildrenMutation, CloneChildrenFlag, IsShadowTree, Node, NodeDamage, NodeFlags,
172 NodeTraits, UnbindContext,
173};
174use crate::dom::nodelist::NodeList;
175use crate::dom::range::Range;
176use crate::dom::raredata::ElementRareData;
177use crate::dom::sanitizer::Sanitizer;
178use crate::dom::servoparser::ServoParser;
179use crate::dom::shadowroot::shadowroot::{IsUserAgentWidget, ShadowRoot};
180use crate::dom::svg::svgelement::SVGElement;
181use crate::dom::text::Text;
182use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
183use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
184use crate::dom::validation::Validatable;
185use crate::dom::validitystate::ValidationFlags;
186use crate::dom::window::Window;
187use crate::dom::window::scrolling_box::{ScrollAxisState, ScrollingBox};
188use crate::event_loop::script_thread::ScriptThread;
189use crate::layout_dom::ServoDangerousStyleElement;
190use crate::realms::enter_auto_realm;
191
192#[dom_struct]
198pub struct Element {
199 node: Node,
200 #[no_trace]
201 local_name: LocalName,
202 tag_name: TagName,
203 #[no_trace]
204 namespace: Namespace,
205 #[no_trace]
206 prefix: DomRefCell<Option<Prefix>>,
207 attrs: AttributeStorage,
208 #[no_trace]
209 id_attribute: DomRefCell<Option<Atom>>,
210 #[no_trace]
212 is: DomRefCell<Option<LocalName>>,
213 #[conditional_malloc_size_of]
214 #[no_trace]
215 style_attribute: DomRefCell<Option<ServoArc<Locked<PropertyDeclarationBlock>>>>,
216 attr_list: MutNullableDom<NamedNodeMap>,
217 class_list: MutNullableDom<DOMTokenList>,
218 #[no_trace]
219 state: Cell<ElementState>,
220 selector_flags: AtomicUsize,
223 rare_data: DomRefCell<Option<Box<ElementRareData>>>,
224
225 #[no_trace]
228 style_data: DomRefCell<Option<Box<StyleData>>>,
229}
230
231impl fmt::Debug for Element {
232 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
233 write!(f, "<{}", self.local_name)?;
234 if let Some(ref id) = *self.id_attribute.borrow() {
235 write!(f, " id={}", id)?;
236 }
237 write!(f, ">")
238 }
239}
240
241#[derive(MallocSizeOf, PartialEq)]
242pub(crate) enum ElementCreator {
243 ParserCreated(u64),
244 ScriptCreated,
245}
246
247pub(crate) enum CustomElementCreationMode {
248 Synchronous,
249 Asynchronous,
250}
251
252impl ElementCreator {
253 pub(crate) fn is_parser_created(&self) -> bool {
254 match *self {
255 ElementCreator::ParserCreated(_) => true,
256 ElementCreator::ScriptCreated => false,
257 }
258 }
259 pub(crate) fn return_line_number(&self) -> u64 {
260 match *self {
261 ElementCreator::ParserCreated(l) => l,
262 ElementCreator::ScriptCreated => 1,
263 }
264 }
265}
266
267pub(crate) enum AdjacentPosition {
268 BeforeBegin,
269 AfterEnd,
270 AfterBegin,
271 BeforeEnd,
272}
273
274impl FromStr for AdjacentPosition {
275 type Err = Error;
276
277 fn from_str(position: &str) -> Result<Self, Self::Err> {
278 match_ignore_ascii_case! { position,
279 "beforebegin" => Ok(AdjacentPosition::BeforeBegin),
280 "afterbegin" => Ok(AdjacentPosition::AfterBegin),
281 "beforeend" => Ok(AdjacentPosition::BeforeEnd),
282 "afterend" => Ok(AdjacentPosition::AfterEnd),
283 _ => Err(Error::Syntax(None))
284 }
285 }
286}
287
288impl Element {
292 pub(crate) fn create(
293 cx: &mut JSContext,
294 name: QualName,
295 is: Option<LocalName>,
296 document: &Document,
297 creator: ElementCreator,
298 mode: CustomElementCreationMode,
299 proto: Option<HandleObject>,
300 ) -> DomRoot<Element> {
301 create_element(cx, name, is, document, creator, mode, proto)
302 }
303
304 pub(crate) fn new_inherited(
305 local_name: LocalName,
306 namespace: Namespace,
307 prefix: Option<Prefix>,
308 document: &Document,
309 ) -> Element {
310 Element::new_inherited_with_state(
311 ElementState::empty(),
312 local_name,
313 namespace,
314 prefix,
315 document,
316 )
317 }
318
319 pub(crate) fn new_inherited_with_state(
320 state: ElementState,
321 local_name: LocalName,
322 namespace: Namespace,
323 prefix: Option<Prefix>,
324 document: &Document,
325 ) -> Element {
326 Element {
327 node: Node::new_inherited(document),
328 local_name,
329 tag_name: TagName::new(),
330 namespace,
331 prefix: DomRefCell::new(prefix),
332 attrs: Default::default(),
333 id_attribute: DomRefCell::new(None),
334 is: DomRefCell::new(None),
335 style_attribute: DomRefCell::new(None),
336 attr_list: Default::default(),
337 class_list: Default::default(),
338 state: Cell::new(state),
339 selector_flags: Default::default(),
340 rare_data: Default::default(),
341 style_data: Default::default(),
342 }
343 }
344
345 pub(crate) fn set_had_duplicate_attributes(&self, no_gc: &NoGC) {
346 self.ensure_rare_data(no_gc).had_duplicate_attributes = true;
347 }
348
349 pub(crate) fn new(
350 cx: &mut JSContext,
351 local_name: LocalName,
352 namespace: Namespace,
353 prefix: Option<Prefix>,
354 document: &Document,
355 proto: Option<HandleObject>,
356 ) -> DomRoot<Element> {
357 Node::reflect_node_with_proto(
358 cx,
359 Box::new(Element::new_inherited(
360 local_name, namespace, prefix, document,
361 )),
362 document,
363 proto,
364 )
365 }
366
367 fn rare_data(&self) -> Ref<'_, Option<Box<ElementRareData>>> {
368 self.rare_data.borrow()
369 }
370
371 fn rare_data_mut(&self) -> RefMut<'_, Option<Box<ElementRareData>>> {
372 self.rare_data.borrow_mut()
373 }
374
375 pub(crate) fn ensure_rare_data<'a: 'b, 'b>(
376 &'a self,
377 no_gc: &'b NoGC,
378 ) -> RefMut<'b, Box<ElementRareData>> {
379 let mut rare_data = self.rare_data.safe_borrow_mut(no_gc);
380 if rare_data.is_none() {
381 *rare_data = Some(Default::default());
382 }
383 RefMut::map(rare_data, |rare_data| rare_data.as_mut().unwrap())
384 }
385
386 pub(crate) fn clean_up_style_data(&self) {
387 self.style_data.borrow_mut().take();
388 }
389
390 pub(crate) fn restyle(&self, no_gc: &NoGC, damage: NodeDamage) {
391 let doc = self.node.owner_doc();
392 let mut restyle = doc.ensure_pending_restyle(self);
393
394 restyle.hint.insert(RestyleHint::RESTYLE_SELF);
397
398 match damage {
399 NodeDamage::Style => {},
400 NodeDamage::ContentOrHeritage => {
401 doc.note_dirty_element(no_gc, self);
402 restyle
403 .damage
404 .insert(RestyleDamage::from(LayoutDamage::DescendantHasBoxDamage));
405 },
406 NodeDamage::Other => {
407 doc.note_dirty_element(no_gc, self);
408 restyle.damage.insert(RestyleDamage::reconstruct());
409 },
410 }
411 }
412
413 pub(crate) fn has_dirty_descendants(&self) -> bool {
414 self.upcast::<Node>()
415 .get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)
416 }
417
418 pub(crate) fn note_dirty_descendants(&self, no_gc: &NoGC) {
419 self.upcast::<Node>()
420 .owner_doc_unrooted(no_gc)
421 .note_dirty_element(no_gc, self);
422 }
423
424 pub(crate) fn set_is(&self, is: LocalName) {
425 *self.is.borrow_mut() = Some(is);
426 }
427
428 pub(crate) fn get_is(&self) -> Option<LocalName> {
430 self.is.borrow().clone()
431 }
432
433 pub(crate) fn set_initial_custom_element_state_to_uncustomized(&self) {
442 let mut state = self.state.get();
443 state.insert(ElementState::DEFINED);
444 self.state.set(state);
445 }
446
447 pub(crate) fn set_custom_element_state(&self, state: CustomElementState, no_gc: &NoGC) {
449 if state != CustomElementState::Uncustomized {
451 self.ensure_rare_data(no_gc).custom_element_state = state;
452 }
453
454 let in_defined_state = matches!(
455 state,
456 CustomElementState::Uncustomized | CustomElementState::Custom
457 );
458 self.set_state(ElementState::DEFINED, in_defined_state)
459 }
460
461 pub(crate) fn get_custom_element_state(&self) -> CustomElementState {
462 if let Some(rare_data) = self.rare_data().as_ref() {
463 return rare_data.custom_element_state;
464 }
465 CustomElementState::Uncustomized
466 }
467
468 pub(crate) fn is_custom(&self) -> bool {
470 self.get_custom_element_state() == CustomElementState::Custom
471 }
472
473 pub(crate) fn set_custom_element_definition(
474 &self,
475 definition: Rc<CustomElementDefinition>,
476 no_gc: &NoGC,
477 ) {
478 self.ensure_rare_data(no_gc).custom_element_definition = Some(definition);
479 }
480
481 pub(crate) fn get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>> {
482 self.rare_data().as_ref()?.custom_element_definition.clone()
483 }
484
485 pub(crate) fn clear_custom_element_definition(&self, no_gc: &NoGC) {
486 self.ensure_rare_data(no_gc).custom_element_definition = None;
487 }
488
489 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
490 pub(crate) fn push_callback_reaction(
491 &self,
492 function: RootedCallback<Function>,
493 args: Box<[Heap<JSVal>]>,
494 no_gc: &NoGC,
495 ) {
496 self.ensure_rare_data(no_gc)
497 .custom_element_reaction_queue
498 .push(CustomElementReaction::Callback(function.to_traced(), args));
499 }
500
501 pub(crate) fn push_upgrade_reaction(
502 &self,
503 definition: Rc<CustomElementDefinition>,
504 no_gc: &NoGC,
505 ) {
506 self.ensure_rare_data(no_gc)
507 .custom_element_reaction_queue
508 .push(CustomElementReaction::Upgrade(definition));
509 }
510
511 pub(crate) fn clear_reaction_queue(&self) {
512 if let Some(ref mut rare_data) = *self.rare_data_mut() {
513 rare_data.custom_element_reaction_queue.clear();
514 }
515 }
516
517 pub(crate) fn invoke_reactions(&self, cx: &mut JSContext) {
518 loop {
519 rooted_vec!(let mut reactions);
520 match *self.rare_data_mut() {
521 Some(ref mut data) => {
522 mem::swap(&mut *reactions, &mut data.custom_element_reaction_queue)
523 },
524 None => break,
525 };
526
527 if reactions.is_empty() {
528 break;
529 }
530
531 for reaction in reactions.iter() {
532 reaction.invoke(cx, self);
533 }
534
535 reactions.clear();
536 }
537 }
538
539 pub(crate) fn has_css_layout_box(&self) -> bool {
541 self.style().is_some_and(|s| !s.get_display().is_none())
542 }
543
544 pub(crate) fn is_potentially_scrollable_body(&self) -> bool {
546 self.is_potentially_scrollable_body_shared_logic(false)
547 }
548
549 pub(crate) fn is_potentially_scrollable_body_for_scrolling_element(&self) -> bool {
551 self.is_potentially_scrollable_body_shared_logic(true)
552 }
553
554 fn is_potentially_scrollable_body_shared_logic(
556 &self,
557 treat_overflow_clip_on_parent_as_hidden: bool,
558 ) -> bool {
559 let node = self.upcast::<Node>();
560 debug_assert!(
561 node.owner_doc().GetBody().as_deref() == self.downcast::<HTMLElement>(),
562 "Called is_potentially_scrollable_body on element that is not the <body>"
563 );
564
565 if !self.has_css_layout_box() {
569 return false;
570 }
571
572 if let Some(parent) = node.GetParentElement() &&
575 let Some(style) = parent.style()
576 {
577 let mut overflow_x = style.get_box().get_overflow_x();
578 let mut overflow_y = style.get_box().get_overflow_y();
579
580 if treat_overflow_clip_on_parent_as_hidden {
583 if overflow_x == &Overflow::Clip {
584 overflow_x = &Overflow::Hidden;
585 }
586 if overflow_y == &Overflow::Clip {
587 overflow_y = &Overflow::Hidden;
588 }
589 }
590
591 if !overflow_x.is_scrollable() && !overflow_y.is_scrollable() {
592 return false;
593 }
594 };
595
596 if let Some(style) = self.style() &&
599 !style.get_box().get_overflow_x().is_scrollable() &&
600 !style.get_box().get_overflow_y().is_scrollable()
601 {
602 return false;
603 };
604
605 true
606 }
607
608 pub(crate) fn establishes_scroll_container(&self) -> bool {
611 self.upcast::<Node>()
613 .effective_overflow()
614 .is_some_and(|overflow| overflow.establishes_scroll_container())
615 }
616
617 pub(crate) fn establishes_scroll_container_without_reflow(&self) -> bool {
619 self.upcast::<Node>()
620 .effective_overflow_without_reflow()
621 .is_some_and(|overflow| overflow.establishes_scroll_container())
622 }
623
624 pub(crate) fn has_overflow(&self, no_gc: &NoGC) -> bool {
625 self.ScrollHeight() > self.ClientHeight(no_gc) ||
626 self.ScrollWidth() > self.ClientWidth(no_gc)
627 }
628
629 fn has_scrolling_box(&self, no_gc: &NoGC) -> bool {
637 self.has_css_layout_box() && self.establishes_scroll_container() && self.has_overflow(no_gc)
638 }
639
640 pub(crate) fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
641 self.rare_data()
642 .as_ref()?
643 .shadow_root
644 .as_ref()
645 .map(|sr| DomRoot::from_ref(&**sr))
646 }
647
648 pub(crate) fn shadow_root_unrooted<'a>(
649 &self,
650 no_gc: &'a NoGC,
651 ) -> Option<UnrootedDom<'a, ShadowRoot>> {
652 self.rare_data()
653 .as_ref()?
654 .shadow_root
655 .as_ref()
656 .map(|shadow_root| shadow_root.as_unrooted(no_gc))
657 }
658
659 pub(crate) fn is_shadow_host(&self) -> bool {
660 self.shadow_root().is_some()
661 }
662
663 #[expect(clippy::too_many_arguments)]
665 pub(crate) fn attach_shadow(
666 &self,
667 cx: &mut JSContext,
668 is_ua_widget: IsUserAgentWidget,
669 mode: ShadowRootMode,
670 clonable: bool,
671 serializable: bool,
672 delegates_focus: bool,
673 slot_assignment_mode: SlotAssignmentMode,
674 ) -> Fallible<DomRoot<ShadowRoot>> {
675 if self.namespace != ns!(html) {
678 return Err(Error::NotSupported(Some(
679 "Cannot attach shadow roots to elements with non-HTML namespaces".to_owned(),
680 )));
681 }
682
683 if !is_valid_shadow_host_name(self.local_name()) {
686 if is_ua_widget != IsUserAgentWidget::Yes {
688 let error_message = format!(
689 "Cannot attach shadow roots to <{}> elements",
690 *self.local_name()
691 );
692 return Err(Error::NotSupported(Some(error_message)));
693 }
694 }
695
696 if is_valid_custom_element_name(self.local_name()) || self.get_is().is_some() {
699 let definition = self.get_custom_element_definition();
703 if definition.is_some_and(|definition| definition.disable_shadow) {
706 let error_message = format!(
707 "The custom element constructor of <{}> disabled attachment of shadow roots",
708 self.local_name()
709 );
710 return Err(Error::NotSupported(Some(error_message)));
711 }
712 }
713
714 if let Some(current_shadow_root) = self.shadow_root() {
717 if !current_shadow_root.is_declarative() ||
721 current_shadow_root.shadow_root_mode() != mode
722 {
723 return Err(Error::NotSupported(Some(
724 "Cannot attach a second shadow root to the same element".into(),
725 )));
726 }
727
728 for child in current_shadow_root.upcast::<Node>().children() {
730 child.remove_self(cx);
731 }
732
733 current_shadow_root.set_declarative(false);
735
736 return Ok(current_shadow_root);
738 }
739
740 let document = self.node.owner_doc();
747 let shadow_root = ShadowRoot::new(
748 cx,
749 self,
750 &document,
751 mode,
752 slot_assignment_mode,
753 clonable,
754 is_ua_widget,
755 );
756
757 let node = self.upcast::<Node>();
761 if node.is_connected() {
762 document.remove_style_and_layout_data_from_subtree(cx.no_gc(), node);
763 }
764 if let Some(selection) = self.owner_document().selection() &&
765 node.get_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION)
766 {
767 selection.set_visible_selection_dirty();
768 }
769
770 shadow_root.set_delegates_focus(delegates_focus);
772
773 if matches!(
776 self.get_custom_element_state(),
777 CustomElementState::Precustomized | CustomElementState::Custom
778 ) {
779 shadow_root.set_available_to_element_internals(true);
780 }
781
782 shadow_root.set_declarative(false);
784
785 shadow_root.set_serializable(serializable);
787
788 self.ensure_rare_data(cx.no_gc()).shadow_root = Some(Dom::from_ref(&*shadow_root));
790 shadow_root
791 .upcast::<Node>()
792 .set_containing_shadow_root(Some(&shadow_root));
793
794 let bind_context = BindContext::new(self.upcast(), IsShadowTree::Yes);
795 shadow_root.bind_to_tree(cx, &bind_context);
796
797 node.dirty(cx.no_gc(), NodeDamage::Other);
798
799 Ok(shadow_root)
800 }
801
802 pub(crate) fn is_translate_enabled(&self) -> bool {
804 let name = &local_name!("translate");
805 if self.has_attribute(name) {
806 let attribute = self.get_string_attribute(name);
807 match_ignore_ascii_case! { &*attribute.str(),
808 "yes" | "" => return true,
809 "no" => return false,
810 _ => {},
811 }
812 }
813 if let Some(parent) = self.upcast::<Node>().GetParentNode() &&
814 let Some(elem) = parent.downcast::<Element>()
815 {
816 return elem.is_translate_enabled();
817 }
818 true
819 }
820
821 pub(crate) fn directionality(&self) -> String {
823 self.downcast::<HTMLElement>()
824 .and_then(|html_element| html_element.directionality())
825 .unwrap_or_else(|| {
826 let node = self.upcast::<Node>();
827 node.parent_directionality()
828 })
829 }
830
831 pub(crate) fn is_root(&self) -> bool {
832 match self.node.GetParentNode() {
833 None => false,
834 Some(node) => node.is::<Document>(),
835 }
836 }
837
838 pub(crate) fn registered_intersection_observers_mut<'a: 'b, 'b>(
841 &'a self,
842 no_gc: &'b NoGC,
843 ) -> RefMut<'b, Vec<IntersectionObserverRegistration>> {
844 RefMut::map(self.ensure_rare_data(no_gc), |rare_data| {
845 &mut rare_data.registered_intersection_observers
846 })
847 }
848
849 pub(crate) fn registered_intersection_observers(
850 &self,
851 ) -> Option<Ref<'_, Vec<IntersectionObserverRegistration>>> {
852 let rare_data: Ref<'_, _> = self.rare_data.borrow();
853
854 if rare_data.is_none() {
855 return None;
856 }
857 Some(Ref::map(rare_data, |rare_data| {
858 &rare_data
859 .as_ref()
860 .unwrap()
861 .registered_intersection_observers
862 }))
863 }
864
865 pub(crate) fn get_intersection_observer_registration(
866 &self,
867 observer: &IntersectionObserver,
868 ) -> Option<Ref<'_, IntersectionObserverRegistration>> {
869 if let Some(registrations) = self.registered_intersection_observers() {
870 registrations
871 .iter()
872 .position(|reg_obs| reg_obs.observer == observer)
873 .map(|index| Ref::map(registrations, |registrations| ®istrations[index]))
874 } else {
875 None
876 }
877 }
878
879 pub(crate) fn add_initial_intersection_observer_registration(
881 &self,
882 observer: &IntersectionObserver,
883 no_gc: &NoGC,
884 ) {
885 self.ensure_rare_data(no_gc)
886 .registered_intersection_observers
887 .push(IntersectionObserverRegistration::new_initial(observer));
888 }
889
890 pub(crate) fn remove_intersection_observer(
892 &self,
893 observer: &IntersectionObserver,
894 no_gc: &NoGC,
895 ) {
896 self.ensure_rare_data(no_gc)
897 .registered_intersection_observers
898 .retain(|reg_obs| *reg_obs.observer != *observer)
899 }
900
901 pub(crate) fn scrolling_box(&self, flags: ScrollContainerQueryFlags) -> Option<ScrollingBox> {
904 self.owner_window()
905 .scrolling_box_query(Some(self.upcast()), flags)
906 }
907
908 pub(crate) fn scroll_into_view_with_options(
910 &self,
911 cx: &mut JSContext,
912 behavior: ScrollBehavior,
913 block: ScrollAxisState,
914 inline: ScrollAxisState,
915 container: Option<&Element>,
916 inner_target_rect: Option<Rect<Au, CSSPixel>>,
917 ) {
918 let get_target_rect = || match inner_target_rect {
919 None => self.upcast::<Node>().border_box().unwrap_or_default(),
920 Some(inner_target_rect) => inner_target_rect.translate(
921 self.upcast::<Node>()
922 .content_box()
923 .unwrap_or_default()
924 .origin
925 .to_vector(),
926 ),
927 };
928
929 let mut parent_scrolling_box = self.scrolling_box(ScrollContainerQueryFlags::empty());
932 while let Some(scrolling_box) = parent_scrolling_box {
933 parent_scrolling_box = scrolling_box.parent();
934
935 let position = scrolling_box.determine_scroll_into_view_position(
946 cx.no_gc(),
947 block,
948 inline,
949 get_target_rect(),
950 );
951
952 if position != scrolling_box.scroll_position() {
957 scrolling_box.scroll_to(cx, position, behavior);
968 }
969
970 if container.is_some_and(|container| {
974 let container_node = container.upcast::<Node>();
975 scrolling_box
976 .node()
977 .is_shadow_including_inclusive_ancestor_of(container_node)
978 }) {
979 return;
980 }
981 }
982
983 let window_proxy = self.owner_window().window_proxy();
984 let Some(frame_element) = window_proxy.frame_element() else {
985 return;
986 };
987
988 let inner_target_rect = Some(get_target_rect());
989
990 let mut realm = enter_auto_realm(cx, frame_element);
991 let cx = &mut realm;
992
993 frame_element.scroll_into_view_with_options(
994 cx,
995 behavior,
996 block,
997 inline,
998 None,
999 inner_target_rect,
1000 )
1001 }
1002
1003 pub(crate) fn ensure_contenteditable_selection_range(
1004 &self,
1005 cx: &mut JSContext,
1006 document: &Document,
1007 ) -> DomRoot<Range> {
1008 let Some(selection_range) = self
1009 .rare_data()
1010 .as_ref()
1011 .and_then(|data| data.contenteditable_selection_range.get())
1012 else {
1013 let range = Range::new_with_doc(cx, document, None);
1014 self.ensure_rare_data(cx.no_gc())
1015 .contenteditable_selection_range
1016 .set(Some(&*range));
1017 return range;
1018 };
1019 selection_range
1020 }
1021
1022 pub(crate) fn handle_scroll_event(&self) {
1027 let document = self.owner_document();
1029
1030 document.finish_handle_scroll_event(self.upcast());
1039 }
1040
1041 pub(crate) fn style(&self) -> Option<ServoArc<ComputedValues>> {
1042 self.owner_window().layout_reflow(QueryMsg::StyleQuery);
1043 self.style_without_reflow()
1044 }
1045
1046 pub(crate) fn style_without_reflow(&self) -> Option<ServoArc<ComputedValues>> {
1049 self.style_data
1050 .borrow()
1051 .as_ref()
1052 .map(|data| data.element_data.borrow().styles.primary().clone())
1053 }
1054
1055 pub(crate) fn is_styled(&self) -> bool {
1056 self.style_data.borrow().is_some()
1057 }
1058
1059 pub(crate) fn is_display_none(&self) -> bool {
1060 self.style_data.borrow().as_ref().is_none_or(|data| {
1061 data.element_data
1062 .borrow()
1063 .styles
1064 .primary()
1065 .get_box()
1066 .display
1067 .is_none()
1068 })
1069 }
1070
1071 pub(crate) fn computed_user_select(&self) -> Option<UserSelect> {
1076 Some(
1077 self.style_data
1078 .borrow()
1079 .as_ref()?
1080 .element_data
1081 .borrow()
1082 .styles
1083 .primary()
1084 .get_ui()
1085 .user_select,
1086 )
1087 }
1088
1089 pub(crate) fn check_style_on_self_or_eager_pseudos(
1090 &self,
1091 check_styles_fn: impl Fn(&ComputedValues) -> bool,
1092 ) -> bool {
1093 let style_data = self.style_data.borrow();
1094 let Some(data) = style_data.as_ref().map(|data| data.element_data.borrow()) else {
1095 return false;
1096 };
1097
1098 if check_styles_fn(data.styles.primary()) {
1099 return true;
1100 }
1101
1102 let mut pseudo_styles = data.styles.pseudos.as_array().iter();
1103 pseudo_styles.any(|style| style.as_deref().is_some_and(&check_styles_fn))
1104 }
1105}
1106
1107#[inline]
1109pub(crate) fn is_valid_shadow_host_name(name: &LocalName) -> bool {
1110 if is_valid_custom_element_name(name) {
1113 return true;
1114 }
1115
1116 matches!(
1119 name,
1120 &local_name!("article") |
1121 &local_name!("aside") |
1122 &local_name!("blockquote") |
1123 &local_name!("body") |
1124 &local_name!("div") |
1125 &local_name!("footer") |
1126 &local_name!("h1") |
1127 &local_name!("h2") |
1128 &local_name!("h3") |
1129 &local_name!("h4") |
1130 &local_name!("h5") |
1131 &local_name!("h6") |
1132 &local_name!("header") |
1133 &local_name!("main") |
1134 &local_name!("nav") |
1135 &local_name!("p") |
1136 &local_name!("section") |
1137 &local_name!("span")
1138 )
1139}
1140
1141#[inline]
1142#[expect(unsafe_code)]
1143pub(crate) fn get_attr_for_layout<'dom>(
1144 elem: LayoutDom<'dom, Element>,
1145 namespace: &Namespace,
1146 name: &LocalName,
1147) -> Option<&'dom AttrValue> {
1148 let storage = unsafe { elem.unsafe_get().attrs.borrow_for_layout() };
1149 storage
1150 .iter()
1151 .find(|e: &&AttributeEntry| {
1152 name == e.local_name_for_layout() && namespace == e.namespace_for_layout()
1153 })
1154 .map(|e: &AttributeEntry| e.value_for_layout())
1155}
1156
1157impl<'dom> LayoutDom<'dom, Element> {
1158 #[inline]
1159 pub(crate) fn is_root(&self) -> bool {
1160 self.upcast::<Node>()
1161 .parent_node_ref()
1162 .is_some_and(|parent| matches!(parent.type_id_for_layout(), NodeTypeId::Document(_)))
1163 }
1164
1165 pub(crate) fn is_body_element_of_html_element_root(&self) -> bool {
1167 if self.local_name() != &local_name!("body") {
1168 return false;
1169 }
1170 let Some(parent_node) = self.upcast::<Node>().parent_node_ref() else {
1171 return false;
1172 };
1173 let Some(parent_element) = parent_node.downcast::<Element>() else {
1174 return false;
1175 };
1176 parent_element.local_name() == &local_name!("html")
1177 }
1178
1179 #[expect(unsafe_code)]
1181 #[inline]
1182 pub(crate) fn each_attr_name_for_layout<F>(self, mut callback: F)
1183 where
1184 F: FnMut(&LocalName),
1185 {
1186 let storage = unsafe { self.unsafe_get().attrs.borrow_for_layout() };
1187 for entry in storage.iter() {
1188 callback(entry.local_name_for_layout());
1189 }
1190 }
1191
1192 #[inline]
1193 pub(crate) fn has_class_or_part_for_layout(
1194 self,
1195 name: &AtomIdent,
1196 attr_name: &LocalName,
1197 case_sensitivity: CaseSensitivity,
1198 ) -> bool {
1199 get_attr_for_layout(self, &ns!(), attr_name).is_some_and(|attr| {
1200 attr.as_tokens()
1201 .iter()
1202 .any(|atom| case_sensitivity.eq_atom(atom, name))
1203 })
1204 }
1205
1206 #[inline]
1207 pub(crate) fn get_classes_for_layout(self) -> Option<&'dom [Atom]> {
1208 get_attr_for_layout(self, &ns!(), &local_name!("class")).map(|attr| attr.as_tokens())
1209 }
1210
1211 #[inline]
1212 pub(crate) fn get_parts_for_layout(self) -> Option<&'dom [Atom]> {
1213 get_attr_for_layout(self, &ns!(), &local_name!("part")).map(|attr| attr.as_tokens())
1214 }
1215
1216 #[inline]
1217 pub(crate) fn dimension_attr_value(self, name: LocalName) -> LengthOrPercentageOrAuto {
1218 self.get_attr_for_layout(&ns!(), &name)
1219 .map(AttrValue::as_dimension)
1220 .cloned()
1221 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1222 }
1223
1224 #[inline]
1225 #[expect(unsafe_code)]
1226 pub(crate) fn style_data(self) -> Option<&'dom StyleData> {
1227 unsafe { self.unsafe_get().style_data.borrow_for_layout().as_deref() }
1228 }
1229
1230 #[inline]
1231 #[expect(unsafe_code)]
1232 pub(crate) unsafe fn initialize_style_data(self) {
1233 let data = unsafe { self.unsafe_get().style_data.borrow_mut_for_layout() };
1234 debug_assert!(data.is_none());
1235 *data = Some(Box::default());
1236 }
1237
1238 #[inline]
1239 #[expect(unsafe_code)]
1240 pub(crate) unsafe fn clear_style_data(self) {
1241 unsafe {
1242 self.unsafe_get().style_data.borrow_mut_for_layout().take();
1243 }
1244 }
1245
1246 #[inline]
1247 fn apply_property_for_length_percentage<Callback>(
1248 self,
1249 length_percentage: LengthOrPercentageOrAuto,
1250 callback: Callback,
1251 ) where
1252 Callback: FnOnce(specified::LengthPercentage),
1253 {
1254 match length_percentage {
1255 LengthOrPercentageOrAuto::Auto => {},
1256 LengthOrPercentageOrAuto::Percentage(percentage) => {
1257 let value = specified::LengthPercentage::Percentage(
1258 specified::NoCalcPercentage::new(percentage),
1259 );
1260 callback(value)
1261 },
1262 LengthOrPercentageOrAuto::Length(length) => {
1263 let value = specified::LengthPercentage::Length(specified::NoCalcLength::from_px(
1264 length.to_f32_px(),
1265 ));
1266 callback(value)
1267 },
1268 }
1269 }
1270
1271 pub(crate) fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
1272 where
1273 V: Push<ApplicableDeclarationBlock>,
1274 {
1275 let document = self.upcast::<Node>().owner_doc_for_layout();
1278 let mut property_declaration_block = None;
1279 let mut push = |declaration| {
1280 property_declaration_block
1281 .get_or_insert_with(PropertyDeclarationBlock::default)
1282 .push(declaration, Importance::Normal);
1283 };
1284
1285 if let Some(lang) = self.get_lang_attr_val_for_layout() {
1288 push(PropertyDeclaration::XLang(specified::XLang(Atom::from(
1289 lang.to_owned(),
1290 ))));
1291 }
1292
1293 let bgcolor = if let Some(this) = self.downcast::<HTMLBodyElement>() {
1294 this.get_background_color()
1295 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1296 this.get_background_color()
1297 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1298 this.get_background_color()
1299 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1300 this.get_background_color()
1301 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1302 this.get_background_color()
1303 } else {
1304 None
1305 };
1306
1307 if let Some(color) = bgcolor {
1308 push(PropertyDeclaration::BackgroundColor(
1309 specified::Color::from_absolute_color(color),
1310 ));
1311 }
1312
1313 if is_element_affected_by_legacy_background_presentational_hint(
1314 self.namespace(),
1315 self.local_name(),
1316 ) && let Some(url) = self
1317 .get_attr_for_layout(&ns!(), &local_name!("background"))
1318 .and_then(AttrValue::as_resolved_url)
1319 .cloned()
1320 {
1321 push(PropertyDeclaration::BackgroundImage(
1322 background_image::SpecifiedValue(vec![specified::Image::for_cascade(url)].into()),
1323 ));
1324 }
1325
1326 let color = if let Some(this) = self.downcast::<HTMLFontElement>() {
1327 this.get_color()
1328 } else if let Some(this) = self.downcast::<HTMLBodyElement>() {
1329 this.get_color()
1331 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1332 this.get_color()
1334 } else {
1335 None
1336 };
1337
1338 if let Some(color) = color {
1339 push(PropertyDeclaration::Color(
1340 longhands::color::SpecifiedValue(specified::Color::from_absolute_color(color)),
1341 ));
1342 }
1343
1344 let font_face = self
1345 .downcast::<HTMLFontElement>()
1346 .and_then(LayoutDom::get_face);
1347 if let Some(font_face) = font_face {
1348 push(PropertyDeclaration::FontFamily(
1349 font_family::SpecifiedValue::Values(computed::font::FontFamilyList {
1350 list: ArcSlice::from_iter(
1351 HTMLFontElement::parse_face_attribute(font_face).into_iter(),
1352 ),
1353 }),
1354 ));
1355 }
1356
1357 let font_size = self
1358 .downcast::<HTMLFontElement>()
1359 .and_then(LayoutDom::get_size);
1360 if let Some(font_size) = font_size {
1361 push(PropertyDeclaration::FontSize(
1362 font_size::SpecifiedValue::from_html_size(font_size as u8),
1363 ));
1364 }
1365
1366 let size = self
1372 .downcast::<HTMLInputElement>()
1373 .and_then(|input_element| {
1374 match self.get_attr_val_for_layout(&ns!(), &local_name!("type")) {
1376 Some("hidden") | Some("range") | Some("color") | Some("checkbox") |
1377 Some("radio") | Some("file") | Some("submit") | Some("image") |
1378 Some("reset") | Some("button") => None,
1379 _ => match input_element.size_for_layout() {
1381 0 => None,
1382 s => Some(s as i32),
1383 },
1384 }
1385 });
1386
1387 if let Some(size) = size {
1388 let value = specified::NoCalcLength::from_servo_character_width(size);
1389 push(PropertyDeclaration::Width(
1390 specified::Size::LengthPercentage(NonNegative(
1391 specified::LengthPercentage::Length(value),
1392 )),
1393 ));
1394 }
1395
1396 let width = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1397 this.width()
1398 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1399 this.width()
1400 } else if let Some(this) = self.downcast::<HTMLInputElement>() {
1401 this.width()
1402 } else if let Some(this) = self.downcast::<HTMLMarqueeElement>() {
1403 this.width()
1404 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1405 this.width()
1406 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1407 this.width()
1408 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1409 this.width()
1410 } else if let Some(this) = self.downcast::<HTMLTableColElement>() {
1411 this.width()
1412 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
1413 this.width()
1415 } else {
1416 LengthOrPercentageOrAuto::Auto
1417 };
1418
1419 self.apply_property_for_length_percentage(width, |value| {
1420 push(PropertyDeclaration::Width(
1421 specified::Size::LengthPercentage(NonNegative(value)),
1422 ));
1423 });
1424
1425 let height = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
1426 this.height()
1427 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
1428 this.height()
1429 } else if let Some(this) = self.downcast::<HTMLInputElement>() {
1430 this.height()
1431 } else if let Some(this) = self.downcast::<HTMLMarqueeElement>() {
1432 this.height()
1433 } else if let Some(this) = self.downcast::<HTMLVideoElement>() {
1434 this.height()
1435 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
1436 this.height()
1437 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
1438 this.height()
1439 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
1440 this.height()
1441 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
1442 this.height()
1443 } else {
1444 LengthOrPercentageOrAuto::Auto
1445 };
1446
1447 self.apply_property_for_length_percentage(height, |value| {
1448 push(PropertyDeclaration::Height(
1449 specified::Size::LengthPercentage(NonNegative(value)),
1450 ));
1451 });
1452
1453 let margin_right_left = if let Some(this) = self.downcast::<HTMLImageElement>() {
1454 this.margin_right_left()
1455 } else {
1456 LengthOrPercentageOrAuto::Auto
1457 };
1458
1459 self.apply_property_for_length_percentage(margin_right_left, |value| {
1460 let value = specified::Margin::LengthPercentage(value);
1461 push(PropertyDeclaration::MarginLeft(value.clone()));
1462 push(PropertyDeclaration::MarginRight(value));
1463 });
1464
1465 let margin_top_bottom = if let Some(this) = self.downcast::<HTMLImageElement>() {
1466 this.margin_top_bottom()
1467 } else {
1468 LengthOrPercentageOrAuto::Auto
1469 };
1470
1471 self.apply_property_for_length_percentage(margin_top_bottom, |value| {
1472 let value = specified::Margin::LengthPercentage(value);
1473 push(PropertyDeclaration::MarginTop(value.clone()));
1474 push(PropertyDeclaration::MarginBottom(value));
1475 });
1476
1477 if let Some(svg_element) = self.downcast::<SVGElement>() {
1478 svg_element.synthesize_presentational_hints(document, &mut push);
1479 }
1480
1481 if (self.is::<HTMLImageElement>() || self.is::<HTMLVideoElement>()) &&
1484 let LengthOrPercentageOrAuto::Length(width) = width &&
1485 let LengthOrPercentageOrAuto::Length(height) = height
1486 {
1487 let width_value = NonNegative(specified::Number::new(width.to_f32_px()));
1488 let height_value = NonNegative(specified::Number::new(height.to_f32_px()));
1489 let aspect_ratio = specified::position::AspectRatio {
1490 auto: true,
1491 ratio: PreferredRatio::Ratio(Ratio(width_value, height_value)),
1492 };
1493 push(PropertyDeclaration::AspectRatio(Box::new(aspect_ratio)));
1494 }
1495
1496 let cols = self
1497 .downcast::<HTMLTextAreaElement>()
1498 .map(LayoutDom::get_cols);
1499 if let Some(cols) = cols {
1500 let cols = cols as i32;
1501 if cols > 0 {
1502 let value = specified::NoCalcLength::from_servo_character_width(cols);
1508 push(PropertyDeclaration::Width(
1509 specified::Size::LengthPercentage(NonNegative(
1510 specified::LengthPercentage::Length(value),
1511 )),
1512 ));
1513 }
1514 }
1515
1516 let rows = self
1517 .downcast::<HTMLTextAreaElement>()
1518 .map(LayoutDom::get_rows);
1519 if let Some(rows) = rows {
1520 let rows = rows as i32;
1521 if rows > 0 {
1522 let value = specified::NoCalcLength::from_em(rows as CSSFloat);
1526 push(PropertyDeclaration::Height(
1527 specified::Size::LengthPercentage(NonNegative(
1528 specified::LengthPercentage::Length(value),
1529 )),
1530 ));
1531 }
1532 }
1533
1534 if let Some(table) = self.downcast::<HTMLTableElement>() {
1535 if let Some(cellspacing) = table.get_cellspacing() {
1536 let width_value = specified::Length::from_px(cellspacing as f32);
1537 push(PropertyDeclaration::BorderSpacing(
1538 border_spacing::SpecifiedValue::new(
1539 width_value.clone().into(),
1540 width_value.into(),
1541 ),
1542 ));
1543 }
1544 if let Some(border) = table.get_border() {
1545 let width_value = specified::BorderSideWidth::from_px(border as f32);
1546 push(PropertyDeclaration::BorderTopWidth(width_value.clone()));
1547 push(PropertyDeclaration::BorderLeftWidth(width_value.clone()));
1548 push(PropertyDeclaration::BorderBottomWidth(width_value.clone()));
1549 push(PropertyDeclaration::BorderRightWidth(width_value));
1550 }
1551 if document.quirks_mode() == QuirksMode::Quirks {
1552 push(PropertyDeclaration::Color(color::SpecifiedValue(
1554 specified::Color::InheritFromBodyQuirk,
1555 )));
1556 }
1557 }
1558
1559 if let Some(cellpadding) = self
1560 .downcast::<HTMLTableCellElement>()
1561 .and_then(|this| this.get_table())
1562 .and_then(|table| table.get_cellpadding())
1563 {
1564 let cellpadding = NonNegative(specified::LengthPercentage::Length(
1565 specified::NoCalcLength::from_px(cellpadding as f32),
1566 ));
1567 push(PropertyDeclaration::PaddingTop(cellpadding.clone()));
1568 push(PropertyDeclaration::PaddingLeft(cellpadding.clone()));
1569 push(PropertyDeclaration::PaddingBottom(cellpadding.clone()));
1570 push(PropertyDeclaration::PaddingRight(cellpadding));
1571 }
1572
1573 if let Some(size_info) = self
1575 .downcast::<HTMLHRElement>()
1576 .and_then(|hr_element| hr_element.get_size_info())
1577 {
1578 match size_info {
1579 SizePresentationalHint::SetHeightTo(height) => {
1580 push(PropertyDeclaration::Height(height));
1581 },
1582 SizePresentationalHint::SetAllBorderWidthValuesTo(border_width) => {
1583 push(PropertyDeclaration::BorderLeftWidth(border_width.clone()));
1584 push(PropertyDeclaration::BorderRightWidth(border_width.clone()));
1585 push(PropertyDeclaration::BorderTopWidth(border_width.clone()));
1586 push(PropertyDeclaration::BorderBottomWidth(border_width));
1587 },
1588 SizePresentationalHint::SetBottomBorderWidthToZero => {
1589 push(PropertyDeclaration::BorderBottomWidth(
1590 specified::border::BorderSideWidth::from_px(0.),
1591 ));
1592 },
1593 }
1594 }
1595
1596 let Some(property_declaration_block) = property_declaration_block else {
1597 return;
1598 };
1599
1600 let shared_lock = &document.shared_style_locks().author;
1601 hints.push(ApplicableDeclarationBlock::from_declarations(
1602 ServoArc::new(shared_lock.wrap(property_declaration_block)),
1603 CascadeLevel::new(CascadeOrigin::PresHints),
1604 LayerOrder::root(),
1605 ));
1606 }
1607
1608 pub(crate) fn get_span(self) -> Option<u32> {
1609 self.downcast::<HTMLTableColElement>()
1611 .and_then(|element| element.get_span())
1612 }
1613
1614 pub(crate) fn get_colspan(self) -> Option<u32> {
1615 self.downcast::<HTMLTableCellElement>()
1617 .and_then(|element| element.get_colspan())
1618 }
1619
1620 pub(crate) fn get_rowspan(self) -> Option<u32> {
1621 self.downcast::<HTMLTableCellElement>()
1623 .and_then(|element| element.get_rowspan())
1624 }
1625
1626 #[inline]
1627 pub(crate) fn is_html_element(&self) -> bool {
1628 *self.namespace() == ns!(html)
1629 }
1630
1631 #[expect(unsafe_code)]
1632 pub(crate) fn id_attribute(self) -> *const Option<Atom> {
1633 unsafe { (self.unsafe_get()).id_attribute.borrow_for_layout() }
1634 }
1635
1636 #[expect(unsafe_code)]
1637 pub(crate) fn style_attribute(
1638 self,
1639 ) -> *const Option<ServoArc<Locked<PropertyDeclarationBlock>>> {
1640 unsafe { (self.unsafe_get()).style_attribute.borrow_for_layout() }
1641 }
1642
1643 pub(crate) fn local_name(self) -> &'dom LocalName {
1644 &(self.unsafe_get()).local_name
1645 }
1646
1647 pub(crate) fn namespace(self) -> &'dom Namespace {
1648 &(self.unsafe_get()).namespace
1649 }
1650
1651 pub(crate) fn get_lang_attr_val_for_layout(self) -> Option<&'dom str> {
1653 if let Some(attr) = self.get_attr_val_for_layout(&ns!(xml), &local_name!("lang")) {
1656 return Some(attr);
1657 }
1658 if self.is_html_element() || self.namespace() == &ns!(svg) {
1662 return self.get_attr_val_for_layout(&ns!(), &local_name!("lang"));
1663 }
1664 None
1665 }
1666
1667 pub(crate) fn get_lang_for_layout(self) -> AtomString {
1669 let mut current_node = Some(self.upcast::<Node>());
1672 while let Some(node) = current_node {
1673 current_node = node.composed_parent_node_ref();
1678 match node.downcast::<Element>() {
1679 Some(elem) => {
1680 if let Some(attr) = elem.get_lang_attr_val_for_layout() {
1681 return AtomString::from(attr);
1682 }
1683 },
1684 None => continue,
1685 }
1686 }
1687 let document = self.upcast::<Node>().owner_doc_for_layout();
1702 if let Some(document_language) = document.default_language_for_layout() {
1703 return AtomString::from(document_language);
1704 }
1705 AtomString::default()
1706 }
1707
1708 #[inline]
1709 pub(crate) fn get_state_for_layout(self) -> ElementState {
1710 (self.unsafe_get()).state.get()
1711 }
1712
1713 #[inline]
1714 pub(crate) fn insert_selector_flags(self, flags: ElementSelectorFlags) {
1715 debug_assert!(thread_state::get().is_layout());
1716 self.unsafe_get().insert_selector_flags(flags);
1717 }
1718
1719 #[inline]
1720 pub(crate) fn get_selector_flags(self) -> ElementSelectorFlags {
1721 self.unsafe_get().get_selector_flags()
1722 }
1723
1724 #[inline]
1725 #[expect(unsafe_code)]
1726 pub(crate) fn get_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
1727 unsafe {
1728 self.unsafe_get()
1729 .rare_data
1730 .borrow_for_layout()
1731 .as_ref()?
1732 .shadow_root
1733 .as_ref()
1734 .map(|sr| sr.to_layout())
1735 }
1736 }
1737
1738 #[inline]
1739 pub(crate) fn get_attr_for_layout(
1740 self,
1741 namespace: &Namespace,
1742 name: &LocalName,
1743 ) -> Option<&'dom AttrValue> {
1744 get_attr_for_layout(self, namespace, name)
1745 }
1746
1747 #[inline]
1748 pub(crate) fn get_attr_val_for_layout(
1749 self,
1750 namespace: &Namespace,
1751 name: &LocalName,
1752 ) -> Option<&'dom str> {
1753 get_attr_for_layout(self, namespace, name).map(|attr| &**attr)
1754 }
1755
1756 #[inline]
1757 #[expect(unsafe_code)]
1758 pub(crate) fn get_attr_vals_for_layout(
1759 self,
1760 name: &LocalName,
1761 ) -> impl Iterator<Item = &'dom AttrValue> {
1762 let storage = unsafe { self.unsafe_get().attrs.borrow_for_layout() };
1763 storage
1764 .iter()
1765 .filter(move |e: &&AttributeEntry| name == e.local_name_for_layout())
1766 .map(|e: &AttributeEntry| e.value_for_layout())
1767 }
1768
1769 #[expect(unsafe_code)]
1770 pub(crate) fn each_custom_state_for_layout(self, mut callback: impl FnMut(&AtomIdent)) {
1771 let rare_data = unsafe { self.unsafe_get().rare_data.borrow_for_layout() };
1772 let Some(rare_data) = rare_data.as_ref() else {
1773 return;
1774 };
1775 let Some(element_internals) = rare_data.element_internals.as_ref() else {
1776 return;
1777 };
1778
1779 let element_internals: LayoutDom<'_, _> = unsafe { element_internals.to_layout() };
1780 if let Some(states) = element_internals.unsafe_get().custom_states_for_layout() {
1781 for state in states.unsafe_get().set_for_layout().iter() {
1782 callback(&AtomIdent::from(&*state.str()));
1784 }
1785 }
1786 }
1787}
1788
1789impl Element {
1790 pub(crate) fn is_html_element(&self) -> bool {
1791 self.namespace == ns!(html)
1792 }
1793
1794 pub(crate) fn is_svg_element(&self) -> bool {
1795 self.namespace == ns!(svg)
1796 }
1797
1798 pub(crate) fn html_element_in_html_document(&self) -> bool {
1799 self.is_html_element() && self.upcast::<Node>().is_in_html_doc()
1800 }
1801
1802 pub(crate) fn local_name(&self) -> &LocalName {
1803 &self.local_name
1804 }
1805
1806 pub(crate) fn parsed_name(&self, mut name: DOMString) -> LocalName {
1807 if self.html_element_in_html_document() {
1808 name.make_ascii_lowercase();
1809 }
1810 LocalName::from(name)
1811 }
1812
1813 pub(crate) fn namespace(&self) -> &Namespace {
1814 &self.namespace
1815 }
1816
1817 pub(crate) fn prefix(&self) -> Ref<'_, Option<Prefix>> {
1818 self.prefix.borrow()
1819 }
1820
1821 pub(crate) fn set_prefix(&self, prefix: Option<Prefix>) {
1822 *self.prefix.borrow_mut() = prefix;
1823 }
1824
1825 pub(crate) fn set_custom_element_registry(
1826 &self,
1827 registry: Option<&CustomElementRegistry>,
1828 no_gc: &NoGC,
1829 ) {
1830 self.ensure_rare_data(no_gc).custom_element_registry = registry.map(Dom::from_ref);
1831 }
1832
1833 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
1834 self.rare_data()
1835 .as_ref()?
1836 .custom_element_registry
1837 .as_deref()
1838 .map(DomRoot::from_ref)
1839 }
1840
1841 pub(crate) fn attrs(&self) -> &AttributeStorage {
1842 &self.attrs
1843 }
1844
1845 pub(crate) fn dom_attrs(&self, cx: &mut JSContext) -> &AttributeStorage {
1846 let len = self.attrs.borrow().len();
1848 for i in 0..len {
1849 self.attrs.ensure_dom(cx, i, self);
1850 }
1851 &self.attrs
1852 }
1853
1854 pub(crate) fn locate_namespace(&self, prefix: Option<DOMString>) -> Namespace {
1856 let namespace_prefix = prefix.as_ref().map(|s| Prefix::from(&*s.str()));
1857
1858 if namespace_prefix == Some(namespace_prefix!("xml")) {
1860 return ns!(xml);
1861 }
1862
1863 if namespace_prefix == Some(namespace_prefix!("xmlns")) {
1865 return ns!(xmlns);
1866 }
1867
1868 let prefix = prefix.map(LocalName::from);
1869
1870 let inclusive_ancestor_elements = self
1871 .upcast::<Node>()
1872 .inclusive_ancestors(ShadowIncluding::No)
1873 .filter_map(DomRoot::downcast::<Self>);
1874
1875 for element in inclusive_ancestor_elements {
1878 if element.namespace() != &ns!() &&
1880 element.prefix().as_ref().map(|p| &**p) == prefix.as_deref()
1881 {
1882 return element.namespace().clone();
1883 }
1884
1885 let found_ns = element.attrs.borrow().iter().find_map(|attr| {
1890 if attr.namespace() != &ns!(xmlns) {
1891 return None;
1892 }
1893 match (attr.prefix(), prefix.as_ref()) {
1894 (Some(&namespace_prefix!("xmlns")), Some(prefix)) => {
1895 if attr.local_name() == prefix {
1896 Some(Namespace::from(&**attr.value()))
1897 } else {
1898 None
1899 }
1900 },
1901 (None, None) => {
1902 if attr.local_name() == &local_name!("xmlns") {
1903 Some(Namespace::from(&**attr.value()))
1904 } else {
1905 None
1906 }
1907 },
1908 _ => None,
1909 }
1910 });
1911
1912 if let Some(ns) = found_ns {
1913 return ns;
1914 }
1915 }
1916
1917 ns!()
1918 }
1919
1920 pub(crate) fn name_attribute(&self) -> Option<Atom> {
1921 self.rare_data().as_ref()?.name_attribute.clone()
1922 }
1923
1924 pub(crate) fn style_attribute(
1925 &self,
1926 ) -> &DomRefCell<Option<ServoArc<Locked<PropertyDeclarationBlock>>>> {
1927 &self.style_attribute
1928 }
1929
1930 #[cfg(feature = "devtools")]
1931 pub(crate) fn summarize(&self) -> Vec<devtools_traits::AttrInfo> {
1932 self.attrs
1933 .borrow()
1934 .iter()
1935 .map(|attr| attr.summarize())
1936 .collect()
1937 }
1938
1939 pub(crate) fn is_void(&self) -> bool {
1940 if self.namespace != ns!(html) {
1941 return false;
1942 }
1943 match self.local_name {
1944 local_name!("area") |
1947 local_name!("base") |
1948 local_name!("basefont") |
1949 local_name!("bgsound") |
1950 local_name!("br") |
1951 local_name!("col") |
1952 local_name!("embed") |
1953 local_name!("frame") |
1954 local_name!("hr") |
1955 local_name!("img") |
1956 local_name!("input") |
1957 local_name!("keygen") |
1958 local_name!("link") |
1959 local_name!("meta") |
1960 local_name!("param") |
1961 local_name!("source") |
1962 local_name!("track") |
1963 local_name!("wbr") => true,
1964 _ => false,
1965 }
1966 }
1967
1968 pub(crate) fn root_element(&self) -> DomRoot<Element> {
1969 if self.node.is_in_a_document_tree() {
1970 self.upcast::<Node>()
1971 .owner_doc()
1972 .GetDocumentElement()
1973 .unwrap()
1974 } else {
1975 self.upcast::<Node>()
1976 .inclusive_ancestors(ShadowIncluding::No)
1977 .filter_map(DomRoot::downcast)
1978 .last()
1979 .expect("We know inclusive_ancestors will return `self` which is an element")
1980 }
1981 }
1982
1983 pub(crate) fn lookup_prefix(&self, namespace: Namespace) -> Option<DOMString> {
1985 for node in self
1986 .upcast::<Node>()
1987 .inclusive_ancestors(ShadowIncluding::No)
1988 {
1989 let element = node.downcast::<Element>()?;
1990 if *element.namespace() == namespace &&
1992 let Some(prefix) = element.GetPrefix()
1993 {
1994 return Some(prefix);
1995 }
1996
1997 for attr in element.attrs.borrow().iter() {
1999 if attr.prefix() == Some(&namespace_prefix!("xmlns")) &&
2000 **attr.value() == *namespace
2001 {
2002 return Some(DOMString::from(&**attr.local_name()));
2003 }
2004 }
2005 }
2006 None
2007 }
2008
2009 pub(crate) fn is_document_element(&self) -> bool {
2011 if let Some(document_element) = self.owner_document().GetDocumentElement() {
2012 *document_element == *self
2013 } else {
2014 false
2015 }
2016 }
2017
2018 pub(crate) fn is_active_element(&self) -> bool {
2020 if let Some(active_element) = self.owner_document().GetActiveElement() {
2021 *active_element == *self
2022 } else {
2023 false
2024 }
2025 }
2026
2027 pub(crate) fn is_actually_disabled(&self) -> bool {
2028 let node = self.upcast::<Node>();
2029 match node.type_id() {
2030 NodeTypeId::Element(ElementTypeId::HTMLElement(
2031 HTMLElementTypeId::HTMLButtonElement,
2032 )) |
2033 NodeTypeId::Element(ElementTypeId::HTMLElement(
2034 HTMLElementTypeId::HTMLInputElement,
2035 )) |
2036 NodeTypeId::Element(ElementTypeId::HTMLElement(
2037 HTMLElementTypeId::HTMLSelectElement,
2038 )) |
2039 NodeTypeId::Element(ElementTypeId::HTMLElement(
2040 HTMLElementTypeId::HTMLTextAreaElement,
2041 )) |
2042 NodeTypeId::Element(ElementTypeId::HTMLElement(
2043 HTMLElementTypeId::HTMLOptionElement,
2044 )) => self.disabled_state(),
2045 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
2046 self.downcast::<HTMLElement>()
2047 .unwrap()
2048 .is_form_associated_custom_element() &&
2049 self.disabled_state()
2050 },
2051 _ => false,
2056 }
2057 }
2058
2059 pub(crate) fn push_new_attribute(
2060 &self,
2061 cx: &mut JSContext,
2062 local_name: LocalName,
2063 value: AttrValue,
2064 name: LocalName,
2065 namespace: Namespace,
2066 prefix: Option<Prefix>,
2067 ) {
2068 let data = ContentAttributeData {
2073 identifier: AttrName::new(local_name, name, namespace, prefix),
2074 value,
2075 };
2076 let attr_ref = AttrRef::Raw(&data);
2077 self.will_mutate_attr(attr_ref);
2078 self.attrs.push_raw(ContentAttributeData {
2080 identifier: data.identifier.clone(),
2081 value: data.value.clone(),
2082 });
2083 self.handle_attribute_changes(cx, attr_ref, None, Some(&*attr_ref.value()));
2085 }
2086
2087 fn handle_attribute_changes(
2089 &self,
2090 cx: &mut JSContext,
2091 attr: AttrRef<'_>,
2092 old_value: Option<&AttrValue>,
2093 new_value: Option<&AttrValue>,
2094 ) {
2095 let name = attr.local_name().clone();
2098 let namespace = attr.namespace().clone();
2099 let mutation = LazyCell::new(|| Mutation::Attribute {
2100 name: name.clone(),
2101 namespace: namespace.clone(),
2102 old_value: old_value.map(|old_value| DOMString::from(&**old_value)),
2103 });
2104 MutationObserver::queue_a_mutation_record(cx, &self.node, mutation);
2105
2106 let has_new_value = new_value.is_some();
2108
2109 if self.is_custom() {
2112 let reaction = CallbackReaction::AttributeChanged(
2113 attr.local_name().clone(),
2114 old_value,
2115 new_value,
2116 attr.namespace().clone(),
2117 );
2118 ScriptThread::enqueue_callback_reaction(cx, self, reaction, None);
2119 }
2120
2121 if is_relevant_attribute(attr.namespace(), attr.local_name()) {
2123 let attribute_mutation = if has_new_value {
2124 AttributeMutation::Set(old_value)
2125 } else {
2126 AttributeMutation::Removed
2127 };
2128 vtable_for(self.upcast()).attribute_mutated(cx, attr, attribute_mutation);
2129 }
2130 }
2131
2132 pub(crate) fn change_attribute(&self, cx: &mut JSContext, attr: &Attr, mut value: AttrValue) {
2134 let old_value = &attr.value().clone();
2138 self.will_mutate_attr(AttrRef::Dom(attr));
2140 attr.swap_value(&mut value);
2141 self.handle_attribute_changes(
2145 cx,
2146 AttrRef::Dom(attr),
2147 Some(old_value),
2148 Some(&*attr.value()),
2149 );
2150 }
2151
2152 pub(crate) fn push_attribute(&self, cx: &mut JSContext, attr: &Attr) {
2154 assert!(attr.GetOwnerElement().as_deref() == Some(self));
2158 assert!(attr.upcast::<Node>().owner_doc() == self.node.owner_doc());
2162 self.will_mutate_attr(AttrRef::Dom(attr));
2164 self.attrs.push_dom(attr);
2165 self.handle_attribute_changes(cx, AttrRef::Dom(attr), None, Some(&*attr.value()));
2169 }
2170
2171 pub(crate) fn with_attribute<R, F>(
2172 &self,
2173 namespace: &Namespace,
2174 local_name: &LocalName,
2175 map_func: F,
2176 ) -> Option<R>
2177 where
2178 F: FnOnce(AttrRef<'_>) -> R,
2179 {
2180 self.attrs
2181 .borrow()
2182 .iter()
2183 .find(|attribute| {
2184 attribute.local_name() == local_name && attribute.namespace() == namespace
2185 })
2186 .map(map_func)
2187 }
2188
2189 pub(crate) fn attribute_str_ref<'a>(
2190 &'a self,
2191 namespace: &Namespace,
2192 local_name: &LocalName,
2193 ) -> Option<AttrStrRef<'a>> {
2194 AttrStrRef::maybe_new(self.attrs.borrow(), namespace, local_name)
2195 }
2196
2197 pub(crate) fn get_attribute_with_namespace(
2203 &self,
2204 cx: &mut JSContext,
2205 namespace: &Namespace,
2206 local_name: &LocalName,
2207 ) -> Option<DomRoot<Attr>> {
2208 let idx = self
2209 .attrs
2210 .borrow()
2211 .iter()
2212 .position(|a| a.local_name() == local_name && a.namespace() == namespace)?;
2213 Some(self.attrs.ensure_dom(cx, idx, self))
2214 }
2215
2216 pub(crate) fn get_attribute_by_name(
2218 &self,
2219 cx: &mut JSContext,
2220 name: DOMString,
2221 ) -> Option<DomRoot<Attr>> {
2222 let name = &self.parsed_name(name);
2223 let idx = self.attrs.borrow().iter().position(|a| a.name() == name)?;
2224 let attr_dom = Some(self.attrs.ensure_dom(cx, idx, self));
2225 fn id_and_name_must_be_atoms(name: &LocalName, maybe_attr: &Option<DomRoot<Attr>>) -> bool {
2226 if *name == local_name!("id") || *name == local_name!("name") {
2227 match maybe_attr {
2228 None => true,
2229 Some(attr) => matches!(*attr.value(), AttrValue::Atom(_)),
2230 }
2231 } else {
2232 true
2233 }
2234 }
2235 debug_assert!(id_and_name_must_be_atoms(name, &attr_dom));
2236 attr_dom
2237 }
2238
2239 pub(crate) fn set_attribute_from_parser(
2240 &self,
2241 cx: &mut JSContext,
2242 qname: QualName,
2243 value: DOMString,
2244 ) {
2245 if self
2247 .attrs
2248 .borrow()
2249 .iter()
2250 .any(|a| *a.local_name() == qname.local && *a.namespace() == qname.ns)
2251 {
2252 return;
2253 }
2254
2255 let name = match qname.prefix {
2256 None => qname.local.clone(),
2257 Some(ref prefix) => {
2258 let name = format!("{}:{}", &**prefix, &*qname.local);
2259 LocalName::from(name)
2260 },
2261 };
2262 let value = self.parse_attribute(&qname.ns, &qname.local, value);
2263 self.push_new_attribute(cx, qname.local, value, name, qname.ns, qname.prefix);
2264 }
2265
2266 pub(crate) fn set_attribute(&self, cx: &mut JSContext, name: &LocalName, value: AttrValue) {
2267 debug_assert_eq!(
2268 *name,
2269 name.to_ascii_lowercase(),
2270 "All attribute accesses should use a lowercase ASCII name"
2271 );
2272 debug_assert!(!name.contains(':'));
2273
2274 self.set_first_matching_attribute(
2275 cx,
2276 name.clone(),
2277 value,
2278 name.clone(),
2279 ns!(),
2280 None,
2281 |attr| attr.local_name() == name,
2282 );
2283 }
2284
2285 pub(crate) fn set_attribute_with_namespace(
2286 &self,
2287 cx: &mut JSContext,
2288 local_name: LocalName,
2289 value: AttrValue,
2290 name: LocalName,
2291 namespace: Namespace,
2292 prefix: Option<Prefix>,
2293 ) {
2294 self.set_first_matching_attribute(
2295 cx,
2296 local_name.clone(),
2297 value,
2298 name,
2299 namespace.clone(),
2300 prefix,
2301 |attr| *attr.local_name() == local_name && *attr.namespace() == namespace,
2302 );
2303 }
2304
2305 #[expect(clippy::too_many_arguments)]
2307 fn set_first_matching_attribute<F>(
2308 &self,
2309 cx: &mut JSContext,
2310 local_name: LocalName,
2311 value: AttrValue,
2312 name: LocalName,
2313 namespace: Namespace,
2314 prefix: Option<Prefix>,
2315 find: F,
2316 ) where
2317 F: Fn(AttrRef<'_>) -> bool,
2318 {
2319 let found_idx = self.attrs.borrow().iter().position(find);
2322 if let Some(idx) = found_idx {
2323 let attr = self.attrs.ensure_dom(cx, idx, self);
2324 self.will_mutate_attr(AttrRef::Dom(&attr));
2326 self.change_attribute(cx, &attr, value);
2327 } else {
2328 self.push_new_attribute(cx, local_name, value, name, namespace, prefix);
2333 };
2334 }
2335
2336 pub(crate) fn parse_attribute(
2337 &self,
2338 namespace: &Namespace,
2339 local_name: &LocalName,
2340 value: DOMString,
2341 ) -> AttrValue {
2342 if is_relevant_attribute(namespace, local_name) {
2343 vtable_for(self.upcast()).parse_plain_attribute(local_name, value)
2344 } else {
2345 AttrValue::String(value.into())
2346 }
2347 }
2348
2349 pub(crate) fn remove_attribute(
2350 &self,
2351 cx: &mut JSContext,
2352 namespace: &Namespace,
2353 local_name: &LocalName,
2354 ) -> Option<DomRoot<Attr>> {
2355 self.remove_first_matching_attribute(cx, |attr| {
2356 attr.namespace() == namespace && attr.local_name() == local_name
2357 })
2358 }
2359
2360 pub(crate) fn remove_attribute_by_name(
2361 &self,
2362 cx: &mut JSContext,
2363 name: &LocalName,
2364 ) -> Option<DomRoot<Attr>> {
2365 self.remove_first_matching_attribute(cx, |attr| attr.name() == name)
2366 }
2367
2368 fn remove_first_matching_attribute<F>(
2370 &self,
2371 cx: &mut JSContext,
2372 find: F,
2373 ) -> Option<DomRoot<Attr>>
2374 where
2375 F: Fn(AttrRef<'_>) -> bool,
2376 {
2377 let idx = self.attrs.borrow().iter().position(find);
2378 idx.map(|idx| {
2379 let attr = self.attrs.ensure_dom(cx, idx, self);
2380
2381 self.will_mutate_attr(AttrRef::Dom(&attr));
2383 self.attrs.remove(idx);
2384 attr.set_owner(cx, None);
2386 self.handle_attribute_changes(cx, AttrRef::Dom(&attr), Some(&attr.value()), None);
2388
2389 attr
2390 })
2391 }
2392
2393 pub(crate) fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2394 self.any_tokenlist_attribute(&local_name!("class"), |atom| {
2395 case_sensitivity.eq_atom(name, atom)
2396 })
2397 }
2398
2399 pub(crate) fn has_attribute(&self, local_name: &LocalName) -> bool {
2400 debug_assert_eq!(
2401 *local_name,
2402 local_name.to_ascii_lowercase(),
2403 "All attribute accesses should use a lowercase ASCII name"
2404 );
2405 debug_assert!(!local_name.contains(':'));
2406 self.attrs
2407 .borrow()
2408 .iter()
2409 .any(|attr| attr.local_name() == local_name && attr.namespace() == &ns!())
2410 }
2411
2412 pub(crate) fn will_mutate_attr(&self, attr: AttrRef<'_>) {
2413 let node = self.upcast::<Node>();
2414 node.owner_doc().element_attr_will_change(self, attr);
2415 }
2416
2417 fn update_style_attribute(
2419 &self,
2420 cx: &mut JSContext,
2421 attr: AttrRef<'_>,
2422 mutation: AttributeMutation,
2423 ) {
2424 let doc = self.upcast::<Node>().owner_doc();
2425 *self.style_attribute.borrow_mut() = match mutation {
2427 AttributeMutation::Set(_) => {
2428 let value = attr.as_attr().map_or_else(
2429 || attr.value(),
2430 |attribute| AttrValueRef::Borrowed(attribute.value()),
2431 );
2432
2433 Some(match &*value {
2434 AttrValue::Declaration { block, .. } => block.clone(),
2435 _ => {
2436 let win = self.owner_window();
2437 let source = &**attr.value();
2438 let global = &self.owner_global();
2439 if global
2444 .get_csp_list()
2445 .should_elements_inline_type_behavior_be_blocked(
2446 cx,
2447 global,
2448 self,
2449 InlineCheckType::StyleAttribute,
2450 source,
2451 doc.get_current_parser_line(),
2452 )
2453 {
2454 return;
2455 }
2456 ServoArc::new(doc.style_shared_author_lock().wrap(parse_style_attribute(
2457 source,
2458 &UrlExtraData(doc.base_url().get_arc()),
2459 Some(win.css_error_reporter()),
2460 doc.quirks_mode(),
2461 CssRuleType::Style,
2462 )))
2463 },
2464 })
2465 },
2466 AttributeMutation::Removed => None,
2467 };
2468 }
2469
2470 fn set_attribute_node(
2474 &self,
2475 cx: &mut JSContext,
2476 attr: &Attr,
2477 ) -> Fallible<Option<DomRoot<Attr>>> {
2478 let verified_value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
2482 cx,
2483 self.namespace(),
2484 self.local_name(),
2485 attr.local_name(),
2486 Some(attr.namespace()),
2487 TrustedTypeOrString::String(attr.Value()),
2488 &self.owner_global(),
2489 )?;
2490
2491 if let Some(owner) = attr.GetOwnerElement() &&
2494 &*owner != self
2495 {
2496 return Err(Error::InUseAttribute(None));
2497 }
2498
2499 let vtable = vtable_for(self.upcast());
2500
2501 attr.swap_value(
2507 &mut vtable.parse_plain_attribute(attr.local_name(), verified_value.clone()),
2508 );
2509
2510 let position = self.attrs.borrow().iter().position(|old_attr| {
2512 attr.namespace() == old_attr.namespace() && attr.local_name() == old_attr.local_name()
2513 });
2514
2515 let old_attr = if let Some(position) = position {
2516 let old_attr = self.attrs.ensure_dom(cx, position, self);
2517
2518 if &*old_attr == attr {
2520 return Ok(Some(DomRoot::from_ref(attr)));
2521 }
2522
2523 self.will_mutate_attr(AttrRef::Dom(attr));
2533 self.attrs
2534 .set(position, AttributeEntry::Dom(Dom::from_ref(attr)));
2535 attr.set_owner(cx, Some(self));
2537 attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2539 old_attr.set_owner(cx, None);
2541 self.handle_attribute_changes(
2543 cx,
2544 AttrRef::Dom(attr),
2545 Some(&old_attr.value()),
2546 Some(&AttrValue::String(verified_value.into())),
2547 );
2548
2549 Some(old_attr)
2550 } else {
2551 attr.set_owner(cx, Some(self));
2553 attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2554 self.push_attribute(cx, attr);
2555
2556 None
2557 };
2558
2559 Ok(old_attr)
2561 }
2562
2563 pub(crate) fn update_nonce_internal_slot(&self, nonce: String, no_gc: &NoGC) {
2565 self.ensure_rare_data(no_gc).cryptographic_nonce = nonce;
2566 }
2567
2568 pub(crate) fn nonce_value(&self) -> String {
2570 match self.rare_data().as_ref() {
2571 None => String::new(),
2572 Some(rare_data) => rare_data.cryptographic_nonce.clone(),
2573 }
2574 }
2575
2576 pub(crate) fn update_nonce_post_connection(&self, cx: &mut JSContext) {
2578 if !self.upcast::<Node>().is_connected_with_browsing_context() {
2581 return;
2582 }
2583 let global = self.owner_global();
2584 let csp_list = match global.get_csp_list() {
2586 None => return,
2587 Some(csp_list) => csp_list,
2588 };
2589 if !csp_list.contains_a_header_delivered_content_security_policy() ||
2592 self.get_string_attribute(&local_name!("nonce")).is_empty()
2593 {
2594 return;
2595 }
2596 let nonce = self.nonce_value();
2598 self.set_string_attribute(cx, &local_name!("nonce"), DOMString::new());
2600 self.update_nonce_internal_slot(nonce, cx.no_gc());
2602 }
2603
2604 pub(crate) fn is_nonceable(&self) -> bool {
2606 if !self.has_attribute(&local_name!("nonce")) {
2608 return false;
2609 }
2610 if self.is::<HTMLScriptElement>() {
2612 for attr in self.attrs().borrow().iter() {
2613 let attr_name = attr.name().to_ascii_lowercase();
2618 if attr_name.contains("<script") || attr_name.contains("<style") {
2619 return false;
2620 }
2621 let attr_value = attr.value();
2624 let attr_value = string_as_ascii_lowercase(&attr_value);
2625 if attr_value.contains("<script") || attr_value.contains("<style") {
2626 return false;
2627 }
2628 }
2629 }
2630 if self
2632 .rare_data()
2633 .as_ref()
2634 .is_some_and(|d| d.had_duplicate_attributes)
2635 {
2636 return false;
2637 }
2638 true
2640 }
2641
2642 pub(crate) fn insert_adjacent(
2644 &self,
2645 cx: &mut JSContext,
2646 where_: AdjacentPosition,
2647 node: &Node,
2648 ) -> Fallible<Option<DomRoot<Node>>> {
2649 let self_node = self.upcast::<Node>();
2650 match where_ {
2651 AdjacentPosition::BeforeBegin => {
2652 if let Some(parent) = self_node.GetParentNode() {
2653 Node::pre_insert(cx, node, &parent, Some(self_node)).map(Some)
2654 } else {
2655 Ok(None)
2656 }
2657 },
2658 AdjacentPosition::AfterBegin => {
2659 Node::pre_insert(cx, node, self_node, self_node.GetFirstChild().as_deref())
2660 .map(Some)
2661 },
2662 AdjacentPosition::BeforeEnd => Node::pre_insert(cx, node, self_node, None).map(Some),
2663 AdjacentPosition::AfterEnd => {
2664 if let Some(parent) = self_node.GetParentNode() {
2665 Node::pre_insert(cx, node, &parent, self_node.GetNextSibling().as_deref())
2666 .map(Some)
2667 } else {
2668 Ok(None)
2669 }
2670 },
2671 }
2672 }
2673
2674 pub(crate) fn scroll(&self, cx: &mut JSContext, x: f64, y: f64, behavior: ScrollBehavior) {
2676 let x = if x.is_finite() { x } else { 0.0 } as f32;
2681 let y = if y.is_finite() { y } else { 0.0 } as f32;
2682
2683 let node = self.upcast::<Node>();
2684
2685 let document = node.owner_doc();
2687
2688 if !document.is_fully_active() {
2692 return;
2693 }
2694
2695 let Some(window) = document.GetDefaultView() else {
2697 return;
2701 };
2702
2703 if *self.root_element() == *self {
2708 if document.quirks_mode() == QuirksMode::Quirks {
2709 return;
2710 }
2711
2712 window.scroll(cx, x, y, behavior);
2717 return;
2718 }
2719
2720 if document.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
2726 document.quirks_mode() == QuirksMode::Quirks &&
2727 !self.is_potentially_scrollable_body()
2728 {
2729 window.scroll(cx, x, y, behavior);
2730 return;
2731 }
2732
2733 if !self.has_scrolling_box(cx.no_gc()) {
2741 return;
2742 }
2743
2744 window.scroll_an_element(cx, self, x, y, behavior);
2749
2750 }
2753
2754 pub(crate) fn parse_fragment(
2756 &self,
2757 markup: DOMString,
2758 cx: &mut JSContext,
2759 ) -> Fallible<DomRoot<DocumentFragment>> {
2760 let new_children = ServoParser::parse_html_fragment(cx, self, markup, false);
2763 let context_document = {
2766 if let Some(template) = self.downcast::<HTMLTemplateElement>() {
2767 template.Content(cx).upcast::<Node>().owner_doc()
2768 } else {
2769 self.owner_document()
2770 }
2771 };
2772 let fragment = DocumentFragment::new(cx, &context_document);
2773 for child in new_children {
2775 fragment.upcast::<Node>().AppendChild(cx, &child).unwrap();
2776 }
2777 Ok(fragment)
2779 }
2780
2781 pub(crate) fn fragment_parsing_context(
2784 cx: &mut JSContext,
2785 owner_doc: &Document,
2786 element: Option<&Self>,
2787 ) -> DomRoot<Self> {
2788 match element {
2790 Some(elem)
2791 if elem.local_name() != &local_name!("html") ||
2795 !elem.html_element_in_html_document() =>
2796 {
2797 DomRoot::from_ref(elem)
2798 },
2799 _ => Element::create(
2802 cx,
2803 QualName::new(None, ns!(html), local_name!("body")),
2804 None,
2805 owner_doc,
2806 ElementCreator::ScriptCreated,
2807 CustomElementCreationMode::Asynchronous,
2808 None
2809 ),
2810 }
2811 }
2812
2813 pub(crate) fn is_in_same_home_subtree<T>(&self, other: &T) -> bool
2815 where
2816 T: DerivedFrom<Element> + DomObject,
2817 {
2818 let other = other.upcast::<Element>();
2819 self.root_element() == other.root_element()
2820 }
2821
2822 pub(crate) fn get_id(&self) -> Option<Atom> {
2823 self.id_attribute.borrow().clone()
2824 }
2825
2826 pub(crate) fn get_name(&self) -> Option<Atom> {
2827 self.rare_data().as_ref()?.name_attribute.clone()
2828 }
2829
2830 pub(crate) fn get_element_internals(&self) -> Option<DomRoot<ElementInternals>> {
2831 self.rare_data()
2832 .as_ref()?
2833 .element_internals
2834 .as_ref()
2835 .map(|sr| DomRoot::from_ref(&**sr))
2836 }
2837
2838 pub(crate) fn outer_html(&self, cx: &mut JSContext) -> Fallible<DOMString> {
2839 match self.GetOuterHTML(cx)? {
2840 TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => Ok(str),
2841 TrustedHTMLOrNullIsEmptyString::TrustedHTML(_) => unreachable!(),
2842 }
2843 }
2844
2845 pub(crate) fn compute_source_position(&self, line_number: u32) -> SourcePosition {
2846 SourcePosition {
2847 source_file: self.owner_global().get_url().to_string(),
2848 line_number: line_number + 2,
2849 column_number: 0,
2850 }
2851 }
2852
2853 pub(crate) fn explicitly_set_tab_index(&self) -> Option<i32> {
2854 if self.has_attribute(&local_name!("tabindex")) {
2855 Some(self.get_int_attribute(&local_name!("tabindex"), 0))
2856 } else {
2857 None
2858 }
2859 }
2860
2861 pub(crate) fn tab_index(&self) -> i32 {
2863 if let Some(tab_index) = self.explicitly_set_tab_index() {
2869 return tab_index;
2870 }
2871
2872 if matches!(
2878 self.upcast::<Node>().type_id(),
2879 NodeTypeId::Element(ElementTypeId::HTMLElement(
2880 HTMLElementTypeId::HTMLAnchorElement |
2881 HTMLElementTypeId::HTMLAreaElement |
2882 HTMLElementTypeId::HTMLButtonElement |
2883 HTMLElementTypeId::HTMLFrameElement |
2884 HTMLElementTypeId::HTMLIFrameElement |
2885 HTMLElementTypeId::HTMLInputElement |
2886 HTMLElementTypeId::HTMLObjectElement |
2887 HTMLElementTypeId::HTMLSelectElement |
2888 HTMLElementTypeId::HTMLTextAreaElement
2889 ))
2890 ) {
2891 return 0;
2892 }
2893 if self
2894 .downcast::<HTMLElement>()
2895 .is_some_and(|html_element| html_element.is_a_summary_for_its_parent_details())
2896 {
2897 return 0;
2898 }
2899
2900 -1
2901 }
2902
2903 #[inline]
2904 fn insert_selector_flags(&self, flags: ElementSelectorFlags) {
2905 self.selector_flags
2906 .fetch_or(flags.bits(), Ordering::Relaxed);
2907 }
2908
2909 #[inline]
2910 fn get_selector_flags(&self) -> ElementSelectorFlags {
2911 ElementSelectorFlags::from_bits_retain(self.selector_flags.load(Ordering::Relaxed))
2912 }
2913
2914 pub(crate) fn needs_preserved_style_attribute_after_change(&self) -> bool {
2919 self.owner_window().get_exists_mut_observer() ||
2926 self.get_custom_element_definition()
2927 .is_some_and(|custom_element_definition| {
2928 custom_element_definition.has_attribute_changed_callback()
2929 })
2930 }
2931
2932 pub(crate) fn register_current_id_and_name_attribute(&self, cx: &mut JSContext) {
2933 if let Some(shadow_root) = self.containing_shadow_root() {
2934 if let Some(ref id) = *self.id_attribute.borrow() {
2935 shadow_root.register_element_id(self, id);
2936 }
2937 } else {
2938 let document = self.owner_document();
2939 if let Some(ref id) = *self.id_attribute.borrow() {
2940 document.register_element_id(cx, self, id);
2941 }
2942 if let Some(ref name) = self.name_attribute() {
2943 document.register_element_name(self, name);
2944 }
2945 }
2946 }
2947
2948 pub(crate) fn unregister_current_id_and_name_attribute(&self, cx: &mut JSContext) {
2949 if let Some(shadow_root) = self.containing_shadow_root() {
2950 if self.upcast::<Node>().is_in_a_shadow_tree() {
2954 return;
2955 }
2956 if let Some(ref id) = *self.id_attribute.borrow() {
2957 shadow_root.unregister_element_id(id);
2958 }
2959 } else {
2960 let document = self.owner_document();
2961 if let Some(ref id) = *self.id_attribute.borrow() {
2962 document.unregister_element_id(cx, id);
2963 }
2964 if let Some(ref name) = self.name_attribute() {
2965 document.unregister_element_name(name);
2966 }
2967 }
2968 }
2969
2970 pub(crate) fn qualified_name(&self) -> Cow<'_, str> {
2974 match &*self.prefix.borrow() {
2975 Some(prefix) => Cow::Owned(format!("{}:{}", prefix, &*self.local_name)),
2976 None => Cow::Borrowed(&*self.local_name),
2977 }
2978 }
2979}
2980
2981impl ElementMethods<crate::DomTypeHolder> for Element {
2982 fn GetNamespaceURI(&self) -> Option<DOMString> {
2984 Node::namespace_to_string(self.namespace.clone())
2985 }
2986
2987 fn LocalName(&self) -> DOMString {
2989 DOMString::from(&*self.local_name)
2991 }
2992
2993 fn GetPrefix(&self) -> Option<DOMString> {
2995 self.prefix.borrow().as_ref().map(|p| DOMString::from(&**p))
2996 }
2997
2998 fn TagName(&self) -> DOMString {
3000 let name = self.tag_name.or_init(|| {
3004 let qualified_name = self.qualified_name();
3006 if self.html_element_in_html_document() {
3010 LocalName::from(qualified_name.to_ascii_uppercase())
3011 } else {
3012 LocalName::from(qualified_name)
3013 }
3014 });
3015 DOMString::from(&*name)
3016 }
3017
3018 fn Id(&self) -> DOMString {
3022 self.get_string_attribute(&local_name!("id"))
3023 }
3024
3025 fn SetId(&self, cx: &mut JSContext, id: DOMString) {
3027 self.set_atomic_attribute(cx, &local_name!("id"), id);
3028 }
3029
3030 fn ClassName(&self) -> DOMString {
3032 self.get_string_attribute(&local_name!("class"))
3033 }
3034
3035 fn SetClassName(&self, cx: &mut JSContext, class: DOMString) {
3037 self.set_tokenlist_attribute(cx, &local_name!("class"), class);
3038 }
3039
3040 fn ClassList(&self, cx: &mut js::context::JSContext) -> DomRoot<DOMTokenList> {
3042 self.class_list
3043 .or_init(|| DOMTokenList::new(cx, self, &local_name!("class"), None))
3044 }
3045
3046 make_getter!(Slot, "slot");
3048
3049 make_setter!(SetSlot, "slot");
3051
3052 fn Attributes(&self, cx: &mut JSContext) -> DomRoot<NamedNodeMap> {
3054 self.attr_list
3055 .or_init(|| NamedNodeMap::new(cx, &self.owner_window(), self))
3056 }
3057
3058 fn HasAttributes(&self) -> bool {
3060 !self.attrs.borrow().is_empty()
3061 }
3062
3063 fn GetAttributeNames(&self) -> Vec<DOMString> {
3065 self.attrs
3066 .borrow()
3067 .iter()
3068 .map(|attr| DOMString::from(&**attr.name()))
3069 .collect()
3070 }
3071
3072 fn GetAttribute(&self, cx: &mut JSContext, name: DOMString) -> Option<DOMString> {
3074 self.GetAttributeNode(cx, name).map(|s| s.Value())
3075 }
3076
3077 fn GetAttributeNS(
3079 &self,
3080 cx: &mut JSContext,
3081 namespace: Option<DOMString>,
3082 local_name: DOMString,
3083 ) -> Option<DOMString> {
3084 self.GetAttributeNodeNS(cx, namespace, local_name)
3085 .map(|attr| attr.Value())
3086 }
3087
3088 fn GetAttributeNode(&self, cx: &mut JSContext, name: DOMString) -> Option<DomRoot<Attr>> {
3090 self.get_attribute_by_name(cx, name)
3091 }
3092
3093 fn GetAttributeNodeNS(
3095 &self,
3096 cx: &mut JSContext,
3097 namespace: Option<DOMString>,
3098 local_name: DOMString,
3099 ) -> Option<DomRoot<Attr>> {
3100 let namespace = &namespace_from_domstring(namespace);
3101 self.get_attribute_with_namespace(cx, namespace, &LocalName::from(local_name))
3102 }
3103
3104 fn ToggleAttribute(
3106 &self,
3107 cx: &mut JSContext,
3108 name: DOMString,
3109 force: Option<bool>,
3110 ) -> Fallible<bool> {
3111 if !is_valid_attribute_local_name(&name.str()) {
3114 return Err(Error::InvalidCharacter(None));
3115 }
3116
3117 let attribute = self.GetAttribute(cx, name.clone());
3119
3120 let name = self.parsed_name(name);
3122 match attribute {
3123 None => match force {
3125 None | Some(true) => {
3127 self.set_first_matching_attribute(
3128 cx,
3129 name.clone(),
3130 AttrValue::String(String::new()),
3131 name.clone(),
3132 ns!(),
3133 None,
3134 |attr| *attr.name() == name,
3135 );
3136 Ok(true)
3137 },
3138 Some(false) => Ok(false),
3140 },
3141 Some(_index) => match force {
3142 None | Some(false) => {
3144 self.remove_attribute_by_name(cx, &name);
3145 Ok(false)
3146 },
3147 Some(true) => Ok(true),
3149 },
3150 }
3151 }
3152
3153 fn SetAttribute(
3155 &self,
3156 cx: &mut JSContext,
3157 name: DOMString,
3158 value: TrustedTypeOrString,
3159 ) -> ErrorResult {
3160 if !is_valid_attribute_local_name(&name.str()) {
3163 return Err(Error::InvalidCharacter(None));
3164 }
3165
3166 let name = self.parsed_name(name);
3169
3170 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3174 cx,
3175 self.namespace(),
3176 self.local_name(),
3177 &name,
3178 None,
3179 value,
3180 &self.owner_global(),
3181 )?;
3182
3183 let value = self.parse_attribute(&ns!(), &name, value);
3188 self.set_first_matching_attribute(
3189 cx,
3190 name.clone(),
3191 value,
3192 name.clone(),
3193 ns!(),
3194 None,
3195 |attr| *attr.name() == name,
3196 );
3197 Ok(())
3198 }
3199
3200 fn SetAttributeNS(
3202 &self,
3203 cx: &mut JSContext,
3204 namespace: Option<DOMString>,
3205 qualified_name: DOMString,
3206 value: TrustedTypeOrString,
3207 ) -> ErrorResult {
3208 let (namespace, prefix, local_name) =
3210 domname::validate_and_extract(namespace, &qualified_name, domname::Context::Element)?;
3211 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3214 cx,
3215 self.namespace(),
3216 self.local_name(),
3217 &local_name,
3218 Some(&namespace),
3219 value,
3220 &self.owner_global(),
3221 )?;
3222 let value = self.parse_attribute(&namespace, &local_name, value);
3224 self.set_attribute_with_namespace(
3225 cx,
3226 local_name,
3227 value,
3228 LocalName::from(qualified_name),
3229 namespace,
3230 prefix,
3231 );
3232 Ok(())
3233 }
3234
3235 fn SetAttributeNode(&self, cx: &mut JSContext, attr: &Attr) -> Fallible<Option<DomRoot<Attr>>> {
3237 self.set_attribute_node(cx, attr)
3238 }
3239
3240 fn SetAttributeNodeNS(
3242 &self,
3243 cx: &mut JSContext,
3244 attr: &Attr,
3245 ) -> Fallible<Option<DomRoot<Attr>>> {
3246 self.set_attribute_node(cx, attr)
3247 }
3248
3249 fn RemoveAttribute(&self, cx: &mut JSContext, name: DOMString) {
3251 let name = self.parsed_name(name);
3252 self.remove_attribute_by_name(cx, &name);
3253 }
3254
3255 fn RemoveAttributeNS(
3257 &self,
3258 cx: &mut JSContext,
3259 namespace: Option<DOMString>,
3260 local_name: DOMString,
3261 ) {
3262 let namespace = namespace_from_domstring(namespace);
3263 let local_name = LocalName::from(local_name);
3264 self.remove_attribute(cx, &namespace, &local_name);
3265 }
3266
3267 fn RemoveAttributeNode(&self, cx: &mut JSContext, attr: &Attr) -> Fallible<DomRoot<Attr>> {
3269 self.remove_first_matching_attribute(cx, |a| a.as_attr().is_some_and(|a| a == attr))
3272 .ok_or(Error::NotFound(None))
3273 }
3274
3275 fn HasAttribute(&self, cx: &mut JSContext, name: DOMString) -> bool {
3277 self.GetAttribute(cx, name).is_some()
3278 }
3279
3280 fn HasAttributeNS(
3282 &self,
3283 cx: &mut JSContext,
3284 namespace: Option<DOMString>,
3285 local_name: DOMString,
3286 ) -> bool {
3287 self.GetAttributeNS(cx, namespace, local_name).is_some()
3288 }
3289
3290 fn GetElementsByTagName(
3292 &self,
3293 cx: &mut JSContext,
3294 localname: DOMString,
3295 ) -> DomRoot<HTMLCollection> {
3296 let window = self.owner_window();
3297 HTMLCollection::by_qualified_name(cx, &window, self.upcast(), LocalName::from(localname))
3298 }
3299
3300 fn GetElementsByTagNameNS(
3302 &self,
3303 cx: &mut JSContext,
3304 maybe_ns: Option<DOMString>,
3305 localname: DOMString,
3306 ) -> DomRoot<HTMLCollection> {
3307 let window = self.owner_window();
3308 HTMLCollection::by_tag_name_ns(cx, &window, self.upcast(), localname, maybe_ns)
3309 }
3310
3311 fn GetElementsByClassName(
3313 &self,
3314 cx: &mut JSContext,
3315 classes: DOMString,
3316 ) -> DomRoot<HTMLCollection> {
3317 let window = self.owner_window();
3318 HTMLCollection::by_class_name(cx, &window, self.upcast(), classes)
3319 }
3320
3321 fn GetClientRects(&self, cx: &mut JSContext) -> DomRoot<DOMRectList> {
3323 let win = self.owner_window();
3324 let raw_rects = self.upcast::<Node>().border_boxes();
3325 let rects: Vec<DomRoot<DOMRect>> = raw_rects
3326 .into_iter()
3327 .map(|rect| {
3328 DOMRect::new(
3329 cx,
3330 win.upcast(),
3331 rect.origin.x.to_f64_px(),
3332 rect.origin.y.to_f64_px(),
3333 rect.size.width.to_f64_px(),
3334 rect.size.height.to_f64_px(),
3335 )
3336 })
3337 .collect();
3338 DOMRectList::new(cx, &win, rects)
3339 }
3340
3341 fn GetBoundingClientRect(&self, cx: &mut JSContext) -> DomRoot<DOMRect> {
3343 let win = self.owner_window();
3344 let rect = self.upcast::<Node>().border_box().unwrap_or_default();
3345 debug_assert!(rect.size.width.to_f64_px() >= 0.0 && rect.size.height.to_f64_px() >= 0.0);
3346 DOMRect::new(
3347 cx,
3348 win.upcast(),
3349 rect.origin.x.to_f64_px(),
3350 rect.origin.y.to_f64_px(),
3351 rect.size.width.to_f64_px(),
3352 rect.size.height.to_f64_px(),
3353 )
3354 }
3355
3356 fn Scroll(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3358 let left = options.left.unwrap_or(self.ScrollLeft());
3360 let top = options.top.unwrap_or(self.ScrollTop());
3361 self.scroll(cx, left, top, options.parent.behavior);
3362 }
3363
3364 fn Scroll_(&self, cx: &mut JSContext, x: f64, y: f64) {
3366 self.scroll(cx, x, y, ScrollBehavior::Auto);
3367 }
3368
3369 fn ScrollTo(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3371 self.Scroll(cx, options);
3372 }
3373
3374 fn ScrollTo_(&self, cx: &mut JSContext, x: f64, y: f64) {
3376 self.Scroll_(cx, x, y);
3377 }
3378
3379 fn ScrollBy(&self, cx: &mut JSContext, options: &ScrollToOptions) {
3381 let delta_left = options.left.unwrap_or(0.0f64);
3383 let delta_top = options.top.unwrap_or(0.0f64);
3384 let left = self.ScrollLeft();
3385 let top = self.ScrollTop();
3386 self.scroll(
3387 cx,
3388 left + delta_left,
3389 top + delta_top,
3390 options.parent.behavior,
3391 );
3392 }
3393
3394 fn ScrollBy_(&self, cx: &mut JSContext, x: f64, y: f64) {
3396 let left = self.ScrollLeft();
3397 let top = self.ScrollTop();
3398 self.scroll(cx, left + x, top + y, ScrollBehavior::Auto);
3399 }
3400
3401 fn ScrollTop(&self) -> f64 {
3403 let node = self.upcast::<Node>();
3404
3405 let document = node.owner_doc();
3407
3408 if !document.is_fully_active() {
3411 return 0.0;
3412 }
3413
3414 let Some(window) = document.GetDefaultView() else {
3416 return 0.0;
3418 };
3419
3420 if self.is_document_element() {
3421 if document.quirks_mode() == QuirksMode::Quirks {
3424 return 0.0;
3425 }
3426
3427 return window.ScrollY() as f64;
3430 }
3431
3432 if document.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3436 document.quirks_mode() == QuirksMode::Quirks &&
3437 !self.is_potentially_scrollable_body()
3438 {
3439 return window.ScrollY() as f64;
3440 }
3441
3442 if !self.has_css_layout_box() {
3445 return 0.0;
3446 }
3447
3448 let point = window.scroll_offset_query(node);
3451 point.y.abs() as f64
3452 }
3453
3454 fn SetScrollTop(&self, cx: &mut JSContext, y_: f64) {
3456 let behavior = ScrollBehavior::Auto;
3457
3458 let y = if y_.is_finite() { y_ } else { 0.0 } as f32;
3461
3462 let node = self.upcast::<Node>();
3463
3464 let document = node.owner_doc();
3466
3467 if !document.is_fully_active() {
3469 return;
3470 }
3471
3472 let Some(window) = document.GetDefaultView() else {
3474 return;
3476 };
3477
3478 if self.is_document_element() {
3479 if document.quirks_mode() == QuirksMode::Quirks {
3482 return;
3483 }
3484
3485 window.scroll(cx, window.ScrollX() as f32, y, behavior);
3489 return;
3490 }
3491
3492 if document.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3497 document.quirks_mode() == QuirksMode::Quirks &&
3498 !self.is_potentially_scrollable_body()
3499 {
3500 window.scroll(cx, window.ScrollX() as f32, y, behavior);
3501 return;
3502 }
3503
3504 if !self.has_scrolling_box(cx.no_gc()) {
3508 return;
3509 }
3510
3511 window.scroll_an_element(cx, self, self.ScrollLeft() as f32, y, behavior);
3514 }
3515
3516 fn ScrollLeft(&self) -> f64 {
3518 let node = self.upcast::<Node>();
3519
3520 let document = node.owner_doc();
3522
3523 if !document.is_fully_active() {
3526 return 0.0;
3527 }
3528
3529 let Some(window) = document.GetDefaultView() else {
3532 return 0.0;
3533 };
3534
3535 if self.is_document_element() {
3536 if document.quirks_mode() == QuirksMode::Quirks {
3539 return 0.0;
3540 }
3541
3542 return window.ScrollX() as f64;
3544 }
3545
3546 if document.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3550 document.quirks_mode() == QuirksMode::Quirks &&
3551 !self.is_potentially_scrollable_body()
3552 {
3553 return window.ScrollX() as f64;
3554 }
3555
3556 if !self.has_css_layout_box() {
3559 return 0.0;
3560 }
3561
3562 let point = window.scroll_offset_query(node);
3565 point.x.abs() as f64
3566 }
3567
3568 fn SetScrollLeft(&self, cx: &mut JSContext, x: f64) {
3570 let behavior = ScrollBehavior::Auto;
3571
3572 let x = if x.is_finite() { x } else { 0.0 } as f32;
3575
3576 let node = self.upcast::<Node>();
3578 let document = node.owner_doc();
3579
3580 if !document.is_fully_active() {
3582 return;
3583 }
3584
3585 let Some(window) = document.GetDefaultView() else {
3587 return;
3589 };
3590
3591 if self.is_document_element() {
3592 if document.quirks_mode() == QuirksMode::Quirks {
3595 return;
3596 }
3597
3598 window.scroll(cx, x, window.ScrollY() as f32, behavior);
3602 return;
3603 }
3604
3605 if document.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
3610 document.quirks_mode() == QuirksMode::Quirks &&
3611 !self.is_potentially_scrollable_body()
3612 {
3613 window.scroll(cx, x, window.ScrollY() as f32, behavior);
3614 return;
3615 }
3616
3617 if !self.has_scrolling_box(cx.no_gc()) {
3621 return;
3622 }
3623
3624 window.scroll_an_element(cx, self, x, self.ScrollTop() as f32, behavior);
3627 }
3628
3629 fn ScrollIntoView(&self, cx: &mut JSContext, arg: BooleanOrScrollIntoViewOptions) {
3631 let (behavior, block, inline, container) = match arg {
3632 BooleanOrScrollIntoViewOptions::Boolean(true) => (
3634 ScrollBehavior::Auto, ScrollLogicalPosition::Start, ScrollLogicalPosition::Nearest, None, ),
3639 BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(options) => (
3642 options.parent.behavior,
3643 options.block,
3644 options.inline,
3645 if options.container == ScrollIntoViewContainer::Nearest {
3648 Some(self)
3649 } else {
3650 None
3651 },
3652 ),
3653 BooleanOrScrollIntoViewOptions::Boolean(false) => (
3655 ScrollBehavior::Auto,
3656 ScrollLogicalPosition::End,
3657 ScrollLogicalPosition::Nearest,
3658 None,
3659 ),
3660 };
3661
3662 if !self.has_css_layout_box() {
3665 return;
3666 }
3667
3668 self.scroll_into_view_with_options(
3670 cx,
3671 behavior,
3672 ScrollAxisState::new_always_scroll_position(block),
3673 ScrollAxisState::new_always_scroll_position(inline),
3674 container,
3675 None,
3676 );
3677
3678 }
3681
3682 fn ScrollWidth(&self) -> i32 {
3684 self.upcast::<Node>().scroll_area().size.width
3685 }
3686
3687 fn ScrollHeight(&self) -> i32 {
3689 self.upcast::<Node>().scroll_area().size.height
3690 }
3691
3692 fn ClientTop(&self, no_gc: &NoGC) -> i32 {
3694 self.client_rect(no_gc).origin.y
3695 }
3696
3697 fn ClientLeft(&self, no_gc: &NoGC) -> i32 {
3699 self.client_rect(no_gc).origin.x
3700 }
3701
3702 fn ClientWidth(&self, no_gc: &NoGC) -> i32 {
3704 self.client_rect(no_gc).size.width
3705 }
3706
3707 fn ClientHeight(&self, no_gc: &NoGC) -> i32 {
3709 self.client_rect(no_gc).size.height
3710 }
3711
3712 fn CurrentCSSZoom(&self) -> Finite<f64> {
3714 let window = self.owner_window();
3715 Finite::wrap(window.current_css_zoom_query(self.upcast::<Node>()) as f64)
3716 }
3717
3718 fn SetHTMLUnsafe(
3720 &self,
3721 cx: &mut JSContext,
3722 html: TrustedHTMLOrString,
3723 options: &SetHTMLUnsafeOptions,
3724 ) -> ErrorResult {
3725 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
3729 cx,
3730 &self.owner_global(),
3731 html,
3732 "Element setHTMLUnsafe",
3733 )?;
3734 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3736 DomRoot::upcast(template.Content(cx))
3737 } else {
3738 DomRoot::from_ref(self.upcast())
3739 };
3740
3741 Sanitizer::set_and_filter_html(cx, &target, self, compliant_html, options, false)?;
3743
3744 Ok(())
3745 }
3746
3747 fn SetHTML(
3749 &self,
3750 cx: &mut JSContext,
3751 html: DOMString,
3752 options: &SetHTMLOptions,
3753 ) -> ErrorResult {
3754 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3756 DomRoot::upcast(template.Content(cx))
3757 } else {
3758 DomRoot::from_ref(self.upcast())
3759 };
3760
3761 Sanitizer::set_and_filter_html(cx, &target, self, html, options, true)
3763 }
3764
3765 fn GetHTML(&self, cx: &mut JSContext, options: &GetHTMLOptions) -> DOMString {
3767 self.upcast::<Node>().html_serialize(
3770 cx,
3771 TraversalScope::ChildrenOnly(None),
3772 options.serializableShadowRoots,
3773 options.shadowRoots.clone(),
3774 )
3775 }
3776
3777 fn GetInnerHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3779 let qname = QualName::new(
3780 self.prefix().clone(),
3781 self.namespace().clone(),
3782 self.local_name().clone(),
3783 );
3784
3785 let result = if self.owner_document().is_html_document() {
3788 self.upcast::<Node>()
3789 .html_serialize(cx, ChildrenOnly(Some(qname)), false, vec![])
3790 } else {
3791 self.upcast::<Node>()
3792 .xml_serialize(XmlChildrenOnly(Some(qname)))?
3793 };
3794
3795 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3796 }
3797
3798 fn SetInnerHTML(
3800 &self,
3801 cx: &mut JSContext,
3802 value: TrustedHTMLOrNullIsEmptyString,
3803 ) -> ErrorResult {
3804 let value = TrustedHTML::get_trusted_type_compliant_string(
3808 cx,
3809 &self.owner_global(),
3810 value.convert(),
3811 "Element innerHTML",
3812 )?;
3813 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
3815 DomRoot::upcast(template.Content(cx))
3818 } else {
3819 DomRoot::from_ref(self.upcast())
3821 };
3822
3823 if !self.node.has_weird_parser_insertion_mode() &&
3826 value.len_utf8_or_latin1() < 100 &&
3827 !value
3828 .as_bytes(cx.no_gc())
3829 .iter()
3830 .any(|c| matches!(*c, b'&' | b'\0' | b'<' | b'\r'))
3831 {
3832 return Node::SetTextContent(&target, cx, Some(value));
3833 }
3834
3835 let frag = self.parse_fragment(value, cx)?;
3838
3839 Node::replace_all(cx, Some(frag.upcast()), &target);
3841 Ok(())
3842 }
3843
3844 fn GetOuterHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
3846 let result = if self.owner_document().is_html_document() {
3849 self.upcast::<Node>()
3850 .html_serialize(cx, IncludeNode, false, vec![])
3851 } else {
3852 self.upcast::<Node>().xml_serialize(XmlIncludeNode)?
3853 };
3854
3855 Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
3856 }
3857
3858 fn SetOuterHTML(
3860 &self,
3861 cx: &mut JSContext,
3862 value: TrustedHTMLOrNullIsEmptyString,
3863 ) -> ErrorResult {
3864 let value = TrustedHTML::get_trusted_type_compliant_string(
3868 cx,
3869 &self.owner_global(),
3870 value.convert(),
3871 "Element outerHTML",
3872 )?;
3873 let context_document = self.owner_document();
3874 let context_node = self.upcast::<Node>();
3875 let context_parent = match context_node.GetParentNode() {
3877 None => {
3878 return Ok(());
3881 },
3882 Some(parent) => parent,
3883 };
3884
3885 let parent = match context_parent.type_id() {
3886 NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed(None)),
3888
3889 NodeTypeId::DocumentFragment(_) => {
3892 let body_elem = Element::create(
3893 cx,
3894 QualName::new(None, ns!(html), local_name!("body")),
3895 None,
3896 &context_document,
3897 ElementCreator::ScriptCreated,
3898 CustomElementCreationMode::Synchronous,
3899 None,
3900 );
3901 DomRoot::upcast(body_elem)
3902 },
3903 _ => context_node.GetParentElement().unwrap(),
3904 };
3905
3906 let frag = parent.parse_fragment(value, cx)?;
3909 context_parent.ReplaceChild(cx, frag.upcast(), context_node)?;
3911 Ok(())
3912 }
3913
3914 fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
3916 self.upcast::<Node>()
3917 .preceding_siblings()
3918 .find_map(DomRoot::downcast)
3919 }
3920
3921 fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
3923 self.upcast::<Node>()
3924 .following_siblings()
3925 .find_map(DomRoot::downcast)
3926 }
3927
3928 fn Children(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
3930 let window = self.owner_window();
3931 HTMLCollection::children(cx, &window, self.upcast())
3932 }
3933
3934 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
3936 self.upcast::<Node>().child_elements().next()
3937 }
3938
3939 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
3941 self.upcast::<Node>()
3942 .rev_children()
3943 .find_map(DomRoot::downcast::<Element>)
3944 }
3945
3946 fn ChildElementCount(&self) -> u32 {
3948 self.upcast::<Node>().child_elements().count() as u32
3949 }
3950
3951 fn Prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3953 self.upcast::<Node>().prepend(cx, nodes)
3954 }
3955
3956 fn Append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3958 self.upcast::<Node>().append(cx, nodes)
3959 }
3960
3961 fn ReplaceChildren(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3963 self.upcast::<Node>().replace_children(cx, nodes)
3964 }
3965
3966 fn MoveBefore(&self, cx: &mut JSContext, node: &Node, child: Option<&Node>) -> ErrorResult {
3968 self.upcast::<Node>().move_before(cx, node, child)
3969 }
3970
3971 fn QuerySelector(
3973 &self,
3974 cx: &mut JSContext,
3975 selectors: DOMString,
3976 ) -> Fallible<Option<DomRoot<Element>>> {
3977 let root = self.upcast::<Node>();
3978 root.query_selector(cx.no_gc(), selectors)
3979 }
3980
3981 fn QuerySelectorAll(
3983 &self,
3984 cx: &mut JSContext,
3985 selectors: DOMString,
3986 ) -> Fallible<DomRoot<NodeList>> {
3987 let root = self.upcast::<Node>();
3988 root.query_selector_all(cx, selectors)
3989 }
3990
3991 fn Before(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3993 self.upcast::<Node>().before(cx, nodes)
3994 }
3995
3996 fn After(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
3998 self.upcast::<Node>().after(cx, nodes)
3999 }
4000
4001 fn ReplaceWith(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
4003 self.upcast::<Node>().replace_with(cx, nodes)
4004 }
4005
4006 fn Remove(&self, cx: &mut JSContext) {
4008 self.upcast::<Node>().remove_self(cx);
4009 }
4010
4011 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
4013 fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
4014 let document = self.owner_document();
4015 let url = document.url();
4016 let selectors = match SelectorParser::parse_author_origin_no_namespace(
4017 &selectors.str(),
4018 &UrlExtraData(url.get_arc()),
4019 ) {
4020 Err(_) => {
4021 return Err(Error::Syntax(
4022 format!("'{selectors}' is not a valid selector").into(),
4023 ));
4024 },
4025 Ok(selectors) => selectors,
4026 };
4027
4028 let traced_self = Dom::from_ref(self);
4030 let quirks_mode = document.quirks_mode();
4031 Ok(with_layout_state(|| {
4032 #[expect(unsafe_code)]
4033 let layout_element: LayoutDom<'_, _> = unsafe { traced_self.to_layout() };
4034 dom_apis::element_matches(
4035 &ServoDangerousStyleElement::from(layout_element.upcast()),
4036 &selectors,
4037 quirks_mode,
4038 )
4039 }))
4040 }
4041
4042 fn WebkitMatchesSelector(&self, selectors: DOMString) -> Fallible<bool> {
4044 self.Matches(selectors)
4045 }
4046
4047 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
4049 fn Closest(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
4050 let document = self.owner_document();
4051 let url = document.url();
4052 let selectors = match SelectorParser::parse_author_origin_no_namespace(
4053 &selectors.str(),
4054 &UrlExtraData(url.get_arc()),
4055 ) {
4056 Err(_) => return Err(Error::Syntax(None)),
4057 Ok(selectors) => selectors,
4058 };
4059
4060 let traced_self = Dom::from_ref(self);
4062 let quirks_mode = document.quirks_mode();
4063 let closest_element = with_layout_state(|| {
4064 #[expect(unsafe_code)]
4065 let layout_element: LayoutDom<'_, _> = unsafe { traced_self.to_layout() };
4066 dom_apis::element_closest(
4067 ServoDangerousStyleElement::from(layout_element.upcast()),
4068 &selectors,
4069 quirks_mode,
4070 )
4071 });
4072 Ok(closest_element.map(ServoDangerousStyleElement::rooted))
4073 }
4074
4075 fn InsertAdjacentElement(
4077 &self,
4078 cx: &mut JSContext,
4079 where_: DOMString,
4080 element: &Element,
4081 ) -> Fallible<Option<DomRoot<Element>>> {
4082 let where_ = where_.parse::<AdjacentPosition>()?;
4083 let inserted_node = self.insert_adjacent(cx, where_, element.upcast())?;
4084 Ok(inserted_node.map(|node| DomRoot::downcast(node).unwrap()))
4085 }
4086
4087 fn InsertAdjacentText(
4089 &self,
4090 cx: &mut JSContext,
4091 where_: DOMString,
4092 data: DOMString,
4093 ) -> ErrorResult {
4094 let text = Text::new(cx, data, &self.owner_document());
4096
4097 let where_ = where_.parse::<AdjacentPosition>()?;
4099 self.insert_adjacent(cx, where_, text.upcast()).map(|_| ())
4100 }
4101
4102 fn InsertAdjacentHTML(
4104 &self,
4105 cx: &mut JSContext,
4106 position: DOMString,
4107 text: TrustedHTMLOrString,
4108 ) -> ErrorResult {
4109 let text = TrustedHTML::get_trusted_type_compliant_string(
4113 cx,
4114 &self.owner_global(),
4115 text,
4116 "Element insertAdjacentHTML",
4117 )?;
4118 let position = position.parse::<AdjacentPosition>()?;
4119
4120 let context = match position {
4123 AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => {
4126 match self.upcast::<Node>().GetParentNode() {
4127 Some(ref node) if node.is::<Document>() => {
4129 return Err(Error::NoModificationAllowed(None));
4130 },
4131 None => return Err(Error::NoModificationAllowed(None)),
4132 Some(node) => node,
4134 }
4135 },
4136 AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => {
4139 DomRoot::from_ref(self.upcast::<Node>())
4141 },
4142 };
4143
4144 let context = Element::fragment_parsing_context(
4146 cx,
4147 &context.owner_doc(),
4148 context.downcast::<Element>(),
4149 );
4150
4151 let fragment = context.parse_fragment(text, cx)?;
4154
4155 self.insert_adjacent(cx, position, fragment.upcast())
4157 .map(|_| ())
4158 }
4159
4160 fn EnterFormalActivationState(&self) -> ErrorResult {
4162 match self.as_maybe_activatable() {
4163 Some(a) => {
4164 a.enter_formal_activation_state();
4165 Ok(())
4166 },
4167 None => Err(Error::NotSupported(None)),
4168 }
4169 }
4170
4171 fn ExitFormalActivationState(&self) -> ErrorResult {
4172 match self.as_maybe_activatable() {
4173 Some(a) => {
4174 a.exit_formal_activation_state();
4175 Ok(())
4176 },
4177 None => Err(Error::NotSupported(None)),
4178 }
4179 }
4180
4181 fn RequestFullscreen(&self, cx: &mut CurrentRealm) -> RootedPromise {
4183 let doc = self.owner_document();
4184 doc.enter_fullscreen(cx, self)
4185 }
4186
4187 fn SetPointerCapture(&self, pointer_id: i32) -> ErrorResult {
4189 let document = self.owner_document();
4190 let event_handler = document.event_handler();
4191
4192 if !self.upcast::<Node>().is_connected() {
4203 return Err(Error::InvalidState(Some(
4204 "Can't capture pointer on an unconnected element".into(),
4205 )));
4206 }
4207
4208 if !event_handler.is_active_pointer(pointer_id) {
4218 return Ok(());
4219 }
4220
4221 event_handler.set_pending_pointer_capture(pointer_id, self);
4224
4225 Ok(())
4226 }
4227
4228 fn ReleasePointerCapture(&self, pointer_id: i32) -> ErrorResult {
4230 let document = self.owner_document();
4231 let event_handler = document.event_handler();
4232
4233 if !event_handler.is_active_pointer(pointer_id) {
4237 return Err(Error::NotFound(Some(
4238 "Can't release a pointer that is not active".into(),
4239 )));
4240 }
4241
4242 if !event_handler.has_pointer_capture(pointer_id, self) {
4245 return Ok(());
4246 }
4247
4248 event_handler.clear_pending_pointer_capture(pointer_id);
4250
4251 Ok(())
4252 }
4253
4254 fn HasPointerCapture(&self, pointer_id: i32) -> bool {
4256 let document = self.owner_document();
4257 let event_handler = document.event_handler();
4258 event_handler.has_pointer_capture(pointer_id, self)
4259 }
4260
4261 fn AttachShadow(
4263 &self,
4264 cx: &mut JSContext,
4265 init: &ShadowRootInit,
4266 ) -> Fallible<DomRoot<ShadowRoot>> {
4267 let shadow_root = self.attach_shadow(
4270 cx,
4271 IsUserAgentWidget::No,
4272 init.mode,
4273 init.clonable,
4274 init.serializable,
4275 init.delegatesFocus,
4276 init.slotAssignment,
4277 )?;
4278
4279 Ok(shadow_root)
4281 }
4282
4283 fn GetShadowRoot(&self) -> Option<DomRoot<ShadowRoot>> {
4285 let shadow_or_none = self.shadow_root();
4287
4288 let shadow = shadow_or_none?;
4290 if shadow.Mode() == ShadowRootMode::Closed {
4291 return None;
4292 }
4293
4294 Some(shadow)
4296 }
4297
4298 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
4300 self.custom_element_registry()
4302 }
4303
4304 fn GetRole(&self) -> Option<DOMString> {
4306 self.get_nullable_string_attribute(&local_name!("role"))
4307 }
4308
4309 fn SetRole(&self, cx: &mut JSContext, value: Option<DOMString>) {
4311 self.set_nullable_tokenlist_attribute(cx, &local_name!("role"), value);
4312 }
4313
4314 fn GetAriaAtomic(&self) -> Option<DOMString> {
4315 self.get_nullable_string_attribute(&local_name!("aria-atomic"))
4316 }
4317
4318 fn SetAriaAtomic(&self, cx: &mut JSContext, value: Option<DOMString>) {
4319 self.set_nullable_string_attribute(cx, &local_name!("aria-atomic"), value);
4320 }
4321
4322 fn GetAriaAutoComplete(&self) -> Option<DOMString> {
4323 self.get_nullable_string_attribute(&local_name!("aria-autocomplete"))
4324 }
4325
4326 fn SetAriaAutoComplete(&self, cx: &mut JSContext, value: Option<DOMString>) {
4327 self.set_nullable_string_attribute(cx, &local_name!("aria-autocomplete"), value);
4328 }
4329
4330 fn GetAriaBrailleLabel(&self) -> Option<DOMString> {
4331 self.get_nullable_string_attribute(&local_name!("aria-braillelabel"))
4332 }
4333
4334 fn SetAriaBrailleLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4335 self.set_nullable_string_attribute(cx, &local_name!("aria-braillelabel"), value);
4336 }
4337
4338 fn GetAriaBrailleRoleDescription(&self) -> Option<DOMString> {
4339 self.get_nullable_string_attribute(&local_name!("aria-brailleroledescription"))
4340 }
4341
4342 fn SetAriaBrailleRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4343 self.set_nullable_string_attribute(cx, &local_name!("aria-brailleroledescription"), value);
4344 }
4345
4346 fn GetAriaBusy(&self) -> Option<DOMString> {
4347 self.get_nullable_string_attribute(&local_name!("aria-busy"))
4348 }
4349
4350 fn SetAriaBusy(&self, cx: &mut JSContext, value: Option<DOMString>) {
4351 self.set_nullable_string_attribute(cx, &local_name!("aria-busy"), value);
4352 }
4353
4354 fn GetAriaChecked(&self) -> Option<DOMString> {
4355 self.get_nullable_string_attribute(&local_name!("aria-checked"))
4356 }
4357
4358 fn SetAriaChecked(&self, cx: &mut JSContext, value: Option<DOMString>) {
4359 self.set_nullable_string_attribute(cx, &local_name!("aria-checked"), value);
4360 }
4361
4362 fn GetAriaColCount(&self) -> Option<DOMString> {
4363 self.get_nullable_string_attribute(&local_name!("aria-colcount"))
4364 }
4365
4366 fn SetAriaColCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4367 self.set_nullable_string_attribute(cx, &local_name!("aria-colcount"), value);
4368 }
4369
4370 fn GetAriaColIndex(&self) -> Option<DOMString> {
4371 self.get_nullable_string_attribute(&local_name!("aria-colindex"))
4372 }
4373
4374 fn SetAriaColIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4375 self.set_nullable_string_attribute(cx, &local_name!("aria-colindex"), value);
4376 }
4377
4378 fn GetAriaColIndexText(&self) -> Option<DOMString> {
4379 self.get_nullable_string_attribute(&local_name!("aria-colindextext"))
4380 }
4381
4382 fn SetAriaColIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4383 self.set_nullable_string_attribute(cx, &local_name!("aria-colindextext"), value);
4384 }
4385
4386 fn GetAriaColSpan(&self) -> Option<DOMString> {
4387 self.get_nullable_string_attribute(&local_name!("aria-colspan"))
4388 }
4389
4390 fn SetAriaColSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4391 self.set_nullable_string_attribute(cx, &local_name!("aria-colspan"), value);
4392 }
4393
4394 fn GetAriaCurrent(&self) -> Option<DOMString> {
4395 self.get_nullable_string_attribute(&local_name!("aria-current"))
4396 }
4397
4398 fn SetAriaCurrent(&self, cx: &mut JSContext, value: Option<DOMString>) {
4399 self.set_nullable_string_attribute(cx, &local_name!("aria-current"), value);
4400 }
4401
4402 fn GetAriaDescription(&self) -> Option<DOMString> {
4403 self.get_nullable_string_attribute(&local_name!("aria-description"))
4404 }
4405
4406 fn SetAriaDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4407 self.set_nullable_string_attribute(cx, &local_name!("aria-description"), value);
4408 }
4409
4410 fn GetAriaDisabled(&self) -> Option<DOMString> {
4411 self.get_nullable_string_attribute(&local_name!("aria-disabled"))
4412 }
4413
4414 fn SetAriaDisabled(&self, cx: &mut JSContext, value: Option<DOMString>) {
4415 self.set_nullable_string_attribute(cx, &local_name!("aria-disabled"), value);
4416 }
4417
4418 fn GetAriaExpanded(&self) -> Option<DOMString> {
4419 self.get_nullable_string_attribute(&local_name!("aria-expanded"))
4420 }
4421
4422 fn SetAriaExpanded(&self, cx: &mut JSContext, value: Option<DOMString>) {
4423 self.set_nullable_string_attribute(cx, &local_name!("aria-expanded"), value);
4424 }
4425
4426 fn GetAriaHasPopup(&self) -> Option<DOMString> {
4427 self.get_nullable_string_attribute(&local_name!("aria-haspopup"))
4428 }
4429
4430 fn SetAriaHasPopup(&self, cx: &mut JSContext, value: Option<DOMString>) {
4431 self.set_nullable_string_attribute(cx, &local_name!("aria-haspopup"), value);
4432 }
4433
4434 fn GetAriaHidden(&self) -> Option<DOMString> {
4435 self.get_nullable_string_attribute(&local_name!("aria-hidden"))
4436 }
4437
4438 fn SetAriaHidden(&self, cx: &mut JSContext, value: Option<DOMString>) {
4439 self.set_nullable_string_attribute(cx, &local_name!("aria-hidden"), value);
4440 }
4441
4442 fn GetAriaInvalid(&self) -> Option<DOMString> {
4443 self.get_nullable_string_attribute(&local_name!("aria-invalid"))
4444 }
4445
4446 fn SetAriaInvalid(&self, cx: &mut JSContext, value: Option<DOMString>) {
4447 self.set_nullable_string_attribute(cx, &local_name!("aria-invalid"), value);
4448 }
4449
4450 fn GetAriaKeyShortcuts(&self) -> Option<DOMString> {
4451 self.get_nullable_string_attribute(&local_name!("aria-keyshortcuts"))
4452 }
4453
4454 fn SetAriaKeyShortcuts(&self, cx: &mut JSContext, value: Option<DOMString>) {
4455 self.set_nullable_string_attribute(cx, &local_name!("aria-keyshortcuts"), value);
4456 }
4457
4458 fn GetAriaLabel(&self) -> Option<DOMString> {
4459 self.get_nullable_string_attribute(&local_name!("aria-label"))
4460 }
4461
4462 fn SetAriaLabel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4463 self.set_nullable_string_attribute(cx, &local_name!("aria-label"), value);
4464 }
4465
4466 fn GetAriaLevel(&self) -> Option<DOMString> {
4467 self.get_nullable_string_attribute(&local_name!("aria-level"))
4468 }
4469
4470 fn SetAriaLevel(&self, cx: &mut JSContext, value: Option<DOMString>) {
4471 self.set_nullable_string_attribute(cx, &local_name!("aria-level"), value);
4472 }
4473
4474 fn GetAriaLive(&self) -> Option<DOMString> {
4475 self.get_nullable_string_attribute(&local_name!("aria-live"))
4476 }
4477
4478 fn SetAriaLive(&self, cx: &mut JSContext, value: Option<DOMString>) {
4479 self.set_nullable_string_attribute(cx, &local_name!("aria-live"), value);
4480 }
4481
4482 fn GetAriaModal(&self) -> Option<DOMString> {
4483 self.get_nullable_string_attribute(&local_name!("aria-modal"))
4484 }
4485
4486 fn SetAriaModal(&self, cx: &mut JSContext, value: Option<DOMString>) {
4487 self.set_nullable_string_attribute(cx, &local_name!("aria-modal"), value);
4488 }
4489
4490 fn GetAriaMultiLine(&self) -> Option<DOMString> {
4491 self.get_nullable_string_attribute(&local_name!("aria-multiline"))
4492 }
4493
4494 fn SetAriaMultiLine(&self, cx: &mut JSContext, value: Option<DOMString>) {
4495 self.set_nullable_string_attribute(cx, &local_name!("aria-multiline"), value);
4496 }
4497
4498 fn GetAriaMultiSelectable(&self) -> Option<DOMString> {
4499 self.get_nullable_string_attribute(&local_name!("aria-multiselectable"))
4500 }
4501
4502 fn SetAriaMultiSelectable(&self, cx: &mut JSContext, value: Option<DOMString>) {
4503 self.set_nullable_string_attribute(cx, &local_name!("aria-multiselectable"), value);
4504 }
4505
4506 fn GetAriaOrientation(&self) -> Option<DOMString> {
4507 self.get_nullable_string_attribute(&local_name!("aria-orientation"))
4508 }
4509
4510 fn SetAriaOrientation(&self, cx: &mut JSContext, value: Option<DOMString>) {
4511 self.set_nullable_string_attribute(cx, &local_name!("aria-orientation"), value);
4512 }
4513
4514 fn GetAriaPlaceholder(&self) -> Option<DOMString> {
4515 self.get_nullable_string_attribute(&local_name!("aria-placeholder"))
4516 }
4517
4518 fn SetAriaPlaceholder(&self, cx: &mut JSContext, value: Option<DOMString>) {
4519 self.set_nullable_string_attribute(cx, &local_name!("aria-placeholder"), value);
4520 }
4521
4522 fn GetAriaPosInSet(&self) -> Option<DOMString> {
4523 self.get_nullable_string_attribute(&local_name!("aria-posinset"))
4524 }
4525
4526 fn SetAriaPosInSet(&self, cx: &mut JSContext, value: Option<DOMString>) {
4527 self.set_nullable_string_attribute(cx, &local_name!("aria-posinset"), value);
4528 }
4529
4530 fn GetAriaPressed(&self) -> Option<DOMString> {
4531 self.get_nullable_string_attribute(&local_name!("aria-pressed"))
4532 }
4533
4534 fn SetAriaPressed(&self, cx: &mut JSContext, value: Option<DOMString>) {
4535 self.set_nullable_string_attribute(cx, &local_name!("aria-pressed"), value);
4536 }
4537
4538 fn GetAriaReadOnly(&self) -> Option<DOMString> {
4539 self.get_nullable_string_attribute(&local_name!("aria-readonly"))
4540 }
4541
4542 fn SetAriaReadOnly(&self, cx: &mut JSContext, value: Option<DOMString>) {
4543 self.set_nullable_string_attribute(cx, &local_name!("aria-readonly"), value);
4544 }
4545
4546 fn GetAriaRelevant(&self) -> Option<DOMString> {
4547 self.get_nullable_string_attribute(&local_name!("aria-relevant"))
4548 }
4549
4550 fn SetAriaRelevant(&self, cx: &mut JSContext, value: Option<DOMString>) {
4551 self.set_nullable_string_attribute(cx, &local_name!("aria-relevant"), value);
4552 }
4553
4554 fn GetAriaRequired(&self) -> Option<DOMString> {
4555 self.get_nullable_string_attribute(&local_name!("aria-required"))
4556 }
4557
4558 fn SetAriaRequired(&self, cx: &mut JSContext, value: Option<DOMString>) {
4559 self.set_nullable_string_attribute(cx, &local_name!("aria-required"), value);
4560 }
4561
4562 fn GetAriaRoleDescription(&self) -> Option<DOMString> {
4563 self.get_nullable_string_attribute(&local_name!("aria-roledescription"))
4564 }
4565
4566 fn SetAriaRoleDescription(&self, cx: &mut JSContext, value: Option<DOMString>) {
4567 self.set_nullable_string_attribute(cx, &local_name!("aria-roledescription"), value);
4568 }
4569
4570 fn GetAriaRowCount(&self) -> Option<DOMString> {
4571 self.get_nullable_string_attribute(&local_name!("aria-rowcount"))
4572 }
4573
4574 fn SetAriaRowCount(&self, cx: &mut JSContext, value: Option<DOMString>) {
4575 self.set_nullable_string_attribute(cx, &local_name!("aria-rowcount"), value);
4576 }
4577
4578 fn GetAriaRowIndex(&self) -> Option<DOMString> {
4579 self.get_nullable_string_attribute(&local_name!("aria-rowindex"))
4580 }
4581
4582 fn SetAriaRowIndex(&self, cx: &mut JSContext, value: Option<DOMString>) {
4583 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindex"), value);
4584 }
4585
4586 fn GetAriaRowIndexText(&self) -> Option<DOMString> {
4587 self.get_nullable_string_attribute(&local_name!("aria-rowindextext"))
4588 }
4589
4590 fn SetAriaRowIndexText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4591 self.set_nullable_string_attribute(cx, &local_name!("aria-rowindextext"), value);
4592 }
4593
4594 fn GetAriaRowSpan(&self) -> Option<DOMString> {
4595 self.get_nullable_string_attribute(&local_name!("aria-rowspan"))
4596 }
4597
4598 fn SetAriaRowSpan(&self, cx: &mut JSContext, value: Option<DOMString>) {
4599 self.set_nullable_string_attribute(cx, &local_name!("aria-rowspan"), value);
4600 }
4601
4602 fn GetAriaSelected(&self) -> Option<DOMString> {
4603 self.get_nullable_string_attribute(&local_name!("aria-selected"))
4604 }
4605
4606 fn SetAriaSelected(&self, cx: &mut JSContext, value: Option<DOMString>) {
4607 self.set_nullable_string_attribute(cx, &local_name!("aria-selected"), value);
4608 }
4609
4610 fn GetAriaSetSize(&self) -> Option<DOMString> {
4611 self.get_nullable_string_attribute(&local_name!("aria-setsize"))
4612 }
4613
4614 fn SetAriaSetSize(&self, cx: &mut JSContext, value: Option<DOMString>) {
4615 self.set_nullable_string_attribute(cx, &local_name!("aria-setsize"), value);
4616 }
4617
4618 fn GetAriaSort(&self) -> Option<DOMString> {
4619 self.get_nullable_string_attribute(&local_name!("aria-sort"))
4620 }
4621
4622 fn SetAriaSort(&self, cx: &mut JSContext, value: Option<DOMString>) {
4623 self.set_nullable_string_attribute(cx, &local_name!("aria-sort"), value);
4624 }
4625
4626 fn GetAriaValueMax(&self) -> Option<DOMString> {
4627 self.get_nullable_string_attribute(&local_name!("aria-valuemax"))
4628 }
4629
4630 fn SetAriaValueMax(&self, cx: &mut JSContext, value: Option<DOMString>) {
4631 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemax"), value);
4632 }
4633
4634 fn GetAriaValueMin(&self) -> Option<DOMString> {
4635 self.get_nullable_string_attribute(&local_name!("aria-valuemin"))
4636 }
4637
4638 fn SetAriaValueMin(&self, cx: &mut JSContext, value: Option<DOMString>) {
4639 self.set_nullable_string_attribute(cx, &local_name!("aria-valuemin"), value);
4640 }
4641
4642 fn GetAriaValueNow(&self) -> Option<DOMString> {
4643 self.get_nullable_string_attribute(&local_name!("aria-valuenow"))
4644 }
4645
4646 fn SetAriaValueNow(&self, cx: &mut JSContext, value: Option<DOMString>) {
4647 self.set_nullable_string_attribute(cx, &local_name!("aria-valuenow"), value);
4648 }
4649
4650 fn GetAriaValueText(&self) -> Option<DOMString> {
4651 self.get_nullable_string_attribute(&local_name!("aria-valuetext"))
4652 }
4653
4654 fn SetAriaValueText(&self, cx: &mut JSContext, value: Option<DOMString>) {
4655 self.set_nullable_string_attribute(cx, &local_name!("aria-valuetext"), value);
4656 }
4657
4658 fn GetAssignedSlot(&self, cx: &JSContext) -> Option<DomRoot<HTMLSlotElement>> {
4660 rooted!(&in(cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4663 slottable.find_a_slot(cx.no_gc(), true)
4664 }
4665
4666 fn Part(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
4668 let Some(part) = self.rare_data().as_ref().and_then(|data| data.part.get()) else {
4669 let part = DOMTokenList::new(cx, self, &local_name!("part"), None);
4670 self.ensure_rare_data(cx.no_gc()).part.set(Some(&*part));
4671 return part;
4672 };
4673 part
4674 }
4675
4676 fn Animate(
4678 &self,
4679 cx: &mut JSContext,
4680 keyframes: *mut JSObject,
4681 options: UnrestrictedDoubleOrKeyframeAnimationOptions,
4682 ) -> DomRoot<Animation> {
4683 let window = self.owner_window();
4684
4685 let target = self;
4687
4688 let timeline =
4692 if let UnrestrictedDoubleOrKeyframeAnimationOptions::KeyframeAnimationOptions(options) =
4693 &options
4694 {
4695 options.timeline.clone().flatten()
4696 } else {
4697 None
4698 };
4699 let timeline = timeline.unwrap_or_else(|| {
4700 let document = self.owner_document();
4701 DomRoot::upcast(document.Timeline())
4702 });
4703
4704 let parent_options = match options {
4712 UnrestrictedDoubleOrKeyframeAnimationOptions::UnrestrictedDouble(value) => {
4713 UnrestrictedDoubleOrKeyframeEffectOptions::UnrestrictedDouble(value)
4714 },
4715 UnrestrictedDoubleOrKeyframeAnimationOptions::KeyframeAnimationOptions(options) => {
4716 UnrestrictedDoubleOrKeyframeEffectOptions::KeyframeEffectOptions(options.parent)
4717 },
4718 };
4719 let effect =
4720 KeyframeEffect::Constructor(cx, &window, None, Some(target), keyframes, parent_options);
4721
4722 let animation = Animation::Constructor(
4726 cx,
4727 &window,
4728 None,
4729 Some(effect.upcast()),
4730 Some(Some(&timeline)),
4731 );
4732
4733 animation
4740 }
4741}
4742
4743impl VirtualMethods for Element {
4744 fn super_type(&self) -> Option<&dyn VirtualMethods> {
4745 Some(self.upcast::<Node>() as &dyn VirtualMethods)
4746 }
4747
4748 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
4749 if attr.local_name() == &local_name!("lang") {
4751 return true;
4752 }
4753
4754 self.super_type()
4755 .unwrap()
4756 .attribute_affects_presentational_hints(attr)
4757 }
4758
4759 fn attribute_mutated(
4760 &self,
4761 cx: &mut JSContext,
4762 attr: AttrRef<'_>,
4763 mutation: AttributeMutation,
4764 ) {
4765 self.super_type()
4766 .unwrap()
4767 .attribute_mutated(cx, attr, mutation);
4768 let node = self.upcast::<Node>();
4769 let doc = node.owner_doc();
4770 match *attr.local_name() {
4771 ref name if name.starts_with("on") && EventTarget::is_content_event_handler(name) => {
4775 let evtarget = self.upcast::<EventTarget>();
4776 let event_name = &name[2..];
4777 match mutation {
4778 AttributeMutation::Set(_) => {
4780 let source = &**attr.value();
4781 let source_line = 1; evtarget.set_event_handler_uncompiled(
4783 cx,
4784 self.owner_window().get_url(),
4785 source_line,
4786 event_name,
4787 source,
4788 );
4789 },
4790 AttributeMutation::Removed => {
4792 evtarget
4793 .set_event_handler_common::<EventHandlerNonNull>(cx, event_name, None);
4794 },
4795 }
4796 },
4797 local_name!("style") => self.update_style_attribute(cx, attr, mutation),
4798 local_name!("id") => {
4799 *self.id_attribute.borrow_mut() = mutation.new_value(attr).and_then(|value| {
4801 let value = value.as_atom();
4802 if value != &atom!("") {
4803 Some(value.clone())
4805 } else {
4806 None
4808 }
4809 });
4810
4811 let containing_shadow_root = self.containing_shadow_root();
4812 if node.is_in_a_document_tree() || node.is_in_a_shadow_tree() {
4813 let value = attr.value().as_atom().clone();
4814 match mutation {
4815 AttributeMutation::Set(old_value) => {
4816 if let Some(old_value) = old_value {
4817 let old_value = old_value.as_atom();
4818 if let Some(ref shadow_root) = containing_shadow_root {
4819 shadow_root.unregister_element_id(old_value);
4820 } else {
4821 doc.unregister_element_id(cx, old_value);
4822 }
4823 }
4824 if value != atom!("") {
4825 if let Some(ref shadow_root) = containing_shadow_root {
4826 shadow_root.register_element_id(self, &value);
4827 } else {
4828 doc.register_element_id(cx, self, &value);
4829 }
4830 }
4831 },
4832 AttributeMutation::Removed => {
4833 if value != atom!("") {
4834 if let Some(ref shadow_root) = containing_shadow_root {
4835 shadow_root.unregister_element_id(&value);
4836 } else {
4837 doc.unregister_element_id(cx, &value);
4838 }
4839 }
4840 },
4841 }
4842 }
4843 },
4844 local_name!("name") => {
4845 self.ensure_rare_data(cx.no_gc()).name_attribute =
4847 mutation.new_value(attr).and_then(|value| {
4848 let value = value.as_atom();
4849 if value != &atom!("") {
4850 Some(value.clone())
4851 } else {
4852 None
4853 }
4854 });
4855 if node.is_connected() && node.containing_shadow_root().is_none() {
4858 let value = attr.value().as_atom().clone();
4859 match mutation {
4860 AttributeMutation::Set(old_value) => {
4861 if let Some(old_value) = old_value {
4862 doc.unregister_element_name(old_value.as_atom());
4863 }
4864 if value != atom!("") {
4865 doc.register_element_name(self, &value);
4866 }
4867 },
4868 AttributeMutation::Removed => {
4869 if value != atom!("") {
4870 doc.unregister_element_name(&value);
4871 }
4872 },
4873 }
4874 }
4875 },
4876 local_name!("slot") => {
4877 rooted!(&in(cx) let slottable = Slottable(Dom::from_ref(self.upcast::<Node>())));
4879
4880 if let Some(assigned_slot) = slottable.assigned_slot() {
4882 assigned_slot.assign_slottables(cx);
4883 }
4884 slottable.assign_a_slot(cx);
4885 },
4886 local_name!("role") => {
4887 self.upcast::<Node>()
4888 .add_pending_accessibility_damage(AccessibilityDamage::Node);
4889 },
4890 _ => {
4891 if attr.namespace() == &ns!() && attr.local_name() == &local_name!("src") {
4894 node.dirty(cx.no_gc(), NodeDamage::Other);
4895 }
4896 },
4897 };
4898
4899 if self
4902 .upcast::<Node>()
4903 .get_flag(NodeFlags::USES_ATTR_IN_CONTENT_ATTRIBUTE)
4904 {
4905 node.dirty(cx.no_gc(), NodeDamage::ContentOrHeritage);
4906 }
4907
4908 node.rev_version(cx.no_gc());
4912
4913 #[cfg(feature = "devtools")]
4915 {
4916 let window = self.owner_window();
4917 if window.live_devtools_updates() {
4918 let global = window.upcast::<GlobalScope>();
4919 if let Some(sender) = global.devtools_chan() {
4920 let pipeline_id = global.pipeline_id();
4921 if ScriptThread::devtools_want_updates_for_node(pipeline_id, self.upcast()) {
4922 let devtools_message =
4923 devtools_traits::ScriptToDevtoolsControlMsg::DomMutation(
4924 pipeline_id,
4925 devtools_traits::DomMutation::AttributeModified {
4926 node: self.upcast::<Node>().unique_id(pipeline_id),
4927 attribute_name: attr.local_name().to_string(),
4928 new_value: mutation
4929 .new_value(attr)
4930 .map(|value| value.to_string()),
4931 },
4932 );
4933 sender.send(devtools_message).unwrap();
4934 }
4935 }
4936 }
4937 }
4938 }
4939
4940 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
4941 match *name {
4942 local_name!("id") => AttrValue::Atom(value.into()),
4943 local_name!("name") => AttrValue::Atom(value.into()),
4944 local_name!("class") | local_name!("part") | local_name!("role") => {
4945 AttrValue::from_serialized_tokenlist(value.into())
4946 },
4947 local_name!("exportparts") => AttrValue::from_shadow_parts(value.into()),
4948 local_name!("tabindex") => AttrValue::from_i32(value.into(), -1),
4949 _ => self
4950 .super_type()
4951 .unwrap()
4952 .parse_plain_attribute(name, value),
4953 }
4954 }
4955
4956 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
4957 if let Some(s) = self.super_type() {
4958 s.bind_to_tree(cx, context);
4959 }
4960
4961 if let Some(f) = self.as_maybe_form_control() {
4962 f.bind_form_control_to_tree(cx);
4963 }
4964
4965 if let Some(ref shadow_root) = self.shadow_root() {
4966 shadow_root.bind_to_tree(cx, context);
4967 }
4968
4969 if !context.is_in_tree() {
4970 return;
4971 }
4972
4973 self.register_current_id_and_name_attribute(cx);
4974 }
4975
4976 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
4977 self.super_type().unwrap().unbind_from_tree(cx, context);
4978
4979 if let Some(f) = self.as_maybe_form_control() {
4980 f.unbind_form_control_from_tree(cx);
4984 }
4985
4986 if !context.tree_is_in_a_document_tree && !context.tree_is_in_a_shadow_tree {
4987 return;
4988 }
4989
4990 let doc = self.owner_document();
4991
4992 let fullscreen = doc.fullscreen_element();
4993 if fullscreen.as_deref() == Some(self) {
4994 doc.exit_fullscreen(cx);
4995 }
4996
4997 self.unregister_current_id_and_name_attribute(cx);
4998 }
4999
5000 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
5001 if let Some(s) = self.super_type() {
5002 s.children_changed(cx, mutation);
5003 }
5004
5005 let flags = self.get_selector_flags();
5006 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
5007 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
5009 } else {
5010 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) &&
5011 let Some(next_child) = mutation.next_child()
5012 {
5013 for child in next_child.inclusively_following_siblings_unrooted(cx.no_gc()) {
5014 if child.is::<Element>() {
5015 child.dirty(cx.no_gc(), NodeDamage::Other);
5016 }
5017 }
5018 }
5019 if flags.intersects(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) &&
5020 let Some(child) = mutation.modified_edge_element(cx.no_gc())
5021 {
5022 child.dirty(cx.no_gc(), NodeDamage::Other);
5023 }
5024 }
5025 }
5026
5027 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
5028 self.super_type().unwrap().adopting_steps(cx, old_doc);
5029
5030 if self.owner_document().is_html_document() != old_doc.is_html_document() {
5031 self.tag_name.clear();
5032 }
5033 }
5034
5035 fn post_connection_steps(&self, cx: &mut JSContext) {
5036 if let Some(s) = self.super_type() {
5037 s.post_connection_steps(cx);
5038 }
5039
5040 self.update_nonce_post_connection(cx);
5041 }
5042
5043 fn cloning_steps(
5045 &self,
5046 cx: &mut JSContext,
5047 copy: &Node,
5048 maybe_doc: Option<&Document>,
5049 clone_children: CloneChildrenFlag,
5050 ) {
5051 if let Some(s) = self.super_type() {
5052 s.cloning_steps(cx, copy, maybe_doc, clone_children);
5053 }
5054 let elem = copy.downcast::<Element>().unwrap();
5055 if let Some(rare_data) = self.rare_data().as_ref() {
5056 elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone(), cx.no_gc());
5057 }
5058 }
5059}
5060impl Element {
5061 pub(crate) fn client_rect(&self, no_gc: &NoGC) -> Rect<i32, CSSPixel> {
5062 let doc = self.node.owner_doc();
5063
5064 if let Some(rect) = self
5065 .rare_data()
5066 .as_ref()
5067 .and_then(|data| data.client_rect.as_ref())
5068 .and_then(|rect| rect.get().ok()) &&
5069 doc.restyle_reason(no_gc).is_empty()
5070 {
5071 return rect;
5072 }
5073
5074 let mut rect = self.upcast::<Node>().client_rect();
5075 let in_quirks_mode = doc.quirks_mode() == QuirksMode::Quirks;
5076
5077 if (in_quirks_mode && doc.GetBody().as_deref() == self.downcast::<HTMLElement>()) ||
5078 (!in_quirks_mode && self.is_document_element())
5079 {
5080 rect.size = doc.window().viewport_details().size.round().to_i32();
5081 }
5082
5083 self.ensure_rare_data(no_gc).client_rect =
5084 Some(self.owner_window().cache_layout_value(rect));
5085 rect
5086 }
5087
5088 pub(crate) fn as_maybe_activatable(&self) -> Option<&dyn Activatable> {
5089 let element = match self.upcast::<Node>().type_id() {
5090 NodeTypeId::Element(ElementTypeId::HTMLElement(
5091 HTMLElementTypeId::HTMLInputElement,
5092 )) => {
5093 let element = self.downcast::<HTMLInputElement>().unwrap();
5094 Some(element as &dyn Activatable)
5095 },
5096 NodeTypeId::Element(ElementTypeId::HTMLElement(
5097 HTMLElementTypeId::HTMLButtonElement,
5098 )) => {
5099 let element = self.downcast::<HTMLButtonElement>().unwrap();
5100 Some(element as &dyn Activatable)
5101 },
5102 NodeTypeId::Element(ElementTypeId::HTMLElement(
5103 HTMLElementTypeId::HTMLAnchorElement,
5104 )) => {
5105 let element = self.downcast::<HTMLAnchorElement>().unwrap();
5106 Some(element as &dyn Activatable)
5107 },
5108 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) => {
5109 let element = self.downcast::<HTMLAreaElement>().unwrap();
5110 Some(element as &dyn Activatable)
5111 },
5112 NodeTypeId::Element(ElementTypeId::HTMLElement(
5113 HTMLElementTypeId::HTMLLabelElement,
5114 )) => {
5115 let element = self.downcast::<HTMLLabelElement>().unwrap();
5116 Some(element as &dyn Activatable)
5117 },
5118 NodeTypeId::Element(ElementTypeId::HTMLElement(
5119 HTMLElementTypeId::HTMLSelectElement,
5120 )) => {
5121 let element = self.downcast::<HTMLSelectElement>().unwrap();
5122 Some(element as &dyn Activatable)
5123 },
5124 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
5125 let element = self.downcast::<HTMLElement>().unwrap();
5126 Some(element as &dyn Activatable)
5127 },
5128 _ => None,
5129 };
5130 element.filter(|elem| elem.is_instance_activatable())
5131 }
5132
5133 pub(crate) fn as_stylesheet_owner(&self) -> Option<&dyn StylesheetOwner> {
5134 if let Some(s) = self.downcast::<HTMLStyleElement>() {
5135 return Some(s as &dyn StylesheetOwner);
5136 }
5137
5138 if let Some(l) = self.downcast::<HTMLLinkElement>() {
5139 return Some(l as &dyn StylesheetOwner);
5140 }
5141
5142 None
5143 }
5144
5145 pub(crate) fn as_maybe_validatable(&self) -> Option<&dyn Validatable> {
5147 match self.upcast::<Node>().type_id() {
5148 NodeTypeId::Element(ElementTypeId::HTMLElement(
5149 HTMLElementTypeId::HTMLInputElement,
5150 )) => {
5151 let element = self.downcast::<HTMLInputElement>().unwrap();
5152 Some(element as &dyn Validatable)
5153 },
5154 NodeTypeId::Element(ElementTypeId::HTMLElement(
5155 HTMLElementTypeId::HTMLButtonElement,
5156 )) => {
5157 let element = self.downcast::<HTMLButtonElement>().unwrap();
5158 Some(element as &dyn Validatable)
5159 },
5160 NodeTypeId::Element(ElementTypeId::HTMLElement(
5161 HTMLElementTypeId::HTMLObjectElement,
5162 )) => {
5163 let element = self.downcast::<HTMLObjectElement>().unwrap();
5164 Some(element as &dyn Validatable)
5165 },
5166 NodeTypeId::Element(ElementTypeId::HTMLElement(
5167 HTMLElementTypeId::HTMLSelectElement,
5168 )) => {
5169 let element = self.downcast::<HTMLSelectElement>().unwrap();
5170 Some(element as &dyn Validatable)
5171 },
5172 NodeTypeId::Element(ElementTypeId::HTMLElement(
5173 HTMLElementTypeId::HTMLTextAreaElement,
5174 )) => {
5175 let element = self.downcast::<HTMLTextAreaElement>().unwrap();
5176 Some(element as &dyn Validatable)
5177 },
5178 NodeTypeId::Element(ElementTypeId::HTMLElement(
5179 HTMLElementTypeId::HTMLFieldSetElement,
5180 )) => {
5181 let element = self.downcast::<HTMLFieldSetElement>().unwrap();
5182 Some(element as &dyn Validatable)
5183 },
5184 NodeTypeId::Element(ElementTypeId::HTMLElement(
5185 HTMLElementTypeId::HTMLOutputElement,
5186 )) => {
5187 let element = self.downcast::<HTMLOutputElement>().unwrap();
5188 Some(element as &dyn Validatable)
5189 },
5190 _ => None,
5191 }
5192 }
5193
5194 pub(crate) fn is_invalid(&self, cx: &mut JSContext, needs_update: bool) -> bool {
5195 if let Some(validatable) = self.as_maybe_validatable() {
5196 if needs_update {
5197 validatable
5198 .validity_state(cx)
5199 .perform_validation_and_update(cx, ValidationFlags::all());
5200 }
5201 return validatable.is_instance_validatable(cx.no_gc()) &&
5202 !validatable.satisfies_constraints(cx);
5203 }
5204
5205 if let Some(internals) = self.get_element_internals() {
5206 return internals.is_invalid(cx);
5207 }
5208 false
5209 }
5210
5211 pub(crate) fn is_instance_validatable(&self, no_gc: &NoGC) -> bool {
5212 if let Some(validatable) = self.as_maybe_validatable() {
5213 return validatable.is_instance_validatable(no_gc);
5214 }
5215 if let Some(internals) = self.get_element_internals() {
5216 return internals.is_instance_validatable(no_gc);
5217 }
5218 false
5219 }
5220
5221 pub(crate) fn init_state_for_internals(&self) {
5222 self.set_enabled_state(true);
5223 self.set_state(ElementState::VALID, true);
5224 self.set_state(ElementState::INVALID, false);
5225 }
5226
5227 pub(crate) fn click_in_progress(&self) -> bool {
5228 self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
5229 }
5230
5231 pub(crate) fn set_click_in_progress(&self, click: bool) {
5232 self.upcast::<Node>()
5233 .set_flag(NodeFlags::CLICK_IN_PROGRESS, click)
5234 }
5235
5236 pub fn state(&self) -> ElementState {
5237 self.state.get()
5238 }
5239
5240 pub(crate) fn set_state(&self, which: ElementState, value: bool) {
5241 let mut state = self.state.get();
5242 let previous_state = state;
5243 if value {
5244 state.insert(which);
5245 } else {
5246 state.remove(which);
5247 }
5248
5249 if previous_state == state {
5250 return;
5252 }
5253
5254 {
5257 let document = self.owner_document();
5258 let mut entry = document.ensure_pending_restyle(self);
5259 if entry.snapshot.is_none() {
5260 entry.snapshot = Some(Snapshot::new());
5261 }
5262 let snapshot = entry.snapshot.as_mut().unwrap();
5263 if snapshot.state.is_none() {
5264 snapshot.state = Some(self.state());
5265 }
5266 }
5267
5268 self.state.set(state);
5269 }
5270
5271 pub(crate) fn set_active_state(&self, value: bool) {
5273 self.set_state(ElementState::ACTIVE, value);
5274
5275 if let Some(parent) = self.upcast::<Node>().GetParentElement() {
5276 parent.set_active_state(value);
5277 }
5278 }
5279
5280 pub(crate) fn focus_state(&self) -> bool {
5281 self.state.get().contains(ElementState::FOCUS)
5282 }
5283
5284 pub(crate) fn set_focus_state(&self, value: bool) {
5285 self.set_state(ElementState::FOCUS, value);
5286 }
5287
5288 pub(crate) fn set_hover_state(&self, value: bool) {
5289 self.set_state(ElementState::HOVER, value);
5290 }
5291
5292 pub(crate) fn enabled_state(&self) -> bool {
5293 self.state.get().contains(ElementState::ENABLED)
5294 }
5295
5296 pub(crate) fn set_enabled_state(&self, value: bool) {
5297 self.set_state(ElementState::ENABLED, value)
5298 }
5299
5300 pub(crate) fn disabled_state(&self) -> bool {
5301 self.state.get().contains(ElementState::DISABLED)
5302 }
5303
5304 pub(crate) fn set_disabled_state(&self, value: bool) {
5305 self.set_state(ElementState::DISABLED, value)
5306 }
5307
5308 pub(crate) fn read_write_state(&self) -> bool {
5309 self.state.get().contains(ElementState::READWRITE)
5310 }
5311
5312 pub(crate) fn set_read_write_state(&self, value: bool) {
5313 self.set_state(ElementState::READWRITE, value)
5314 }
5315
5316 pub(crate) fn set_open_state(&self, value: bool) {
5317 self.set_state(ElementState::OPEN, value);
5318 }
5319
5320 pub(crate) fn set_placeholder_shown_state(&self, value: bool) {
5321 self.set_state(ElementState::PLACEHOLDER_SHOWN, value);
5322 }
5323
5324 pub(crate) fn set_modal_state(&self, value: bool) {
5325 self.set_state(ElementState::MODAL, value);
5326 }
5327
5328 pub(crate) fn set_target_state(&self, value: bool) {
5329 self.set_state(ElementState::URLTARGET, value)
5330 }
5331
5332 pub(crate) fn set_fullscreen_state(&self, value: bool) {
5333 self.set_state(ElementState::FULLSCREEN, value)
5334 }
5335
5336 pub(crate) fn is_connected(&self) -> bool {
5338 self.upcast::<Node>().is_connected()
5339 }
5340
5341 pub(crate) fn cannot_navigate(&self) -> bool {
5343 let document = self.owner_document();
5347 !document.is_fully_active() || (!self.is::<HTMLAnchorElement>() && !self.is_connected())
5348 }
5349
5350 pub(crate) fn get_computed_role(&self) -> Option<DOMString> {
5351 let accesskit_node = self
5352 .owner_window()
5353 .accesskit_node_query(self.upcast::<Node>().to_trusted_node_address())?;
5354 let aria_role = ACCESSKIT_ROLE_TO_ARIA_ROLE.get(&accesskit_node.role())?;
5355 Some(DOMString::from(aria_role.as_str()))
5356 }
5357}
5358
5359impl Element {
5360 pub(crate) fn check_ancestors_disabled_state_for_form_control(&self, no_gc: &NoGC) {
5361 let node = self.upcast::<Node>();
5362 if self.disabled_state() {
5363 return;
5364 }
5365 for ancestor in node.ancestors_unrooted(no_gc) {
5366 if !ancestor.is::<HTMLFieldSetElement>() {
5367 continue;
5368 }
5369 if !ancestor.downcast::<Element>().unwrap().disabled_state() {
5370 continue;
5371 }
5372 if ancestor.is_parent_of(node) {
5373 self.set_disabled_state(true);
5374 self.set_enabled_state(false);
5375 return;
5376 }
5377 if let Some(ref legend) = ancestor
5378 .children_unrooted(no_gc)
5379 .find(|n| n.is::<HTMLLegendElement>())
5380 {
5381 if node
5383 .ancestors_unrooted(no_gc)
5384 .any(|ancestor| ancestor == *legend)
5385 {
5386 continue;
5387 }
5388 }
5389 self.set_disabled_state(true);
5390 self.set_enabled_state(false);
5391 return;
5392 }
5393 }
5394
5395 pub(crate) fn check_parent_disabled_state_for_option(&self) {
5396 if self.disabled_state() {
5397 return;
5398 }
5399 let node = self.upcast::<Node>();
5400 if let Some(ref parent) = node.GetParentNode() &&
5401 parent.is::<HTMLOptGroupElement>() &&
5402 parent.downcast::<Element>().unwrap().disabled_state()
5403 {
5404 self.set_disabled_state(true);
5405 self.set_enabled_state(false);
5406 }
5407 }
5408
5409 pub(crate) fn check_disabled_attribute(&self) {
5410 let has_disabled_attrib = self.has_attribute(&local_name!("disabled"));
5411 self.set_disabled_state(has_disabled_attrib);
5412 self.set_enabled_state(!has_disabled_attrib);
5413 }
5414
5415 pub(crate) fn update_read_write_state_from_readonly_attribute(&self) {
5416 let has_readonly_attribute = self.has_attribute(&local_name!("readonly"));
5417 self.set_read_write_state(has_readonly_attribute);
5418 }
5419}
5420
5421#[derive(Clone, Copy)]
5422pub(crate) enum AttributeMutation<'a> {
5423 Set(Option<&'a AttrValue>),
5426
5427 Removed,
5430}
5431
5432impl AttributeMutation<'_> {
5433 pub(crate) fn is_removal(&self) -> bool {
5434 match *self {
5435 AttributeMutation::Removed => true,
5436 AttributeMutation::Set(_) => false,
5437 }
5438 }
5439
5440 pub(crate) fn new_value<'b>(&self, attr: AttrRef<'b>) -> Option<AttrValueRef<'b>> {
5441 match *self {
5442 AttributeMutation::Set(_) => Some(attr.value()),
5443 AttributeMutation::Removed => None,
5444 }
5445 }
5446
5447 pub(crate) fn old_value(&self, attr: AttrRef<'_>) -> Option<String> {
5448 match *self {
5449 AttributeMutation::Set(old) => old.map(|value| value.to_string()),
5450 AttributeMutation::Removed => Some(attr.value().to_string()),
5451 }
5452 }
5453}
5454
5455#[derive(JSTraceable, MallocSizeOf)]
5459struct TagName {
5460 #[no_trace]
5461 ptr: DomRefCell<Option<LocalName>>,
5462}
5463
5464impl TagName {
5465 fn new() -> TagName {
5466 TagName {
5467 ptr: DomRefCell::new(None),
5468 }
5469 }
5470
5471 fn or_init<F>(&self, cb: F) -> LocalName
5474 where
5475 F: FnOnce() -> LocalName,
5476 {
5477 match &mut *self.ptr.borrow_mut() {
5478 &mut Some(ref name) => name.clone(),
5479 ptr => {
5480 let name = cb();
5481 *ptr = Some(name.clone());
5482 name
5483 },
5484 }
5485 }
5486
5487 fn clear(&self) {
5490 *self.ptr.borrow_mut() = None;
5491 }
5492}
5493
5494pub(crate) fn reflect_cross_origin_attribute(element: &Element) -> Option<DOMString> {
5496 element
5497 .get_attribute_string_ref(&local_name!("crossorigin"))
5498 .map(|value| {
5499 DOMString::from_static(
5500 ["anonymous", "use-credentials"]
5501 .into_iter()
5502 .find(|keyword| value.eq_ignore_ascii_case(keyword))
5503 .unwrap_or("anonymous"),
5504 )
5505 })
5506}
5507
5508pub(crate) fn set_cross_origin_attribute(
5509 cx: &mut JSContext,
5510 element: &Element,
5511 value: Option<DOMString>,
5512) {
5513 match value {
5514 Some(val) => element.set_string_attribute(cx, &local_name!("crossorigin"), val),
5515 None => {
5516 element.remove_attribute(cx, &ns!(), &local_name!("crossorigin"));
5517 },
5518 }
5519}
5520
5521pub(crate) fn reflect_referrer_policy_attribute(element: &Element) -> DOMString {
5523 element
5524 .get_attribute_string_ref(&local_name!("referrerpolicy"))
5525 .map(|value| {
5526 DOMString::from(
5527 [
5528 "no-referrer",
5529 "no-referrer-when-downgrade",
5530 "same-origin",
5531 "origin",
5532 "strict-origin",
5533 "origin-when-cross-origin",
5534 "strict-origin-when-cross-origin",
5535 "unsafe-url",
5536 ]
5537 .into_iter()
5538 .find(|keyword| value.eq_ignore_ascii_case(keyword))
5539 .unwrap_or(""),
5540 )
5541 })
5542 .unwrap_or_default()
5543}
5544
5545pub(crate) fn referrer_policy_for_element(element: &Element) -> ReferrerPolicy {
5546 element
5547 .get_attribute_string_ref(&local_name!("referrerpolicy"))
5548 .map(|value| {
5549 let value = value.as_attr_ref().value();
5550 ReferrerPolicy::from(value.as_ref())
5551 })
5552 .unwrap_or(element.owner_document().get_referrer_policy())
5553}
5554
5555pub(crate) fn cors_setting_for_element(element: &Element) -> Option<CorsSettings> {
5556 element
5557 .get_attribute_string_ref(&local_name!("crossorigin"))
5558 .map(|value| {
5559 let value = value.as_attr_ref().value();
5560 CorsSettings::from_enumerated_attribute(&value)
5561 })
5562}
5563
5564pub(crate) fn cors_settings_attribute_credential_mode(element: &Element) -> CredentialsMode {
5566 element
5567 .get_attribute_string_ref(&local_name!("crossorigin"))
5568 .map(|value| {
5569 if value.eq_ignore_ascii_case("use-credentials") {
5570 CredentialsMode::Include
5571 } else {
5572 CredentialsMode::CredentialsSameOrigin
5574 }
5575 })
5576 .unwrap_or(CredentialsMode::CredentialsSameOrigin)
5578}
5579
5580pub(crate) fn is_element_affected_by_legacy_background_presentational_hint(
5581 namespace: &Namespace,
5582 local_name: &LocalName,
5583) -> bool {
5584 *namespace == ns!(html) &&
5585 matches!(
5586 *local_name,
5587 local_name!("body") |
5588 local_name!("table") |
5589 local_name!("thead") |
5590 local_name!("tbody") |
5591 local_name!("tfoot") |
5592 local_name!("tr") |
5593 local_name!("td") |
5594 local_name!("th")
5595 )
5596}
5597
5598impl script_bindings::callback::OwnerWindow<crate::DomTypeHolder> for Element {
5599 fn owner_window(&self) -> Option<DomRoot<Window>> {
5600 Some(NodeTraits::owner_window(self))
5601 }
5602}