@@ -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