el="stylesheet";link.media="only x"});setTimeout(enableStylesheet,3000)};rp.poly=function(){if(rp.support()){return} var links=w.document.getElementsByTagName("link");for(var i=0;i JS; } /** * Insert critical CSS before combined CSS when option is active. * * @since 2.11.5 * * @param string $buffer HTML output of the page. * * @return string Updated HTML output */ public function insert_critical_css_buffer( $buffer ) { if ( ! $this->should_async_css() ) { return $buffer; } $critical_css_content = $this->critical_css->get_critical_css_content(); if ( empty( $critical_css_content ) ) { return $buffer; } $this->critical_css_content = str_replace( '\\', '\\\\', $critical_css_content ); $buffer = preg_replace( '##iU', $this->return_remove_cpcss_script() . '', $buffer, 1 ); return $this->add_meta_comment( 'async_css', $buffer ); } /** * Insert critical CSS into head. * * @param array $items Head elements. * @return mixed */ public function insert_css_in_head( $items ) { $css = $this->get_critical_css_content(); if ( empty( $css ) ) { return $items; } $items[] = $this->style_tag( $css, [ 'id' => 'rocket-critical-css', ] ); return $items; } /** * Get critical CSS content, getter method for critical_css_content property. * * @return string */ public function get_critical_css_content() { return $this->should_async_css() ? $this->critical_css_content : ''; } /** * Returns JS script to remove the critical css style from frontend. * * @since 3.6 * * @return string */ protected function return_remove_cpcss_script() { $filename = rocket_get_constant( 'SCRIPT_DEBUG' ) ? 'cpcss-removal.js' : 'cpcss-removal.min.js'; $script = rocket_get_constant( 'WP_ROCKET_PATH' ) . "assets/js/{$filename}"; if ( ! is_readable( $script ) ) { return ''; } return sprintf( '', $this->filesystem->get_contents( $script ) ); } /** * Adds wprRemoveCPCSS to excluded inline JS array. * * @since 3.6 * * @param array $excluded_inline Array of inline JS excluded from being combined. * * @return array */ public function exclude_inline_js( array $excluded_inline ) { $excluded_inline[] = 'wprRemoveCPCSS'; return $excluded_inline; } /** * Defer loading of CSS files. * * @since 2.10 * * @param string $buffer HTML code. * * @return string Updated HTML code */ public function async_css( $buffer ) { if ( ! $this->should_async_css() ) { return $buffer; } if ( empty( $this->critical_css->get_current_page_critical_css() ) && empty( $this->options->get( 'critical_css', '' ) ) ) { return $buffer; } $excluded_css = array_flip( $this->critical_css->get_exclude_async_css() ); /** * Filters the pattern used to get all stylesheets in the HTML. * * @since 2.10 * * @param string $css_pattern Regex pattern to get all stylesheets in the HTML. */ $css_pattern = apply_filters( 'rocket_async_css_regex_pattern', '/(?=]*\s(rel\s*=\s*[\'"]stylesheet["\']))]*\shref\s*=\s*[\'"]([^\'"]+)[\'"](.*)>/iU' ); // Remove comments from the buffer. $clean_buffer = $this->hide_comments( $buffer ); $clean_buffer = $this->hide_noscripts( $clean_buffer ); // Get all css files with this regex. preg_match_all( $css_pattern, $clean_buffer, $tags_match ); if ( ! isset( $tags_match[0] ) ) { return $buffer; } $noscripts = ''; return str_replace( '', $noscripts . '', $buffer ); } /** * Regenerates the CPCSS when switching theme if the option is active. * * @since 3.3 */ public function maybe_regenerate_cpcss() { if ( ! $this->options->get( 'async_css' ) ) { return; } if ( ! $this->is_mobile_cpcss_active() ) { $this->critical_css->process_handler( 'default', 'all' ); return; } $this->critical_css->process_handler( 'all' ); } /** * Checks if mobile CPCSS is active. * * @since 3.6 * * @return boolean CPCSS active or not. */ private function is_mobile_cpcss_active() { return ( $this->options->get( 'async_css', 0 ) && $this->options->get( 'cache_mobile', 0 ) && $this->options->get( 'do_caching_mobile_files', 0 ) ) && $this->options->get( 'async_css_mobile', 0 ); } /** * Checks if we should async CSS * * @since 3.6.2.1 * * @return boolean */ private function should_async_css() { if ( rocket_get_constant( 'DONOTROCKETOPTIMIZE' ) ) { return false; } if ( ! $this->options->get( 'async_css', 0 ) ) { return false; } return ! is_rocket_post_excluded_option( 'async_css' ); } /** * Stops the critical CSS generation. * * @since 3.10 * * @return void */ public function stop_critical_css_generation() { $this->critical_css->stop_generation(); delete_transient( 'rocket_critical_css_generation_process_running' ); delete_transient( 'rocket_critical_css_generation_process_complete' ); } /** * Stops the critical CSS generation when deactivating the async CSS option via CLI and remove the notices. * * @note CL. * @since 3.12.6 */ public function stop_process_on_cli_deactivation() { $this->stop_critical_css_generation(); } /** * Sanitizes options * * @param array $input Array of values submitted from the form. * @param \WP_Rocket\Engine\Admin\Settings\Settings $settings Settings class instance. * * @note CL * @return array */ public function sanitize_options( $input, $settings ) : array { $is_wp2_env = ( function_exists( 'clsop_is_wp2_environment' ) && clsop_is_wp2_environment() ); // WP2 branch: checkbox only toggles OCD and triggers clwpos-user; no async_css* normalization. if ( $is_wp2_env ) { // Only record intended action; do not change unrelated options here. $this->cpcss_toggle_action = null; $option_key = rocket_get_constant( 'WP_ROCKET_SLUG', 'wp_rocket_settings' ); $posted = filter_input( INPUT_POST, $option_key, FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ); // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( ! is_array( $posted ) && isset( $_POST[ $option_key ] ) && is_array( $_POST[ $option_key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing $posted = array_map( 'sanitize_text_field', wp_unslash( $_POST[ $option_key ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing } $posted = is_array( $posted ) ? $posted : []; if ( array_key_exists( 'optimize_css_delivery', $posted ) ) { $raw_value = $posted['optimize_css_delivery']; $ocd_checked = (int) sanitize_text_field( (string) $raw_value ) === 1; $this->cpcss_toggle_action = $ocd_checked ? 'enable' : 'disable'; set_transient( 'clsop_ui_cpcss_toggle_action', $this->cpcss_toggle_action, MINUTE_IN_SECONDS ); return array_merge( (array) $input, [ 'optimize_css_delivery' => $ocd_checked ? '1' : 0 ] ); } // Unchecked: payload exists but field is absent. if ( is_array( $posted ) && ! empty( $posted ) && ! array_key_exists( 'optimize_css_delivery', $posted ) ) { $this->cpcss_toggle_action = 'disable'; set_transient( 'clsop_ui_cpcss_toggle_action', $this->cpcss_toggle_action, MINUTE_IN_SECONDS ); return array_merge( (array) $input, [ 'optimize_css_delivery' => 0 ] ); } // Fallback to sanitized input if POST is unavailable in this context. if ( array_key_exists( 'optimize_css_delivery', $input ) ) { $ocd_checked = (int) sanitize_text_field( (string) $input['optimize_css_delivery'] ) === 1; $this->cpcss_toggle_action = $ocd_checked ? 'enable' : 'disable'; set_transient( 'clsop_ui_cpcss_toggle_action', $this->cpcss_toggle_action, MINUTE_IN_SECONDS ); return array_merge( (array) $input, [ 'optimize_css_delivery' => $ocd_checked ? '1' : 0 ] ); } return $input; } // Non-WP2 branch. $feature_enabled = (int) get_rocket_option( 'async_css', 0 ) === 1; $uuid = (string) get_rocket_option( 'ccss_awp_unique_id', '' ); // CL: In standalone mode, allow user to toggle the checkbox via UI. // Preserve existing uuid if any. if ( function_exists( 'clsop_is_standalone' ) && clsop_is_standalone() ) { if ( ! empty( $input['async_css'] ) || ! empty( $input['optimize_css_delivery'] ) ) { $input['ccss_awp_unique_id'] = $uuid; // Preserve existing, not used for API auth. $input['async_css'] = 1; $input['async_css_mobile'] = 1; } return $input; } // CLOS mode: preserve legacy normalization (managed by CLI). if ( ! $feature_enabled ) { // No normalization when feature disabled; keep input as-is. return $input; } // If input is empty: UUID first (if any), then enforced keys in fixed order. if ( empty( $input ) || ! is_array( $input ) ) { $normalized = []; if ( '' !== $uuid ) { $normalized['ccss_awp_unique_id'] = $uuid; } $normalized += [ 'async_css' => 1, 'async_css_mobile' => 1, 'remove_unused_css' => 0, 'optimize_css_delivery' => '1', ]; return array_merge( (array) $input, $normalized ); } // Non-empty input: enforce values and put UUID last to match fixtures. $normalized = [ 'async_css' => 1, 'async_css_mobile' => 1, 'remove_unused_css' => 0, 'optimize_css_delivery' => '1', ]; if ( '' !== $uuid ) { $normalized['ccss_awp_unique_id'] = $uuid; } return array_merge( (array) $input, $normalized ); } /** * After options are saved, if OCD was checked in WP2, enable hosting feature via clwpos-user. * * @param array $value New options. * @return void */ public function on_options_changed_after_ui( $value ) { // Retrieve recorded action from transient if property is not set. if ( null === $this->cpcss_toggle_action ) { $this->cpcss_toggle_action = get_transient( 'clsop_ui_cpcss_toggle_action' ); delete_transient( 'clsop_ui_cpcss_toggle_action' ); } if ( null === $this->cpcss_toggle_action ) { return; } $env_is_wp2 = ( function_exists( 'clsop_is_wp2_environment' ) && clsop_is_wp2_environment() ); // Allow direct override of the client (e.g., tests) before consulting the container. $client = apply_filters( 'rocket_awp_clwpos_user_client', null ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound if ( ! is_object( $client ) ) { $container = apply_filters( 'rocket_container', null ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound if ( ! is_object( $container ) || ! method_exists( $container, 'get' ) ) { return; } $client = $container->get( 'awp_clwpos_user' ); } if ( ! is_object( $client ) ) { return; } // Only proceed if WP2 env OR the client/binary is available. if ( ! $env_is_wp2 ) { if ( ! method_exists( $client, 'is_available' ) || ! $client->is_available() ) { return; } } if ( 'disable' === $this->cpcss_toggle_action && method_exists( $client, 'disable_critical_css_current_site' ) ) { $client->disable_critical_css_current_site(); } elseif ( 'enable' === $this->cpcss_toggle_action && method_exists( $client, 'enable_critical_css_current_site' ) ) { $client->enable_critical_css_current_site(); } // Reset the recorded action. $this->cpcss_toggle_action = null; } /** * Display a notice to pass from CPCSS to RUCSS. * * @return void */ public function switch_to_rucss_notice() { // CL: removed. } /** * Switch to RUCSS. * * @return void */ public function switch_to_rucss() { // CL: removed. } /** * CL: Enable inline CSS for Google Fonts on mobile when aggressive optimization is active. * * This eliminates render-blocking font CSS requests on mobile devices by inlining * the font-face declarations directly into the HTML. * * @since 3.x * * @param bool $inline Whether to inline Google Fonts CSS. * @return bool True to inline fonts CSS on mobile with aggressive optimization. */ public function maybe_inline_fonts_on_mobile( $inline ) { // Already enabled by another filter. if ( $inline ) { return $inline; } // Check if Critical CSS (async_css) is enabled. if ( ! $this->options->get( 'async_css', 0 ) ) { return $inline; } // Check if aggressive mobile optimization is enabled. if ( ! $this->options->get( 'async_css_mobile_aggressive', 0 ) ) { return $inline; } // Only apply on mobile devices. if ( ! wp_is_mobile() ) { return $inline; } return true; } /** * CL: Filter CSS exclusions for aggressive mobile optimization. * * Reduces the list of CSS files excluded from async loading on mobile devices, * keeping only critical patterns like admin-bar and dashicons. * * @since 3.x * * @param array $exclusions Current list of CSS files excluded from async loading. * @return array Reduced list of exclusions for mobile optimization. */ public function aggressive_mobile_css_optimization( $exclusions ) { if ( ! is_array( $exclusions ) || empty( $exclusions ) ) { return $exclusions; } /** * Filter the list of CSS patterns to keep excluded even in aggressive mobile mode. * * Some CSS files should remain excluded from async loading even on mobile * to prevent layout shifts or critical functionality issues. * * @since 3.x * * @param array $keep_excluded Patterns of CSS files to keep excluded. */ $keep_excluded = (array) apply_filters( 'rocket_aggressive_mobile_css_keep_excluded', [ 'admin-bar', 'dashicons', 'wp-admin', ] ); if ( empty( $keep_excluded ) ) { return []; } $filtered_exclusions = []; foreach ( $exclusions as $exclusion ) { foreach ( $keep_excluded as $pattern ) { if ( false !== strpos( $exclusion, $pattern ) ) { $filtered_exclusions[] = $exclusion; break; } } } return $filtered_exclusions; } }