PluginProbe
Autoptimize / trunk
Autoptimize vtrunk
3.1.16 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 All 108 releases
← All changes | classes/autoptimizeStyles.php +79 -20 2.7.6 → trunk View file →
@@ -163,14 +163,13 @@
163 163 */
164 164 public function read( $options )
165 165 {
166 166 $noptimize_css = apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content );
167 - if ( $noptimize_css ) {
167 + if ( $noptimize_css || false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_css_optimize' ) ) {
168 168 return false;
169 169 }
170 170
171 171 $allowlist_css = apply_filters( 'autoptimize_filter_css_allowlist', '', $this->content );
172 - $allowlist_css = apply_filters( 'autoptimize_filter_css_whitelist', $allowlist_css, $this->content ); // fixme: to be removed in next version.
173 172 if ( ! empty( $allowlist_css ) ) {
174 173 $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_css ) ) );
175 174 }
176 175
@@ -198,8 +197,10 @@
198 197 // Returning true for "dontaggregate" turns off aggregation.
199 198 if ( $this->aggregate && apply_filters( 'autoptimize_filter_css_dontaggregate', false ) ) {
200 199 $this->aggregate = false;
201 200 }
201 + // and the filter that should have been there to begin with.
202 + $this->aggregate = apply_filters( 'autoptimize_filter_css_aggregate', $this->aggregate );
202 203
203 204 // include inline?
204 205 if ( apply_filters( 'autoptimize_css_include_inline', $options['include_inline'] ) ) {
205 206 $this->include_inline = true;
@@ -215,16 +216,24 @@
215 216
216 217 // forcefully exclude CSS with data-noptimize attrib.
217 218 $this->dontmove[] = 'data-noptimize';
218 219
220 + // forcefully exclude inline CSS with ".wp-container-" which due to the random-ish nature busts AO's cache continuously.
221 + $this->dontmove[] = '.wp-container-';
222 +
219 223 // Should we defer css?
220 224 // value: true / false.
221 225 $this->defer = $options['defer'];
222 226 $this->defer = apply_filters( 'autoptimize_filter_css_defer', $this->defer, $this->content );
223 227
228 + // If page/ post check post_meta to see if optimize is off.
229 + if ( $this->defer && false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_ccss' ) ) {
230 + $this->defer = false;
231 + }
232 +
224 233 // Should we inline while deferring?
225 234 // value: inlined CSS.
226 - $this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $options['defer_inline'], $this->content );
235 + $this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $this->sanitize_css( $options['defer_inline'] ), $this->content );
227 236
228 237 // Should we inline?
229 238 // value: true / false.
230 239 $this->inline = $options['inline'];
@@ -271,16 +280,20 @@
271 280 } elseif ( $this->ismovable( $tag ) ) {
272 281 // Get the media.
273 282 if ( false !== strpos( $tag, 'media=' ) ) {
274 283 preg_match( '#media=(?:"|\')([^>]*)(?:"|\')#Ui', $tag, $medias );
275 - $medias = explode( ',', $medias[1] );
276 - $media = array();
277 - foreach ( $medias as $elem ) {
278 - if ( empty( $elem ) ) {
279 - $elem = 'all';
284 + if ( ! empty( $medias ) ) {
285 + $medias = explode( ',', $medias[1] );
286 + $media = array();
287 + foreach ( $medias as $elem ) {
288 + if ( empty( $elem ) ) {
289 + $elem = 'all';
290 + }
291 +
292 + $media[] = $elem;
280 293 }
281 -
282 - $media[] = $elem;
294 + } else {
295 + $media = array( 'all' );
283 296 }
284 297 } else {
285 298 // No media specified - applies to all.
286 299 $media = array( 'all' );
@@ -354,8 +367,13 @@
354 367
355 368 if ( '' !== $new_tag ) {
356 369 // Optionally defer (preload) non-aggregated CSS.
357 370 $new_tag = $this->optionally_defer_excluded( $new_tag, $url );
371 +
372 + // Check if we still need to CDN (esp. for already minified resources).
373 + if ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) {
374 + $new_tag = str_replace( $url, $this->url_replace_cdn( $url ), $new_tag );
375 + }
358 376 }
359 377
360 378 // And replace!
361 379 if ( ( '' !== $new_tag && $new_tag !== $tag ) || ( '' === $new_tag && apply_filters( 'autoptimize_filter_css_remove_empty_files', false ) ) ) {
@@ -472,9 +490,9 @@
472 490
473 491 private function check_datauri_exclude_list( $url )
474 492 {
475 493 static $exclude_list = null;
476 - $no_datauris = array();
494 + static $no_datauris = array();
477 495
478 496 // Again, skip doing certain stuff repeatedly when loop-called.
479 497 if ( null === $exclude_list ) {
480 498 $exclude_list = apply_filters( 'autoptimize_filter_css_datauri_exclude', '' );
@@ -620,9 +638,11 @@
620 638 if ( ! empty( $this->cdn_url ) ) {
621 639 $replacement_url = $this->url_replace_cdn( $url );
622 640 // Prepare replacements array.
623 641 $replacements[ $url_src_matches[1][ $count ] ] = str_replace(
624 - $original_url, $replacement_url, $url_src_matches[1][ $count ]
642 + $original_url,
643 + $replacement_url,
644 + $url_src_matches[1][ $count ]
625 645 );
626 646 }
627 647 }
628 648 }
@@ -746,9 +766,11 @@
746 766 if ( ! $inlined && ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) ) {
747 767 // Just do the "simple" CDN replacement.
748 768 $replacement_url = $this->url_replace_cdn( $url );
749 769 $imgreplace[ $url_src_matches[1][ $count ] ] = str_replace(
750 - $original_url, $replacement_url, $url_src_matches[1][ $count ]
770 + $original_url,
771 + $replacement_url,
772 + $url_src_matches[1][ $count ]
751 773 );
752 774 }
753 775 }
754 776 }
@@ -854,9 +876,9 @@
854 876 $code = self::build_injectlater_marker( $path, md5( $code ) );
855 877 }
856 878
857 879 if ( ! empty( $code ) ) {
858 - $tmp_thiscss = preg_replace( '#(/\*FILESTART\*/.*)' . preg_quote( $import, '#' ) . '#Us', '/*FILESTART2*/' . $code . '$1', $thiscss );
880 + $tmp_thiscss = str_replace( $import, stripcslashes( $code ), $thiscss );
859 881 if ( ! empty( $tmp_thiscss ) ) {
860 882 $thiscss = $tmp_thiscss;
861 883 $import_ok = true;
862 884 unset( $tmp_thiscss );
@@ -980,10 +1002,20 @@
980 1002 if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
981 1003 $type_css = 'type="text/css" ';
982 1004 }
983 1005
984 - // Inject the new stylesheets.
985 - $replace_tag = array( '<title', 'before' );
1006 + // Inject the new stylesheets, if possible after SEO stuff, but we need to
1007 + // already restore script to be able to inject before ld+json instead of title
1008 + // this should be safe here as all has been extracted already but behind a filter anyway.
1009 + if ( $this->inline && true === apply_filters( 'autoptimize_filter_css_restore_js_early', true ) ) {
1010 + $this->content = $this->restore_marked_content( 'SCRIPT', $this->content );
1011 + }
1012 + $_strpos_ldjson = strpos( $this->content, '<script type="application/ld+json"' );
1013 + if ( false !== $_strpos_ldjson && $_strpos_ldjson < strpos( $this->content, '</head' ) ) {
1014 + $replace_tag = array( '<script type="application/ld+json"', 'before' );
1015 + } else {
1016 + $replace_tag = array( '<title', 'before' );
1017 + }
986 1018 $replace_tag = apply_filters( 'autoptimize_filter_css_replacetag', $replace_tag, $this->content );
987 1019
988 1020 if ( $this->inline ) {
989 1021 foreach ( $this->csscode as $media => $code ) {
@@ -1026,16 +1058,16 @@
1026 1058 // Add the stylesheet either deferred (import at bottom) or normal links in head.
1027 1059 if ( $this->defer && 'print' !== $media ) {
1028 1060 $preload_onload = autoptimizeConfig::get_ao_css_preload_onload( $media );
1029 1061
1030 - $preload_css_block .= '<link rel="stylesheet" media="print" href="' . $url . '" onload="' . $preload_onload . '" />';
1031 - if ( apply_filters( 'autoptimize_fitler_css_preload_and_print', false ) ) {
1062 + $preload_css_block .= apply_filters( 'autoptimize_filter_css_single_deferred_link', '<link rel="stylesheet" media="print" href="' . $url . '" onload="' . $preload_onload . '">' );
1063 + if ( apply_filters( 'autoptimize_filter_css_preload_and_print', false ) ) {
1032 1064 $preload_css_block = '<link rel="preload" as="stylesheet" href="' . $url . '"/>' . $preload_css_block;
1033 1065 }
1034 - $noscript_css_block .= '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />';
1066 + $noscript_css_block .= '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet">';
1035 1067 } else {
1036 1068 if ( strlen( $this->csscode[ $media ] ) > $this->cssinlinesize ) {
1037 - $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />' ), $replace_tag );
1069 + $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet">' ), $replace_tag );
1038 1070 } elseif ( strlen( $this->csscode[ $media ] ) > 0 ) {
1039 1071 $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<style ' . $type_css . 'media="' . $media . '">' . $this->csscode[ $media ] . '</style>' ), $replace_tag );
1040 1072 }
1041 1073 }
@@ -1207,8 +1239,15 @@
1207 1239 {
1208 1240 $contents = $this->prepare_minify_single( $filepath );
1209 1241
1210 1242 if ( empty( $contents ) ) {
1243 + // if aggregate is off and CCSS is used but all files are minified already, then we
1244 + // must make sure the autoptimize_action_css_hash action still fires for CCSS's sake.
1245 + $ao_ccss_key = get_option( 'autoptimize_ccss_key', '' );
1246 + if ( false === $this->aggregate && isset( $ao_ccss_key ) && ! empty( $ao_ccss_key ) ) {
1247 + $hash = 'single_' . md5( file_get_contents( $filepath ) );
1248 + do_action( 'autoptimize_action_css_hash', $hash );
1249 + }
1211 1250 return false;
1212 1251 }
1213 1252
1214 1253 // Check cache.
@@ -1273,6 +1312,26 @@
1273 1312
1274 1313 public function getOption( $name )
1275 1314 {
1276 1315 return $this->options[ $name ];
1316 + }
1317 +
1318 + /**
1319 + * Sanitize user-provided CSS.
1320 + *
1321 + * For now just strip_tags (the WordPress way) and preg_replace to escape < in certain cases but might do full CSS escaping in the future, see:
1322 + * https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-4-css-encode-and-strictly-validate-before-inserting-untrusted-data-into-html-style-property-values
1323 + * https://github.com/twigphp/Twig/blob/3.x/src/Extension/EscaperExtension.php#L300-L319
1324 + * https://github.com/laminas/laminas-escaper/blob/2.8.x/src/Escaper.php#L205-L221
1325 + *
1326 + * @param string $css the to be sanitized CSS.
1327 + * @return string sanitized CSS.
1328 + */
1329 + public static function sanitize_css( $css )
1330 + {
1331 + $css = wp_strip_all_tags( $css );
1332 + if ( strpos( $css, '<' ) !== false ) {
1333 + $css = preg_replace( '#<(\/?\w+)#', '\00003C$1', $css );
1334 + }
1335 + return $css;
1277 1336 }
1278 1337 }