Skip to content

Commit

Permalink
Merge pull request #42 from xwp/add/restore-turbo
Browse files Browse the repository at this point in the history
Precache assets used in the admin (Turbo mode)
  • Loading branch information
westonruter authored Sep 27, 2018
2 parents a9653d1 + c558d8c commit 16cd551
Show file tree
Hide file tree
Showing 13 changed files with 285 additions and 56 deletions.
1 change: 1 addition & 0 deletions pwa.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
require_once PWA_PLUGIN_DIR . '/wp-includes/integrations/class-wp-service-worker-scripts-integration.php';
require_once PWA_PLUGIN_DIR . '/wp-includes/integrations/class-wp-service-worker-styles-integration.php';
require_once PWA_PLUGIN_DIR . '/wp-includes/integrations/class-wp-service-worker-fonts-integration.php';
require_once PWA_PLUGIN_DIR . '/wp-includes/integrations/class-wp-service-worker-admin-assets-integration.php';

/** WordPress Service Worker Functions */
require_once PWA_PLUGIN_DIR . '/wp-includes/service-workers.php';
Expand Down
21 changes: 6 additions & 15 deletions tests/test-class-wp-service-workers.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function test_serve_request_front() {
add_action( 'wp_admin_service_worker', array( $this, 'register_foo_sw' ) );

ob_start();
wp_service_workers()->serve_request( WP_Service_Workers::SCOPE_FRONT );
wp_service_workers()->serve_request();
$output = ob_get_clean();
$this->assertContains( $this->return_foo_sw(), $output );
$this->assertContains( $this->return_bar_sw(), $output );
Expand All @@ -87,7 +87,8 @@ public function test_serve_request_admin() {
add_action( 'wp_admin_service_worker', array( $this, 'register_foo_sw' ) );

ob_start();
wp_service_workers()->serve_request( WP_Service_Workers::SCOPE_ADMIN );
set_current_screen( 'admin-ajax' );
wp_service_workers()->serve_request();
$output = ob_get_clean();

$this->assertContains( $this->return_foo_sw(), $output );
Expand All @@ -98,17 +99,6 @@ public function test_serve_request_admin() {
);
}

/**
* Test serve_request for invalid scope.
*
* @covers WP_Service_Workers::serve_request()
*/
public function test_serve_request_invalid_scope() {
ob_start();
wp_service_workers()->serve_request( 'bad' );
$this->assertContains( 'invalid_scope_requested', ob_get_clean() );
}

/**
* Test serve_request for bad src callback.
*
Expand All @@ -125,7 +115,8 @@ public function test_serve_request_bad_src_callback() {
);

ob_start();
wp_service_workers()->serve_request( WP_Service_Workers::SCOPE_ADMIN );
set_current_screen( 'admin-ajax' );
wp_service_workers()->serve_request();
$output = ob_get_clean();

$this->assertContains( 'Service worker src is invalid', $output );
Expand All @@ -147,7 +138,7 @@ public function test_serve_request_bad_src_url() {
);

ob_start();
wp_service_workers()->serve_request( WP_Service_Workers::SCOPE_FRONT );
wp_service_workers()->serve_request();
$output = ob_get_clean();

$this->assertContains( 'Service worker src is invalid', $output );
Expand Down
4 changes: 2 additions & 2 deletions wp-includes/class-wp-service-worker-scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
*
* @see WP_Dependencies
*
* @method WP_Service_Worker_Precaching_Routes_Component precaching_routes()
* @method WP_Service_Worker_Caching_Routes_Component caching_routes()
* @method WP_Service_Worker_Precaching_Routes precaching_routes()
* @method WP_Service_Worker_Caching_Routes caching_routes()
*/
class WP_Service_Worker_Scripts extends WP_Scripts implements WP_Service_Worker_Registry {

Expand Down
38 changes: 13 additions & 25 deletions wp-includes/class-wp-service-workers.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,31 +78,19 @@ public function get_registry() {
/**
* Get the current scope for the service worker request.
*
* @global WP $wp
*
* @return int Scope. Either SCOPE_FRONT, SCOPE_ADMIN, or if neither then 0.
* @todo We don't really need this. A simple call to is_admin() is all that is required.
* @return int Scope. Either SCOPE_FRONT or SCOPE_ADMIN.
*/
public function get_current_scope() {
global $wp;
if ( ! isset( $wp->query_vars[ self::QUERY_VAR ] ) || ! is_numeric( $wp->query_vars[ self::QUERY_VAR ] ) ) {
return 0;
}
$scope = (int) $wp->query_vars[ self::QUERY_VAR ];
if ( self::SCOPE_FRONT === $scope ) {
return self::SCOPE_FRONT;
} elseif ( self::SCOPE_ADMIN === $scope ) {
return self::SCOPE_ADMIN;
}
return 0;
return is_admin() ? self::SCOPE_ADMIN : self::SCOPE_FRONT;
}

/**
* Get service worker logic for scope.
*
* @see wp_service_worker_loaded()
* @param int $scope Scope of the Service Worker.
*/
public function serve_request( $scope ) {
public function serve_request() {
/*
* Per Workbox <https://developers.google.com/web/tools/workbox/guides/service-worker-checklist#cache-control_of_your_service_worker_file>:
* "Generally, most developers will want to set the Cache-Control header to no-cache,
Expand All @@ -114,7 +102,7 @@ public function serve_request( $scope ) {

@header( 'Content-Type: text/javascript; charset=utf-8' ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.NoSilencedErrors.Discouraged

if ( self::SCOPE_FRONT === $scope ) {
if ( ! is_admin() ) {
wp_enqueue_scripts();

/**
Expand All @@ -134,7 +122,13 @@ public function serve_request( $scope ) {
* @param WP_Service_Worker_Scripts $scripts Instance to register service worker behavior with.
*/
do_action( 'wp_front_service_worker', $this->scripts );
} elseif ( self::SCOPE_ADMIN === $scope ) {
} else {
$hook_name = 'service-worker';
set_current_screen( $hook_name );

/** This action is documented in wp-admin/admin-header.php */
do_action( 'admin_enqueue_scripts', $hook_name );

/**
* Fires before serving the wp-admin service worker, when its scripts should be registered, caching routes established, and assets precached.
*
Expand All @@ -145,13 +139,7 @@ public function serve_request( $scope ) {
do_action( 'wp_admin_service_worker', $this->scripts );
}

if ( self::SCOPE_FRONT !== $scope && self::SCOPE_ADMIN !== $scope ) {
status_header( 400 );
echo '/* invalid_scope_requested */';
return;
}

printf( "/* PWA v%s */\n\n", esc_html( PWA_VERSION ) );
printf( "/* PWA v%s-%s */\n\n", esc_html( PWA_VERSION ), is_admin() ? 'admin' : 'front' );

ob_start();
$this->scripts->do_items( array_keys( $this->scripts->registered ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ public function serve( WP_Service_Worker_Scripts $scripts ) {
// Ensure the user-specific offline/500 pages are precached, and thet they update when user logs out or switches to another user.
$revision .= sprintf( ';user-%d', get_current_user_id() );

$current_scope = wp_service_workers()->get_current_scope();
if ( WP_Service_Workers::SCOPE_FRONT === $current_scope ) {
if ( ! is_admin() ) {
$offline_error_template_file = pwa_locate_template( array( 'offline.php', 'error.php' ) );
$offline_error_precache_entry = array(
'url' => add_query_arg( 'wp_error_template', 'offline', home_url( '/' ) ),
Expand Down Expand Up @@ -117,7 +116,7 @@ public function serve( WP_Service_Worker_Scripts $scripts ) {
}

$blacklist_patterns = array();
if ( WP_Service_Workers::SCOPE_FRONT === $current_scope ) {
if ( ! is_admin() ) {
$blacklist_patterns[] = '^' . preg_quote( untrailingslashit( wp_parse_url( admin_url(), PHP_URL_PATH ) ), '/' ) . '($|\?.*|/.*)';
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,29 @@ public function register( $url, $args = array() ) {
);
}

/**
* Register Emoji script.
*
* Short-circuit if SCRIPT_DEBUG (hence file not built) or print_emoji_detection_script has been removed.
*
* @since 0.2
*
* @return bool Whether emoji script was registered.
*/
public function register_emoji_script() {
if ( SCRIPT_DEBUG || false === has_action( is_admin() ? 'admin_print_scripts' : 'wp_head', 'print_emoji_detection_script' ) ) {
return false;
}

$this->register(
includes_url( 'js/wp-emoji-release.min.js' ),
array(
'revision' => get_bloginfo( 'version' ),
)
);
return true;
}

/**
* Gets all registered routes.
*
Expand Down
4 changes: 4 additions & 0 deletions wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
}

add_action( 'parse_query', 'wp_service_worker_loaded' );
add_action( 'wp_ajax_wp_service_worker', 'wp_ajax_wp_service_worker' );
add_action( 'wp_ajax_nopriv_wp_service_worker', 'wp_ajax_wp_service_worker' );
add_action( 'parse_query', 'wp_hide_admin_bar_offline' );

add_action( 'wp_head', 'wp_add_error_template_no_robots' );
add_action( 'error_head', 'wp_add_error_template_no_robots' );
add_action( 'wp_default_service_workers', 'wp_default_service_workers' );

add_action( 'admin_init', 'wp_disable_script_concatenation' );
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?php
/**
* WP_Service_Worker_Admin_Assets_Integration class.
*
* @package PWA
*/

/**
* Class representing the admin assets service worker integration.
*
* @since 0.2
*/
class WP_Service_Worker_Admin_Assets_Integration extends WP_Service_Worker_Base_Integration {

/**
* Registers the integration functionality.
*
* @since 0.2
*
* @param WP_Service_Worker_Scripts $scripts Instance to register service worker behavior with.
*/
public function register( WP_Service_Worker_Scripts $scripts ) {
if ( ! function_exists( 'list_files' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}

$admin_dir = ABSPATH . 'wp-admin/';
$admin_images = list_files( $admin_dir . 'images/' );
$inc_images = list_files( ABSPATH . WPINC . '/images/' );

// @todo This should not enqueue TinyMCE if rich editing is disabled?
$this->flag_admin_assets_with_precache( wp_scripts()->registered );
$this->flag_admin_assets_with_precache( wp_styles()->registered );

$routes = array_merge(
$this->get_routes_from_file_list( $admin_images, 'wp-admin' ),
$this->get_routes_from_file_list( $inc_images, 'wp-includes' ),
$this->get_woff_file_list(),
$this->get_tinymce_file_list()
);

foreach ( $routes as $options ) {
if ( isset( $options['url'] ) ) {
$url = $options['url'];
unset( $options['url'] );
$scripts->precaching_routes()->register( $url, $options );
}
}
}

/**
* Defines the scope of this integration by setting `$this->scope`.
*
* @since 0.2
*/
protected function define_scope() {
$this->scope = WP_Service_Workers::SCOPE_ADMIN;
}

/**
* Flags admin assets with precache.
*
* @param _WP_Dependency[] $dependencies Array of _WP_Dependency objects.
* @return array Array of routes.
*/
protected function flag_admin_assets_with_precache( $dependencies ) {
$routes = array();
foreach ( $dependencies as $handle => $params ) {

// Only precache scripts from wp-admin and wp-includes (and Gutenberg).
if ( preg_match( '#/(wp-admin|wp-includes|wp-content/plugins/gutenberg)/#', $params->src ) ) {
$params->add_data( 'precache', true );
}
}
return $routes;
}

/**
* Get static list of .woff files to precache.
*
* @todo These should be also available to the frontend. So this should go into WP_Service_Worker_Precaching_Routes.
* @return array
*/
protected function get_woff_file_list() {
return array(
array(
'revision' => get_bloginfo( 'version' ),
'url' => '/wp-includes/fonts/dashicons.woff',
),
array(
'revision' => get_bloginfo( 'version' ),
'url' => '/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.woff',
),
array(
'revision' => get_bloginfo( 'version' ),
'url' => '/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.woff',
),
);
}

/**
* Get list of TinyMCE files for precaching.
*
* @return array Routes.
*/
protected function get_tinymce_file_list() {
global $tinymce_version;
$tinymce_routes = array();
$tinymce_files = list_files( ABSPATH . WPINC . '/js/tinymce/' );

foreach ( $tinymce_files as $tinymce_file ) {
$basename = basename( $tinymce_file );
if ( preg_match( '#\.min\.(css|js)$#', $tinymce_file ) ) {
$url = includes_url( preg_replace( '/.*' . WPINC . '/', '', $tinymce_file ) );

$tinymce_routes[] = array(
'url' => $url,
'revision' => $tinymce_version,
);
}
}
return $tinymce_routes;
}

/**
* Get routes from file paths list.
*
* @param array $list List of file paths.
* @param string $folder Folder -- either 'wp-admin' or 'wp-includes'.
* @return array List of routes.
*/
protected function get_routes_from_file_list( $list, $folder ) {
$routes = array();
foreach ( $list as $filename ) {
$ext = pathinfo( $filename, PATHINFO_EXTENSION );
if ( ! in_array( $ext, array( 'png', 'gif', 'svg' ), true ) ) {
continue;
}

$routes[] = array(
'url' => strstr( $filename, '/' . $folder ),
'revision' => get_bloginfo( 'version' ),
);
}

return $routes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,27 @@ protected function get_attachment_image_urls( $attachment_id, $image_size ) {

return array();
}

/**
* Determines whether a URL is for a local file.
*
* @since 0.2
*
* @param string $url URL.
* @return bool Whether local.
*/
protected function is_local_file_url( $url ) {
$re_proto = '#^\w+(?://)#';
$url = preg_replace( $re_proto, '', $url );
$home_url = preg_replace( $re_proto, '', home_url( '/' ) );
$site_url = preg_replace( $re_proto, '', site_url( '/' ) );

return (
'/' === substr( $url, 0, 1 )
||
substr( $url, 0, strlen( $home_url ) ) === $home_url
||
substr( $url, 0, strlen( $site_url ) ) === $site_url
);
}
}
Loading

0 comments on commit 16cd551

Please sign in to comment.