From 243f72a24798bfaff8cc40c95b77ef7abfaa58ad Mon Sep 17 00:00:00 2001 From: Ella Date: Mon, 1 Apr 2024 19:52:43 +0300 Subject: [PATCH 1/9] Templates perf: resolve patterns server side --- lib/compat/wordpress-6.6/resolve-patterns.php | 47 +++++++++++++++++++ lib/load.php | 1 + 2 files changed, 48 insertions(+) create mode 100644 lib/compat/wordpress-6.6/resolve-patterns.php diff --git a/lib/compat/wordpress-6.6/resolve-patterns.php b/lib/compat/wordpress-6.6/resolve-patterns.php new file mode 100644 index 00000000000000..4545bab22505c9 --- /dev/null +++ b/lib/compat/wordpress-6.6/resolve-patterns.php @@ -0,0 +1,47 @@ +get_registered( $blocks[ $i ]['attrs']['slug'] ); + $pattern_content = $pattern['content']; + $blocks_to_insert = parse_blocks( $pattern_content ); + array_splice( $blocks, $i, 1, $blocks_to_insert ); + + if ( $inner_content ) { + $nullIndices = array_keys( $inner_content, null, true ); + $content_index = $nullIndices[ $i ]; + $nulls = array_fill( 0, count( $blocks_to_insert ), null ); + array_splice( $inner_content, $content_index, 1, $nulls ); + } + } else if ( ! empty( $blocks[ $i ]['innerBlocks'] ) ) { + $blocks[ $i ]['innerBlocks'] = replace_pattern_blocks( + $blocks[ $i ]['innerBlocks'], + $blocks[ $i ]['innerContent'] + ); + } + $i++; + } + return $blocks; +} + +add_filter( + 'get_block_templates', + /** + * Resolves all pattern blocks in the template content. + * + * @param WP_Block_Template[] $query_result Array of found block templates. + */ + function( $templates ) { + foreach ( $templates as $template ) { + $blocks = parse_blocks( $template->content ); + $blocks = replace_pattern_blocks( $blocks ); + $template->content = serialize_blocks( $blocks ); + } + return $templates; + } +); + diff --git a/lib/load.php b/lib/load.php index d6c705790d9704..8f03f59db8ab45 100644 --- a/lib/load.php +++ b/lib/load.php @@ -125,6 +125,7 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.5/script-loader.php'; // WordPress 6.6 compat. +require __DIR__ . '/compat/wordpress-6.6/resolve-patterns.php'; require __DIR__ . '/compat/wordpress-6.6/block-bindings/pattern-overrides.php'; require __DIR__ . '/compat/wordpress-6.6/option.php'; require __DIR__ . '/compat/wordpress-6.6/class-gutenberg-rest-templates-controller-6-6.php'; From 19136e05dd4cc4c986f830b5b7ea8be4b856bd14 Mon Sep 17 00:00:00 2001 From: Ella Date: Mon, 1 Apr 2024 20:10:08 +0300 Subject: [PATCH 2/9] Also filter get_block_template --- lib/compat/wordpress-6.6/resolve-patterns.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/compat/wordpress-6.6/resolve-patterns.php b/lib/compat/wordpress-6.6/resolve-patterns.php index 4545bab22505c9..c98b17b69b5d1a 100644 --- a/lib/compat/wordpress-6.6/resolve-patterns.php +++ b/lib/compat/wordpress-6.6/resolve-patterns.php @@ -45,3 +45,13 @@ function( $templates ) { } ); +add_filter( + 'get_block_template', + function( $template ) { + $blocks = parse_blocks( $template->content ); + $blocks = replace_pattern_blocks( $blocks ); + $template->content = serialize_blocks( $blocks ); + return $template; + } +); + From a5fdfc7209efec74716328268118f6f77ee21247 Mon Sep 17 00:00:00 2001 From: Ella Date: Mon, 1 Apr 2024 21:59:01 +0300 Subject: [PATCH 3/9] Also resolve for patterns --- lib/compat/wordpress-6.6/resolve-patterns.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/compat/wordpress-6.6/resolve-patterns.php b/lib/compat/wordpress-6.6/resolve-patterns.php index c98b17b69b5d1a..9d3f21eb34c8d5 100644 --- a/lib/compat/wordpress-6.6/resolve-patterns.php +++ b/lib/compat/wordpress-6.6/resolve-patterns.php @@ -55,3 +55,21 @@ function( $template ) { } ); +add_filter( 'rest_post_dispatch', function( $result, $server, $request ) { + if ( $request->get_route() !== '/wp/v2/block-patterns/patterns' ) { + return $result; + } + + $data = $result->get_data(); + + foreach ( $data as $index => $pattern ) { + $blocks = parse_blocks( $pattern['content'] ); + $blocks = replace_pattern_blocks( $blocks ); + $data[ $index ]['content'] = serialize_blocks( $blocks ); + } + + $result->set_data( $data ); + + return $result; +}, 10, 3 ); + From b159d95d8904e342e7dd642fd4528890ed541a91 Mon Sep 17 00:00:00 2001 From: Ella Date: Tue, 2 Apr 2024 09:18:21 +0300 Subject: [PATCH 4/9] Make PHP linter happy --- lib/compat/wordpress-6.6/resolve-patterns.php | 104 ++++++++++-------- 1 file changed, 58 insertions(+), 46 deletions(-) diff --git a/lib/compat/wordpress-6.6/resolve-patterns.php b/lib/compat/wordpress-6.6/resolve-patterns.php index 9d3f21eb34c8d5..1462fcec4aae67 100644 --- a/lib/compat/wordpress-6.6/resolve-patterns.php +++ b/lib/compat/wordpress-6.6/resolve-patterns.php @@ -1,44 +1,51 @@ get_registered( $blocks[ $i ]['attrs']['slug'] ); - $pattern_content = $pattern['content']; - $blocks_to_insert = parse_blocks( $pattern_content ); - array_splice( $blocks, $i, 1, $blocks_to_insert ); +if ( ! function_exists( 'gutenberg_replace_pattern_blocks' ) ) { + /** + * Replaces pattern blocks with their content. + * + * @param array $blocks Array of blocks. + * @param array $inner_content Optional array of inner content. + * @return array Array of blocks with patterns replaced. + */ + function gutenberg_replace_pattern_blocks( $blocks, &$inner_content = null ) { + $i = 0; + // Also process new blocks that are inserted. + while ( $i < count( $blocks ) ) { + if ( 'core/pattern' === $blocks[ $i ]['blockName'] ) { + $registry = WP_Block_Patterns_Registry::get_instance(); + $pattern = $registry->get_registered( $blocks[ $i ]['attrs']['slug'] ); + $pattern_content = $pattern['content']; + $blocks_to_insert = parse_blocks( $pattern_content ); + array_splice( $blocks, $i, 1, $blocks_to_insert ); - if ( $inner_content ) { - $nullIndices = array_keys( $inner_content, null, true ); - $content_index = $nullIndices[ $i ]; - $nulls = array_fill( 0, count( $blocks_to_insert ), null ); - array_splice( $inner_content, $content_index, 1, $nulls ); + // If we have inner content, we need to insert nulls in the + // inner content array, otherwise serialize_blocks will skip + // blocks. + if ( $inner_content ) { + $null_indices = array_keys( $inner_content, null, true ); + $content_index = $null_indices[ $i ]; + $nulls = array_fill( 0, count( $blocks_to_insert ), null ); + array_splice( $inner_content, $content_index, 1, $nulls ); + } + } elseif ( ! empty( $blocks[ $i ]['innerBlocks'] ) ) { + $blocks[ $i ]['innerBlocks'] = gutenberg_replace_pattern_blocks( + $blocks[ $i ]['innerBlocks'], + $blocks[ $i ]['innerContent'] + ); } - } else if ( ! empty( $blocks[ $i ]['innerBlocks'] ) ) { - $blocks[ $i ]['innerBlocks'] = replace_pattern_blocks( - $blocks[ $i ]['innerBlocks'], - $blocks[ $i ]['innerContent'] - ); + ++$i; } - $i++; + return $blocks; } - return $blocks; } add_filter( 'get_block_templates', - /** - * Resolves all pattern blocks in the template content. - * - * @param WP_Block_Template[] $query_result Array of found block templates. - */ - function( $templates ) { + function ( $templates ) { foreach ( $templates as $template ) { - $blocks = parse_blocks( $template->content ); - $blocks = replace_pattern_blocks( $blocks ); + $blocks = parse_blocks( $template->content ); + $blocks = gutenberg_replace_pattern_blocks( $blocks ); $template->content = serialize_blocks( $blocks ); } return $templates; @@ -47,29 +54,34 @@ function( $templates ) { add_filter( 'get_block_template', - function( $template ) { - $blocks = parse_blocks( $template->content ); - $blocks = replace_pattern_blocks( $blocks ); + function ( $template ) { + $blocks = parse_blocks( $template->content ); + $blocks = gutenberg_replace_pattern_blocks( $blocks ); $template->content = serialize_blocks( $blocks ); return $template; } ); -add_filter( 'rest_post_dispatch', function( $result, $server, $request ) { - if ( $request->get_route() !== '/wp/v2/block-patterns/patterns' ) { - return $result; - } +add_filter( + 'rest_post_dispatch', + function ( $result, $server, $request ) { + if ( $request->get_route() !== '/wp/v2/block-patterns/patterns' ) { + return $result; + } - $data = $result->get_data(); + $data = $result->get_data(); - foreach ( $data as $index => $pattern ) { - $blocks = parse_blocks( $pattern['content'] ); - $blocks = replace_pattern_blocks( $blocks ); - $data[ $index ]['content'] = serialize_blocks( $blocks ); - } + foreach ( $data as $index => $pattern ) { + $blocks = parse_blocks( $pattern['content'] ); + $blocks = gutenberg_replace_pattern_blocks( $blocks ); + $data[ $index ]['content'] = serialize_blocks( $blocks ); + } - $result->set_data( $data ); + $result->set_data( $data ); - return $result; -}, 10, 3 ); + return $result; + }, + 10, + 3 +); From 1ad598088978d12809dc68e85408ed69d45c048b Mon Sep 17 00:00:00 2001 From: Ella Date: Tue, 2 Apr 2024 09:50:06 +0300 Subject: [PATCH 5/9] Fix recursion e2e test --- lib/compat/wordpress-6.6/resolve-patterns.php | 3 +- .../editor/plugins/pattern-recursion.spec.js | 30 +++++++++++-------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/compat/wordpress-6.6/resolve-patterns.php b/lib/compat/wordpress-6.6/resolve-patterns.php index 1462fcec4aae67..2774c51f503f32 100644 --- a/lib/compat/wordpress-6.6/resolve-patterns.php +++ b/lib/compat/wordpress-6.6/resolve-patterns.php @@ -23,7 +23,7 @@ function gutenberg_replace_pattern_blocks( $blocks, &$inner_content = null ) { // inner content array, otherwise serialize_blocks will skip // blocks. if ( $inner_content ) { - $null_indices = array_keys( $inner_content, null, true ); + $null_indices = array_keys( $inner_content, null, true ); $content_index = $null_indices[ $i ]; $nulls = array_fill( 0, count( $blocks_to_insert ), null ); array_splice( $inner_content, $content_index, 1, $nulls ); @@ -84,4 +84,3 @@ function ( $result, $server, $request ) { 10, 3 ); - diff --git a/test/e2e/specs/editor/plugins/pattern-recursion.spec.js b/test/e2e/specs/editor/plugins/pattern-recursion.spec.js index 72498bcdf9914d..478a465f4c621c 100644 --- a/test/e2e/specs/editor/plugins/pattern-recursion.spec.js +++ b/test/e2e/specs/editor/plugins/pattern-recursion.spec.js @@ -4,20 +4,24 @@ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); test.describe( 'Preventing Pattern Recursion', () => { - test.beforeAll( async ( { requestUtils } ) => { - await requestUtils.activatePlugin( - 'gutenberg-test-protection-against-recursive-patterns' - ); - } ); - - test.beforeEach( async ( { admin } ) => { + test.beforeEach( async ( { admin, editor, page } ) => { await admin.createNewPost(); - } ); - - test.afterAll( async ( { requestUtils } ) => { - await requestUtils.deactivatePlugin( - 'gutenberg-test-protection-against-recursive-patterns' - ); + await editor.canvas + .locator( 'role=button[name="Add default block"i]' ) + .click(); + await page.evaluate( () => { + window.wp.data.dispatch( 'core/block-editor' ).updateSettings( { + __experimentalBlockPatterns: [ + { + name: 'evil/recursive', + title: 'Evil recursive', + description: 'Evil recursive', + content: + '

