Skip to content

Commit ae47481

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 f7d9b1b commit ae47481

1 file changed

Lines changed: 141 additions & 89 deletions

File tree

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
@@ -2826,10 +2826,39 @@ public function get_tag(): ?string {
28262826
}
28272827

28282828
/**
2829-
* Returns the adjusted tag name for a given token, taking into
2830-
* account the current parsing context, whether HTML, SVG, or MathML.
2829+
* Returns the normative adjusted tag name for an element in its given
2830+
* namespace, usually for display or XML output only.
2831+
*
2832+
* For checking if a tag has a given name, rely on {@see static::get_tag()}
2833+
* which takes HTML case-folding rules into account and always returns an
2834+
* ASCII upper-case variant of the matched tag name.
2835+
*
2836+
* - Some SVG tags are mixed-case, such as `altGlyph`.
2837+
* - HTML custom elements’ casing is preserved.
2838+
* - All other tag names will be lower-cased.
2839+
*
2840+
* Example:
2841+
*
2842+
* $processor = new WP_HTML_Tag_Processor( '<div><Task-Item>' );
2843+
* $processor->next_tag();
2844+
* 'DIV' === $processor->get_tag();
2845+
* 'div' === $processor->get_qualified_tag_name();
2846+
*
2847+
* $processor->next_tag();
2848+
* 'TASK-ITEM' === $processor->get_tag();
2849+
* 'Task-Item' === $processor->get_qualified_tag_name();
2850+
*
2851+
* $processor = WP_HTML_Processor::create_fragment( '<svg><altglyph/><rect/></svg>' );
2852+
* $processor->next_tag( 'altglyph' );
2853+
* 'ALTGLYPH' === $processor->get_tag();
2854+
* 'altGlyph' === $processor->get_qualified_tag_name();
2855+
*
2856+
* $processor->next_tag();
2857+
* 'RECT' === $processor->get_tag();
2858+
* 'rect' === $processor->get_qualified_tag_name();
28312859
*
28322860
* @since 6.7.0
2861+
* @since 6.9.0 Respects mixed-case SVG and case-sensitive custome element tag names.
28332862
*
28342863
* @return string|null Name of current tag name.
28352864
*/
@@ -2839,140 +2868,160 @@ public function get_qualified_tag_name(): ?string {
28392868
return null;
28402869
}
28412870

