Autoptimize - Version 2.7.7

Version Description

  • critical CSS: make sure pages get a path-based rule even if a CPT or template matches (when "path based rules for pages" option is on)
  • critical CSS: make sure the "unload CCSS javascript" is only added once
  • settings screens: switch jQuery .attr() to .prop() as suggested by jQuery Migrate to prepare for the great oncoming big jQuery updates
  • HTML minify: reverse placeholder array to make sure last replaced placeholder is changed back first to fix rare issues
  • security fix: kudos to Erin Germ for finding & reporting an authenticated XSS vulnerability
  • security fix: props to an anonymous pentester for finding & reporting an authenticated malicous file upload vulnerability
Download this release

Release Info

Developer futtta
Plugin Icon 128x128 Autoptimize
Version 2.7.7
Comparing to
See all releases

Code changes from version 2.7.6 to 2.7.7

autoptimize.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Autoptimize
4
  * Plugin URI: https://autoptimize.com/
5
  * Description: Makes your site faster by optimizing CSS, JS, Images, Google fonts and more.
6
- * Version: 2.7.6
7
  * Author: Frank Goossens (futtta)
8
  * Author URI: https://autoptimize.com/
9
  * Text Domain: autoptimize
@@ -21,7 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) {
21
  exit;
22
  }
23
 
24
- define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.7.6' );
25
 
26
  // plugin_dir_path() returns the trailing slash!
27
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
3
  * Plugin Name: Autoptimize
4
  * Plugin URI: https://autoptimize.com/
5
  * Description: Makes your site faster by optimizing CSS, JS, Images, Google fonts and more.
6
+ * Version: 2.7.7
7
  * Author: Frank Goossens (futtta)
8
  * Author URI: https://autoptimize.com/
9
  * Text Domain: autoptimize
21
  exit;
22
  }
23
 
24
+ define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.7.7' );
25
 
26
  // plugin_dir_path() returns the trailing slash!
27
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
classes/autoptimizeCache.php CHANGED
@@ -669,6 +669,11 @@ class autoptimizeCache
669
  $js_or_css = pathinfo( $original_request, PATHINFO_EXTENSION );
670
  $fallback_path = AUTOPTIMIZE_CACHE_DIR . $js_or_css . '/autoptimize_fallback.' . $js_or_css;
671
 
 
 
 
 
 
672
  // set fallback URL.
673
  $fallback_target = preg_replace( '/(.*)_(?:[a-z0-9]{32})\.(js|css)$/', '${1}_fallback.${2}', $original_request );
674
 
@@ -756,6 +761,8 @@ class autoptimizeCache
756
  w3tc_pgcache_flush();
