Skip to content

Commit

Permalink
Improve handling of validation errors for enqueued scripts
Browse files Browse the repository at this point in the history
* Eliminate enqueued_script validation error in favor of merging sources into invalid_element errors
* Add source information for inline scripts attached to enqueued scripts.
* Account for enqueueing script bundles (where src is false).
* Capture inline script contents in validation errors. Fixes #1031.
  • Loading branch information
westonruter committed Apr 26, 2018
1 parent 5f067bc commit 9365a6b
Showing 1 changed file with 77 additions and 23 deletions.
100 changes: 77 additions & 23 deletions includes/utils/class-amp-validation-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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'];
Expand Down Expand Up @@ -619,7 +613,69 @@ 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' ) ) {

// External script.
$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 ) {

// Inline script.
$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;
}
}
} elseif ( $node instanceof DOMAttr ) {
if ( ! isset( $data['code'] ) ) {
Expand Down Expand Up @@ -1227,20 +1283,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'];
}
}
}
Expand Down Expand Up @@ -2253,7 +2307,7 @@ public static function store_validation_errors( $validation_errors, $url ) {
&&
isset( $post_data['post_type'] )
&&
self::POST_TYPE_SLUG === $post_data['post_type']
AMP_Validation_Utils::POST_TYPE_SLUG === $post_data['post_type']
);
if ( $should_supply_post_content ) {
$post_data['post_content'] = wp_slash( $post_content );
Expand Down

0 comments on commit 9365a6b

Please sign in to comment.