Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions src/wp-includes/block-editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -668,15 +668,16 @@ function get_block_editor_settings( array $custom_settings, $block_editor_contex
*
* @since 5.8.0
*
* @global WP_Post $post Global post object.
* @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts.
* @global WP_Styles $wp_styles The WP_Styles object for printing styles.
* @global WP_Post $post Global post object.
* @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts.
* @global WP_Styles $wp_styles The WP_Styles object for printing styles.
* @global WP_Script_Modules $wp_script_modules The WP_Script_Modules object for printing script modules.
*
* @param (string|string[])[] $preload_paths List of paths to preload.
* @param WP_Block_Editor_Context $block_editor_context The current block editor context.
*/
function block_editor_rest_api_preload( array $preload_paths, $block_editor_context ) {
global $post, $wp_scripts, $wp_styles;
global $post, $wp_scripts, $wp_styles, $wp_script_modules;

/**
* Filters the array of REST API paths that will be used to preloaded common data for the block editor.
Expand Down Expand Up @@ -710,15 +711,25 @@ function block_editor_rest_api_preload( array $preload_paths, $block_editor_cont
}

/*
* Ensure the global $post, $wp_scripts, and $wp_styles remain the same after
* API data is preloaded.
* Ensure the globals $post, $wp_scripts, $wp_styles, and $wp_script_modules
* remain the same after API data is preloaded.
* Because API preloading can call the_content and other filters, plugins
* can unexpectedly modify the global $post or enqueue assets which are not
* intended for the block editor.
*
* Copies are swapped in for the duration of the preload and the original
* instances are restored afterwards, so that hooks and other references
* bound to those instances remain valid.
*/
$backup_global_post = ! empty( $post ) ? clone $post : $post;
$backup_wp_scripts = ! empty( $wp_scripts ) ? clone $wp_scripts : $wp_scripts;
$backup_wp_styles = ! empty( $wp_styles ) ? clone $wp_styles : $wp_styles;
$original_post = $post;
$original_wp_scripts = $wp_scripts;
$original_wp_styles = $wp_styles;
$original_wp_script_modules = $wp_script_modules;

$post = ! empty( $post ) ? clone $post : $post;
$wp_scripts = ! empty( $wp_scripts ) ? clone $wp_scripts : $wp_scripts;
$wp_styles = ! empty( $wp_styles ) ? clone $wp_styles : $wp_styles;
$wp_script_modules = ! empty( $wp_script_modules ) ? clone $wp_script_modules : $wp_script_modules;

foreach ( $preload_paths as &$path ) {
if ( is_string( $path ) && ! str_starts_with( $path, '/' ) ) {
Expand All @@ -739,10 +750,14 @@ function block_editor_rest_api_preload( array $preload_paths, $block_editor_cont
array()
);

// Restore the global $post, $wp_scripts, and $wp_styles as they were before API preloading.
$post = $backup_global_post;
$wp_scripts = $backup_wp_scripts;
$wp_styles = $backup_wp_styles;
// Restore the original $post, $wp_scripts, $wp_styles, and $wp_script_modules instances.
$post = $original_post;
if ( ! empty( $post ) ) {
setup_postdata( $post );
}
$wp_scripts = $original_wp_scripts;
$wp_styles = $original_wp_styles;
$wp_script_modules = $original_wp_script_modules;

wp_add_inline_script(
'wp-api-fetch',
Expand Down
104 changes: 88 additions & 16 deletions tests/phpunit/tests/blocks/editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
class Tests_Blocks_Editor extends WP_UnitTestCase {
/**
* Sets up each test method.
*
* @global WP_Scripts $wp_scripts
* @global WP_Styles $wp_styles
* @global WP_Script_Modules $wp_script_modules
*/
public function set_up() {
global $post;
Expand All @@ -33,19 +37,33 @@ public function set_up() {
global $post_ID;
$post_ID = 1;

global $wp_scripts, $wp_styles;
$this->original_wp_scripts = $wp_scripts;
$this->original_wp_styles = $wp_styles;
$wp_scripts = null;
$wp_styles = null;
global $wp_scripts, $wp_styles, $wp_script_modules;
$this->original_wp_scripts = $wp_scripts;
$this->original_wp_styles = $wp_styles;
$this->original_wp_script_modules = $wp_script_modules;
$wp_scripts = null;
$wp_styles = null;
$wp_script_modules = null;
wp_scripts();
wp_styles();
/*
* The script module print callbacks are bound to the instance that
* registers them, so the fresh instance must register its own hooks.
* WP_UnitTestCase restores $wp_filter between tests.
*/
wp_script_modules()->add_hooks();
}

public function tear_down() {
global $wp_scripts, $wp_styles;
$wp_scripts = $this->original_wp_scripts;
$wp_styles = $this->original_wp_styles;
global $wp_scripts, $wp_styles, $wp_script_modules;
$wp_scripts = $this->original_wp_scripts;
$wp_styles = $this->original_wp_styles;
$wp_script_modules = $this->original_wp_script_modules;

$registry = WP_Block_Type_Registry::get_instance();
if ( $registry->is_registered( 'tests/preload-view-assets' ) ) {
$registry->unregister( 'tests/preload-view-assets' );
}

/** @var WP_REST_Server $wp_rest_server */
global $wp_rest_server;
Expand All @@ -60,15 +78,11 @@ public function tear_down() {
parent::tear_down();
}

/**
* @var WP_Scripts|null
*/
protected $original_wp_scripts;
protected ?WP_Scripts $original_wp_scripts;

/**
* @var WP_Styles|null
*/
protected $original_wp_styles;
protected ?WP_Styles $original_wp_styles;

protected ?WP_Script_Modules $original_wp_script_modules;

/**
* Original stylesheet.
Expand Down Expand Up @@ -685,6 +699,64 @@ public function test_block_editor_rest_api_preload_adds_missing_leading_slash( a
$this->assertStringContainsString( $expected, $haystack );
}

/**
* Tests that scripts, styles, and script modules enqueued while rendering blocks
* during REST API preloading are not enqueued in the block editor.
*
* @ticket 64484
* @ticket 55151
*
* @covers ::block_editor_rest_api_preload
*/
public function test_block_editor_rest_api_preload_discards_assets_enqueued_during_preload() {
Comment thread
sirreal marked this conversation as resolved.
wp_register_script( 'test-view-script', '/test-view-script.js', array(), null );
wp_register_style( 'test-view-style', '/test-view-style.css', array(), null );
wp_register_script_module( 'test-view-script-module', '/test-view-script-module.js' );
wp_register_script_module( 'test-editor-script-module', '/test-editor-script-module.js' );

$rendered = false;
register_block_type(
'tests/preload-view-assets',
array(
'render_callback' => static function () use ( &$rendered ) {
$rendered = true;
return '<p>Block content.</p>';
},
'view_script_handles' => array( 'test-view-script' ),
'view_style_handles' => array( 'test-view-style' ),
'view_script_module_ids' => array( 'test-view-script-module' ),
)
);

$post_id = self::factory()->post->create(
array(
'post_content' => '<!-- wp:tests/preload-view-assets /-->',
)
);
wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );

// Enqueued before preloading, so it must still be enqueued afterwards.
wp_enqueue_script_module( 'test-editor-script-module' );

block_editor_rest_api_preload(
array( "/wp/v2/posts/{$post_id}?context=edit" ),
new WP_Block_Editor_Context( array( 'post' => get_post( $post_id ) ) )
);

$admin_footer_scripts = get_echo(
static function () {
/** This action is documented in wp-admin/admin-footer.php */
do_action( 'admin_print_footer_scripts' );
}
);

$this->assertTrue( $rendered, 'The block should render during preloading.' );
$this->assertNotContains( 'test-view-script', wp_scripts()->queue, 'The block view script should not be enqueued.' );
$this->assertNotContains( 'test-view-style', wp_styles()->queue, 'The block view style should not be enqueued.' );
$this->assertStringNotContainsString( 'test-view-script-module', $admin_footer_scripts, 'The block view script module should not be printed.' );
$this->assertStringContainsString( 'test-editor-script-module', $admin_footer_scripts, 'The script module enqueued before preloading should be printed.' );
}

/**
* @ticket 57547
*
Expand Down
Loading