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