From 67b7e4c3de00fb1b22d7b6fc956bce7fd9691945 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 7 Apr 2019 14:25:52 -0700 Subject: [PATCH 1/9] Add tree shaking for attribute-extant selectors --- .../sanitizers/class-amp-style-sanitizer.php | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index 2f447637a06..2df1788a42f 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -558,7 +558,7 @@ private function has_used_tag_names( $tag_names ) { * Check whether the attributes exist. * * @since 1.1 - * @todo Make $attribute_names into $attributes as an associative array and implement lookups of specific values. + * @todo Make $attribute_names into $attributes as an associative array and implement lookups of specific values. Since attribute values can vary (e.g. with amp-bind), this may not be feasible. * * @param string[] $attribute_names Attribute names. * @return bool Whether all supplied attributes are used. @@ -1463,8 +1463,18 @@ function( $selector ) { // Remove :not() and pseudo selectors to eliminate false negatives, such as with `body:not(.title-tagline-hidden) .site-branding-text`. $reduced_selector = preg_replace( '/:[a-zA-Z0-9_-]+(\(.+?\))?/', '', $selector ); - // Remove attribute selectors to eliminate false negative, such as with `.social-navigation a[href*="example.com"]:before`. - $reduced_selector = preg_replace( '/\[\w.*?\]/', '', $reduced_selector ); + /* + * Gather attribute names while removing attribute selectors to eliminate false negative, + * such as with `.social-navigation a[href*="example.com"]:before`. + */ + $reduced_selector = preg_replace_callback( + '/\[([A-Za-z0-9_-]+)(\W?=[\]]+)?\]/', + function( $matches ) use ( $selector, &$selectors_parsed ) { + $selectors_parsed[ $selector ]['attribute_names'][] = $matches[1]; + return ''; + }, + $reduced_selector + ); // Ignore any selector terms that occur under a dynamic selector. if ( $dynamic_selector_pattern ) { @@ -2611,6 +2621,13 @@ function( $id ) use ( $dom ) { || $this->has_used_tag_names( $parsed_selector['tags'] ) ) + && + // If all attribute names are used in the doc. + ( + empty( $parsed_selector['attribute_names'] ) + || + $this->has_used_attributes( $parsed_selector['attribute_names'] ) + ) ) ); if ( $should_include ) { From 62dbcc155b06c4177a7a4c2e5f393b41de59f1ac Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 7 Apr 2019 14:39:42 -0700 Subject: [PATCH 2/9] Reduce size stored parsed selectors by using constants for array indices --- .../sanitizers/class-amp-style-sanitizer.php | 67 +++++++++++++++---- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index 2df1788a42f..696060e9184 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -47,6 +47,42 @@ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer { */ const INLINE_SPECIFICITY_MULTIPLIER = 5; // @todo The correctness of using "5" should be validated. + /** + * Array index for tag names extracted from a selector. + * + * @private + * @since 1.1 + * @see \AMP_Style_Sanitizer::prepare_stylesheet() + */ + const SELECTOR_EXTRACTED_TAGS = 0; + + /** + * Array index for class names extracted from a selector. + * + * @private + * @since 1.1 + * @see \AMP_Style_Sanitizer::prepare_stylesheet() + */ + const SELECTOR_EXTRACTED_CLASSES = 1; + + /** + * Array index for IDs extracted from a selector. + * + * @private + * @since 1.1 + * @see \AMP_Style_Sanitizer::prepare_stylesheet() + */ + const SELECTOR_EXTRACTED_IDS = 2; + + /** + * Array index for attributes extracted from a selector. + * + * @private + * @since 1.1 + * @see \AMP_Style_Sanitizer::prepare_stylesheet() + */ + const SELECTOR_EXTRACTED_ATTRIBUTES = 3; + /** * Array of flags used to control sanitization. * @@ -1106,7 +1142,7 @@ private function fetch_external_stylesheet( $url ) { private function process_stylesheet( $stylesheet, $options = array() ) { $parsed = null; $cache_key = null; - $cache_group = 'amp-parsed-stylesheet-v15'; // This should be bumped whenever the PHP-CSS-Parser is updated. + $cache_group = 'amp-parsed-stylesheet-v16'; // This should be bumped whenever the PHP-CSS-Parser is updated or parsed format is updated. $cache_impacting_options = array_merge( wp_array_slice_assoc( @@ -1456,6 +1492,7 @@ function( $selector ) { $selectors = explode( $between_selectors . ',', $split_stylesheet[ ++$i ] ); $declaration = $split_stylesheet[ ++$i ]; + // @todo The following logic could be made much more robust of PHP-CSS-Parser did parsing of selectors. See . $selectors_parsed = array(); foreach ( $selectors as $selector ) { $selectors_parsed[ $selector ] = array(); @@ -1470,7 +1507,7 @@ function( $selector ) { $reduced_selector = preg_replace_callback( '/\[([A-Za-z0-9_-]+)(\W?=[\]]+)?\]/', function( $matches ) use ( $selector, &$selectors_parsed ) { - $selectors_parsed[ $selector ]['attribute_names'][] = $matches[1]; + $selectors_parsed[ $selector ][ self::SELECTOR_EXTRACTED_ATTRIBUTES ][] = $matches[1]; return ''; }, $reduced_selector @@ -1481,25 +1518,29 @@ function( $matches ) use ( $selector, &$selectors_parsed ) { $reduced_selector = preg_replace( '#((?:' . $dynamic_selector_pattern . ')(?:\.[a-z0-9_-]+)*)[^a-z0-9_-].*#si', '$1', $reduced_selector . ' ' ); } + // Extract class names. $reduced_selector = preg_replace_callback( '/\.([a-zA-Z0-9_-]+)/', function( $matches ) use ( $selector, &$selectors_parsed ) { - $selectors_parsed[ $selector ]['classes'][] = $matches[1]; + $selectors_parsed[ $selector ][ self::SELECTOR_EXTRACTED_CLASSES ][] = $matches[1]; return ''; }, $reduced_selector ); + + // Extract IDs. $reduced_selector = preg_replace_callback( '/#([a-zA-Z0-9_-]+)/', function( $matches ) use ( $selector, &$selectors_parsed ) { - $selectors_parsed[ $selector ]['ids'][] = $matches[1]; + $selectors_parsed[ $selector ][ self::SELECTOR_EXTRACTED_IDS ][] = $matches[1]; return ''; }, $reduced_selector ); + // Extract tag names. if ( preg_match_all( '/[a-zA-Z0-9_-]+/', $reduced_selector, $matches ) ) { - $selectors_parsed[ $selector ]['tags'] = $matches[0]; + $selectors_parsed[ $selector ][ self::SELECTOR_EXTRACTED_TAGS ] = $matches[0]; } } @@ -2596,18 +2637,18 @@ private function finalize_stylesheet_set( $stylesheet_set ) { ( // If all class names are used in the doc. ( - empty( $parsed_selector['classes'] ) + empty( $parsed_selector[ self::SELECTOR_EXTRACTED_CLASSES ] ) || - $this->has_used_class_name( $parsed_selector['classes'] ) + $this->has_used_class_name( $parsed_selector[ self::SELECTOR_EXTRACTED_CLASSES ] ) ) && // If all IDs are used in the doc. ( - empty( $parsed_selector['ids'] ) + empty( $parsed_selector[ self::SELECTOR_EXTRACTED_IDS ] ) || 0 === count( array_filter( - $parsed_selector['ids'], + $parsed_selector[ self::SELECTOR_EXTRACTED_IDS ], function( $id ) use ( $dom ) { return ! $dom->getElementById( $id ); } @@ -2617,16 +2658,16 @@ function( $id ) use ( $dom ) { && // If tag names are present in the doc. ( - empty( $parsed_selector['tags'] ) + empty( $parsed_selector[ self::SELECTOR_EXTRACTED_TAGS ] ) || - $this->has_used_tag_names( $parsed_selector['tags'] ) + $this->has_used_tag_names( $parsed_selector[ self::SELECTOR_EXTRACTED_TAGS ] ) ) && // If all attribute names are used in the doc. ( - empty( $parsed_selector['attribute_names'] ) + empty( $parsed_selector[ self::SELECTOR_EXTRACTED_ATTRIBUTES ] ) || - $this->has_used_attributes( $parsed_selector['attribute_names'] ) + $this->has_used_attributes( $parsed_selector[ self::SELECTOR_EXTRACTED_ATTRIBUTES ] ) ) ) ); From 1091b018f425a63c90d86ddd8b95b01b272df32f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 7 Apr 2019 15:30:32 -0700 Subject: [PATCH 3/9] Fix regex for attribute value, and allow colons in names; fix tests --- includes/sanitizers/class-amp-style-sanitizer.php | 2 +- tests/test-amp-style-sanitizer.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index 696060e9184..6eccbbe3443 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -1505,7 +1505,7 @@ function( $selector ) { * such as with `.social-navigation a[href*="example.com"]:before`. */ $reduced_selector = preg_replace_callback( - '/\[([A-Za-z0-9_-]+)(\W?=[\]]+)?\]/', + '/\[([A-Za-z0-9_:-]+)(\W?=[^\]]+)?\]/', function( $matches ) use ( $selector, &$selectors_parsed ) { $selectors_parsed[ $selector ][ self::SELECTOR_EXTRACTED_ATTRIBUTES ][] = $matches[1]; return ''; diff --git a/tests/test-amp-style-sanitizer.php b/tests/test-amp-style-sanitizer.php index 98ef72f9279..c781aa6b00a 100644 --- a/tests/test-amp-style-sanitizer.php +++ b/tests/test-amp-style-sanitizer.php @@ -318,7 +318,7 @@ public function get_link_and_style_test_data() { ) ), array( - 'form [submit-success] b,div[submit-failure] b{color:green}', + 'form [submit-success] b{color:green}', // The [submit-failure] selector is removed because there is no div[submit-failure]. 'amp-live-list li .highlighted{background:yellow}', '', 'body amp-list .portland{color:blue}', @@ -358,7 +358,7 @@ public function get_link_and_style_test_data() { array(), ), 'unamerican_lang_attribute_selectors_removed' => array( // USA is used for convenience here. No political statement intended. - 'Test', + 'Test', array( 'html[lang=en-US]{color:red}html[lang="en-US"]{color:white}html[lang^=en]{color:blue}', ), @@ -1045,7 +1045,7 @@ public function test_large_custom_css_and_rule_removal() { @media screen {} '; - $html .= '...'; + $html .= '...'; $dom = AMP_DOM_Utils::get_dom( $html ); $error_codes = array(); From e5fcb63b4641886652cf8b8910e4e328eb142e3d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 8 Apr 2019 13:43:09 -0700 Subject: [PATCH 4/9] Account for doubled-colons in pseudo classes; align tag extraction with other extractions --- .../sanitizers/class-amp-style-sanitizer.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index 6eccbbe3443..d457d17be2b 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -1498,7 +1498,7 @@ function( $selector ) { $selectors_parsed[ $selector ] = array(); // Remove :not() and pseudo selectors to eliminate false negatives, such as with `body:not(.title-tagline-hidden) .site-branding-text`. - $reduced_selector = preg_replace( '/:[a-zA-Z0-9_-]+(\(.+?\))?/', '', $selector ); + $reduced_selector = preg_replace( '/::?[a-zA-Z0-9_-]+(\(.+?\))?/', '', $selector ); /* * Gather attribute names while removing attribute selectors to eliminate false negative, @@ -1539,9 +1539,17 @@ function( $matches ) use ( $selector, &$selectors_parsed ) { ); // Extract tag names. - if ( preg_match_all( '/[a-zA-Z0-9_-]+/', $reduced_selector, $matches ) ) { - $selectors_parsed[ $selector ][ self::SELECTOR_EXTRACTED_TAGS ] = $matches[0]; - } + $reduced_selector = preg_replace_callback( + '/[a-zA-Z0-9_-]+/', + function( $matches ) use ( $selector, &$selectors_parsed ) { + $selectors_parsed[ $selector ][ self::SELECTOR_EXTRACTED_TAGS ][] = $matches[0]; + return ''; + }, + $reduced_selector + ); + + // At this point, $reduced_selector should contain just the remnants of the selector, primarily combinators. + unset( $reduced_selector ); } $stylesheet[] = array( From 506a75514ee002bd1f749d7bc8f552f10161dd39 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 8 Apr 2019 13:48:15 -0700 Subject: [PATCH 5/9] Add test for attribute_selectors --- tests/test-amp-style-sanitizer.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test-amp-style-sanitizer.php b/tests/test-amp-style-sanitizer.php index c781aa6b00a..688fca16b88 100644 --- a/tests/test-amp-style-sanitizer.php +++ b/tests/test-amp-style-sanitizer.php @@ -592,6 +592,12 @@ public function get_amp_selector_data() { 'div img.logo{border:solid 1px red}', '', // The selector is removed because there is no div element. ), + 'attribute_selectors' => array( + // @todo The [hidden] selector should not be removed even when the attribute is not present. + '
Top', + '[type="button"], [type="reset"], [type^="submit"] {color:red} a[href^=http]:after, a[href^="#"]:after { color:blue } [hidden] {display:none}#content[tabindex="-1"]:focus{ outline: solid 1px red; }', + '[type="button"],[type="reset"],[type^="submit"]{color:red}a[href^=http]:after,a[href^="#"]:after{color:blue}[hidden]{display:none}#content[tabindex="-1"]:focus{outline:solid 1px red}', // Any selector mentioning [type] or [href] will persist since value is not used for tree shaking. + ), 'playbuzz' => array( '

hello

', 'p + div.pb_feed{border:solid 1px blue}', From 13fcdbfd4c754bbc2cdc8937af419886ed066cf4 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 9 Apr 2019 08:59:10 -0700 Subject: [PATCH 6/9] Improve format of parsed cached CSS transient key, putting amp at start --- includes/sanitizers/class-amp-style-sanitizer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index d457d17be2b..e7f9fef57c3 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -1163,7 +1163,7 @@ private function process_stylesheet( $stylesheet, $options = array() ) { if ( wp_using_ext_object_cache() ) { $parsed = wp_cache_get( $cache_key, $cache_group ); } else { - $parsed = get_transient( $cache_key . $cache_group ); + $parsed = get_transient( $cache_group . '-' . $cache_key ); } /* @@ -1192,7 +1192,7 @@ private function process_stylesheet( $stylesheet, $options = array() ) { wp_cache_set( $cache_key, $parsed, $cache_group ); } else { // The expiration is to ensure transient doesn't stick around forever since no LRU flushing like with external object cache. - set_transient( $cache_key . $cache_group, $parsed, MONTH_IN_SECONDS ); + set_transient( $cache_group . '-' . $cache_key, $parsed, MONTH_IN_SECONDS ); } } From 7e84717e3a54eb97e25722d74a19d1c9d572df76 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 9 Apr 2019 09:02:01 -0700 Subject: [PATCH 7/9] Prevent tree-shaking for amp-bind-mutable boolean attributes; add tests --- .../sanitizers/class-amp-style-sanitizer.php | 17 ++- tests/test-amp-style-sanitizer.php | 121 +++++++++++++++++- 2 files changed, 133 insertions(+), 5 deletions(-) diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index e7f9fef57c3..6ba6b68fe95 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -205,10 +205,25 @@ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer { /** * Attributes used in the document. * + * This is initially populated with boolean attributes which can be mutated by AMP at runtime, + * since they can by dynamically added at any time. + * * @since 1.1 * @var array */ - private $used_attributes = array(); + private $used_attributes = array( + 'autofocus' => true, + 'checked' => true, + 'controls' => true, + 'disabled' => true, + 'hidden' => true, + 'loop' => true, + 'multiple' => true, + 'open' => true, + 'readonly' => true, + 'required' => true, + 'selected' => true, + ); /** * Tag names used in document. diff --git a/tests/test-amp-style-sanitizer.php b/tests/test-amp-style-sanitizer.php index 688fca16b88..cf8b8efb049 100644 --- a/tests/test-amp-style-sanitizer.php +++ b/tests/test-amp-style-sanitizer.php @@ -593,10 +593,9 @@ public function get_amp_selector_data() { '', // The selector is removed because there is no div element. ), 'attribute_selectors' => array( - // @todo The [hidden] selector should not be removed even when the attribute is not present. - '
Top', - '[type="button"], [type="reset"], [type^="submit"] {color:red} a[href^=http]:after, a[href^="#"]:after { color:blue } [hidden] {display:none}#content[tabindex="-1"]:focus{ outline: solid 1px red; }', - '[type="button"],[type="reset"],[type^="submit"]{color:red}a[href^=http]:after,a[href^="#"]:after{color:blue}[hidden]{display:none}#content[tabindex="-1"]:focus{outline:solid 1px red}', // Any selector mentioning [type] or [href] will persist since value is not used for tree shaking. + '
Top', + '[type="button"], [type="reset"], [type^="submit"] {color:red} a[href^=http]:after, a[href^="#"]:after { color:blue } span[hidden] {display:none}#content[tabindex="-1"]:focus{ outline: solid 1px red; }', + '[type="button"],[type="reset"],[type^="submit"]{color:red}a[href^=http]:after,a[href^="#"]:after{color:blue}span[hidden]{display:none}#content[tabindex="-1"]:focus{outline:solid 1px red}', // Any selector mentioning [type] or [href] will persist since value is not used for tree shaking. ), 'playbuzz' => array( '

hello

', @@ -661,6 +660,120 @@ public function test_amp_selector_conversion( $markup, $input, $output ) { $this->assertEquals( $output, $stylesheets[0] ); } + /** + * Provide data for attribute selector test. + * + * @return array Data. + */ + public function get_attribute_selector_data() { + return array( + 'type_attribute' => array( + '', + // All selectors remain because only the existence of the attribute is examined. + array( + '[type="button"]' => true, + '[type*="reset"]' => true, + '[type^="submit"]' => true, + '[type$="button"]' => true, + ), + ), + 'tabindex_attribute' => array( + '', + // The div[tabindex] is removed because there is no div. The span[tabindex^=2] remains because value is not considered. + array( + 'div[tabindex]' => false, + 'span[tabindex]' => true, + 'span[tabindex=-1]' => true, + 'span[tabindex^=2]' => true, + ), + ), + 'href_attribute' => array( + 'Foo', + array( + 'a[href^=http]:after' => true, + 'a[href^="#"]:after' => true, + ), + ), + 'hidden_attribute' => array( + 'not hidden', + // Only div[hidden] should be removed because there is no div element; the other [hidden] selectors remain because it can be dynamically added. + array( + 'span[hidden]' => true, + '[hidden]' => true, + 'div[hidden]' => false, + 'span:not([hidden])' => true, + ), + ), + 'selected_readonly_disabled_multiple_autofocus_required' => array( + '', + array( + '[autofocus]' => true, + '[checked]' => true, + '[disabled]' => true, + '[multiple]' => true, + '[readonly]' => true, + '[required]' => true, + '[selected]' => true, + ), + ), + 'open_attribute' => array( + '
MoreDetails
', + array( + '[open]' => true, + 'amp-lightbox[open]' => false, + 'details[open]' => true, + ), + ), + 'media_attributes' => array( + '', + array( + '[loop]' => true, + '[controls]' => true, + ), + ), + ); + } + + /** + * Test attribute selector tree shaking. + * + * @dataProvider get_attribute_selector_data + * + * @param string $markup Source HTML markup. + * @param array $selectors Mapping of selectors to whether they are expected. + */ + public function test_attribute_selector( $markup, $selectors ) { + $style = implode( + '', + array_map( + function ( $selector ) { + return sprintf( '%s{ color: red; }', $selector ); + }, + array_keys( $selectors ) + ) + ); + + $html = "$markup"; + $dom = AMP_DOM_Utils::get_dom( $html ); + + $sanitizer_classes = amp_get_content_sanitizers(); + $sanitizer_classes['AMP_Style_Sanitizer']['remove_unused_rules'] = 'always'; + + $sanitized = AMP_Content_Sanitizer::sanitize_document( + $dom, + $sanitizer_classes, + array( + 'use_document_element' => true, + ) + ); + + $stylesheets = array_values( $sanitized['stylesheets'] ); + + $actual_selectors = array_values( array_filter( preg_split( '/{.+?}/s', $stylesheets[0] ) ) ); + $expected_selectors = array_keys( array_filter( $selectors ) ); + $this->assertEqualSets( $expected_selectors, $actual_selectors ); + } + /** * Data for testing CSS hack removal. * From 4a78a2df53b00b9794b66406c9908d94a47f610b Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 9 Apr 2019 12:25:02 -0700 Subject: [PATCH 8/9] Move attribute extraction after dynamic selector has been handled --- includes/sanitizers/class-amp-style-sanitizer.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index 6ba6b68fe95..3ae10da5949 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -1515,6 +1515,11 @@ function( $selector ) { // Remove :not() and pseudo selectors to eliminate false negatives, such as with `body:not(.title-tagline-hidden) .site-branding-text`. $reduced_selector = preg_replace( '/::?[a-zA-Z0-9_-]+(\(.+?\))?/', '', $selector ); + // Ignore any selector terms that occur under a dynamic selector. + if ( $dynamic_selector_pattern ) { + $reduced_selector = preg_replace( '#((?:' . $dynamic_selector_pattern . ')(?:\.[a-z0-9_-]+)*)[^a-z0-9_-].*#si', '$1', $reduced_selector . ' ' ); + } + /* * Gather attribute names while removing attribute selectors to eliminate false negative, * such as with `.social-navigation a[href*="example.com"]:before`. @@ -1528,11 +1533,6 @@ function( $matches ) use ( $selector, &$selectors_parsed ) { $reduced_selector ); - // Ignore any selector terms that occur under a dynamic selector. - if ( $dynamic_selector_pattern ) { - $reduced_selector = preg_replace( '#((?:' . $dynamic_selector_pattern . ')(?:\.[a-z0-9_-]+)*)[^a-z0-9_-].*#si', '$1', $reduced_selector . ' ' ); - } - // Extract class names. $reduced_selector = preg_replace_callback( '/\.([a-zA-Z0-9_-]+)/', From ccf80c22e4d8984da4cf64fbff23a973a89996d4 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 11 Apr 2019 14:51:50 -0700 Subject: [PATCH 9/9] Fix typo in command and add link to CssSelector issue --- includes/sanitizers/class-amp-style-sanitizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index 3ae10da5949..f9da47b754d 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -1507,7 +1507,7 @@ function( $selector ) { $selectors = explode( $between_selectors . ',', $split_stylesheet[ ++$i ] ); $declaration = $split_stylesheet[ ++$i ]; - // @todo The following logic could be made much more robust of PHP-CSS-Parser did parsing of selectors. See . + // @todo The following logic could be made much more robust if PHP-CSS-Parser did parsing of selectors. See and . $selectors_parsed = array(); foreach ( $selectors as $selector ) { $selectors_parsed[ $selector ] = array();