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; }