@@ -105,8 +105,53 @@ impl HtmlTag {
105105 bail ! ( "tag name must not be empty" ) ;
106106 }
107107
108- if let Some ( c) = string. chars ( ) . find ( |& c| !charsets:: is_valid_in_tag_name ( c) ) {
109- bail ! ( "the character {} is not valid in a tag name" , c. repr( ) ) ;
108+ let mut has_hyphen = false ;
109+ let mut has_uppercase = false ;
110+
111+ for c in string. chars ( ) {
112+ if c == '-' {
113+ has_hyphen = true ;
114+ } else if !charsets:: is_valid_in_tag_name ( c) {
115+ bail ! ( "the character {} is not valid in a tag name" , c. repr( ) ) ;
116+ } else {
117+ has_uppercase |= c. is_ascii_uppercase ( ) ;
118+ }
119+ }
120+
121+ // If we encounter a hyphen, we are dealing with a custom element rather
122+ // than a standard HTML element.
123+ //
124+ // A valid custom element name must:
125+ // - Contain at least one hyphen (U+002D)
126+ // - Start with an ASCII lowercase letter (a-z)
127+ // - Not contain any ASCII uppercase letters (A-Z)
128+ // - Not be one of the reserved names
129+ // - Only contain valid characters (ASCII alphanumeric and hyphens)
130+ //
131+ // See https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
132+ if has_hyphen {
133+ if !string. starts_with ( |c : char | c. is_ascii_lowercase ( ) ) {
134+ bail ! ( "custom element name must start with a lowercase letter" ) ;
135+ }
136+ if has_uppercase {
137+ bail ! ( "custom element name must not contain uppercase letters" ) ;
138+ }
139+
140+ // These names are used in SVG and MathML. Since `html.elem` only
141+ // supports creation of _HTML_ elements, they are forbidden.
142+ if matches ! (
143+ string,
144+ "annotation-xml"
145+ | "color-profile"
146+ | "font-face"
147+ | "font-face-src"
148+ | "font-face-uri"
149+ | "font-face-format"
150+ | "font-face-name"
151+ | "missing-glyph"
152+ ) {
153+ bail ! ( "name is reserved and not valid for a custom element" ) ;
154+ }
110155 }
111156
112157 Ok ( Self ( PicoStr :: intern ( string) ) )
0 commit comments