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