1use std::cell::Cell;
6use std::collections::VecDeque;
7use std::ffi::CStr;
8use std::ptr::NonNull;
9use std::rc::Rc;
10use std::{mem, ptr};
11
12use dom_struct::dom_struct;
13use html5ever::{LocalName, Namespace, Prefix, ns};
14use js::context::JSContext;
15use js::conversions::{FromJSValConvertible, ToJSValConvertible};
16use js::glue::UnwrapObjectStatic;
17use js::jsapi::{HandleValueArray, Heap, IsCallable, IsConstructor, JSObject};
18use js::jsval::{BooleanValue, JSVal, NullValue, ObjectValue, UndefinedValue};
19use js::realm::{AutoRealm, CurrentRealm};
20use js::rust::wrappers2::{Construct1, SameValue};
21use js::rust::{HandleObject, MutableHandleValue};
22use rustc_hash::{FxBuildHasher, FxHashSet};
23use script_bindings::callback::HasCallbackHolder;
24use script_bindings::cell::DomRefCell;
25use script_bindings::domstring::TracedDOMString;
26use script_bindings::js::JS_GetProperty;
27use script_bindings::reflector::{DomObject, Reflector, reflect_dom_object_with_proto};
28use script_bindings::settings_stack::{run_a_callback, run_a_script};
29use style::attr::AttrValue;
30
31use crate::DomTypeHolder;
32use crate::dom::bindings::callback::{ExceptionHandling, RootedCallback, TracedCallback};
33use crate::dom::bindings::codegen::Bindings::CustomElementRegistryBinding::{
34 CustomElementConstructor, CustomElementRegistryMethods, ElementDefinitionOptions,
35};
36use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
37use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
38use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
39use crate::dom::bindings::conversions::{ConversionResult, StringificationBehavior, get_property};
40use crate::dom::bindings::error::{
41 Error, ErrorResult, Fallible, report_pending_exception, throw_dom_exception,
42};
43use crate::dom::bindings::inheritance::{Castable, DocumentFragmentTypeId, NodeTypeId};
44use crate::dom::bindings::reflector::DomGlobal;
45use crate::dom::bindings::root::{AsHandleValue, Dom, DomRoot, UnrootedDom};
46use crate::dom::bindings::str::DOMString;
47use crate::dom::bindings::trace::HashMapTracedValues;
48use crate::dom::document::Document;
49use crate::dom::domexception::{DOMErrorName, DOMException};
50use crate::dom::element::Element;
51use crate::dom::globalscope::GlobalScope;
52use crate::dom::html::htmlelement::HTMLElement;
53use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
54use crate::dom::iterators::ShadowIncluding;
55use crate::dom::node::{Node, NodeTraits};
56use crate::dom::promise::Promise;
57use crate::dom::shadowroot::shadowroot::ShadowRoot;
58use crate::dom::window::Window;
59use crate::dom::{RootedPromise, TracedPromise};
60use crate::event_loop::script_thread::ScriptThread;
61use crate::realms::enter_auto_realm;
62use crate::runtime::job_queue::CustomElementReactionMicrotask;
63
64#[derive(Clone, Copy, Default, Eq, JSTraceable, MallocSizeOf, PartialEq)]
66pub(crate) enum CustomElementState {
67 Undefined,
68 Failed,
69 #[default]
70 Uncustomized,
71 Precustomized,
72 Custom,
73}
74
75#[dom_struct]
77pub(crate) struct CustomElementRegistry {
78 reflector_: Reflector,
79
80 window: Dom<Window>,
81
82 when_defined: DomRefCell<HashMapTracedValues<LocalName, TracedPromise, FxBuildHasher>>,
86
87 element_definition_is_running: Cell<bool>,
89
90 is_scoped: Cell<bool>,
92
93 scoped_document_set: DomRefCell<FxHashSet<Dom<Document>>>,
95
96 #[conditional_malloc_size_of]
97 definitions:
99 DomRefCell<HashMapTracedValues<LocalName, Rc<CustomElementDefinition>, FxBuildHasher>>,
100}
101
102impl CustomElementRegistry {
103 fn new_inherited(window: &Window) -> CustomElementRegistry {
104 CustomElementRegistry {
105 reflector_: Reflector::new(),
106 window: Dom::from_ref(window),
107 when_defined: DomRefCell::new(HashMapTracedValues::new_fx()),
108 element_definition_is_running: Cell::new(false),
109 is_scoped: Cell::new(false),
110 scoped_document_set: Default::default(),
111 definitions: DomRefCell::new(HashMapTracedValues::new_fx()),
112 }
113 }
114
115 pub(crate) fn new(cx: &mut JSContext, window: &Window) -> DomRoot<CustomElementRegistry> {
116 CustomElementRegistry::new_with_proto(cx, window, None)
117 }
118
119 fn new_with_proto(
120 cx: &mut JSContext,
121 window: &Window,
122 proto: Option<HandleObject>,
123 ) -> DomRoot<CustomElementRegistry> {
124 reflect_dom_object_with_proto(
125 cx,
126 Box::new(CustomElementRegistry::new_inherited(window)),
127 window,
128 proto,
129 )
130 }
131
132 pub(crate) fn is_scoped(&self) -> bool {
134 self.is_scoped.get()
135 }
136
137 pub(crate) fn teardown(&self) {
140 self.when_defined.borrow_mut().0.clear()
141 }
142
143 pub(crate) fn lookup_definition_by_constructor(
147 &self,
148 constructor: HandleObject,
149 ) -> Option<Rc<CustomElementDefinition>> {
150 self.definitions
151 .borrow()
152 .0
153 .values()
154 .find(|definition| definition.constructor.callback() == constructor.get())
155 .cloned()
156 }
157
158 pub(crate) fn lookup_a_custom_element_registry(
160 node: &Node,
161 ) -> Option<DomRoot<CustomElementRegistry>> {
162 match node.type_id() {
163 NodeTypeId::Element(_) => node
165 .downcast::<Element>()
166 .expect("Nodes with element type must be an element")
167 .custom_element_registry(),
168 NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot) => node
170 .downcast::<ShadowRoot>()
171 .expect("Nodes with ShadowRoot type must be a ShadowRoot")
172 .custom_element_registry(),
173 NodeTypeId::Document(_) => node
175 .downcast::<Document>()
176 .expect("Nodes with document type must be a document")
177 .custom_element_registry(),
178 _ => None,
180 }
181 }
182
183 pub(crate) fn lookup_custom_element_definition(
185 registry: Option<&CustomElementRegistry>,
186 namespace: &Namespace,
187 local_name: &LocalName,
188 is: Option<&LocalName>,
189 ) -> Option<Rc<CustomElementDefinition>> {
190 let registry = registry?;
192
193 if *namespace != ns!(html) {
195 return None;
196 }
197
198 registry
206 .definitions
207 .borrow()
208 .0
209 .values()
210 .find(|definition| {
211 definition.local_name == *local_name &&
212 (definition.name == *local_name || Some(&definition.name) == is)
213 })
214 .cloned()
215 }
216
217 pub(crate) fn is_a_global_element_registry(registry: Option<&CustomElementRegistry>) -> bool {
219 registry.is_some_and(|r| !r.is_scoped())
222 }
223
224 fn check_prototype(
227 &self,
228 cx: &mut JSContext,
229 constructor: HandleObject,
230 mut prototype: MutableHandleValue,
231 ) -> ErrorResult {
232 if !JS_GetProperty(cx, constructor, c"prototype", prototype.reborrow()) {
234 return Err(Error::JSFailed);
235 }
236
237 if !prototype.is_object() {
239 return Err(Error::Type(
240 c"constructor.prototype is not an object".to_owned(),
241 ));
242 }
243 Ok(())
244 }
245
246 fn get_callbacks(
250 &self,
251 cx: &mut JSContext,
252 prototype: HandleObject,
253 ) -> Fallible<RootedLifecycleCallbacks> {
254 Ok(RootedLifecycleCallbacks {
256 connected_callback: get_callback(cx, prototype, c"connectedCallback")?,
257 disconnected_callback: get_callback(cx, prototype, c"disconnectedCallback")?,
258 connected_move_callback: get_callback(cx, prototype, c"connectedMoveCallback")?,
259 adopted_callback: get_callback(cx, prototype, c"adoptedCallback")?,
260 attribute_changed_callback: get_callback(cx, prototype, c"attributeChangedCallback")?,
261
262 form_associated_callback: None,
263 form_disabled_callback: None,
264 form_reset_callback: None,
265 form_state_restore_callback: None,
266 })
267 }
268
269 #[expect(unsafe_code)]
272 unsafe fn add_form_associated_callbacks(
273 &self,
274 cx: &mut JSContext,
275 prototype: HandleObject,
276 callbacks: &mut RootedLifecycleCallbacks,
277 ) -> ErrorResult {
278 callbacks.form_associated_callback =
279 get_callback(cx, prototype, c"formAssociatedCallback")?;
280 callbacks.form_reset_callback = get_callback(cx, prototype, c"formResetCallback")?;
281 callbacks.form_disabled_callback = get_callback(cx, prototype, c"formDisabledCallback")?;
282 callbacks.form_state_restore_callback =
283 get_callback(cx, prototype, c"formStateRestoreCallback")?;
284
285 Ok(())
286 }
287
288 fn upgrade_particular_elements_within_a_document(
290 &self,
291 cx: &JSContext,
292 document: &Document,
293 definition: &Rc<CustomElementDefinition>,
294 local_name: &LocalName,
295 name: &LocalName,
296 ) {
297 for candidate in document
303 .upcast::<Node>()
304 .traverse_preorder_unrooted(cx, ShadowIncluding::Yes)
305 .filter_map(UnrootedDom::downcast::<Element>)
306 {
307 let registry_matches = if self.is_scoped.get() {
312 candidate
313 .custom_element_registry()
314 .is_some_and(|registry| *registry == *self)
315 } else {
316 candidate
317 .custom_element_registry()
318 .is_none_or(|registry| *registry == *self)
319 };
320 if *candidate.local_name() == *local_name &&
321 *candidate.namespace() == ns!(html) &&
322 registry_matches &&
323 (*name == *local_name || candidate.get_is().as_ref() == Some(name))
324 {
325 ScriptThread::enqueue_upgrade_reaction(cx, &candidate, definition.clone());
328 }
329 }
330 }
331
332 pub(crate) fn add_scoped_document(&self, document: &Document) {
333 self.scoped_document_set
334 .borrow_mut()
335 .insert(Dom::from_ref(document));
336 }
337}
338
339#[expect(unsafe_code)]
342fn get_callback(
343 cx: &mut JSContext,
344 prototype: HandleObject,
345 name: &CStr,
346) -> Fallible<Option<RootedCallback<Function>>> {
347 rooted!(&in(cx) let mut callback = UndefinedValue());
348 unsafe {
349 if !JS_GetProperty(cx, prototype, name, callback.handle_mut()) {
351 return Err(Error::JSFailed);
352 }
353
354 if !callback.is_undefined() {
356 if !callback.is_object() || !IsCallable(callback.to_object()) {
357 return Err(Error::Type(
358 c"Lifecycle callback is not callable".to_owned(),
359 ));
360 }
361 Ok(Some(Function::new(cx, callback.to_object())))
362 } else {
363 Ok(None)
364 }
365 }
366}
367
368impl CustomElementRegistryMethods<crate::DomTypeHolder> for CustomElementRegistry {
369 fn Constructor(
371 cx: &mut JSContext,
372 window: &Window,
373 proto: Option<HandleObject>,
374 ) -> DomRoot<CustomElementRegistry> {
375 let registry = CustomElementRegistry::new_with_proto(cx, window, proto);
376
377 registry.is_scoped.set(true);
379 registry
380 }
381
382 #[expect(unsafe_code)]
383 fn Define(
385 &self,
386 cx: &mut JSContext,
387 name: DOMString,
388 constructor_: RootedCallback<CustomElementConstructor>,
389 options: &ElementDefinitionOptions,
390 ) -> ErrorResult {
391 rooted!(&in(cx) let constructor = constructor_.callback());
392 let name = LocalName::from(name);
393
394 rooted!(&in(cx) let unwrapped_constructor = unsafe { UnwrapObjectStatic(constructor.get()) });
397
398 if unwrapped_constructor.is_null() {
399 return Err(Error::Security(None));
401 }
402
403 if unsafe { !IsConstructor(unwrapped_constructor.get()) } {
404 return Err(Error::Type(
405 c"Second argument of CustomElementRegistry.define is not a constructor".to_owned(),
406 ));
407 }
408
409 if !is_valid_custom_element_name(&name) {
411 return Err(Error::Syntax(Some(format!(
412 "{} name is not a valid custom element name",
413 name
414 ))));
415 }
416
417 if self.definitions.borrow().contains_key(&name) {
420 return Err(Error::NotSupported(Some(format!(
421 "{} has already been defined as a custom element",
422 name
423 ))));
424 }
425
426 if self
429 .definitions
430 .borrow()
431 .iter()
432 .any(|(_, def)| def.constructor.callback() == constructor_.callback())
433 {
434 return Err(Error::NotSupported(None));
435 }
436
437 let extends = &options.extends;
439
440 let local_name = if let Some(ref extended_name) = *extends {
442 if self.is_scoped.get() {
444 return Err(Error::NotSupported(Some(
445 "Scoped custom element registries cannot define customized built-in elements"
446 .to_owned(),
447 )));
448 }
449
450 if is_valid_custom_element_name(&extended_name.str()) {
452 return Err(Error::NotSupported(None));
453 }
454
455 if !is_extendable_element_interface(&extended_name.str()) {
459 return Err(Error::NotSupported(None));
460 }
461
462 LocalName::from(extended_name)
464 } else {
465 name.clone()
467 };
468
469 if self.element_definition_is_running.get() {
471 return Err(Error::NotSupported(None));
472 }
473
474 self.element_definition_is_running.set(true);
476
477 rooted!(&in(cx) let mut prototype = UndefinedValue());
482 {
483 let mut realm = AutoRealm::new_from_handle(cx, constructor.handle());
484 if let Err(error) =
485 self.check_prototype(&mut realm, constructor.handle(), prototype.handle_mut())
486 {
487 self.element_definition_is_running.set(false);
488 return Err(error);
489 }
490 };
491
492 rooted!(&in(cx) let proto_object = prototype.to_object());
498 let mut callbacks = {
499 let mut realm = AutoRealm::new_from_handle(cx, proto_object.handle());
500 match self.get_callbacks(&mut realm, proto_object.handle()) {
501 Ok(callbacks) => callbacks,
502 Err(error) => {
503 self.element_definition_is_running.set(false);
504 return Err(error);
505 },
506 }
507 };
508
509 let observed_attributes: Vec<DOMString> = if callbacks.attribute_changed_callback.is_some()
512 {
513 let mut realm = AutoRealm::new_from_handle(cx, constructor.handle());
514 match get_property(
515 &mut realm,
516 constructor.handle(),
517 c"observedAttributes",
518 StringificationBehavior::Default,
519 ) {
520 Ok(attributes) => attributes.unwrap_or_default(),
521 Err(error) => {
522 self.element_definition_is_running.set(false);
523 return Err(error);
524 },
525 }
526 } else {
527 Vec::new()
528 };
529
530 let (disable_internals, disable_shadow) = {
532 let mut realm = AutoRealm::new_from_handle(cx, constructor.handle());
533 match get_property::<Vec<DOMString>>(
534 &mut realm,
535 constructor.handle(),
536 c"disabledFeatures",
537 StringificationBehavior::Default,
538 ) {
539 Ok(sequence) => {
540 let sequence = sequence.unwrap_or_default();
541 (
542 sequence.iter().any(|s| *s == "internals"),
543 sequence.iter().any(|s| *s == "shadow"),
544 )
545 },
546 Err(error) => {
547 self.element_definition_is_running.set(false);
548 return Err(error);
549 },
550 }
551 };
552
553 let form_associated: bool = {
555 let mut realm = AutoRealm::new_from_handle(cx, constructor.handle());
556 match get_property(&mut realm, constructor.handle(), c"formAssociated", ()) {
557 Ok(flag) => flag.unwrap_or_default(),
558 Err(error) => {
559 self.element_definition_is_running.set(false);
560 return Err(error);
561 },
562 }
563 };
564
565 if form_associated {
567 let mut realm = AutoRealm::new_from_handle(cx, proto_object.handle());
568 unsafe {
569 if let Err(error) = self.add_form_associated_callbacks(
570 &mut realm,
571 proto_object.handle(),
572 &mut callbacks,
573 ) {
574 self.element_definition_is_running.set(false);
575 return Err(error);
576 }
577 }
578 }
579
580 self.element_definition_is_running.set(false);
581
582 let definition = Rc::new(CustomElementDefinition::new(
588 name.clone(),
589 local_name.clone(),
590 constructor_,
591 observed_attributes,
592 callbacks,
593 form_associated,
594 disable_internals,
595 disable_shadow,
596 ));
597
598 self.definitions
600 .borrow_mut()
601 .insert(name.clone(), definition.clone());
602
603 if self.is_scoped.get() {
607 for document in self.scoped_document_set.borrow().iter() {
608 self.upgrade_particular_elements_within_a_document(
609 cx,
610 document,
611 &definition,
612 &local_name,
613 &local_name,
614 );
615 }
616 } else {
617 self.upgrade_particular_elements_within_a_document(
621 cx,
622 &self.window.Document(),
623 &definition,
624 &local_name,
625 &name,
626 );
627 }
628
629 let promise = self
632 .when_defined
633 .borrow_mut()
634 .remove(&name)
635 .map(|promise| promise.root(cx));
636 if let Some(promise) = promise {
637 rooted!(&in(cx) let mut constructor = UndefinedValue());
638 definition
639 .constructor
640 .to_jsval(cx, constructor.handle_mut());
641 promise.resolve_native(cx, &constructor.get());
643 }
644 Ok(())
645 }
646
647 fn Get(&self, cx: &mut JSContext, name: DOMString, mut retval: MutableHandleValue) {
649 match self.definitions.borrow().get(&LocalName::from(name)) {
650 Some(definition) => definition.constructor.to_jsval(cx, retval),
651 None => retval.set(UndefinedValue()),
652 }
653 }
654
655 fn GetName(&self, constructor: RootedCallback<CustomElementConstructor>) -> Option<DOMString> {
657 self.definitions
658 .borrow()
659 .0
660 .values()
661 .find(|definition| definition.constructor.callback() == constructor.callback())
662 .map(|definition| TracedDOMString::from(definition.name.clone()).root())
663 }
664
665 fn WhenDefined(&self, realm: &mut CurrentRealm, name: DOMString) -> RootedPromise {
667 let name = LocalName::from(name);
668
669 if !is_valid_custom_element_name(&name) {
671 let promise = Promise::new_in_realm(realm);
672 let error = DOMException::new(
673 realm,
674 self.window.as_global_scope(),
675 DOMErrorName::SyntaxError,
676 );
677 promise.reject_native(realm, &error);
678 return promise;
679 }
680
681 if let Some(definition) = self.definitions.borrow().get(&LocalName::from(&*name)) {
683 rooted!(&in(*realm) let mut constructor = UndefinedValue());
684 definition
685 .constructor
686 .to_jsval(realm, constructor.handle_mut());
687 let promise = Promise::new_in_realm(realm);
688 promise.resolve_native(realm, &constructor.get());
689 return promise;
690 }
691
692 let existing_promise = self
694 .when_defined
695 .borrow()
696 .get(&name)
697 .map(|promise| promise.root(realm));
698 existing_promise.unwrap_or_else(|| {
699 let promise = Promise::new_in_realm(realm);
700 self.when_defined
701 .borrow_mut()
702 .insert(name, promise.to_traced());
703 promise
704 })
705 }
706
707 fn Upgrade(&self, cx: &JSContext, node: &Node) {
709 for node in node.traverse_preorder_unrooted(cx, ShadowIncluding::Yes) {
712 let Some(element) = node.downcast::<Element>() else {
714 continue;
715 };
716 if element
719 .custom_element_registry()
720 .is_some_and(|registry| *registry != *self)
721 {
722 continue;
723 }
724 try_upgrade_element(cx, element);
726 }
727 }
728
729 fn Initialize(&self, cx: &JSContext, root: &Node) -> ErrorResult {
731 if !self.is_scoped.get() {
735 let is_document = root.is::<Document>();
736 let registry_mismatch = root
737 .owner_doc()
738 .custom_element_registry()
739 .is_some_and(|registry| *registry != *self);
740 if is_document || registry_mismatch {
741 return Err(Error::NotSupported(Some(
742 "Initialize is not allowed on a non-scoped registry for this root".to_owned(),
743 )));
744 }
745 }
746
747 if let Some(document) = root.downcast::<Document>() {
750 if document.custom_element_registry().is_none() {
751 document.set_custom_element_registry(self);
752 }
753 }
754 else if let Some(shadow_root) = root.downcast::<ShadowRoot>() &&
757 shadow_root.custom_element_registry().is_none()
758 {
759 shadow_root.set_custom_element_registry(Some(self));
760 }
761
762 for node in root.traverse_preorder(ShadowIncluding::No) {
764 let Some(element) = node.downcast::<Element>() else {
766 continue;
767 };
768
769 if element
773 .custom_element_registry()
774 .is_none_or(|registry| Self::is_a_global_element_registry(Some(&*registry)))
775 {
776 element.set_custom_element_registry(Some(self), cx.no_gc());
778
779 if self.is_scoped.get() {
782 let document = element.upcast::<Node>().owner_doc();
783 self.scoped_document_set
784 .borrow_mut()
785 .insert(Dom::from_ref(&document));
786 }
787 } else if element
789 .custom_element_registry()
790 .is_none_or(|registry| *registry != *self)
791 {
792 continue;
793 }
794
795 try_upgrade_element(cx, element);
797 }
798 Ok(())
799 }
800}
801
802#[derive(Clone, JSTraceable, MallocSizeOf)]
803#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
804pub(crate) struct LifecycleCallbacks {
805 connected_callback: Option<TracedCallback<Function>>,
806 connected_move_callback: Option<TracedCallback<Function>>,
807 disconnected_callback: Option<TracedCallback<Function>>,
808 adopted_callback: Option<TracedCallback<Function>>,
809 attribute_changed_callback: Option<TracedCallback<Function>>,
810 form_associated_callback: Option<TracedCallback<Function>>,
811 form_reset_callback: Option<TracedCallback<Function>>,
812 form_disabled_callback: Option<TracedCallback<Function>>,
813 form_state_restore_callback: Option<TracedCallback<Function>>,
814}
815
816struct RootedLifecycleCallbacks {
817 connected_callback: Option<RootedCallback<Function>>,
818 connected_move_callback: Option<RootedCallback<Function>>,
819 disconnected_callback: Option<RootedCallback<Function>>,
820 adopted_callback: Option<RootedCallback<Function>>,
821 attribute_changed_callback: Option<RootedCallback<Function>>,
822 form_associated_callback: Option<RootedCallback<Function>>,
823 form_reset_callback: Option<RootedCallback<Function>>,
824 form_disabled_callback: Option<RootedCallback<Function>>,
825 form_state_restore_callback: Option<RootedCallback<Function>>,
826}
827
828impl From<RootedLifecycleCallbacks> for LifecycleCallbacks {
829 fn from(callbacks: RootedLifecycleCallbacks) -> Self {
830 Self {
831 connected_callback: callbacks
832 .connected_callback
833 .map(|callback| callback.to_traced()),
834 connected_move_callback: callbacks
835 .connected_move_callback
836 .map(|callback| callback.to_traced()),
837 disconnected_callback: callbacks
838 .disconnected_callback
839 .map(|callback| callback.to_traced()),
840 adopted_callback: callbacks
841 .adopted_callback
842 .map(|callback| callback.to_traced()),
843 attribute_changed_callback: callbacks
844 .attribute_changed_callback
845 .map(|callback| callback.to_traced()),
846 form_associated_callback: callbacks
847 .form_associated_callback
848 .map(|callback| callback.to_traced()),
849 form_reset_callback: callbacks
850 .form_reset_callback
851 .map(|callback| callback.to_traced()),
852 form_disabled_callback: callbacks
853 .form_disabled_callback
854 .map(|callback| callback.to_traced()),
855 form_state_restore_callback: callbacks
856 .form_state_restore_callback
857 .map(|callback| callback.to_traced()),
858 }
859 }
860}
861
862#[derive(Clone, JSTraceable, MallocSizeOf)]
863#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
864pub(crate) enum ConstructionStackEntry {
865 Element(Dom<Element>),
866 AlreadyConstructedMarker,
867}
868
869#[derive(Clone, JSTraceable, MallocSizeOf)]
874#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
875#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_in_rc)]
876pub(crate) struct CustomElementDefinition {
877 #[no_trace]
879 pub(crate) name: LocalName,
880
881 #[no_trace]
883 pub(crate) local_name: LocalName,
884
885 pub(crate) constructor: TracedCallback<CustomElementConstructor>,
887
888 pub(crate) observed_attributes: Vec<DOMString>,
890
891 pub(crate) callbacks: LifecycleCallbacks,
893
894 pub(crate) construction_stack: DomRefCell<Vec<ConstructionStackEntry>>,
896
897 pub(crate) form_associated: bool,
899
900 pub(crate) disable_internals: bool,
902
903 pub(crate) disable_shadow: bool,
905}
906
907impl CustomElementDefinition {
908 #[expect(clippy::too_many_arguments)]
909 fn new(
910 name: LocalName,
911 local_name: LocalName,
912 constructor: RootedCallback<CustomElementConstructor>,
913 observed_attributes: Vec<DOMString>,
914 callbacks: RootedLifecycleCallbacks,
915 form_associated: bool,
916 disable_internals: bool,
917 disable_shadow: bool,
918 ) -> CustomElementDefinition {
919 CustomElementDefinition {
920 name,
921 local_name,
922 constructor: constructor.to_traced(),
923 observed_attributes,
924 callbacks: callbacks.into(),
925 construction_stack: Default::default(),
926 form_associated,
927 disable_internals,
928 disable_shadow,
929 }
930 }
931
932 pub(crate) fn is_autonomous(&self) -> bool {
934 self.name == self.local_name
935 }
936
937 #[expect(unsafe_code)]
939 pub(crate) fn create_element(
940 &self,
941 cx: &mut JSContext,
942 document: &Document,
943 prefix: Option<Prefix>,
944 registry: Option<&CustomElementRegistry>,
945 ) -> Fallible<DomRoot<Element>> {
946 let window = document.window();
947
948 rooted!(&in(cx) let constructor = ObjectValue(self.constructor.callback()));
950 rooted!(&in(cx) let mut element = ptr::null_mut::<JSObject>());
951 {
952 let mut realm = AutoRealm::new(cx, NonNull::new(self.constructor.callback()).unwrap());
954 let cx = &mut realm;
955
956 run_a_script::<DomTypeHolder, _, _>(cx, window.upcast(), |cx| {
959 run_a_callback::<DomTypeHolder, _>(window.upcast(), || {
960 let args = HandleValueArray::empty();
961 if unsafe { !Construct1(cx, constructor.handle(), &args, element.handle_mut()) }
962 {
963 Err(Error::JSFailed)
964 } else {
965 Ok(())
966 }
967 })
968 })?;
969 }
970
971 rooted!(&in(cx) let element_val = ObjectValue(element.get()));
972 let element: DomRoot<Element> =
973 match FromJSValConvertible::from_jsval(cx, element_val.handle(), ()) {
974 Ok(ConversionResult::Success(element)) => element,
975 Ok(ConversionResult::Failure(..)) => {
976 return Err(Error::Type(
977 c"Constructor did not return a DOM node".to_owned(),
978 ));
979 },
980 _ => return Err(Error::JSFailed),
981 };
982
983 assert!(element.is::<HTMLElement>());
989
990 if element.HasAttributes() ||
996 element.upcast::<Node>().children_count() > 0 ||
997 element.upcast::<Node>().has_parent() ||
998 &*element.upcast::<Node>().owner_doc() != document ||
999 *element.namespace() != ns!(html) ||
1000 *element.local_name() != self.local_name
1001 {
1002 return Err(Error::NotSupported(None));
1003 }
1004
1005 element.set_prefix(prefix);
1007
1008 element.set_custom_element_registry(registry, cx.no_gc());
1013
1014 Ok(element)
1015 }
1016
1017 pub(crate) fn has_attribute_changed_callback(&self) -> bool {
1018 self.callbacks.attribute_changed_callback.is_some()
1019 }
1020}
1021
1022pub(crate) fn upgrade_element(
1024 cx: &mut JSContext,
1025 definition: Rc<CustomElementDefinition>,
1026 element: &Element,
1027) {
1028 let state = element.get_custom_element_state();
1030 if state != CustomElementState::Undefined && state != CustomElementState::Uncustomized {
1031 return;
1032 }
1033
1034 element.set_custom_element_definition(Rc::clone(&definition), cx.no_gc());
1036
1037 element.set_custom_element_state(CustomElementState::Failed, cx.no_gc());
1039
1040 let custom_element_reaction_stack = ScriptThread::custom_element_reaction_stack();
1044 for attr in element.attrs().borrow().iter() {
1045 let local_name = attr.local_name().clone();
1046 let namespace = attr.namespace().clone();
1047 custom_element_reaction_stack.enqueue_callback_reaction(
1048 cx,
1049 element,
1050 CallbackReaction::AttributeChanged(local_name, None, Some(&*attr.value()), namespace),
1051 Some(definition.clone()),
1052 );
1053 }
1054
1055 if element.is_connected() {
1058 custom_element_reaction_stack.enqueue_callback_reaction(
1059 cx,
1060 element,
1061 CallbackReaction::Connected,
1062 Some(definition.clone()),
1063 );
1064 }
1065
1066 definition
1068 .construction_stack
1069 .borrow_mut()
1070 .push(ConstructionStackEntry::Element(Dom::from_ref(element)));
1071
1072 let result = run_upgrade_constructor(cx, &definition, element);
1074
1075 definition.construction_stack.borrow_mut().pop();
1077
1078 if let Err(error) = result {
1080 element.clear_custom_element_definition(cx.no_gc());
1082
1083 element.clear_reaction_queue();
1085
1086 let global = GlobalScope::current().expect("No current global");
1088
1089 let mut realm = enter_auto_realm(cx, &*global);
1090 let cx = &mut realm.current_realm();
1091
1092 throw_dom_exception(cx, &global, error);
1093 report_pending_exception(cx);
1094
1095 return;
1096 }
1097
1098 if let Some(html_element) = element.downcast::<HTMLElement>() &&
1100 html_element.is_form_associated_custom_element()
1101 {
1102 html_element.reset_form_owner(cx);
1106 if let Some(form) = html_element.form_owner() {
1107 form.upcast::<Node>().rev_version(cx.no_gc());
1110 }
1114
1115 element.check_disabled_attribute(cx.no_gc());
1122 element.check_ancestors_disabled_state_for_form_control(cx.no_gc());
1123 element.update_read_write_state_from_readonly_attribute(cx.no_gc());
1124
1125 if element.disabled_state() {
1128 custom_element_reaction_stack.enqueue_callback_reaction(
1129 cx,
1130 element,
1131 CallbackReaction::FormDisabled(true),
1132 Some(definition),
1133 )
1134 }
1135 }
1136
1137 element.set_custom_element_state(CustomElementState::Custom, cx.no_gc());
1139}
1140
1141#[expect(unsafe_code)]
1144fn run_upgrade_constructor(
1145 cx: &mut JSContext,
1146 definition: &CustomElementDefinition,
1147 element: &Element,
1148) -> ErrorResult {
1149 let constructor = &definition.constructor;
1150 let window = element.owner_window();
1151 rooted!(&in(cx) let constructor_val = ObjectValue(constructor.callback()));
1152 rooted!(&in(cx) let mut element_val = UndefinedValue());
1153 element.to_jsval(cx, element_val.handle_mut());
1154 rooted!(&in(cx) let mut construct_result = ptr::null_mut::<JSObject>());
1155 {
1156 if definition.disable_shadow && element.is_shadow_host() {
1159 return Err(Error::NotSupported(None));
1160 }
1161
1162 let mut realm = AutoRealm::new(cx, NonNull::new(constructor.callback()).unwrap());
1164 let cx = &mut *realm;
1165
1166 let args = HandleValueArray::empty();
1167 element.set_custom_element_state(CustomElementState::Precustomized, cx.no_gc());
1169
1170 run_a_script::<DomTypeHolder, _, _>(cx, window.upcast(), |cx| {
1173 run_a_callback::<DomTypeHolder, _>(window.upcast(), || {
1174 if unsafe {
1175 !Construct1(
1176 cx,
1177 constructor_val.handle(),
1178 &args,
1179 construct_result.handle_mut(),
1180 )
1181 } {
1182 Err(Error::JSFailed)
1183 } else {
1184 Ok(())
1185 }
1186 })
1187 })?;
1188
1189 let mut same = false;
1190 rooted!(&in(cx) let construct_result_val = ObjectValue(construct_result.get()));
1191
1192 if unsafe {
1194 !SameValue(
1195 cx,
1196 construct_result_val.handle(),
1197 element_val.handle(),
1198 &mut same,
1199 )
1200 } {
1201 return Err(Error::JSFailed);
1202 }
1203 if !same {
1204 return Err(Error::Type(
1205 c"Returned element is not SameValue as the upgraded element".to_owned(),
1206 ));
1207 }
1208 }
1209 Ok(())
1210}
1211
1212pub(crate) fn try_upgrade_element(cx: &JSContext, element: &Element) {
1214 let lookup_registry = {
1218 let registry = element.custom_element_registry();
1221 if registry
1222 .as_ref()
1223 .is_some_and(|registry| registry.is_scoped())
1224 {
1225 registry
1226 } else {
1227 element
1228 .owner_document_unrooted(cx.no_gc())
1229 .custom_element_registry()
1230 }
1231 };
1232 if let Some(definition) = CustomElementRegistry::lookup_custom_element_definition(
1233 lookup_registry.as_deref(),
1234 element.namespace(),
1235 element.local_name(),
1236 element.get_is().as_ref(),
1237 ) {
1238 ScriptThread::enqueue_upgrade_reaction(cx, element, definition);
1241 }
1242}
1243
1244#[derive(JSTraceable, MallocSizeOf)]
1245#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1246pub(crate) enum CustomElementReaction {
1247 Upgrade(#[conditional_malloc_size_of] Rc<CustomElementDefinition>),
1248 Callback(
1249 TracedCallback<Function>,
1250 #[ignore_malloc_size_of = "mozjs"] Box<[Heap<JSVal>]>,
1251 ),
1252}
1253
1254impl CustomElementReaction {
1255 pub(crate) fn invoke(&self, cx: &mut JSContext, element: &Element) {
1257 match *self {
1259 CustomElementReaction::Upgrade(ref definition) => {
1260 upgrade_element(cx, definition.clone(), element)
1261 },
1262 CustomElementReaction::Callback(ref callback, ref arguments) => {
1263 let arguments = arguments.iter().map(|arg| arg.as_handle_value()).collect();
1265 rooted!(&in(cx) let mut value: JSVal);
1266 let _ = callback.Call_(
1267 cx,
1268 element,
1269 arguments,
1270 value.handle_mut(),
1271 ExceptionHandling::Report,
1272 );
1273 },
1274 }
1275 }
1276}
1277
1278pub(crate) enum CallbackReaction<'a> {
1279 Connected,
1280 Disconnected,
1281 Adopted(DomRoot<Document>, DomRoot<Document>),
1282 AttributeChanged(
1283 LocalName,
1284 Option<&'a AttrValue>,
1285 Option<&'a AttrValue>,
1286 Namespace,
1287 ),
1288 FormAssociated(Option<DomRoot<HTMLFormElement>>),
1289 FormDisabled(bool),
1290 FormReset,
1291 ConnectedMove,
1292}
1293
1294#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
1296enum BackupElementQueueFlag {
1297 Processing,
1298 NotProcessing,
1299}
1300
1301#[derive(JSTraceable, MallocSizeOf)]
1306#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1307#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_in_rc)]
1308pub(crate) struct CustomElementReactionStack {
1309 stack: DomRefCell<Vec<ElementQueue>>,
1310 backup_queue: ElementQueue,
1311 processing_backup_element_queue: Cell<BackupElementQueueFlag>,
1312}
1313
1314impl CustomElementReactionStack {
1315 pub(crate) fn new() -> CustomElementReactionStack {
1316 CustomElementReactionStack {
1317 stack: DomRefCell::new(Vec::new()),
1318 backup_queue: ElementQueue::new(),
1319 processing_backup_element_queue: Cell::new(BackupElementQueueFlag::NotProcessing),
1320 }
1321 }
1322
1323 pub(crate) fn push_new_element_queue(&self) {
1324 self.stack.borrow_mut().push(ElementQueue::new());
1325 }
1326
1327 pub(crate) fn pop_current_element_queue(&self, cx: &mut JSContext) {
1328 rooted_vec!(let mut stack);
1329 mem::swap(&mut *stack, &mut *self.stack.borrow_mut());
1330
1331 if let Some(current_queue) = stack.last() {
1332 current_queue.invoke_reactions(cx);
1333 }
1334 stack.pop();
1335
1336 mem::swap(&mut *self.stack.borrow_mut(), &mut *stack);
1337 self.stack.borrow_mut().append(&mut *stack);
1338 }
1339
1340 pub(crate) fn invoke_backup_element_queue(&self, cx: &mut JSContext) {
1343 self.backup_queue.invoke_reactions(cx);
1345
1346 self.processing_backup_element_queue
1348 .set(BackupElementQueueFlag::NotProcessing);
1349 }
1350
1351 pub(crate) fn enqueue_element(&self, cx: &JSContext, element: &Element) {
1353 if let Some(current_queue) = self.stack.borrow().last() {
1354 current_queue.append_element(element);
1356 } else {
1357 self.backup_queue.append_element(element);
1359
1360 if self.processing_backup_element_queue.get() == BackupElementQueueFlag::Processing {
1362 return;
1363 }
1364
1365 self.processing_backup_element_queue
1367 .set(BackupElementQueueFlag::Processing);
1368
1369 ScriptThread::enqueue_microtask(cx, Box::new(CustomElementReactionMicrotask::new()));
1371 }
1372 }
1373
1374 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1376 pub(crate) fn enqueue_callback_reaction(
1377 &self,
1378 cx: &mut JSContext,
1379 element: &Element,
1380 reaction: CallbackReaction,
1381 definition: Option<Rc<CustomElementDefinition>>,
1382 ) {
1383 let definition = match definition.or_else(|| element.get_custom_element_definition()) {
1385 Some(definition) => definition,
1386 None => return,
1387 };
1388
1389 let (callback, args) = match reaction {
1392 CallbackReaction::Connected => {
1393 (definition.callbacks.connected_callback.clone(), Vec::new())
1394 },
1395 CallbackReaction::Disconnected => (
1396 definition.callbacks.disconnected_callback.clone(),
1397 Vec::new(),
1398 ),
1399 CallbackReaction::Adopted(ref old_doc, ref new_doc) => {
1400 let args = vec![Heap::default(), Heap::default()];
1401 args[0].set(ObjectValue(old_doc.reflector().get_jsobject().get()));
1402 args[1].set(ObjectValue(new_doc.reflector().get_jsobject().get()));
1403 (definition.callbacks.adopted_callback.clone(), args)
1404 },
1405 CallbackReaction::AttributeChanged(local_name, old_val, val, namespace) => {
1406 if !definition
1408 .observed_attributes
1409 .iter()
1410 .any(|attr| *attr == *local_name)
1411 {
1412 return;
1413 }
1414
1415 let mut realm = enter_auto_realm(cx, &*element.global());
1419 let cx = &mut realm;
1420
1421 let local_name = TracedDOMString::from(local_name).root();
1422 rooted!(&in(cx) let mut name_value = UndefinedValue());
1423 local_name.to_jsval(cx, name_value.handle_mut());
1424
1425 rooted!(&in(cx) let mut old_value = NullValue());
1426 if let Some(old_val) = old_val {
1427 old_val.to_jsval(cx, old_value.handle_mut());
1428 }
1429
1430 rooted!(&in(cx) let mut value = NullValue());
1431 if let Some(val) = val {
1432 val.to_jsval(cx, value.handle_mut());
1433 }
1434
1435 rooted!(&in(cx) let mut namespace_value = NullValue());
1436 if namespace != ns!() {
1437 let namespace = TracedDOMString::from(namespace).root();
1438 namespace.to_jsval(cx, namespace_value.handle_mut());
1439 }
1440
1441 let args = vec![
1442 Heap::default(),
1443 Heap::default(),
1444 Heap::default(),
1445 Heap::default(),
1446 ];
1447 args[0].set(name_value.get());
1448 args[1].set(old_value.get());
1449 args[2].set(value.get());
1450 args[3].set(namespace_value.get());
1451
1452 (
1453 definition.callbacks.attribute_changed_callback.clone(),
1454 args,
1455 )
1456 },
1457 CallbackReaction::FormAssociated(form) => {
1458 let args = vec![Heap::default()];
1459 if let Some(form) = form {
1460 args[0].set(ObjectValue(form.reflector().get_jsobject().get()));
1461 } else {
1462 args[0].set(NullValue());
1463 }
1464 (definition.callbacks.form_associated_callback.clone(), args)
1465 },
1466 CallbackReaction::FormDisabled(disabled) => {
1467 rooted!(&in(cx) let disabled_value = BooleanValue(disabled));
1468 let args = vec![Heap::default()];
1469 args[0].set(disabled_value.get());
1470 (definition.callbacks.form_disabled_callback.clone(), args)
1471 },
1472 CallbackReaction::FormReset => {
1473 (definition.callbacks.form_reset_callback.clone(), Vec::new())
1474 },
1475 CallbackReaction::ConnectedMove => {
1476 let callback = definition.callbacks.connected_move_callback.clone();
1477 if callback.is_none() {
1479 let disconnected_callback = definition.callbacks.disconnected_callback.clone();
1482
1483 let connected_callback = definition.callbacks.connected_callback.clone();
1486
1487 if disconnected_callback.is_none() && connected_callback.is_none() {
1490 return;
1491 }
1492
1493 if let Some(disconnected_callback) = disconnected_callback {
1497 element.push_callback_reaction(
1498 disconnected_callback.root(cx),
1499 Box::new([]),
1500 cx.no_gc(),
1501 );
1502 }
1503 if let Some(connected_callback) = connected_callback {
1506 element.push_callback_reaction(
1507 connected_callback.root(cx),
1508 Box::new([]),
1509 cx.no_gc(),
1510 );
1511 }
1512
1513 self.enqueue_element(cx, element);
1514 return;
1515 }
1516
1517 (callback, Vec::new())
1518 },
1519 };
1520
1521 let callback = match callback {
1523 Some(callback) => callback,
1524 None => return,
1525 };
1526
1527 element.push_callback_reaction(callback.root(cx), args.into_boxed_slice(), cx.no_gc());
1530
1531 self.enqueue_element(cx, element);
1533 }
1534
1535 pub(crate) fn enqueue_upgrade_reaction(
1537 &self,
1538 cx: &JSContext,
1539 element: &Element,
1540 definition: Rc<CustomElementDefinition>,
1541 ) {
1542 element.push_upgrade_reaction(definition, cx.no_gc());
1545
1546 self.enqueue_element(cx, element);
1548 }
1549}
1550
1551#[derive(JSTraceable, MallocSizeOf)]
1553#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1554struct ElementQueue {
1555 queue: DomRefCell<VecDeque<Dom<Element>>>,
1556}
1557
1558impl ElementQueue {
1559 fn new() -> ElementQueue {
1560 ElementQueue {
1561 queue: Default::default(),
1562 }
1563 }
1564
1565 fn invoke_reactions(&self, cx: &mut JSContext) {
1567 while let Some(element) = self.next_element() {
1569 element.invoke_reactions(cx)
1570 }
1571 self.queue.borrow_mut().clear();
1572 }
1573
1574 fn next_element(&self) -> Option<DomRoot<Element>> {
1575 self.queue
1576 .borrow_mut()
1577 .pop_front()
1578 .as_deref()
1579 .map(DomRoot::from_ref)
1580 }
1581
1582 fn append_element(&self, element: &Element) {
1583 self.queue.borrow_mut().push_back(Dom::from_ref(element));
1584 }
1585}
1586
1587pub(crate) fn is_valid_custom_element_name(name: &str) -> bool {
1589 let mut chars = name.chars();
1592 if !chars.next().is_some_and(|c| c.is_ascii_lowercase()) {
1593 return false;
1594 }
1595
1596 let mut has_dash = false;
1597
1598 for c in chars {
1599 if c == '-' {
1600 has_dash = true;
1601 continue;
1602 }
1603
1604 if !is_potential_custom_element_char(c) {
1605 return false;
1606 }
1607 }
1608
1609 if !has_dash {
1610 return false;
1611 }
1612
1613 if name == "annotation-xml" ||
1614 name == "color-profile" ||
1615 name == "font-face" ||
1616 name == "font-face-src" ||
1617 name == "font-face-uri" ||
1618 name == "font-face-format" ||
1619 name == "font-face-name" ||
1620 name == "missing-glyph"
1621 {
1622 return false;
1623 }
1624
1625 true
1626}
1627
1628fn is_potential_custom_element_char(c: char) -> bool {
1631 c == '-' ||
1632 c == '.' ||
1633 c == '_' ||
1634 c == '\u{B7}' ||
1635 c.is_ascii_digit() ||
1636 c.is_ascii_lowercase() ||
1637 ('\u{C0}'..='\u{D6}').contains(&c) ||
1638 ('\u{D8}'..='\u{F6}').contains(&c) ||
1639 ('\u{F8}'..='\u{37D}').contains(&c) ||
1640 ('\u{37F}'..='\u{1FFF}').contains(&c) ||
1641 ('\u{200C}'..='\u{200D}').contains(&c) ||
1642 ('\u{203F}'..='\u{2040}').contains(&c) ||
1643 ('\u{2070}'..='\u{218F}').contains(&c) ||
1644 ('\u{2C00}'..='\u{2FEF}').contains(&c) ||
1645 ('\u{3001}'..='\u{D7FF}').contains(&c) ||
1646 ('\u{F900}'..='\u{FDCF}').contains(&c) ||
1647 ('\u{FDF0}'..='\u{FFFD}').contains(&c) ||
1648 ('\u{10000}'..='\u{EFFFF}').contains(&c)
1649}
1650
1651fn is_extendable_element_interface(element: &str) -> bool {
1652 element == "a" ||
1653 element == "abbr" ||
1654 element == "acronym" ||
1655 element == "address" ||
1656 element == "area" ||
1657 element == "article" ||
1658 element == "aside" ||
1659 element == "audio" ||
1660 element == "b" ||
1661 element == "base" ||
1662 element == "bdi" ||
1663 element == "bdo" ||
1664 element == "big" ||
1665 element == "blockquote" ||
1666 element == "body" ||
1667 element == "br" ||
1668 element == "button" ||
1669 element == "canvas" ||
1670 element == "caption" ||
1671 element == "center" ||
1672 element == "cite" ||
1673 element == "code" ||
1674 element == "col" ||
1675 element == "colgroup" ||
1676 element == "data" ||
1677 element == "datalist" ||
1678 element == "dd" ||
1679 element == "del" ||
1680 element == "details" ||
1681 element == "dfn" ||
1682 element == "dialog" ||
1683 element == "dir" ||
1684 element == "div" ||
1685 element == "dl" ||
1686 element == "dt" ||
1687 element == "em" ||
1688 element == "embed" ||
1689 element == "fieldset" ||
1690 element == "figcaption" ||
1691 element == "figure" ||
1692 element == "font" ||
1693 element == "footer" ||
1694 element == "form" ||
1695 element == "frame" ||
1696 element == "frameset" ||
1697 element == "h1" ||
1698 element == "h2" ||
1699 element == "h3" ||
1700 element == "h4" ||
1701 element == "h5" ||
1702 element == "h6" ||
1703 element == "head" ||
1704 element == "header" ||
1705 element == "hgroup" ||
1706 element == "hr" ||
1707 element == "html" ||
1708 element == "i" ||
1709 element == "iframe" ||
1710 element == "img" ||
1711 element == "input" ||
1712 element == "ins" ||
1713 element == "kbd" ||
1714 element == "label" ||
1715 element == "legend" ||
1716 element == "li" ||
1717 element == "link" ||
1718 element == "listing" ||
1719 element == "main" ||
1720 element == "map" ||
1721 element == "mark" ||
1722 element == "marquee" ||
1723 element == "menu" ||
1724 element == "meta" ||
1725 element == "meter" ||
1726 element == "nav" ||
1727 element == "nobr" ||
1728 element == "noframes" ||
1729 element == "noscript" ||
1730 element == "object" ||
1731 element == "ol" ||
1732 element == "optgroup" ||
1733 element == "option" ||
1734 element == "output" ||
1735 element == "p" ||
1736 element == "param" ||
1737 element == "picture" ||
1738 element == "plaintext" ||
1739 element == "pre" ||
1740 element == "progress" ||
1741 element == "q" ||
1742 element == "rp" ||
1743 element == "rt" ||
1744 element == "ruby" ||
1745 element == "s" ||
1746 element == "samp" ||
1747 element == "script" ||
1748 element == "section" ||
1749 element == "select" ||
1750 element == "slot" ||
1751 element == "small" ||
1752 element == "source" ||
1753 element == "span" ||
1754 element == "strike" ||
1755 element == "strong" ||
1756 element == "style" ||
1757 element == "sub" ||
1758 element == "summary" ||
1759 element == "sup" ||
1760 element == "table" ||
1761 element == "tbody" ||
1762 element == "td" ||
1763 element == "template" ||
1764 element == "textarea" ||
1765 element == "tfoot" ||
1766 element == "th" ||
1767 element == "thead" ||
1768 element == "time" ||
1769 element == "title" ||
1770 element == "tr" ||
1771 element == "tt" ||
1772 element == "track" ||
1773 element == "u" ||
1774 element == "ul" ||
1775 element == "var" ||
1776 element == "video" ||
1777 element == "wbr" ||
1778 element == "xmp"
1779}