diff --git a/includes/utils/class-amp-validation-utils.php b/includes/utils/class-amp-validation-utils.php index 54dffc75f65..b4104d7589f 100644 --- a/includes/utils/class-amp-validation-utils.php +++ b/includes/utils/class-amp-validation-utils.php @@ -110,13 +110,6 @@ class AMP_Validation_Utils { */ const INVALID_ATTRIBUTE_CODE = 'invalid_attribute'; - /** - * Validation code for when script is enqueued (which is not allowed). - * - * @var string - */ - const ENQUEUED_SCRIPT_CODE = 'enqueued_script'; - /** * The key for removed elements. * @@ -588,7 +581,8 @@ public static function has_cap() { * @return bool Whether the validation error should result in sanitization. */ public static function add_validation_error( array $data ) { - $node = null; + $node = null; + $matches = null; if ( isset( $data['node'] ) && $data['node'] instanceof DOMNode ) { $node = $data['node']; @@ -619,8 +613,71 @@ public static function add_validation_error( array $data ) { isset( self::$enqueued_style_sources[ $matches['handle'] ] ) ); if ( $is_enqueued_link ) { - $data['sources'] = self::$enqueued_style_sources[ $matches['handle'] ]; + $data['sources'] = array_merge( + $data['sources'], + self::$enqueued_style_sources[ $matches['handle'] ] + ); + } + + /** + * Script dependency. + * + * @var _WP_Dependency $script_dependency + */ + if ( 'script' === $node->nodeName ) { + $enqueued_script_handles = array_intersect( wp_scripts()->done, array_keys( self::$enqueued_script_sources ) ); + if ( $node->hasAttribute( 'src' ) ) { + $src = $node->getAttribute( 'src' ); + foreach ( $enqueued_script_handles as $enqueued_script_handle ) { + $script_dependency = wp_scripts()->registered[ $enqueued_script_handle ]; + $is_matching_script = ( + $script_dependency + && + $script_dependency->src + && + // Script attribute is haystack because includes protocol and may include query args (like ver). + false !== strpos( $src, preg_replace( '#^https?:(?=//)#', '', $script_dependency->src ) ) + ); + if ( $is_matching_script ) { + $data['sources'] = array_merge( + $data['sources'], + self::$enqueued_script_sources[ $enqueued_script_handle ] + ); + break; + } + } + } elseif ( $node->firstChild ) { + $text = $node->textContent; + foreach ( $enqueued_script_handles as $enqueued_script_handle ) { + $inline_scripts = array_filter( array_merge( + (array) wp_scripts()->get_data( $enqueued_script_handle, 'data' ), + (array) wp_scripts()->get_data( $enqueued_script_handle, 'before' ), + (array) wp_scripts()->get_data( $enqueued_script_handle, 'after' ) + ) ); + foreach ( $inline_scripts as $inline_script ) { + /* + * Check to see if the inline script is inside (or the same) as the script in the document. + * Note that WordPress takes the registered inline script and will output it with newlines + * padding it, and sometimes with the script wrapped by CDATA blocks. + */ + if ( false !== strpos( $text, trim( $inline_script ) ) ) { + $data['sources'] = array_merge( + $data['sources'], + self::$enqueued_script_sources[ $enqueued_script_handle ] + ); + break; + } + } + } + $data['text'] = $text; + } } + + $is_enqueued_script = ( + 'script' === $node->nodeName + && + $node->hasAttribute( 'src' ) + ); } elseif ( $node instanceof DOMAttr ) { if ( ! isset( $data['code'] ) ) { $data['code'] = self::INVALID_ATTRIBUTE_CODE; @@ -1227,20 +1284,18 @@ public static function wrapped_callback( $callback ) { } } - // Keep track of which source enqueued the scripts, and immediately report validity . + // Keep track of which source enqueued the scripts, and immediately report validity. if ( isset( $wp_scripts ) && isset( $wp_scripts->queue ) ) { - foreach ( array_diff( $wp_scripts->queue, $before_scripts_enqueued ) as $handle ) { - AMP_Validation_Utils::$enqueued_script_sources[ $handle ][] = $callback['source']; - - // Flag all scripts not loaded from the AMP CDN as validation errors. - if ( isset( $wp_scripts->registered[ $handle ] ) && 0 !== strpos( $wp_scripts->registered[ $handle ]->src, 'https://cdn.ampproject.org/' ) ) { - self::add_validation_error( array( - 'code' => self::ENQUEUED_SCRIPT_CODE, - 'handle' => $handle, - 'sources' => array( - $callback['source'], - ), - ) ); + foreach ( array_diff( $wp_scripts->queue, $before_scripts_enqueued ) as $queued_handle ) { + $handles = array( $queued_handle ); + + // Account for case where registered script is a placeholder for a set of scripts (e.g. jquery). + if ( isset( $wp_scripts->registered[ $queued_handle ] ) && false === $wp_scripts->registered[ $queued_handle ]->src ) { + $handles = array_merge( $handles, $wp_scripts->registered[ $queued_handle ]->deps ); + } + + foreach ( $handles as $handle ) { + AMP_Validation_Utils::$enqueued_script_sources[ $handle ][] = $callback['source']; } } }