Skip to content

Commit f5d1c7d

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 4de4b72 commit f5d1c7d

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
@@ -2901,10 +2901,39 @@ public function get_tag(): ?string {
29012901
}
29022902

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

2917-
if ( 'html' === $this->get_namespace() ) {
2918-
return $tag_name;
2946+
$lower_tag_name = strtolower( $tag_name );
2947+
$namespace = $this->get_namespace();
2948+
2949+
switch ( $namespace ) {
2950+
case 'html':
2951+
// Preserve casing of custom elements.
2952+
return str_contains( $tag_name, '-' )
2953+
? substr( $this->html, $this->tag_name_starts_at, $this->tag_name_length )
2954+
: $lower_tag_name;
2955+
2956+
case 'math':
2957+
return $lower_tag_name;
29192958
}
29202959

2921-
$lower_tag_name = strtolower( $tag_name );
2922-
if ( 'math' === $this->get_namespace() ) {
2960+
if ( 'svg' !== $namespace ) {
2961+
// This should be unreachable, but prevents tools from inaccurately reporting type errors.
29232962
return $lower_tag_name;
29242963
}
29252964

2926-
if ( 'svg' === $this->get_namespace() ) {
2927-
switch ( $lower_tag_name ) {
2928-
case 'altglyph':
2929-
return 'altGlyph';
2965+
switch ( $lower_tag_name ) {
2966+
case 'altglyph':
2967+
return 'altGlyph';
29302968

2931-
case 'altglyphdef':
2932-
return 'altGlyphDef';
2969+
case 'altglyphdef':
2970+
return 'altGlyphDef';
29332971

2934-
case 'altglyphitem':
2935-
return 'altGlyphItem';
2972+
case 'altglyphitem':
2973+
return 'altGlyphItem';
29362974

2937-
case 'animatecolor':
2938-
return 'animateColor';
2975+
case 'animatecolor':
2976+
return 'animateColor';
29392977

2940-
case 'animatemotion':
2941-
return 'animateMotion';
2978+
case 'animatemotion':
2979+
return 'animateMotion';
29422980

2943-
case 'animatetransform':
2944-
return 'animateTransform';
2981+
case 'animatetransform':
2982+
return 'animateTransform';
29452983

2946-
case 'clippath':
2947-
return 'clipPath';
2984+
case 'clippath':
2985+
return 'clipPath';
29482986

2949-
case 'feblend':
2950-
return 'feBlend';
2987+
case 'feblend':
2988+
return 'feBlend';
29512989

2952-
case 'fecolormatrix':
2953-
return 'feColorMatrix';
2990+
case 'fecolormatrix':
2991+
return 'feColorMatrix';
29542992

2955-
case 'fecomponenttransfer':
2956-
return 'feComponentTransfer';
2993+
case 'fecomponenttransfer':
2994+
return 'feComponentTransfer';
29572995

2958-
case 'fecomposite':
2959-
return 'feComposite';
2996+
case 'fecomposite':
2997+
return 'feComposite';
29602998

2961-
case 'feconvolvematrix':
2962-
return 'feConvolveMatrix';
2999+
case 'feconvolvematrix':
3000+
return 'feConvolveMatrix';
29633001

2964-
case 'fediffuselighting':
2965-
return 'feDiffuseLighting';
3002+
case 'fediffuselighting':
3003+
return 'feDiffuseLighting';
29663004

2967-
case 'fedisplacementmap':
2968-
return 'feDisplacementMap';
3005+
case 'fedisplacementmap':
3006+
return 'feDisplacementMap';
29693007

2970-
case 'fedistantlight':
2971-
return 'feDistantLight';
3008+
case 'fedistantlight':
3009+
return 'feDistantLight';
29723010

2973-
case 'fedropshadow':
2974-
return 'feDropShadow';
3011+
case 'fedropshadow':
3012+
return 'feDropShadow';
29753013

2976-
case 'feflood':
2977-
return 'feFlood';
3014+
case 'feflood':
3015+
return 'feFlood';
29783016

2979-
case 'fefunca':
2980-
return 'feFuncA';
3017+
case 'fefunca':
3018+
return 'feFuncA';
29813019

2982-
case 'fefuncb':
2983-
return 'feFuncB';
3020+
case 'fefuncb':
3021+
return 'feFuncB';
29843022

2985-
case 'fefuncg':
2986-
return 'feFuncG';
3023+
case 'fefuncg':
3024+
return 'feFuncG';
29873025

2988-
case 'fefuncr':
2989-
return 'feFuncR';
3026+
case 'fefuncr':
3027+
return 'feFuncR';
29903028

2991-
case 'fegaussianblur':
2992-
return 'feGaussianBlur';
3029+
case 'fegaussianblur':
3030+
return 'feGaussianBlur';
29933031

2994-
case 'feimage':
2995-
return 'feImage';
3032+
case 'feimage':
3033+
return 'feImage';
29963034

2997-
case 'femerge':
2998-
return 'feMerge';
3035+
case 'femerge':
3036+
return 'feMerge';
29993037

3000-
case 'femergenode':
3001-
return 'feMergeNode';
3038+
case 'femergenode':
3039+
return 'feMergeNode';
30023040

3003-
case 'femorphology':
3004-
return 'feMorphology';
3041+
case 'femorphology':
3042+
return 'feMorphology';
30053043

3006-
case 'feoffset':
3007-
return 'feOffset';
3044+
case 'feoffset':
3045+
return 'feOffset';
30083046

3009-
case 'fepointlight':
3010-
return 'fePointLight';
3047+
case 'fepointlight':
3048+
return 'fePointLight';
30113049

3012-
case 'fespecularlighting':
3013-
return 'feSpecularLighting';
3050+
case 'fespecularlighting':
3051+
return 'feSpecularLighting';
30143052

3015-
case 'fespotlight':
3016-
return 'feSpotLight';
3053+
case 'fespotlight':
3054+
return 'feSpotLight';
30173055

3018-
case 'fetile':
3019-
return 'feTile';
3056+
case 'fetile':
3057+
return 'feTile';
30203058

3021-
case 'feturbulence':
3022-
return 'feTurbulence';
3059+
case 'feturbulence':
3060+
return 'feTurbulence';
30233061

3024-
case 'foreignobject':
3025-
return 'foreignObject';
3062+
case 'foreignobject':
3063+
return 'foreignObject';
30263064

3027-
case 'glyphref':
3028-
return 'glyphRef';
3065+
case 'glyphref':
3066+
return 'glyphRef';
30293067

3030-
case 'lineargradient':
3031-
return 'linearGradient';
3068+
case 'lineargradient':
3069+
return 'linearGradient';
30323070

3033-
case 'radialgradient':
3034-
return 'radialGradient';
3071+
case 'radialgradient':
3072+
return 'radialGradient';
30353073

3036-
case 'textpath':
3037-
return 'textPath';
3074+
case 'solidcolor':
3075+
return 'solidColor';
30383076

3039-
default:
3040-
return $lower_tag_name;
3041-
}
3077+
case 'textarea':
3078+
return 'textArea';
3079+
3080+
case 'textpath':
3081+
return 'textPath';
30423082
}
30433083

3044-
// This unnecessary return prevents tools from inaccurately reporting type errors.
3045-
return $tag_name;
3084+
return $lower_tag_name;
30463085
}
30473086

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

3124+
// @todo Is this right?
30753125
case 'attributetype':
30763126
return 'attributeType';
30773127

30783128
case 'basefrequency':
30793129
return 'baseFrequency';
30803130

3131+
// @todo Is this right?
30813132
case 'baseprofile':
30823133
return 'baseProfile';
30833134

@@ -3189,6 +3240,7 @@ public function get_qualified_attribute_name( $attribute_name ): ?string {
31893240
case 'requiredextensions':
31903241
return 'requiredExtensions';
31913242

3243+
// @todo Is this right?
31923244
case 'requiredfeatures':
31933245
return 'requiredFeatures';
31943246

0 commit comments

Comments
 (0)