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

@ -301,29 +301,37 @@ final class WP_Interactivity_API {
continue; continue;
} }
/* // Directive processing might be different depending on if it is entering the tag or exiting it.
* Sorts the attributes by the order of the `directives_processor` array $modes = array(
* and checks what directives are present in this element. The processing 'enter' => ! $p->is_tag_closer(),
* order is reversed for tag closers. 'exit' => $p->is_tag_closer() || ! $p->has_and_visits_its_closer_tag(),
*/
$directives_prefixes = array_intersect(
$p->is_tag_closer()
? $directive_processor_prefixes_reversed
: $directive_processor_prefixes,
$directives_prefixes
); );
// Executes the directive processors present in this element. foreach ( $modes as $mode => $should_run ) {
foreach ( $directives_prefixes as $directive_prefix ) { 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 ] ) $func = is_array( self::$directive_processors[ $directive_prefix ] )
? self::$directive_processors[ $directive_prefix ] ? self::$directive_processors[ $directive_prefix ]
: array( $this, self::$directive_processors[ $directive_prefix ] ); : array( $this, self::$directive_processors[ $directive_prefix ] );
call_user_func_array( call_user_func_array(
$func, $func,
array( $p, &$context_stack, &$namespace_stack, &$tag_stack ) array( $p, $mode, &$context_stack, &$namespace_stack, &$tag_stack )
); );
} }
} }
}
/* /*
* It returns null if the HTML is unbalanced because unbalanced HTML is * It returns null if the HTML is unbalanced because unbalanced HTML is
@ -474,12 +482,13 @@ final class WP_Interactivity_API {
* @since 6.5.0 * @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.
* @param array $context_stack The reference to the context stack. * @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace 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 ) { private function data_wp_interactive_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
// In closing tags, it removes the last namespace from the stack. // When exiting tags, it removes the last namespace from the stack.
if ( $p->is_tag_closer() ) { if ( 'exit' === $mode ) {
array_pop( $namespace_stack ); array_pop( $namespace_stack );
return; return;
} }
@ -518,12 +527,13 @@ final class WP_Interactivity_API {
* @since 6.5.0 * @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.
* @param array $context_stack The reference to the context stack. * @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace 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 ) { private function data_wp_context_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
// In closing tags, it removes the last context from the stack. // When exiting tags, it removes the last context from the stack.
if ( $p->is_tag_closer() ) { if ( 'exit' === $mode ) {
array_pop( $context_stack ); array_pop( $context_stack );
return; return;
} }
@ -564,11 +574,12 @@ final class WP_Interactivity_API {
* @since 6.5.0 * @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.
* @param array $context_stack The reference to the context stack. * @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace 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 ) { private function data_wp_bind_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) { if ( 'enter' === $mode ) {
$all_bind_directives = $p->get_attribute_names_with_prefix( 'data-wp-bind--' ); $all_bind_directives = $p->get_attribute_names_with_prefix( 'data-wp-bind--' );
foreach ( $all_bind_directives as $attribute_name ) { foreach ( $all_bind_directives as $attribute_name ) {
@ -608,11 +619,12 @@ final class WP_Interactivity_API {
* @since 6.5.0 * @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.
* @param array $context_stack The reference to the context stack. * @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace 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 ) { private function data_wp_class_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) { if ( 'enter' === $mode ) {
$all_class_directives = $p->get_attribute_names_with_prefix( 'data-wp-class--' ); $all_class_directives = $p->get_attribute_names_with_prefix( 'data-wp-class--' );
foreach ( $all_class_directives as $attribute_name ) { foreach ( $all_class_directives as $attribute_name ) {
@ -642,11 +654,12 @@ final class WP_Interactivity_API {
* @since 6.5.0 * @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.
* @param array $context_stack The reference to the context stack. * @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace 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 ) { private function data_wp_style_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) { if ( 'enter' === $mode ) {
$all_style_attributes = $p->get_attribute_names_with_prefix( 'data-wp-style--' ); $all_style_attributes = $p->get_attribute_names_with_prefix( 'data-wp-style--' );
foreach ( $all_style_attributes as $attribute_name ) { foreach ( $all_style_attributes as $attribute_name ) {
@ -734,11 +747,12 @@ final class WP_Interactivity_API {
* @since 6.5.0 * @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.
* @param array $context_stack The reference to the context stack. * @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace 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 ) { private function data_wp_text_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) { if ( 'enter' === $mode ) {
$attribute_value = $p->get_attribute( 'data-wp-text' ); $attribute_value = $p->get_attribute( 'data-wp-text' );
$result = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) ); $result = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) );
@ -832,9 +846,10 @@ HTML;
* @since 6.5.0 * @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 ) { private function data_wp_router_region_processor( WP_Interactivity_API_Directives_Processor $p, string $mode ) {
if ( ! $p->is_tag_closer() && ! $this->has_processed_router_region ) { if ( 'enter' === $mode && ! $this->has_processed_router_region ) {
$this->has_processed_router_region = true; $this->has_processed_router_region = true;
// Initialize the `core/router` store. // Initialize the `core/router` store.
@ -870,12 +885,13 @@ HTML;
* @since 6.5.0 * @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.
* @param array $context_stack The reference to the context stack. * @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack. * @param array $namespace_stack The reference to the store namespace stack.
* @param array $tag_stack The reference to the tag 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 ) { private function data_wp_each_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack, array &$tag_stack ) {
if ( ! $p->is_tag_closer() && 'TEMPLATE' === $p->get_tag() ) { if ( 'enter' === $mode && 'TEMPLATE' === $p->get_tag() ) {
$attribute_name = $p->get_attribute_names_with_prefix( 'data-wp-each' )[0]; $attribute_name = $p->get_attribute_names_with_prefix( 'data-wp-each' )[0];
$extracted_suffix = $this->extract_prefix_and_suffix( $attribute_name ); $extracted_suffix = $this->extract_prefix_and_suffix( $attribute_name );
$item_name = isset( $extracted_suffix[1] ) ? $this->kebab_to_camel_case( $extracted_suffix[1] ) : 'item'; $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 * @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. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.