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, ScrollContainerQueryType, 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 let mut item_attr_values = HashSet::new();
381 for attr_value in &atoms {
382 item_attr_values.insert(DOMString::from(String::from(attr_value.trim())));
383 }
384
385 Some(item_attr_values.into_iter().collect())
386 }
387
388 fn PropertyNames(&self) -> Option<Vec<DOMString>> {
390 let atoms = self
391 .element
392 .get_tokenlist_attribute(&local_name!("itemprop"));
393
394 if atoms.is_empty() {
395 return None;
396 }
397
398 let mut item_attr_values = HashSet::new();
399 for attr_value in &atoms {
400 item_attr_values.insert(DOMString::from(String::from(attr_value.trim())));
401 }
402
403 Some(item_attr_values.into_iter().collect())
404 }
405
406 fn Click(&self, can_gc: CanGc) {
408 let element = self.as_element();
409 if element.disabled_state() {
410 return;
411 }
412 if element.click_in_progress() {
413 return;
414 }
415 element.set_click_in_progress(true);
416
417 self.upcast::<Node>()
418 .fire_synthetic_pointer_event_not_trusted(DOMString::from("click"), can_gc);
419 element.set_click_in_progress(false);
420 }
421
422 fn Focus(&self, options: &FocusOptions, can_gc: CanGc) {
424 let document = self.owner_document();
427 document.request_focus_with_options(
428 Some(self.upcast()),
429 FocusInitiator::Local,
430 FocusOptions {
431 preventScroll: options.preventScroll,
432 },
433 can_gc,
434 );
435 }
436
437 fn Blur(&self, can_gc: CanGc) {
439 if !self.as_element().focus_state() {
442 return;
443 }
444 let document = self.owner_document();
446 document.request_focus(None, FocusInitiator::Local, can_gc);
447 }
448
449 #[allow(unsafe_code)]
451 fn GetScrollParent(&self) -> Option<DomRoot<Element>> {
452 self.owner_window()
453 .scroll_container_query(self.upcast(), ScrollContainerQueryType::ForScrollParent)
454 .and_then(|response| match response {
455 ScrollContainerResponse::Viewport => self.owner_document().GetScrollingElement(),
456 ScrollContainerResponse::Element(parent_node_address) => {
457 let node = unsafe { from_untrusted_node_address(parent_node_address) };
458 DomRoot::downcast(node)
459 },
460 })
461 }
462
463 fn GetOffsetParent(&self) -> Option<DomRoot<Element>> {
465 if self.is::<HTMLBodyElement>() || self.upcast::<Element>().is_root() {
466 return None;
467 }
468
469 let node = self.upcast::<Node>();
470 let window = self.owner_window();
471 let (element, _) = window.offset_parent_query(node);
472
473 element
474 }
475
476 fn OffsetTop(&self) -> i32 {
478 if self.is_body_element() {
479 return 0;
480 }
481
482 let node = self.upcast::<Node>();
483 let window = self.owner_window();
484 let (_, rect) = window.offset_parent_query(node);
485
486 rect.origin.y.to_nearest_px()
487 }
488
489 fn OffsetLeft(&self) -> i32 {
491 if self.is_body_element() {
492 return 0;
493 }
494
495 let node = self.upcast::<Node>();
496 let window = self.owner_window();
497 let (_, rect) = window.offset_parent_query(node);
498
499 rect.origin.x.to_nearest_px()
500 }
501
502 fn OffsetWidth(&self) -> i32 {
504 let node = self.upcast::<Node>();
505 let window = self.owner_window();
506 let (_, rect) = window.offset_parent_query(node);
507
508 rect.size.width.to_nearest_px()
509 }
510
511 fn OffsetHeight(&self) -> i32 {
513 let node = self.upcast::<Node>();
514 let window = self.owner_window();
515 let (_, rect) = window.offset_parent_query(node);
516
517 rect.size.height.to_nearest_px()
518 }
519
520 fn InnerText(&self) -> DOMString {
522 self.get_inner_outer_text()
523 }
524
525 fn SetInnerText(&self, input: DOMString, can_gc: CanGc) {
527 self.set_inner_text(input, can_gc)
528 }
529
530 fn GetOuterText(&self) -> Fallible<DOMString> {
532 Ok(self.get_inner_outer_text())
533 }
534
535 fn SetOuterText(&self, input: DOMString, can_gc: CanGc) -> Fallible<()> {
537 let Some(parent) = self.upcast::<Node>().GetParentNode() else {
539 return Err(Error::NoModificationAllowed);
540 };
541
542 let node = self.upcast::<Node>();
543 let document = self.owner_document();
544
545 let next = node.GetNextSibling();
547
548 let previous = node.GetPreviousSibling();
550
551 let fragment = self.rendered_text_fragment(input, can_gc);
554
555 if fragment.upcast::<Node>().children_count() == 0 {
558 let text_node = Text::new(DOMString::from("".to_owned()), &document, can_gc);
559
560 fragment
561 .upcast::<Node>()
562 .AppendChild(text_node.upcast(), can_gc)?;
563 }
564
565 parent.ReplaceChild(fragment.upcast(), node, can_gc)?;
567
568 if let Some(next_sibling) = next {
571 if let Some(node) = next_sibling.GetPreviousSibling() {
572 Self::merge_with_the_next_text_node(node, can_gc);
573 }
574 }
575
576 if let Some(previous) = previous {
578 Self::merge_with_the_next_text_node(previous, can_gc)
579 }
580
581 Ok(())
582 }
583
584 fn Translate(&self) -> bool {
586 self.as_element().is_translate_enabled()
587 }
588
589 fn SetTranslate(&self, yesno: bool, can_gc: CanGc) {
591 self.as_element().set_string_attribute(
592 &html5ever::local_name!("translate"),
593 match yesno {
594 true => DOMString::from("yes"),
595 false => DOMString::from("no"),
596 },
597 can_gc,
598 );
599 }
600
601 fn ContentEditable(&self) -> DOMString {
603 self.as_element()
605 .get_attribute(&ns!(), &local_name!("contenteditable"))
606 .map(|attr| DOMString::from(&**attr.value()))
607 .unwrap_or_else(|| DOMString::from("inherit"))
608 }
609
610 fn SetContentEditable(&self, _: DOMString) {
612 warn!("The contentEditable attribute is not implemented yet");
614 }
615
616 fn IsContentEditable(&self) -> bool {
618 false
620 }
621
622 fn AttachInternals(&self, can_gc: CanGc) -> Fallible<DomRoot<ElementInternals>> {
624 let element = self.as_element();
625 if element.get_is().is_some() {
627 return Err(Error::NotSupported);
628 }
629
630 let registry = self.owner_window().CustomElements();
635 let definition = registry.lookup_definition(self.as_element().local_name(), None);
636
637 let definition = match definition {
639 Some(definition) => definition,
640 None => return Err(Error::NotSupported),
641 };
642
643 if definition.disable_internals {
645 return Err(Error::NotSupported);
646 }
647
648 let internals = element.ensure_element_internals(can_gc);
650 if internals.attached() {
651 return Err(Error::NotSupported);
652 }
653
654 if !matches!(
657 element.get_custom_element_state(),
658 CustomElementState::Precustomized | CustomElementState::Custom
659 ) {
660 return Err(Error::NotSupported);
661 }
662
663 if self.is_form_associated_custom_element() {
664 element.init_state_for_internals();
665 }
666
667 internals.set_attached();
669 Ok(internals)
670 }
671
672 fn Nonce(&self) -> DOMString {
674 self.as_element().nonce_value().into()
675 }
676
677 fn SetNonce(&self, value: DOMString) {
679 self.as_element()
680 .update_nonce_internal_slot(value.to_string())
681 }
682
683 fn Autofocus(&self) -> bool {
685 self.element.has_attribute(&local_name!("autofocus"))
686 }
687
688 fn SetAutofocus(&self, autofocus: bool, can_gc: CanGc) {
690 self.element
691 .set_bool_attribute(&local_name!("autofocus"), autofocus, can_gc);
692 }
693}
694
695fn append_text_node_to_fragment(
696 document: &Document,
697 fragment: &DocumentFragment,
698 text: String,
699 can_gc: CanGc,
700) {
701 let text = Text::new(DOMString::from(text), document, can_gc);
702 fragment
703 .upcast::<Node>()
704 .AppendChild(text.upcast(), can_gc)
705 .unwrap();
706}
707
708static DATA_PREFIX: &str = "data-";
711static DATA_HYPHEN_SEPARATOR: char = '\x2d';
712
713fn to_snake_case(name: DOMString) -> DOMString {
714 let mut attr_name = String::with_capacity(name.len() + DATA_PREFIX.len());
715 attr_name.push_str(DATA_PREFIX);
716 for ch in name.chars() {
717 if ch.is_ascii_uppercase() {
718 attr_name.push(DATA_HYPHEN_SEPARATOR);
719 attr_name.push(ch.to_ascii_lowercase());
720 } else {
721 attr_name.push(ch);
722 }
723 }
724 DOMString::from(attr_name)
725}
726
727fn to_camel_case(name: &str) -> Option<DOMString> {
733 if !name.starts_with(DATA_PREFIX) {
734 return None;
735 }
736 let name = &name[5..];
737 let has_uppercase = name.chars().any(|curr_char| curr_char.is_ascii_uppercase());
738 if has_uppercase {
739 return None;
740 }
741 let mut result = String::with_capacity(name.len().saturating_sub(DATA_PREFIX.len()));
742 let mut name_chars = name.chars();
743 while let Some(curr_char) = name_chars.next() {
744 if curr_char == DATA_HYPHEN_SEPARATOR {
746 if let Some(next_char) = name_chars.next() {
747 if next_char.is_ascii_lowercase() {
748 result.push(next_char.to_ascii_uppercase());
749 } else {
750 result.push(curr_char);
751 result.push(next_char);
752 }
753 } else {
754 result.push(curr_char);
755 }
756 } else {
757 result.push(curr_char);
758 }
759 }
760 Some(DOMString::from(result))
761}
762
763impl HTMLElement {
764 pub(crate) fn set_custom_attr(
765 &self,
766 name: DOMString,
767 value: DOMString,
768 can_gc: CanGc,
769 ) -> ErrorResult {
770 if name
771 .chars()
772 .skip_while(|&ch| ch != '\u{2d}')
773 .nth(1)
774 .is_some_and(|ch| ch.is_ascii_lowercase())
775 {
776 return Err(Error::Syntax(None));
777 }
778 self.as_element()
779 .set_custom_attribute(to_snake_case(name), value, can_gc)
780 }
781
782 pub(crate) fn get_custom_attr(&self, local_name: DOMString) -> Option<DOMString> {
783 let local_name = LocalName::from(to_snake_case(local_name));
785 self.as_element()
786 .get_attribute(&ns!(), &local_name)
787 .map(|attr| {
788 DOMString::from(&**attr.value()) })
790 }
791
792 pub(crate) fn delete_custom_attr(&self, local_name: DOMString, can_gc: CanGc) {
793 let local_name = LocalName::from(to_snake_case(local_name));
795 self.as_element()
796 .remove_attribute(&ns!(), &local_name, can_gc);
797 }
798
799 pub(crate) fn is_labelable_element(&self) -> bool {
801 match self.upcast::<Node>().type_id() {
802 NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
803 HTMLElementTypeId::HTMLInputElement => {
804 self.downcast::<HTMLInputElement>().unwrap().input_type() != InputType::Hidden
805 },
806 HTMLElementTypeId::HTMLButtonElement |
807 HTMLElementTypeId::HTMLMeterElement |
808 HTMLElementTypeId::HTMLOutputElement |
809 HTMLElementTypeId::HTMLProgressElement |
810 HTMLElementTypeId::HTMLSelectElement |
811 HTMLElementTypeId::HTMLTextAreaElement => true,
812 _ => self.is_form_associated_custom_element(),
813 },
814 _ => false,
815 }
816 }
817
818 pub(crate) fn is_form_associated_custom_element(&self) -> bool {
820 if let Some(definition) = self.as_element().get_custom_element_definition() {
821 definition.is_autonomous() && definition.form_associated
822 } else {
823 false
824 }
825 }
826
827 pub(crate) fn is_listed_element(&self) -> bool {
829 match self.upcast::<Node>().type_id() {
830 NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
831 HTMLElementTypeId::HTMLButtonElement |
832 HTMLElementTypeId::HTMLFieldSetElement |
833 HTMLElementTypeId::HTMLInputElement |
834 HTMLElementTypeId::HTMLObjectElement |
835 HTMLElementTypeId::HTMLOutputElement |
836 HTMLElementTypeId::HTMLSelectElement |
837 HTMLElementTypeId::HTMLTextAreaElement => true,
838 _ => self.is_form_associated_custom_element(),
839 },
840 _ => false,
841 }
842 }
843
844 pub(crate) fn is_body_element(&self) -> bool {
846 let self_node = self.upcast::<Node>();
847 self_node.GetParentNode().is_some_and(|parent| {
848 let parent_node = parent.upcast::<Node>();
849 (self_node.is::<HTMLBodyElement>() || self_node.is::<HTMLFrameSetElement>()) &&
850 parent_node.is::<HTMLHtmlElement>() &&
851 self_node
852 .preceding_siblings()
853 .all(|n| !n.is::<HTMLBodyElement>() && !n.is::<HTMLFrameSetElement>())
854 })
855 }
856
857 pub(crate) fn is_submittable_element(&self) -> bool {
859 match self.upcast::<Node>().type_id() {
860 NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) => match type_id {
861 HTMLElementTypeId::HTMLButtonElement |
862 HTMLElementTypeId::HTMLInputElement |
863 HTMLElementTypeId::HTMLSelectElement |
864 HTMLElementTypeId::HTMLTextAreaElement => true,
865 _ => self.is_form_associated_custom_element(),
866 },
867 _ => false,
868 }
869 }
870
871 pub(crate) fn supported_prop_names_custom_attr(&self) -> Vec<DOMString> {
872 let element = self.as_element();
873 element
874 .attrs()
875 .iter()
876 .filter_map(|attr| {
877 let raw_name = attr.local_name();
878 to_camel_case(raw_name)
879 })
880 .collect()
881 }
882
883 pub(crate) fn label_at(&self, index: u32) -> Option<DomRoot<Node>> {
886 let element = self.as_element();
887
888 let root_element = element.root_element();
899 let root_node = root_element.upcast::<Node>();
900 root_node
901 .traverse_preorder(ShadowIncluding::No)
902 .filter_map(DomRoot::downcast::<HTMLLabelElement>)
903 .filter(|elem| match elem.GetControl() {
904 Some(control) => &*control == self,
905 _ => false,
906 })
907 .nth(index as usize)
908 .map(|n| DomRoot::from_ref(n.upcast::<Node>()))
909 }
910
911 pub(crate) fn labels_count(&self) -> u32 {
914 let element = self.as_element();
916 let root_element = element.root_element();
917 let root_node = root_element.upcast::<Node>();
918 root_node
919 .traverse_preorder(ShadowIncluding::No)
920 .filter_map(DomRoot::downcast::<HTMLLabelElement>)
921 .filter(|elem| match elem.GetControl() {
922 Some(control) => &*control == self,
923 _ => false,
924 })
925 .count() as u32
926 }
927
928 pub(crate) fn directionality(&self) -> Option<String> {
932 let element_direction: &str = &self.Dir();
933
934 if element_direction == "ltr" {
935 return Some("ltr".to_owned());
936 }
937
938 if element_direction == "rtl" {
939 return Some("rtl".to_owned());
940 }
941
942 if let Some(input) = self.downcast::<HTMLInputElement>() {
943 if input.input_type() == InputType::Tel {
944 return Some("ltr".to_owned());
945 }
946 }
947
948 if element_direction == "auto" {
949 if let Some(directionality) = self
950 .downcast::<HTMLInputElement>()
951 .and_then(|input| input.auto_directionality())
952 {
953 return Some(directionality);
954 }
955
956 if let Some(area) = self.downcast::<HTMLTextAreaElement>() {
957 return Some(area.auto_directionality());
958 }
959 }
960
961 None
968 }
969
970 pub(crate) fn summary_activation_behavior(&self) {
972 debug_assert!(self.as_element().local_name() == &local_name!("summary"));
973
974 if !self.is_a_summary_for_its_parent_details() {
976 return;
977 }
978
979 let parent = if self.is_implicit_summary_element() {
981 DomRoot::downcast::<HTMLDetailsElement>(self.containing_shadow_root().unwrap().Host())
982 .unwrap()
983 } else {
984 self.upcast::<Node>()
985 .GetParentNode()
986 .and_then(DomRoot::downcast::<HTMLDetailsElement>)
987 .unwrap()
988 };
989
990 parent.toggle();
993 }
994
995 fn is_a_summary_for_its_parent_details(&self) -> bool {
997 if self.is_implicit_summary_element() {
998 return true;
999 }
1000
1001 let Some(parent) = self.upcast::<Node>().GetParentNode() else {
1004 return false;
1005 };
1006
1007 let Some(details) = parent.downcast::<HTMLDetailsElement>() else {
1009 return false;
1010 };
1011
1012 details
1016 .find_corresponding_summary_element()
1017 .is_some_and(|summary| &*summary == self.upcast())
1018 }
1019
1020 fn is_implicit_summary_element(&self) -> bool {
1023 self.containing_shadow_root()
1027 .as_deref()
1028 .map(ShadowRoot::Host)
1029 .is_some_and(|host| host.is::<HTMLDetailsElement>())
1030 }
1031
1032 fn rendered_text_fragment(&self, input: DOMString, can_gc: CanGc) -> DomRoot<DocumentFragment> {
1034 let document = self.owner_document();
1036 let fragment = DocumentFragment::new(&document, can_gc);
1037
1038 let mut position = input.chars().peekable();
1041
1042 let mut text = String::new();
1044
1045 while let Some(ch) = position.next() {
1047 match ch {
1048 '\u{000A}' | '\u{000D}' => {
1051 if ch == '\u{000D}' && position.peek() == Some(&'\u{000A}') {
1052 position.next();
1055 }
1056
1057 if !text.is_empty() {
1058 append_text_node_to_fragment(&document, &fragment, text, can_gc);
1059 text = String::new();
1060 }
1061
1062 let br = Element::create(
1063 QualName::new(None, ns!(html), local_name!("br")),
1064 None,
1065 &document,
1066 ElementCreator::ScriptCreated,
1067 CustomElementCreationMode::Asynchronous,
1068 None,
1069 can_gc,
1070 );
1071 fragment
1072 .upcast::<Node>()
1073 .AppendChild(br.upcast(), can_gc)
1074 .unwrap();
1075 },
1076 _ => {
1077 text.push(ch);
1080 },
1081 }
1082 }
1083
1084 if !text.is_empty() {
1087 append_text_node_to_fragment(&document, &fragment, text, can_gc);
1088 }
1089
1090 fragment
1091 }
1092
1093 fn merge_with_the_next_text_node(node: DomRoot<Node>, can_gc: CanGc) {
1099 if !node.is::<Text>() {
1101 return;
1102 }
1103
1104 let next = match node.GetNextSibling() {
1106 Some(next) => next,
1107 None => return,
1108 };
1109
1110 if !next.is::<Text>() {
1112 return;
1113 }
1114 let node_chars = node.downcast::<CharacterData>().expect("Node is Text");
1116 let next_chars = next.downcast::<CharacterData>().expect("Next node is Text");
1117 node_chars
1118 .ReplaceData(node_chars.Length(), 0, next_chars.Data())
1119 .expect("Got chars from Text");
1120
1121 next.remove_self(can_gc);
1123 }
1124}
1125
1126impl VirtualMethods for HTMLElement {
1127 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1128 Some(self.as_element() as &dyn VirtualMethods)
1129 }
1130
1131 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1132 self.super_type()
1133 .unwrap()
1134 .attribute_mutated(attr, mutation, can_gc);
1135 let element = self.as_element();
1136 match (attr.local_name(), mutation) {
1137 (name, mutation)
1139 if name.starts_with("on") && EventTarget::is_content_event_handler(name) =>
1140 {
1141 let evtarget = self.upcast::<EventTarget>();
1142 let event_name = &name[2..];
1143 match mutation {
1144 AttributeMutation::Set(_) => {
1146 let source = &**attr.value();
1147 let source_line = 1; evtarget.set_event_handler_uncompiled(
1149 self.owner_window().get_url(),
1150 source_line,
1151 event_name,
1152 source,
1153 );
1154 },
1155 AttributeMutation::Removed => {
1157 evtarget.set_event_handler_common::<EventHandlerNonNull>(event_name, None);
1158 },
1159 }
1160 },
1161
1162 (&local_name!("form"), mutation) if self.is_form_associated_custom_element() => {
1163 self.form_attribute_mutated(mutation, can_gc);
1164 },
1165 (&local_name!("disabled"), AttributeMutation::Set(_))
1167 if self.is_form_associated_custom_element() && element.enabled_state() =>
1168 {
1169 element.set_disabled_state(true);
1170 element.set_enabled_state(false);
1171 ScriptThread::enqueue_callback_reaction(
1172 element,
1173 CallbackReaction::FormDisabled(true),
1174 None,
1175 );
1176 },
1177 (&local_name!("disabled"), AttributeMutation::Removed)
1180 if self.is_form_associated_custom_element() && element.disabled_state() =>
1181 {
1182 element.set_disabled_state(false);
1183 element.set_enabled_state(true);
1184 element.check_ancestors_disabled_state_for_form_control();
1185 if element.enabled_state() {
1186 ScriptThread::enqueue_callback_reaction(
1187 element,
1188 CallbackReaction::FormDisabled(false),
1189 None,
1190 );
1191 }
1192 },
1193 (&local_name!("readonly"), mutation) if self.is_form_associated_custom_element() => {
1194 match mutation {
1195 AttributeMutation::Set(_) => {
1196 element.set_read_write_state(true);
1197 },
1198 AttributeMutation::Removed => {
1199 element.set_read_write_state(false);
1200 },
1201 }
1202 },
1203 (&local_name!("nonce"), mutation) => match mutation {
1204 AttributeMutation::Set(_) => {
1205 let nonce = &**attr.value();
1206 element.update_nonce_internal_slot(nonce.to_owned());
1207 },
1208 AttributeMutation::Removed => {
1209 element.update_nonce_internal_slot("".to_owned());
1210 },
1211 },
1212 _ => {},
1213 }
1214 }
1215
1216 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1217 if let Some(super_type) = self.super_type() {
1218 super_type.bind_to_tree(context, can_gc);
1219 }
1220 let element = self.as_element();
1221 element.update_sequentially_focusable_status(can_gc);
1222
1223 if self.is_form_associated_custom_element() && element.enabled_state() {
1226 element.check_ancestors_disabled_state_for_form_control();
1227 if element.disabled_state() {
1228 ScriptThread::enqueue_callback_reaction(
1229 element,
1230 CallbackReaction::FormDisabled(true),
1231 None,
1232 );
1233 }
1234 }
1235 }
1236
1237 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1238 if let Some(super_type) = self.super_type() {
1239 super_type.unbind_from_tree(context, can_gc);
1240 }
1241
1242 let element = self.as_element();
1247 if self.is_form_associated_custom_element() && element.disabled_state() {
1248 element.check_disabled_attribute();
1249 element.check_ancestors_disabled_state_for_form_control();
1250 if element.enabled_state() {
1251 ScriptThread::enqueue_callback_reaction(
1252 element,
1253 CallbackReaction::FormDisabled(false),
1254 None,
1255 );
1256 }
1257 }
1258 }
1259
1260 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1261 match *name {
1262 local_name!("itemprop") => AttrValue::from_serialized_tokenlist(value.into()),
1263 local_name!("itemtype") => AttrValue::from_serialized_tokenlist(value.into()),
1264 _ => self
1265 .super_type()
1266 .unwrap()
1267 .parse_plain_attribute(name, value),
1268 }
1269 }
1270}
1271
1272impl Activatable for HTMLElement {
1273 fn as_element(&self) -> &Element {
1274 self.upcast::<Element>()
1275 }
1276
1277 fn is_instance_activatable(&self) -> bool {
1278 self.as_element().local_name() == &local_name!("summary")
1279 }
1280
1281 fn activation_behavior(&self, _event: &Event, _target: &EventTarget, _can_gc: CanGc) {
1283 self.summary_activation_behavior();
1284 }
1285}
1286
1287impl FormControl for HTMLElement {
1293 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1294 debug_assert!(self.is_form_associated_custom_element());
1295 self.as_element()
1296 .get_element_internals()
1297 .and_then(|e| e.form_owner())
1298 }
1299
1300 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
1301 debug_assert!(self.is_form_associated_custom_element());
1302 self.as_element()
1303 .ensure_element_internals(CanGc::note())
1304 .set_form_owner(form);
1305 }
1306
1307 fn to_element(&self) -> &Element {
1308 self.as_element()
1309 }
1310
1311 fn is_listed(&self) -> bool {
1312 debug_assert!(self.is_form_associated_custom_element());
1313 true
1314 }
1315
1316 }