Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server directive processing: Stop processing non-interactive blocks #56302

Merged
merged 42 commits into from Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
18397dc
Initial commit
cbravobernal Nov 17, 2023
7f313a4
It works, but is stripping comments
cbravobernal Nov 18, 2023
d6582c4
Added an extra return for debugging
cbravobernal Nov 18, 2023
1f8986d
Refactor to use string instead of arrays to compare
cbravobernal Nov 18, 2023
abc0e73
Use hidden textarea to save comments in production html
cbravobernal Nov 18, 2023
08bc3bc
Use divs as delimiters, comments not working for interactive innner c…
cbravobernal Nov 18, 2023
009e5aa
Back to array for references and comment delimiters
cbravobernal Nov 28, 2023
af92974
Something is working :-)
cbravobernal Nov 30, 2023
8bc6184
Refactor, process working
cbravobernal Dec 4, 2023
82c2b16
experiment replacing inner blocks
cbravobernal Dec 5, 2023
65487d1
now working if there is only 1 interactive block
cbravobernal Dec 13, 2023
f43d498
now working with 2 interactive blocks
cbravobernal Dec 13, 2023
3288fbe
Commented stopping at first custom element, after innerblocks is not …
cbravobernal Dec 13, 2023
cb26cbe
Try using bookmarks, fix autoclosing tag not working
cbravobernal Dec 16, 2023
7e05658
Not using bookmarks anymore, just processing inner inside processing …
cbravobernal Dec 16, 2023
f23d2e0
Seems to be working, still needs a good test battery
cbravobernal Dec 18, 2023
1fc73ae
Fix interactivity API directive processing
cbravobernal Dec 18, 2023
07f819a
Remove tests that will be updated in next commits
cbravobernal Dec 18, 2023
30e366e
Add markup tests
cbravobernal Dec 18, 2023
b3063a7
Use correct div group structure
cbravobernal Dec 19, 2023
b9ac02c
Small refactor
cbravobernal Dec 19, 2023
35e4d9f
remove not needed block name in interactive markers
cbravobernal Dec 19, 2023
2761518
Add more markup tests
cbravobernal Dec 20, 2023
b66c522
refactor tests
cbravobernal Dec 20, 2023
6e5c579
Make inner blocks optional
cbravobernal Dec 20, 2023
6a9ba8f
Move directives declaration to declare them only if needed
cbravobernal Dec 21, 2023
90f1c6a
Refactor more code, thanks to @darerodz
cbravobernal Dec 21, 2023
51caefd
Remove only the first ocurrence
luisherranz Dec 21, 2023
bfc0ecd
Improve comments and format
luisherranz Dec 21, 2023
5a01ef2
Update tests to use HTML API, fix non interactive blocks not being pa…
cbravobernal Dec 21, 2023
f5ac832
Unmark children of interactive blocks according to @DAreRodz comment
cbravobernal Dec 22, 2023
4843cb7
Add a p tag check test
cbravobernal Dec 22, 2023
0911938
Execute directives by priority
DAreRodz Nov 30, 2023
aea5f74
Remove gutenberg name from tests and use camelCase for context property
luisherranz Dec 24, 2023
93be756
Fix empty style attribute error
luisherranz Dec 24, 2023
2c0b722
Add test for directive ordering
luisherranz Dec 24, 2023
d500b19
Fix evaluate should only execute anonymous functions test
luisherranz Dec 24, 2023
ec51bda
Fix wp-style tests
luisherranz Dec 24, 2023
9c0290d
Improve tests
luisherranz Dec 24, 2023
3c00d97
Test that we don't process non-interactive blocks
luisherranz Dec 25, 2023
22f6131
Stop unmarking children of interactive blocks
luisherranz Dec 25, 2023
b106720
Move directives inside gutenberg_process_interactive_html
luisherranz Dec 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
138 changes: 41 additions & 97 deletions lib/experimental/interactivity-api/class-wp-directive-processor.php
Expand Up @@ -13,65 +13,79 @@
/**
* This processor is built on top of the HTML Tag Processor and augments its
* capabilities to process the Interactivity API directives.
*
* IMPORTANT DISCLAIMER: This code is highly experimental and its only purpose
* is to provide a way to test the server-side rendering of the Interactivity
* API. Most of this code will be discarded once the HTML Processor is
* available. Please restrain from investing unnecessary time and effort trying
* to improve this code.
*/
class WP_Directive_Processor extends Gutenberg_HTML_Tag_Processor_6_5 {
/**
* String containing the current root block.
*
* @var string
*/
public static $root_block = null;

/**
* An array of root blocks.
* Array containing the direct children of interactive blocks.
*
* @var array
*/
public static $root_block = null;
public static $children_of_interactive_block = array();

/**
* Add a root block to the variable.
* Sets the current root block.
*
* @param array $block The block to add.
*
* @return void
*/
public static function mark_root_block( $block ) {
self::$root_block = md5( serialize( $block ) );
}

/**
* Remove a root block to the variable.
*
* @return void
* Resets the root block.
*/
public static function unmark_root_block() {
self::$root_block = null;
}

/**
* Check if block is a root block.
* Checks if block is a root block.
*
* @param array $block The block to check.
*
* @return bool True if block is a root block, false otherwise.
*/
public static function is_marked_as_root_block( $block ) {
return md5( serialize( $block ) ) === self::$root_block;
}

/**
* Check if a root block has already been defined.
* Checks if a root block has already been defined.
*
* @return bool True if block is a root block, false otherwise.
* @return bool True if there is a root block, false otherwise.
*/
public static function has_root_block() {
return isset( self::$root_block );
}

/**
* Stores a reference to a direct children of an interactive block to be able
* to identify it later.
*
* @param array $block The block to add.
*/
public static function mark_children_of_interactive_block( $block ) {
self::$children_of_interactive_block[] = md5( serialize( $block ) );
}

/**
* Find the matching closing tag for an opening tag.
* Checks if block is marked as children of an interactive block.
*
* @param array $block The block to check.
* @return bool True if block is a children of an interactive block, false otherwise.
*/
public static function is_marked_as_children_of_interactive_block( $block ) {
return in_array( md5( serialize( $block ) ), self::$children_of_interactive_block, true );
}

/**
* Finds the matching closing tag for an opening tag.
*
* When called while on an open tag, traverse the HTML until we find the
* matching closing tag, respecting any in-between content, including nested
Expand Down Expand Up @@ -111,76 +125,7 @@ public function next_balanced_closer() {
}

/**
* Traverses the HTML searching for Interactivity API directives and processing
* them.
*
* @param WP_Directive_Processor $tags An instance of the WP_Directive_Processor.
* @param string $prefix Attribute prefix.
* @param string[] $directives Directives.
*
* @return WP_Directive_Processor The modified instance of the
* WP_Directive_Processor.
*/
public function process_rendered_html( $tags, $prefix, $directives ) {
$context = new WP_Directive_Context();
$tag_stack = array();

while ( $tags->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
$tag_name = $tags->get_tag();

// Is this a tag that closes the latest opening tag?
if ( $tags->is_tag_closer() ) {
if ( 0 === count( $tag_stack ) ) {
continue;
}

list( $latest_opening_tag_name, $attributes ) = end( $tag_stack );
if ( $latest_opening_tag_name === $tag_name ) {
array_pop( $tag_stack );

// If the matching opening tag didn't have any directives, we move on.
if ( 0 === count( $attributes ) ) {
continue;
}
}
} else {
$attributes = array();
foreach ( $tags->get_attribute_names_with_prefix( $prefix ) as $name ) {
/*
* Removes the part after the double hyphen before looking for
* the directive processor inside `$directives`, e.g., "wp-bind"
* from "wp-bind--src" and "wp-context" from "wp-context" etc...
*/
list( $type ) = WP_Directive_Processor::parse_attribute_name( $name );
if ( array_key_exists( $type, $directives ) ) {
$attributes[] = $type;
}
}

/*
* If this is an open tag, and if it either has directives, or if
* we're inside a tag that does, take note of this tag and its
* directives so we can call its directive processor once we
* encounter the matching closing tag.
*/
if (
! WP_Directive_Processor::is_html_void_element( $tags->get_tag() ) &&
( 0 !== count( $attributes ) || 0 !== count( $tag_stack ) )
) {
$tag_stack[] = array( $tag_name, $attributes );
}
}

foreach ( $attributes as $attribute ) {
call_user_func( $directives[ $attribute ], $tags, $context );
}
}

return $tags;
}

/**
* Return the content between two balanced tags.
* Returns the content between two balanced tags.
*
* When called on an opening tag, return the HTML content found between that
* opening tag and its matching closing tag.
Expand All @@ -206,14 +151,13 @@ public function get_inner_html() {
}

/**
* Set the content between two balanced tags.
* Sets the content between two balanced tags.
*
* When called on an opening tag, set the HTML content found between that
* opening tag and its matching closing tag.
*
* @param string $new_html The string to replace the content between the
* matching tags with.
*
* @return bool Whether the content was successfully replaced.
*/
public function set_inner_html( $new_html ) {
Expand All @@ -237,7 +181,7 @@ public function set_inner_html( $new_html ) {
}

/**
* Return a pair of bookmarks for the current opening tag and the matching
* Returns a pair of bookmarks for the current opening tag and the matching
* closing tag.
*
* @return array|false A pair of bookmarks, or false if there's no matching
Expand Down Expand Up @@ -267,12 +211,12 @@ public function get_balanced_tag_bookmarks() {
}

/**
* Whether a given HTML element is void (e.g. <br>).
* Checks whether a given HTML element is void (e.g. <br>).
*
* @see https://html.spec.whatwg.org/#elements-2
*
* @param string $tag_name The element in question.
* @return bool True if the element is void.
*
* @see https://html.spec.whatwg.org/#elements-2
*/
public static function is_html_void_element( $tag_name ) {
switch ( $tag_name ) {
Expand All @@ -297,7 +241,7 @@ public static function is_html_void_element( $tag_name ) {
}

/**
* Extract and return the directive type and the the part after the double
* Extracts and return the directive type and the the part after the double
* hyphen from an attribute name (if present), in an array format.
*
* Examples:
Expand All @@ -307,7 +251,7 @@ public static function is_html_void_element( $tag_name ) {
* 'wp-thing--and--thang' => array( 'wp-thing', 'and--thang' )
*
* @param string $name The attribute name.
* @return array The resulting array
* @return array The resulting array.
*/
public static function parse_attribute_name( $name ) {
return explode( '--', $name, 2 );
Expand Down