diff --git a/.travis.yml b/.travis.yml index f9aa26a17..a8e3601af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,8 +39,8 @@ jobs: - stage: test php: "7.2" env: WP_VERSION=trunk - - php: "5.2" - env: WP_VERSION=latest DEV_LIB_SKIP=composer,phpcs +# - php: "5.2" +# env: WP_VERSION=latest DEV_LIB_SKIP=composer,phpcs - php: "5.3" env: WP_VERSION=latest DEV_LIB_SKIP=composer,phpcs - php: "5.4" diff --git a/amp/amp-service-worker-runtime-precaching.js b/amp/amp-service-worker-runtime-precaching.js new file mode 100644 index 000000000..06f9a0cf9 --- /dev/null +++ b/amp/amp-service-worker-runtime-precaching.js @@ -0,0 +1,9 @@ +/* global URLS */ +// See AMP_Service_Workers::add_amp_runtime_caching() and . +{ + self.addEventListener( 'install', event => { + event.waitUntil( + caches.open( wp.serviceWorker.core.cacheNames.runtime ).then( cache => cache.addAll( URLS ) ) + ); + } ); +} diff --git a/amp/class-amp-service-worker.php b/amp/class-amp-service-worker.php new file mode 100644 index 000000000..4862fc4c7 --- /dev/null +++ b/amp/class-amp-service-worker.php @@ -0,0 +1,265 @@ +register( + 'amp-cdn-runtime-cache', + function() { + $urls = AMP_Service_Worker::get_runtime_precache_urls(); + if ( empty( $urls ) ) { + return ''; + } + + $js = file_get_contents( dirname( __FILE__ ) . '/amp-service-worker-runtime-precaching.js' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, WordPress.WP.AlternativeFunctions.file_system_read_file_get_contents + $js = preg_replace( '#/\*\s*global.+?\*/#', '', $js ); + $js = str_replace( + 'URLS', + wp_json_encode( $urls ), + $js + ); + return $js; + } + ); + + // Serve the AMP Runtime from cache and check for an updated version in the background. See . + $service_workers->caching_routes()->register( + '^https:\/\/cdn\.ampproject\.org\/.*', + array( + 'strategy' => WP_Service_Worker_Caching_Routes::STRATEGY_STALE_WHILE_REVALIDATE, + ) + ); + } + + /** + * Configure the front service worker for AMP. + * + * @link https://github.com/ampproject/amp-by-example/blob/master/boilerplate-generator/templates/files/serviceworkerJs.js + * + * @param WP_Service_Worker_Scripts $service_workers Service workers. + */ + public function add_image_runtime_caching( $service_workers ) { + if ( ! ( $service_workers instanceof WP_Service_Worker_Scripts ) ) { + _doing_it_wrong( __METHOD__, esc_html__( 'Expected argument to be WP_Service_Worker_Scripts.', 'amp' ), '1.0' ); + return; + } + + $service_workers->caching_routes()->register( + '/wp-content/.*\.(?:png|gif|jpg|jpeg|svg|webp)(\?.*)?$', + array( + 'strategy' => WP_Service_Worker_Caching_Routes::STRATEGY_CACHE_FIRST, + 'cacheName' => 'images', + 'plugins' => array( + 'cacheableResponse' => array( + 'statuses' => array( 0, 200 ), + ), + 'expiration' => array( + 'maxEntries' => 60, + 'maxAgeSeconds' => MONTH_IN_SECONDS, + ), + ), + ) + ); + } + + /** + * Register URLs that will be precached in the runtime cache. (Yes, this sounds somewhat strange.) + * + * Note that the PWA plugin handles the precaching of custom logo, custom header, + * and custom background. The PWA plugin also automatically adds runtime caching + * for Google Fonts. The PWA plugin also handles precaching & serving of the + * offline/500 error pages, enabling navigation preload, + * + * @link https://github.com/ampproject/amp-by-example/blob/master/boilerplate-generator/templates/files/serviceworkerJs.js + * + * @return array Runtime pre-cached URLs. + */ + public function get_runtime_precache_urls() { + + // List of AMP scripts that we know will be used in WordPress always. + $precached_handles = array( + 'amp-runtime', + 'amp-bind', // Used by comments. + 'amp-form', // Used by comments. + ); + + $theme_support = AMP_Theme_Support::get_theme_support_args(); + if ( ! empty( $theme_support['comments_live_list'] ) ) { + $precached_handles[] = 'amp-live-list'; + } + + if ( amp_get_analytics() ) { + $precached_handles[] = 'amp-analytics'; + } + + $urls = array(); + foreach ( $precached_handles as $handle ) { + if ( wp_script_is( $handle, 'registered' ) ) { + $urls[] = wp_scripts()->registered[ $handle ]->src; + } + } + + return $urls; + } + + /** + * Add hooks to install the service worker from AMP page. + */ + public function add_install_hooks() { + if ( current_theme_supports( 'amp' ) && is_amp_endpoint() ) { + add_action( 'wp_footer', array( $this, 'install_service_worker' ) ); + + // Prevent validation error due to the script that installs the service worker on non-AMP pages. + $priority = has_action( 'wp_print_scripts', 'wp_print_service_workers' ); + if ( false !== $priority ) { + remove_action( 'wp_print_scripts', 'wp_print_service_workers', $priority ); + } + } + add_action( 'amp_post_template_footer', array( $this, 'install_service_worker' ) ); + } + + /** + * Install service worker(s). + * + * @since 1.0 + * @see wp_print_service_workers() + * @link https://github.com/xwp/pwa-wp + */ + public function install_service_worker() { + if ( ! function_exists( 'wp_service_workers' ) || ! function_exists( 'wp_get_service_worker_url' ) ) { + return; + } + + $src = wp_get_service_worker_url( WP_Service_Workers::SCOPE_FRONT ); + $iframe_src = add_query_arg( + self::INSTALL_SERVICE_WORKER_IFRAME_QUERY_VAR, + WP_Service_Workers::SCOPE_FRONT, + home_url( '/', 'https' ) + ); + ?> + + + query_vars[ self::INSTALL_SERVICE_WORKER_IFRAME_QUERY_VAR ] ) ) { + return; + } + + $scope = intval( $GLOBALS['wp']->query_vars[ self::INSTALL_SERVICE_WORKER_IFRAME_QUERY_VAR ] ); + if ( WP_Service_Workers::SCOPE_ADMIN !== $scope && WP_Service_Workers::SCOPE_FRONT !== $scope ) { + wp_die( + esc_html__( 'No service workers registered for the requested scope.', 'amp' ), + esc_html__( 'Service Worker Installation', 'amp' ), + array( 'response' => 404 ) + ); + } + + $front_scope = home_url( '/', 'relative' ); + + ?> + + + + + <?php esc_html_e( 'Service Worker Installation', 'amp' ); ?> + + + + navigator.serviceWorker.register( %s, %s );', + wp_json_encode( wp_get_service_worker_url( $scope ) ), + wp_json_encode( array( 'scope' => $front_scope ) ) + ); + ?> + + + init(); + +/** + * Bootstrap AMP integration for PWA. + * + * This will be moved to the AMP plugin once the PWA plugin's API stabilizes. + */ +function pwa_amp_bootstrap() { + global $amp_service_worker; + if ( function_exists( 'is_amp_endpoint' ) && version_compare( PHP_VERSION, '5.3', '>=' ) ) { + require_once dirname( __FILE__ ) . '/amp/class-amp-service-worker.php'; + $amp_service_worker = new AMP_Service_Worker(); + $amp_service_worker->init(); + } +} +add_action( 'plugins_loaded', 'pwa_amp_bootstrap' );