Skip to content

Commit db30ce9

Browse files
committed
HTML API: Add missing tags in IN BODY insertion mode to HTML Processor.
As part of work to add more spec support to the HTML API, this patch adds support for the remaining missing tags in the IN BODY insertion mode. Not all of the added tags are supported, because in some cases they reset the insertion mode and are reprocessed where they will be rejected. This patch also improves the support of `get_modifiable_text()`, removing a leading newline inside a LISTING, PRE, or TEXTAREA element. Developed in #6972 Discussed in https://core.trac.wordpress.org/ticket/61576 Props dmsnell, jonsurrell, westonruter. See #61576. git-svn-id: https://develop.svn.wordpress.org/trunk@58779 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 485fdca commit db30ce9

14 files changed

Lines changed: 1214 additions & 530 deletions

src/wp-includes/html-api/class-wp-html-active-formatting-elements.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,22 @@ public function current_node() {
8686
return $current_node ? $current_node : null;
8787
}
8888

89+
/**
90+
* Inserts a "marker" at the end of the list of active formatting elements.
91+
*
92+
* > The markers are inserted when entering applet, object, marquee,
93+
* > template, td, th, and caption elements, and are used to prevent
94+
* > formatting from "leaking" into applet, object, marquee, template,
95+
* > td, th, and caption elements.
96+
*
97+
* @see https://html.spec.whatwg.org/#concept-parser-marker
98+
*
99+
* @since 6.7.0
100+
*/
101+
public function insert_marker(): void {
102+
$this->push( new WP_HTML_Token( null, 'marker', false ) );
103+
}
104+
89105
/**
90106
* Pushes a node onto the stack of active formatting elements.
91107
*
@@ -184,4 +200,30 @@ public function walk_up() {
184200
yield $this->stack[ $i ];
185201
}
186202
}
203+
204+
/**
205+
* Clears the list of active formatting elements up to the last marker.
206+
*
207+
* > When the steps below require the UA to clear the list of active formatting elements up to
208+
* > the last marker, the UA must perform the following steps:
209+
* >
210+
* > 1. Let entry be the last (most recently added) entry in the list of active
211+
* > formatting elements.
212+
* > 2. Remove entry from the list of active formatting elements.
213+
* > 3. If entry was a marker, then stop the algorithm at this point.
214+
* > The list has been cleared up to the last marker.
215+
* > 4. Go to step 1.
216+
*
217+
* @see https://html.spec.whatwg.org/multipage/parsing.html#clear-the-list-of-active-formatting-elements-up-to-the-last-marker
218+
*
219+
* @since 6.7.0
220+
*/
221+
public function clear_up_to_last_marker(): void {
222+
foreach ( $this->walk_up() as $item ) {
223+
array_pop( $this->stack );
224+
if ( 'marker' === $item->node_name ) {
225+
break;
226+
}
227+
}
228+
}
187229
}

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

Lines changed: 157 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,49 @@ public function set_push_handler( Closure $handler ): void {
101101
$this->push_handler = $handler;
102102
}
103103

