diff --git a/includes/amp-helper-functions.php b/includes/amp-helper-functions.php index fd00df52fd5..ef4a9cfa4f4 100644 --- a/includes/amp-helper-functions.php +++ b/includes/amp-helper-functions.php @@ -181,9 +181,13 @@ function amp_get_content_embed_handlers( $post = null ) { 'AMP_Vimeo_Embed_Handler' => array(), 'AMP_SoundCloud_Embed_Handler' => array(), 'AMP_Instagram_Embed_Handler' => array(), + 'AMP_Issuu_Embed_Handler' => array(), + 'AMP_Meetup_Embed_Handler' => array(), 'AMP_Vine_Embed_Handler' => array(), 'AMP_Facebook_Embed_Handler' => array(), 'AMP_Pinterest_Embed_Handler' => array(), + 'AMP_Reddit_Embed_Handler' => array(), + 'AMP_Tumblr_Embed_Handler' => array(), 'AMP_Gallery_Embed_Handler' => array(), 'WPCOM_AMP_Polldaddy_Embed' => array(), ), diff --git a/includes/class-amp-autoloader.php b/includes/class-amp-autoloader.php index 25d7646bad8..b0904dd69e8 100644 --- a/includes/class-amp-autoloader.php +++ b/includes/class-amp-autoloader.php @@ -39,8 +39,12 @@ class AMP_Autoloader { 'AMP_Facebook_Embed_Handler' => 'includes/embeds/class-amp-facebook-embed', 'AMP_Gallery_Embed_Handler' => 'includes/embeds/class-amp-gallery-embed', 'AMP_Instagram_Embed_Handler' => 'includes/embeds/class-amp-instagram-embed', + 'AMP_Issuu_Embed_Handler' => 'includes/embeds/class-amp-issuu-embed-handler', + 'AMP_Meetup_Embed_Handler' => 'includes/embeds/class-amp-meetup-embed-handler', 'AMP_Pinterest_Embed_Handler' => 'includes/embeds/class-amp-pinterest-embed', + 'AMP_Reddit_Embed_Handler' => 'includes/embeds/class-amp-reddit-embed-handler', 'AMP_SoundCloud_Embed_Handler' => 'includes/embeds/class-amp-soundcloud-embed', + 'AMP_Tumblr_Embed_Handler' => 'includes/embeds/class-amp-tumblr-embed-handler', 'AMP_Twitter_Embed_Handler' => 'includes/embeds/class-amp-twitter-embed', 'AMP_Vimeo_Embed_Handler' => 'includes/embeds/class-amp-vimeo-embed', 'AMP_Vine_Embed_Handler' => 'includes/embeds/class-amp-vine-embed', diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php index c85bdd9f941..70db1c81fde 100644 --- a/includes/class-amp-theme-support.php +++ b/includes/class-amp-theme-support.php @@ -26,20 +26,6 @@ class AMP_Theme_Support { */ const CUSTOM_STYLES_PLACEHOLDER = '/* AMP:CUSTOM_STYLES_PLACEHOLDER */'; - /** - * AMP Scripts. - * - * @var array - */ - protected static $amp_scripts = array(); - - /** - * AMP Styles. - * - * @var array - */ - protected static $amp_styles = array(); - /** * Sanitizer classes. * @@ -155,6 +141,7 @@ public static function register_paired_hooks() { 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', 'locale_stylesheet' ); // Replaced below in add_amp_custom_style_placeholder() method. remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); remove_action( 'wp_head', 'wp_print_styles', 8 ); // Replaced below in add_amp_custom_style_placeholder() method. @@ -433,20 +420,14 @@ public static function add_amp_custom_style_placeholder() { /** * Get custom styles. * + * @param string[] $stylesheets Initial stylesheets. * @see wp_custom_css_cb() - * @return string Styles. + * @return string Concatenated stylesheets. */ - public static function get_amp_custom_styles() { + public static function get_amp_custom_styles( $stylesheets ) { $css = wp_styles()->print_code; - // Add styles gleaned from sanitizers. - foreach ( self::$amp_styles as $selector => $properties ) { - $css .= sprintf( - '%s{%s}', - $selector, - join( ';', $properties ) . ';' - ); - } + $css .= join( $stylesheets ); /** * Filters AMP custom CSS before it is injected onto the output buffer for the response. @@ -466,10 +447,10 @@ public static function get_amp_custom_styles() { /** * Determine required AMP scripts. * + * @param array $amp_scripts Initial scripts. * @return string Scripts to inject into the HEAD. */ - public static function get_amp_component_scripts() { - $amp_scripts = self::$amp_scripts; + public static function get_amp_component_scripts( $amp_scripts ) { foreach ( self::$embed_handlers as $embed_handler ) { $amp_scripts = array_merge( @@ -527,9 +508,6 @@ public static function finish_output_buffering( $output ) { $assets = AMP_Content_Sanitizer::sanitize_document( $dom, self::$sanitizer_classes, $args ); - self::$amp_scripts = array_merge( self::$amp_scripts, $assets['scripts'] ); - self::$amp_styles = array_merge( self::$amp_styles, $assets['styles'] ); - /* * @todo The sanitize method needs to be updated to sanitize the entire HTML element and not just the BODY. * This will require updating mandatory_parent_blacklist in amphtml-update.py to include elements that appear in the HEAD. @@ -547,7 +525,7 @@ public static function finish_output_buffering( $output ) { // Inject required scripts. $output = preg_replace( '#' . preg_quote( self::COMPONENT_SCRIPTS_PLACEHOLDER, '#' ) . '#', - self::get_amp_component_scripts(), + self::get_amp_component_scripts( $assets['scripts'] ), $output, 1 ); @@ -555,7 +533,7 @@ public static function finish_output_buffering( $output ) { // Inject styles. $output = preg_replace( '#' . preg_quote( self::CUSTOM_STYLES_PLACEHOLDER, '#' ) . '#', - self::get_amp_custom_styles(), + self::get_amp_custom_styles( $assets['stylesheets'] ), $output, 1 ); diff --git a/includes/embeds/class-amp-base-embed-handler.php b/includes/embeds/class-amp-base-embed-handler.php index 983cee4fab7..f46874b1974 100644 --- a/includes/embeds/class-amp-base-embed-handler.php +++ b/includes/embeds/class-amp-base-embed-handler.php @@ -30,7 +30,7 @@ function __construct( $args = array() ) { /** * Get mapping of AMP component names to AMP script URLs. * - * This is normally no longer needed because the wnitelist + * This is normally no longer needed because the whitelist * sanitizer will automatically detect the need for them via * the spec. * diff --git a/includes/embeds/class-amp-issuu-embed-handler.php b/includes/embeds/class-amp-issuu-embed-handler.php new file mode 100644 index 00000000000..6ffb38197ab --- /dev/null +++ b/includes/embeds/class-amp-issuu-embed-handler.php @@ -0,0 +1,65 @@ + $attr['width'], + 'height' => $attr['height'], + 'src' => $url, + 'sandbox' => 'allow-scripts allow-same-origin', + ) + ); + } + return $return; + } +} + diff --git a/includes/embeds/class-amp-meetup-embed-handler.php b/includes/embeds/class-amp-meetup-embed-handler.php new file mode 100644 index 00000000000..2957850fd08 --- /dev/null +++ b/includes/embeds/class-amp-meetup-embed-handler.php @@ -0,0 +1,45 @@ +render( array( 'url' => $url ) ); + } + + /** + * Output the Reddit amp element. + * + * @param array $args parameters used for output. + * @return string Rendered content. + */ + public function render( $args ) { + $args = wp_parse_args( $args, array( + 'url' => false, + ) ); + + if ( empty( $args['url'] ) ) { + return ''; + } + + // @todo Sizing is not yet correct. See . + return AMP_HTML_Utils::build_tag( + 'amp-reddit', + array( + 'layout' => 'responsive', + 'data-embedtype' => 'post', + 'width' => '100', + 'height' => '100', + 'data-src' => $args['url'], + ) + ); + } +} + diff --git a/includes/embeds/class-amp-tumblr-embed-handler.php b/includes/embeds/class-amp-tumblr-embed-handler.php new file mode 100644 index 00000000000..75650849c9a --- /dev/null +++ b/includes/embeds/class-amp-tumblr-embed-handler.php @@ -0,0 +1,60 @@ +https://embed.tumblr.com/embed/post/\w+/\w+)"#', $cache, $matches ) ) { + $cache = AMP_HTML_Utils::build_tag( + 'amp-iframe', + array( + 'width' => $this->args['width'], + 'height' => $this->args['height'], + 'layout' => 'responsive', + 'sandbox' => 'allow-scripts allow-popups', // The allow-scripts is needed to allow the iframe to render; allow-popups needed to allow clicking. + 'src' => $matches['href'], + ), + sprintf( 'Tumblr', $url ) + ); + } + + return $cache; + } + +} + diff --git a/includes/sanitizers/class-amp-base-sanitizer.php b/includes/sanitizers/class-amp-base-sanitizer.php index 7067c9683cd..efa9ff59a79 100644 --- a/includes/sanitizers/class-amp-base-sanitizer.php +++ b/includes/sanitizers/class-amp-base-sanitizer.php @@ -109,12 +109,30 @@ public function get_scripts() { * * @since 0.4 * - * @return string[] This are empty in this the base class. + * @return array[][] Mapping of CSS selectors to arrays of properties. */ public function get_styles() { return array(); } + /** + * Get stylesheets. + * + * @since 0.7 + * @returns array Values are the CSS stylesheets. Keys are MD5 hashes of the stylesheets. + */ + public function get_stylesheets() { + $stylesheets = array(); + + foreach ( $this->get_styles() as $selector => $properties ) { + $stylesheet = sprintf( '%s { %s }', $selector, join( '; ', $properties ) . ';' ); + + $stylesheets[ md5( $stylesheet ) ] = $stylesheet; + } + + return $stylesheets; + } + /** * Get HTML body as DOMElement from DOMDocument received by the constructor. * diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index 9cb33aab8a8..4b40b89cd73 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -8,6 +8,7 @@ /** * Class AMP_Style_Sanitizer * + * @todo This needs to also run on the CSS that is gathered for amp-custom. * Collects inline styles and outputs them in the amp-custom stylesheet. */ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer { @@ -21,12 +22,22 @@ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer { */ private $styles = array(); + /** + * Stylesheets. + * + * Values are the CSS stylesheets. Keys are MD5 hashes of the stylesheets + * + * @since 0.7 + * @var string[] + */ + private $stylesheets = array(); + /** * Get list of CSS styles in HTML content of DOMDocument ($this->dom). * * @since 0.4 * - * @return string[] + * @return string[] Mapping CSS selectors to array of properties, or mapping of keys starting with 'stylesheet:' with value being the stylesheet. */ public function get_styles() { if ( ! $this->did_convert_elements ) { @@ -35,6 +46,16 @@ public function get_styles() { return $this->styles; } + /** + * Get stylesheets. + * + * @since 0.7 + * @returns array Values are the CSS stylesheets. Keys are MD5 hashes of the stylesheets. + */ + public function get_stylesheets() { + return array_merge( parent::get_stylesheets(), $this->stylesheets ); + } + /** * Sanitize CSS styles within the HTML contained in this instance's DOMDocument. * @@ -42,12 +63,50 @@ public function get_styles() { */ public function sanitize() { $body = $this->get_body_node(); + + $this->collect_style_elements(); + $this->collect_styles_recursive( $body ); $this->did_convert_elements = true; } /** - * Collect and store all CSS styles. + * Collect and sanitize all style elements. + */ + public function collect_style_elements() { + $style_elements = $this->dom->getElementsByTagName( 'style' ); + $nodes_to_remove = array(); + + foreach ( $style_elements as $style_element ) { + /** + * Style element. + * + * @var DOMElement $style_element + */ + + if ( 'head' === $style_element->parentNode->nodeName && ( $style_element->hasAttribute( 'amp-boilerplate' ) || $style_element->hasAttribute( 'amp-custom' ) ) ) { + continue; + } + + $nodes_to_remove[] = $style_element; + + // @todo This should perhaps be done in document order to ensure proper cascade. + $rules = trim( $style_element->textContent ); + + // @todo This needs proper CSS parser, and de-duplication with \AMP_Style_Sanitizer::filter_style(). + $rules = preg_replace( '/\s*!important\s*(?=\s*;|})/', '', $rules ); + $rules = preg_replace( '/overflow\s*:\s*(auto|scroll)\s*;?\s*/', '', $rules ); + + $this->stylesheets[ md5( $rules ) ] = $rules; + } + + foreach ( $nodes_to_remove as $node_to_remove ) { + $node_to_remove->parentNode->removeChild( $node_to_remove ); + } + } + + /** + * Collect and store all CSS style attributes. * * Collects the CSS styles from within the HTML contained in this instance's DOMDocument. * @@ -56,6 +115,7 @@ public function sanitize() { * @since 0.4 * * @note Uses recursion to traverse down the tree of DOMDocument nodes. + * @todo This could use XPath to more efficiently find all elements with style attributes. * * @param DOMNode $node Node. */ @@ -155,6 +215,7 @@ private function filter_style( $property, $value ) { /** * Remove overflow if value is `auto` or `scroll`; not allowed in AMP * + * @todo This removal needs to be reported. * @see https://www.ampproject.org/docs/reference/spec.html#properties */ if ( preg_match( '#^overflow#i', $property ) && preg_match( '#^(auto|scroll)$#i', $value ) ) { @@ -167,6 +228,8 @@ private function filter_style( $property, $value ) { /** * Remove `!important`; not allowed in AMP + * + * @todo This removal needs to be reported. */ if ( false !== strpos( $value, 'important' ) ) { $value = preg_replace( '/\s*\!\s*important$/', '', $value ); diff --git a/includes/templates/class-amp-content-sanitizer.php b/includes/templates/class-amp-content-sanitizer.php index ddf533091cd..d7b75c28ec3 100644 --- a/includes/templates/class-amp-content-sanitizer.php +++ b/includes/templates/class-amp-content-sanitizer.php @@ -16,20 +16,26 @@ class AMP_Content_Sanitizer { * Sanitize _content_. * * @since 0.4.1 + * @since 0.7 Passing return_styles=false in $global_args causes stylesheets to be returned instead of styles. * * @param string $content HTML content string or DOM document. * @param string[] $sanitizer_classes Sanitizer classes. * @param array $global_args Global args. - * @return array Tuple containing sanitized HTML, scripts array, and styles array. + * @return array Tuple containing sanitized HTML, scripts array, and styles array (or stylesheets, if return_styles=false is passed in $global_args). */ public static function sanitize( $content, array $sanitizer_classes, $global_args = array() ) { $dom = AMP_DOM_Utils::get_dom_from_content( $content ); + // For back-compat. + if ( ! isset( $global_args['return_styles'] ) ) { + $global_args['return_styles'] = true; + } + $results = self::sanitize_document( $dom, $sanitizer_classes, $global_args ); return array( AMP_DOM_Utils::get_content_from_dom( $dom ), $results['scripts'], - $results['styles'], + empty( $global_args['return_styles'] ) ? $results['stylesheets'] : $results['styles'], ); } @@ -40,18 +46,23 @@ public static function sanitize( $content, array $sanitizer_classes, $global_arg * * @param DOMDocument $dom HTML document. * @param string[] $sanitizer_classes Sanitizer classes. - * @param array $global_args Global args passed into . + * @param array $args Global args passed into sanitizers. * @return array { - * Scripts and styles needed by sanitizers. + * Scripts and stylesheets needed by sanitizers. * - * @type array $scripts Scripts. - * @type array $styles Styles. + * @type array $scripts Scripts. + * @type array $stylesheets Stylesheets. If $args['return_styles'] is empty. + * @type array $styles Styles. If $args['return_styles'] is not empty. For legacy purposes. * } */ - public static function sanitize_document( &$dom, $sanitizer_classes, $global_args ) { - $scripts = array(); - $styles = array(); - foreach ( $sanitizer_classes as $sanitizer_class => $args ) { + public static function sanitize_document( &$dom, $sanitizer_classes, $args ) { + $scripts = array(); + $stylesheets = array(); + $styles = array(); + + $return_styles = ! empty( $args['return_styles'] ); + unset( $args['return_styles'] ); + foreach ( $sanitizer_classes as $sanitizer_class => $sanitizer_args ) { if ( ! class_exists( $sanitizer_class ) ) { /* translators: %s is sanitizer class */ _doing_it_wrong( __METHOD__, sprintf( esc_html__( 'Sanitizer (%s) class does not exist', 'amp' ), esc_html( $sanitizer_class ) ), '0.4.1' ); @@ -63,7 +74,7 @@ public static function sanitize_document( &$dom, $sanitizer_classes, $global_arg * * @type AMP_Base_Sanitizer $sanitizer */ - $sanitizer = new $sanitizer_class( $dom, array_merge( $global_args, $args ) ); + $sanitizer = new $sanitizer_class( $dom, array_merge( $args, $sanitizer_args ) ); if ( ! is_subclass_of( $sanitizer, 'AMP_Base_Sanitizer' ) ) { /* translators: %s is sanitizer class */ @@ -74,10 +85,14 @@ public static function sanitize_document( &$dom, $sanitizer_classes, $global_arg $sanitizer->sanitize(); $scripts = array_merge( $scripts, $sanitizer->get_scripts() ); - $styles = array_merge( $styles, $sanitizer->get_styles() ); + if ( $return_styles ) { + $styles = array_merge( $styles, $sanitizer->get_styles() ); + } else { + $stylesheets = array_merge( $stylesheets, $sanitizer->get_stylesheets() ); + } } - return compact( 'scripts', 'styles' ); + return compact( 'scripts', 'styles', 'stylesheets' ); } } diff --git a/tests/test-amp-soundcloud-embed.php b/tests/test-amp-soundcloud-embed.php index 6cb271e64ab..cc7b1435478 100644 --- a/tests/test-amp-soundcloud-embed.php +++ b/tests/test-amp-soundcloud-embed.php @@ -12,6 +12,21 @@ */ class AMP_SoundCloud_Embed_Test extends WP_UnitTestCase { + /** + * The oEmbed URL. + * + * @var string + */ + protected $oembed_url = 'https://soundcloud.com/jack-villano-villano/mozart-requiem-in-d-minor'; + + /** + * Response for oEmbed request. + * + * @see AMP_SoundCloud_Embed_Test::$oembed_url + * @var string + */ + protected $oembed_response = '{"version":1.0,"type":"rich","provider_name":"SoundCloud","provider_url":"http://soundcloud.com","height":400,"width":500,"title":"Mozart - Requiem in D minor Complete Full by Jack Villano Villano","description":"mass in D Minor ","thumbnail_url":"http://i1.sndcdn.com/artworks-000046826426-o7i9ki-t500x500.jpg","html":"\u003Ciframe width=\"500\" height=\"400\" scrolling=\"no\" frameborder=\"no\" src=\"https://w.soundcloud.com/player/?visual=true\u0026url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F90097394\u0026show_artwork=true\u0026maxwidth=500\u0026maxheight=750\u0026dnt=1\"\u003E\u003C/iframe\u003E","author_name":"Jack Villano Villano","author_url":"https://soundcloud.com/jack-villano-villano"}'; + /** * Set up. * @@ -34,6 +49,41 @@ public function setUp() { if ( function_exists( 'soundcloud_shortcode' ) ) { add_shortcode( 'soundcloud', 'soundcloud_shortcode' ); } + + add_filter( 'pre_http_request', array( $this, 'mock_http_request' ), 10, 3 ); + } + + /** + * After a test method runs, reset any state in WordPress the test method might have changed. + */ + public function tearDown() { + remove_filter( 'pre_http_request', array( $this, 'mock_http_request' ) ); + parent::tearDown(); + } + + /** + * Mock HTTP request. + * + * @param mixed $preempt Whether to preempt an HTTP request's return value. Default false. + * @param mixed $r HTTP request arguments. + * @param string $url The request URL. + * @return array Response data. + */ + public function mock_http_request( $preempt, $r, $url ) { + unset( $r ); + if ( false !== strpos( $url, 'soundcloud.com' ) ) { + return array( + 'body' => $this->oembed_response, + 'headers' => array(), + 'response' => array( + 'code' => 200, + 'message' => 'ok', + ), + 'cookies' => array(), + 'http_response' => null, + ); + } + return $preempt; } /** @@ -49,7 +99,7 @@ public function get_conversion_data() { ), 'url_simple' => array( - 'https://soundcloud.com/jack-villano-villano/mozart-requiem-in-d-minor' . PHP_EOL, + $this->oembed_url . PHP_EOL, '

' . PHP_EOL, ), ); @@ -111,7 +161,7 @@ public function get_scripts_data() { array(), ), 'converted' => array( - 'https://soundcloud.com/jack-villano-villano/mozart-requiem-in-d-minor' . PHP_EOL, + $this->oembed_url . PHP_EOL, array( 'amp-soundcloud' => 'https://cdn.ampproject.org/v0/amp-soundcloud-latest.js' ), ), ); diff --git a/tests/test-amp-style-sanitizer.php b/tests/test-amp-style-sanitizer.php index 78448cf6885..e12a6d9bc4e 100644 --- a/tests/test-amp-style-sanitizer.php +++ b/tests/test-amp-style-sanitizer.php @@ -1,6 +1,20 @@ array( @@ -13,9 +27,7 @@ public function get_data() { 'This is green.', 'This is green.', array( - '.amp-wp-inline-ad0e57ab02197f7023aa5b93bcf6c97e' => array( - 'color:#00ff00', - ), + '.amp-wp-inline-ad0e57ab02197f7023aa5b93bcf6c97e { color:#00ff00; }', ), ), @@ -23,9 +35,7 @@ public function get_data() { 'This is green.', 'This is green.', array( - '.amp-wp-inline-ad0e57ab02197f7023aa5b93bcf6c97e' => array( - 'color:#00ff00', - ), + '.amp-wp-inline-ad0e57ab02197f7023aa5b93bcf6c97e { color:#00ff00; }', ), ), @@ -33,10 +43,7 @@ public function get_data() { 'This is green.', 'This is green.', array( - '.amp-wp-inline-58550689c128f3d396444313296e4c47' => array( - 'background-color:#000', - 'color:#00ff00', - ), + '.amp-wp-inline-58550689c128f3d396444313296e4c47 { background-color:#000; color:#00ff00; }', ), ), @@ -44,9 +51,7 @@ public function get_data() { '
', '
', array( - '.amp-wp-inline-2676cd1bfa7e8feb4f0e0e8086ae9ce4' => array( - 'max-width:300px', - ), + '.amp-wp-inline-2676cd1bfa7e8feb4f0e0e8086ae9ce4 { max-width:300px; }', ), ), @@ -66,9 +71,7 @@ public function get_data() { '!important not allowed.', '!important not allowed.', array( - '.amp-wp-inline-b370df7c42957a3192cac40a8ddcff79' => array( - 'margin:1px', - ), + '.amp-wp-inline-b370df7c42957a3192cac40a8ddcff79 { margin:1px; }', ), ), @@ -76,9 +79,7 @@ public function get_data() { '!important not allowed.', '!important not allowed.', array( - '.amp-wp-inline-5b88d03e432f20476a218314084d3a05' => array( - 'color:red', - ), + '.amp-wp-inline-5b88d03e432f20476a218314084d3a05 { color:red; }', ), ), @@ -86,10 +87,7 @@ public function get_data() { '!important not allowed.', '!important not allowed.', array( - '.amp-wp-inline-ef4329d562b6b3486a8a661df5c5280f' => array( - 'background:blue', - 'color:red', - ), + '.amp-wp-inline-ef4329d562b6b3486a8a661df5c5280f { background:blue; color:red; }', ), ), @@ -97,12 +95,8 @@ public function get_data() { 'This is red.', 'This is red.', array( - '.amp-wp-inline-ad0e57ab02197f7023aa5b93bcf6c97e' => array( - 'color:#00ff00', - ), - '.amp-wp-inline-f146f9bb819d875bbe5cf83e36368b44' => array( - 'color:#ff0000', - ), + '.amp-wp-inline-ad0e57ab02197f7023aa5b93bcf6c97e { color:#00ff00; }', + '.amp-wp-inline-f146f9bb819d875bbe5cf83e36368b44 { color:#ff0000; }', ), ), @@ -110,29 +104,43 @@ public function get_data() { '
', '
', array( - '.amp-wp-inline-3be9b2f79873ad78941ba2b3c03025a3' => array( - 'background:#000', - ), + '.amp-wp-inline-3be9b2f79873ad78941ba2b3c03025a3 { background:#000; }', ), ), + + 'inline_style_element_with_multiple_rules_containing_selectors_is_removed' => array( + '
bold!
', + '
bold!
', + array( + 'div > span { font-weight:bold; font-style: italic; }', + ), + ), ); } /** + * Test sanitizer. + * * @dataProvider get_data + * @param string $source Source. + * @param string $expected_content Expected content. + * @param string $expected_stylesheets Expected stylesheets. */ - public function test_sanitizer( $source, $expected_content, $expected_stylesheet ) { + public function test_sanitizer( $source, $expected_content, $expected_stylesheets ) { $dom = AMP_DOM_Utils::get_dom_from_content( $source ); + $sanitizer = new AMP_Style_Sanitizer( $dom ); $sanitizer->sanitize(); - // Test content + $whitelist_sanitizer = new AMP_Tag_And_Attribute_Sanitizer( $dom ); + $whitelist_sanitizer->sanitize(); + + // Test content. $content = AMP_DOM_Utils::get_content_from_dom( $dom ); $this->assertEquals( $expected_content, $content ); - // Test stylesheet - $stylesheet = $sanitizer->get_styles(); - $this->assertEquals( $expected_stylesheet, $stylesheet ); + // Test stylesheet. + $this->assertEquals( $expected_stylesheets, array_values( $sanitizer->get_stylesheets() ) ); } }