Hello

', + }, + ], + } ); + } ); } ); test( 'prevents infinite loops due to recursive patterns', async ( { From 7f09e52fdb67702b92a888b08aab28c066046d05 Mon Sep 17 00:00:00 2001 From: Ella Date: Tue, 2 Apr 2024 10:38:53 +0300 Subject: [PATCH 6/9] Address feedback --- lib/compat/wordpress-6.6/resolve-patterns.php | 133 +++++++++--------- 1 file changed, 64 insertions(+), 69 deletions(-) diff --git a/lib/compat/wordpress-6.6/resolve-patterns.php b/lib/compat/wordpress-6.6/resolve-patterns.php index 2774c51f503f32..f06fcd8944f9f7 100644 --- a/lib/compat/wordpress-6.6/resolve-patterns.php +++ b/lib/compat/wordpress-6.6/resolve-patterns.php @@ -1,86 +1,81 @@ get_registered( $blocks[ $i ]['attrs']['slug'] ); - $pattern_content = $pattern['content']; - $blocks_to_insert = parse_blocks( $pattern_content ); - array_splice( $blocks, $i, 1, $blocks_to_insert ); +/** + * Replaces pattern blocks with their content. + * + * @param array $blocks Array of blocks. + * @param array $inner_content Optional array of inner content. + * @return array Array of blocks with patterns replaced. + */ +function gutenberg_replace_pattern_blocks( $blocks, &$inner_content = null ) { + $i = 0; + // Also process new blocks that are inserted. + while ( $i < count( $blocks ) ) { + if ( 'core/pattern' === $blocks[ $i ]['blockName'] ) { + $registry = WP_Block_Patterns_Registry::get_instance(); + $pattern = $registry->get_registered( $blocks[ $i ]['attrs']['slug'] ); + $pattern_content = $pattern['content']; + $blocks_to_insert = parse_blocks( $pattern_content ); + array_splice( $blocks, $i, 1, $blocks_to_insert ); - // If we have inner content, we need to insert nulls in the - // inner content array, otherwise serialize_blocks will skip - // blocks. - if ( $inner_content ) { - $null_indices = array_keys( $inner_content, null, true ); - $content_index = $null_indices[ $i ]; - $nulls = array_fill( 0, count( $blocks_to_insert ), null ); - array_splice( $inner_content, $content_index, 1, $nulls ); - } - } elseif ( ! empty( $blocks[ $i ]['innerBlocks'] ) ) { - $blocks[ $i ]['innerBlocks'] = gutenberg_replace_pattern_blocks( - $blocks[ $i ]['innerBlocks'], - $blocks[ $i ]['innerContent'] - ); + // If we have inner content, we need to insert nulls in the + // inner content array, otherwise serialize_blocks will skip + // blocks. + if ( $inner_content ) { + $null_indices = array_keys( $inner_content, null, true ); + $content_index = $null_indices[ $i ]; + $nulls = array_fill( 0, count( $blocks_to_insert ), null ); + array_splice( $inner_content, $content_index, 1, $nulls ); } - ++$i; + } elseif ( ! empty( $blocks[ $i ]['innerBlocks'] ) ) { + $blocks[ $i ]['innerBlocks'] = gutenberg_replace_pattern_blocks( + $blocks[ $i ]['innerBlocks'], + $blocks[ $i ]['innerContent'] + ); } - return $blocks; + ++$i; } + return $blocks; } -add_filter( - 'get_block_templates', - function ( $templates ) { - foreach ( $templates as $template ) { - $blocks = parse_blocks( $template->content ); - $blocks = gutenberg_replace_pattern_blocks( $blocks ); - $template->content = serialize_blocks( $blocks ); - } - return $templates; - } -); - -add_filter( - 'get_block_template', - function ( $template ) { +function gutenberg_replace_pattern_blocks_get_block_templates( $templates ) { + foreach ( $templates as $template ) { $blocks = parse_blocks( $template->content ); $blocks = gutenberg_replace_pattern_blocks( $blocks ); $template->content = serialize_blocks( $blocks ); - return $template; } -); + return $templates; +} -add_filter( - 'rest_post_dispatch', - function ( $result, $server, $request ) { - if ( $request->get_route() !== '/wp/v2/block-patterns/patterns' ) { - return $result; - } +function gutenberg_replace_pattern_blocks_get_block_template( $template ) { + $blocks = parse_blocks( $template->content ); + $blocks = gutenberg_replace_pattern_blocks( $blocks ); + $template->content = serialize_blocks( $blocks ); + return $template; +} + +function gutenberg_replace_pattern_blocks_patterns_endpoint( $result, $server, $request ) { + if ( $request->get_route() !== '/wp/v2/block-patterns/patterns' ) { + return $result; + } - $data = $result->get_data(); + $data = $result->get_data(); - foreach ( $data as $index => $pattern ) { - $blocks = parse_blocks( $pattern['content'] ); - $blocks = gutenberg_replace_pattern_blocks( $blocks ); - $data[ $index ]['content'] = serialize_blocks( $blocks ); - } + foreach ( $data as $index => $pattern ) { + $blocks = parse_blocks( $pattern['content'] ); + $blocks = gutenberg_replace_pattern_blocks( $blocks ); + $data[ $index ]['content'] = serialize_blocks( $blocks ); + } - $result->set_data( $data ); + $result->set_data( $data ); - return $result; - }, - 10, - 3 -); + return $result; +} + +// For core merge, we should avoid the double parse and replace the patterns in templates here: +// https://github.com/WordPress/wordpress-develop/blob/02fb53498f1ce7e63d807b9bafc47a7dba19d169/src/wp-includes/block-template-utils.php#L558 +add_filter( 'get_block_templates', 'gutenberg_replace_pattern_blocks_get_block_templates' ); +add_filter( 'get_block_template', 'gutenberg_replace_pattern_blocks_get_block_template' ); +// Similarly, for patterns, we can avoid the double parse here: +// https://github.com/WordPress/wordpress-develop/blob/02fb53498f1ce7e63d807b9bafc47a7dba19d169/src/wp-includes/class-wp-block-patterns-registry.php#L175 +add_filter( 'rest_post_dispatch', 'gutenberg_replace_pattern_blocks_patterns_endpoint', 10, 3 ); From 0bfe0d674c28b9aee05529652f33c93300d833ce Mon Sep 17 00:00:00 2001 From: Ella Date: Tue, 2 Apr 2024 16:15:07 +0300 Subject: [PATCH 7/9] Guard for pattern recursion --- lib/compat/wordpress-6.6/resolve-patterns.php | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/compat/wordpress-6.6/resolve-patterns.php b/lib/compat/wordpress-6.6/resolve-patterns.php index f06fcd8944f9f7..10c99b1ce26a9c 100644 --- a/lib/compat/wordpress-6.6/resolve-patterns.php +++ b/lib/compat/wordpress-6.6/resolve-patterns.php @@ -8,14 +8,25 @@ * @return array Array of blocks with patterns replaced. */ function gutenberg_replace_pattern_blocks( $blocks, &$inner_content = null ) { + // Keep track of seen references to avoid infinite loops. + static $seen_refs = array(); $i = 0; - // Also process new blocks that are inserted. while ( $i < count( $blocks ) ) { if ( 'core/pattern' === $blocks[ $i ]['blockName'] ) { + $slug = $blocks[ $i ]['attrs']['slug']; + + if ( isset( $seen_refs[ $slug ] ) ) { + // Skip recursive patterns. + array_splice( $blocks, $i, 1 ); + continue; + } + $registry = WP_Block_Patterns_Registry::get_instance(); - $pattern = $registry->get_registered( $blocks[ $i ]['attrs']['slug'] ); - $pattern_content = $pattern['content']; - $blocks_to_insert = parse_blocks( $pattern_content ); + $pattern = $registry->get_registered( $slug ); + $blocks_to_insert = parse_blocks( $pattern['content'] ); + $seen_refs[ $slug ] = true; + $blocks_to_insert = gutenberg_replace_pattern_blocks( $blocks_to_insert ); + unset( $seen_refs[ $slug ] ); array_splice( $blocks, $i, 1, $blocks_to_insert ); // If we have inner content, we need to insert nulls in the @@ -27,13 +38,18 @@ function gutenberg_replace_pattern_blocks( $blocks, &$inner_content = null ) { $nulls = array_fill( 0, count( $blocks_to_insert ), null ); array_splice( $inner_content, $content_index, 1, $nulls ); } - } elseif ( ! empty( $blocks[ $i ]['innerBlocks'] ) ) { - $blocks[ $i ]['innerBlocks'] = gutenberg_replace_pattern_blocks( - $blocks[ $i ]['innerBlocks'], - $blocks[ $i ]['innerContent'] - ); + + // Skip inserted blocks. + $i += count( $blocks_to_insert ); + } else { + if ( ! empty( $blocks[ $i ]['innerBlocks'] ) ) { + $blocks[ $i ]['innerBlocks'] = gutenberg_replace_pattern_blocks( + $blocks[ $i ]['innerBlocks'], + $blocks[ $i ]['innerContent'] + ); + } + ++$i; } - ++$i; } return $blocks; } From a65767ef0323b53be3ae9214b2d44627e393e776 Mon Sep 17 00:00:00 2001 From: Ella Date: Tue, 2 Apr 2024 16:36:45 +0300 Subject: [PATCH 8/9] Add e2e test for recursion server side --- lib/compat/wordpress-6.6/resolve-patterns.php | 10 ++-- .../editor/plugins/pattern-recursion.spec.js | 48 ++++++++++++++++++- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/lib/compat/wordpress-6.6/resolve-patterns.php b/lib/compat/wordpress-6.6/resolve-patterns.php index 10c99b1ce26a9c..d09d04b90884af 100644 --- a/lib/compat/wordpress-6.6/resolve-patterns.php +++ b/lib/compat/wordpress-6.6/resolve-patterns.php @@ -10,7 +10,7 @@ function gutenberg_replace_pattern_blocks( $blocks, &$inner_content = null ) { // Keep track of seen references to avoid infinite loops. static $seen_refs = array(); - $i = 0; + $i = 0; while ( $i < count( $blocks ) ) { if ( 'core/pattern' === $blocks[ $i ]['blockName'] ) { $slug = $blocks[ $i ]['attrs']['slug']; @@ -21,11 +21,11 @@ function gutenberg_replace_pattern_blocks( $blocks, &$inner_content = null ) { continue; } - $registry = WP_Block_Patterns_Registry::get_instance(); - $pattern = $registry->get_registered( $slug ); - $blocks_to_insert = parse_blocks( $pattern['content'] ); + $registry = WP_Block_Patterns_Registry::get_instance(); + $pattern = $registry->get_registered( $slug ); + $blocks_to_insert = parse_blocks( $pattern['content'] ); $seen_refs[ $slug ] = true; - $blocks_to_insert = gutenberg_replace_pattern_blocks( $blocks_to_insert ); + $blocks_to_insert = gutenberg_replace_pattern_blocks( $blocks_to_insert ); unset( $seen_refs[ $slug ] ); array_splice( $blocks, $i, 1, $blocks_to_insert ); diff --git a/test/e2e/specs/editor/plugins/pattern-recursion.spec.js b/test/e2e/specs/editor/plugins/pattern-recursion.spec.js index 478a465f4c621c..069f33d671d683 100644 --- a/test/e2e/specs/editor/plugins/pattern-recursion.spec.js +++ b/test/e2e/specs/editor/plugins/pattern-recursion.spec.js @@ -3,7 +3,7 @@ */ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); -test.describe( 'Preventing Pattern Recursion', () => { +test.describe( 'Preventing Pattern Recursion (client)', () => { test.beforeEach( async ( { admin, editor, page } ) => { await admin.createNewPost(); await editor.canvas @@ -37,3 +37,49 @@ test.describe( 'Preventing Pattern Recursion', () => { await expect( warning ).toBeVisible(); } ); } ); + +test.describe( 'Preventing Pattern Recursion (server)', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activatePlugin( + 'gutenberg-test-protection-against-recursive-patterns' + ); + } ); + + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.deactivatePlugin( + 'gutenberg-test-protection-against-recursive-patterns' + ); + } ); + + test( 'prevents infinite loops due to recursive patterns', async ( { + page, + editor, + } ) => { + // Click the Toggle block inserter button + await page + .getByRole( 'button', { name: 'Toggle block inserter' } ) + .click(); + // Click the Patterns tab + await page.getByRole( 'tab', { name: 'Patterns' } ).click(); + // Click the Uncategorized tab + await page.getByRole( 'button', { name: 'Uncategorized' } ).click(); + // Click the Evil recursive pattern + await page.getByRole( 'option', { name: 'Evil recursive' } ).click(); + // By simply checking the editor content, we know that the pattern + // endpoint did not crash. + expect( await editor.getBlocks() ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: 'Hello' }, + }, + { + name: 'core/paragraph', + attributes: { content: 'Hello' }, + }, + ] ); + } ); +} ); From 7ba871e36892d762172774fe74a25a79af9ba275 Mon Sep 17 00:00:00 2001 From: Ella Date: Tue, 2 Apr 2024 16:39:55 +0300 Subject: [PATCH 9/9] That linter again --- lib/compat/wordpress-6.6/resolve-patterns.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/compat/wordpress-6.6/resolve-patterns.php b/lib/compat/wordpress-6.6/resolve-patterns.php index d09d04b90884af..f9d9cd675987d9 100644 --- a/lib/compat/wordpress-6.6/resolve-patterns.php +++ b/lib/compat/wordpress-6.6/resolve-patterns.php @@ -21,11 +21,11 @@ function gutenberg_replace_pattern_blocks( $blocks, &$inner_content = null ) { continue; } - $registry = WP_Block_Patterns_Registry::get_instance(); - $pattern = $registry->get_registered( $slug ); - $blocks_to_insert = parse_blocks( $pattern['content'] ); + $registry = WP_Block_Patterns_Registry::get_instance(); + $pattern = $registry->get_registered( $slug ); + $blocks_to_insert = parse_blocks( $pattern['content'] ); $seen_refs[ $slug ] = true; - $blocks_to_insert = gutenberg_replace_pattern_blocks( $blocks_to_insert ); + $blocks_to_insert = gutenberg_replace_pattern_blocks( $blocks_to_insert ); unset( $seen_refs[ $slug ] ); array_splice( $blocks, $i, 1, $blocks_to_insert );