104+
/**
105+
* Returns the name of the node at the nth position on the stack
106+
* of open elements, or `null` if no such position exists.
107+
*
108+
* Note that this uses a 1-based index, which represents the
109+
* "nth item" on the stack, counting from the top, where the
110+
* top-most element is the 1st, the second is the 2nd, etc...
111+
*
112+
* @since 6.7.0
113+
*
114+
* @param int $nth Retrieve the nth item on the stack, with 1 being
115+
* 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.
118+
*/
119+
public function at( int $nth ): ?string {
120+
foreach ( $this->walk_down() as $item ) {
121+
if ( 0 === --$nth ) {
122+
return $item->node_name;
123+
}
124+
}
125+
126+
return null;
127+
}
128+
129+
/**
130+
* Reports if a node of a given name is in the stack of open elements.
131+
*
132+
* @since 6.7.0
133+
*
134+
* @param string $node_name Name of node for which to check.
135+
* @return bool Whether a node of the given name is in the stack of open elements.
136+
*/
137+
public function contains( string $node_name ): bool {
138+
foreach ( $this->walk_up() as $item ) {
139+
if ( $node_name === $item->node_name ) {
140+
return true;
141+
}
142+
}
143+
144+
return false;
145+
}
146+
104147
/**
105148
* Reports if a specific node is in the stack of open elements.
106149
*
@@ -111,7 +154,7 @@ public function set_push_handler( Closure $handler ): void {
111154
*/
112155
public function contains_node( WP_HTML_Token $token ): bool {
113156
foreach ( $this->walk_up() as $item ) {
114-
if ( $token->bookmark_name === $item->bookmark_name ) {
157+
if ( $token === $item ) {
115158
return true;
116159
}
117160
}
@@ -210,11 +253,6 @@ public function has_element_in_specific_scope( string $tag_name, $termination_li
210253
return true;
211254
}
212255

213-
switch ( $node->node_name ) {
214-
case 'HTML':
215-
return false;
216-
}
217-
218256
if ( in_array( $node->node_name, $termination_list, true ) ) {
219257
return false;
220258
}
@@ -226,7 +264,31 @@ public function has_element_in_specific_scope( string $tag_name, $termination_li
226264
/**
227265
* Returns whether a particular element is in scope.
228266
*
267+
* > The stack of open elements is said to have a particular element in
268+
* > scope when it has that element in the specific scope consisting of
269+
* > the following element types:
270+
* >
271+
* > - applet
272+
* > - caption
273+
* > - html
274+
* > - table
275+
* > - td
276+
* > - th
277+
* > - marquee
278+
* > - object
279+
* > - template
280+
* > - MathML mi
281+
* > - MathML mo
282+
* > - MathML mn
283+
* > - MathML ms
284+
* > - MathML mtext
285+
* > - MathML annotation-xml
286+
* > - SVG foreignObject
287+
* > - SVG desc
288+
* > - SVG title
289+
*
229290
* @since 6.4.0
291+
* @since 6.7.0 Supports all required HTML elements.
230292
*
231293
* @see https://html.spec.whatwg.org/#has-an-element-in-scope
232294
*
@@ -237,23 +299,34 @@ public function has_element_in_scope( string $tag_name ): bool {
237299
return $this->has_element_in_specific_scope(
238300
$tag_name,
239301
array(
240-
241-
/*
242-
* Because it's not currently possible to encounter
243-
* one of the termination elements, they don't need
244-
* to be listed here. If they were, they would be
245-
* unreachable and only waste CPU cycles while
246-
* scanning through HTML.
247-
*/
302+
'APPLET',
303+
'CAPTION',
304+
'HTML',
305+
'TABLE',
306+
'TD',
307+
'TH',
308+
'MARQUEE',
309+
'OBJECT',
310+
'TEMPLATE',
311+
// @todo: Support SVG and MathML nodes when support for foreign content is added.
248312
)
249313
);
250314
}
251315

