Interactivity API: Do not propagate context from void tags to its siblings.

Resolves an issue where context on a void tag element such as `<img>` was incorrectly passed to following elements.
Adds tests.

Reviewed by gziolo.
Merges [57832] to the to the 6.5 branch.

Props santosguillamot, luisherranz, cbravobernal, dmsnell, gziolo, swissspidy.
Fixes #60768.
Built from https://develop.svn.wordpress.org/branches/6.5@57834


git-svn-id: http://core.svn.wordpress.org/branches/6.5@57335 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Pascal Birchler 2024-03-14 15:12:10 +00:00
parent 4958667689
commit 9eed18532d
2 changed files with 57 additions and 41 deletions

View File

@ -294,34 +294,42 @@ final class WP_Interactivity_API {
}
}
/*
* If the matching opener tag didn't have any directives, it can skip the
* processing.
*/
* If the matching opener tag didn't have any directives, it can skip the
* processing.
*/
if ( 0 === count( $directives_prefixes ) ) {
continue;
}
/*
* Sorts the attributes by the order of the `directives_processor` array
* and checks what directives are present in this element. The processing
* order is reversed for tag closers.
*/
$directives_prefixes = array_intersect(
$p->is_tag_closer()
? $directive_processor_prefixes_reversed
: $directive_processor_prefixes,
$directives_prefixes
// Directive processing might be different depending on if it is entering the tag or exiting it.
$modes = array(
'enter' => ! $p->is_tag_closer(),
'exit' => $p->is_tag_closer() || ! $p->has_and_visits_its_closer_tag(),
);
// Executes the directive processors present in this element.
foreach ( $directives_prefixes as $directive_prefix ) {
$func = is_array( self::$directive_processors[ $directive_prefix ] )
? self::$directive_processors[ $directive_prefix ]
: array( $this, self::$directive_processors[ $directive_prefix ] );
call_user_func_array(
$func,
array( $p, &$context_stack, &$namespace_stack, &$tag_stack )
foreach ( $modes as $mode => $should_run ) {
if ( ! $should_run ) {
continue;
}
/*
* Sorts the attributes by the order of the `directives_processor` array
* and checks what directives are present in this element.
*/
$existing_directives_prefixes = array_intersect(
'enter' === $mode ? $directive_processor_prefixes : $directive_processor_prefixes_reversed,
$directives_prefixes
);
foreach ( $existing_directives_prefixes as $directive_prefix ) {
$func = is_array( self::$directive_processors[ $directive_prefix ] )
? self::$directive_processors[ $directive_prefix ]
: array( $this, self::$directive_processors[ $directive_prefix ] );
call_user_func_array(
$func,
array( $p, $mode, &$context_stack, &$namespace_stack, &$tag_stack )
);
}
}
}
@ -474,12 +482,13 @@ final class WP_Interactivity_API {
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_interactive_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
// In closing tags, it removes the last namespace from the stack.
if ( $p->is_tag_closer() ) {
private function data_wp_interactive_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
// When exiting tags, it removes the last namespace from the stack.
if ( 'exit' === $mode ) {
array_pop( $namespace_stack );
return;
}
@ -518,12 +527,13 @@ final class WP_Interactivity_API {
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_context_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
// In closing tags, it removes the last context from the stack.
if ( $p->is_tag_closer() ) {
private function data_wp_context_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
// When exiting tags, it removes the last context from the stack.
if ( 'exit' === $mode ) {
array_pop( $context_stack );
return;
}
@ -564,11 +574,12 @@ final class WP_Interactivity_API {
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_bind_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) {
private function data_wp_bind_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( 'enter' === $mode ) {
$all_bind_directives = $p->get_attribute_names_with_prefix( 'data-wp-bind--' );
foreach ( $all_bind_directives as $attribute_name ) {
@ -608,11 +619,12 @@ final class WP_Interactivity_API {
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_class_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) {
private function data_wp_class_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( 'enter' === $mode ) {
$all_class_directives = $p->get_attribute_names_with_prefix( 'data-wp-class--' );
foreach ( $all_class_directives as $attribute_name ) {
@ -642,11 +654,12 @@ final class WP_Interactivity_API {
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_style_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) {
private function data_wp_style_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( 'enter' === $mode ) {
$all_style_attributes = $p->get_attribute_names_with_prefix( 'data-wp-style--' );
foreach ( $all_style_attributes as $attribute_name ) {
@ -734,11 +747,12 @@ final class WP_Interactivity_API {
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_text_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) {
private function data_wp_text_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( 'enter' === $mode ) {
$attribute_value = $p->get_attribute( 'data-wp-text' );
$result = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) );
@ -831,10 +845,11 @@ HTML;
*
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
*/
private function data_wp_router_region_processor( WP_Interactivity_API_Directives_Processor $p ) {
if ( ! $p->is_tag_closer() && ! $this->has_processed_router_region ) {
private function data_wp_router_region_processor( WP_Interactivity_API_Directives_Processor $p, string $mode ) {
if ( 'enter' === $mode && ! $this->has_processed_router_region ) {
$this->has_processed_router_region = true;
// Initialize the `core/router` store.
@ -870,12 +885,13 @@ HTML;
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
* @param array $tag_stack The reference to the tag stack.
*/
private function data_wp_each_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack, array &$tag_stack ) {
if ( ! $p->is_tag_closer() && 'TEMPLATE' === $p->get_tag() ) {
private function data_wp_each_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack, array &$tag_stack ) {
if ( 'enter' === $mode && 'TEMPLATE' === $p->get_tag() ) {
$attribute_name = $p->get_attribute_names_with_prefix( 'data-wp-each' )[0];
$extracted_suffix = $this->extract_prefix_and_suffix( $attribute_name );
$item_name = isset( $extracted_suffix[1] ) ? $this->kebab_to_camel_case( $extracted_suffix[1] ) : 'item';

View File

@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
$wp_version = '6.5-RC2-57830';
$wp_version = '6.5-RC2-57834';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.