1use std::collections::HashSet;
6use std::default::Default;
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use html5ever::{LocalName, Prefix, QualName, local_name, ns};
11use js::rust::HandleObject;
12use layout_api::{QueryMsg, ScrollContainerQueryFlags, ScrollContainerResponse};
13use script_bindings::codegen::GenericBindings::DocumentBinding::DocumentMethods;
14use style::attr::AttrValue;
15use stylo_dom::ElementState;
16
17use crate::dom::activation::Activatable;
18use crate::dom::attr::Attr;
19use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterData_Binding::CharacterDataMethods;
20use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::{
21 EventHandlerNonNull, OnErrorEventHandlerNonNull,
22};
23use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
24use crate::dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
25use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
26use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
27use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
28use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
29use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
30use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
31use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
32use crate::dom::bindings::str::DOMString;
33use crate::dom::characterdata::CharacterData;
34use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
35use crate::dom::customelementregistry::{CallbackReaction, CustomElementState};
36use crate::dom::document::{Document, FocusInitiator};
37use crate::dom::documentfragment::DocumentFragment;
38use crate::dom::domstringmap::DOMStringMap;
39use crate::dom::element::{AttributeMutation, CustomElementCreationMode, Element, ElementCreator};
40use crate::dom::elementinternals::ElementInternals;
41use crate::dom::event::Event;
42use crate::dom::eventtarget::EventTarget;
43use crate::dom::html::htmlbodyelement::HTMLBodyElement;
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::{
52 BindContext, Node, NodeTraits, ShadowIncluding, UnbindContext, from_untrusted_node_address,
53};
54use crate::dom::shadowroot::ShadowRoot;
55use crate::dom::text::Text;
56use crate::dom::virtualmethods::VirtualMethods;
57use crate::script_runtime::CanGc;
58use crate::script_thread::ScriptThread;
59
60#[dom_struct]
61pub(crate) struct HTMLElement {
62 element: Element,
63 style_decl: MutNullableDom<CSSStyleDeclaration>,
64 dataset: MutNullableDom<DOMStringMap>,
65}
66
67impl HTMLElement {
68 pub(crate) fn new_inherited(
69 tag_name: LocalName,
70 prefix: Option<Prefix>,
71 document: &Document,
72 ) -> HTMLElement {
73 HTMLElement::new_inherited_with_state(ElementState::empty(), tag_name, prefix, document)
74 }
75
76 pub(crate) fn new_inherited_with_state(
77 state: ElementState,
78 tag_name: LocalName,
79 prefix: Option<Prefix>,
80 document: &Document,
81 ) -> HTMLElement {
82 HTMLElement {
83 element: Element::new_inherited_with_state(
84 state,
85 tag_name,
86 ns!(html),
87 prefix,
88 document,
89 ),
90 style_decl: Default::default(),
91 dataset: Default::default(),
92 }
93 }
94
95 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
96 pub(crate) fn new(
97 local_name: LocalName,
98 prefix: Option<Prefix>,
99 document: &Document,
100 proto: Option<HandleObject>,
101 can_gc: CanGc,
102 ) -> DomRoot<HTMLElement> {
103 Node::reflect_node_with_proto(
104 Box::new(HTMLElement::new_inherited(local_name, prefix, document)),
105 document,
106 proto,
107 can_gc,
108 )
109 }
110
111 fn is_body_or_frameset(&self) -> bool {
112 let eventtarget = self.upcast::<EventTarget>();
113 eventtarget.is::<HTMLBodyElement>() || eventtarget.is::<HTMLFrameSetElement>()
114 }
115
116 pub(crate) fn get_inner_outer_text(&self) -> DOMString {
122 let node = self.upcast::<Node>();
123 let window = node.owner_window();
124 let element = self.as_element();
125
126 let element_not_rendered = !node.is_connected() || !element.has_css_layout_box();
128 if element_not_rendered {
129 return node.GetTextContent().unwrap();
130 }
131
132 window.layout_reflow(QueryMsg::ElementInnerOuterTextQuery);
133 let text = window
134 .layout()
135 .query_element_inner_outer_text(node.to_trusted_node_address());
136
137 DOMString::from(text)
138 }
139
140 pub(crate) fn set_inner_text(&self, input: DOMString, can_gc: CanGc) {
142 let fragment = self.rendered_text_fragment(input, can_gc);
145
146 Node::replace_all(Some(fragment.upcast()), self.upcast::<Node>(), can_gc);
148 }
149}
150
151impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement {
152 fn Style(&self, can_gc: CanGc) -> DomRoot<CSSStyleDeclaration> {
154 self.style_decl.or_init(|| {
155 let global = self.owner_window();
156 CSSStyleDeclaration::new(
157 &global,
158 CSSStyleOwner::Element(Dom::from_ref(self.upcast())),
159 None,
160 CSSModificationAccess::ReadWrite,
161 can_gc,
162 )
163 })
164 }
165
166 make_getter!(Title, "title");
168 make_setter!(SetTitle, "title");
170
171 make_getter!(Lang, "lang");
173 make_setter!(SetLang, "lang");
175
176 make_enumerated_getter!(
178 Dir,
179 "dir",
180 "ltr" | "rtl" | "auto",
181 missing => "",
182 invalid => ""
183 );
184
185 make_setter!(SetDir, "dir");
187
188 make_bool_getter!(Hidden, "hidden");
190 make_bool_setter!(SetHidden, "hidden");
192
193 global_event_handlers!(NoOnload);
195
196 fn Dataset(&self, can_gc: CanGc) -> DomRoot<DOMStringMap> {
198 self.dataset.or_init(|| DOMStringMap::new(self, can_gc))
199 }
200
201 fn GetOnerror(&self, can_gc: CanGc) -> Option<Rc<OnErrorEventHandlerNonNull>> {
203 if self.is_body_or_frameset() {
204 let document = self.owner_document();
205 if document.has_browsing_context() {
206 document.window().GetOnerror()
207 } else {
208 None
209 }
210 } else {
211 self.upcast::<EventTarget>()
212 .get_event_handler_common("error", can_gc)
213 }
214 }
215
216 fn SetOnerror(&self, listener: Option<Rc<OnErrorEventHandlerNonNull>>) {
218 if self.is_body_or_frameset() {
219 let document = self.owner_document();
220 if document.has_browsing_context() {
221 document.window().SetOnerror(listener)
222 }
223 } else {
224 self.upcast::<EventTarget>()
226 .set_error_event_handler("error", listener)
227 }
228 }
229
230 fn GetOnload(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
232 if self.is_body_or_frameset() {
233 let document = self.owner_document();
234 if document.has_browsing_context() {
235 document.window().GetOnload()
236 } else {
237 None
238 }
239 } else {
240 self.upcast::<EventTarget>()
241 .get_event_handler_common("load", can_gc)
242 }
243 }
244
245 fn SetOnload(&self, listener: Option<Rc<EventHandlerNonNull>>) {
247 if self.is_body_or_frameset() {
248 let document = self.owner_document();
249 if document.has_browsing_context() {
250 document.window().SetOnload(listener)
251 }
252 } else {
253 self.upcast::<EventTarget>()
254 .set_event_handler_common("load", listener)
255 }
256 }
257
258 fn GetOnblur(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
260 if self.is_body_or_frameset() {
261 let document = self.owner_document();
262 if document.has_browsing_context() {
263 document.window().GetOnblur()
264 } else {
265 None
266 }
267 } else {
268 self.upcast::<EventTarget>()
269 .get_event_handler_common("blur", can_gc)
270 }
271 }
272
273 fn SetOnblur(&self, listener: Option<Rc<EventHandlerNonNull>>) {
275 if self.is_body_or_frameset() {
276 let document = self.owner_document();
277 if document.has_browsing_context() {
278 document.window().SetOnblur(listener)
279 }
280 } else {
281 self.upcast::<EventTarget>()
282 .set_event_handler_common("blur", listener)
283 }
284 }
285
286 fn GetOnfocus(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
288 if self.is_body_or_frameset() {
289 let document = self.owner_document();
290 if document.has_browsing_context() {
291 document.window().GetOnfocus()
292 } else {
293 None
294 }
295 } else {
296 self.upcast::<EventTarget>()
297 .get_event_handler_common("focus", can_gc)
298 }
299 }
300
301 fn SetOnfocus(&self, listener: Option<Rc<EventHandlerNonNull>>) {
303 if self.is_body_or_frameset() {
304 let document = self.owner_document();
305 if document.has_browsing_context() {
306 document.window().SetOnfocus(listener)
307 }
308 } else {
309 self.upcast::<EventTarget>()
310 .set_event_handler_common("focus", listener)
311 }
312 }
313
314 fn GetOnresize(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
316 if self.is_body_or_frameset() {
317 let document = self.owner_document();
318 if document.has_browsing_context() {
319 document.window().GetOnresize()
320 } else {
321 None
322 }
323 } else {
324 self.upcast::<EventTarget>()
325 .get_event_handler_common("resize", can_gc)
326 }
327 }
328
329 fn SetOnresize(&self, listener: Option<Rc<EventHandlerNonNull>>) {
331 if self.is_body_or_frameset() {
332 let document = self.owner_document();
333 if document.has_browsing_context() {
334 document.window().SetOnresize(listener)
335 }
336 } else {
337 self.upcast::<EventTarget>()
338 .set_event_handler_common("resize", listener)
339 }
340 }
341
342 fn GetOnscroll(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
344 if self.is_body_or_frameset() {
345 let document = self.owner_document();
346 if document.has_browsing_context() {
347 document.window().GetOnscroll()
348 } else {
349 None
350 }
351 } else {
352 self.upcast::<EventTarget>()
353 .get_event_handler_common("scroll", can_gc)
354 }
355 }
356
357 fn SetOnscroll(&self, listener: Option<Rc<EventHandlerNonNull>>) {
359 if self.is_body_or_frameset() {
360 let document = self.owner_document();
361 if document.has_browsing_context() {
362 document.window().SetOnscroll(listener)
363 }
364 } else {
365 self.upcast::<EventTarget>()
366 .set_event_handler_common("scroll", listener)
367 }
368 }
369
370 fn Itemtypes(&self) -> Option<Vec<DOMString>> {
372 let atoms = self
373 .element
374 .get_tokenlist_attribute(&local_name!("itemtype"));
375
376 if atoms.is_empty() {
377 return None;
378 }
379
380 #[allow(clippy::mutable_key_type)]
381 let mut item_attr_values = HashSet::new();
383 for attr_value in &atoms {
384 item_attr_values.insert(DOMString::from(String::from(attr_value.trim())));
385 }
386
387 Some(item_attr_values.into_iter().collect())
388 }
389
390 fn PropertyNames(&self) -> Option<Vec<DOMString>> {
392 let atoms = self
393 .element
394 .get_tokenlist_attribute(&local_name!("itemprop"));
395
396 if atoms.is_empty() {
397 return None;
398 }
399
400 #[allow(clippy::mutable_key_type)]
401 let mut item_attr_values = HashSet::new();
403 for attr_value in &atoms {
404 item_attr_values.insert(DOMString::from(String::from(attr_value.trim())));
405 }
406
407 Some(item_attr_values.into_iter().collect())
408 }
409
410 fn Click(&self, can_gc: CanGc) {
412 let element = self.as_element();
413 if element.disabled_state() {
414 return;
415 }
416 if element.click_in_progress() {
417 return;
418 }
419 element.set_click_in_progress(true);
420
421 self.upcast::<Node>()
422 .fire_synthetic_pointer_event_not_trusted(DOMString::from("click"), can_gc);
423 element.set_click_in_progress(false);
424 }
425
426 fn Focus(&self, options: &FocusOptions, can_gc: CanGc) {
428 let document = self.owner_document();
431 document.request_focus_with_options(
432 Some(self.upcast()),
433 FocusInitiator::Local,
434 FocusOptions {
435 preventScroll: options.preventScroll,
436 },
437 can_gc,
438 );
439 }
440
441 fn Blur(&self, can_gc: CanGc) {
443 if !self.as_element().focus_state() {
446 return;
447 }
448 let document = self.owner_document();
450 document.request_focus(None, FocusInitiator::Local, can_gc);
451 }
452
453 #[allow(unsafe_code)]
455 fn GetScrollParent(&self) -> Option<DomRoot<Element>> {
456 self.owner_window()
457 .scroll_container_query(
458 Some(self.upcast()),
459 ScrollContainerQueryFlags::ForScrollParent,
460 )
461 .and_then(|response| match response {
462 ScrollContainerResponse::Viewport(_) => self.owner_document().GetScrollingElement(),
463 ScrollContainerResponse::Element(parent_node_address, _) => {
464 let node = unsafe { from_untrusted_node_address(parent_node_address) };
465 DomRoot::downcast(node)
466 },
467 })
468 }
469
470 fn GetOffsetParent(&self) -> Option<DomRoot<Element>> {
472 if self.is::<HTMLBodyElement>() || self.upcast::<Element>().is_root() {
473 return None;
474 }
475
476 let node = self.upcast::<Node>();
477 let window = self.owner_window();
478 let (element, _) = window.offset_parent_query(node);
479
480 element
481 }
482
483 fn OffsetTop(&self) -> i32 {
485 if self.is_body_element() {
486 return 0;
487 }
488
489 let node = self.upcast::<Node>();
490 let window = self.owner_window();
491 let (_, rect) = window.offset_parent_query(node);
492
493 rect.origin.y.to_nearest_px()
494 }
495
496 fn OffsetLeft(&self) -> i32 {
498 if self.is_body_element() {
499 return 0;
500 }
501
502 let node = self.upcast::<Node>();
503 let window = self.owner_window();
504 let (_, rect) = window.offset_parent_query(node);
505
506 rect.origin.x.to_nearest_px()
507 }
508
509 fn OffsetWidth(&self) -> i32 {
511 let node = self.upcast::<Node>();
512 let window = self.owner_window();
513 let (_, rect) = window.offset_parent_query(node);
514
515 rect.size.width.to_nearest_px()
516 }
517
518 fn OffsetHeight(&self) -> i32 {
520 let node = self.upcast::<Node>();
521 let window = self.owner_window();
522 let (_, rect) = window.offset_parent_query(node);
523
524 rect.size.height.to_nearest_px()
525 }
526
527 fn InnerText(&self) -> DOMString {
529 self.get_inner_outer_text()
530 }
531
532 fn SetInnerText(&self, input: DOMString, can_gc: CanGc) {
534 self.set_inner_text(input, can_gc)
535 }
536
537 fn GetOuterText(&self) -> Fallible<DOMString> {
539 Ok(self.get_inner_outer_text())
540 }
541
542 fn SetOuterText(&self, input: DOMString, can_gc: CanGc) -> Fallible<()> {
544 let Some(parent) = self.upcast::<Node>().GetParentNode() else {
546 return Err(Error::NoModificationAllowed);
547 };
548
549 let node = self.upcast::<Node>();
550 let document = self.owner_document();
551
552 let next = node.GetNextSibling();
554
555 let previous = node.GetPreviousSibling();
557
558 let fragment = self.rendered_text_fragment(input, can_gc);
561
562 if fragment.upcast::<Node>().children_count() == 0 {
565 let text_node = Text::new(DOMString::from("".to_owned()), &document, can_gc);
566
567 fragment
568 .upcast::<Node>()
569 .AppendChild(text_node.upcast(), can_gc)?;
570 }
571
572 parent.ReplaceChild(fragment.upcast(), node, can_gc)?;
574
575 if let Some(next_sibling) = next {
578 if let Some(node) = next_sibling.GetPreviousSibling() {
579 Self::merge_with_the_next_text_node(node, can_gc);
580 }
581 }
582
583 if let Some(previous) = previous {
585 Self::merge_with_the_next_text_node(previous, can_gc)
586 }
587
588 Ok(())
589 }
590
591 fn Translate(&self) -> bool {
593 self.as_element().is_translate_enabled()
594 }
595
596 fn SetTranslate(&self, yesno: bool, can_gc: CanGc) {
598 self.as_element().set_string_attribute(
599 &html5ever::local_name!("translate"),
600 match yesno {
601 true => DOMString::from("yes"),
602 false => DOMString::from("no"),
603 },
604 can_gc,
605 );
606 }
607
608 fn ContentEditable(&self) -> DOMString {
610 self.as_element()
612 .get_attribute(&ns!(), &local_name!("contenteditable"))
613 .map(|attr| DOMString::from(&**attr.value()))
614 .unwrap_or_else(|| DOMString::from("inherit"))
615 }
616
617 fn SetContentEditable(&self, _: DOMString) {
619 warn!("The contentEditable attribute is not implemented yet");
621 }
622
623 fn IsContentEditable(&self) -> bool {
625 false
627 }
628
629 fn AttachInternals(&self, can_gc: CanGc) -> Fallible<DomRoot<ElementInternals>> {
631 let element = self.as_element();
632 if element.get_is().is_some() {
634 return Err(Error::NotSupported);
635 }
636
637 let registry = self.owner_window().CustomElements();
642 let definition = registry.lookup_definition(self.as_element().local_name(), None);
643
644 let definition = match definition {
646 Some(definition) => definition,
647 None => return Err(Error::NotSupported),
648 };
649
650 if definition.disable_internals {
652 return Err(Error::NotSupported);
653 }
654
655 let internals = element.ensure_element_internals(can_gc);
657 if internals.attached() {
658 return Err(Error::NotSupported);
659 }
660
661 if !matches!(
664 element.get_custom_element_state(),
665 CustomElementState::Precustomized | CustomElementState::Custom
666 ) {
667 return Err(Error::NotSupported);
668 }
669
670 if self.is_form_associated_custom_element() {
671 element.init_state_for_internals();
672 }
673
674 internals.set_attached();
676 Ok(internals)
677 }
678
679 fn Nonce(&self) -> DOMString {
681 self.as_element().nonce_value().into()
682 }
683
684 fn SetNonce(&self, value: DOMString) {
686 self.as_element()
687 .update_nonce_internal_slot(value.to_string())
688 }
689
690 fn Autofocus(&self) -> bool {
692 self.element.has_attribute(&local_name!("autofocus"))
693 }
694
695 fn SetAutofocus(&self, autofocus: bool, can_gc: CanGc) {
697 self.element
698 .set_bool_attribute(&local_name!("autofocus"), autofocus, can_gc);
699 }
700}
701
702fn append_text_node_to_fragment(
703 document: &Document,
704 fragment: &DocumentFragment,
705 text: String,
706 can_gc: CanGc,
707) {
708 let text = Text::new(DOMString::from(text), document, can_gc);
709 fragment
710 .upcast::<Node>()
711 .AppendChild(text.upcast(), can_gc)
712 .unwrap();
713}
714
715static DATA_PREFIX: &str = "data-";
718static DATA_HYPHEN_SEPARATOR: char = '\x2d';
719
720fn to_snake_case(name: DOMString) -> DOMString {
721 let mut attr_name = String::with_capacity(name.len() + DATA_PREFIX.len());
722 attr_name.push_str(DATA_PREFIX);
723 for ch in name.str().chars() {
724 if ch.is_ascii_uppercase() {
725 attr_name.push(DATA_HYPHEN_SEPARATOR);
726 attr_name.push(ch.to_ascii_lowercase());
727 } else {
728 attr_name.push(ch);
729 }
730 }
731 DOMString::from(attr_name)
732}
733
734fn to_camel_case(name: &str) -> Option<DOMString> {
740 if !name.starts_with(DATA_PREFIX) {
741 return None;
742 }
743 let name = &name[5..];
744 let has_uppercase = name.chars().any(|curr_char| curr_char.is_ascii_uppercase());
745 if has_uppercase {
746 return None;
747 }
748 let mut result = String::with_capacity(name.len().saturating_sub(DATA_PREFIX.len()));
749 let mut name_chars = name.chars();
750 while let Some(curr_char) = name_chars.next() {
751 if curr_char == DATA_HYPHEN_SEPARATOR {
753 if let Some(next_char) = name_chars.next() {
754 if next_char.is_ascii_lowercase() {
755 result.push(next_char.to_ascii_uppercase());
756 } else {
757 result.push(curr_char);
758 result.push(next_char);
759 }
760 } else {
761 result.push(curr_char);
762 }
763 } else {
764 result.push(curr_char);
765 }
766 }
767 Some(DOMString::from(result))
768}
769
770impl HTMLElement {
771 pub(crate) fn set_custom_attr(
772 &self,
773 name: DOMString,
774 value: DOMString,
775 can_gc: CanGc,
776 ) -> ErrorResult {
777 if name
778 .str()
779 .chars()
780 .skip_while(|&ch| ch != '\u{2d}')
781 .nth(1)
782 .is_some_and(|ch| ch.is_ascii_lowercase())
783 {
784 return Err(Error::Syntax(None));
785 }
786 self.as_element()
787 .set_custom_attribute(to_snake_case(name), value, can_gc)
788 }
789
790 pub(crate) fn get_custom_attr(&self, local_name: DOMString) -> Option<DOMString> {
791 let local_name = LocalName::from(to_snake_case(local_name));
793 self.as_element()
794 .get_attribute(&ns!(), &local_name)
795 .map(|attr| {
796 DOMString::from(&**attr.value()) })
798 }
799
800 pub(crate) fn delete_custom_attr(&self, local_name: DOMString, can_gc: CanGc) {
801 let local_name = LocalName::from(to_snake_case(local_name));
803 self.as_element()
804 .remove_attribute(&ns!(), &local_name, can_gc);
805 }
806
807 pub(crate) fn is_labelable_element(&self) -> bool {
809 match self.upcast::<Node>().type_id() {
810 NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
811 HTMLElementTypeId::HTMLInputElement => {
812 self.downcast::<HTMLInputElement>().unwrap().input_type() != InputType::Hidden
813 },
814 HTMLElementTypeId::HTMLButtonElement |
815 HTMLElementTypeId::HTMLMeterElement |
816 HTMLElementTypeId::HTMLOutputElement |
817 HTMLElementTypeId::HTMLProgressElement |
818 HTMLElementTypeId::HTMLSelectElement |
819 HTMLElementTypeId::HTMLTextAreaElement => true,
820 _ => self.is_form_associated_custom_element(),
821 },
822 _ => false,
823 }
824 }
825
826 pub(crate) fn is_form_associated_custom_element(&self) -> bool {
828 if let Some(definition) = self.as_element().get_custom_element_definition() {
829 definition.is_autonomous() && definition.form_associated
830 } else {
831 false
832 }
833 }
834
835 pub(crate) fn is_listed_element(&self) -> bool {
837 match self.upcast::<Node>().type_id() {
838 NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
839 HTMLElementTypeId::HTMLButtonElement |
840 HTMLElementTypeId::HTMLFieldSetElement |
841 HTMLElementTypeId::HTMLInputElement |
842 HTMLElementTypeId::HTMLObjectElement |
843 HTMLElementTypeId::HTMLOutputElement |
844 HTMLElementTypeId::HTMLSelectElement |
845 HTMLElementTypeId::HTMLTextAreaElement => true,
846 _ => self.is_form_associated_custom_element(),
847 },
848 _ => false,
849 }
850 }
851
852 pub(crate) fn is_body_element(&self) -> bool {
854 let self_node = self.upcast::<Node>();
855 self_node.GetParentNode().is_some_and(|parent| {
856 let parent_node = parent.upcast::<Node>();
857 (self_node.is::<HTMLBodyElement>() || self_node.is::<HTMLFrameSetElement>()) &&
858 parent_node.is::<HTMLHtmlElement>() &&
859 self_node
860 .preceding_siblings()
861 .all(|n| !n.is::<HTMLBodyElement>() && !n.is::<HTMLFrameSetElement>())
862 })
863 }
864
865 pub(crate) fn is_submittable_element(&self) -> bool {
867 match self.upcast::<Node>().type_id() {
868 NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
869 HTMLElementTypeId::HTMLButtonElement |
870 HTMLElementTypeId::HTMLInputElement |
871 HTMLElementTypeId::HTMLSelectElement |
872 HTMLElementTypeId::HTMLTextAreaElement => true,
873 _ => self.is_form_associated_custom_element(),
874 },
875 _ => false,
876 }
877 }
878
879 pub(crate) fn supported_prop_names_custom_attr(&self) -> Vec<DOMString> {
880 let element = self.as_element();
881 element
882 .attrs()
883 .iter()
884 .filter_map(|attr| {
885 let raw_name = attr.local_name();
886 to_camel_case(raw_name)
887 })
888 .collect()
889 }
890
891 pub(crate) fn label_at(&self, index: u32) -> Option<DomRoot<Node>> {
894 let element = self.as_element();
895
896 let root_element = element.root_element();
907 let root_node = root_element.upcast::<Node>();
908 root_node
909 .traverse_preorder(ShadowIncluding::No)
910 .filter_map(DomRoot::downcast::<HTMLLabelElement>)
911 .filter(|elem| match elem.GetControl() {
912 Some(control) => &*control == self,
913 _ => false,
914 })
915 .nth(index as usize)
916 .map(|n| DomRoot::from_ref(n.upcast::<Node>()))
917 }
918
919 pub(crate) fn labels_count(&self) -> u32 {
922 let element = self.as_element();
924 let root_element = element.root_element();
925 let root_node = root_element.upcast::<Node>();
926 root_node
927 .traverse_preorder(ShadowIncluding::No)
928 .filter_map(DomRoot::downcast::<HTMLLabelElement>)
929 .filter(|elem| match elem.GetControl() {
930 Some(control) => &*control == self,
931 _ => false,
932 })
933 .count() as u32
934 }
935
936 pub(crate) fn directionality(&self) -> Option<String> {
940 let element_direction = &self.Dir();
941
942 if element_direction == "ltr" {
943 return Some("ltr".to_owned());
944 }
945
946 if element_direction == "rtl" {
947 return Some("rtl".to_owned());
948 }
949
950 if let Some(input) = self.downcast::<HTMLInputElement>() {
951 if input.input_type() == InputType::Tel {
952 return Some("ltr".to_owned());
953 }
954 }
955
956 if element_direction == "auto" {
957 if let Some(directionality) = self
958 .downcast::<HTMLInputElement>()
959 .and_then(|input| input.auto_directionality())
960 {
961 return Some(directionality);
962 }
963
964 if let Some(area) = self.downcast::<HTMLTextAreaElement>() {
965 return Some(area.auto_directionality());
966 }
967 }
968
969 None
976 }
977
978 pub(crate) fn summary_activation_behavior(&self) {
980 debug_assert!(self.as_element().local_name() == &local_name!("summary"));
981
982 if !self.is_a_summary_for_its_parent_details() {
984 return;
985 }
986
987 let parent = if self.is_implicit_summary_element() {
989 DomRoot::downcast::<HTMLDetailsElement>(self.containing_shadow_root().unwrap().Host())
990 .unwrap()
991 } else {
992 self.upcast::<Node>()
993 .GetParentNode()
994 .and_then(DomRoot::downcast::<HTMLDetailsElement>)
995 .unwrap()
996 };
997
998 parent.toggle();
1001 }
1002
1003 fn is_a_summary_for_its_parent_details(&self) -> bool {
1005 if self.is_implicit_summary_element() {
1006 return true;
1007 }
1008
1009 let Some(parent) = self.upcast::<Node>().GetParentNode() else {
1012 return false;
1013 };
1014
1015 let Some(details) = parent.downcast::<HTMLDetailsElement>() else {
1017 return false;
1018 };
1019
1020 details
1024 .find_corresponding_summary_element()
1025 .is_some_and(|summary| &*summary == self.upcast())
1026 }
1027
1028 fn is_implicit_summary_element(&self) -> bool {
1031 self.containing_shadow_root()
1035 .as_deref()
1036 .map(ShadowRoot::Host)
1037 .is_some_and(|host| host.is::<HTMLDetailsElement>())
1038 }
1039
1040 fn rendered_text_fragment(&self, input: DOMString, can_gc: CanGc) -> DomRoot<DocumentFragment> {
1042 let document = self.owner_document();
1044 let fragment = DocumentFragment::new(&document, can_gc);
1045
1046 let input = input.str();
1049 let mut position = input.chars().peekable();
1050
1051 let mut text = String::new();
1053
1054 while let Some(ch) = position.next() {
1056 match ch {
1057 '\u{000A}' | '\u{000D}' => {
1060 if ch == '\u{000D}' && position.peek() == Some(&'\u{000A}') {
1061 position.next();
1064 }
1065
1066 if !text.is_empty() {
1067 append_text_node_to_fragment(&document, &fragment, text, can_gc);
1068 text = String::new();
1069 }
1070
1071 let br = Element::create(
1072 QualName::new(None, ns!(html), local_name!("br")),
1073 None,
1074 &document,
1075 ElementCreator::ScriptCreated,
1076 CustomElementCreationMode::Asynchronous,
1077 None,
1078 can_gc,
1079 );
1080 fragment
1081 .upcast::<Node>()
1082 .AppendChild(br.upcast(), can_gc)
1083 .unwrap();
1084 },
1085 _ => {
1086 text.push(ch);
1089 },
1090 }
1091 }
1092
1093 if !text.is_empty() {
1096 append_text_node_to_fragment(&document, &fragment, text, can_gc);
1097 }
1098
1099 fragment
1100 }
1101
1102 fn merge_with_the_next_text_node(node: DomRoot<Node>, can_gc: CanGc) {
1108 if !node.is::<Text>() {
1110 return;
1111 }
1112
1113 let next = match node.GetNextSibling() {
1115 Some(next) => next,
1116 None => return,
1117 };
1118
1119 if !next.is::<Text>() {
1121 return;
1122 }
1123 let node_chars = node.downcast::<CharacterData>().expect("Node is Text");
1125 let next_chars = next.downcast::<CharacterData>().expect("Next node is Text");
1126 node_chars
1127 .ReplaceData(node_chars.Length(), 0, next_chars.Data())
1128 .expect("Got chars from Text");
1129
1130 next.remove_self(can_gc);
1132 }
1133}
1134
1135impl VirtualMethods for HTMLElement {
1136 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1137 Some(self.as_element() as &dyn VirtualMethods)
1138 }
1139
1140 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1141 self.super_type()
1142 .unwrap()
1143 .attribute_mutated(attr, mutation, can_gc);
1144 let element = self.as_element();
1145 match (attr.local_name(), mutation) {
1146 (name, mutation)
1148 if name.starts_with("on") && EventTarget::is_content_event_handler(name) =>
1149 {
1150 let evtarget = self.upcast::<EventTarget>();
1151 let event_name = &name[2..];
1152 match mutation {
1153 AttributeMutation::Set(_) => {
1155 let source = &**attr.value();
1156 let source_line = 1; evtarget.set_event_handler_uncompiled(
1158 self.owner_window().get_url(),
1159 source_line,
1160 event_name,
1161 source,
1162 );
1163 },
1164 AttributeMutation::Removed => {
1166 evtarget.set_event_handler_common::<EventHandlerNonNull>(event_name, None);
1167 },
1168 }
1169 },
1170
1171 (&local_name!("form"), mutation) if self.is_form_associated_custom_element() => {
1172 self.form_attribute_mutated(mutation, can_gc);
1173 },
1174 (&local_name!("disabled"), AttributeMutation::Set(_))
1176 if self.is_form_associated_custom_element() && element.enabled_state() =>
1177 {
1178 element.set_disabled_state(true);
1179 element.set_enabled_state(false);
1180 ScriptThread::enqueue_callback_reaction(
1181 element,
1182 CallbackReaction::FormDisabled(true),
1183 None,
1184 );
1185 },
1186 (&local_name!("disabled"), AttributeMutation::Removed)
1189 if self.is_form_associated_custom_element() && element.disabled_state() =>
1190 {
1191 element.set_disabled_state(false);
1192 element.set_enabled_state(true);
1193 element.check_ancestors_disabled_state_for_form_control();
1194 if element.enabled_state() {
1195 ScriptThread::enqueue_callback_reaction(
1196 element,
1197 CallbackReaction::FormDisabled(false),
1198 None,
1199 );
1200 }
1201 },
1202 (&local_name!("readonly"), mutation) if self.is_form_associated_custom_element() => {
1203 match mutation {
1204 AttributeMutation::Set(_) => {
1205 element.set_read_write_state(true);
1206 },
1207 AttributeMutation::Removed => {
1208 element.set_read_write_state(false);
1209 },
1210 }
1211 },
1212 (&local_name!("nonce"), mutation) => match mutation {
1213 AttributeMutation::Set(_) => {
1214 let nonce = &**attr.value();
1215 element.update_nonce_internal_slot(nonce.to_owned());
1216 },
1217 AttributeMutation::Removed => {
1218 element.update_nonce_internal_slot("".to_owned());
1219 },
1220 },
1221 _ => {},
1222 }
1223 }
1224
1225 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1226 if let Some(super_type) = self.super_type() {
1227 super_type.bind_to_tree(context, can_gc);
1228 }
1229 let element = self.as_element();
1230 element.update_sequentially_focusable_status(can_gc);
1231
1232 if self.is_form_associated_custom_element() && element.enabled_state() {
1235 element.check_ancestors_disabled_state_for_form_control();
1236 if element.disabled_state() {
1237 ScriptThread::enqueue_callback_reaction(
1238 element,
1239 CallbackReaction::FormDisabled(true),
1240 None,
1241 );
1242 }
1243 }
1244 }
1245
1246 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1247 if let Some(super_type) = self.super_type() {
1248 super_type.unbind_from_tree(context, can_gc);
1249 }
1250
1251 let element = self.as_element();
1256 if self.is_form_associated_custom_element() && element.disabled_state() {
1257 element.check_disabled_attribute();
1258 element.check_ancestors_disabled_state_for_form_control();
1259 if element.enabled_state() {
1260 ScriptThread::enqueue_callback_reaction(
1261 element,
1262 CallbackReaction::FormDisabled(false),
1263 None,
1264 );
1265 }
1266 }
1267 }
1268
1269 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1270 match *name {
1271 local_name!("itemprop") => AttrValue::from_serialized_tokenlist(value.into()),
1272 local_name!("itemtype") => AttrValue::from_serialized_tokenlist(value.into()),
1273 _ => self
1274 .super_type()
1275 .unwrap()
1276 .parse_plain_attribute(name, value),
1277 }
1278 }
1279}
1280
1281impl Activatable for HTMLElement {
1282 fn as_element(&self) -> &Element {
1283 self.upcast::<Element>()
1284 }
1285
1286 fn is_instance_activatable(&self) -> bool {
1287 self.as_element().local_name() == &local_name!("summary")
1288 }
1289
1290 fn activation_behavior(&self, _event: &Event, _target: &EventTarget, _can_gc: CanGc) {
1292 self.summary_activation_behavior();
1293 }
1294}
1295
1296impl FormControl for HTMLElement {
1302 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1303 debug_assert!(self.is_form_associated_custom_element());
1304 self.as_element()
1305 .get_element_internals()
1306 .and_then(|e| e.form_owner())
1307 }
1308
1309 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
1310 debug_assert!(self.is_form_associated_custom_element());
1311 self.as_element()
1312 .ensure_element_internals(CanGc::note())
1313 .set_form_owner(form);
1314 }
1315
1316 fn to_element(&self) -> &Element {
1317 self.as_element()
1318 }
1319
1320 fn is_listed(&self) -> bool {
1321 debug_assert!(self.is_form_associated_custom_element());
1322 true
1323 }
1324
1325 }