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