2842-
if ( 'html' === $this->get_namespace() ) {
2843-
return $tag_name;
2871+
$lower_tag_name = strtolower( $tag_name );
2872+
$namespace = $this->get_namespace();
2873+
2874+
switch ( $namespace ) {
2875+
case 'html':
2876+
// Preserve casing of custom elements.
2877+
return str_contains( $tag_name, '-' )
2878+
? substr( $this->html, $this->tag_name_starts_at, $this->tag_name_length )
2879+
: $lower_tag_name;
2880+
2881+
case 'math':
2882+
return $lower_tag_name;
28442883
}
28452884

2846-
$lower_tag_name = strtolower( $tag_name );
2847-
if ( 'math' === $this->get_namespace() ) {
2885+
if ( 'svg' !== $namespace ) {
2886+
// This should be unreachable, but prevents tools from inaccurately reporting type errors.
28482887
return $lower_tag_name;
28492888
}
28502889

2851-
if ( 'svg' === $this->get_namespace() ) {
2852-
switch ( $lower_tag_name ) {
2853-
case 'altglyph':
2854-
return 'altGlyph';
2890+
switch ( $lower_tag_name ) {
2891+
case 'altglyph':
2892+
return 'altGlyph';
28552893

2856-
case 'altglyphdef':
2857-
return 'altGlyphDef';
2894+
case 'altglyphdef':
2895+
return 'altGlyphDef';
28582896

2859-
case 'altglyphitem':
2860-
return 'altGlyphItem';
2897+
case 'altglyphitem':
2898+
return 'altGlyphItem';
28612899

2862-
case 'animatecolor':
2863-
return 'animateColor';
2900+
case 'animatecolor':
2901+
return 'animateColor';
28642902

2865-
case 'animatemotion':
2866-
return 'animateMotion';
2903+
case 'animatemotion':
2904+
return 'animateMotion';
28672905

2868-
case 'animatetransform':
2869-
return 'animateTransform';
2906+
case 'animatetransform':
2907+
return 'animateTransform';
28702908

2871-
case 'clippath':
2872-
return 'clipPath';
2909+
case 'clippath':
2910+
return 'clipPath';
28732911

2874-
case 'feblend':
2875-
return 'feBlend';
2912+
case 'feblend':
2913+
return 'feBlend';
28762914

2877-
case 'fecolormatrix':
2878-
return 'feColorMatrix';
2915+
case 'fecolormatrix':
2916+
return 'feColorMatrix';
28792917

2880-
case 'fecomponenttransfer':
2881-
return 'feComponentTransfer';
2918+
case 'fecomponenttransfer':
2919+
return 'feComponentTransfer';
28822920

2883-
case 'fecomposite':
2884-
return 'feComposite';
2921+
case 'fecomposite':
2922+
return 'feComposite';
28852923

2886-
case 'feconvolvematrix':
2887-
return 'feConvolveMatrix';
2924+
case 'feconvolvematrix':
2925+
return 'feConvolveMatrix';
28882926

2889-
case 'fediffuselighting':
2890-
return 'feDiffuseLighting';
2927+
case 'fediffuselighting':
2928+
return 'feDiffuseLighting';
28912929

2892-
case 'fedisplacementmap':
2893-
return 'feDisplacementMap';
2930+
case 'fedisplacementmap':
2931+
return 'feDisplacementMap';
28942932

2895-
case 'fedistantlight':
2896-
return 'feDistantLight';
2933+
case 'fedistantlight':
2934+
return 'feDistantLight';
28972935

2898-
case 'fedropshadow':
2899-
return 'feDropShadow';
2936+
case 'fedropshadow':
2937+
return 'feDropShadow';
29002938

2901-
case 'feflood':
2902-
return 'feFlood';
2939+
case 'feflood':
2940+
return 'feFlood';
29032941

2904-
case 'fefunca':
2905-
return 'feFuncA';
2942+
case 'fefunca':
2943+
return 'feFuncA';
29062944

2907-
case 'fefuncb':
2908-
return 'feFuncB';
2945+
case 'fefuncb':
2946+
return 'feFuncB';
29092947

2910-
case 'fefuncg':
2911-
return 'feFuncG';
2948+
case 'fefuncg':
2949+
return 'feFuncG';
29122950

2913-
case 'fefuncr':
2914-
return 'feFuncR';
2951+
case 'fefuncr':
2952+
return 'feFuncR';
29152953

2916-
case 'fegaussianblur':
2917-
return 'feGaussianBlur';
2954+
case 'fegaussianblur':
2955+
return 'feGaussianBlur';
29182956

2919-
case 'feimage':
2920-
return 'feImage';
2957+
case 'feimage':
2958+
return 'feImage';
29212959

2922-
case 'femerge':
2923-
return 'feMerge';
2960+
case 'femerge':
2961+
return 'feMerge';
29242962

2925-
case 'femergenode':
2926-
return 'feMergeNode';
2963+
case 'femergenode':
2964+
return 'feMergeNode';
29272965

2928-
case 'femorphology':
2929-
return 'feMorphology';
2966+
case 'femorphology':
2967+
return 'feMorphology';
29302968

2931-
case 'feoffset':
2932-
return 'feOffset';
2969+
case 'feoffset':
2970+
return 'feOffset';
29332971

2934-
case 'fepointlight':
2935-
return 'fePointLight';
2972+
case 'fepointlight':
2973+
return 'fePointLight';
29362974

2937-
case 'fespecularlighting':
2938-
return 'feSpecularLighting';
2975+
case 'fespecularlighting':
2976+
return 'feSpecularLighting';
29392977

2940-
case 'fespotlight':
2941-
return 'feSpotLight';
2978+
case 'fespotlight':
2979+
return 'feSpotLight';
29422980

2943-
case 'fetile':
2944-
return 'feTile';
2981+
case 'fetile':
2982+
return 'feTile';
29452983

2946-
case 'feturbulence':
2947-
return 'feTurbulence';
2984+
case 'feturbulence':
2985+
return 'feTurbulence';
29482986

2949-
case 'foreignobject':
2950-
return 'foreignObject';
2987+
case 'foreignobject':
2988+
return 'foreignObject';
29512989

2952-
case 'glyphref':
2953-
return 'glyphRef';
2990+
case 'glyphref':
2991+
return 'glyphRef';
29542992

2955-
case 'lineargradient':
2956-
return 'linearGradient';
2993+
case 'lineargradient':
2994+
return 'linearGradient';
29572995

2958-
case 'radialgradient':
2959-
return 'radialGradient';
2996+
case 'radialgradient':
2997+
return 'radialGradient';
29602998

2961-
case 'textpath':
2962-
return 'textPath';
2999+
case 'solidcolor':
3000+
return 'solidColor';
29633001

2964-
default:
2965-
return $lower_tag_name;
2966-
}
3002+
case 'textarea':
3003+
return 'textArea';
3004+
3005+
case 'textpath':
3006+
return 'textPath';
29673007
}
29683008

2969-
// This unnecessary return prevents tools from inaccurately reporting type errors.
2970-
return $tag_name;
3009+
return $lower_tag_name;
29713010
}
29723011

29733012
/**
2974-
* Returns the adjusted attribute name for a given attribute, taking into
2975-
* account the current parsing context, whether HTML, SVG, or MathML.
3013+
* Returns the normative adjusted attribute name for an element’s attribute
3014+
* in the element’s namespace, usually for display or XML output only.
3015+
*
3016+
* For checking which attributes are present on a given tag, rely on
3017+
* {@see static::get_attribute_names_with_prefix()} which takes into account
3018+
* HTML case-folding rules and always returns an ASCII lower-case variant of
3019+
* the attribute names.
3020+
*
3021+
* - Some SVG attribute names are mixed-case, such as `baseFrequency`.
3022+
* - The `definitionURL` in SVG and MathML is mixed-case.
3023+
* - Some HTML attributes which have special meaning in XML are separated
3024+
* into a namespace and local name, such as `xml:lang` turning into `xml lang`.
29763025
*
29773026
* @since 6.7.0
29783027
*
@@ -2997,12 +3046,14 @@ public function get_qualified_attribute_name( $attribute_name ): ?string {
29973046
case 'attributename':
29983047
return 'attributeName';
29993048

3049+
// @todo Is this right?
30003050
case 'attributetype':
30013051
return 'attributeType';
30023052

30033053
case 'basefrequency':
30043054
return 'baseFrequency';
30053055

3056+
// @todo Is this right?
30063057
case 'baseprofile':
30073058
return 'baseProfile';
30083059

@@ -3114,6 +3165,7 @@ public function get_qualified_attribute_name( $attribute_name ): ?string {
31143165
case 'requiredextensions':
31153166
return 'requiredExtensions';
31163167

3168+
// @todo Is this right?
31173169
case 'requiredfeatures':
31183170
return 'requiredFeatures';
31193171

0 commit comments

Comments
 (0)