From efc64c5c00d1fe62f0e4bbba813c9f77403c2d3b Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Wed, 26 Jun 2024 16:30:58 -0700 Subject: [PATCH 1/3] HTML API: Report breadcrumbs properly when paused on virtual nodes. --- .../html-api/class-wp-html-open-elements.php | 14 +++++- .../html-api/class-wp-html-processor.php | 46 +++++++++++++++++-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/html-api/class-wp-html-open-elements.php b/src/wp-includes/html-api/class-wp-html-open-elements.php index 7f148a7af706..15479801dda1 100644 --- a/src/wp-includes/html-api/class-wp-html-open-elements.php +++ b/src/wp-includes/html-api/class-wp-html-open-elements.php @@ -308,11 +308,15 @@ public function has_p_in_button_scope() { */ public function pop() { $item = array_pop( $this->stack ); - if ( null === $item ) { return false; } + if ( 'context-node' === $item->bookmark_name ) { + $this->stack[] = $item; + return false; + } + $this->after_element_pop( $item ); return true; } @@ -329,6 +333,10 @@ public function pop() { */ public function pop_until( $tag_name ) { foreach ( $this->walk_up() as $item ) { + if ( 'context-node' === $item->bookmark_name ) { + return true; + } + $this->pop(); if ( @@ -369,6 +377,10 @@ public function push( $stack_item ) { * @return bool Whether the node was found and removed from the stack of open elements. */ public function remove_node( $token ) { + if ( 'context-node' === $token->bookmark_name ) { + return false; + } + foreach ( $this->walk_up() as $position_from_end => $item ) { if ( $token->bookmark_name !== $item->bookmark_name ) { continue; diff --git a/src/wp-includes/html-api/class-wp-html-processor.php b/src/wp-includes/html-api/class-wp-html-processor.php index 0eb597edcd80..9cef8cf23d8f 100644 --- a/src/wp-includes/html-api/class-wp-html-processor.php +++ b/src/wp-includes/html-api/class-wp-html-processor.php @@ -519,10 +519,12 @@ public function next_token() { return false; } - if ( 0 === count( $this->element_queue ) && ! $this->step() ) { - while ( $this->state->stack_of_open_elements->pop() ) { + if ( 'done' !== $this->has_seen_context_node && 0 === count( $this->element_queue ) && ! $this->step() ) { + while ( 'context-node' !== $this->state->stack_of_open_elements->current_node()->bookmark_name && $this->state->stack_of_open_elements->pop() ) { continue; } + $this->has_seen_context_node = 'done'; + return $this->next_token(); } $this->current_element = array_shift( $this->element_queue ); @@ -537,7 +539,11 @@ public function next_token() { } if ( ! isset( $this->current_element ) ) { - return $this->next_token(); + if ( 'done' === $this->has_seen_context_node ) { + return false; + } else { + return $this->next_token(); + } } if ( isset( $this->context_node ) && WP_HTML_Stack_Event::POP === $this->current_element->operation && $this->context_node === $this->current_element->token ) { @@ -788,10 +794,44 @@ public function step( $node_to_process = self::PROCESS_NEXT_NODE ) { */ public function get_breadcrumbs() { $breadcrumbs = array(); + foreach ( $this->state->stack_of_open_elements->walk_down() as $stack_item ) { $breadcrumbs[] = $stack_item->node_name; } + if ( ! $this->is_virtual() ) { + return $breadcrumbs; + } + + foreach ( $this->element_queue as $queue_item ) { + if ( $this->current_element->token->bookmark_name === $queue_item->token->bookmark_name ) { + break; + } + + if ( 'context-node' === $queue_item->token->bookmark_name ) { + break; + } + + if ( 'real' === $queue_item->provenance ) { + break; + } + + if ( WP_HTML_Stack_Event::PUSH === $queue_item->operation ) { + $breadcrumbs[] = $queue_item->token->node_name; + } else { + array_pop( $breadcrumbs ); + } + } + + if ( null !== parent::get_token_name() && ! parent::is_tag_closer() ) { + array_pop( $breadcrumbs ); + } + + // Add the virtual node we're at. + if ( WP_HTML_Stack_Event::PUSH === $this->current_element->operation ) { + $breadcrumbs[] = $this->current_element->token->node_name; + } + return $breadcrumbs; } From 21bc01b956ded3bef4ed0df0371ba376bc6c1a8f Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 27 Jun 2024 09:27:50 -0700 Subject: [PATCH 2/3] Update get_current_depth() to use breadcrumbs instead of stateful stack. --- src/wp-includes/html-api/class-wp-html-processor.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wp-includes/html-api/class-wp-html-processor.php b/src/wp-includes/html-api/class-wp-html-processor.php index 9cef8cf23d8f..984af1f5ba4b 100644 --- a/src/wp-includes/html-api/class-wp-html-processor.php +++ b/src/wp-includes/html-api/class-wp-html-processor.php @@ -788,8 +788,6 @@ public function step( $node_to_process = self::PROCESS_NEXT_NODE ) { * * @since 6.4.0 * - * @todo make aware of queue of elements, because stack operations have already been done by now. - * * @return string[]|null Array of tag names representing path to matched node, if matched, otherwise NULL. */ public function get_breadcrumbs() { @@ -861,7 +859,7 @@ public function get_breadcrumbs() { * @return int Nesting-depth of current location in the document. */ public function get_current_depth() { - return $this->state->stack_of_open_elements->count(); + return count( $this->get_breadcrumbs() ); } /** From 5bc7d18ebc153e47d9ca85d4c8f4cd6a0b1c766b Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 27 Jun 2024 11:11:17 -0700 Subject: [PATCH 3/3] Optimize depth lookup for real nodes. --- src/wp-includes/html-api/class-wp-html-processor.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/html-api/class-wp-html-processor.php b/src/wp-includes/html-api/class-wp-html-processor.php index 984af1f5ba4b..29f1c7ac6d4c 100644 --- a/src/wp-includes/html-api/class-wp-html-processor.php +++ b/src/wp-includes/html-api/class-wp-html-processor.php @@ -859,7 +859,9 @@ public function get_breadcrumbs() { * @return int Nesting-depth of current location in the document. */ public function get_current_depth() { - return count( $this->get_breadcrumbs() ); + return $this->is_virtual() + ? count( $this->get_breadcrumbs() ) + : $this->state->stack_of_open_elements->count(); } /**