@@ -381,15 +381,6 @@ public static function get_css_parser_validation_error_codes() {
381381 * @return bool Returns true if the plugin's forked version of PHP-CSS-Parser is loaded by Composer.
382382 */
383383 public static function has_required_php_css_parser () {
384- $ has_required_methods = (
385- method_exists ( 'Sabberworm\CSS\CSSList\Document ' , 'splice ' )
386- &&
387- method_exists ( 'Sabberworm\CSS\CSSList\Document ' , 'replace ' )
388- );
389- if ( ! $ has_required_methods ) {
390- return false ;
391- }
392-
393384 $ reflection = new ReflectionClass ( 'Sabberworm\CSS\OutputFormat ' );
394385
395386 $ has_output_format_extensions = (
@@ -1570,7 +1561,7 @@ private function get_parsed_stylesheet( $stylesheet, $options = [] ) {
15701561 $ parsed = null ;
15711562 $ cache_key = null ;
15721563 $ cached = true ;
1573- $ cache_group = 'amp-parsed-stylesheet-v33 ' ; // This should be bumped whenever the PHP-CSS-Parser is updated or parsed format is updated.
1564+ $ cache_group = 'amp-parsed-stylesheet-v34 ' ; // This should be bumped whenever the PHP-CSS-Parser is updated or parsed format is updated.
15741565 $ use_transients = $ this ->should_use_transient_caching ();
15751566
15761567 $ cache_impacting_options = array_merge (
@@ -1743,25 +1734,48 @@ private function splice_imported_stylesheet( Import $item, CSSList $css_list, $o
17431734 );
17441735 $ viewport_rules = $ parsed_stylesheet ['viewport_rules ' ];
17451736
1746- if ( ! empty ( $ parsed_stylesheet ['css_document ' ] ) && method_exists ( $ css_list , ' replace ' ) ) {
1737+ if ( ! empty ( $ parsed_stylesheet ['css_document ' ] ) ) {
17471738 /**
17481739 * CSS Doc.
17491740 *
17501741 * @var CSSDocument $css_document
17511742 */
17521743 $ css_document = $ parsed_stylesheet ['css_document ' ];
17531744
1754- // Work around bug in \Sabberworm\CSS\CSSList\CSSList::replace() when array keys are not 0-based.
1755- $ css_list ->setContents ( array_values ( $ css_list ->getContents () ) );
1756-
1757- $ css_list ->replace ( $ item , $ css_document ->getContents () );
1745+ $ this ->replace_inside_css_list ( $ css_list , $ item , $ css_document ->getContents () );
17581746 } else {
17591747 $ css_list ->remove ( $ item );
17601748 }
17611749
17621750 return compact ( 'validation_results ' , 'imported_font_urls ' , 'viewport_rules ' );
17631751 }
17641752
1753+ /**
1754+ * Replace an item inside of a CSSList.
1755+ *
1756+ * This is being used instead of `CSSList::splice()` because it uses `array_splice()` which does not work properly
1757+ * if the array keys are not sequentially indexed from 0, which happens when `CSSList::remove()` is employed.
1758+ *
1759+ * @see CSSList::splice()
1760+ * @see CSSList::replace()
1761+ * @see CSSList::remove()
1762+ *
1763+ * @param CSSList $css_list CSS list.
1764+ * @param AtRule|RuleSet|CSSList $old_item Old item.
1765+ * @param AtRule[]|RuleSet[]|CSSList[] $new_items New item(s). If empty, the old item is simply removed.
1766+ * @return bool Whether the replacement was successful.
1767+ */
1768+ private function replace_inside_css_list ( CSSList $ css_list , $ old_item , $ new_items = [] ) {
1769+ $ contents = array_values ( $ css_list ->getContents () ); // Required to obtain the offset instead of the index.
1770+ $ offset = array_search ( $ old_item , $ contents , true );
1771+ if ( false !== $ offset ) {
1772+ array_splice ( $ contents , $ offset , 1 , $ new_items );
1773+ $ css_list ->setContents ( $ contents );
1774+ return true ;
1775+ }
1776+ return false ;
1777+ }
1778+
17651779 /**
17661780 * Create validated CSS document.
17671781 *
@@ -2138,7 +2152,24 @@ private function process_css_list( CSSList $css_list, $options ) {
21382152 $ this ->process_css_declaration_block ( $ css_item , $ css_list , $ options )
21392153 );
21402154 } elseif ( $ css_item instanceof AtRuleBlockList ) {
2141- if ( ! in_array ( $ css_item ->atRuleName (), $ options ['allowed_at_rules ' ], true ) ) {
2155+ if (
2156+ '-moz-document ' === $ css_item ->atRuleName ()
2157+ &&
2158+ 'url-prefix() ' === $ css_item ->atRuleArgs ()
2159+ &&
2160+ in_array ( 'supports ' , $ options ['allowed_at_rules ' ], true )
2161+ ) {
2162+ // Replace `@-moz-document url-prefix()` with `@supports (-moz-appearance:meterbar)` as an alternative
2163+ // way to provide Firefox-specific style rules. This is a workaround since @-moz-document is not
2164+ // yet allowed in AMP, and this use of @supports is another recognized Firefox-specific CSS hack,
2165+ // per <http://browserhacks.com/#hack-8e9b5504d9fda44ec75169381b3c3157>.
2166+ // For adding @-moz-document to AMP, see <https://github.com/ampproject/amphtml/issues/26406>.
2167+ $ new_css_item = new AtRuleBlockList ( 'supports ' , '(-moz-appearance:meterbar) ' );
2168+ $ new_css_item ->setContents ( $ css_item ->getContents () );
2169+ $ this ->replace_inside_css_list ( $ css_list , $ css_item , [ $ new_css_item ] );
2170+ $ css_item = $ new_css_item ; // To process_css_list below.
2171+ $ sanitized = false ;
2172+ } elseif ( ! in_array ( $ css_item ->atRuleName (), $ options ['allowed_at_rules ' ], true ) ) {
21422173 $ error = [
21432174 'code ' => self ::CSS_SYNTAX_INVALID_AT_RULE ,
21442175 'at_rule ' => $ css_item ->atRuleName (),
@@ -2702,13 +2733,12 @@ static function( Selector $old_selector ) {
27022733 );
27032734 $ important_ruleset ->setRules ( $ importants );
27042735
2705- $ i = array_search ( $ ruleset , $ css_list ->getContents (), true );
2706- if ( false !== $ i && method_exists ( $ css_list , ' splice ' ) ) {
2707- $ css_list -> splice ( $ i + 1 , 0 , [ $ important_ruleset ] );
2708- } else {
2709- $ css_list ->append ( $ important_ruleset );
2736+ $ contents = array_values ( $ css_list ->getContents () ); // Ensure keys are 0-indexed and sequential.
2737+ $ offset = array_search ( $ ruleset , $ contents , true );
2738+ if ( false !== $ offset ) {
2739+ array_splice ( $ contents , $ offset + 1 , 0 , [ $ important_ruleset ] );
2740+ $ css_list ->setContents ( $ contents );
27102741 }
2711-
27122742 return $ results ;
27132743 }
27142744
0 commit comments