Skip to content

Commit 5c0a1e7

Browse files
committed
HTML API: Lower-case HTML tag names in get_qualified_tag_name().
Since this method is meant for printing and display, a more expected return value would be the lower-case variant of a given HTML tag name. This patch changes the behavior accordingly. Follow-up to [58867]. See Core-61576.
1 parent 2ae6561 commit 5c0a1e7

File tree

1 file changed

+141
-89
lines changed

1 file changed

+141
-89
lines changed

src/wp-includes/html-api/class-wp-html-tag-processor.php

Lines changed: 141 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2903,10 +2903,39 @@ public function get_tag(): ?string {
29032903
}
29042904

29052905
/**
2906-
* Returns the adjusted tag name for a given token, taking into
2907-
* account the current parsing context, whether HTML, SVG, or MathML.
2906+
* Returns the normative adjusted tag name for an element in its given
2907+
* namespace, usually for display or XML output only.
2908+
*
2909+
* For checking if a tag has a given name, rely on {@see static::get_tag()}
2910+
* which takes HTML case-folding rules into account and always returns an
2911+
* ASCII upper-case variant of the matched tag name.
2912+
*
2913+
* - Some SVG tags are mixed-case, such as `altGlyph`.
2914+
* - HTML custom elements’ casing is preserved.
2915+
* - All other tag names will be lower-cased.
2916+
*
2917+
* Example:
2918+
*
2919+
* $processor = new WP_HTML_Tag_Processor( '<div><Task-Item>' );
2920+
* $processor->next_tag();
2921+
* 'DIV' === $processor->get_tag();
2922+
* 'div' === $processor->get_qualified_tag_name();
2923+
*
2924+
* $processor->next_tag();
2925+
* 'TASK-ITEM' === $processor->get_tag();
2926+
* 'Task-Item' === $processor->get_qualified_tag_name();
2927+
*
2928+
* $processor = WP_HTML_Processor::create_fragment( '<svg><altglyph/><rect/></svg>' );
2929+
* $processor->next_tag( 'altglyph' );
2930+
* 'ALTGLYPH' === $processor->get_tag();
2931+
* 'altGlyph' === $processor->get_qualified_tag_name();
2932+
*
2933+
* $processor->next_tag();
2934+
* 'RECT' === $processor->get_tag();
2935+
* 'rect' === $processor->get_qualified_tag_name();
29082936
*
29092937
* @since 6.7.0
2938+
* @since 6.9.0 Respects mixed-case SVG and case-sensitive custome element tag names.
29102939
*
29112940
* @return string|null Name of current tag name.
29122941
*/
@@ -2916,140 +2945,160 @@ public function get_qualified_tag_name(): ?string {
29162945
return null;
29172946
}
29182947

