Skip to content

Commit de084d7

Browse files
committed
HTML API: Add support for SVG and MathML (Foreign content)
As part of work to add more spec support to the HTML API, this patch adds support for SVG and MathML elements, or more generally, "foreign content." The rules in foreign content are a mix of XML and HTML parsing rules and introduce additional complexity into the processor, but is important in order to avoid getting lost when inside these elements. Developed in #6006 Discussed in https://core.trac.wordpress.org/ticket/61576 Props: dmsnell, jonsurrell, westonruter. See #61576. git-svn-id: https://develop.svn.wordpress.org/trunk@58867 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 4026237 commit de084d7

10 files changed

+1199
-431
lines changed

src/wp-includes/html-api/class-wp-html-open-elements.php

Lines changed: 65 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,13 @@ public function set_push_handler( Closure $handler ): void {
113113
*
114114
* @param int $nth Retrieve the nth item on the stack, with 1 being
115115
* the top element, 2 being the second, etc...
116-
* @return string|null Name of the node on the stack at the given location,
117-
* or `null` if the location isn't on the stack.
116+
* @return WP_HTML_Token|null Name of the node on the stack at the given location,
117+
* or `null` if the location isn't on the stack.
118118
*/
119-
public function at( int $nth ): ?string {
119+
public function at( int $nth ): ?WP_HTML_Token {
120120
foreach ( $this->walk_down() as $item ) {
121121
if ( 0 === --$nth ) {
122-
return $item->node_name;
122+
return $item;
123123
}
124124
}
125125

@@ -242,18 +242,22 @@ public function current_node_is( string $identity ): bool {
242242
*/
243243
public function has_element_in_specific_scope( string $tag_name, $termination_list ): bool {
244244
foreach ( $this->walk_up() as $node ) {
245-
if ( $node->node_name === $tag_name ) {
245+
$namespaced_name = 'html' === $node->namespace
246+
? $node->node_name
247+
: "{$node->namespace} {$node->node_name}";
248+
249+
if ( $namespaced_name === $tag_name ) {
246250
return true;
247251
}
248252

249253
if (
250254
'(internal: H1 through H6 - do not use)' === $tag_name &&
251-
in_array( $node->node_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true )
255+
in_array( $namespaced_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true )
252256
) {
253257
return true;
254258
}
255259

256-
if ( in_array( $node->node_name, $termination_list, true ) ) {
260+
if ( in_array( $namespaced_name, $termination_list, true ) ) {
257261
return false;
258262
}
259263
}
@@ -288,7 +292,7 @@ public function has_element_in_specific_scope( string $tag_name, $termination_li
288292
* > - SVG title
289293
*
290294
* @since 6.4.0
291-
* @since 6.7.0 Supports all required HTML elements.
295+
* @since 6.7.0 Full support.
292296
*
293297
* @see https://html.spec.whatwg.org/#has-an-element-in-scope
294298
*
@@ -309,19 +313,16 @@ public function has_element_in_scope( string $tag_name ): bool {
309313
'OBJECT',
310314
'TEMPLATE',
311315

312-
/*
313-
* @todo Support SVG and MathML nodes when support for foreign content is added.
314-
*
315-
* - MathML mi
316-
* - MathML mo
317-
* - MathML mn
318-
* - MathML ms
319-
* - MathML mtext
320-
* - MathML annotation-xml
321-
* - SVG foreignObject
322-
* - SVG desc
323-
* - SVG title
324-
*/
316+
'math MI',
317+
'math MO',
318+
'math MN',
319+
'math MS',
320+
'math MTEXT',
321+
'math ANNOTATION-XML',
322+
323+
'svg FOREIGNOBJECT',
324+
'svg DESC',
325+
'svg TITLE',
325326
)
326327
);
327328
}
@@ -363,19 +364,16 @@ public function has_element_in_list_item_scope( string $tag_name ): bool {
363364
'TEMPLATE',
364365
'UL',
365366

366-
/*
367-
* @todo Support SVG and MathML nodes when support for foreign content is added.
368-
*
369-
* - MathML mi
370-
* - MathML mo
371-
* - MathML mn
372-
* - MathML ms
373-
* - MathML mtext
374-
* - MathML annotation-xml
375-
* - SVG foreignObject
376-
* - SVG desc
377-
* - SVG title
378-
*/
367+
'math MI',
368+
'math MO',
369+
'math MN',
370+
'math MS',
371+
'math MTEXT',
372+
'math ANNOTATION-XML',
373+
374+
'svg FOREIGNOBJECT',
375+
'svg DESC',
376+
'svg TITLE',
379377
)
380378
);
381379
}
@@ -413,19 +411,16 @@ public function has_element_in_button_scope( string $tag_name ): bool {
413411
'OBJECT',
414412
'TEMPLATE',
415413

416-
/*
417-
* @todo Support SVG and MathML nodes when support for foreign content is added.
418-
*
419-
* - MathML mi
420-
* - MathML mo
421-
* - MathML mn
422-
* - MathML ms
423-
* - MathML mtext
424-
* - MathML annotation-xml
425-
* - SVG foreignObject
426-
* - SVG desc
427-
* - SVG title
428-
*/
414+
'math MI',
415+
'math MO',
416+
'math MN',
417+
'math MS',
418+
'math MTEXT',
419+
'math ANNOTATION-XML',
420+
421+
'svg FOREIGNOBJECT',
422+
'svg DESC',
423+
'svg TITLE',
429424
)
430425
);
431426
}
@@ -692,11 +687,15 @@ public function walk_up( ?WP_HTML_Token $above_this_node = null ) {
692687
* @param WP_HTML_Token $item Element that was added to the stack of open elements.
693688
*/
694689
public function after_element_push( WP_HTML_Token $item ): void {
690+
$namespaced_name = 'html' === $item->namespace
691+
? $item->node_name
692+
: "{$item->namespace} {$item->node_name}";
693+
695694
/*
696695
* When adding support for new elements, expand this switch to trap
697696
* cases where the precalculated value needs to change.
698697
*/
699-
switch ( $item->node_name ) {
698+
switch ( $namespaced_name ) {
700699
case 'APPLET':
701700
case 'BUTTON':
702701
case 'CAPTION':
@@ -707,6 +706,15 @@ public function after_element_push( WP_HTML_Token $item ): void {
707706
case 'MARQUEE':
708707
case 'OBJECT':
709708
case 'TEMPLATE':
709+
case 'math MI':
710+
case 'math MO':
711+
case 'math MN':
712+
case 'math MS':
713+
case 'math MTEXT':
714+
case 'math ANNOTATION-XML':
715+
case 'svg FOREIGNOBJECT':
716+
case 'svg DESC':
717+
case 'svg TITLE':
710718
$this->has_p_in_button_scope = false;
711719
break;
712720

@@ -750,6 +758,15 @@ public function after_element_pop( WP_HTML_Token $item ): void {
750758
case 'MARQUEE':
751759
case 'OBJECT':
752760
case 'TEMPLATE':
761+
case 'math MI':
762+
case 'math MO':
763+
case 'math MN':
764+
case 'math MS':
765+
case 'math MTEXT':
766+
case 'math ANNOTATION-XML':
767+
case 'svg FOREIGNOBJECT':
768+
case 'svg DESC':
769+
case 'svg TITLE':
753770
$this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' );
754771
break;
755772
}

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

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -299,18 +299,6 @@ class WP_HTML_Processor_State {
299299
*/
300300
const INSERTION_MODE_AFTER_AFTER_FRAMESET = 'insertion-mode-after-after-frameset';
301301

302-
/**
303-
* In foreign content insertion mode for full HTML parser.
304-
*
305-
* @since 6.7.0
306-
*
307-
* @see https://html.spec.whatwg.org/#parsing-main-inforeign
308-
* @see WP_HTML_Processor_State::$insertion_mode
309-
*
310-
* @var string
311-
*/
312-
const INSERTION_MODE_IN_FOREIGN_CONTENT = 'insertion-mode-in-foreign-content';
313-
314302
/**
315303
* No-quirks mode document compatability mode.
316304
*

0 commit comments

Comments
 (0)