757
  } elseif ( function_exists( 'wp_fast_cache_bulk_delete_all' ) ) {
758
  wp_fast_cache_bulk_delete_all();
 
 
759
  } elseif ( class_exists( 'WpFastestCache' ) ) {
760
  $wpfc = new WpFastestCache();
761
  $wpfc->deleteCache();
669
  $js_or_css = pathinfo( $original_request, PATHINFO_EXTENSION );
670
  $fallback_path = AUTOPTIMIZE_CACHE_DIR . $js_or_css . '/autoptimize_fallback.' . $js_or_css;
671
 
672
+ // prepare for Shakeeb's Unused CSS files to be 404-handled as well.
673
+ if ( strpos( $original_request, 'uucss/uucss-' ) !== false ) {
674
+ $original_request = preg_replace( '/uucss\/uucss-[a-z0-9]{32}-/', 'css/', $original_request );
675
+ }
676
+
677
  // set fallback URL.
678
  $fallback_target = preg_replace( '/(.*)_(?:[a-z0-9]{32})\.(js|css)$/', '${1}_fallback.${2}', $original_request );
679
 
761
  w3tc_pgcache_flush();
762
  } elseif ( function_exists( 'wp_fast_cache_bulk_delete_all' ) ) {
763
  wp_fast_cache_bulk_delete_all();
764
+ } elseif ( class_exists( 'Swift_Performance_Cache' ) ) {
765
+ Swift_Performance_Cache::clear_all_cache();
766
  } elseif ( class_exists( 'WpFastestCache' ) ) {
767
  $wpfc = new WpFastestCache();
768
  $wpfc->deleteCache();
classes/autoptimizeConfig.php CHANGED
@@ -96,7 +96,7 @@ class autoptimizeConfig
96
  public function show_config()
97
  {
98
  $conf = self::instance();
99
- ?>
100
  <style>
101
  /* title and button */
102
  #ao_title_and_button:after {content:''; display:block; clear:both;}
@@ -250,7 +250,7 @@ if ( is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network()
250
  <?php } ?>
251
  <tr valign="top" class="js_sub">
252
  <th scope="row"><?php _e( 'Exclude scripts from Autoptimize:', 'autoptimize' ); ?></th>
253
- <td><label><input type="text" style="width:100%;" name="autoptimize_js_exclude" value="<?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_exclude', 'wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.js' ); ?>"/><br />
254
  <?php
255
  echo __( 'A comma-separated list of scripts you want to exclude from being optimized, for example \'whatever.js, another.js\' (without the quotes) to exclude those scripts from being aggregated by Autoptimize.', 'autoptimize' ) . ' ' . __( 'Important: excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
256
  ?>
@@ -321,7 +321,7 @@ echo sprintf( __( 'This can be fully automated for different types of pages on t
321
  </tr>
322
  <tr valign="top" class="css_sub">
323
  <th scope="row"><?php _e( 'Exclude CSS from Autoptimize:', 'autoptimize' ); ?></th>
324
- <td><label><input type="text" style="width:100%;" name="autoptimize_css_exclude" value="<?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_exclude', 'wp-content/cache/, wp-content/uploads/, admin-bar.min.css, dashicons.min.css' ); ?>"/><br />
325
  <?php
326
  echo __( 'A comma-separated list of CSS you want to exclude from being optimized.', 'autoptimize' ) . ' ' . __( 'Important: excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
327
  ?>
@@ -519,7 +519,7 @@ echo __( 'A comma-separated list of CSS you want to exclude from being optimized
519
  });
520
 
521
  jQuery( "#autoptimize_js_aggregate" ).change(function() {
522
- if (this.checked && jQuery("#autoptimize_js").attr('checked')) {
523
  jQuery(".js_aggregate:visible").fadeTo("fast",1);
524
  jQuery( "#min_excl_row" ).show();
525
  } else {
@@ -539,7 +539,7 @@ echo __( 'A comma-separated list of CSS you want to exclude from being optimized
539
  });
540
 
541
  jQuery( "#autoptimize_css_aggregate" ).change(function() {
542
- if (this.checked && jQuery("#autoptimize_css").attr('checked')) {
543
  jQuery(".css_aggregate:visible").fadeTo("fast",1);
544
  jQuery( "#min_excl_row" ).show();
545
  } else {
@@ -593,25 +593,25 @@ echo __( 'A comma-separated list of CSS you want to exclude from being optimized
593
  }
594
 
595
  function check_ini_state() {
596
- if (!jQuery("#autoptimize_css_defer").attr('checked')) {
597
  jQuery("#autoptimize_css_defer_inline").hide();
598
  }
599
- if (!jQuery("#autoptimize_html").attr('checked')) {
600
  jQuery(".html_sub:visible").fadeTo('fast',.33);
601
  }
602
- if (!jQuery("#autoptimize_css").attr('checked')) {
603
  jQuery(".css_sub:visible").fadeTo('fast',.33);
604
  }
605
- if (!jQuery("#autoptimize_css_aggregate").attr('checked')) {
606
  jQuery(".css_aggregate:visible").fadeTo('fast',.33);
607
  }
608
- if (!jQuery("#autoptimize_js").attr('checked')) {
609
  jQuery(".js_sub:visible").fadeTo('fast',.33);
610
  }
611
- if (!jQuery("#autoptimize_js_aggregate").attr('checked')) {
612
  jQuery(".js_aggregate:visible").fadeTo('fast',.33);
613
  }
614
- if (jQuery("#autoptimize_enable_site_config").attr('checked')) {
615
  jQuery("li.itemDetail:not(.multiSite)").fadeTo('fast',.33);
616
  }
617
  }
96
  public function show_config()
97
  {
98
  $conf = self::instance();
99
+ ?>
100
  <style>
101
  /* title and button */
102
  #ao_title_and_button:after {content:''; display:block; clear:both;}
250
  <?php } ?>
251
  <tr valign="top" class="js_sub">
252
  <th scope="row"><?php _e( 'Exclude scripts from Autoptimize:', 'autoptimize' ); ?></th>
253
+ <td><label><input type="text" style="width:100%;" name="autoptimize_js_exclude" value="<?php echo esc_html( autoptimizeOptionWrapper::get_option( 'autoptimize_js_exclude', 'wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.js' ) ); ?>"/><br />
254
  <?php
255
  echo __( 'A comma-separated list of scripts you want to exclude from being optimized, for example \'whatever.js, another.js\' (without the quotes) to exclude those scripts from being aggregated by Autoptimize.', 'autoptimize' ) . ' ' . __( 'Important: excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
256
  ?>
321
  </tr>
322
  <tr valign="top" class="css_sub">
323
  <th scope="row"><?php _e( 'Exclude CSS from Autoptimize:', 'autoptimize' ); ?></th>
324
+ <td><label><input type="text" style="width:100%;" name="autoptimize_css_exclude" value="<?php echo esc_html( autoptimizeOptionWrapper::get_option( 'autoptimize_css_exclude', 'wp-content/cache/, wp-content/uploads/, admin-bar.min.css, dashicons.min.css' ) ); ?>"/><br />
325
  <?php
326
  echo __( 'A comma-separated list of CSS you want to exclude from being optimized.', 'autoptimize' ) . ' ' . __( 'Important: excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
327
  ?>
519
  });
520
 
521
  jQuery( "#autoptimize_js_aggregate" ).change(function() {
522
+ if (this.checked && jQuery("#autoptimize_js").prop('checked')) {
523
  jQuery(".js_aggregate:visible").fadeTo("fast",1);
524
  jQuery( "#min_excl_row" ).show();
525
  } else {
539
  });
540
 
541
  jQuery( "#autoptimize_css_aggregate" ).change(function() {
542
+ if (this.checked && jQuery("#autoptimize_css").prop('checked')) {
543
  jQuery(".css_aggregate:visible").fadeTo("fast",1);
544
  jQuery( "#min_excl_row" ).show();
545
  } else {
593
  }
594
 
595
  function check_ini_state() {
596
+ if (!jQuery("#autoptimize_css_defer").prop('checked')) {
597
  jQuery("#autoptimize_css_defer_inline").hide();
598
  }
599
+ if (!jQuery("#autoptimize_html").prop('checked')) {
600
  jQuery(".html_sub:visible").fadeTo('fast',.33);
601
  }
602
+ if (!jQuery("#autoptimize_css").prop('checked')) {
603
  jQuery(".css_sub:visible").fadeTo('fast',.33);
604
  }
605
+ if (!jQuery("#autoptimize_css_aggregate").prop('checked')) {
606
  jQuery(".css_aggregate:visible").fadeTo('fast',.33);
607
  }
608
+ if (!jQuery("#autoptimize_js").prop('checked')) {
609
  jQuery(".js_sub:visible").fadeTo('fast',.33);
610
  }
611
+ if (!jQuery("#autoptimize_js_aggregate").prop('checked')) {
612
  jQuery(".js_aggregate:visible").fadeTo('fast',.33);
613
  }
614
+ if (jQuery("#autoptimize_enable_site_config").prop('checked')) {
615
  jQuery("li.itemDetail:not(.multiSite)").fadeTo('fast',.33);
616
  }
617
  }
classes/autoptimizeCriticalCSSCore.php CHANGED
@@ -195,6 +195,10 @@ class autoptimizeCriticalCSSCore {
195
  // set media attrib of inline CCSS to none at onLoad to avoid it impacting full CSS (rarely needed).
196
  $_unloadccss_js = apply_filters( 'autoptimize_filter_ccss_core_unloadccss_js', '<script>window.addEventListener("load", function(event) {document.getElementById("aoatfcss").media="none";})</script>' );
197
 
 
 
 
 
198
  return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
199
  }
200
 
195
  // set media attrib of inline CCSS to none at onLoad to avoid it impacting full CSS (rarely needed).
196
  $_unloadccss_js = apply_filters( 'autoptimize_filter_ccss_core_unloadccss_js', '<script>window.addEventListener("load", function(event) {document.getElementById("aoatfcss").media="none";})</script>' );
197
 
198
+ if ( false !== strpos( $html_in, $_unloadccss_js . '</body>' ) ) {
199
+ return $html_in;
200
+ }
201
+
202
  return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
203
  }
204
 
classes/autoptimizeCriticalCSSEnqueue.php CHANGED
@@ -189,6 +189,7 @@ class autoptimizeCriticalCSSEnqueue {
189
  // Get the type of a page
190
  // Attach the conditional tags array.
191
  global $ao_ccss_types;
 
192
 
193
  // By default, a page type is false.
194
  $page_type = false;
@@ -198,14 +199,14 @@ class autoptimizeCriticalCSSEnqueue {
198
  if ( is_404() ) {
199
  $page_type = 'is_404';
200
  break;
201
- } elseif ( strpos( $type, 'custom_post_' ) !== false ) {
202
- // Match custom post types.
203
  if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
204
  $page_type = $type;
205
  break;
206
  }
207
- } elseif ( strpos( $type, 'template_' ) !== false ) {
208
- // Match templates.
209
  if ( is_page_template( substr( $type, 9 ) ) ) {
210
  $page_type = $type;
211
  break;
189
  // Get the type of a page
190
  // Attach the conditional tags array.
191
  global $ao_ccss_types;
192
+ global $ao_ccss_forcepath;
193
 
194
  // By default, a page type is false.
195
  $page_type = false;
199
  if ( is_404() ) {
200
  $page_type = 'is_404';
201
  break;
202
+ } elseif ( strpos( $type, 'custom_post_' ) !== false && ( ! $ao_ccss_forcepath || ! is_page() ) ) {
203
+ // Match custom post types and not page or page not forced to path-based.
204
  if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
205
  $page_type = $type;
206
  break;
207
  }
208
+ } elseif ( strpos( $type, 'template_' ) !== false && ( ! $ao_ccss_forcepath || ! is_page() ) ) {
209
+ // Match templates if not page or if page is not forced to path-based.
210
  if ( is_page_template( substr( $type, 9 ) ) ) {
211
  $page_type = $type;
212
  break;
classes/autoptimizeCriticalCSSSettingsAjax.php CHANGED
@@ -281,7 +281,7 @@ class autoptimizeCriticalCSSSettingsAjax {
281
  $error = false;
282
 
283
  // Process an uploaded file with no errors.
284
- if ( ! $_FILES['file']['error'] ) {
285
  // Save file to the cache directory.
286
  $zipfile = AO_CCSS_DIR . $_FILES['file']['name'];
287
  move_uploaded_file( $_FILES['file']['tmp_name'], $zipfile );
@@ -292,10 +292,20 @@ class autoptimizeCriticalCSSSettingsAjax {
292
  $zip->extractTo( AO_CCSS_DIR );
293
  $zip->close();
294
  } else {
295
- $error = 'extracting';
296
  }
297
 
298
  if ( ! $error ) {
 
 
 
 
 
 
 
 
 
 
299
  // Archive extraction ok, continue settings importing
300
  // Settings file.
301
  $importfile = AO_CCSS_DIR . 'settings.json';
@@ -318,6 +328,8 @@ class autoptimizeCriticalCSSSettingsAjax {
318
  $error = 'settings file does not exist';
319
  }
320
  }
 
 
321
  }
322
 
323
  // Prepare response.
281
  $error = false;
282
 
283
  // Process an uploaded file with no errors.
284
+ if ( current_user_can( 'manage_options' ) && ! $_FILES['file']['error'] && strpos( $_FILES['file']['name'], '.zip' ) === strlen( $_FILES['file']['name'] ) - 4 ) {
285
  // Save file to the cache directory.
286
  $zipfile = AO_CCSS_DIR . $_FILES['file']['name'];
287
  move_uploaded_file( $_FILES['file']['tmp_name'], $zipfile );
292
  $zip->extractTo( AO_CCSS_DIR );
293
  $zip->close();
294
  } else {
295
+ $error = 'could not extract';
296
  }
297
 
298
  if ( ! $error ) {
299
+ // only known files allowed, all others are deleted.
300
+ $_dir_contents_ccss = glob( AO_CCSS_DIR . 'ccss_*.css' );
301
+ $_dir_known_ok = array( AO_CCSS_DIR . 'queue.lock', AO_CCSS_DIR . 'queuelog.html', AO_CCSS_DIR . 'index.html', AO_CCSS_DIR . 'settings.json' );
302
+ $_dir_contents_ok = array_merge( $_dir_contents_ccss, $_dir_known_ok );
303
+ $_dir_contents_all = glob( AO_CCSS_DIR . '*' );
304
+ $_dir_to_be_deleted = array_diff( $_dir_contents_all, $_dir_contents_ok );
305
+ foreach ( $_dir_to_be_deleted as $_file_to_be_deleted ) {
306
+ unlink( $_file_to_be_deleted );
307
+ }
308
+
309
  // Archive extraction ok, continue settings importing
310
  // Settings file.
311
  $importfile = AO_CCSS_DIR . 'settings.json';
328
  $error = 'settings file does not exist';
329
  }
330
  }
331
+ } else {
332
+ $error = 'file could not be saved';
333
  }
334
 
335
  // Prepare response.
classes/autoptimizeImages.php CHANGED
@@ -393,7 +393,7 @@ class autoptimizeImages
393
  return $imgopt_base_url;
394
  }
395
 
396
- private function can_optimize_image( $url )
397
  {
398
  static $cdn_url = null;
399
  static $nopti_images = null;
@@ -425,7 +425,7 @@ class autoptimizeImages
425
  } elseif ( ! empty( $nopti_images ) ) {
426
  $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
427
  foreach ( $nopti_images_array as $nopti_image ) {
428
- if ( strpos( $url, $nopti_image ) !== false ) {
429
  return false;
430
  }
431
  }
@@ -482,7 +482,7 @@ class autoptimizeImages
482
  public function replace_img_callback( $matches, $width = 0, $height = 0 )
483
  {
484
  $_normalized_img_url = $this->normalize_img_url( $matches[1] );
485
- if ( $this->can_optimize_image( $matches[1] ) ) {
486
  return str_replace( $matches[1], $this->build_imgopt_url( $_normalized_img_url, $width, $height ), $matches[0] );
487
  } else {
488
  return $matches[0];
@@ -538,7 +538,7 @@ class autoptimizeImages
538
  if ( isset( $indiv_srcset_parts[1] ) && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
539
  $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
540
  }
541
- if ( $this->can_optimize_image( $indiv_srcset_parts[0] ) ) {
542
  $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
543
  $tag = str_replace( $indiv_srcset_parts[0], $imgopt_url, $tag );
544
  }
@@ -557,7 +557,7 @@ class autoptimizeImages
557
  foreach ( $urls as $url ) {
558
  $full_src_orig = $url[0];
559
  $url = $url[1];
560
- if ( $this->can_optimize_image( $url ) ) {
561
  $imgopt_url = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
562
  $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
563
  $tag = str_replace( $full_src_orig, $full_imgopt_src, $tag );
@@ -579,7 +579,7 @@ class autoptimizeImages
579
  $_url = $this->normalize_img_url( $_url );
580
 
581
  $placeholder = '';
582
- if ( $this->can_optimize_image( $_url ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
583
  $lqip_w = '';
584
  $lqip_h = '';
585
  if ( isset( $imgopt_w ) && ! empty( $imgopt_w ) ) {
@@ -917,7 +917,7 @@ class autoptimizeImages
917
  $_picture_replacement = $_source[0];
918
 
919
  // should we optimize the image?
920
- if ( $imgopt && $this->can_optimize_image( $_source[1] ) ) {
921
  $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
922
  }
923
  // should we lazy-load?
393
  return $imgopt_base_url;
394
  }
395
 
396
+ private function can_optimize_image( $url, $tag = '' )
397
  {
398
  static $cdn_url = null;
399
  static $nopti_images = null;
425
  } elseif ( ! empty( $nopti_images ) ) {
426
  $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
427
  foreach ( $nopti_images_array as $nopti_image ) {
428
+ if ( strpos( $url, $nopti_image ) !== false || ( ( '' !== $tag && strpos( $tag, $nopti_image ) !== false ) ) ) {
429
  return false;
430
  }
431
  }
482
  public function replace_img_callback( $matches, $width = 0, $height = 0 )
483
  {
484
  $_normalized_img_url = $this->normalize_img_url( $matches[1] );
485
+ if ( $this->can_optimize_image( $matches[1], $matches[0] ) ) {
486
  return str_replace( $matches[1], $this->build_imgopt_url( $_normalized_img_url, $width, $height ), $matches[0] );
487
  } else {
488
  return $matches[0];
538
  if ( isset( $indiv_srcset_parts[1] ) && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
539
  $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
540
  }
541
+ if ( $this->can_optimize_image( $indiv_srcset_parts[0], $tag ) ) {
542
  $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
543
  $tag = str_replace( $indiv_srcset_parts[0], $imgopt_url, $tag );
544
  }
557
  foreach ( $urls as $url ) {
558
  $full_src_orig = $url[0];
559
  $url = $url[1];
560
+ if ( $this->can_optimize_image( $url, $full_src_orig ) ) {
561
  $imgopt_url = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
562
  $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
563
  $tag = str_replace( $full_src_orig, $full_imgopt_src, $tag );
579
  $_url = $this->normalize_img_url( $_url );
580
 
581
  $placeholder = '';
582
+ if ( $this->can_optimize_image( $_url, $tag ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
583
  $lqip_w = '';
584
  $lqip_h = '';
585
  if ( isset( $imgopt_w ) && ! empty( $imgopt_w ) ) {
917
  $_picture_replacement = $_source[0];
918
 
919
  // should we optimize the image?
920
+ if ( $imgopt && $this->can_optimize_image( $_source[1], $_picture[0] ) ) {
921
  $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
922
  }
923
  // should we lazy-load?
classes/autoptimizeMain.php CHANGED
@@ -611,8 +611,8 @@ class autoptimizeMain
611
  $ao_ccss_dir = WP_CONTENT_DIR . '/uploads/ao_ccss/';
612
  if ( file_exists( $ao_ccss_dir ) && is_dir( $ao_ccss_dir ) ) {
613
  // fixme: should check for subdirs when in multisite and remove contents of those as well.
614
- array_map( 'unlink', glob( AO_CCSS_DIR . '*.{css,html,json,log,zip,lock}', GLOB_BRACE ) );
615
- rmdir( AO_CCSS_DIR );
616
  }
617
  }
618
 
611
  $ao_ccss_dir = WP_CONTENT_DIR . '/uploads/ao_ccss/';
612
  if ( file_exists( $ao_ccss_dir ) && is_dir( $ao_ccss_dir ) ) {
613
  // fixme: should check for subdirs when in multisite and remove contents of those as well.
614
+ array_map( 'unlink', glob( $ao_ccss_dir . '*.{css,html,json,log,zip,lock}', GLOB_BRACE ) );
615
+ rmdir( $ao_ccss_dir );
616
  }
617
  }
618
 
classes/external/js/jquery.cookie.js CHANGED
@@ -32,7 +32,7 @@
32
  * If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
33
  * If set to null or omitted, the cookie will be a session cookie and will not be retained
34
  * when the the browser exits.
35
- * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
36
  * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
37
  * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
38
  * require a secure protocol (like HTTPS).
32
  * If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
33
  * If set to null or omitted, the cookie will be a session cookie and will not be retained
34
  * when the the browser exits.
35
+ * @option String path The value of the path attribute of the cookie (default: path of page that created the cookie).
36
  * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
37
  * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
38
  * require a secure protocol (like HTTPS).
classes/external/php/minify-html.php CHANGED
@@ -158,6 +158,9 @@ class Minify_HTML {
158
  // use newlines before 1st attribute in open tags (to limit line lengths)
159
  //$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
160
 
 
 
 
161
  // fill placeholders
162
  $this->_html = str_replace(
163
  array_keys($this->_placeholders)
158
  // use newlines before 1st attribute in open tags (to limit line lengths)
159
  //$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
160
 
161
+ // reverse order while preserving keys to ensure the last replacement is done first, etc ...
162
+ $this->_placeholders = array_reverse( $this->_placeholders, true );
163
+
164
  // fill placeholders
165
  $this->_html = str_replace(
166
  array_keys($this->_placeholders)
config/autoptimize_404_handler.php CHANGED
@@ -21,6 +21,11 @@
21
  */
22
 
23
  $original_request = strtok( $_SERVER['REQUEST_URI'], '?' );
 
 
 
 
 
24
  $fallback_target = preg_replace( '/(.*)_(?:[a-z0-9]{32})\.(js|css)$/', '${1}_fallback.${2}', $original_request );
25
  $ao_cache_dir = '<!--ao-cache-dir-->';
26
  $js_or_css = pathinfo( $original_request, PATHINFO_EXTENSION );
21
  */
22
 
23
  $original_request = strtok( $_SERVER['REQUEST_URI'], '?' );
24
+
25
+ if ( strpos( $original_request, 'uucss/uucss-' ) !== false ) {
26
+ $original_request = preg_replace( '/uucss\/uucss-[a-z0-9]{32}-/', 'css/', $original_request );
27
+ }
28
+
29
  $fallback_target = preg_replace( '/(.*)_(?:[a-z0-9]{32})\.(js|css)$/', '${1}_fallback.${2}', $original_request );
30
  $ao_cache_dir = '<!--ao-cache-dir-->';
31
  $js_or_css = pathinfo( $original_request, PATHINFO_EXTENSION );
readme.txt CHANGED
@@ -5,14 +5,14 @@ Donate link: http://blog.futtta.be/2013/10/21/do-not-donate-to-me/
5
  Requires at least: 4.9
6
  Tested up to: 5.5
7
  Requires PHP: 5.6
8
- Stable tag: 2.7.6
9
 
10
  Autoptimize speeds up your website by optimizing JS, CSS, images (incl. lazy-load), HTML and Google Fonts, asyncing JS, removing emoji cruft and more.
11
 
12
  == Description ==
13
 
14
  Autoptimize makes optimizing your site really easy. It can aggregate, minify and cache scripts and styles, injects CSS in the page head by default but can also inline critical CSS and defer the aggregated full CSS, moves and defers scripts to the footer and minifies HTML. You can optimize and lazy-load images, optimize Google Fonts, async non-aggregated JavaScript, remove WordPress core emoji cruft and more. As such it can improve your site's performance even when already on HTTP/2! There is extensive API available to enable you to tailor Autoptimize to each and every site's specific needs.
15
- If you consider performance important, you really should use one of the many caching plugins to do page caching. Some good candidates to complement Autoptimize that way are e.g. [Speed Booster Pack](https://wordpress.org/plugins/speed-booster-pack/), [KeyCDN's Cache Enabler](https://wordpress.org/plugins/cache-enabler) or [WP Super Cache](http://wordpress.org/plugins/wp-super-cache/).
16
 
17
  > <strong>Premium Support</strong><br>
18
  > We provide great [Autoptimize Pro Support and Web Performance Optimization services](https://autoptimize.com/), check out our offering on [https://autoptimize.com/](https://autoptimize.com/)!
@@ -311,6 +311,14 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
311
 
312
  == Changelog ==
313
 
 
 
 
 
 
 
 
 
314
  = 2.7.6 =
315
  * fix for top frontend admin-bar being invisible when "inline & defer" is active.
316
  * fix for 3rd party CSS-files not being deferred when "inline & defer" is active.
5
  Requires at least: 4.9
6
  Tested up to: 5.5
7
  Requires PHP: 5.6
8
+ Stable tag: 2.7.7
9
 
10
  Autoptimize speeds up your website by optimizing JS, CSS, images (incl. lazy-load), HTML and Google Fonts, asyncing JS, removing emoji cruft and more.
11
 
12
  == Description ==
13
 
14
  Autoptimize makes optimizing your site really easy. It can aggregate, minify and cache scripts and styles, injects CSS in the page head by default but can also inline critical CSS and defer the aggregated full CSS, moves and defers scripts to the footer and minifies HTML. You can optimize and lazy-load images, optimize Google Fonts, async non-aggregated JavaScript, remove WordPress core emoji cruft and more. As such it can improve your site's performance even when already on HTTP/2! There is extensive API available to enable you to tailor Autoptimize to each and every site's specific needs.
15
+ If you consider performance important, you really should use one of the many caching plugins to do page caching. Some good candidates to complement Autoptimize that way are e.g. [Speed Booster pack](https://wordpress.org/plugins/speed-booster-pack/), [KeyCDN's Cache Enabler](https://wordpress.org/plugins/cache-enabler), [WP Super Cache](http://wordpress.org/plugins/wp-super-cache/) or if you use Cloudflare [WP Cloudflare Super Page Cache](https://wordpress.org/plugins/wp-cloudflare-page-cache/).
16
 
17
  > <strong>Premium Support</strong><br>
18
  > We provide great [Autoptimize Pro Support and Web Performance Optimization services](https://autoptimize.com/), check out our offering on [https://autoptimize.com/](https://autoptimize.com/)!
311
 
312
  == Changelog ==
313
 
314
+ = 2.7.7 =
315
+ * critical CSS: make sure pages get a path-based rule even if a CPT or template matches (when "path based rules for pages" option is on)
316
+ * critical CSS: make sure the "unload CCSS javascript" is only added once
317
+ * settings screens: switch jQuery .attr() to .prop() as suggested by jQuery Migrate to prepare for [the great oncoming big jQuery updates](https://wptavern.com/major-jquery-changes-on-the-way-for-wordpress-5-5-and-beyond)
318
+ * HTML minify: reverse placeholder array to make sure last replaced placeholder is changed back first to fix rare issues
319
+ * security fix: kudos to [Erin Germ](https://eringerm.com/) for finding & reporting an authenticated XSS vulnerability
320
+ * security fix: props to an anonymous pentester for finding & reporting an authenticated malicous file upload vulnerability
321
+
322
  = 2.7.6 =
323
  * fix for top frontend admin-bar being invisible when "inline & defer" is active.
324
  * fix for 3rd party CSS-files not being deferred when "inline & defer" is active.