From 3335f26cf41de200abc9ea4ea9c0b5dc95435db1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 9 Feb 2018 22:40:24 -0800 Subject: [PATCH 1/6] Allow dirty AMP styles when in Customizer preview iframe --- includes/class-amp-theme-support.php | 5 ++- .../sanitizers/class-amp-base-sanitizer.php | 5 +++ .../sanitizers/class-amp-style-sanitizer.php | 5 +++ .../class-amp-tag-and-attribute-sanitizer.php | 43 +++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php index acda180917f..fc34bb3aeb9 100644 --- a/includes/class-amp-theme-support.php +++ b/includes/class-amp-theme-support.php @@ -160,7 +160,7 @@ public static function register_hooks() { * install is not on utf-8 and we may need to do a encoding conversion. */ add_action( 'wp_head', array( __CLASS__, 'add_amp_component_scripts' ), 10 ); - add_action( 'wp_head', array( __CLASS__, 'print_amp_styles' ) ); + add_action( 'wp_print_styles', array( __CLASS__, 'print_amp_styles' ), 0 ); // Print boilerplate before theme and plugin stylesheets. add_action( 'wp_head', 'amp_add_generator_metadata', 20 ); add_action( 'wp_head', 'amp_print_schemaorg_metadata' ); @@ -770,7 +770,7 @@ public static function finish_output_buffering() { * @global int $content_width */ public static function prepare_response( $response, $args = array() ) { - global $content_width; + global $content_width, $wp_customize; /* * Check if the response starts with HTML markup. @@ -786,6 +786,7 @@ public static function prepare_response( $response, $args = array() ) { 'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat. 'use_document_element' => true, 'remove_invalid_callback' => null, + 'allow_dirty_styles' => is_customize_preview() && $wp_customize->get_messenger_channel(), // When rendering in Customizer preview *iframe*. ), $args ); diff --git a/includes/sanitizers/class-amp-base-sanitizer.php b/includes/sanitizers/class-amp-base-sanitizer.php index 8e7a936afb7..f5814893828 100644 --- a/includes/sanitizers/class-amp-base-sanitizer.php +++ b/includes/sanitizers/class-amp-base-sanitizer.php @@ -48,6 +48,11 @@ abstract class AMP_Base_Sanitizer { * @type string[] $amp_allowed_tags * @type string[] $amp_globally_allowed_attributes * @type string[] $amp_layout_allowed_attributes + * @type array $amp_allowed_tags + * @type array $amp_globally_allowed_attributes + * @type array $amp_layout_allowed_attributes + * @type array $amp_bind_placeholder_prefix + * @type bool $allow_dirty_styles * } */ protected $args; diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index 92e9831d454..f085144cf87 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -152,6 +152,11 @@ public function get_stylesheets() { public function sanitize() { $elements = array(); + // Do nothing if inline styles are allowed. + if ( ! empty( $this->args['allow_dirty_styles'] ) ) { + return; + } + /* * Note that xpath is used to query the DOM so that the link and style elements will be * in document order. DOMNode::compareDocumentPosition() is not yet implemented. diff --git a/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php b/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php index b4c0faeee31..8f6123d3854 100644 --- a/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php +++ b/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php @@ -103,6 +103,49 @@ public function __construct( $dom, $args = array() ) { parent::__construct( $dom, $args ); + if ( ! empty( $this->args['allow_dirty_styles'] ) ) { + + // Allow style attribute on all elements. + $this->args['amp_globally_allowed_attributes']['style'] = array(); + + // Allow style elements. + $this->args['amp_allowed_tags']['style'][] = array( + 'attr_spec_list' => array( + 'type' => array( + 'value_casei' => 'text/css', + ), + ), + 'cdata' => array(), + 'tag_spec' => array( + 'spec_name' => 'style for Customizer preview', + ), + ); + + // Allow stylesheet links. + $this->args['amp_allowed_tags']['link'][] = array( + 'attr_spec_list' => array( + 'async' => array(), + 'crossorigin' => array(), + 'href' => array( + 'mandatory' => true, + ), + 'integrity' => array(), + 'media' => array(), + 'rel' => array( + 'dispatch_key' => 2, + 'mandatory' => true, + 'value_casei' => 'stylesheet', + ), + 'type' => array( + 'value_casei' => 'text/css', + ), + ), + 'tag_spec' => array( + 'spec_name' => 'link rel=stylesheet for Customizer preview', // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet + ), + ); + } + // Prepare whitelists. $this->allowed_tags = $this->args['amp_allowed_tags']; foreach ( AMP_Rule_Spec::$additional_allowed_tags as $tag_name => $tag_rule_spec ) { From a9ca57b3ec34a1cf043d232bae89908b4cb3dfac Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 9 Feb 2018 22:50:55 -0800 Subject: [PATCH 2/6] Dequeue Customizer preview styles when not in iframe --- includes/class-amp-theme-support.php | 35 +++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php index fc34bb3aeb9..12b3df0ca6b 100644 --- a/includes/class-amp-theme-support.php +++ b/includes/class-amp-theme-support.php @@ -128,6 +128,18 @@ public static function is_paired_available() { return true; } + /** + * Determine whether the user is in the Customizer preview iframe. + * + * @since 0.7 + * + * @return bool Whether in Customizer preview iframe. + */ + public static function is_customize_preview_iframe() { + global $wp_customize; + return is_customize_preview() && ! $wp_customize->get_messenger_channel(); + } + /** * Register hooks for paired mode. */ @@ -164,6 +176,10 @@ public static function register_hooks() { add_action( 'wp_head', 'amp_add_generator_metadata', 20 ); add_action( 'wp_head', 'amp_print_schemaorg_metadata' ); + if ( self::is_customize_preview_iframe() ) { + add_action( 'wp_enqueue_scripts', array( __CLASS__, 'dequeue_customize_preview_scripts' ), 1000 ); + } + add_action( 'wp_footer', 'amp_print_analytics' ); /* @@ -721,6 +737,23 @@ protected static function ensure_required_markup( DOMDocument $dom ) { } } + /** + * Dequeue Customizer assets which are not necessary outside the preview iframe. + * + * Prevent enqueueing customize-preview styles if not in customizer preview iframe. + * These are only needed for when there is live editing of content, such as selective refresh. + * + * @since 0.7 + */ + public static function dequeue_customize_preview_scripts() { + wp_dequeue_style( 'customize-preview' ); + foreach ( wp_styles()->registered as $handle => $dependency ) { + if ( in_array( 'customize-preview', $dependency->deps, true ) ) { + wp_dequeue_style( $handle ); + } + } + } + /** * Start output buffering. * @@ -786,7 +819,7 @@ public static function prepare_response( $response, $args = array() ) { 'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat. 'use_document_element' => true, 'remove_invalid_callback' => null, - 'allow_dirty_styles' => is_customize_preview() && $wp_customize->get_messenger_channel(), // When rendering in Customizer preview *iframe*. + 'allow_dirty_styles' => self::is_customize_preview_iframe(), ), $args ); From a1f331771a18e0838026730e18c6266a9c7eb284 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Feb 2018 19:50:22 -0800 Subject: [PATCH 3/6] Better encapsulate functionality into AMP_Theme_Support --- amp.php | 33 ++++---- includes/amp-helper-functions.php | 91 --------------------- includes/class-amp-theme-support.php | 108 ++++++++++++++++++++++++- tests/test-amp-helper-functions.php | 65 --------------- tests/test-class-amp-theme-support.php | 69 ++++++++++++++++ 5 files changed, 187 insertions(+), 179 deletions(-) diff --git a/amp.php b/amp.php index 66b44f38762..0e59906c557 100644 --- a/amp.php +++ b/amp.php @@ -87,15 +87,7 @@ function amp_after_setup_theme() { define( 'AMP_QUERY_VAR', apply_filters( 'amp_query_var', 'amp' ) ); } - add_action( 'init', 'amp_init' ); - add_action( 'widgets_init', 'AMP_Theme_Support::register_widgets' ); // @todo Let this be called by AMP_Theme_Support::init(). - add_action( 'init', 'AMP_Theme_Support::setup_commenting' ); // @todo Let this be called by AMP_Theme_Support::init(). - add_action( 'admin_init', 'AMP_Options_Manager::register_settings' ); - add_action( 'wp_loaded', 'amp_post_meta_box' ); - add_action( 'wp_loaded', 'amp_add_options_menu' ); - add_action( 'parse_query', 'amp_correct_query_when_is_front_page' ); - AMP_Post_Type_Support::add_post_type_support(); - AMP_Validation_Utils::init(); + add_action( 'init', 'amp_init', 0 ); // Must be 0 because widgets_init happens at init priority 1. } add_action( 'after_setup_theme', 'amp_after_setup_theme', 5 ); @@ -103,7 +95,6 @@ function amp_after_setup_theme() { * Init AMP. * * @since 0.1 - * @global string $pagenow */ function amp_init() { @@ -118,8 +109,14 @@ function amp_init() { add_rewrite_endpoint( AMP_QUERY_VAR, EP_PERMALINK ); + AMP_Theme_Support::init(); + AMP_Post_Type_Support::add_post_type_support(); + AMP_Validation_Utils::init(); add_filter( 'request', 'amp_force_query_var_value' ); - add_action( 'wp', 'amp_maybe_add_actions' ); + add_action( 'admin_init', 'AMP_Options_Manager::register_settings' ); + add_action( 'wp_loaded', 'amp_post_meta_box' ); + add_action( 'wp_loaded', 'amp_add_options_menu' ); + add_action( 'parse_query', 'amp_correct_query_when_is_front_page' ); // Redirect the old url of amp page to the updated url. add_filter( 'old_slug_redirect_url', 'amp_redirect_old_slug_to_new_url' ); @@ -127,7 +124,9 @@ function amp_init() { if ( class_exists( 'Jetpack' ) && ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) { require_once AMP__DIR__ . '/jetpack-helper.php'; } - amp_handle_xhr_request(); + + // Add actions for legacy post templates. + add_action( 'wp', 'amp_maybe_add_actions' ); } // Make sure the `amp` query var has an explicit value. @@ -150,15 +149,9 @@ function amp_force_query_var_value( $query_vars ) { * @return void */ function amp_maybe_add_actions() { - $is_amp_endpoint = is_amp_endpoint(); - // Add hooks for when a themes that support AMP. + // Short-circuit when theme supports AMP, as everything is handled by AMP_Theme_Support. if ( current_theme_supports( 'amp' ) ) { - if ( $is_amp_endpoint ) { - AMP_Theme_Support::init(); - } else { - amp_add_frontend_actions(); - } return; } @@ -168,6 +161,8 @@ function amp_maybe_add_actions() { return; } + $is_amp_endpoint = is_amp_endpoint(); + /** * Queried post object. * diff --git a/includes/amp-helper-functions.php b/includes/amp-helper-functions.php index d8c4690568c..88a66213664 100644 --- a/includes/amp-helper-functions.php +++ b/includes/amp-helper-functions.php @@ -483,94 +483,3 @@ function amp_print_schemaorg_metadata() { get_error_message(); - } - $amp_mustache_allowed_html_tags = array( 'strong', 'b', 'em', 'i', 'u', 's', 'small', 'mark', 'del', 'ins', 'sup', 'sub' ); - wp_send_json( array( - 'error' => wp_kses( $error, array_fill_keys( $amp_mustache_allowed_html_tags, array() ) ), - ) ); - }; - } ); - - // Send AMP header. - $origin = esc_url_raw( wp_unslash( $_GET['__amp_source_origin'] ) ); // WPCS: CSRF ok. - header( 'AMP-Access-Control-Allow-Source-Origin: ' . $origin, true ); -} - -/** - * Intercept the response to a non-comment POST request. - * - * @since 0.7.0 - * @param string $location The location to redirect to. - */ -function amp_intercept_post_request_redirect( $location ) { - - // Make sure relative redirects get made absolute. - $parsed_location = array_merge( - array( - 'scheme' => 'https', - 'host' => wp_parse_url( home_url(), PHP_URL_HOST ), - 'path' => strtok( wp_unslash( $_SERVER['REQUEST_URI'] ), '?' ), - ), - wp_parse_url( $location ) - ); - - $absolute_location = $parsed_location['scheme'] . '://' . $parsed_location['host']; - if ( isset( $parsed_location['port'] ) ) { - $absolute_location .= ':' . $parsed_location['port']; - } - $absolute_location .= $parsed_location['path']; - if ( isset( $parsed_location['query'] ) ) { - $absolute_location .= '?' . $parsed_location['query']; - } - if ( isset( $parsed_location['fragment'] ) ) { - $absolute_location .= '#' . $parsed_location['fragment']; - } - - header( 'AMP-Redirect-To: ' . $absolute_location ); - header( 'Access-Control-Expose-Headers: AMP-Redirect-To' ); - // Send json success as no data is required. - wp_send_json_success(); -} diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php index 12b3df0ca6b..42388d9c889 100644 --- a/includes/class-amp-theme-support.php +++ b/includes/class-amp-theme-support.php @@ -71,6 +71,20 @@ class AMP_Theme_Support { * Initialize. */ public static function init() { + if ( ! current_theme_supports( 'amp' ) ) { + return; + } + + self::purge_amp_query_vars(); + self::handle_xhr_request(); + + if ( ! is_amp_endpoint() ) { + amp_add_frontend_actions(); + } else { + self::setup_commenting(); + add_action( 'widgets_init', array( __CLASS__, 'register_widgets' ) ); + } + require_once AMP__DIR__ . '/includes/amp-post-template-actions.php'; // Validate theme support usage. @@ -95,7 +109,6 @@ public static function init() { self::register_paired_hooks(); } - self::purge_amp_query_vars(); // Note that amp_prepare_xhr_post() still looks at $_GET['__amp_source_origin']. self::register_hooks(); self::$embed_handlers = self::register_content_embed_handlers(); self::$sanitizer_classes = amp_get_content_sanitizers(); @@ -263,13 +276,100 @@ public static function purge_amp_query_vars() { } /** - * Set up commenting. + * Hook into a form submissions, such as comment the form or some other . + * + * @since 0.7.0 + * @global string $pagenow */ - public static function setup_commenting() { - if ( ! current_theme_supports( AMP_QUERY_VAR ) ) { + public static function handle_xhr_request() { + global $pagenow; + if ( empty( self::$purged_amp_query_vars['__amp_source_origin'] ) ) { return; } + if ( isset( $pagenow ) && 'wp-comments-post.php' === $pagenow ) { + // We don't need any data, so just send a success. + add_filter( 'comment_post_redirect', function() { + // We don't need any data, so just send a success. + wp_send_json_success(); + }, PHP_INT_MAX, 2 ); + self::handle_xhr_headers_output(); + } elseif ( ! empty( self::$purged_amp_query_vars['_wp_amp_action_xhr_converted'] ) ) { + add_filter( 'wp_redirect', array( __CLASS__, 'intercept_post_request_redirect' ), PHP_INT_MAX, 2 ); + self::handle_xhr_headers_output(); + } + } + + /** + * Handle the AMP XHR headers and output errors. + * + * @since 0.7.0 + */ + public static function handle_xhr_headers_output() { + // Add die handler for AMP error display. + add_filter( 'wp_die_handler', function() { + /** + * New error handler for AMP form submission. + * + * @param WP_Error|string $error The error to handle. + */ + return function( $error ) { + status_header( 400 ); + if ( is_wp_error( $error ) ) { + $error = $error->get_error_message(); + } + $amp_mustache_allowed_html_tags = array( 'strong', 'b', 'em', 'i', 'u', 's', 'small', 'mark', 'del', 'ins', 'sup', 'sub' ); + wp_send_json( array( + 'error' => wp_kses( $error, array_fill_keys( $amp_mustache_allowed_html_tags, array() ) ), + ) ); + }; + } ); + + // Send AMP header. + $origin = esc_url_raw( self::$purged_amp_query_vars['__amp_source_origin'] ); + header( 'AMP-Access-Control-Allow-Source-Origin: ' . $origin, true ); + } + + /** + * Intercept the response to a non-comment POST request. + * + * @since 0.7.0 + * @param string $location The location to redirect to. + */ + public static function intercept_post_request_redirect( $location ) { + + // Make sure relative redirects get made absolute. + $parsed_location = array_merge( + array( + 'scheme' => 'https', + 'host' => wp_parse_url( home_url(), PHP_URL_HOST ), + 'path' => strtok( wp_unslash( $_SERVER['REQUEST_URI'] ), '?' ), + ), + wp_parse_url( $location ) + ); + + $absolute_location = $parsed_location['scheme'] . '://' . $parsed_location['host']; + if ( isset( $parsed_location['port'] ) ) { + $absolute_location .= ':' . $parsed_location['port']; + } + $absolute_location .= $parsed_location['path']; + if ( isset( $parsed_location['query'] ) ) { + $absolute_location .= '?' . $parsed_location['query']; + } + if ( isset( $parsed_location['fragment'] ) ) { + $absolute_location .= '#' . $parsed_location['fragment']; + } + + header( 'AMP-Redirect-To: ' . $absolute_location ); + header( 'Access-Control-Expose-Headers: AMP-Redirect-To' ); + // Send json success as no data is required. + wp_send_json_success(); + } + + /** + * Set up commenting. + */ + public static function setup_commenting() { /* * Temporarily force comments to be listed in descending order. * diff --git a/tests/test-amp-helper-functions.php b/tests/test-amp-helper-functions.php index 26a4963e27d..d6846c33708 100644 --- a/tests/test-amp-helper-functions.php +++ b/tests/test-amp-helper-functions.php @@ -372,69 +372,4 @@ public function test_amp_get_schemaorg_metadata() { $this->assertArrayHasKey( 'did_amp_schemaorg_metadata', $metadata ); $this->assertEquals( 'George', $metadata['author']['name'] ); } - - /** - * Test amp_intercept_post_request_redirect(). - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * @covers amp_intercept_post_request_redirect() - */ - public function test_amp_intercept_post_request_redirect() { - if ( ! function_exists( 'xdebug_get_headers' ) ) { - $this->markTestSkipped( 'xdebug is required for this test' ); - } - - add_theme_support( 'amp' ); - $url = get_home_url(); - - add_filter( 'wp_doing_ajax', '__return_true' ); - add_filter( 'wp_die_ajax_handler', function () { - return '__return_false'; - } ); - - ob_start(); - amp_intercept_post_request_redirect( $url ); - $this->assertEquals( '{"success":true}', ob_get_clean() ); - - $this->assertContains( 'AMP-Redirect-To: ' . $url, xdebug_get_headers() ); - $this->assertContains( 'Access-Control-Expose-Headers: AMP-Redirect-To', xdebug_get_headers() ); - - ob_start(); - amp_intercept_post_request_redirect( '/new-location/' ); - $this->assertEquals( '{"success":true}', ob_get_clean() ); - $this->assertContains( 'AMP-Redirect-To: https://example.org/new-location/', xdebug_get_headers() ); - - ob_start(); - amp_intercept_post_request_redirect( '//example.com/new-location/' ); - $this->assertEquals( '{"success":true}', ob_get_clean() ); - $headers = xdebug_get_headers(); - $this->assertContains( 'AMP-Redirect-To: https://example.com/new-location/', $headers ); - - ob_start(); - amp_intercept_post_request_redirect( '' ); - $this->assertEquals( '{"success":true}', ob_get_clean() ); - $this->assertContains( 'AMP-Redirect-To: https://example.org', xdebug_get_headers() ); - } - - /** - * Test amp_handle_xhr_request(). - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * @covers amp_handle_xhr_headers_output() - */ - public function test_amp_handle_xhr_request() { - global $pagenow; - if ( ! function_exists( 'xdebug_get_headers' ) ) { - $this->markTestSkipped( 'xdebug is required for this test' ); - } - - $_GET['__amp_source_origin'] = 'https://example.org'; - $pagenow = 'wp-comments-post.php'; - - amp_handle_xhr_request(); - $this->assertContains( 'AMP-Access-Control-Allow-Source-Origin: https://example.org', xdebug_get_headers() ); - - } } diff --git a/tests/test-class-amp-theme-support.php b/tests/test-class-amp-theme-support.php index b71c4d41228..3196d8c7eaa 100644 --- a/tests/test-class-amp-theme-support.php +++ b/tests/test-class-amp-theme-support.php @@ -90,11 +90,15 @@ public function test_register_widgets() { /** * Test prepare_response. * + * @global WP_Widget_Factory $wp_widget_factory * @covers AMP_Theme_Support::prepare_response() */ public function test_prepare_response() { add_theme_support( 'amp' ); AMP_Theme_Support::init(); + $wp_widget_factory = new WP_Widget_Factory(); + wp_widgets_init(); + ob_start(); ?> @@ -249,4 +253,69 @@ public function test_get_amp_scripts() { $scripts ); } + + /** + * Test intercept_post_request_redirect(). + * + * @runInSeparateProcess + * @preserveGlobalState disabled + * @covers AMP_Theme_Support::intercept_post_request_redirect() + */ + public function test_intercept_post_request_redirect() { + if ( ! function_exists( 'xdebug_get_headers' ) ) { + $this->markTestSkipped( 'xdebug is required for this test' ); + } + + add_theme_support( 'amp' ); + $url = get_home_url(); + + add_filter( 'wp_doing_ajax', '__return_true' ); + add_filter( 'wp_die_ajax_handler', function () { + return '__return_false'; + } ); + + ob_start(); + AMP_Theme_Support::intercept_post_request_redirect( $url ); + $this->assertEquals( '{"success":true}', ob_get_clean() ); + + $this->assertContains( 'AMP-Redirect-To: ' . $url, xdebug_get_headers() ); + $this->assertContains( 'Access-Control-Expose-Headers: AMP-Redirect-To', xdebug_get_headers() ); + + ob_start(); + AMP_Theme_Support::intercept_post_request_redirect( '/new-location/' ); + $this->assertEquals( '{"success":true}', ob_get_clean() ); + $this->assertContains( 'AMP-Redirect-To: https://example.org/new-location/', xdebug_get_headers() ); + + ob_start(); + AMP_Theme_Support::intercept_post_request_redirect( '//example.com/new-location/' ); + $this->assertEquals( '{"success":true}', ob_get_clean() ); + $headers = xdebug_get_headers(); + $this->assertContains( 'AMP-Redirect-To: https://example.com/new-location/', $headers ); + + ob_start(); + AMP_Theme_Support::intercept_post_request_redirect( '' ); + $this->assertEquals( '{"success":true}', ob_get_clean() ); + $this->assertContains( 'AMP-Redirect-To: https://example.org', xdebug_get_headers() ); + } + + /** + * Test handle_xhr_request(). + * + * @runInSeparateProcess + * @preserveGlobalState disabled + * @covers AMP_Theme_Support::handle_xhr_request() + */ + public function test_handle_xhr_request() { + global $pagenow; + if ( ! function_exists( 'xdebug_get_headers' ) ) { + $this->markTestSkipped( 'xdebug is required for this test' ); + } + + $_GET['__amp_source_origin'] = 'https://example.org'; + $pagenow = 'wp-comments-post.php'; + AMP_Theme_Support::purge_amp_query_vars(); + + AMP_Theme_Support::handle_xhr_request(); + $this->assertContains( 'AMP-Access-Control-Allow-Source-Origin: https://example.org', xdebug_get_headers() ); + } } From ab9643554e4efb5ec9ff452a370565c101af0c1c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Feb 2018 20:58:58 -0800 Subject: [PATCH 4/6] Allow scripts in customizer preview --- includes/class-amp-theme-support.php | 25 +++++++++++-------- .../sanitizers/class-amp-base-sanitizer.php | 2 ++ .../class-amp-tag-and-attribute-sanitizer.php | 16 ++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php index 42388d9c889..679778f3357 100644 --- a/includes/class-amp-theme-support.php +++ b/includes/class-amp-theme-support.php @@ -150,7 +150,7 @@ public static function is_paired_available() { */ public static function is_customize_preview_iframe() { global $wp_customize; - return is_customize_preview() && ! $wp_customize->get_messenger_channel(); + return is_customize_preview() && $wp_customize->get_messenger_channel(); } /** @@ -171,9 +171,8 @@ public static function register_hooks() { // Remove core actions which are invalid AMP. remove_action( 'wp_head', 'wp_post_preview_js', 1 ); remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); - remove_action( 'wp_head', 'wp_print_head_scripts', 9 ); - remove_action( 'wp_footer', 'wp_print_footer_scripts', 20 ); remove_action( 'wp_print_styles', 'print_emoji_styles' ); + remove_action( 'wp_head', 'wp_oembed_add_host_js' ); /* * Add additional markup required by AMP . @@ -189,7 +188,7 @@ public static function register_hooks() { add_action( 'wp_head', 'amp_add_generator_metadata', 20 ); add_action( 'wp_head', 'amp_print_schemaorg_metadata' ); - if ( self::is_customize_preview_iframe() ) { + if ( is_customize_preview() ) { add_action( 'wp_enqueue_scripts', array( __CLASS__, 'dequeue_customize_preview_scripts' ), 1000 ); } @@ -207,6 +206,7 @@ public static function register_hooks() { */ add_action( 'template_redirect', array( __CLASS__, 'start_output_buffering' ), 0 ); + // Commenting hooks. add_filter( 'wp_list_comments_args', array( __CLASS__, 'amp_set_comments_walker' ), PHP_INT_MAX ); add_filter( 'comment_form_defaults', array( __CLASS__, 'filter_comment_form_defaults' ) ); add_filter( 'comment_reply_link', array( __CLASS__, 'filter_comment_reply_link' ), 10, 4 ); @@ -846,10 +846,14 @@ protected static function ensure_required_markup( DOMDocument $dom ) { * @since 0.7 */ public static function dequeue_customize_preview_scripts() { - wp_dequeue_style( 'customize-preview' ); - foreach ( wp_styles()->registered as $handle => $dependency ) { - if ( in_array( 'customize-preview', $dependency->deps, true ) ) { - wp_dequeue_style( $handle ); + + // Dequeue styles unnecessary unless in customizer preview iframe when editing (such as for edit shortcuts). + if ( ! self::is_customize_preview_iframe() ) { + wp_dequeue_style( 'customize-preview' ); + foreach ( wp_styles()->registered as $handle => $dependency ) { + if ( in_array( 'customize-preview', $dependency->deps, true ) ) { + wp_dequeue_style( $handle ); + } } } } @@ -903,7 +907,7 @@ public static function finish_output_buffering() { * @global int $content_width */ public static function prepare_response( $response, $args = array() ) { - global $content_width, $wp_customize; + global $content_width; /* * Check if the response starts with HTML markup. @@ -919,7 +923,8 @@ public static function prepare_response( $response, $args = array() ) { 'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat. 'use_document_element' => true, 'remove_invalid_callback' => null, - 'allow_dirty_styles' => self::is_customize_preview_iframe(), + 'allow_dirty_styles' => self::is_customize_preview_iframe(), // Dirty styles only needed when editing (e.g. for edit shortcodes). + 'allow_dirty_scripts' => is_customize_preview(), // Scripts are always needed to inject changeset UUID. ), $args ); diff --git a/includes/sanitizers/class-amp-base-sanitizer.php b/includes/sanitizers/class-amp-base-sanitizer.php index f5814893828..6c28df43402 100644 --- a/includes/sanitizers/class-amp-base-sanitizer.php +++ b/includes/sanitizers/class-amp-base-sanitizer.php @@ -53,6 +53,8 @@ abstract class AMP_Base_Sanitizer { * @type array $amp_layout_allowed_attributes * @type array $amp_bind_placeholder_prefix * @type bool $allow_dirty_styles + * @type bool $allow_dirty_scripts + * @type callable $remove_invalid_callback * } */ protected $args; diff --git a/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php b/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php index 8f6123d3854..b63708836ed 100644 --- a/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php +++ b/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php @@ -146,6 +146,22 @@ public function __construct( $dom, $args = array() ) { ); } + // Allow scripts if requested. + if ( ! empty( $this->args['allow_dirty_scripts'] ) ) { + $this->args['amp_allowed_tags']['script'][] = array( + 'attr_spec_list' => array( + 'type' => array(), + 'src' => array(), + 'async' => array(), + 'defer' => array(), + ), + 'cdata' => array(), + 'tag_spec' => array( + 'spec_name' => 'scripts for Customizer preview', + ), + ); + } + // Prepare whitelists. $this->allowed_tags = $this->args['amp_allowed_tags']; foreach ( AMP_Rule_Spec::$additional_allowed_tags as $tag_name => $tag_rule_spec ) { From be72a00cf633e0533d0817436fd1e0880530e47d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 14 Feb 2018 10:43:13 +0100 Subject: [PATCH 5/6] Apply preprocessor on selective refresh partial renders --- includes/class-amp-theme-support.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php index 679778f3357..6ea0fa8ddb3 100644 --- a/includes/class-amp-theme-support.php +++ b/includes/class-amp-theme-support.php @@ -191,6 +191,7 @@ public static function register_hooks() { if ( is_customize_preview() ) { add_action( 'wp_enqueue_scripts', array( __CLASS__, 'dequeue_customize_preview_scripts' ), 1000 ); } + add_filter( 'customize_partial_render', array( __CLASS__, 'filter_customize_partial_render' ) ); add_action( 'wp_footer', 'amp_print_analytics' ); @@ -892,6 +893,31 @@ public static function finish_output_buffering() { echo self::prepare_response( ob_get_clean() ); // WPCS: xss ok. } + /** + * Filter rendered partial to convert to AMP. + * + * @see WP_Customize_Partial::render() + * + * @param string|mixed $partial Rendered partial. + * @return string|mixed Filtered partial. + * @global int $content_width + */ + public static function filter_customize_partial_render( $partial ) { + global $content_width; + if ( is_string( $partial ) && preg_match( '/<\w/', $partial ) ) { + $dom = AMP_DOM_Utils::get_dom_from_content( $partial ); + $args = array( + 'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat. + 'use_document_element' => false, + 'allow_dirty_styles' => true, + 'allow_dirty_scripts' => false, + ); + AMP_Content_Sanitizer::sanitize_document( $dom, self::$sanitizer_classes, $args ); // @todo Include script assets in response? + $partial = AMP_DOM_Utils::get_content_from_dom( $dom ); + } + return $partial; + } + /** * Process response to ensure AMP validity. * From dd15fea0ad329d91d5e764cddeb0e13d19ba0254 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 22 Feb 2018 10:01:15 -0700 Subject: [PATCH 6/6] Remove accepted_args param --- includes/class-amp-theme-support.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php index 48f30793f85..2bee8dc416e 100644 --- a/includes/class-amp-theme-support.php +++ b/includes/class-amp-theme-support.php @@ -295,10 +295,10 @@ public static function handle_xhr_request() { add_filter( 'comment_post_redirect', function() { // We don't need any data, so just send a success. wp_send_json_success(); - }, PHP_INT_MAX, 2 ); + }, PHP_INT_MAX ); self::handle_xhr_headers_output(); } elseif ( ! empty( self::$purged_amp_query_vars['_wp_amp_action_xhr_converted'] ) ) { - add_filter( 'wp_redirect', array( __CLASS__, 'intercept_post_request_redirect' ), PHP_INT_MAX, 2 ); + add_filter( 'wp_redirect', array( __CLASS__, 'intercept_post_request_redirect' ), PHP_INT_MAX ); self::handle_xhr_headers_output(); } }