1use std::collections::HashSet;
6use std::default::Default;
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use html5ever::{LocalName, Prefix, local_name, ns};
11use js::rust::HandleObject;
12use layout_api::QueryMsg;
13use style::attr::AttrValue;
14use stylo_dom::ElementState;
15
16use crate::dom::activation::Activatable;
17use crate::dom::attr::Attr;
18use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterData_Binding::CharacterDataMethods;
19use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::{
20 EventHandlerNonNull, OnErrorEventHandlerNonNull,
21};
22use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
23use crate::dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
24use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
25use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
26use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
27use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
28use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
29use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
30use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
31use crate::dom::bindings::str::DOMString;
32use crate::dom::characterdata::CharacterData;
33use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
34use crate::dom::customelementregistry::{CallbackReaction, CustomElementState};
35use crate::dom::document::{Document, FocusInitiator};
36use crate::dom::documentfragment::DocumentFragment;
37use crate::dom::domstringmap::DOMStringMap;
38use crate::dom::element::{AttributeMutation, Element};
39use crate::dom::elementinternals::ElementInternals;
40use crate::dom::event::Event;
41use crate::dom::eventtarget::EventTarget;
42use crate::dom::html::htmlbodyelement::HTMLBodyElement;
43use crate::dom::html::htmlbrelement::HTMLBRElement;
44use crate::dom::html::htmldetailselement::HTMLDetailsElement;
45use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
46use crate::dom::html::htmlframesetelement::HTMLFrameSetElement;
47use crate::dom::html::htmlhtmlelement::HTMLHtmlElement;
48use crate::dom::html::htmlinputelement::{HTMLInputElement, InputType};
49use crate::dom::html::htmllabelelement::HTMLLabelElement;
50use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
51use crate::dom::node::{BindContext, Node, NodeTraits, ShadowIncluding, UnbindContext};
52use crate::dom::shadowroot::ShadowRoot;
53use crate::dom::text::Text;
54use crate::dom::virtualmethods::VirtualMethods;
55use crate::script_runtime::CanGc;
56use crate::script_thread::ScriptThread;
57
58#[dom_struct]
59pub(crate) struct HTMLElement {
60 element: Element,
61 style_decl: MutNullableDom<CSSStyleDeclaration>,
62 dataset: MutNullableDom<DOMStringMap>,
63}
64
65impl HTMLElement {
66 pub(crate) fn new_inherited(
67 tag_name: LocalName,
68 prefix: Option<Prefix>,
69 document: &Document,
70 ) -> HTMLElement {
71 HTMLElement::new_inherited_with_state(ElementState::empty(), tag_name, prefix, document)
72 }
73
74 pub(crate) fn new_inherited_with_state(
75 state: ElementState,
76 tag_name: LocalName,
77 prefix: Option<Prefix>,
78 document: &Document,
79 ) -> HTMLElement {
80 HTMLElement {
81 element: Element::new_inherited_with_state(
82 state,
83 tag_name,
84 ns!(html),
85 prefix,
86 document,
87 ),
88 style_decl: Default::default(),
89 dataset: Default::default(),
90 }
91 }
92
93 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
94 pub(crate) fn new(
95 local_name: LocalName,
96 prefix: Option<Prefix>,
97 document: &Document,
98 proto: Option<HandleObject>,
99 can_gc: CanGc,
100 ) -> DomRoot<HTMLElement> {
101 Node::reflect_node_with_proto(
102 Box::new(HTMLElement::new_inherited(local_name, prefix, document)),
103 document,
104 proto,
105 can_gc,
106 )
107 }
108
109 fn is_body_or_frameset(&self) -> bool {
110 let eventtarget = self.upcast::<EventTarget>();
111 eventtarget.is::<HTMLBodyElement>() || eventtarget.is::<HTMLFrameSetElement>()
112 }
113
114 pub(crate) fn get_inner_outer_text(&self) -> DOMString {
120 let node = self.upcast::<Node>();
121 let window = node.owner_window();
122 let element = self.as_element();
123
124 let element_not_rendered = !node.is_connected() || !element.has_css_layout_box();
126 if element_not_rendered {
127 return node.GetTextContent().unwrap();
128 }
129
130 window.layout_reflow(QueryMsg::ElementInnerOuterTextQuery);
131 let text = window
132 .layout()
133 .query_element_inner_outer_text(node.to_trusted_node_address());
134
135 DOMString::from(text)
136 }
137
138 pub(crate) fn set_inner_text(&self, input: DOMString, can_gc: CanGc) {
140 let fragment = self.rendered_text_fragment(input, can_gc);
143
144 Node::replace_all(Some(fragment.upcast()), self.upcast::<Node>(), can_gc);
146 }
147}
148
149impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement {
150 fn Style(&self, can_gc: CanGc) -> DomRoot<CSSStyleDeclaration> {
152 self.style_decl.or_init(|| {
153 let global = self.owner_window();
154 CSSStyleDeclaration::new(
155 &global,
156 CSSStyleOwner::Element(Dom::from_ref(self.upcast())),
157 None,
158 CSSModificationAccess::ReadWrite,
159 can_gc,
160 )
161 })
162 }
163
164 make_getter!(Title, "title");
166 make_setter!(SetTitle, "title");
168
169 make_getter!(Lang, "lang");
171 make_setter!(SetLang, "lang");
173
174 make_enumerated_getter!(
176 Dir,
177 "dir",
178 "ltr" | "rtl" | "auto",
179 missing => "",
180 invalid => ""
181 );
182
183 make_setter!(SetDir, "dir");
185
186 make_bool_getter!(Hidden, "hidden");
188 make_bool_setter!(SetHidden, "hidden");
190
191 global_event_handlers!(NoOnload);
193
194 fn Dataset(&self, can_gc: CanGc) -> DomRoot<DOMStringMap> {
196 self.dataset.or_init(|| DOMStringMap::new(self, can_gc))
197 }
198
199 fn GetOnerror(&self, can_gc: CanGc) -> Option<Rc<OnErrorEventHandlerNonNull>> {
201 if self.is_body_or_frameset() {
202 let document = self.owner_document();
203 if document.has_browsing_context() {
204 document.window().GetOnerror()
205 } else {
206 None
207 }
208 } else {
209 self.upcast::<EventTarget>()
210 .get_event_handler_common("error", can_gc)
211 }
212 }
213
214 fn SetOnerror(&self, listener: Option<Rc<OnErrorEventHandlerNonNull>>) {
216 if self.is_body_or_frameset() {
217 let document = self.owner_document();
218 if document.has_browsing_context() {
219 document.window().SetOnerror(listener)
220 }
221 } else {
222 self.upcast::<EventTarget>()
224 .set_error_event_handler("error", listener)
225 }
226 }
227
228 fn GetOnload(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
230 if self.is_body_or_frameset() {
231 let document = self.owner_document();
232 if document.has_browsing_context() {
233 document.window().GetOnload()
234 } else {
235 None
236 }
237 } else {
238 self.upcast::<EventTarget>()
239 .get_event_handler_common("load", can_gc)
240 }
241 }
242
243 fn SetOnload(&self, listener: Option<Rc<EventHandlerNonNull>>) {
245 if self.is_body_or_frameset() {
246 let document = self.owner_document();
247 if document.has_browsing_context() {
248 document.window().SetOnload(listener)
249 }
250 } else {
251 self.upcast::<EventTarget>()
252 .set_event_handler_common("load", listener)
253 }
254 }
255
256 fn GetOnblur(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
258 if self.is_body_or_frameset() {
259 let document = self.owner_document();
260 if document.has_browsing_context() {
261 document.window().GetOnblur()
262 } else {
263 None
264 }
265 } else {
266 self.upcast::<EventTarget>()
267 .get_event_handler_common("blur", can_gc)
268 }
269 }
270
271 fn SetOnblur(&self, listener: Option<Rc<EventHandlerNonNull>>) {
273 if self.is_body_or_frameset() {
274 let document = self.owner_document();
275 if document.has_browsing_context() {
276 document.window().SetOnblur(listener)
277 }
278 } else {
279 self.upcast::<EventTarget>()
280 .set_event_handler_common("blur", listener)
281 }
282 }
283
284 fn GetOnfocus(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
286 if self.is_body_or_frameset() {
287 let document = self.owner_document();
288 if document.has_browsing_context() {
289 document.window().GetOnfocus()
290 } else {
291 None
292 }
293 } else {
294 self.upcast::<EventTarget>()
295 .get_event_handler_common("focus", can_gc)
296 }
297 }
298
299 fn SetOnfocus(&self, listener: Option<Rc<EventHandlerNonNull>>) {
301 if self.is_body_or_frameset() {
302 let document = self.owner_document();
303 if document.has_browsing_context() {
304 document.window().SetOnfocus(listener)
305 }
306 } else {
307 self.upcast::<EventTarget>()
308 .set_event_handler_common("focus", listener)
309 }
310 }
311
312 fn GetOnresize(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
314 if self.is_body_or_frameset() {
315 let document = self.owner_document();
316 if document.has_browsing_context() {
317 document.window().GetOnresize()
318 } else {
319 None
320 }
321 } else {
322 self.upcast::<EventTarget>()
323 .get_event_handler_common("resize", can_gc)
324 }
325 }
326
327 fn SetOnresize(&self, listener: Option<Rc<EventHandlerNonNull>>) {
329 if self.is_body_or_frameset() {
330 let document = self.owner_document();
331 if document.has_browsing_context() {
332 document.window().SetOnresize(listener)
333 }
334 } else {
335 self.upcast::<EventTarget>()
336 .set_event_handler_common("resize", listener)
337 }
338 }
339
340 fn GetOnscroll(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
342 if self.is_body_or_frameset() {
343 let document = self.owner_document();
344 if document.has_browsing_context() {
345 document.window().GetOnscroll()
346 } else {
347 None
348 }
349 } else {
350 self.upcast::<EventTarget>()
351 .get_event_handler_common("scroll", can_gc)
352 }
353 }
354
355 fn SetOnscroll(&self, listener: Option<Rc<EventHandlerNonNull>>) {
357 if self.is_body_or_frameset() {
358 let document = self.owner_document();
359 if document.has_browsing_context() {
360 document.window().SetOnscroll(listener)
361 }
362 } else {
363 self.upcast::<EventTarget>()
364 .set_event_handler_common("scroll", listener)
365 }
366 }
367
368 fn Itemtypes(&self) -> Option<Vec<DOMString>> {
370 let atoms = self
371 .element
372 .get_tokenlist_attribute(&local_name!("itemtype"));
373
374 if atoms.is_empty() {
375 return None;
376 }
377
378 let mut item_attr_values = HashSet::new();
379 for attr_value in &atoms {
380 item_attr_values.insert(DOMString::from(String::from(attr_value.trim())));
381 }
382
383 Some(item_attr_values.into_iter().collect())
384 }
385
386 fn PropertyNames(&self) -> Option<Vec<DOMString>> {
388 let atoms = self
389 .element
390 .get_tokenlist_attribute(&local_name!("itemprop"));
391
392 if atoms.is_empty() {
393 return None;
394 }
395
396 let mut item_attr_values = HashSet::new();
397 for attr_value in &atoms {
398 item_attr_values.insert(DOMString::from(String::from(attr_value.trim())));
399 }
400
401 Some(item_attr_values.into_iter().collect())
402 }
403
404 fn Click(&self, can_gc: CanGc) {
406 let element = self.as_element();
407 if element.disabled_state() {
408 return;
409 }
410 if element.click_in_progress() {
411 return;
412 }
413 element.set_click_in_progress(true);
414
415 self.upcast::<Node>()
416 .fire_synthetic_pointer_event_not_trusted(DOMString::from("click"), can_gc);
417 element.set_click_in_progress(false);
418 }
419
420 fn Focus(&self, options: &FocusOptions, can_gc: CanGc) {
422 let document = self.owner_document();
425 document.request_focus_with_options(
426 Some(self.upcast()),
427 FocusInitiator::Local,
428 FocusOptions {
429 preventScroll: options.preventScroll,
430 },
431 can_gc,
432 );
433 }
434
435 fn Blur(&self, can_gc: CanGc) {
437 if !self.as_element().focus_state() {
440 return;
441 }
442 let document = self.owner_document();
444 document.request_focus(None, FocusInitiator::Local, can_gc);
445 }
446
447 fn GetOffsetParent(&self) -> Option<DomRoot<Element>> {
449 if self.is_body_element() || self.is::<HTMLHtmlElement>() {
450 return None;
451 }
452
453 let node = self.upcast::<Node>();
454 let window = self.owner_window();
455 let (element, _) = window.offset_parent_query(node);
456
457 element
458 }
459
460 fn OffsetTop(&self) -> i32 {
462 if self.is_body_element() {
463 return 0;
464 }
465
466 let node = self.upcast::<Node>();
467 let window = self.owner_window();
468 let (_, rect) = window.offset_parent_query(node);
469
470 rect.origin.y.to_nearest_px()
471 }
472
473 fn OffsetLeft(&self) -> i32 {
475 if self.is_body_element() {
476 return 0;
477 }
478
479 let node = self.upcast::<Node>();
480 let window = self.owner_window();
481 let (_, rect) = window.offset_parent_query(node);
482
483 rect.origin.x.to_nearest_px()
484 }
485
486 fn OffsetWidth(&self) -> i32 {
488 let node = self.upcast::<Node>();
489 let window = self.owner_window();
490 let (_, rect) = window.offset_parent_query(node);
491
492 rect.size.width.to_nearest_px()
493 }
494
495 fn OffsetHeight(&self) -> i32 {
497 let node = self.upcast::<Node>();
498 let window = self.owner_window();
499 let (_, rect) = window.offset_parent_query(node);
500
501 rect.size.height.to_nearest_px()
502 }
503
504 fn InnerText(&self) -> DOMString {
506 self.get_inner_outer_text()
507 }
508
509 fn SetInnerText(&self, input: DOMString, can_gc: CanGc) {
511 self.set_inner_text(input, can_gc)
512 }
513
514 fn GetOuterText(&self) -> Fallible<DOMString> {
516 Ok(self.get_inner_outer_text())
517 }
518
519 fn SetOuterText(&self, input: DOMString, can_gc: CanGc) -> Fallible<()> {
521 let Some(parent) = self.upcast::<Node>().GetParentNode() else {
523 return Err(Error::NoModificationAllowed);
524 };
525
526 let node = self.upcast::<Node>();
527 let document = self.owner_document();
528
529 let next = node.GetNextSibling();
531
532 let previous = node.GetPreviousSibling();
534
535 let fragment = self.rendered_text_fragment(input, can_gc);
538
539 if fragment.upcast::<Node>().children_count() == 0 {
542 let text_node = Text::new(DOMString::from("".to_owned()), &document, can_gc);
543
544 fragment
545 .upcast::<Node>()
546 .AppendChild(text_node.upcast(), can_gc)?;
547 }
548
549 parent.ReplaceChild(fragment.upcast(), node, can_gc)?;
551
552 if let Some(next_sibling) = next {
555 if let Some(node) = next_sibling.GetPreviousSibling() {
556 Self::merge_with_the_next_text_node(node, can_gc);
557 }
558 }
559
560 if let Some(previous) = previous {
562 Self::merge_with_the_next_text_node(previous, can_gc)
563 }
564
565 Ok(())
566 }
567
568 fn Translate(&self) -> bool {
570 self.as_element().is_translate_enabled()
571 }
572
573 fn SetTranslate(&self, yesno: bool, can_gc: CanGc) {
575 self.as_element().set_string_attribute(
576 &html5ever::local_name!("translate"),
577 match yesno {
578 true => DOMString::from("yes"),
579 false => DOMString::from("no"),
580 },
581 can_gc,
582 );
583 }
584
585 fn ContentEditable(&self) -> DOMString {
587 self.as_element()
589 .get_attribute(&ns!(), &local_name!("contenteditable"))
590 .map(|attr| DOMString::from(&**attr.value()))
591 .unwrap_or_else(|| DOMString::from("inherit"))
592 }
593
594 fn SetContentEditable(&self, _: DOMString) {
596 warn!("The contentEditable attribute is not implemented yet");
598 }
599
600 fn IsContentEditable(&self) -> bool {
602 false
604 }
605
606 fn AttachInternals(&self, can_gc: CanGc) -> Fallible<DomRoot<ElementInternals>> {
608 let element = self.as_element();
609 if element.get_is().is_some() {
611 return Err(Error::NotSupported);
612 }
613
614 let registry = self.owner_window().CustomElements();
619 let definition = registry.lookup_definition(self.as_element().local_name(), None);
620
621 let definition = match definition {
623 Some(definition) => definition,
624 None => return Err(Error::NotSupported),
625 };
626
627 if definition.disable_internals {
629 return Err(Error::NotSupported);
630 }
631
632 let internals = element.ensure_element_internals(can_gc);
634 if internals.attached() {
635 return Err(Error::NotSupported);
636 }
637
638 if !matches!(
641 element.get_custom_element_state(),
642 CustomElementState::Precustomized | CustomElementState::Custom
643 ) {
644 return Err(Error::NotSupported);
645 }
646
647 if self.is_form_associated_custom_element() {
648 element.init_state_for_internals();
649 }
650
651 internals.set_attached();
653 Ok(internals)
654 }
655
656 fn Nonce(&self) -> DOMString {
658 self.as_element().nonce_value().into()
659 }
660
661 fn SetNonce(&self, value: DOMString) {
663 self.as_element()
664 .update_nonce_internal_slot(value.to_string())
665 }
666
667 fn Autofocus(&self) -> bool {
669 self.element.has_attribute(&local_name!("autofocus"))
670 }
671
672 fn SetAutofocus(&self, autofocus: bool, can_gc: CanGc) {
674 self.element
675 .set_bool_attribute(&local_name!("autofocus"), autofocus, can_gc);
676 }
677}
678
679fn append_text_node_to_fragment(
680 document: &Document,
681 fragment: &DocumentFragment,
682 text: String,
683 can_gc: CanGc,
684) {
685 let text = Text::new(DOMString::from(text), document, can_gc);
686 fragment
687 .upcast::<Node>()
688 .AppendChild(text.upcast(), can_gc)
689 .unwrap();
690}
691
692static DATA_PREFIX: &str = "data-";
695static DATA_HYPHEN_SEPARATOR: char = '\x2d';
696
697fn to_snake_case(name: DOMString) -> DOMString {
698 let mut attr_name = String::with_capacity(name.len() + DATA_PREFIX.len());
699 attr_name.push_str(DATA_PREFIX);
700 for ch in name.chars() {
701 if ch.is_ascii_uppercase() {
702 attr_name.push(DATA_HYPHEN_SEPARATOR);
703 attr_name.push(ch.to_ascii_lowercase());
704 } else {
705 attr_name.push(ch);
706 }
707 }
708 DOMString::from(attr_name)
709}
710
711fn to_camel_case(name: &str) -> Option<DOMString> {
717 if !name.starts_with(DATA_PREFIX) {
718 return None;
719 }
720 let name = &name[5..];
721 let has_uppercase = name.chars().any(|curr_char| curr_char.is_ascii_uppercase());
722 if has_uppercase {
723 return None;
724 }
725 let mut result = String::with_capacity(name.len().saturating_sub(DATA_PREFIX.len()));
726 let mut name_chars = name.chars();
727 while let Some(curr_char) = name_chars.next() {
728 if curr_char == DATA_HYPHEN_SEPARATOR {
730 if let Some(next_char) = name_chars.next() {
731 if next_char.is_ascii_lowercase() {
732 result.push(next_char.to_ascii_uppercase());
733 } else {
734 result.push(curr_char);
735 result.push(next_char);
736 }
737 } else {
738 result.push(curr_char);
739 }
740 } else {
741 result.push(curr_char);
742 }
743 }
744 Some(DOMString::from(result))
745}
746
747impl HTMLElement {
748 pub(crate) fn set_custom_attr(
749 &self,
750 name: DOMString,
751 value: DOMString,
752 can_gc: CanGc,
753 ) -> ErrorResult {
754 if name
755 .chars()
756 .skip_while(|&ch| ch != '\u{2d}')
757 .nth(1)
758 .is_some_and(|ch| ch.is_ascii_lowercase())
759 {
760 return Err(Error::Syntax);
761 }
762 self.as_element()
763 .set_custom_attribute(to_snake_case(name), value, can_gc)
764 }
765
766 pub(crate) fn get_custom_attr(&self, local_name: DOMString) -> Option<DOMString> {
767 let local_name = LocalName::from(to_snake_case(local_name));
769 self.as_element()
770 .get_attribute(&ns!(), &local_name)
771 .map(|attr| {
772 DOMString::from(&**attr.value()) })
774 }
775
776 pub(crate) fn delete_custom_attr(&self, local_name: DOMString, can_gc: CanGc) {
777 let local_name = LocalName::from(to_snake_case(local_name));
779 self.as_element()
780 .remove_attribute(&ns!(), &local_name, can_gc);
781 }
782
783 pub(crate) fn is_labelable_element(&self) -> bool {
785 match self.upcast::<Node>().type_id() {
786 NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
787 HTMLElementTypeId::HTMLInputElement => {
788 self.downcast::<HTMLInputElement>().unwrap().input_type() != InputType::Hidden
789 },
790 HTMLElementTypeId::HTMLButtonElement |
791 HTMLElementTypeId::HTMLMeterElement |
792 HTMLElementTypeId::HTMLOutputElement |
793 HTMLElementTypeId::HTMLProgressElement |
794 HTMLElementTypeId::HTMLSelectElement |
795 HTMLElementTypeId::HTMLTextAreaElement => true,
796 _ => self.is_form_associated_custom_element(),
797 },
798 _ => false,
799 }
800 }
801
802 pub(crate) fn is_form_associated_custom_element(&self) -> bool {
804 if let Some(definition) = self.as_element().get_custom_element_definition() {
805 definition.is_autonomous() && definition.form_associated
806 } else {
807 false
808 }
809 }
810
811 pub(crate) fn is_listed_element(&self) -> bool {
813 match self.upcast::<Node>().type_id() {
814 NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
815 HTMLElementTypeId::HTMLButtonElement |
816 HTMLElementTypeId::HTMLFieldSetElement |
817 HTMLElementTypeId::HTMLInputElement |
818 HTMLElementTypeId::HTMLObjectElement |
819 HTMLElementTypeId::HTMLOutputElement |
820 HTMLElementTypeId::HTMLSelectElement |
821 HTMLElementTypeId::HTMLTextAreaElement => true,
822 _ => self.is_form_associated_custom_element(),
823 },
824 _ => false,
825 }
826 }
827
828 pub(crate) fn is_body_element(&self) -> bool {
830 let self_node = self.upcast::<Node>();
831 self_node.GetParentNode().is_some_and(|parent| {
832 let parent_node = parent.upcast::<Node>();
833 (self_node.is::<HTMLBodyElement>() || self_node.is::<HTMLFrameSetElement>()) &&
834 parent_node.is::<HTMLHtmlElement>() &&
835 self_node
836 .preceding_siblings()
837 .all(|n| !n.is::<HTMLBodyElement>() && !n.is::<HTMLFrameSetElement>())
838 })
839 }
840
841 pub(crate) fn is_submittable_element(&self) -> bool {
843 match self.upcast::<Node>().type_id() {
844 NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
845 HTMLElementTypeId::HTMLButtonElement |
846 HTMLElementTypeId::HTMLInputElement |
847 HTMLElementTypeId::HTMLSelectElement |
848 HTMLElementTypeId::HTMLTextAreaElement => true,
849 _ => self.is_form_associated_custom_element(),
850 },
851 _ => false,
852 }
853 }
854
855 pub(crate) fn supported_prop_names_custom_attr(&self) -> Vec<DOMString> {
856 let element = self.as_element();
857 element
858 .attrs()
859 .iter()
860 .filter_map(|attr| {
861 let raw_name = attr.local_name();
862 to_camel_case(raw_name)
863 })
864 .collect()
865 }
866
867 pub(crate) fn label_at(&self, index: u32) -> Option<DomRoot<Node>> {
870 let element = self.as_element();
871
872 let root_element = element.root_element();
883 let root_node = root_element.upcast::<Node>();
884 root_node
885 .traverse_preorder(ShadowIncluding::No)
886 .filter_map(DomRoot::downcast::<HTMLLabelElement>)
887 .filter(|elem| match elem.GetControl() {
888 Some(control) => &*control == self,
889 _ => false,
890 })
891 .nth(index as usize)
892 .map(|n| DomRoot::from_ref(n.upcast::<Node>()))
893 }
894
895 pub(crate) fn labels_count(&self) -> u32 {
898 let element = self.as_element();
900 let root_element = element.root_element();
901 let root_node = root_element.upcast::<Node>();
902 root_node
903 .traverse_preorder(ShadowIncluding::No)
904 .filter_map(DomRoot::downcast::<HTMLLabelElement>)
905 .filter(|elem| match elem.GetControl() {
906 Some(control) => &*control == self,
907 _ => false,
908 })
909 .count() as u32
910 }
911
912 pub(crate) fn directionality(&self) -> Option<String> {
916 let element_direction: &str = &self.Dir();
917
918 if element_direction == "ltr" {
919 return Some("ltr".to_owned());
920 }
921
922 if element_direction == "rtl" {
923 return Some("rtl".to_owned());
924 }
925
926 if let Some(input) = self.downcast::<HTMLInputElement>() {
927 if input.input_type() == InputType::Tel {
928 return Some("ltr".to_owned());
929 }
930 }
931
932 if element_direction == "auto" {
933 if let Some(directionality) = self
934 .downcast::<HTMLInputElement>()
935 .and_then(|input| input.auto_directionality())
936 {
937 return Some(directionality);
938 }
939
940 if let Some(area) = self.downcast::<HTMLTextAreaElement>() {
941 return Some(area.auto_directionality());
942 }
943 }
944
945 None
952 }
953
954 pub(crate) fn summary_activation_behavior(&self) {
956 debug_assert!(self.as_element().local_name() == &local_name!("summary"));
957
958 if !self.is_a_summary_for_its_parent_details() {
960 return;
961 }
962
963 let parent = if self.is_implicit_summary_element() {
965 DomRoot::downcast::<HTMLDetailsElement>(self.containing_shadow_root().unwrap().Host())
966 .unwrap()
967 } else {
968 self.upcast::<Node>()
969 .GetParentNode()
970 .and_then(DomRoot::downcast::<HTMLDetailsElement>)
971 .unwrap()
972 };
973
974 parent.toggle();
977 }
978
979 fn is_a_summary_for_its_parent_details(&self) -> bool {
981 if self.is_implicit_summary_element() {
982 return true;
983 }
984
985 let Some(parent) = self.upcast::<Node>().GetParentNode() else {
988 return false;
989 };
990
991 let Some(details) = parent.downcast::<HTMLDetailsElement>() else {
993 return false;
994 };
995
996 details
1000 .find_corresponding_summary_element()
1001 .is_some_and(|summary| &*summary == self.upcast())
1002 }
1003
1004 fn is_implicit_summary_element(&self) -> bool {
1007 self.containing_shadow_root()
1011 .as_deref()
1012 .map(ShadowRoot::Host)
1013 .is_some_and(|host| host.is::<HTMLDetailsElement>())
1014 }
1015
1016 fn rendered_text_fragment(&self, input: DOMString, can_gc: CanGc) -> DomRoot<DocumentFragment> {
1018 let document = self.owner_document();
1020 let fragment = DocumentFragment::new(&document, can_gc);
1021
1022 let mut position = input.chars().peekable();
1025
1026 let mut text = String::new();
1028
1029 while let Some(ch) = position.next() {
1031 match ch {
1032 '\u{000A}' | '\u{000D}' => {
1035 if ch == '\u{000D}' && position.peek() == Some(&'\u{000A}') {
1036 position.next();
1039 }
1040
1041 if !text.is_empty() {
1042 append_text_node_to_fragment(&document, &fragment, text, can_gc);
1043 text = String::new();
1044 }
1045
1046 let br = HTMLBRElement::new(local_name!("br"), None, &document, None, can_gc);
1047 fragment
1048 .upcast::<Node>()
1049 .AppendChild(br.upcast(), can_gc)
1050 .unwrap();
1051 },
1052 _ => {
1053 text.push(ch);
1056 },
1057 }
1058 }
1059
1060 if !text.is_empty() {
1063 append_text_node_to_fragment(&document, &fragment, text, can_gc);
1064 }
1065
1066 fragment
1067 }
1068
1069 fn merge_with_the_next_text_node(node: DomRoot<Node>, can_gc: CanGc) {
1075 if !node.is::<Text>() {
1077 return;
1078 }
1079
1080 let next = match node.GetNextSibling() {
1082 Some(next) => next,
1083 None => return,
1084 };
1085
1086 if !next.is::<Text>() {
1088 return;
1089 }
1090 let node_chars = node.downcast::<CharacterData>().expect("Node is Text");
1092 let next_chars = next.downcast::<CharacterData>().expect("Next node is Text");
1093 node_chars
1094 .ReplaceData(node_chars.Length(), 0, next_chars.Data())
1095 .expect("Got chars from Text");
1096
1097 next.remove_self(can_gc);
1099 }
1100}
1101
1102impl VirtualMethods for HTMLElement {
1103 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1104 Some(self.as_element() as &dyn VirtualMethods)
1105 }
1106
1107 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1108 self.super_type()
1109 .unwrap()
1110 .attribute_mutated(attr, mutation, can_gc);
1111 let element = self.as_element();
1112 match (attr.local_name(), mutation) {
1113 (name, mutation)
1115 if name.starts_with("on") && EventTarget::is_content_event_handler(name) =>
1116 {
1117 let evtarget = self.upcast::<EventTarget>();
1118 let event_name = &name[2..];
1119 match mutation {
1120 AttributeMutation::Set(_) => {
1122 let source = &**attr.value();
1123 let source_line = 1; evtarget.set_event_handler_uncompiled(
1125 self.owner_window().get_url(),
1126 source_line,
1127 event_name,
1128 source,
1129 );
1130 },
1131 AttributeMutation::Removed => {
1133 evtarget.set_event_handler_common::<EventHandlerNonNull>(event_name, None);
1134 },
1135 }
1136 },
1137
1138 (&local_name!("form"), mutation) if self.is_form_associated_custom_element() => {
1139 self.form_attribute_mutated(mutation, can_gc);
1140 },
1141 (&local_name!("disabled"), AttributeMutation::Set(_))
1143 if self.is_form_associated_custom_element() && element.enabled_state() =>
1144 {
1145 element.set_disabled_state(true);
1146 element.set_enabled_state(false);
1147 ScriptThread::enqueue_callback_reaction(
1148 element,
1149 CallbackReaction::FormDisabled(true),
1150 None,
1151 );
1152 },
1153 (&local_name!("disabled"), AttributeMutation::Removed)
1156 if self.is_form_associated_custom_element() && element.disabled_state() =>
1157 {
1158 element.set_disabled_state(false);
1159 element.set_enabled_state(true);
1160 element.check_ancestors_disabled_state_for_form_control();
1161 if element.enabled_state() {
1162 ScriptThread::enqueue_callback_reaction(
1163 element,
1164 CallbackReaction::FormDisabled(false),
1165 None,
1166 );
1167 }
1168 },
1169 (&local_name!("readonly"), mutation) if self.is_form_associated_custom_element() => {
1170 match mutation {
1171 AttributeMutation::Set(_) => {
1172 element.set_read_write_state(true);
1173 },
1174 AttributeMutation::Removed => {
1175 element.set_read_write_state(false);
1176 },
1177 }
1178 },
1179 (&local_name!("nonce"), mutation) => match mutation {
1180 AttributeMutation::Set(_) => {
1181 let nonce = &**attr.value();
1182 element.update_nonce_internal_slot(nonce.to_owned());
1183 },
1184 AttributeMutation::Removed => {
1185 element.update_nonce_internal_slot("".to_owned());
1186 },
1187 },
1188 _ => {},
1189 }
1190 }
1191
1192 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1193 if let Some(super_type) = self.super_type() {
1194 super_type.bind_to_tree(context, can_gc);
1195 }
1196 let element = self.as_element();
1197 element.update_sequentially_focusable_status(can_gc);
1198
1199 if self.is_form_associated_custom_element() && element.enabled_state() {
1202 element.check_ancestors_disabled_state_for_form_control();
1203 if element.disabled_state() {
1204 ScriptThread::enqueue_callback_reaction(
1205 element,
1206 CallbackReaction::FormDisabled(true),
1207 None,
1208 );
1209 }
1210 }
1211 }
1212
1213 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1214 if let Some(super_type) = self.super_type() {
1215 super_type.unbind_from_tree(context, can_gc);
1216 }
1217
1218 let element = self.as_element();
1223 if self.is_form_associated_custom_element() && element.disabled_state() {
1224 element.check_disabled_attribute();
1225 element.check_ancestors_disabled_state_for_form_control();
1226 if element.enabled_state() {
1227 ScriptThread::enqueue_callback_reaction(
1228 element,
1229 CallbackReaction::FormDisabled(false),
1230 None,
1231 );
1232 }
1233 }
1234 }
1235
1236 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1237 match *name {
1238 local_name!("itemprop") => AttrValue::from_serialized_tokenlist(value.into()),
1239 local_name!("itemtype") => AttrValue::from_serialized_tokenlist(value.into()),
1240 _ => self
1241 .super_type()
1242 .unwrap()
1243 .parse_plain_attribute(name, value),
1244 }
1245 }
1246}
1247
1248impl Activatable for HTMLElement {
1249 fn as_element(&self) -> &Element {
1250 self.upcast::<Element>()
1251 }
1252
1253 fn is_instance_activatable(&self) -> bool {
1254 self.as_element().local_name() == &local_name!("summary")
1255 }
1256
1257 fn activation_behavior(&self, _event: &Event, _target: &EventTarget, _can_gc: CanGc) {
1259 self.summary_activation_behavior();
1260 }
1261}
1262
1263impl FormControl for HTMLElement {
1269 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1270 debug_assert!(self.is_form_associated_custom_element());
1271 self.as_element()
1272 .get_element_internals()
1273 .and_then(|e| e.form_owner())
1274 }
1275
1276 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
1277 debug_assert!(self.is_form_associated_custom_element());
1278 self.as_element()
1279 .ensure_element_internals(CanGc::note())
1280 .set_form_owner(form);
1281 }
1282
1283 fn to_element(&self) -> &Element {
1284 self.as_element()
1285 }
1286
1287 fn is_listed(&self) -> bool {
1288 debug_assert!(self.is_form_associated_custom_element());
1289 true
1290 }
1291
1292 }