252316
/**
253317
* Returns whether a particular element is in list item scope.
254318
*
319+
* > The stack of open elements is said to have a particular element
320+
* > in list item scope when it has that element in the specific scope
321+
* > consisting of the following element types:
322+
* >
323+
* > - All the element types listed above for the has an element in scope algorithm.
324+
* > - ol in the HTML namespace
325+
* > - ul in the HTML namespace
326+
*
255327
* @since 6.4.0
256328
* @since 6.5.0 Implemented: no longer throws on every invocation.
329+
* @since 6.7.0 Supports all required HTML elements.
257330
*
258331
* @see https://html.spec.whatwg.org/#has-an-element-in-list-item-scope
259332
*
@@ -264,43 +337,88 @@ public function has_element_in_list_item_scope( string $tag_name ): bool {
264337
return $this->has_element_in_specific_scope(
265338
$tag_name,
266339
array(
267-
// There are more elements that belong here which aren't currently supported.
340+
'APPLET',
341+
'BUTTON',
342+
'CAPTION',
343+
'HTML',
344+
'TABLE',
345+
'TD',
346+
'TH',
347+
'MARQUEE',
348+
'OBJECT',
268349
'OL',
350+
'TEMPLATE',
269351
'UL',
352+
// @todo: Support SVG and MathML nodes when support for foreign content is added.
270353
)
271354
);
272355
}
273356

274357
/**
275358
* Returns whether a particular element is in button scope.
276359
*
360+
* > The stack of open elements is said to have a particular element
361+
* > in button scope when it has that element in the specific scope
362+
* > consisting of the following element types:
363+
* >
364+
* > - All the element types listed above for the has an element in scope algorithm.
365+
* > - button in the HTML namespace
366+
*
277367
* @since 6.4.0
368+
* @since 6.7.0 Supports all required HTML elements.
278369
*
279370
* @see https://html.spec.whatwg.org/#has-an-element-in-button-scope
280371
*
281372
* @param string $tag_name Name of tag to check.
282373
* @return bool Whether given element is in scope.
283374
*/
284375
public function has_element_in_button_scope( string $tag_name ): bool {
285-
return $this->has_element_in_specific_scope( $tag_name, array( 'BUTTON' ) );
376+
return $this->has_element_in_specific_scope(
377+
$tag_name,
378+
array(
379+
'APPLET',
380+
'BUTTON',
381+
'CAPTION',
382+
'HTML',
383+
'TABLE',
384+
'TD',
385+
'TH',
386+
'MARQUEE',
387+
'OBJECT',
388+
'TEMPLATE',
389+
// @todo: Support SVG and MathML nodes when support for foreign content is added.
390+
)
391+
);
286392
}
287393

288394
/**
289395
* Returns whether a particular element is in table scope.
290396
*
397+
* > The stack of open elements is said to have a particular element
398+
* > in table scope when it has that element in the specific scope
399+
* > consisting of the following element types:
400+
* >
401+
* > - html in the HTML namespace
402+
* > - table in the HTML namespace
403+
* > - template in the HTML namespace
404+
*
291405
* @since 6.4.0
406+
* @since 6.7.0 Full implementation.
292407
*
293408
* @see https://html.spec.whatwg.org/#has-an-element-in-table-scope
294409
*
295-
* @throws WP_HTML_Unsupported_Exception Always until this function is implemented.
296-
*
297410
* @param string $tag_name Name of tag to check.
298411
* @return bool Whether given element is in scope.
299412
*/
300413
public function has_element_in_table_scope( string $tag_name ): bool {
301-
throw new WP_HTML_Unsupported_Exception( 'Cannot process elements depending on table scope.' );
302-
303-
return false; // The linter requires this unreachable code until the function is implemented and can return.
414+
return $this->has_element_in_specific_scope(
415+
$tag_name,
416+
array(
417+
'HTML',
418+
'TABLE',
419+
'TEMPLATE',
420+
)
421+
);
304422
}
305423

306424
/**
@@ -540,7 +658,16 @@ public function after_element_push( WP_HTML_Token $item ): void {
540658
* cases where the precalculated value needs to change.
541659
*/
542660
switch ( $item->node_name ) {
661+
case 'APPLET':
543662
case 'BUTTON':
663+
case 'CAPTION':
664+
case 'HTML':
665+
case 'TABLE':
666+
case 'TD':
667+
case 'TH':
668+
case 'MARQUEE':
669+
case 'OBJECT':
670+
case 'TEMPLATE':
544671
$this->has_p_in_button_scope = false;
545672
break;
546673

@@ -573,11 +700,17 @@ public function after_element_pop( WP_HTML_Token $item ): void {
573700
* cases where the precalculated value needs to change.
574701
*/
575702
switch ( $item->node_name ) {
703+
case 'APPLET':
576704
case 'BUTTON':
577-
$this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' );
578-
break;
579-
705+
case 'CAPTION':
706+
case 'HTML':
580707
case 'P':
708+
case 'TABLE':
709+
case 'TD':
710+
case 'TH':
711+
case 'MARQUEE':
712+
case 'OBJECT':
713+
case 'TEMPLATE':
581714
$this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' );
582715
break;
583716
}

0 commit comments

Comments
 (0)