2919-
if ( 'html' === $this->get_namespace() ) {
2920-
return $tag_name;
2948+
$lower_tag_name = strtolower( $tag_name );
2949+
$namespace = $this->get_namespace();
2950+
2951+
switch ( $namespace ) {
2952+
case 'html':
2953+
// Preserve casing of custom elements.
2954+
return str_contains( $tag_name, '-' )
2955+
? substr( $this->html, $this->tag_name_starts_at, $this->tag_name_length )
2956+
: $lower_tag_name;
2957+
2958+
case 'math':
2959+
return $lower_tag_name;
29212960
}
29222961

2923-
$lower_tag_name = strtolower( $tag_name );
2924-
if ( 'math' === $this->get_namespace() ) {
2962+
if ( 'svg' !== $namespace ) {
2963+
// This should be unreachable, but prevents tools from inaccurately reporting type errors.
29252964
return $lower_tag_name;
29262965
}
29272966

2928-
if ( 'svg' === $this->get_namespace() ) {
2929-
switch ( $lower_tag_name ) {
2930-
case 'altglyph':
2931-
return 'altGlyph';
2967+
switch ( $lower_tag_name ) {
2968+
case 'altglyph':
2969+
return 'altGlyph';
29322970

2933-
case 'altglyphdef':
2934-
return 'altGlyphDef';
2971+
case 'altglyphdef':
2972+
return 'altGlyphDef';
29352973

2936-
case 'altglyphitem':
2937-
return 'altGlyphItem';
2974+
case 'altglyphitem':
2975+
return 'altGlyphItem';
29382976

2939-
case 'animatecolor':
2940-
return 'animateColor';
2977+
case 'animatecolor':
2978+
return 'animateColor';
29412979

2942-
case 'animatemotion':
2943-
return 'animateMotion';
2980+
case 'animatemotion':
2981+
return 'animateMotion';
29442982

2945-
case 'animatetransform':
2946-
return 'animateTransform';
2983+
case 'animatetransform':
2984+
return 'animateTransform';
29472985

2948-
case 'clippath':
2949-
return 'clipPath';
2986+
case 'clippath':
2987+
return 'clipPath';
29502988

2951-
case 'feblend':
2952-
return 'feBlend';
2989+
case 'feblend':
2990+
return 'feBlend';
29532991

2954-
case 'fecolormatrix':
2955-
return 'feColorMatrix';
2992+
case 'fecolormatrix':
2993+
return 'feColorMatrix';
29562994

2957-
case 'fecomponenttransfer':
2958-
return 'feComponentTransfer';
2995+
case 'fecomponenttransfer':
2996+
return 'feComponentTransfer';
29592997

2960-
case 'fecomposite':
2961-
return 'feComposite';
2998+
case 'fecomposite':
2999+
return 'feComposite';
29623000

2963-
case 'feconvolvematrix':
2964-
return 'feConvolveMatrix';
3001+
case 'feconvolvematrix':
3002+
return 'feConvolveMatrix';
29653003

2966-
case 'fediffuselighting':
2967-
return 'feDiffuseLighting';
3004+
case 'fediffuselighting':
3005+
return 'feDiffuseLighting';
29683006

2969-
case 'fedisplacementmap':
2970-
return 'feDisplacementMap';
3007+
case 'fedisplacementmap':
3008+
return 'feDisplacementMap';
29713009

2972-
case 'fedistantlight':
2973-
return 'feDistantLight';
3010+
case 'fedistantlight':
3011+
return 'feDistantLight';
29743012

2975-
case 'fedropshadow':
2976-
return 'feDropShadow';
3013+
case 'fedropshadow':
3014+
return 'feDropShadow';
29773015

2978-
case 'feflood':
2979-
return 'feFlood';
3016+
case 'feflood':
3017+
return 'feFlood';
29803018

2981-
case 'fefunca':
2982-
return 'feFuncA';
3019+
case 'fefunca':
3020+
return 'feFuncA';
29833021

2984-
case 'fefuncb':
2985-
return 'feFuncB';
3022+
case 'fefuncb':
3023+
return 'feFuncB';
29863024

2987-
case 'fefuncg':
2988-
return 'feFuncG';
3025+
case 'fefuncg':
3026+
return 'feFuncG';
29893027

2990-
case 'fefuncr':
2991-
return 'feFuncR';
3028+
case 'fefuncr':
3029+
return 'feFuncR';
29923030

2993-
case 'fegaussianblur':
2994-
return 'feGaussianBlur';
3031+
case 'fegaussianblur':
3032+
return 'feGaussianBlur';
29953033

2996-
case 'feimage':
2997-
return 'feImage';
3034+
case 'feimage':
3035+
return 'feImage';
29983036

2999-
case 'femerge':
3000-
return 'feMerge';
3037+
case 'femerge':
3038+
return 'feMerge';
30013039

3002-
case 'femergenode':
3003-
return 'feMergeNode';
3040+
case 'femergenode':
3041+
return 'feMergeNode';
30043042

3005-
case 'femorphology':
3006-
return 'feMorphology';
3043+
case 'femorphology':
3044+
return 'feMorphology';
30073045

3008-
case 'feoffset':
3009-
return 'feOffset';
3046+
case 'feoffset':
3047+
return 'feOffset';
30103048

3011-
case 'fepointlight':
3012-
return 'fePointLight';
3049+
case 'fepointlight':
3050+
return 'fePointLight';
30133051

3014-
case 'fespecularlighting':
3015-
return 'feSpecularLighting';
3052+
case 'fespecularlighting':
3053+
return 'feSpecularLighting';
30163054

3017-
case 'fespotlight':
3018-
return 'feSpotLight';
3055+
case 'fespotlight':
3056+
return 'feSpotLight';
30193057

3020-
case 'fetile':
3021-
return 'feTile';
3058+
case 'fetile':
3059+
return 'feTile';
30223060

3023-
case 'feturbulence':
3024-
return 'feTurbulence';
3061+
case 'feturbulence':
3062+
return 'feTurbulence';
30253063

3026-
case 'foreignobject':
3027-
return 'foreignObject';
3064+
case 'foreignobject':
3065+
return 'foreignObject';
30283066

3029-
case 'glyphref':
3030-
return 'glyphRef';
3067+
case 'glyphref':
3068+
return 'glyphRef';
30313069

3032-
case 'lineargradient':
3033-
return 'linearGradient';
3070+
case 'lineargradient':
3071+
return 'linearGradient';
30343072

3035-
case 'radialgradient':
3036-
return 'radialGradient';
3073+
case 'radialgradient':
3074+
return 'radialGradient';
30373075

3038-
case 'textpath':
3039-
return 'textPath';
3076+
case 'solidcolor':
3077+
return 'solidColor';
30403078

3041-
default:
3042-
return $lower_tag_name;
3043-
}
3079+
case 'textarea':
3080+
return 'textArea';
3081+
3082+
case 'textpath':
3083+
return 'textPath';
30443084
}
30453085

3046-
// This unnecessary return prevents tools from inaccurately reporting type errors.
3047-
return $tag_name;
3086+
return $lower_tag_name;
30483087
}
30493088

30503089
/**
3051-
* Returns the adjusted attribute name for a given attribute, taking into
3052-
* account the current parsing context, whether HTML, SVG, or MathML.
3090+
* Returns the normative adjusted attribute name for an element’s attribute
3091+
* in the element’s namespace, usually for display or XML output only.
3092+
*
3093+
* For checking which attributes are present on a given tag, rely on
3094+
* {@see static::get_attribute_names_with_prefix()} which takes into account
3095+
* HTML case-folding rules and always returns an ASCII lower-case variant of
3096+
* the attribute names.
3097+
*
3098+
* - Some SVG attribute names are mixed-case, such as `baseFrequency`.
3099+
* - The `definitionURL` in SVG and MathML is mixed-case.
3100+
* - Some HTML attributes which have special meaning in XML are separated
3101+
* into a namespace and local name, such as `xml:lang` turning into `xml lang`.
30533102
*
30543103
* @since 6.7.0
30553104
*
@@ -3074,12 +3123,14 @@ public function get_qualified_attribute_name( $attribute_name ): ?string {
30743123
case 'attributename':
30753124
return 'attributeName';
30763125

3126+
// @todo Is this right?
30773127
case 'attributetype':
30783128
return 'attributeType';
30793129

30803130
case 'basefrequency':
30813131
return 'baseFrequency';
30823132

3133+
// @todo Is this right?
30833134
case 'baseprofile':
30843135
return 'baseProfile';
30853136

@@ -3191,6 +3242,7 @@ public function get_qualified_attribute_name( $attribute_name ): ?string {
31913242
case 'requiredextensions':
31923243
return 'requiredExtensions';
31933244

3245+
// @todo Is this right?
31943246
case 'requiredfeatures':
31953247
return 'requiredFeatures';
31963248

0 commit comments

Comments
 (0)